mirror of
https://github.com/veden/Rampant.git
synced 2024-12-24 20:14:35 +02:00
631 lines
18 KiB
Lua
631 lines
18 KiB
Lua
-- 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/>.
|
|
|
|
|
|
if ChunkPropertyUtilsG then
|
|
return ChunkPropertyUtilsG
|
|
end
|
|
local ChunkPropertyUtils = {}
|
|
|
|
--
|
|
|
|
local Universe
|
|
local Position
|
|
|
|
--
|
|
|
|
local Constants = require("Constants")
|
|
local MathUtils = require("MathUtils")
|
|
|
|
-- Constants
|
|
|
|
local DURATION_ACTIVE_NEST = Constants.DURATION_ACTIVE_NEST
|
|
|
|
local PLAYER_GENERATOR_PERSISTANCE = Constants.PLAYER_GENERATOR_PERSISTANCE
|
|
local PLAYER_PHEROMONE_GENERATOR_AMOUNT = Constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
|
|
|
local COOLDOWN_DRAIN = Constants.COOLDOWN_DRAIN
|
|
|
|
local RAIDING_MINIMUM_BASE_THRESHOLD = Constants.RAIDING_MINIMUM_BASE_THRESHOLD
|
|
|
|
local PLAYER_PHEROMONE = Constants.PLAYER_PHEROMONE
|
|
local BASE_PHEROMONE = Constants.BASE_PHEROMONE
|
|
|
|
local MOVEMENT_GENERATOR_PERSISTANCE = Constants.MOVEMENT_GENERATOR_PERSISTANCE
|
|
local CHUNK_ALL_DIRECTIONS = Constants.CHUNK_ALL_DIRECTIONS
|
|
|
|
local MAGIC_MAXIMUM_NUMBER = Constants.MAGIC_MAXIMUM_NUMBER
|
|
|
|
-- imported functions
|
|
|
|
local activateMap
|
|
|
|
local euclideanDistancePoints = MathUtils.euclideanDistancePoints
|
|
local tableSize = table_size
|
|
|
|
local mMin = math.min
|
|
|
|
-- module code
|
|
|
|
local function getActiveTick(chunk)
|
|
return chunk.activeTick or 0
|
|
end
|
|
|
|
local function registerActiveSpawner(chunk, register, activeType, tick)
|
|
local chunkId = chunk.id
|
|
if Universe[register][chunkId] then
|
|
return
|
|
end
|
|
|
|
chunk.activeTick = tick + DURATION_ACTIVE_NEST
|
|
|
|
local base = chunk.base
|
|
base[activeType] = base[activeType] + 1
|
|
Universe[register][chunkId] = chunk
|
|
end
|
|
|
|
local function unregisterActiveSpawner(chunk, register, iterator, activeType)
|
|
local chunkId = chunk.id
|
|
if not Universe[register][chunkId] then
|
|
return
|
|
end
|
|
|
|
chunk.activeTick = nil
|
|
|
|
local base = chunk.base
|
|
base[activeType] = base[activeType] - 1
|
|
Universe[register][chunkId] = nil
|
|
if (Universe[iterator] == chunkId) then
|
|
Universe[iterator] = nil
|
|
end
|
|
end
|
|
|
|
local function registerActiveNest(chunk, tick)
|
|
registerActiveSpawner(chunk, "chunkToActiveNest", "activeNests", tick)
|
|
end
|
|
|
|
local function registerActiveRaidNest(chunk, tick)
|
|
registerActiveSpawner(chunk, "chunkToActiveRaidNest", "activeRaidNests", tick)
|
|
end
|
|
|
|
local function unregisterActiveNest(chunk)
|
|
unregisterActiveSpawner(
|
|
chunk,
|
|
"chunkToActiveNest",
|
|
"processActiveSpawnerIterator",
|
|
"activeNests"
|
|
)
|
|
end
|
|
|
|
local function unregisterActiveRaidNest(chunk)
|
|
unregisterActiveSpawner(
|
|
chunk,
|
|
"chunkToActiveRaidNest",
|
|
"processActiveRaidSpawnerIterator",
|
|
"activeRaidNests"
|
|
)
|
|
end
|
|
|
|
local function addEnemyStructure(chunk, unitNumber, ids, counts, register)
|
|
activateMap(chunk.map)
|
|
local chunkId = chunk.id
|
|
local entityIds = chunk[ids]
|
|
if not entityIds then
|
|
entityIds = {}
|
|
chunk[ids] = entityIds
|
|
end
|
|
|
|
if entityIds[unitNumber] then
|
|
return false
|
|
end
|
|
|
|
entityIds[unitNumber] = true
|
|
chunk[counts] = (chunk[counts] or 0) + 1
|
|
if register then
|
|
Universe[register][chunkId] = chunk
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function removeEnemyStructure(chunk, unitNumber, ids, counts, register)
|
|
local chunkId = chunk.id
|
|
local entityIds = chunk[ids]
|
|
if not (entityIds and entityIds[unitNumber]) then
|
|
return false
|
|
end
|
|
|
|
entityIds[unitNumber] = nil
|
|
chunk[counts] = chunk[counts] - 1
|
|
if chunk[counts] > 0 then
|
|
return true
|
|
end
|
|
|
|
chunk[ids] = nil
|
|
chunk[counts] = nil
|
|
if register then
|
|
Universe[register][chunkId] = nil
|
|
end
|
|
if (ids == "nestIds") then
|
|
unregisterActiveNest(chunk)
|
|
unregisterActiveRaidNest(chunk)
|
|
if (Universe.processMigrationIterator == chunkId) then
|
|
Universe.processMigrationIterator = nil
|
|
end
|
|
if (Universe.processNestIterator == chunkId) then
|
|
Universe.processNestIterator = nil
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function ChunkPropertyUtils.addTurretCount(chunk, unitNumber)
|
|
return addEnemyStructure(chunk, unitNumber, "turretIds", "turretCount")
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeTurretCount(chunk, unitNumber)
|
|
return removeEnemyStructure(chunk, unitNumber, "turretIds", "turretCount")
|
|
end
|
|
|
|
function ChunkPropertyUtils.addUtilitiesCount(chunk, unitNumber)
|
|
return addEnemyStructure(chunk, unitNumber, "utilityIds", "utilityCount", "chunkToUtilities")
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeUtilitiesCount(chunk, unitNumber)
|
|
return removeEnemyStructure(chunk, unitNumber, "utilityIds", "utilityCount", "chunkToUtilities")
|
|
end
|
|
|
|
function ChunkPropertyUtils.addHiveCount(chunk, unitNumber)
|
|
return addEnemyStructure(chunk, unitNumber, "hiveIds", "hiveCount", "chunkToHives")
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeHiveCount(chunk, unitNumber)
|
|
return removeEnemyStructure(chunk, unitNumber, "hiveIds", "hiveCount", "chunkToHives")
|
|
end
|
|
|
|
function ChunkPropertyUtils.addNestCount(chunk, unitNumber)
|
|
return addEnemyStructure(chunk, unitNumber, "nestIds", "nestCount", "chunkToNests")
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeNestCount(chunk, unitNumber)
|
|
return removeEnemyStructure(chunk, unitNumber, "nestIds", "nestCount", "chunkToNests")
|
|
end
|
|
|
|
function ChunkPropertyUtils.isActiveNest(chunk)
|
|
return Universe.chunkToActiveNest[chunk.id] ~= nil
|
|
end
|
|
|
|
function ChunkPropertyUtils.isActiveRaidNest(chunk)
|
|
return Universe.chunkToActiveRaidNest[chunk.id] ~= nil
|
|
end
|
|
|
|
function ChunkPropertyUtils.addBaseResourceChunk(base, chunk)
|
|
if not chunk.resourceGenerator then
|
|
return
|
|
end
|
|
|
|
base.resourceChunkCount = base.resourceChunkCount + 1
|
|
base.resourceChunks[chunk.id] = true
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeBaseResourceChunk(base, chunk)
|
|
if not base.resourceChunks[chunk.id] then
|
|
return
|
|
end
|
|
|
|
base.resourceChunkCount = base.resourceChunkCount - 1
|
|
base.resourceChunks[chunk.id] = nil
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeChunkBase(chunk, base)
|
|
if not chunk.base then
|
|
return
|
|
end
|
|
|
|
base.chunkCount = base.chunkCount - 1
|
|
base.totalX = base.totalX - chunk.x
|
|
base.totalY = base.totalY - chunk.y
|
|
base.x = base.totalX / base.chunkCount
|
|
base.y = base.totalY / base.chunkCount
|
|
chunk.base = nil
|
|
end
|
|
|
|
function ChunkPropertyUtils.setChunkBase(chunk, base)
|
|
if chunk.base then
|
|
return
|
|
end
|
|
|
|
base.chunkCount = base.chunkCount + 1
|
|
base.totalX = base.totalX + chunk.x
|
|
base.totalY = base.totalY + chunk.y
|
|
base.x = base.totalX / base.chunkCount
|
|
base.y = base.totalY / base.chunkCount
|
|
chunk.base = base
|
|
end
|
|
|
|
function ChunkPropertyUtils.getEnemyStructureCount(chunk)
|
|
return (chunk.nestCount or 0)
|
|
+ (chunk.turretCount or 0)
|
|
+ (chunk.utilityCount or 0)
|
|
+ (chunk.hiveCount or 0)
|
|
end
|
|
|
|
function ChunkPropertyUtils.setDrainPylons(map, entity1, entity2)
|
|
local pair = {entity1, entity2}
|
|
map.drainPylons[entity1.unit_number] = pair
|
|
map.drainPylons[entity2.unit_number] = pair
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeDrainPylons(map, unitNumber)
|
|
local pair = map.drainPylons[unitNumber]
|
|
if pair then
|
|
local target = pair[1]
|
|
local pole = pair[2]
|
|
if target.unit_number == unitNumber then
|
|
map.drainPylons[unitNumber] = nil
|
|
if pole.valid then
|
|
map.drainPylons[pole.unit_number] = nil
|
|
pole.destroy()
|
|
end
|
|
elseif (pole.unit_number == unitNumber) then
|
|
map.drainPylons[unitNumber] = nil
|
|
if target.valid then
|
|
map.drainPylons[target.unit_number] = nil
|
|
target.die()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.getDrainPylonPair(map, unitNumber)
|
|
return map.drainPylons[unitNumber]
|
|
end
|
|
|
|
function ChunkPropertyUtils.isDrained(chunk, tick)
|
|
local drainTick = Universe.chunkToDrained[chunk.id]
|
|
if not drainTick then
|
|
return false
|
|
end
|
|
return (tick - drainTick) < COOLDOWN_DRAIN
|
|
end
|
|
|
|
function ChunkPropertyUtils.setDrainedTick(chunk, tick)
|
|
Universe.chunkToDrained[chunk.id] = tick
|
|
end
|
|
|
|
function ChunkPropertyUtils.getRetreatTick(chunk)
|
|
return Universe.chunkToRetreats[chunk.id] or 0
|
|
end
|
|
|
|
function ChunkPropertyUtils.getRallyTick(chunk)
|
|
return Universe.chunkToRallys[chunk.id] or 0
|
|
end
|
|
|
|
function ChunkPropertyUtils.setRallyTick(chunk, tick)
|
|
Universe.chunkToRallys[chunk.id] = tick
|
|
end
|
|
|
|
function ChunkPropertyUtils.setRetreatTick(chunk, tick)
|
|
Universe.chunkToRetreats[chunk.id] = tick
|
|
end
|
|
|
|
function ChunkPropertyUtils.setResourceGenerator(chunk, resourceGenerator)
|
|
if (resourceGenerator <= 0) then
|
|
chunk.resourceGenerator = nil
|
|
else
|
|
chunk.resourceGenerator = resourceGenerator
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.getResourceGenerator(chunk)
|
|
return chunk.resourceGenerator or 0
|
|
end
|
|
|
|
function ChunkPropertyUtils.addResourceGenerator(chunk, delta)
|
|
chunk.resourceGenerator = (chunk.resourceGenerator or 0) + delta
|
|
end
|
|
|
|
function ChunkPropertyUtils.getPassable(chunk)
|
|
return chunk.passable or CHUNK_ALL_DIRECTIONS
|
|
end
|
|
|
|
function ChunkPropertyUtils.setPassable(chunk, value)
|
|
if (value == CHUNK_ALL_DIRECTIONS) then
|
|
chunk.passable = nil
|
|
else
|
|
chunk.passable = value
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.getPathRating(chunk)
|
|
return chunk.pathRating or 1
|
|
end
|
|
|
|
function ChunkPropertyUtils.setPathRating(chunk, value)
|
|
if (value == 1) then
|
|
chunk.pathRating = nil
|
|
else
|
|
chunk.pathRating = value
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.getDeathGeneratorRating(chunk)
|
|
return 1 + (chunk.deathGenerator or 0)
|
|
end
|
|
|
|
function ChunkPropertyUtils.getCombinedDeathGeneratorRating(chunk)
|
|
local amount = 1 + ((chunk.deathGenerator or 0) + (chunk.permanentDeathGenerator or 0))
|
|
if (amount > 1) then
|
|
return 1
|
|
elseif (amount <= 0) then
|
|
return 0.001
|
|
else
|
|
return amount
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.getDeathGenerator(chunk)
|
|
return chunk.deathGenerator or 0
|
|
end
|
|
|
|
function ChunkPropertyUtils.getPermanentDeathGeneratorRating(chunk)
|
|
return 1 + (chunk.permanentDeathGenerator or 0)
|
|
end
|
|
|
|
function ChunkPropertyUtils.getCombinedDeathGenerator(chunk)
|
|
local amount = (chunk.deathGenerator or 0) + (chunk.permanentDeathGenerator or 0)
|
|
if (amount > 1) then
|
|
return 1
|
|
elseif (amount < -1) then
|
|
return -1
|
|
else
|
|
return amount
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.addPermanentDeathGenerator(chunk, amount)
|
|
local adjustedAmount = (amount * 0.25) + (chunk.permanentDeathGenerator or 0)
|
|
if (adjustedAmount > 0.75) then
|
|
chunk.permanentDeathGenerator = 0.75
|
|
elseif (adjustedAmount < -0.75) then
|
|
chunk.permanentDeathGenerator = -0.75
|
|
else
|
|
chunk.permanentDeathGenerator = adjustedAmount
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.addDeathGenerator(chunk, value)
|
|
local currentAmount = (chunk.deathGenerator or 0) + value
|
|
if (currentAmount > 1) then
|
|
chunk.deathGenerator = 1
|
|
elseif (currentAmount < -1) then
|
|
chunk.deathGenerator = -1
|
|
else
|
|
chunk.deathGenerator = currentAmount
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.setDeathGenerator(chunk, value)
|
|
if (value > 1) then
|
|
chunk.deathGenerator = 1
|
|
elseif (value < -1) then
|
|
chunk.deathGenerator = -1
|
|
else
|
|
chunk.deathGenerator = value
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.addVictoryGenerator(chunk, value)
|
|
local cToV = Universe.chunkToVictory
|
|
local chunkId = chunk.id
|
|
if not cToV[chunkId] then
|
|
cToV[chunkId] = {
|
|
chunk = chunk,
|
|
v = 0
|
|
}
|
|
end
|
|
cToV[chunkId].v = cToV[chunkId].v + value
|
|
end
|
|
|
|
function ChunkPropertyUtils.decayDeathGenerator(chunk)
|
|
local gen = chunk.deathGenerator
|
|
if gen then
|
|
local v = gen * MOVEMENT_GENERATOR_PERSISTANCE
|
|
if (v < 0.0001) and (v > -0.0001) then
|
|
chunk.deathGenerator = nil
|
|
else
|
|
chunk.deathGenerator = v
|
|
end
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.decayPlayerGenerator(chunk)
|
|
local gen = chunk.playerGenerator
|
|
if gen then
|
|
local v = gen * PLAYER_GENERATOR_PERSISTANCE
|
|
if (v < 0.0001) and (v > -0.0001) then
|
|
chunk.playerGenerator = nil
|
|
else
|
|
chunk.playerGenerator = v
|
|
end
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.addPlayerGenerator(chunk, playerMaxGenerator)
|
|
local value = chunk.playerGenerator
|
|
if value then
|
|
chunk.playerGenerator = mMin(
|
|
value + PLAYER_PHEROMONE_GENERATOR_AMOUNT,
|
|
playerMaxGenerator
|
|
)
|
|
else
|
|
chunk.playerGenerator = PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.getPlayerGenerator(chunk)
|
|
return chunk.playerGenerator or 0
|
|
end
|
|
|
|
function ChunkPropertyUtils.getPlayerBaseGenerator(chunk)
|
|
return chunk.playerBaseGenerator or 0
|
|
end
|
|
|
|
function ChunkPropertyUtils.addSquadToChunk(chunk, squad)
|
|
if (chunk ~= -1)
|
|
and (
|
|
(squad.chunk == -1)
|
|
or (squad.chunk.id ~= chunk.id)
|
|
)
|
|
then
|
|
ChunkPropertyUtils.removeSquadFromChunk(squad)
|
|
local squads = chunk.squads
|
|
if not squads then
|
|
squads = {}
|
|
chunk.squads = squads
|
|
end
|
|
squads[squad.groupNumber] = squad
|
|
squad.chunk = chunk
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.removeSquadFromChunk(squad)
|
|
local chunk = squad.chunk
|
|
if (chunk ~= -1) then
|
|
local squads = chunk.squads
|
|
if squads then
|
|
squads[squad.groupNumber] = nil
|
|
if (tableSize(squads) == 0) then
|
|
chunk.squads = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.setPlayerBaseGenerator(chunk, playerBaseGenerator)
|
|
if (playerBaseGenerator <= 0) then
|
|
chunk.playerBaseGenerator = nil
|
|
return 0
|
|
else
|
|
chunk.playerBaseGenerator = playerBaseGenerator
|
|
return playerBaseGenerator
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.addPlayerBaseGenerator(chunk, playerBaseGenerator)
|
|
local amount = (chunk.playerBaseGenerator or 0) + playerBaseGenerator
|
|
if amount <= 0 then
|
|
chunk.playerBaseGenerator = nil
|
|
return 0
|
|
else
|
|
chunk.playerBaseGenerator = amount
|
|
return amount
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.findNearbyBase(chunk)
|
|
local foundBase = chunk.base
|
|
if foundBase then
|
|
return foundBase
|
|
end
|
|
|
|
return ChunkPropertyUtils.findNearbyBaseByPosition(
|
|
chunk.map,
|
|
chunk.x,
|
|
chunk.y
|
|
)
|
|
end
|
|
|
|
function ChunkPropertyUtils.findNearbyBaseByPosition(map, x, y)
|
|
local foundBase
|
|
|
|
local closest = MAGIC_MAXIMUM_NUMBER
|
|
for _, base in pairs(map.bases) do
|
|
local distance = euclideanDistancePoints(base.x, base.y, x, y)
|
|
if (distance <= base.distanceThreshold) and (distance < closest) then
|
|
closest = distance
|
|
foundBase = base
|
|
end
|
|
end
|
|
|
|
return foundBase
|
|
end
|
|
|
|
function ChunkPropertyUtils.processNestActiveness(chunk, tick)
|
|
if getActiveTick(chunk) > tick then
|
|
return
|
|
end
|
|
|
|
if chunk.nestCount then
|
|
local surface = chunk.map.surface
|
|
if Universe.attackUsePlayer and (chunk[PLAYER_PHEROMONE] > Universe.attackPlayerThreshold) then
|
|
registerActiveNest(chunk, tick)
|
|
elseif (chunk[BASE_PHEROMONE] > 0) then
|
|
local getPollution = surface.get_pollution
|
|
if (getPollution(chunk) > 0) then
|
|
registerActiveNest(chunk, tick)
|
|
else
|
|
local x = chunk.x
|
|
local y = chunk.y
|
|
local pollutionThreshold = Universe.pollutionDiffuseMinimum
|
|
Position.x = x + 32
|
|
Position.y = y
|
|
if (getPollution(Position) > pollutionThreshold) then
|
|
registerActiveNest(chunk, tick)
|
|
else
|
|
Position.x = x - 32
|
|
if (getPollution(Position) > pollutionThreshold) then
|
|
registerActiveNest(chunk, tick)
|
|
else
|
|
Position.x = x
|
|
Position.y = y - 32
|
|
if (getPollution(Position) > pollutionThreshold) then
|
|
registerActiveNest(chunk, tick)
|
|
else
|
|
Position.y = y + 32
|
|
if (getPollution(Position) > pollutionThreshold) then
|
|
registerActiveNest(chunk, tick)
|
|
else
|
|
unregisterActiveNest(chunk)
|
|
if (chunk[BASE_PHEROMONE] > RAIDING_MINIMUM_BASE_THRESHOLD) then
|
|
registerActiveRaidNest(chunk, tick)
|
|
else
|
|
unregisterActiveRaidNest(chunk)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
unregisterActiveNest(chunk)
|
|
unregisterActiveRaidNest(chunk)
|
|
end
|
|
else
|
|
unregisterActiveNest(chunk)
|
|
unregisterActiveRaidNest(chunk)
|
|
end
|
|
end
|
|
|
|
function ChunkPropertyUtils.init(universe, mapUtils)
|
|
Universe = universe
|
|
activateMap = mapUtils.activateMap
|
|
if universe.chunkPropertyUtilsQueries then
|
|
Position = universe.chunkPropertyUtilsQueries.position
|
|
end
|
|
end
|
|
|
|
ChunkPropertyUtilsG = ChunkPropertyUtils
|
|
return ChunkPropertyUtils
|