Added interval for each entity. Optimized event regrowth algorithem and structure check.

This commit is contained in:
secXsQuared 2018-01-27 04:23:18 -05:00
parent 165648c40a
commit 4f11d8b07e
5 changed files with 364 additions and 136 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 secXsQuared
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,7 +1,7 @@
name = "World Regrowth Ex" name = "Advanced World Regrowth"
description = "Advanced world regrowth" description = "Advanced world regrowth"
author = "lolo" author = "lolo"
version = "0.0.1" version = "1.0.0"
forumthread = "" forumthread = ""

View File

@ -1,11 +1,19 @@
if GLOBAL.STRINGS.NAMES.MIGRATION_PORTAL then if GLOBAL.STRINGS.NAMES.MIGRATION_PORTAL then
AddPrefabPostInit("world", function(inst) -- we have caves
AddPrefabPostInit("forest", function(inst)
if inst.ismastersim then
inst:AddComponent("natural_regrowth")
inst:AddComponent("event_regrowth")
end
end)
AddPrefabPostInit("cave", function(inst)
if inst.ismastersim then if inst.ismastersim then
inst:AddComponent("natural_regrowth") inst:AddComponent("natural_regrowth")
inst:AddComponent("event_regrowth") inst:AddComponent("event_regrowth")
end end
end) end)
else else
-- only overworld
AddPrefabPostInit("world", function(inst) AddPrefabPostInit("world", function(inst)
if inst.ismastersim then if inst.ismastersim then
inst:AddComponent("natural_regrowth") inst:AddComponent("natural_regrowth")
@ -14,18 +22,80 @@ else
end) end)
end end
local natural =
{
--plants
berrybush = 1440,
berrybush2 = 1440,
berrybush_juicy = 1440,
carrot_planted = 240,
evergreen = 30,
deciduoustree = 30,
marsh_tree = 480,
twiggytree = 480,
flower = 240,
flower_evil = 480,
grass = 240,
blue_mushroom = 240,
red_mushroom = 240,
green_mushroom = 240,
reeds = 480,
sapling = 240,
marsh_bush = 480,
cactus = 480,
rock1 = 240,
rock2 = 240,
rock_flintless = 240,
marbletree=1440,
rock_moon = 480,
stalagmite = 240,
stalagmite_tall = 240,
}
local event =
{
houndbone = 960,
pighead = 960,
marblepillar = 1440,
livingtree = 960,
mandrake = 960,
beehive = 480,
wasphive = 960,
houndmound = 1440,
pighouse = 960,
mermhouse = 960,
spiderden = 960,
molehill = 960,
catcoonden = 960,
tentacle = 480,
rabbithole = 480,
fireflies = 480,
knight = 23,
bishop = 9,
rook = 34,
knight_nightmare = 1440,
bishop_nightmare = 1440,
rook_nightmare = 1440,
monkeypods = 1440,
ruins_statue_mage = 960,
ruins_statue_mage_nogem = 960,
ruins_statue_head = 960,
ruins_statue_head_nogem = 960,
rabbithouse = 960
}
AddComponentPostInit("natural_regrowth", function(component) AddComponentPostInit("natural_regrowth", function(component)
component:RegisterRegrowth("evergreen", "evergreen") for prefab, time in pairs(natural) do
component:RegisterRegrowth(prefab, prefab, time)
end
end) end)
AddComponentPostInit("event_regrowth", function(component) AddComponentPostInit("event_regrowth", function(component)
component:RegisterRegrowth("knight", "knight") for prefab, time in pairs(event) do
component:RegisterRegrowth("evergreen", "evergreen") component:RegisterRegrowth(prefab, prefab, time)
component:RegisterRegrowth("grass", "grass") end
component:RegisterRegrowth("rock1", "rock1")
component:RegisterRegrowth("rock2", "rock2")
component:FinishModConfig() component:FinishModConfig()
end) end)
--"forest" for the overworld --"forest" for the overworld

View File

@ -1,25 +1,36 @@
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ EventRegrowth class definition ]] --[[ EventRegrowth class definition ]]
-- A modified version of the original regrowthmanager.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) return Class(function(self, inst)
assert(TheWorld.ismastersim, "event_regrowth should not exist on client") assert(inst.ismastersim, "event_regrowth should not exist on client")
require "map/terrain" require "map/terrain"
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ Constants ]] --[[ Constants ]]
-------------------------------------------------------------------------- --------------------------------------------------------------------------
local DEBUG = true local DEBUG = true
local DEBUG_TELE = true local DEBUG_TELE = false
local RETRY_PER_PREFAB = 10 -- retry 5 times for each prefab
local UPDATE_PERIOD = 3 -- less likely to update on the same frame as others local UPDATE_PERIOD = 11
local BASE_RADIUS = 20 local BASE_RADIUS = 20
local EXCLUDE_RADIUS = 2 local EXCLUDE_RADIUS = 3
local JITTER_RADIUS = 10 local JITTER_RADIUS = 6
local MIN_PLAYER_DISTANCE = 40 -- this is our "outer" sleep radius local TOTAL_RADIUS = 1000
local MIN_PLAYER_DISTANCE = 40
local THREADS_PER_BATCH = 5 -- since we retry a lot, we reduce the # of threads to guarantee performance
local THREADS_PER_BATCH_HOOK = 5
local REGROW_STATUS = {
SUCCESS = 0,
FAILED = 1,
CACHE = 2,
}
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ Member variables ]] --[[ Member variables ]]
@ -43,83 +54,145 @@ return Class(function(self, inst)
end end
local position = ent:GetPosition() local position = ent:GetPosition()
table.insert(entity_list[ent.prefab], position) table.insert(entity_list[ent.prefab], {position = position, interval = regrowth_table[ent.prefab].interval})
ent:RemoveEventCallback("onremove", EntityDeathEventHandler, nil) ent:RemoveEventCallback("onremove", EntityDeathEventHandler, nil)
if DEBUG then if DEBUG then
print("[EventRegrowth] Entity of type ", ent.prefab, " was removed at ", position) print("[EventRegrowth] ", ent.prefab, " was removed at ", position)
end end
end end
local function TestForRegrow(x, y, z, tile) local function TestForRegrow(x, y, z, tile)
if TheWorld.Map:GetTileAtPoint(x, y, z) ~= tile then local ents = TheSim:FindEntities(x,y,z, BASE_RADIUS, nil, nil, { "structure", "wall" })
if #ents > 0 then
-- No regrowth around players and their bases
return REGROW_STATUS.FAILED
end
if inst.Map:GetTileAtPoint(x, y, z) ~= tile then
-- keep things in their biome (more or less) -- keep things in their biome (more or less)
return false return REGROW_STATUS.CACHE
end end
local ents = TheSim:FindEntities(x,y,z, EXCLUDE_RADIUS) local ents = TheSim:FindEntities(x,y,z, EXCLUDE_RADIUS)
if #ents > 0 then if #ents > 0 then
-- Too dense -- Too dense
return false return REGROW_STATUS.CACHE
end end
if IsAnyPlayerInRange(x,y,z, MIN_PLAYER_DISTANCE, nil) then if IsAnyPlayerInRange(x,y,z, MIN_PLAYER_DISTANCE, nil) then
return REGROW_STATUS.CACHE
end
return REGROW_STATUS.SUCCESS
end
-- duplicate of canregrow in natural regrowth
local function CanRegrow(x, y, z, prefab)
if IsAnyPlayerInRange(x,y,z, MIN_PLAYER_DISTANCE, nil) then
return false
end
local ents = TheSim:FindEntities(x,y,z, EXCLUDE_RADIUS)
if #ents > 0 then
-- Too dense
return false return false
end end
local ents = TheSim:FindEntities(x,y,z, BASE_RADIUS, nil, nil, { "structure", "wall" }) local ents = TheSim:FindEntities(x,y,z, BASE_RADIUS, nil, nil, { "structure", "wall" })
if #ents > 0 then if #ents > 0 then
-- No regrowth around players and their bases -- Don't spawn inside bases
return false
end
if not (inst.Map:CanPlantAtPoint(x, y, z) and
inst.Map:CanPlacePrefabFilteredAtPoint(x, y, z, prefab))
or (RoadManager ~= nil and RoadManager:IsOnRoad(x, 0, z)) then
-- Not ground we can grow on
return false return false
end end
return true return true
end end
local function TryRegrowth(prefab, product, position)
local x = position.x
local y = position.y
local z = position.z
local orig_tile = TheWorld.Map:GetTileAtPoint(x,y,z)
local function GetRandomLocation(x, y, z, radius)
local theta = math.random() * 2 * PI local theta = math.random() * 2 * PI
local radius = math.random() * JITTER_RADIUS local radius = math.random() * radius
local x = x + radius * math.cos(theta) local x = x + radius * math.cos(theta)
local z = z - radius * math.sin(theta) local z = z - radius * math.sin(theta)
return x,y,z
if TestForRegrow(x,y,z, orig_tile) then
local instance = SpawnPrefab(product)
if instance ~= nil then
instance.Transform:SetPosition(x,y,z)
instance:ListenForEvent("onremove", EntityDeathEventHandler, nil)
if DEBUG then
print("[EventRegrowth] Spawned a ",product," for prefab ",prefab," at ", "(", x,0,z, ")")
end
if DEBUG_TELE then
c_teleport(x,0,z)
end
end
return true
else
return false
end
end end
local function HookAllEntities() local function TryRegrowth(prefab, product, position)
local x,y,z = GetRandomLocation(position.x,position.y,position.z,JITTER_RADIUS)
local orig_tile = inst.Map:GetTileAtPoint(x,y,z)
local status = TestForRegrow(x,y,z, orig_tile)
if status == REGROW_STATUS.CACHE then
if DEBUG then
print("[EventRegrowth] Cached a ",product," for prefab ",prefab," at ", x, ",", y,",",z)
end
return false
end
if status == REGROW_STATUS.FAILED then
-- for the failed case, we want to try spawning at a random location
x,y,z = GetRandomLocation(position.x,position.y,position.z,TOTAL_RADIUS)
if not CanRegrow(x,y,z, product) then
-- if cannot regrow, return CACHE status
if DEBUG then
print("[EventRegrowth] Failed to spawn a ",product," for prefab ",prefab," at ", x, ",", y,",",z)
end
return false
end
end
local instance = SpawnPrefab(product)
if instance ~= nil then
instance.Transform:SetPosition(x,y,z)
instance:ListenForEvent("onremove", EntityDeathEventHandler, nil)
if DEBUG then
print("[EventRegrowth] Spawned a ",product," for prefab ",prefab," at ", x, ",", y,",",z)
end
if DEBUG_TELE then
c_teleport(x,0,z)
end
end
return true
end
local function HookEntities(prefab)
while next(Ents) == nil do while next(Ents) == nil do
end end
local count = 0 local count = 0
for k, v in pairs(Ents) do for k, v in pairs(Ents) do
if regrowth_table[v.prefab] ~= nil then if v.prefab == prefab then
v:RemoveEventCallback("onremove", EntityDeathEventHandler, nil) v:RemoveEventCallback("onremove", EntityDeathEventHandler, nil)
v:ListenForEvent("onremove", EntityDeathEventHandler, nil) v:ListenForEvent("onremove", EntityDeathEventHandler, nil)
count = count + 1 count = count + 1
end end
end end
if DEBUG then if DEBUG then
print("[EventRegrowth] Hooked ", count, " entities.") print("[EventRegrowth] Hooked ", count, " ",prefab)
end
end
local function HookAllEntities(ents)
local count = 0
local delay = 0
for prefab in pairs(ents) do
inst:DoTaskInTime(delay, function() HookEntities(prefab) end)
count = count + 1
if math.fmod(count, THREADS_PER_BATCH_HOOK) == 0 then
delay = delay + 1
end
end end
end end
@ -130,11 +203,15 @@ return Class(function(self, inst)
regrowth_table_populated_by_mod = true regrowth_table_populated_by_mod = true
end end
function self:RegisterRegrowth(prefab, product) function self:RegisterRegrowth(prefab, product, interval)
if regrowth_table[prefab] == nil then if regrowth_table[prefab] == nil then
-- avoid duplicate registration -- avoid duplicate registration
regrowth_table[prefab] = product regrowth_table[prefab] =
HookAllEntities() {
product = product,
interval = interval
}
HookEntities(prefab)
end end
if DEBUG then if DEBUG then
@ -147,12 +224,23 @@ return Class(function(self, inst)
-------------------------------------------------------------------------- --------------------------------------------------------------------------
inst:DoPeriodicTask(UPDATE_PERIOD, function() self:LongUpdate(UPDATE_PERIOD) end) inst:DoPeriodicTask(UPDATE_PERIOD, function() self:LongUpdate(UPDATE_PERIOD) end)
inst:ListenForEvent("ms_cyclecomplete", function() HookAllEntities(regrowth_table) end) -- every ~ 1 day we rehook every entities
inst:DoTaskInTime(0, function() HookAllEntities(regrowth_table) end)
inst:DoPeriodicTask(99, HookAllEntities, 0)
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ Update ]] --[[ Update ]]
-------------------------------------------------------------------------- --------------------------------------------------------------------------
local function RegrowPrefabTask(prefab)
for i = #entity_list[prefab],1,-1 do
local success = TryRegrowth(prefab, regrowth_table[prefab].product, entity_list[prefab][i].position)
if success then
-- remove from the list if it's success or failed
table.remove(entity_list[prefab], i)
end
end
end
function self:LongUpdate(dt) function self:LongUpdate(dt)
if not regrowth_table_populated_by_mod then if not regrowth_table_populated_by_mod then
-- do nothing if the table is not fully initialized -- do nothing if the table is not fully initialized
@ -160,40 +248,39 @@ return Class(function(self, inst)
return return
end end
local count = 0
local delay = 0
for prefab in pairs(entity_list) do for prefab in pairs(entity_list) do
if entity_list[prefab] == nil or #entity_list[prefab] == 0 then if entity_list[prefab] == nil or #entity_list[prefab] == 0 then
-- only do meaningful work -- only do meaningful work
else else
if DEBUG then
print("[EventRegrowth] Regrowing ", prefab, "...")
end
if regrowth_table[prefab] == nil then if regrowth_table[prefab] == nil then
-- if we don't have it registered, discard -- if we don't have it registered, discard
entity_list[prefab] = nil entity_list[prefab] = nil
if DEBUG then
print("[EventRegrowth] Discarded")
end
else else
for i = #entity_list[prefab],1,-1 do for i = 1, #entity_list[prefab] do
-- decrease the interval
if entity_list[prefab][i].interval > UPDATE_PERIOD then
entity_list[prefab][i].interval = entity_list[prefab][i].interval - UPDATE_PERIOD
else
-- else set to 0 and regen
entity_list[prefab][i].interval = 0
end
if DEBUG then if DEBUG then
print("[EventRegrowth] Spawning at location", entity_list[prefab][i]) print("[EventRegrowth]", prefab, " at ", entity_list[prefab][i].position, " has interval ", entity_list[prefab][i].interval )
end end
local attempts = 0
while attempts < RETRY_PER_PREFAB do if entity_list[prefab][i].interval == 0 then
local success = TryRegrowth(prefab, regrowth_table[prefab], entity_list[prefab][i]) -- different threads
attempts = attempts + 1 inst:DoTaskInTime(delay, function() RegrowPrefabTask(prefab) end)
if success then -- try not to flood the server with threads
print("[EventRegrowth] Succeeded after ", attempts, " attempts.") count = count + 1
-- we respawned this guy, remove from the list if math.fmod( count,THREADS_PER_BATCH ) == 0 then
table.remove(entity_list[prefab], i) delay = delay + 1
break
end end
end end
if DEBUG and attempts == RETRY_PER_PREFAB then
print("[EventRegrowth] Failed after ", attempts, " attempts.")
end
end end
end end
end end
@ -205,11 +292,27 @@ return Class(function(self, inst)
-------------------------------------------------------------------------- --------------------------------------------------------------------------
function self:OnSave() function self:OnSave()
local data = {
entities = {}
}
for prefab in pairs(entity_list) do
data.entities[prefab] = {}
for i = 1, #entity_list[prefab] do
table.insert(data.entities[prefab], {interval = entity_list[prefab][i].interval, position = entity_list[prefab][i].position})
end
end
return data
end end
function self:OnLoad(data) function self:OnLoad(data)
for prefab in pairs(data.entities) do
if entity_list[prefab] == nil then
entity_list[prefab] = {}
end
for i = 1, #data.entities[prefab] do
table.insert(entity_list[prefab], {interval = data.entities[prefab][i].interval, position = data.entities[prefab][i].position})
end
end
end end
-------------------------------------------------------------------------- --------------------------------------------------------------------------

View File

@ -3,27 +3,25 @@
-- A modified version of the original desolationspawner.lua -- A modified version of the original desolationspawner.lua
-- It acts as a standalone regrowth manager and is independent of the 3 existing ones -- 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) -- It's unlikely affected by game updates as long as Klei doesn't change the API (they shouldn't)
-- Klei has copyright over existing code used in this file. -- by lolo Jan. 2018
-- by lolo Jan. 2018.
-------------------------------------------------------------------------- --------------------------------------------------------------------------
return Class(function(self, inst) return Class(function(self, inst)
assert(TheWorld.ismastersim, "natrual_regrowth should not exist on client") assert(inst.ismastersim, "natrual_regrowth should not exist on client")
require "map/terrain" require "map/terrain"
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ Constants ]] --[[ Constants ]]
-------------------------------------------------------------------------- --------------------------------------------------------------------------
local RETRY_PER_PREFAB = 10 -- retry 5 times for each prefab
local DEBUG = false local DEBUG = false
local DEBUG_TELE = false local DEBUG_TELE = false
local UPDATE_PERIOD = 31 -- less likely to update on the same frame as others local UPDATE_PERIOD = 9
local BASE_RADIUS = 20 local BASE_RADIUS = 20
local EXCLUDE_RADIUS = 2 local EXCLUDE_RADIUS = 3
local MIN_PLAYER_DISTANCE = 40 -- this is our "outer" sleep radius local MIN_PLAYER_DISTANCE = 40
local THREADS_PER_BATCH = 5
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ Member variables ]] --[[ Member variables ]]
-------------------------------------------------------------------------- --------------------------------------------------------------------------
@ -34,6 +32,7 @@ return Class(function(self, inst)
--Private --Private
local regrowth_table = {} local regrowth_table = {}
local area_data = {} local area_data = {}
local intervals = {}
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ Private member functions ]] --[[ Private member functions ]]
@ -57,8 +56,8 @@ return Class(function(self, inst)
return false return false
end end
if not (TheWorld.Map:CanPlantAtPoint(x, y, z) and if not (inst.Map:CanPlantAtPoint(x, y, z) and
TheWorld.Map:CanPlacePrefabFilteredAtPoint(x, y, z, prefab)) inst.Map:CanPlacePrefabFilteredAtPoint(x, y, z, prefab))
or (RoadManager ~= nil and RoadManager:IsOnRoad(x, 0, z)) then or (RoadManager ~= nil and RoadManager:IsOnRoad(x, 0, z)) then
-- Not ground we can grow on -- Not ground we can grow on
return false return false
@ -67,13 +66,13 @@ return Class(function(self, inst)
end end
local function TryRegrowth(area, prefab, product) local function TryRegrowth(area, prefab, product)
if TheWorld.topology.nodes[area] == nil then if inst.topology.nodes[area] == nil then
return false return false
end end
local points_x, points_y = TheWorld.Map:GetRandomPointsForSite(TheWorld.topology.nodes[area].x, TheWorld.topology.nodes[area].y, TheWorld.topology.nodes[area].poly, 1) 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 if #points_x < 1 or #points_y < 1 then
return return false
end end
local x = points_x[1] local x = points_x[1]
local z = points_y[1] local z = points_y[1]
@ -95,20 +94,32 @@ return Class(function(self, inst)
return true return true
else else
if DEBUG then
print("[NaturalRegrowth] Failed to spawn a ",product," for prefab ",prefab," at ", "(", x,0,z, ")", " in ", area)
end
return false return false
end end
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) local function PopulateAreaData(prefab)
if TheWorld.generated == nil then if inst.generated == nil then
-- Still starting up, not ready yet. -- Still starting up, not ready yet.
return return
end end
for area, densities in pairs(TheWorld.generated.densities) do -- PrintDensities()
for area, densities in pairs(inst.generated.densities) do
if densities[prefab] ~= nil then if densities[prefab] ~= nil then
for id, v in ipairs(TheWorld.topology.ids) do for id, v in ipairs(inst.topology.ids) do
if v == area then if v == area then
if area_data[prefab] == nil then if area_data[prefab] == nil then
area_data[prefab] = {} area_data[prefab] = {}
@ -120,11 +131,15 @@ return Class(function(self, inst)
end end
end end
end end
if DEBUG then
print("[NaturalRegrowth] Populated ", area_data[prefab] == nil and 0 or #area_data[prefab], " areas for ", prefab)
end
end end
local function PopulateAllAreaData() local function PopulateAllAreaData()
-- This has to be run after 1 frame from startup -- This has to be run after 1 frame from startup
for prefab, _ in pairs(regrowth_table) do for prefab in pairs(regrowth_table) do
PopulateAreaData(prefab) PopulateAreaData(prefab)
end end
end end
@ -133,11 +148,16 @@ return Class(function(self, inst)
--[[ Public member functions ]] --[[ Public member functions ]]
-------------------------------------------------------------------------- --------------------------------------------------------------------------
function self:RegisterRegrowth(prefab, product) function self:RegisterRegrowth(prefab, product, interval)
if DEBUG then if DEBUG then
print("Registered ", product, " for prefab " ,prefab ) print("[NaturalRegrowth] Registered ", product, " for prefab " ,prefab )
end end
regrowth_table[prefab] = product regrowth_table[prefab] = {product = product, interval = interval}
if intervals[prefab] == nil then
intervals[prefab] = interval
end
PopulateAreaData(prefab) PopulateAreaData(prefab)
end end
@ -152,39 +172,44 @@ return Class(function(self, inst)
-------------------------------------------------------------------------- --------------------------------------------------------------------------
--[[ Update ]] --[[ Update ]]
-------------------------------------------------------------------------- --------------------------------------------------------------------------
local function RegrowPrefabTask(areas, prefab)
local rand = math.random(1, #areas)
local success = TryRegrowth(areas[rand], 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) function self:LongUpdate(dt)
local count = 0
local delay = 0
for prefab in pairs(area_data) do for prefab in pairs(area_data) do
if DEBUG then
print("[NaturalRegrowth] Regrowing ", prefab, "...")
end
local areas = area_data[prefab] local areas = area_data[prefab]
if regrowth_table[prefab] == nil then if regrowth_table[prefab] == nil then
if DEBUG then
print("[NaturalRegrowth] Discarded")
end
area_data[prefab] = nil area_data[prefab] = nil
intervals[prefab] = nil
else else
local rand = math.random(1, #areas) if intervals[prefab] > UPDATE_PERIOD then
local attempts = 0 intervals[prefab] = intervals[prefab] - UPDATE_PERIOD
else
while attempts < RETRY_PER_PREFAB do intervals[prefab] = 0
local success = TryRegrowth(areas[rand], prefab, regrowth_table[prefab]) end
attempts = attempts + 1
if DEBUG then
if success then print("[NaturalRegrowth]", prefab, " has interval ", intervals[prefab])
if DEBUG then
print("[NaturalRegrowth] Succeeded after ", attempts, " attempts.")
end
break
end
end end
if DEBUG and attempts == RETRY_PER_PREFAB then if intervals[prefab] == 0 then
print("[NaturalRegrowth] Failed after ", attempts, " attempts.") -- use multiple threads? In the future a threadpool maybe?
inst:DoTaskInTime(delay, function() RegrowPrefabTask(areas,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
end end
@ -196,25 +221,34 @@ return Class(function(self, inst)
function self:OnSave() function self:OnSave()
local data = { local data = {
areas = {} areas = {},
intervals = {}
} }
for prefab in pairs(area_data) do for prefab in pairs(area_data) do
data.areas[prefab] = {} data.areas[prefab] = {}
for area in pairs(area_data[prefab]) do
table.insert(data.areas[prefab], area) for i = 1, #area_data[prefab] do
table.insert(data.areas[prefab], area_data[prefab][i])
end end
end end
for prefab, interval in pairs(intervals) do
data.intervals[prefab] = interval
end
return data return data
end end
function self:OnLoad(data) function self:OnLoad(data)
for prefab in pairs(data.areas) do for prefab in pairs(data.areas) do
for area in pairs(data.areas) do if area_data[prefab] == nil then
if area_data[prefab] == nil then area_data[prefab] = {}
area_data[prefab] = {}
end
table.insert(area_data[prefab], area)
end end
for i = 1, #data.areas[prefab] do
table.insert(area_data[prefab], data.areas[prefab][i])
end
end
for prefab, interval in pairs(data.intervals) do
intervals[prefab] = interval
end end
end end