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

736 lines
22 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/>.
local upgrade = {}
-- imports
local constants = require("libs/Constants")
local chunkProcessor = require("libs/ChunkProcessor")
local mapUtils = require("libs/MapUtils")
-- constants
local MINIMUM_EXPANSION_DISTANCE = constants.MINIMUM_EXPANSION_DISTANCE
local DEFINES_COMMAND_GROUP = defines.command.group
local DEFINES_COMMAND_WANDER = defines.command.wander
local DEFINES_COMMAND_BUILD_BASE = defines.command.build_base
local DEFINES_COMMAND_ATTACK_AREA = defines.command.attack_area
local DEFINES_COMMAND_GO_TO_LOCATION = defines.command.go_to_location
local DEFINES_COMMMAD_COMPOUND = defines.command.compound
local DEFINES_COMMAND_FLEE = defines.command.flee
local DEFINES_COMMAND_STOP = defines.command.stop
local DEFINES_COMPOUND_COMMAND_RETURN_LAST = defines.compound_command.return_last
local DEFINES_DISTRACTION_NONE = defines.distraction.none
local DEFINES_DISTRACTION_BY_ENEMY = defines.distraction.by_enemy
local DEFINES_DISTRACTION_BY_ANYTHING = defines.distraction.by_anything
local CHUNK_SIZE = constants.CHUNK_SIZE
local TRIPLE_CHUNK_SIZE = constants.TRIPLE_CHUNK_SIZE
local ENEMY_PHEROMONE = constants.ENEMY_PHEROMONE
local CHUNK_TICK = constants.CHUNK_TICK
local TICKS_A_MINUTE = constants.TICKS_A_MINUTE
-- imported functions
local sFind = string.find
local queueGeneratedChunk = mapUtils.queueGeneratedChunk
local processPendingChunks = chunkProcessor.processPendingChunks
-- module code
function upgrade.isExcludedSurface(surfaceName)
return
(surfaceName == "aai-signals") or
(surfaceName == "RTStasisRealm") or
(surfaceName == "minime_dummy_dungeon") or
(surfaceName == "minime-preview-character") or
(surfaceName == "pipelayer") or
(surfaceName == "beltlayer") or
sFind(surfaceName, "Factory floor") or
sFind(surfaceName, " Orbit") or
sFind(surfaceName, "clonespace") or
sFind(surfaceName, "BPL_TheLabplayer") or
sFind(surfaceName, "starmap%-") or
sFind(surfaceName, "NiceFill") or
sFind(surfaceName, "Asteroid Belt") or
sFind(surfaceName, "Vault ") or
sFind(surfaceName, "spaceship") or
sFind(surfaceName, "bpsb%-lab%-")
end
local function addCommandSet(queriesAndCommands)
-- preallocating memory to be used in code, making it fast by reducing garbage generated.
queriesAndCommands.neighbors = {
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1
}
queriesAndCommands.cardinalNeighbors = {
-1,
-1,
-1,
-1
}
queriesAndCommands.chunkOverlapArray = {
-1,
-1,
-1,
-1
}
queriesAndCommands.playerForces = {}
queriesAndCommands.enemyForces = {}
queriesAndCommands.npcForces = {}
queriesAndCommands.nonPlayerForces = {}
-- pb
queriesAndCommands.pbFilteredEntitiesPointQueryLimited = {
position = {0, 0},
radius = 10,
limit = 1,
force = queriesAndCommands.enemyForces,
type = {
"unit-spawner",
"turret"
}
}
-- msec
queriesAndCommands.msecFilteredEntitiesEnemyStructureQuery = {
area={
{0,0},
{0,0}
},
force=queriesAndCommands.enemyForces,
type={
"turret",
"unit-spawner"
}
}
-- oba
queriesAndCommands.obaCreateBuildCloudQuery = {
name = "build-clear-cloud-rampant",
position = {0,0}
}
-- sp
local spbSharedChunkArea = {
{0,0},
{0,0}
}
queriesAndCommands.spbHasPlayerStructuresQuery = {
area=spbSharedChunkArea,
force=queriesAndCommands.nonPlayerForces,
invert=true,
limit=1
}
queriesAndCommands.spbFilteredEntitiesPlayerQueryLowest = {
area=spbSharedChunkArea,
force=queriesAndCommands.playerForces,
collision_mask = "player-layer",
type={
"wall",
"transport-belt"
}
}
queriesAndCommands.spbFilteredEntitiesPlayerQueryLow = {
area=spbSharedChunkArea,
force=queriesAndCommands.playerForces,
collision_mask = "player-layer",
type={
"splitter",
"pump",
"offshore-pump",
"lamp",
"solar-panel",
"programmable-speaker",
"accumulator",
"assembling-machine",
"turret",
"ammo-turret"
}
}
queriesAndCommands.spbFilteredEntitiesPlayerQueryHigh = {
area=spbSharedChunkArea,
force=queriesAndCommands.playerForces,
collision_mask = "player-layer",
type={
"furnace",
"lab",
"roboport",
"beacon",
"radar",
"electric-turret",
"boiler",
"generator",
"fluid-turret",
"mining-drill"
}
}
queriesAndCommands.spbFilteredEntitiesPlayerQueryHighest = {
area=spbSharedChunkArea,
force=queriesAndCommands.playerForces,
collision_mask = "player-layer",
type={
"artillery-turret",
"reactor",
"rocket-silo"
}
}
-- is
local isSharedChunkArea = {
{0,0},
{0,0}
}
queriesAndCommands.isFilteredTilesQuery = {
collision_mask="water-tile",
area=isSharedChunkArea
}
queriesAndCommands.isFilteredEntitiesChunkNeutral = {
area=isSharedChunkArea,
collision_mask = "player-layer",
type={
"tree",
"simple-entity"
}
}
queriesAndCommands.isFilteredEntitiesEnemyStructureQuery = {
area=isSharedChunkArea,
force=queriesAndCommands.enemyForces,
type={
"turret",
"unit-spawner"
}
}
queriesAndCommands.isCountResourcesQuery = {
area=isSharedChunkArea,
type="resource"
}
queriesAndCommands.isFilteredEntitiesUnitQuery = {
area=isSharedChunkArea,
force=queriesAndCommands.enemyForces,
type="unit"
}
-- cps
local cpsSharedChunkArea = {
{0,0},
{0,0}
}
queriesAndCommands.cpsFilteredTilesQuery = {
collision_mask="water-tile",
area=cpsSharedChunkArea
}
queriesAndCommands.cpsFilteredEntitiesChunkNeutral = {
area=cpsSharedChunkArea,
collision_mask = "player-layer",
type={
"tree",
"simple-entity"
}
}
queriesAndCommands.cpsFilteredEnemyAnyFound = {
area=cpsSharedChunkArea,
force=queriesAndCommands.enemyForces,
type={
"turret",
"unit-spawner"
},
limit = 1
}
-- msrc
local msrcSharedChunkArea = {
{0,0},
{0,0}
}
queriesAndCommands.msrcFilteredTilesQuery = {
collision_mask="water-tile",
area=msrcSharedChunkArea
}
queriesAndCommands.msrcFilteredEntitiesChunkNeutral = {
area=msrcSharedChunkArea,
collision_mask = "player-layer",
type={
"tree",
"simple-entity"
}
}
queriesAndCommands.msrcCountResourcesQuery = {
area=msrcSharedChunkArea,
type="resource"
}
-- sp
local spSharedAreaChunk = {
{0,0},
{0,0}
}
queriesAndCommands.spFilteredEntitiesCliffQuery = {
area=spSharedAreaChunk,
type="cliff",
limit = 1
}
queriesAndCommands.spFilteredTilesPathQuery = {
area=spSharedAreaChunk,
collision_mask="water-tile",
limit = 1
}
-- ouc
queriesAndCommands.oucCliffQuery = {
area={
{0,0},
{0,0}
},
type="cliff"
}
-- ppu
queriesAndCommands.ppuUpgradeEntityQuery = {
name = "",
position = {0,0}
}
queriesAndCommands.attackCommand = {
type = DEFINES_COMMAND_ATTACK_AREA,
destination = {0,0},
radius = CHUNK_SIZE * 1.5,
distraction = DEFINES_DISTRACTION_BY_ANYTHING
}
queriesAndCommands.moveCommand = {
type = DEFINES_COMMAND_GO_TO_LOCATION,
destination = {0,0},
pathfind_flags = { cache = true },
distraction = DEFINES_DISTRACTION_BY_ENEMY
}
queriesAndCommands.settleCommand = {
type = DEFINES_COMMAND_BUILD_BASE,
destination = {0,0},
distraction = DEFINES_DISTRACTION_BY_ENEMY,
ignore_planner = true
}
queriesAndCommands.wanderCommand = {
type = DEFINES_COMMAND_WANDER,
wander_in_group = false,
radius = TRIPLE_CHUNK_SIZE*2,
ticks_to_wait = 20 * 60
}
queriesAndCommands.wander2Command = {
type = DEFINES_COMMAND_WANDER,
wander_in_group = true,
radius = TRIPLE_CHUNK_SIZE*2,
ticks_to_wait = 2 * 60
}
queriesAndCommands.stopCommand = {
type = DEFINES_COMMAND_STOP
}
queriesAndCommands.compoundSettleCommand = {
type = DEFINES_COMMMAD_COMPOUND,
structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST,
commands = {
queriesAndCommands.wonder2Command,
queriesAndCommands.settleCommand
}
}
queriesAndCommands.retreatCommand = {
type = DEFINES_COMMAND_GROUP,
group = nil,
distraction = DEFINES_DISTRACTION_BY_ANYTHING,
use_group_distraction = true
}
queriesAndCommands.fleeCommand = {
type = DEFINES_COMMAND_FLEE,
from = nil,
distraction = DEFINES_DISTRACTION_NONE
}
queriesAndCommands.compoundRetreatGroupCommand = {
type = DEFINES_COMMMAD_COMPOUND,
structure_type = DEFINES_COMPOUND_COMMAND_RETURN_LAST,
commands = {
queriesAndCommands.stopCommand,
queriesAndCommands.fleeCommand,
queriesAndCommands.retreatCommand
}
}
queriesAndCommands.formGroupCommand = {
type = DEFINES_COMMAND_GROUP,
group = nil,
distraction = DEFINES_DISTRACTION_BY_ANYTHING,
use_group_distraction = false
}
queriesAndCommands.formCommand = {
command = queriesAndCommands.formGroupCommand,
unit_count = 0,
unit_search_distance = TRIPLE_CHUNK_SIZE
}
queriesAndCommands.formRetreatCommand = {
command = queriesAndCommands.compoundRetreatGroupCommand,
unit_count = 1,
unit_search_distance = CHUNK_SIZE
}
end
function upgrade.excludeSurface(universe)
for mapId,map in pairs(universe.maps) do
local toBeRemoved = not map.surface.valid or upgrade.isExcludedSurface(map.surface.name) or universe.excludedSurfaces[map.surface.name]
if toBeRemoved then
if universe.mapIterator == mapId then
universe.mapIterator, universe.activeMap = next(universe.maps, universe.mapIterator)
end
if universe.processMapAIIterator == mapId then
universe.processMapAIIterator = nil
end
universe.maps[mapId] = nil
end
end
end
function upgrade.setCommandForces(universe, npcForces, enemyForces)
for force in pairs(universe.playerForces) do
universe.playerForces[force] = nil
end
for force in pairs(universe.npcForces) do
universe.npcForces[force] = nil
end
for force in pairs(universe.enemyForces) do
universe.enemyForces[force] = nil
end
for force in pairs(universe.nonPlayerForces) do
universe.nonPlayerForces[force] = nil
end
for _,force in pairs(game.forces) do
if not npcForces[force.name] and not enemyForces[force.name] then
universe.playerForces[#universe.playerForces+1] = force.name
end
end
for force in pairs(enemyForces) do
universe.enemyForces[#universe.enemyForces+1] = force
universe.nonPlayerForces[#universe.nonPlayerForces+1] = force
end
for force in pairs(npcForces) do
universe.npcForces[#universe.npcForces+1] = force
universe.nonPlayerForces[#universe.nonPlayerForces+1] = force
end
end
function upgrade.attempt(universe)
local starting = global.version
if not global.version or global.version < 302 then
global.version = 302
if not universe then
universe = {}
global.universe = universe
end
game.forces.enemy.kill_all_units()
universe.safeEntities = {}
universe.aiPointsScaler = settings.global["rampant--aiPointsScaler"].value
universe.aiPointsPrintGainsToChat = settings.global["rampant--aiPointsPrintGainsToChat"].value
universe.aiPointsPrintSpendingToChat = settings.global["rampant--aiPointsPrintSpendingToChat"].value
universe.aiNocturnalMode = settings.global["rampant--permanentNocturnal"].value
universe.mapIterator = nil
universe.retreatThreshold = 0
universe.rallyThreshold = 0
universe.formSquadThreshold = 0
universe.attackWaveSize = 0
universe.attackWaveDeviation = 0
universe.attackWaveUpperBound = 0
universe.unitRefundAmount = 0
universe.regroupIndex = 1
game.map_settings.path_finder.min_steps_to_check_path_find_termination =
constants.PATH_FINDER_MIN_STEPS_TO_CHECK_PATH
universe.kamikazeThreshold = 0
universe.attackWaveLowerBound = 1
universe.expansionMaxDistanceDerivation = nil
universe.settlerCooldown = 0
universe.settlerWaveDeviation = 0
universe.settlerWaveSize = 0
universe.enabledMigration = universe.expansion and settings.global["rampant--enableMigration"].value
universe.peacefulAIToggle = settings.global["rampant--peacefulAIToggle"].value
universe.printAIStateChanges = settings.global["rampant--printAIStateChanges"].value
universe.debugTemperament = settings.global["rampant--debugTemperament"].value
game.map_settings.unit_group.min_group_radius = constants.UNIT_GROUP_MAX_RADIUS * 0.5
game.map_settings.unit_group.max_group_radius = constants.UNIT_GROUP_MAX_RADIUS
game.map_settings.unit_group.max_member_speedup_when_behind = constants.UNIT_GROUP_MAX_SPEED_UP
game.map_settings.unit_group.max_member_slowdown_when_ahead = constants.UNIT_GROUP_MAX_SLOWDOWN
game.map_settings.unit_group.max_group_slowdown_factor = constants.UNIT_GROUP_SLOWDOWN_FACTOR
game.map_settings.max_failed_behavior_count = 3
game.map_settings.unit_group.member_disown_distance = 10
game.map_settings.unit_group.tick_tolerance_when_member_arrives = 60
game.forces.enemy.ai_controllable = true
universe.evolutionLevel = game.forces.enemy.evolution_factor
global.pendingChunks = nil -- removes old pendingChunks
global.natives = nil -- removes old natives
global.map = nil -- removes old map
universe.eventId = 0
universe.chunkId = 0
universe.randomGenerator = nil
game.forces.enemy.kill_all_units()
universe.maps = {}
universe.chunkIdToChunk = {}
universe.groupNumberToSquad = {}
universe.pendingUpgrades = {}
universe.chunkToVictory = {}
universe.pendingChunks = {}
universe.processActiveNest = {}
universe.processActiveNestIterator = nil
universe.deployVengenceIterator = nil
universe.pendingUpgradeIterator = nil
universe.victoryScentIterator = nil
universe.squadIterator = nil
universe.processMapAIIterator = nil
universe.processNestIterator = nil
universe.vengenceQueue = {}
universe.activeMap = nil
universe.mapIterator = nil
universe.builderCount = 0
universe.squadCount = 0
universe.chunkToNests = {}
universe.processActiveSpawnerIterator = nil
universe.processActiveRaidSpawnerIterator = nil
universe.processMigrationIterator = nil
universe.chunkToDrainedIterator = nil
universe.chunkToActiveNest = {}
universe.chunkToActiveRaidNest = {}
universe.chunkToDrained = {}
universe.chunkToRetreatIterator = nil
universe.chunkToRetreats = {}
universe.chunkToRallyIterator = nil
universe.chunkToRallys = {}
universe.chunkToPassScan = {}
universe.chunkToPassScanIterator = nil
universe.baseId = 0
universe.awake = false
universe.recycleBaseIterator = nil
universe.maxPoints = 0
universe.maxOverflowPoints = 0
addCommandSet(universe)
universe.bases = {}
for _,map in pairs(universe.maps) do
if (map.surface.valid) then
local entities = map.surface.find_entities_filtered({type="land-mine"})
for i=1,#entities do
local entity = entities[i]
if entity.valid and string.find(entity.name, "entity-proxy-") then
entity.destroy()
end
end
end
end
upgrade.excludeSurface(universe)
universe.processBaseAIIterator = nil
end
if global.version < 304 then
global.version = 304
universe.random = game.create_random_generator()
for _,map in pairs(universe.maps) do
map.random = universe.random
local processQueue = map.processQueue
for i=1,#processQueue do
local chunk = processQueue[i]
chunk[CHUNK_TICK] = chunk[ENEMY_PHEROMONE]
chunk[ENEMY_PHEROMONE] = 0
end
end
end
if global.version < 305 then
global.version = 305
universe.excludedSurfaces = {}
universe.evolutionTableAlignment = nil
universe.buildingSpaceLookup = nil
universe.enemyAlignmentLookup = nil
universe.upgradeLookup = nil
universe.buildingEvolveLookup = nil
universe.costLookup = nil
universe.buildingHiveTypeLookup = nil
universe.proxyEntityLookup = nil
universe.vanillaEntityTypeLookup = nil
universe.entitySkipCountLookup = nil
universe.evoToTierMapping = nil
end
if global.version < 306 then
global.version = 306
local minDiffuse = game.map_settings.pollution.min_to_diffuse
universe.pollutionDiffuseMinimum = minDiffuse * 0.75
universe.expansion = game.map_settings.enemy_expansion.enabled
universe.expansionMaxDistance = game.map_settings.enemy_expansion.max_expansion_distance * CHUNK_SIZE
universe.expansionMinTime = game.map_settings.enemy_expansion.min_expansion_cooldown * TICKS_A_MINUTE
universe.expansionMaxTime = game.map_settings.enemy_expansion.max_expansion_cooldown * TICKS_A_MINUTE
universe.expansionMinSize = game.map_settings.enemy_expansion.settler_group_min_size
universe.expansionMaxSize = game.map_settings.enemy_expansion.settler_group_max_size
universe.expansionLowTargetDistance = (universe.expansionMaxDistance + MINIMUM_EXPANSION_DISTANCE) * 0.33
universe.expansionMediumTargetDistance = (universe.expansionMaxDistance + MINIMUM_EXPANSION_DISTANCE) * 0.50
universe.expansionHighTargetDistance = (universe.expansionMaxDistance + MINIMUM_EXPANSION_DISTANCE) * 0.75
universe.expansionDistanceDeviation = universe.expansionMediumTargetDistance * 0.33
for _,base in pairs(universe.bases) do
base.maxExpansionGroups = 0
base.sentExpansionGroups = 0
base.resetExpensionGroupsTick = 0
end
game.print("Rampant - Version 3.2.0")
end
return (starting ~= global.version) and global.version
end
function upgrade.prepMap(universe, surface)
local surfaceName = surface.name
if upgrade.isExcludedSurface(surfaceName) then
return
end
game.print("Rampant - Indexing surface:" .. surfaceName .. ", index:" .. tostring(surface.index) .. ", please wait.")
local surfaceIndex = surface.index
if not universe.maps then
universe.maps = {}
end
local map = {}
universe.maps[surfaceIndex] = map
map.activatedMap = false
map.processedChunks = 0
map.processQueue = {}
map.processIndex = 1
map.scanPlayerIndex = 1
map.scanResourceIndex = 1
map.scanEnemyIndex = 1
map.processStaticIndex = 1
map.outgoingScanWave = true
map.outgoingStaticScanWave = true
map.chunkToBase = {}
map.chunkToTurrets = {}
map.chunkToTraps = {}
map.chunkToUtilities = {}
map.chunkToHives = {}
map.chunkToNestIds = {}
map.chunkToHiveIds = {}
map.chunkToTrapIds = {}
map.chunkToTurretIds = {}
map.chunkToUtilityIds = {}
map.drainPylons = {}
map.chunkToPlayerBase = {}
map.chunkToResource = {}
map.chunkToPlayerCount = {}
map.playerToChunk = {}
map.chunkToSquad = {}
map.chunkToPassable = {}
map.chunkToPathRating = {}
map.chunkToDeathGenerator = {}
map.chunkScanCounts = {}
map.emptySquadsOnChunk = {}
map.surface = surface
map.universe = universe
map.bases = {}
map.squads = nil
map.pendingAttack = nil
map.building = nil
map.random = universe.random
-- queue all current chunks that wont be generated during play
local tick = game.tick
for chunk in surface.get_chunks() do
if surface.is_chunk_generated(chunk) then
queueGeneratedChunk(universe,
{
surface = surface,
tick = tick,
area = {
left_top = {
x = chunk.x * 32,
y = chunk.y * 32
}
}
}
)
end
end
processPendingChunks(universe, tick, true)
end
return upgrade