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

559 lines
19 KiB
Lua
Raw Normal View History

2022-01-15 00:08:58 +02:00
-- Copyright (C) 2022 veden
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
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 = 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
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
local BASE_AI_STATE_AGGRESSIVE = constants.BASE_AI_STATE_AGGRESSIVE
local BASE_AI_STATE_SIEGE = constants.BASE_AI_STATE_SIEGE
local BASE_AI_STATE_PEACEFUL = constants.BASE_AI_STATE_PEACEFUL
local BASE_AI_STATE_MIGRATING = constants.BASE_AI_STATE_MIGRATING
2016-11-04 09:26:19 +02:00
local COOLDOWN_DRAIN = constants.COOLDOWN_DRAIN
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
-- imported functions
local findNearbyBase = chunkPropertyUtils.findNearbyBase
local removeChunkToNest = mapUtils.removeChunkToNest
local processStaticPheromone = pheromoneUtils.processStaticPheromone
2016-08-21 23:48:55 +02:00
local processPheromone = pheromoneUtils.processPheromone
local getCombinedDeathGeneratorRating = chunkPropertyUtils.getCombinedDeathGeneratorRating
local processBaseMutation = baseUtils.processBaseMutation
2020-05-20 04:37:16 +02:00
local processNestActiveness = chunkPropertyUtils.processNestActiveness
local getChunkBase = chunkPropertyUtils.getChunkBase
2020-05-20 04:37:16 +02:00
local formSquads = aiAttackWave.formSquads
2019-02-20 08:16:43 +02:00
local formVengenceSquad = aiAttackWave.formVengenceSquad
local formVengenceSettler = aiAttackWave.formVengenceSettler
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
local getChunkById = mapUtils.getChunkById
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
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
-- 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)
local outgoingWave = map.outgoingScanWave
2018-01-14 07:48:21 +02:00
local processQueue = map.processQueue
local processQueueLength = #processQueue
local index = mMin(map.processIndex, processQueueLength)
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]
if 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
map.universe.processedChunks = map.universe.processedChunks + PROCESS_QUEUE_SIZE
2016-08-29 02:05:28 +02:00
end
2021-02-14 06:49:54 +02:00
function mapProcessor.processStaticMap(map)
local outgoingWave = map.outgoingStaticScanWave
local processQueue = map.processQueue
local processQueueLength = #processQueue
local index = mMin(map.processStaticIndex, processQueueLength)
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
2021-12-11 20:44:55 +02:00
processStaticPheromone(map, processQueue[x])
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)
local processActiveNest = map.universe.processActiveNest
2020-05-20 04:37:16 +02:00
local chunkId = chunk.id
if not processActiveNest[chunkId] then
if (getNestActiveness(map, chunk) > 0) or (getRaidNestActiveness(map, chunk) > 0) then
processActiveNest[chunkId] = {
map = map,
chunk = chunk,
tick = tick + DURATION_ACTIVE_NEST
}
end
2020-04-28 05:41:18 +02:00
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-12-06 03:16:14 +02:00
function mapProcessor.processPlayers(players, universe, 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
2018-05-26 00:23:22 +02:00
-- not looping everyone because the cost is high enough already in multiplayer
for i=1,#players do
local player = players[i]
if validPlayer(player) then
local char = player.character
local map = universe.maps[char.surface.index]
if map then
local playerChunk = getChunkByPosition(map, char.position)
if (playerChunk ~= -1) then
addPlayerToChunk(map, playerChunk, player.name)
end
end
end
end
2019-12-07 07:57:20 +02:00
if (#players > 0) then
2021-12-06 03:16:14 +02:00
local player = players[universe.random(#players)]
2021-02-20 09:31:36 +02:00
if validPlayer(player) then
2021-12-06 03:16:14 +02:00
local char = player.character
local map = universe.maps[char.surface.index]
if map then
local playerChunk = getChunkByPosition(map, char.position)
if (playerChunk ~= -1) then
local base = findNearbyBase(map, playerChunk)
if not base then
return
end
local allowingAttacks = canAttack(map, base)
2021-12-06 03:16:14 +02:00
local vengence = allowingAttacks and
(base.unitPoints >= AI_VENGENCE_SQUAD_COST) and
2021-12-06 03:16:14 +02:00
((getEnemyStructureCount(map, playerChunk) > 0) or
(getCombinedDeathGeneratorRating(map, playerChunk) < universe.retreatThreshold))
2021-12-06 03:16:14 +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)
if (chunk ~= -1) and (chunk[CHUNK_TICK] ~= tick) then
chunk[CHUNK_TICK] = tick
processPheromone(map, chunk, true)
if (getNestCount(map, chunk) > 0) then
processNestActiveness(map, chunk)
queueNestSpawners(map, chunk, tick)
if vengence then
2021-12-06 05:40:39 +02:00
local pack = universe.vengenceQueue[chunk.id]
if not pack then
pack = {
v = 0,
map = map,
base = base
2021-12-06 05:40:39 +02:00
}
universe.vengenceQueue[chunk.id] = pack
end
pack.v = pack.v + 1
2021-12-06 03:16:14 +02:00
end
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
end
local function processCleanUp(universe, chunks, iterator, tick, duration)
local chunkId = universe[iterator]
local chunkPack
if not chunkId then
chunkId, chunkPack = next(chunks, nil)
else
chunkPack = chunks[chunkId]
end
if not chunkId then
universe[iterator] = nil
else
universe[iterator] = next(chunks, chunkId)
if (tick - chunkPack.tick) > duration then
chunks[chunkId] = nil
end
end
end
function mapProcessor.cleanUpMapTables(universe, tick)
local retreats = universe.chunkToRetreats
local rallys = universe.chunkToRallys
local drained = universe.chunkToDrained
for _=1,CLEANUP_QUEUE_SIZE do
processCleanUp(universe, retreats, "chunkToRetreatIterator", tick, COOLDOWN_RETREAT)
2018-02-12 05:21:28 +02:00
processCleanUp(universe, rallys, "chunkToRallyIterator", tick, COOLDOWN_RALLY)
2018-02-12 05:21:28 +02:00
processCleanUp(universe, drained, "chunkToDrainedIterator", tick, COOLDOWN_DRAIN)
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
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
mapScanPlayerChunk(processQueue[x], 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
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
mapScanEnemyChunk(processQueue[x], map, tick)
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-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
mapScanResourceChunk(processQueue[x], map)
end
2020-12-05 06:49:51 +02:00
if (endIndex == processQueueLength) then
map.scanResourceIndex = 1
else
map.scanResourceIndex = endIndex + 1
end
end
function mapProcessor.processActiveNests(universe, tick)
local processActiveNest = universe.processActiveNest
local chunkId = universe.processActiveNestIterator
local chunkPack
if not chunkId then
chunkId, chunkPack = next(processActiveNest, nil)
else
chunkPack = processActiveNest[chunkId]
end
if not chunkId then
universe.processActiveNestIterator = nil
else
universe.processActiveNestIterator = next(processActiveNest, chunkId)
if chunkPack.tick < tick then
local map = chunkPack.map
if not map.surface.valid then
processActiveNest[chunkId] = nil
return
end
local chunk = chunkPack.chunk
processNestActiveness(map, chunk)
if (getNestActiveness(map, chunk) == 0) and (getRaidNestActiveness(map, chunk) == 0) then
processActiveNest[chunkId] = nil
else
chunkPack.tick = tick + DURATION_ACTIVE_NEST
2020-04-28 05:41:18 +02:00
end
end
end
end
2021-12-06 05:40:39 +02:00
function mapProcessor.processVengence(universe)
local vengenceQueue = universe.vengenceQueue
local chunkId = universe.deployVengenceIterator
local vengencePack
if not chunkId then
2021-12-06 05:40:39 +02:00
chunkId, vengencePack = next(vengenceQueue, nil)
else
vengencePack = vengenceQueue[chunkId]
2021-11-25 04:31:28 +02:00
end
if not chunkId then
2021-12-06 05:40:39 +02:00
universe.deployVengenceIterator = nil
if (tableSize(vengenceQueue) == 0) then
universe.vengenceQueue = {}
end
2020-05-20 04:37:16 +02:00
else
2021-12-06 05:40:39 +02:00
universe.deployVengenceIterator = next(vengenceQueue, chunkId)
vengenceQueue[chunkId] = nil
local map = vengencePack.map
if not map.surface.valid then
return
end
local chunk = getChunkById(map, chunkId)
local base = vengencePack.base
if canMigrate(map, base) and (universe.random() < 0.075) then
formVengenceSettler(map, chunk, base)
else
formVengenceSquad(map, chunk, base)
end
2020-05-20 04:37:16 +02:00
end
end
function mapProcessor.processNests(universe, tick)
local chunkId = universe.processNestIterator
local chunkPack
if not chunkId then
chunkId,chunkPack = next(universe.chunkToNests, nil)
else
chunkPack = universe.chunkToNests[chunkId]
end
if not chunkId then
universe.processNestIterator = nil
else
universe.processNestIterator = next(universe.chunkToNests, chunkId)
local map = chunkPack.map
if not map.surface.valid then
removeChunkToNest(universe, chunkId)
return
end
local chunk = getChunkById(map, chunkId)
2021-02-20 07:41:30 +02:00
processNestActiveness(map, chunk)
queueNestSpawners(map, chunk, tick)
2020-05-20 04:37:16 +02:00
if universe.NEW_ENEMIES then
processBaseMutation(chunk,
map,
getChunkBase(map, chunk))
end
end
end
local function processSpawnersBody(universe, iterator, chunks)
local chunkId = universe[iterator]
local chunkPack
if not chunkId then
chunkId,chunkPack = next(chunks, nil)
else
chunkPack = chunks[chunkId]
end
if not chunkId then
universe[iterator] = nil
else
universe[iterator] = next(chunks, chunkId)
local map = chunkPack.map
if not map.surface.valid then
if (iterator == "processMigrationIterator") then
removeChunkToNest(universe, chunkId)
else
chunks[chunkId] = nil
end
return
end
local chunk = getChunkById(map, chunkId)
local base = findNearbyBase(map, chunk)
if base.stateAI == BASE_AI_STATE_PEACEFUL then
return
end
if iterator == "processMigrationIterator" then
if (base.stateAI ~= BASE_AI_STATE_MIGRATING) and (base.stateAI ~= BASE_AI_STATE_SIEGE) then
return
end
elseif iterator == "processActiveRaidSpawnerIterator" then
if (base.stateAI == BASE_AI_STATE_AGGRESSIVE) or (base.stateAI == BASE_AI_STATE_MIGRATING) then
return
end
elseif iterator == "processActiveSpawnerIterator" then
if (base.stateAI == BASE_AI_STATE_MIGRATING) then
return
end
end
local migrate = canMigrate(map, base)
local attack = canAttack(map, base)
2021-02-20 07:41:30 +02:00
if migrate then
formSettlers(map, chunk, base)
2021-12-10 21:12:54 +02:00
end
if attack then
formSquads(map, chunk, base)
end
2020-05-20 04:37:16 +02:00
end
end
2020-04-28 05:41:18 +02:00
function mapProcessor.processAttackWaves(universe)
processSpawnersBody(universe,
"processActiveSpawnerIterator",
universe.chunkToActiveNest)
processSpawnersBody(universe,
"processActiveRaidSpawnerIterator",
universe.chunkToActiveRaidNest)
processSpawnersBody(universe,
"processMigrationIterator",
universe.chunkToNests)
2020-05-20 04:37:16 +02:00
end
mapProcessorG = mapProcessor
return mapProcessor