1
0
mirror of https://github.com/veden/Rampant.git synced 2024-12-28 21:08:22 +02:00
Rampant/libs/MapProcessor.lua

388 lines
13 KiB
Lua
Raw Normal View History

2019-02-16 06:17:30 +02:00
if mapProcessorG then
return mapProcessorG
end
local mapProcessor = {}
-- imports
2017-06-11 02:59:06 +02:00
local unitGroupUtils = require("UnitGroupUtils")
2016-08-21 23:48:55 +02:00
local pheromoneUtils = require("PheromoneUtils")
local aiAttackWave = require("AIAttackWave")
2017-05-19 09:47:24 +02:00
local aiPredicates = require("AIPredicates")
2016-08-29 02:05:28 +02:00
local constants = require("Constants")
2016-10-15 02:00:18 +02:00
local mapUtils = require("MapUtils")
2017-05-19 09:47:24 +02:00
local playerUtils = require("PlayerUtils")
2017-11-21 09:27:03 +02:00
local chunkUtils = require("ChunkUtils")
local chunkPropertyUtils = require("ChunkPropertyUtils")
local baseUtils = require("BaseUtils")
2016-09-14 13:16:33 +02:00
-- constants
2020-04-28 05:41:18 +02:00
local DURATION_ACTIVE_NEST_DIVIDER = constants.DURATION_ACTIVE_NEST_DIVIDER
local DURATION_ACTIVE_NEST = constants.DURATION_ACTIVE_NEST
2016-09-14 13:16:33 +02:00
local PROCESS_QUEUE_SIZE = constants.PROCESS_QUEUE_SIZE
2016-11-04 09:26:19 +02:00
2016-09-14 13:16:33 +02:00
local SCAN_QUEUE_SIZE = constants.SCAN_QUEUE_SIZE
local CHUNK_SIZE = constants.CHUNK_SIZE
2017-06-01 03:46:53 +02:00
local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
2016-10-15 02:00:18 +02:00
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
local CHUNK_TICK = constants.CHUNK_TICK
2016-11-04 09:26:19 +02:00
local AI_SQUAD_COST = constants.AI_SQUAD_COST
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST
2018-02-13 09:10:17 +02:00
local AI_SETTLER_COST = constants.AI_SETTLER_COST
2019-04-25 08:13:22 +02:00
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
2016-11-04 09:26:19 +02:00
2019-12-16 03:16:56 +02:00
local processNestActiveness = chunkPropertyUtils.processNestActiveness
2019-02-20 08:16:43 +02:00
2016-11-04 09:26:19 +02:00
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
2019-02-20 08:16:43 +02:00
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
2016-11-04 09:26:19 +02:00
2018-02-12 05:21:28 +02:00
local INTERVAL_RALLY = constants.INTERVAL_RALLY
local INTERVAL_RETREAT = constants.INTERVAL_RETREAT
local INTERVAL_SPAWNER = constants.INTERVAL_SPAWNER
local BASE_PROCESS_INTERVAL = constants.BASE_PROCESS_INTERVAL
-- imported functions
-- local scents = pheromoneUtils.scents
2016-08-21 23:48:55 +02:00
local processPheromone = pheromoneUtils.processPheromone
2018-10-20 07:17:37 +02:00
local commitPheromone = pheromoneUtils.commitPheromone
2017-11-21 09:27:03 +02:00
local playerScent = pheromoneUtils.playerScent
local formSquads = aiAttackWave.formSquads
2019-12-07 07:57:20 +02:00
local formAttackWave = aiAttackWave.formAttackWave
2018-02-13 09:10:17 +02:00
local formSettlers = aiAttackWave.formSettlers
2019-02-20 08:16:43 +02:00
local formVengenceSquad = aiAttackWave.formVengenceSquad
2016-10-15 02:00:18 +02:00
local getChunkByPosition = mapUtils.getChunkByPosition
local getChunkByXY = mapUtils.getChunkByXY
2016-10-15 02:00:18 +02:00
2017-06-11 02:59:06 +02:00
local recycleBiters = unitGroupUtils.recycleBiters
2017-11-21 09:27:03 +02:00
local validPlayer = playerUtils.validPlayer
2020-04-28 05:41:18 +02:00
local createSpawnerProxy = baseUtils.createSpawnerProxy
local mapScanChunk = chunkUtils.mapScanChunk
local getNestCount = chunkPropertyUtils.getNestCount
local getEnemyStructureCount = chunkPropertyUtils.getEnemyStructureCount
2019-02-20 08:16:43 +02:00
local getNestActiveness = chunkPropertyUtils.getNestActiveness
local setNestActiveness = chunkPropertyUtils.setNestActiveness
2020-04-28 05:41:18 +02:00
local getNestActiveTick = chunkPropertyUtils.getNestActiveTick
local setNestActiveTick = chunkPropertyUtils.setNestActiveTick
2019-02-20 08:16:43 +02:00
local getRaidNestActiveness = chunkPropertyUtils.getRaidNestActiveness
local setRaidNestActiveness = chunkPropertyUtils.setRaidNestActiveness
2017-05-19 09:47:24 +02:00
local canAttack = aiPredicates.canAttack
local canMigrate = aiPredicates.canMigrate
2017-05-14 00:32:16 +02:00
local findNearbySquad = unitGroupUtils.findNearbySquad
2017-05-28 06:50:37 +02:00
local processBase = baseUtils.processBase
2016-08-29 02:05:28 +02:00
local mMin = math.min
-- local mMax = math.max
2016-08-29 02:05:28 +02:00
local mRandom = math.random
-- module code
2016-10-15 02:00:18 +02:00
--[[
processing is not consistant as it depends on the number of chunks that have been generated
so if we process 400 chunks an iteration and 200 chunks have been generated than these are
processed 3 times a second and 1200 generated chunks would be processed once a second
2019-02-03 08:01:28 +02:00
In theory, this might be fine as smaller bases have less surface to attack and need to have
2016-10-15 02:00:18 +02:00
pheromone dissipate at a faster rate.
--]]
2019-11-30 02:49:22 +02:00
function mapProcessor.processMap(map, surface, tick)
2018-01-14 07:48:21 +02:00
local roll = map.processRoll
local index = map.processIndex
local chunkToBase = map.chunkToBase
2019-02-03 08:01:28 +02:00
2020-05-15 22:51:38 +02:00
local natives = map.natives
2016-08-29 02:05:28 +02:00
if (index == 1) then
roll = mRandom()
2018-01-14 07:48:21 +02:00
map.processRoll = roll
2020-05-15 22:51:38 +02:00
natives.remainingSquads = constants.AI_MAX_SQUADS_PER_CYCLE
print("squads", natives.squads.len, natives.pendingAttack.len, #natives.building)
2016-08-26 00:20:06 +02:00
end
2018-01-26 09:55:09 +02:00
local newEnemies = natives.newEnemies
2018-10-20 07:17:37 +02:00
local scentStaging = map.scentStaging
2019-02-03 08:01:28 +02:00
2020-05-15 22:51:38 +02:00
local squads = canAttack(natives, surface) and (roll <= 0.45) and (natives.points >= AI_SQUAD_COST)
2019-04-25 08:13:22 +02:00
if squads and (natives.state == AI_STATE_AGGRESSIVE) and (tick < natives.canAttackTick) then
squads = false
end
2020-05-15 22:51:38 +02:00
local settlers = canMigrate(natives, surface) and (roll <= 0.45) and (natives.points >= AI_SETTLER_COST)
2018-01-14 07:48:21 +02:00
local processQueue = map.processQueue
2016-09-14 13:16:33 +02:00
local endIndex = mMin(index + PROCESS_QUEUE_SIZE, #processQueue)
2018-10-20 07:17:37 +02:00
local i = 1
2016-08-29 02:05:28 +02:00
for x=index,endIndex do
2019-02-03 08:01:28 +02:00
local chunk = processQueue[x]
2019-10-19 21:13:48 +02:00
if (chunk[CHUNK_TICK] ~= tick) then
processPheromone(map, chunk, scentStaging[i])
2017-04-22 01:14:04 +02:00
2019-02-20 08:16:43 +02:00
if settlers and (getNestCount(map, chunk) > 0) then
2019-11-30 02:49:22 +02:00
settlers = formSettlers(map, surface, chunk, tick)
2019-02-20 08:16:43 +02:00
end
2019-12-07 07:57:20 +02:00
if squads then
squads = formAttackWave(chunk, map, surface, tick)
end
2020-03-16 00:16:00 +02:00
2019-10-19 21:13:48 +02:00
if newEnemies then
local base = chunkToBase[chunk]
2019-12-07 07:57:20 +02:00
if base and ((tick - base.tick) > BASE_PROCESS_INTERVAL) then
2019-11-30 02:49:22 +02:00
processBase(chunk, surface, natives, tick, base)
2019-10-19 21:13:48 +02:00
end
end
end
i = i + 1
2018-10-20 07:17:37 +02:00
end
i = 1
for x=index,endIndex do
2019-10-19 21:13:48 +02:00
local chunk = processQueue[x]
if (chunk[CHUNK_TICK] ~= tick) then
commitPheromone(map, chunk, scentStaging[i], tick)
end
i = i + 1
2016-08-29 02:05:28 +02:00
end
2019-02-03 08:01:28 +02:00
2016-08-29 02:05:28 +02:00
if (endIndex == #processQueue) then
2018-01-14 07:48:21 +02:00
map.processIndex = 1
2016-08-29 02:05:28 +02:00
else
2018-01-14 07:48:21 +02:00
map.processIndex = endIndex + 1
2016-08-29 02:05:28 +02:00
end
end
2020-04-28 05:41:18 +02:00
local function queueNestSpawners(map, chunk, tick)
local limitPerActiveChunkTick = map.natives.activeNests * DURATION_ACTIVE_NEST_DIVIDER
local processActiveNest = map.processActiveNest
if ((getNestActiveness(map, chunk) > 0) and (getNestActiveTick(map, chunk) == 0)) then
local nextTick = tick + DURATION_ACTIVE_NEST
local slot = processActiveNest[nextTick]
if not slot then
slot = {}
processActiveNest[nextTick] = slot
slot[#slot+1] = chunk
else
if (#slot > limitPerActiveChunkTick) then
while (#slot > limitPerActiveChunkTick) do
nextTick = nextTick + 1
slot = processActiveNest[nextTick]
if not slot then
slot = {}
processActiveNest[nextTick] = slot
slot[#slot+1] = chunk
break
elseif (#slot < limitPerActiveChunkTick) then
slot[#slot+1] = chunk
break
end
end
else
slot[#slot+1] = chunk
end
end
setNestActiveTick(map, chunk, tick)
end
end
2016-10-15 02:00:18 +02:00
--[[
Localized player radius were processing takes place in realtime, doesn't store state
between calls.
2019-02-03 08:01:28 +02:00
vs
2016-10-15 02:00:18 +02:00
the slower passive version processing the entire map in multiple passes.
--]]
2019-11-30 02:49:22 +02:00
function mapProcessor.processPlayers(players, map, surface, tick)
2016-10-15 02:00:18 +02:00
-- put down player pheromone for player hunters
-- randomize player order to ensure a single player isn't singled out
2019-11-30 02:49:22 +02:00
local natives = map.natives
local roll = mRandom()
2017-06-01 03:46:53 +02:00
local allowingAttacks = canAttack(natives, surface)
2016-10-15 02:00:18 +02:00
2018-10-20 07:17:37 +02:00
local scentStaging = map.scentStaging
2017-06-01 03:46:53 +02:00
local squads = allowingAttacks and (0.11 <= roll) and (roll <= 0.20) and (natives.points >= AI_SQUAD_COST)
2019-02-03 08:01:28 +02:00
2018-05-26 00:23:22 +02:00
-- not looping everyone because the cost is high enough already in multiplayer
2019-12-07 07:57:20 +02:00
if (#players > 0) then
local player = players[mRandom(#players)]
2019-10-19 21:13:48 +02:00
if validPlayer(player, natives) then
local playerChunk = getChunkByPosition(map, player.character.position)
2019-02-03 08:01:28 +02:00
2020-05-15 22:51:38 +02:00
if (playerChunk ~= -1) then
2019-02-21 08:31:47 +02:00
local i = 1
2019-10-19 21:13:48 +02:00
local vengence = (allowingAttacks and
(natives.points >= AI_VENGENCE_SQUAD_COST) and
((getEnemyStructureCount(map, playerChunk) > 0) or (playerChunk[MOVEMENT_PHEROMONE] < -natives.retreatThreshold)))
2019-02-03 08:01:28 +02:00
2019-10-19 21:13:48 +02:00
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(map, x, y)
2019-02-03 08:01:28 +02:00
2020-05-15 22:51:38 +02:00
if (chunk ~= -1) and (chunk[CHUNK_TICK] ~= tick) then
2019-10-19 21:13:48 +02:00
processPheromone(map, chunk, scentStaging[i])
2017-04-22 01:14:04 +02:00
processNestActiveness(map, chunk, natives, surface)
2020-04-28 05:41:18 +02:00
queueNestSpawners(map, chunk, tick)
2019-02-20 08:16:43 +02:00
if vengence and (getNestCount(map, chunk) > 0) then
2019-11-30 02:49:22 +02:00
vengence = formVengenceSquad(map, surface, chunk)
2019-02-20 08:16:43 +02:00
end
2019-10-19 21:13:48 +02:00
end
i = i + 1
end
end
i = 1
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(map, x, y)
2020-05-15 22:51:38 +02:00
if (chunk ~= -1) and (chunk[CHUNK_TICK] ~= tick) then
2019-10-19 21:13:48 +02:00
commitPheromone(map, chunk, scentStaging[i], tick)
end
i = i + 1
end
end
end
end
2016-10-15 02:00:18 +02:00
end
2019-03-06 08:18:03 +02:00
2019-12-07 07:57:20 +02:00
for i=1,#players do
local player = players[i]
2019-10-19 21:13:48 +02:00
if validPlayer(player, natives) then
local playerChunk = getChunkByPosition(map, player.character.position)
2020-05-15 22:51:38 +02:00
if (playerChunk ~= -1) then
2019-10-19 21:13:48 +02:00
playerScent(playerChunk)
end
end
2019-03-06 08:18:03 +02:00
end
2016-10-15 02:00:18 +02:00
end
2016-11-04 01:51:35 +02:00
--[[
Passive scan to find entities that have been generated outside the factorio event system
2016-11-04 01:51:35 +02:00
--]]
2019-11-30 02:49:22 +02:00
function mapProcessor.scanMap(map, surface, tick)
2018-01-14 07:48:21 +02:00
local index = map.scanIndex
2017-06-01 03:46:53 +02:00
2018-01-14 07:48:21 +02:00
local unitCountQuery = map.filteredEntitiesEnemyUnitQuery
local offset = unitCountQuery.area[2]
local chunkBox = unitCountQuery.area[1]
2018-02-12 05:21:28 +02:00
local retreats = map.chunkToRetreats
local rallys = map.chunkToRallys
2019-05-16 07:11:43 +02:00
-- local spawners = map.chunkToSpawner
2018-02-17 05:31:29 +02:00
local settlers = map.chunkToSettler
2019-02-19 02:43:01 +02:00
local drained = map.chunkToDrained
2018-02-12 05:21:28 +02:00
2018-01-14 07:48:21 +02:00
local processQueue = map.processQueue
2016-09-14 13:16:33 +02:00
local endIndex = mMin(index + SCAN_QUEUE_SIZE, #processQueue)
2019-02-03 08:01:28 +02:00
2019-05-16 07:11:43 +02:00
local isFullMapScan = settings.global["rampant-enableFullMapScan"].value
2019-11-30 02:49:22 +02:00
local natives = map.natives
2020-03-16 00:16:00 +02:00
2016-08-29 02:05:28 +02:00
for x=index,endIndex do
2019-10-19 21:13:48 +02:00
local chunk = processQueue[x]
2019-10-19 21:13:48 +02:00
chunkBox[1] = chunk.x
chunkBox[2] = chunk.y
2019-02-03 08:01:28 +02:00
2019-10-19 21:13:48 +02:00
offset[1] = chunk.x + CHUNK_SIZE
offset[2] = chunk.y + CHUNK_SIZE
2018-02-12 05:21:28 +02:00
2019-10-19 21:13:48 +02:00
local retreatTick = retreats[chunk]
if retreatTick and ((tick - retreatTick) > INTERVAL_RETREAT) then
retreats[chunk] = nil
end
2018-02-12 05:21:28 +02:00
2019-10-19 21:13:48 +02:00
local rallyTick = rallys[chunk]
if rallyTick and ((tick - rallyTick) > INTERVAL_RALLY) then
rallys[chunk] = nil
end
2018-02-12 05:21:28 +02:00
2019-10-19 21:13:48 +02:00
local settlerTick = settlers[chunk]
if settlerTick and ((tick - settlerTick) > 0) then
settlers[chunk] = nil
end
2018-02-17 05:31:29 +02:00
2019-02-19 02:43:01 +02:00
local drainTick = drained[chunk]
2019-10-19 21:13:48 +02:00
if drainTick and ((tick - drainTick) > 0) then
drained[chunk] = nil
end
2019-02-19 02:43:01 +02:00
2019-10-19 21:13:48 +02:00
local closeBy = findNearbySquad(map, chunk, chunk)
2018-02-17 05:31:29 +02:00
2019-10-19 21:13:48 +02:00
if not closeBy then
local deadGroup = surface.count_entities_filtered(unitCountQuery) > 300
2019-02-03 08:01:28 +02:00
2019-10-19 21:13:48 +02:00
if deadGroup then
recycleBiters(natives, surface.find_enemy_units(chunk, TRIPLE_CHUNK_SIZE))
end
end
2017-04-22 01:14:04 +02:00
2019-05-16 07:11:43 +02:00
if isFullMapScan then
2020-04-28 05:41:18 +02:00
mapScanChunk(chunk, surface, map)
2019-10-19 21:13:48 +02:00
end
2019-02-20 08:16:43 +02:00
2020-03-16 00:16:00 +02:00
processNestActiveness(map, chunk, natives, surface)
2020-04-28 05:41:18 +02:00
queueNestSpawners(map, chunk, tick)
end
2017-04-22 01:14:04 +02:00
2016-08-29 02:05:28 +02:00
if (endIndex == #processQueue) then
2019-02-20 08:16:43 +02:00
map.scanIndex = 1
2016-08-29 02:05:28 +02:00
else
2019-02-20 08:16:43 +02:00
map.scanIndex = endIndex + 1
end
end
2020-04-28 05:41:18 +02:00
function mapProcessor.processActiveNests(map, surface, tick)
local processActiveNest = map.processActiveNest
local slot = processActiveNest[tick]
if slot then
for i=1,#slot do
local chunk = slot[i]
if (getNestActiveness(map, chunk) > 0) then
createSpawnerProxy(map, surface, chunk)
-- setNestActiveTick(map, chunk, tick)
local nextTick = tick + DURATION_ACTIVE_NEST
local nextSlot = processActiveNest[nextTick]
if not nextSlot then
nextSlot = {}
processActiveNest[nextTick] = nextSlot
end
nextSlot[#nextSlot+1] = chunk
else
setNestActiveTick(map, chunk, 0)
end
end
processActiveNest[tick] = nil
end
end
2019-02-16 06:17:30 +02:00
mapProcessorG = mapProcessor
2016-09-08 22:02:23 +02:00
return mapProcessor
2020-04-28 05:41:18 +02:00