world_regrowth_pp/src/scripts/components/natural_regrowth.lua

282 lines
9.9 KiB
Lua

--------------------------------------------------------------------------
--[[ NaturalRegrowth class definition ]]
-- A modified version of the original desolationspawner.lua
-- It acts as a standalone regrowth manager and is independent of the 3 existing ones
-- It's unlikely affected by game updates as long as Klei doesn't change the API (they shouldn't)
-- by lolo Jan. 2018
--------------------------------------------------------------------------
return Class(function(self, inst)
assert(inst.ismastersim, "natural_regrowth should not exist on client")
require "map/terrain"
require "wrpp_util"
--------------------------------------------------------------------------
--[[ Constants ]]
--------------------------------------------------------------------------
local DEBUG = false
local DEBUG_TELE = false
local UPDATE_PERIOD = 11
local THREADS_PER_BATCH = 3
--------------------------------------------------------------------------
--[[ Member variables ]]
--------------------------------------------------------------------------
--Public
self.inst = inst
--Private
local regrowth_table = {}
local area_data = {}
local intervals = {}
local regrowth_table_populated_by_mod = false
--------------------------------------------------------------------------
--[[ Private member functions ]]
--------------------------------------------------------------------------
local function TryRegrowth(x, y, z , prefab, product)
local status = TestRegrowth(x,0,z, product, nil)
if status == REGROW_STATUS.SUCCESS then
local instance = SpawnPrefab(product)
if instance ~= nil then
instance.Transform:SetPosition(x,0,z)
end
if DEBUG then
print("[NaturalRegrowth] Spawned a product " .. product .. " at " .. GetCoordStr(x,0,z) .. " for prefab " .. prefab)
end
if DEBUG_TELE then
c_teleport(x,0,z)
end
return true
else
if DEBUG then
print("[NaturalRegrowth] Failed to spawn a product " .. product .. " at " .. GetCoordStr(x,0,z) .. " for prefab " .. prefab .. " due to " .. GetRStatusStr(status))
end
return false
end
end
local function PrintDensities()
for area, densities in pairs(inst.generated.densities) do
for k,v in pairs(densities) do
print(area, k, v)
end
end
end
local function PopulateAreaData(prefab)
if inst.generated == nil then
-- Still starting up
return
end
if area_data[prefab] ~= nil then
if DEBUG then
print("[NaturalRegrowth] Already populated prefab " .. prefab)
end
return
end
-- PrintDensities()
for area, densities in pairs(inst.generated.densities) do
if densities[prefab] ~= nil then
for id, v in ipairs(inst.topology.ids) do
if v == area then
if area_data[prefab] == nil then
area_data[prefab] = {}
end
area_data[prefab][#area_data[prefab] + 1] = id
break
end
end
end
end
if DEBUG then
print("[NaturalRegrowth] Populated " .. (area_data[prefab] == nil and 0 or #area_data[prefab]) .. " areas for prefab " .. prefab)
end
end
local function PopulateAllAreaData()
-- This has to be run after 1 frame from startup
for prefab in pairs(regrowth_table) do
PopulateAreaData(prefab)
end
end
--------------------------------------------------------------------------
--[[ Public member functions ]]
--------------------------------------------------------------------------
function self:GetUpdatePeriod()
return UPDATE_PERIOD
end
function self:FinishModConfig()
regrowth_table_populated_by_mod = true
end
function self:RegisterRegrowth(prefab, product, interval)
if interval == nil then
if DEBUG then
print("[NaturalRegrowth] WARNING: interval for prefab " .. prefab .. " is null. Using default.")
end
interval = 480
end
if DEBUG then
print("[NaturalRegrowth] Registered product " .. product .. " for prefab " .. prefab .. " with interval " .. interval)
end
regrowth_table[prefab] = {product = product, interval = interval}
if intervals[prefab] == nil then
intervals[prefab] = interval
end
PopulateAreaData(prefab)
end
--------------------------------------------------------------------------
--[[ Initialization ]]
--------------------------------------------------------------------------
inst:DoPeriodicTask(UPDATE_PERIOD, function() self:LongUpdate(UPDATE_PERIOD) end)
inst:DoTaskInTime(0, PopulateAllAreaData)
--------------------------------------------------------------------------
--[[ Update ]]
--------------------------------------------------------------------------
local function RegrowPrefabTask(areas, prefab)
local success = false
local rand = math.random(1, #areas)
local area = areas[rand]
if inst.topology.nodes[area] == nil then
return false
end
local points_x, points_y = inst.Map:GetRandomPointsForSite(inst.topology.nodes[area].x, inst.topology.nodes[area].y, inst.topology.nodes[area].poly, 1)
if #points_x < 1 or #points_y < 1 then
return false
end
success = TryRegrowth(points_x[1], 0, points_y[1], prefab, regrowth_table[prefab].product)
if success then
-- success, reset the timer
intervals[prefab] = regrowth_table[prefab] == nil and nil or regrowth_table[prefab].interval
end
end
function self:LongUpdate(dt)
if not regrowth_table_populated_by_mod then
-- do nothing if the table is not fully initialized
-- in case we accidentally drop some saved entities due to the respawn_table[prefab] == nil check
return
end
local count = 0
local delay = 0
-- area data because we only care about stuff that can naturally spawn
for prefab in pairs(area_data) do
if regrowth_table[prefab] == nil or area_data[prefab] == nil then
-- if regrowth table didn't register, or the entity doesn't have a natural density, do nothing
intervals[prefab] = nil
else
if intervals[prefab] > UPDATE_PERIOD then
intervals[prefab] = intervals[prefab] - UPDATE_PERIOD
else
intervals[prefab] = 0
end
if DEBUG then
print("[NaturalRegrowth] Prefab " .. prefab .. " has interval " .. intervals[prefab])
end
if intervals[prefab] == 0 then
local area = area_data[prefab]
-- use multiple threads? In the future a threadpool maybe?
inst:DoTaskInTime(delay, function() RegrowPrefabTask(area, prefab) end)
-- try not to flood the server with threads
count = count + 1
if math.fmod( count,THREADS_PER_BATCH ) == 0 then
delay = delay + 1
end
end
end
end
end
--------------------------------------------------------------------------
--[[ Save/Load ]]
--------------------------------------------------------------------------
function self:OnSave()
local data = {
areas = {},
intervals = {}
}
for prefab in pairs(area_data) do
data.areas[prefab] = {}
for i = 1, #area_data[prefab] do
data.areas[prefab][#data.areas[prefab] + 1] = area_data[prefab][i]
end
if DEBUG then
print("[NaturalRegrowth] Saved " .. #data.areas[prefab] .. " areas for prefab " .. prefab)
end
end
for prefab, interval in pairs(intervals) do
if interval ~= nil then
-- it can be set to nil in the event loop
data.intervals[prefab] = interval
if DEBUG then
print("[NaturalRegrowth] Saved interval " .. data.intervals[prefab] .. " for prefab " .. prefab)
end
end
end
return data
end
function self:OnLoad(data)
for prefab in pairs(data.areas) do
if area_data[prefab] == nil then
area_data[prefab] = {}
for i = 1, #data.areas[prefab] do
area_data[prefab][#area_data[prefab] + 1] = data.areas[prefab][i]
end
if DEBUG then
print("[NaturalRegrowth] Loaded " .. #area_data[prefab] .. " areas for prefab " .. prefab)
end
end
end
for prefab, interval in pairs(data.intervals) do
intervals[prefab] = interval
if DEBUG then
print("[NaturalRegrowth] Loaded interval " .. intervals[prefab] .. " for prefab " .. prefab)
end
end
end
--------------------------------------------------------------------------
--[[ End ]]
--------------------------------------------------------------------------
end)