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

250 lines
7.5 KiB
Lua
Raw Normal View History

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 mathUtils = require("MathUtils")
2016-09-14 13:16:33 +02:00
-- constants
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 DOUBLE_CHUNK_SIZE = constants.DOUBLE_CHUNK_SIZE
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
2017-11-21 09:27:03 +02:00
local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK
2017-05-12 06:50:06 +02:00
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
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local BASE_PROCESS_INTERVAL = constants.BASE_PROCESS_INTERVAL
-- imported functions
2016-08-21 23:48:55 +02:00
local scents = pheromoneUtils.scents
local processPheromone = pheromoneUtils.processPheromone
2017-11-21 09:27:03 +02:00
local playerScent = pheromoneUtils.playerScent
local formSquads = aiAttackWave.formSquads
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
local analyzeChunk = chunkUtils.analyzeChunk
local getNestCount = chunkPropertyUtils.getNestCount
local getEnemyStructureCount = chunkPropertyUtils.getEnemyStructureCount
2017-05-19 09:47:24 +02:00
local canAttack = aiPredicates.canAttack
2017-05-14 00:32:16 +02:00
local euclideanDistanceNamed = mathUtils.euclideanDistanceNamed
2017-05-28 06:50:37 +02:00
2016-08-29 02:05:28 +02:00
local mMin = math.min
local mRandom = math.random
-- module code
2016-10-15 02:00:18 +02:00
local function nonRepeatingRandom(players)
local ordering = {}
for _,player in pairs(players) do
ordering[#ordering+1] = player.index
end
for i=#ordering,1,-1 do
local s = mRandom(i)
2016-10-15 02:00:18 +02:00
local t = ordering[i]
ordering[i] = ordering[s]
ordering[s] = t
end
return ordering
end
--[[
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
In theory, this might be fine as smaller bases have less surface to attack and need to have
pheromone dissipate at a faster rate.
--]]
2018-01-14 07:48:21 +02:00
function mapProcessor.processMap(map, surface, natives, tick)
local roll = map.processRoll
local index = map.processIndex
local chunkToBase = map.chunkToBase
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
2016-08-26 00:20:06 +02:00
end
2017-05-14 00:32:16 +02:00
2017-06-01 03:46:53 +02:00
local squads = canAttack(natives, surface) and (0.11 <= roll) and (roll <= 0.35) and (natives.points >= AI_SQUAD_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)
2016-08-29 02:05:28 +02:00
for x=index,endIndex do
local chunk = processQueue[x]
2017-04-22 01:14:04 +02:00
2017-06-01 03:46:53 +02:00
if (chunk[CHUNK_TICK] ~= tick) then
chunk[CHUNK_TICK] = tick
2018-01-14 07:48:21 +02:00
processPheromone(map, chunk)
2017-04-22 01:14:04 +02:00
2018-01-14 07:48:21 +02:00
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)
2017-06-01 03:46:53 +02:00
end
local base = chunkToBase[chunk]
if base and ((base.tick - tick) > BASE_PROCESS_INTERVAL) then
end
2017-06-01 03:46:53 +02:00
2018-01-14 07:48:21 +02:00
scents(map, chunk)
2017-06-01 03:46:53 +02:00
end
2016-08-29 02:05:28 +02:00
end
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
2016-10-15 02:00:18 +02:00
--[[
Localized player radius were processing takes place in realtime, doesn't store state
between calls.
vs
the slower passive version processing the entire map in multiple passes.
--]]
2018-01-14 07:48:21 +02:00
function mapProcessor.processPlayers(players, map, surface, natives, 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
local playerOrdering = nonRepeatingRandom(players)
local roll = mRandom()
2017-06-01 03:46:53 +02:00
local allowingAttacks = canAttack(natives, surface)
2016-10-15 02:00:18 +02:00
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)
2016-10-15 02:00:18 +02:00
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
2017-05-19 09:47:24 +02:00
if validPlayer(player) then
2018-01-14 07:48:21 +02:00
local playerChunk = getChunkByPosition(map, player.character.position)
2016-10-15 02:00:18 +02:00
2017-11-21 09:27:03 +02:00
if (playerChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
2016-10-15 02:00:18 +02:00
playerScent(playerChunk)
end
end
end
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
2017-05-19 09:47:24 +02:00
if validPlayer(player) then
2018-01-14 07:48:21 +02:00
local playerChunk = getChunkByPosition(map, player.character.position)
2016-10-15 02:00:18 +02:00
2017-11-21 09:27:03 +02:00
if (playerChunk ~= SENTINEL_IMPASSABLE_CHUNK) then
local vengence = (allowingAttacks and
(natives.points >= AI_VENGENCE_SQUAD_COST) and
2018-01-14 07:48:21 +02:00
((getEnemyStructureCount(map, playerChunk) > 0) or (playerChunk[MOVEMENT_PHEROMONE] < natives.retreatThreshold)))
2017-06-01 03:46:53 +02:00
2017-11-21 09:27:03 +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
2018-01-14 07:48:21 +02:00
local chunk = getChunkByXY(map, x, y)
2017-11-21 09:27:03 +02:00
if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) and (chunk[CHUNK_TICK] ~= tick) then
2016-10-15 02:00:18 +02:00
chunk[CHUNK_TICK] = tick
2017-04-22 01:14:04 +02:00
2018-01-14 07:48:21 +02:00
processPheromone(map, chunk)
2017-04-22 01:14:04 +02:00
2018-01-14 07:48:21 +02:00
if (getNestCount(map, chunk) > 0) then
2017-06-11 02:59:06 +02:00
if squads then
2018-01-14 07:48:21 +02:00
formSquads(map, surface, natives, chunk, AI_SQUAD_COST)
squads = (natives.points >= AI_SQUAD_COST) -- and (#natives.squads < natives.maxSquads)
2017-06-11 02:59:06 +02:00
end
if vengence then
2018-01-14 07:48:21 +02:00
formSquads(map, surface, natives, chunk, AI_VENGENCE_SQUAD_COST)
vengence = (natives.points >= AI_VENGENCE_SQUAD_COST) -- and (#natives.squads < natives.maxSquads)
2017-06-11 02:59:06 +02:00
end
end
2018-01-14 07:48:21 +02:00
scents(map, chunk)
2016-10-15 02:00:18 +02:00
end
end
end
end
end
end
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
--]]
2018-01-14 07:48:21 +02:00
function mapProcessor.scanMap(map, surface, natives)
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-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)
2017-06-01 03:46:53 +02:00
2016-08-29 02:05:28 +02:00
for x=index,endIndex do
2016-10-15 02:00:18 +02:00
local chunk = processQueue[x]
chunkBox[1] = chunk.x
chunkBox[2] = chunk.y
2017-05-28 06:50:37 +02:00
2017-06-01 03:46:53 +02:00
offset[1] = chunk.x + CHUNK_SIZE
offset[2] = chunk.y + CHUNK_SIZE
local unitCount = surface.count_entities_filtered(unitCountQuery)
if (unitCount > 300) then
2017-06-01 03:46:53 +02:00
local closeBy = false
local squads = natives.squads
for i=1, #squads do
local squadGroup = squads[i].group
if squadGroup.valid and (euclideanDistanceNamed(squadGroup.position, chunk) < DOUBLE_CHUNK_SIZE) then
closeBy = true
2017-06-01 03:46:53 +02:00
break
end
end
2017-06-01 03:46:53 +02:00
if not closeBy then
2017-06-11 02:59:06 +02:00
recycleBiters(natives, surface.find_enemy_units(chunk, TRIPLE_CHUNK_SIZE))
2017-04-22 01:14:04 +02:00
end
end
2018-01-14 07:48:21 +02:00
analyzeChunk(chunk, natives, surface, map)
end
2017-04-22 01:14:04 +02:00
2016-08-29 02:05:28 +02:00
if (endIndex == #processQueue) then
2018-01-14 07:48:21 +02:00
map.scanIndex = 1
2016-08-29 02:05:28 +02:00
else
2018-01-14 07:48:21 +02:00
map.scanIndex = endIndex + 1
end
end
2016-09-08 22:02:23 +02:00
return mapProcessor