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

555 lines
17 KiB
Lua
Raw Normal View History

2019-02-16 06:17:30 +02:00
if mapProcessorG then
return mapProcessorG
end
local mapProcessor = {}
-- imports
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
local RESOURCE_QUEUE_SIZE = constants.RESOURCE_QUEUE_SIZE
local ENEMY_QUEUE_SIZE = constants.ENEMY_QUEUE_SIZE
local PLAYER_QUEUE_SIZE = constants.PLAYER_QUEUE_SIZE
2016-11-04 09:26:19 +02:00
local CLEANUP_QUEUE_SIZE = constants.CLEANUP_QUEUE_SIZE
2016-09-14 13:16:33 +02:00
local CHUNK_SIZE = constants.CHUNK_SIZE
2016-10-15 02:00:18 +02:00
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
local CHUNK_TICK = constants.CHUNK_TICK
local PROCESS_STATIC_QUEUE_SIZE = constants.PROCESS_STATIC_QUEUE_SIZE
2016-11-04 09:26:19 +02:00
local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_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
2021-02-14 06:49:54 +02:00
local AI_STATE_PEACEFUL = constants.AI_STATE_PEACEFUL
local AI_STATE_MIGRATING = constants.AI_STATE_MIGRATING
local AI_STATE_SIEGE = constants.AI_STATE_SIEGE
2016-11-04 09:26:19 +02:00
2020-05-20 04:37:16 +02:00
local COOLDOWN_RALLY = constants.COOLDOWN_RALLY
local COOLDOWN_RETREAT = constants.COOLDOWN_RETREAT
2018-02-12 05:21:28 +02:00
local BASE_PROCESS_INTERVAL = constants.BASE_PROCESS_INTERVAL
-- imported functions
local processStaticPheromone = pheromoneUtils.processStaticPheromone
2016-08-21 23:48:55 +02:00
local processPheromone = pheromoneUtils.processPheromone
2020-05-24 06:17:18 +02:00
local getDeathGenerator = chunkPropertyUtils.getDeathGenerator
2020-05-20 04:37:16 +02:00
local processBase = baseUtils.processBase
local processNestActiveness = chunkPropertyUtils.processNestActiveness
local formSquads = aiAttackWave.formSquads
2019-02-20 08:16:43 +02:00
local formVengenceSquad = aiAttackWave.formVengenceSquad
2020-05-20 04:37:16 +02:00
local formSettlers = aiAttackWave.formSettlers
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-11-21 09:27:03 +02:00
local validPlayer = playerUtils.validPlayer
local addPlayerToChunk = chunkPropertyUtils.addPlayerToChunk
local mapScanEnemyChunk = chunkUtils.mapScanEnemyChunk
local mapScanPlayerChunk = chunkUtils.mapScanPlayerChunk
local mapScanResourceChunk = chunkUtils.mapScanResourceChunk
local getNestCount = chunkPropertyUtils.getNestCount
local getEnemyStructureCount = chunkPropertyUtils.getEnemyStructureCount
2019-02-20 08:16:43 +02:00
local getNestActiveness = chunkPropertyUtils.getNestActiveness
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
2017-05-19 09:47:24 +02:00
local canAttack = aiPredicates.canAttack
local canMigrate = aiPredicates.canMigrate
2017-05-14 00:32:16 +02:00
2021-02-14 06:49:54 +02:00
local tableSize = table_size
2017-05-28 06:50:37 +02:00
2016-08-29 02:05:28 +02:00
local mMin = math.min
local mMax = math.max
2016-08-29 02:05:28 +02:00
2020-05-20 04:37:16 +02:00
local next = next
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.
--]]
2021-02-14 06:49:54 +02:00
function mapProcessor.processMap(map, tick)
2018-01-14 07:48:21 +02:00
local index = map.processIndex
local outgoingWave = map.outgoingScanWave
2018-01-14 07:48:21 +02:00
local processQueue = map.processQueue
local processQueueLength = #processQueue
2020-05-20 04:37:16 +02:00
local step
local endIndex
if outgoingWave then
step = 1
endIndex = mMin(index + PROCESS_QUEUE_SIZE, processQueueLength)
else
step = -1
endIndex = mMax(index - PROCESS_QUEUE_SIZE, 1)
2018-10-20 07:17:37 +02:00
end
2020-12-05 06:49:51 +02:00
if (processQueueLength == 0) then
return
end
2021-02-14 06:49:54 +02:00
for x=index,endIndex,step do
2019-10-19 21:13:48 +02:00
local chunk = processQueue[x]
2020-10-19 05:33:01 +02:00
if chunk and (chunk[CHUNK_TICK] ~= tick) then
chunk[CHUNK_TICK] = tick
processPheromone(map, chunk)
2019-10-19 21:13:48 +02:00
end
2016-08-29 02:05:28 +02:00
end
2019-02-03 08:01:28 +02:00
if (endIndex == processQueueLength) then
map.outgoingScanWave = false
elseif (endIndex == 1) then
map.outgoingScanWave = true
elseif outgoingWave then
2018-01-14 07:48:21 +02:00
map.processIndex = endIndex + 1
else
map.processIndex = endIndex - 1
2016-08-29 02:05:28 +02:00
end
end
2021-02-14 06:49:54 +02:00
function mapProcessor.processStaticMap(map)
local index = map.processStaticIndex
local outgoingWave = map.outgoingStaticScanWave
local processQueue = map.processQueue
local processQueueLength = #processQueue
local step
local endIndex
if outgoingWave then
step = 1
endIndex = mMin(index + PROCESS_STATIC_QUEUE_SIZE, processQueueLength)
else
step = -1
endIndex = mMax(index - PROCESS_STATIC_QUEUE_SIZE, 1)
end
2020-12-05 06:49:51 +02:00
if (processQueueLength == 0) then
return
end
2021-02-14 06:49:54 +02:00
for x=index,endIndex,step do
local chunk = processQueue[x]
processStaticPheromone(map, chunk)
end
if (endIndex == processQueueLength) then
map.outgoingStaticScanWave = false
elseif (endIndex == 1) then
map.outgoingStaticScanWave = true
elseif outgoingWave then
map.processStaticIndex = endIndex + 1
else
map.processStaticIndex = endIndex - 1
end
end
2020-04-28 05:41:18 +02:00
local function queueNestSpawners(map, chunk, tick)
2021-02-14 06:49:54 +02:00
local limitPerActiveChunkTick =
2021-02-20 09:31:36 +02:00
(map.activeNests + map.activeRaidNests) * DURATION_ACTIVE_NEST_DIVIDER
2020-04-28 05:41:18 +02:00
local processActiveNest = map.processActiveNest
2020-05-20 04:37:16 +02:00
if ((getNestActiveness(map, chunk) > 0) or (getRaidNestActiveness(map, chunk) > 0)) and
(getNestActiveTick(map, chunk) == 0)
then
2020-04-28 05:41:18 +02:00
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.
--]]
2021-02-20 07:41:30 +02:00
function mapProcessor.processPlayers(players, map, 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
2021-02-20 09:31:36 +02:00
local allowingAttacks = canAttack(map, tick)
local universe = map.universe
2016-10-15 02:00:18 +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)]
2021-02-20 09:31:36 +02:00
if validPlayer(player) then
2019-10-19 21:13:48 +02:00
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
2020-05-20 04:37:16 +02:00
local vengence = allowingAttacks and
2021-02-20 09:31:36 +02:00
(map.points >= AI_VENGENCE_SQUAD_COST) and
2020-05-20 04:37:16 +02:00
((getEnemyStructureCount(map, playerChunk) > 0) or
(-getDeathGenerator(map, playerChunk) < -universe.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
chunk[CHUNK_TICK] = tick
2020-05-24 05:47:14 +02:00
processPheromone(map, chunk, true)
if (getNestCount(map, chunk) > 0) then
2021-02-20 07:41:30 +02:00
processNestActiveness(map, chunk)
queueNestSpawners(map, chunk, tick)
if vengence then
2021-02-20 09:31:36 +02:00
local count = map.vengenceQueue[chunk]
if not count then
count = 0
2021-02-20 09:31:36 +02:00
map.vengenceQueue[chunk] = count
end
2021-02-20 09:31:36 +02:00
map.vengenceQueue[chunk] = count + 1
2020-05-20 04:37:16 +02:00
end
2019-02-20 08:16:43 +02:00
end
2019-10-19 21:13:48 +02:00
end
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]
2021-02-20 09:31:36 +02:00
if validPlayer(player) then
2019-10-19 21:13:48 +02:00
local playerChunk = getChunkByPosition(map, player.character.position)
2020-05-15 22:51:38 +02:00
if (playerChunk ~= -1) then
addPlayerToChunk(map, playerChunk, player.name)
2019-10-19 21:13:48 +02:00
end
end
2019-03-06 08:18:03 +02:00
end
2016-10-15 02:00:18 +02:00
end
function mapProcessor.cleanUpMapTables(map, tick)
local index = map.cleanupIndex
2018-02-12 05:21:28 +02:00
local retreats = map.chunkToRetreats
local rallys = map.chunkToRallys
2019-02-19 02:43:01 +02:00
local drained = map.chunkToDrained
2018-01-14 07:48:21 +02:00
local processQueue = map.processQueue
2020-12-05 06:49:51 +02:00
local processQueueLength = #processQueue
local endIndex = mMin(index + CLEANUP_QUEUE_SIZE, processQueueLength)
if (processQueueLength == 0) then
return
end
2020-05-21 03:03:32 +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
local retreatTick = retreats[chunk]
2020-05-20 04:37:16 +02:00
if retreatTick and ((tick - retreatTick) > COOLDOWN_RETREAT) then
2019-10-19 21:13:48 +02:00
retreats[chunk] = nil
end
2018-02-12 05:21:28 +02:00
2019-10-19 21:13:48 +02:00
local rallyTick = rallys[chunk]
2020-05-20 04:37:16 +02:00
if rallyTick and ((tick - rallyTick) > COOLDOWN_RALLY) then
2019-10-19 21:13:48 +02:00
rallys[chunk] = nil
end
2018-02-12 05:21:28 +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
end
2020-12-05 06:49:51 +02:00
if (endIndex == processQueueLength) then
map.cleanupIndex = 1
else
map.cleanupIndex = endIndex + 1
end
end
--[[
Passive scan to find entities that have been generated outside the factorio event system
--]]
2021-02-20 07:41:30 +02:00
function mapProcessor.scanPlayerMap(map, tick)
2021-02-14 06:49:54 +02:00
if (map.nextProcessMap == tick) or (map.nextPlayerScan == tick) or
(map.nextEnemyScan == tick) or (map.nextChunkProcess == tick)
then
return
end
local index = map.scanPlayerIndex
2021-02-20 09:31:36 +02:00
local area = map.universe.area
local offset = area[2]
local chunkBox = area[1]
local processQueue = map.processQueue
2020-12-05 06:49:51 +02:00
local processQueueLength = #processQueue
2021-02-14 06:49:54 +02:00
2020-12-05 06:49:51 +02:00
local endIndex = mMin(index + PLAYER_QUEUE_SIZE, processQueueLength)
if (processQueueLength == 0) then
return
end
for x=index,endIndex do
local chunk = processQueue[x]
chunkBox[1] = chunk.x
chunkBox[2] = chunk.y
offset[1] = chunk.x + CHUNK_SIZE
offset[2] = chunk.y + CHUNK_SIZE
2021-02-20 07:41:30 +02:00
mapScanPlayerChunk(chunk, map)
end
2020-12-05 06:49:51 +02:00
if (endIndex == processQueueLength) then
map.scanPlayerIndex = 1
else
map.scanPlayerIndex = endIndex + 1
end
end
2021-02-20 07:41:30 +02:00
function mapProcessor.scanEnemyMap(map, tick)
if (map.nextProcessMap == tick) or (map.nextPlayerScan == tick) or (map.nextChunkProcess == tick) then
return
end
local index = map.scanEnemyIndex
2021-02-20 09:31:36 +02:00
local area = map.universe.area
local offset = area[2]
local chunkBox = area[1]
local processQueue = map.processQueue
2020-12-05 06:49:51 +02:00
local processQueueLength = #processQueue
2021-02-14 06:49:54 +02:00
local endIndex = mMin(index + ENEMY_QUEUE_SIZE, #processQueue)
2019-02-19 02:43:01 +02:00
2020-12-05 06:49:51 +02:00
if (processQueueLength == 0) then
return
end
2021-02-14 06:49:54 +02:00
for x=index,endIndex do
local chunk = processQueue[x]
2018-02-17 05:31:29 +02:00
chunkBox[1] = chunk.x
chunkBox[2] = chunk.y
2019-02-03 08:01:28 +02:00
offset[1] = chunk.x + CHUNK_SIZE
offset[2] = chunk.y + CHUNK_SIZE
2019-02-20 08:16:43 +02:00
2021-02-20 07:41:30 +02:00
mapScanEnemyChunk(chunk, map)
end
2017-04-22 01:14:04 +02:00
2020-12-05 06:49:51 +02:00
if (endIndex == processQueueLength) then
map.scanEnemyIndex = 1
2016-08-29 02:05:28 +02:00
else
map.scanEnemyIndex = endIndex + 1
end
end
2021-02-20 07:41:30 +02:00
function mapProcessor.scanResourceMap(map, tick)
2021-02-14 06:49:54 +02:00
if (map.nextProcessMap == tick) or (map.nextPlayerScan == tick) or
(map.nextEnemyScan == tick) or (map.nextChunkProcess == tick)
then
return
end
local index = map.scanResourceIndex
2021-02-20 09:31:36 +02:00
local area = map.universe.area
local offset = area[2]
local chunkBox = area[1]
2021-02-14 06:49:54 +02:00
local processQueue = map.processQueue
2020-12-05 06:49:51 +02:00
local processQueueLength = #processQueue
2021-02-14 06:49:54 +02:00
2020-12-05 06:49:51 +02:00
local endIndex = mMin(index + RESOURCE_QUEUE_SIZE, processQueueLength)
2020-12-05 06:49:51 +02:00
if (processQueueLength == 0) then
return
end
2021-02-14 06:49:54 +02:00
for x=index,endIndex do
local chunk = processQueue[x]
chunkBox[1] = chunk.x
chunkBox[2] = chunk.y
offset[1] = chunk.x + CHUNK_SIZE
offset[2] = chunk.y + CHUNK_SIZE
2021-02-20 07:41:30 +02:00
mapScanResourceChunk(chunk, map)
end
2020-12-05 06:49:51 +02:00
if (endIndex == processQueueLength) then
map.scanResourceIndex = 1
else
map.scanResourceIndex = endIndex + 1
end
end
2021-02-20 07:41:30 +02:00
function mapProcessor.processActiveNests(map, tick)
2020-04-28 05:41:18 +02:00
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) or (getRaidNestActiveness(map, chunk) > 0) then
2021-02-20 07:41:30 +02:00
processNestActiveness(map, chunk)
2020-04-28 05:41:18 +02:00
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
2021-02-20 07:41:30 +02:00
function mapProcessor.processVengence(map)
2021-02-20 09:31:36 +02:00
local ss = map.vengenceQueue
2021-02-14 06:49:54 +02:00
local chunk = next(ss, map.deployVengenceIterator)
2020-05-20 04:37:16 +02:00
if not chunk then
map.deployVengenceIterator = nil
2021-02-14 06:49:54 +02:00
if (tableSize(ss) == 0) then
2021-02-20 09:31:36 +02:00
map.vengenceQueue = {}
end
2020-05-20 04:37:16 +02:00
else
2021-02-20 07:41:30 +02:00
formVengenceSquad(map, chunk)
2020-05-20 04:37:16 +02:00
local nextChunk
2021-02-14 06:49:54 +02:00
nextChunk = next(ss, chunk)
2020-05-20 04:37:16 +02:00
ss[chunk] = nil
chunk = nextChunk
end
map.deployVengenceIterator = chunk
end
2021-02-20 07:41:30 +02:00
function mapProcessor.processNests(map, tick)
2020-05-20 04:37:16 +02:00
local bases = map.chunkToBase
local chunks = map.chunkToNests
2021-02-14 06:49:54 +02:00
local chunk = next(chunks, map.processNestIterator)
2021-02-20 07:41:30 +02:00
if not chunk then
map.processNestIterator = nil
return
else
processNestActiveness(map, chunk)
queueNestSpawners(map, chunk, tick)
2020-05-20 04:37:16 +02:00
2021-02-20 09:31:36 +02:00
if map.universe.newEnemies then
2021-02-20 07:41:30 +02:00
local base = bases[chunk]
if base and ((tick - base.tick) > BASE_PROCESS_INTERVAL) then
2021-02-20 09:31:36 +02:00
processBase(chunk, map, tick, base)
2020-05-20 04:37:16 +02:00
end
end
2021-02-20 07:41:30 +02:00
chunk = next(chunks, chunk)
end
map.processNestIterator = chunk
end
2021-02-20 07:41:30 +02:00
local function processSpawners(map, tick, iterator, chunks)
2021-02-14 06:49:54 +02:00
local chunk = next(chunks, map[iterator])
2021-02-20 09:31:36 +02:00
local migrate = canMigrate(map)
local attack = canAttack(map, tick)
2021-02-20 07:41:30 +02:00
if not chunk then
map[iterator] = nil
return
else
if migrate then
formSettlers(map, chunk)
elseif attack then
formSquads(map, chunk, tick)
end
2021-02-20 07:41:30 +02:00
chunk = next(chunks, chunk)
2020-05-20 04:37:16 +02:00
end
map[iterator] = chunk
end
2020-04-28 05:41:18 +02:00
2021-02-20 07:41:30 +02:00
function mapProcessor.processSpawners(map, tick)
2020-04-28 05:41:18 +02:00
2021-02-20 09:31:36 +02:00
if (map.state ~= AI_STATE_PEACEFUL) then
if (map.state == AI_STATE_MIGRATING) or
((map.state == AI_STATE_SIEGE) and map.temperament <= 0.5)
2020-05-20 04:37:16 +02:00
then
2021-02-20 07:41:30 +02:00
processSpawners(map,
tick,
"processMigrationIterator",
map.chunkToNests)
2020-05-20 04:37:16 +02:00
else
2021-02-20 09:31:36 +02:00
if (map.state ~= AI_STATE_AGGRESSIVE) then
2021-02-20 07:41:30 +02:00
processSpawners(map,
tick,
"processActiveSpawnerIterator",
map.chunkToActiveNest)
2021-02-14 06:49:54 +02:00
processSpawners(map,
tick,
"processActiveRaidSpawnerIterator",
map.chunkToActiveRaidNest)
2020-05-20 04:37:16 +02:00
else
2021-02-20 07:41:30 +02:00
processSpawners(map,
tick,
"processActiveSpawnerIterator",
map.chunkToActiveNest)
2020-05-20 04:37:16 +02:00
end
end
end
end
mapProcessorG = mapProcessor
return mapProcessor