1
0
mirror of https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git synced 2024-12-14 10:23:17 +02:00
FactorioScenarioMultiplayer.../regrowth_map.lua

348 lines
13 KiB
Lua

-- regrowth_map.lua
-- July 2017
--
-- Code tracks all chunks generated and allows for deleting inactive chunks
-- Relies on some changes to RSO to provide random resource locations the next
-- time the land is regenerated.
--
-- Basic rules of regrowth:
-- 1. Area around player is safe for quite a large distance.
-- 2. Rocket silo won't be deleted. - PERMANENT
-- 3. Chunks with pollution won't be deleted.
-- 4. Chunks with railways won't be deleted.
-- 5. Anything within radar range won't be deleted, but radar MUST be active.
-- -- This works by refreshing all chunk timers within radar range using
-- the on_sector_scanned event.
-- 6. Chunks timeout after 1 hour-ish, configurable
-- 7. For now, oarc spawns are deletion safe as well, but only immediate area.
-- Generic Utility Includes
require("locale/oarc_utils")
-- Default timeout of generated chunks
REGROWTH_TIMEOUT_TICKS = TICKS_PER_HOUR
-- We can't delete chunks regularly without causing lag.
-- So we should save them up to delete them.
REGROWTH_CLEANING_INTERVAL_TICKS = REGROWTH_TIMEOUT_TICKS
-- Not used right now.
-- It takes a radar 7 hours and 20 minutes to scan it's whole area completely
-- So I will bump the refresh time of blocks up by 8 hours
-- RADAR_COMPLETE_SCAN_TICKS = TICKS_PER_HOUR*8
-- Additional bonus time for certain things:
-- REFRESH_BONUS_RADAR = RADAR_COMPLETE_SCAN_TICKS
-- Init globals and set player join area to be off limits.
function OarcRegrowthInit()
global.chunk_regrow = {}
global.chunk_regrow.map = {}
global.chunk_regrow.removal_list = {}
global.chunk_regrow.rso_region_roll_counter = 0
global.chunk_regrow.player_refresh_index = 1
global.chunk_regrow.min_x = 0
global.chunk_regrow.max_x = 0
global.chunk_regrow.x_index = 0
global.chunk_regrow.min_y = 0
global.chunk_regrow.max_y = 0
global.chunk_regrow.y_index = 0
global.chunk_regrow.force_removal_flag = -1000
OarcRegrowthOffLimits({x=0,y=0}, 10)
end
function GetChunkTopLeft(pos)
return {x=pos.x-(pos.x % 32), y=pos.y-(pos.y % 32)}
end
function GetChunkCoordsFromPos(pos)
return {x=math.floor(pos.x/32), y=math.floor(pos.y/32)}
end
-- This complicated function checks that if a chunk
function CheckChunkEmpty(pos)
chunkPos = GetChunkCoordsFromPos(pos)
search_top_left = {x=chunkPos.x*32, y=chunkPos.y*32}
search_area = {search_top_left, {x=search_top_left.x+32,y=search_top_left.y+32}}
total = 0
for f,_ in pairs(game.forces) do
if f ~= "neutral" and f ~= "enemy" then
entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area = search_area, force=f}
total = total + #entities
if (#entities > 0) then
for _,e in pairs(entities) do
if ((e.type == "player") or
(e.type == "car") or
(e.type == "logistic-robot") or
(e.type == "construction-robot")) then
total = total - 1
end
end
end
end
end
-- A destroyed entity is still found during the event check.
return (total == 1)
end
-- game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area = {game.player.position, {game.player.position.x+32, game.player.position-`32}}, type= "resource"}
-- If an entity is mined or destroyed, then check if the chunk
-- is empty. If it's empty, reset the refresh timer.
function OarcRegrowthCheckChunkEmpty(event)
if ((event.entity.force ~= nil) and (event.entity.force ~= "neutral") and (event.entity.force ~= "enemy")) then
if CheckChunkEmpty(event.entity.position) then
DebugPrint("Resetting chunk timer."..event.entity.position.x.." "..event.entity.position.y)
OarcRegrowthForceRefreshChunk(event.entity.position, 0)
end
end
end
-- Adds new chunks to the global table to track them.
-- This should always be called first in the chunk generate sequence
-- (Compared to other RSO & Oarc related functions...)
function OarcRegrowthChunkGenerate(pos)
c_pos = GetChunkCoordsFromPos(pos)
-- If this is the first chunk in that row:
if (global.chunk_regrow.map[c_pos.x] == nil) then
global.chunk_regrow.map[c_pos.x] = {}
end
-- Confirm the chunk doesn't already have a value set:
if (global.chunk_regrow.map[c_pos.x][c_pos.y] == nil) then
global.chunk_regrow.map[c_pos.x][c_pos.y] = game.tick
end
-- Store min/max values for x/y dimensions:
if (c_pos.x < global.chunk_regrow.min_x) then
global.chunk_regrow.min_x = c_pos.x
end
if (c_pos.x > global.chunk_regrow.max_x) then
global.chunk_regrow.max_x = c_pos.x
end
if (c_pos.y < global.chunk_regrow.min_y) then
global.chunk_regrow.min_y = c_pos.y
end
if (c_pos.y > global.chunk_regrow.max_y) then
global.chunk_regrow.max_y = c_pos.y
end
end
-- Mark an area for immediate forced removal
function OarcRegrowthMarkForRemoval(pos, chunk_radius)
local c_pos = GetChunkCoordsFromPos(pos)
for i=-chunk_radius,chunk_radius do
for k=-chunk_radius,chunk_radius do
local x = c_pos.x+i
local y = c_pos.y+k
if (global.chunk_regrow.map[x] == nil) then
global.chunk_regrow.map[x] = {}
end
global.chunk_regrow.map[x][y] = nil
table.insert(global.chunk_regrow.removal_list, {x=x,y=y})
end
end
end
-- Marks a chunk a position that won't ever be deleted.
function OarcRegrowthOffLimitsChunk(pos)
local c_pos = GetChunkCoordsFromPos(pos)
if (global.chunk_regrow.map[c_pos.x] == nil) then
global.chunk_regrow.map[c_pos.y] = {}
end
global.chunk_regrow.map[c_pos.x][c_pos.y] = -1
end
-- Marks a safe area around a position that won't ever be deleted.
function OarcRegrowthOffLimits(pos, chunk_radius)
local c_pos = GetChunkCoordsFromPos(pos)
for i=-chunk_radius,chunk_radius do
for k=-chunk_radius,chunk_radius do
local x = c_pos.x+i
local y = c_pos.y+k
if (global.chunk_regrow.map[x] == nil) then
global.chunk_regrow.map[x] = {}
end
global.chunk_regrow.map[x][y] = -1
end
end
end
-- Refreshes timers on a chunk containing position
function OarcRegrowthRefreshChunk(pos, bonus_time)
local c_pos = GetChunkCoordsFromPos(pos)
if (global.chunk_regrow.map[c_pos.x] == nil) then
global.chunk_regrow.map[c_pos.y] = {}
end
if (global.chunk_regrow.map[c_pos.x][c_pos.y] ~= -1) then
global.chunk_regrow.map[c_pos.x][c_pos.y] = game.tick + bonus_time
end
end
-- Forcefully refreshes timers on a chunk containing position
-- Will overwrite -1 flag.
function OarcRegrowthForceRefreshChunk(pos, bonus_time)
local c_pos = GetChunkCoordsFromPos(pos)
if (global.chunk_regrow.map[c_pos.x] == nil) then
global.chunk_regrow.map[c_pos.y] = {}
end
global.chunk_regrow.map[c_pos.x][c_pos.y] = game.tick + bonus_time
end
-- Refreshes timers on all chunks around a certain area
function OarcRegrowthRefreshArea(pos, chunk_radius, bonus_time)
local c_pos = GetChunkCoordsFromPos(pos)
for i=-chunk_radius,chunk_radius do
for k=-chunk_radius,chunk_radius do
local x = c_pos.x+i
local y = c_pos.y+k
if (global.chunk_regrow.map[x] == nil) then
global.chunk_regrow.map[x] = {}
end
if (global.chunk_regrow.map[x][y] ~= -1) then
global.chunk_regrow.map[x][y] = game.tick + bonus_time
end
end
end
end
-- Refreshes timers on all chunks near an ACTIVE radar
function OarcRegrowthSectorScan(event)
OarcRegrowthRefreshArea(event.radar.position, 14, 0)
OarcRegrowthRefreshChunk(event.chunk_position, 0)
end
-- Refresh all chunks near a single player. Cyles through all connected players.
function OarcRegrowthRefreshPlayerArea()
global.chunk_regrow.player_refresh_index = global.chunk_regrow.player_refresh_index + 1
if (global.chunk_regrow.player_refresh_index > #game.connected_players) then
global.chunk_regrow.player_refresh_index = 1
end
if (game.connected_players[global.chunk_regrow.player_refresh_index]) then
OarcRegrowthRefreshArea(game.connected_players[global.chunk_regrow.player_refresh_index].position, 4, 0)
end
end
-- Check each chunk in the 2d array for a timeout value
function OarcRegrowthCheckArray()
-- Increment X
if (global.chunk_regrow.x_index > global.chunk_regrow.max_x) then
global.chunk_regrow.x_index = global.chunk_regrow.min_x
-- Increment Y
if (global.chunk_regrow.y_index > global.chunk_regrow.max_y) then
global.chunk_regrow.y_index = global.chunk_regrow.min_y
DebugPrint("Finished checking regrowth array."..global.chunk_regrow.min_x.." "..global.chunk_regrow.max_x.." "..global.chunk_regrow.min_y.." "..global.chunk_regrow.max_y)
else
global.chunk_regrow.y_index = global.chunk_regrow.y_index + 1
end
else
global.chunk_regrow.x_index = global.chunk_regrow.x_index + 1
end
-- Check row exists, otherwise make one.
if (global.chunk_regrow.map[global.chunk_regrow.x_index] == nil) then
global.chunk_regrow.map[global.chunk_regrow.x_index] = {}
end
-- If the chunk has timed out, add it to the removal list
local c_timer = global.chunk_regrow.map[global.chunk_regrow.x_index][global.chunk_regrow.y_index]
if ((c_timer ~= nil) and (c_timer ~= -1) and ((c_timer+REGROWTH_TIMEOUT_TICKS) < game.tick)) then
-- Check chunk actually exists
if (game.surfaces[GAME_SURFACE_NAME].is_chunk_generated({x=(global.chunk_regrow.x_index),
y=(global.chunk_regrow.y_index)})) then
table.insert(global.chunk_regrow.removal_list, {x=global.chunk_regrow.x_index,
y=global.chunk_regrow.y_index})
global.chunk_regrow.map[global.chunk_regrow.x_index][global.chunk_regrow.y_index] = nil
end
end
end
-- Remove all chunks at same time to reduce impact to FPS/UPS
function OarcRegrowthRemoveAllChunks()
while (#global.chunk_regrow.removal_list > 0) do
local c_pos = table.remove(global.chunk_regrow.removal_list)
local c_timer = global.chunk_regrow.map[c_pos.x][c_pos.y]
-- Confirm chunk is still expired
if (c_timer == nil) then
-- Check for pollution
if (game.surfaces[GAME_SURFACE_NAME].get_pollution({c_pos.x*32,c_pos.y*32}) > 0) then
global.chunk_regrow.map[c_pos.x][c_pos.y] = game.tick
-- Else delete the chunk
else
game.surfaces[GAME_SURFACE_NAME].delete_chunk(c_pos)
global.chunk_regrow.map[c_pos.x][c_pos.y] = nil
end
else
-- DebugPrint("Chunk no longer expired?")
end
end
end
-- This is the main work function, it checks a single chunk in the list
-- per tick. It works according to the rules listed in the header of this
-- file.
function OarcRegrowthOnTick()
-- Every half a second, refresh all chunks near a single player
-- Cyles through all players. Tick is offset by 2
if ((game.tick % (30)) == 2) then
OarcRegrowthRefreshPlayerArea()
end
-- Every tick, check a few points in the 2d array
-- According to /measured-command this shouldn't take more
-- than 0.1ms on average
for i=1,20 do
OarcRegrowthCheckArray()
end
-- Send a broadcast warning before it happens.
if ((game.tick % REGROWTH_CLEANING_INTERVAL_TICKS) == REGROWTH_CLEANING_INTERVAL_TICKS-601) then
if (#global.chunk_regrow.removal_list > 100) then
SendBroadcastMsg("Map cleanup in 10 seconds...")
end
end
-- Delete all listed chunks
if ((game.tick % REGROWTH_CLEANING_INTERVAL_TICKS) == REGROWTH_CLEANING_INTERVAL_TICKS-1) then
if (#global.chunk_regrow.removal_list > 100) then
OarcRegrowthRemoveAllChunks()
SendBroadcastMsg("Map cleanup done...")
end
end
end
-- This function removes any chunks flagged but on demand.
-- Controlled by the global.chunk_regrow.force_removal_flag
-- This function may be used outside of the normal regrowth modse.
function OarcRegrowthForceRemovalOnTick()
-- Catch force remove flag
if (game.tick == global.chunk_regrow.force_removal_flag+60) then
SendBroadcastMsg("Map cleanup in 10 seconds...")
end
if (game.tick == global.chunk_regrow.force_removal_flag+660) then
OarcRegrowthRemoveAllChunks()
SendBroadcastMsg("Map cleanup done...")
end
end