From 165648c40a3d192bf1967de63f66a3fb1cd8f22a Mon Sep 17 00:00:00 2001 From: secXsQuared Date: Tue, 23 Jan 2018 05:13:21 -0500 Subject: [PATCH] Almost done with event regrowth. Have to think about how to monitor player planted entities such as grass from tufts since the source code only pushes the onremove event for renewable resources. TODO: - implmenet event regrowth load/save - implement event regrowth CACHE/FAIL/SUCCESS states - currently the mod rehooks all entities every 3 seconds to mitigate the issue above but it eats performance. Maybe do it once per day? or simply don't care? - refactor natural regrowth's array iteration. --- modmain.lua | 13 +- scripts/components/event_regrowth.lua | 220 ++++++++++++++++++++++++ scripts/components/natural_regrowth.lua | 53 +++--- 3 files changed, 263 insertions(+), 23 deletions(-) create mode 100644 scripts/components/event_regrowth.lua diff --git a/modmain.lua b/modmain.lua index b91cbd1..155e080 100644 --- a/modmain.lua +++ b/modmain.lua @@ -2,22 +2,29 @@ if GLOBAL.STRINGS.NAMES.MIGRATION_PORTAL then AddPrefabPostInit("world", function(inst) if inst.ismastersim then inst:AddComponent("natural_regrowth") + inst:AddComponent("event_regrowth") end end) else - AddPrefabPostInit("forest", function(inst) + AddPrefabPostInit("world", function(inst) if inst.ismastersim then inst:AddComponent("natural_regrowth") + inst:AddComponent("event_regrowth") end end) end AddComponentPostInit("natural_regrowth", function(component) - component:RegisterRegrowth("grass", "grass") + component:RegisterRegrowth("evergreen", "evergreen") end) -AddComponentPostInit("natural_regrowth", function(component) +AddComponentPostInit("event_regrowth", function(component) + component:RegisterRegrowth("knight", "knight") component:RegisterRegrowth("evergreen", "evergreen") + component:RegisterRegrowth("grass", "grass") + component:RegisterRegrowth("rock1", "rock1") + component:RegisterRegrowth("rock2", "rock2") + component:FinishModConfig() end) diff --git a/scripts/components/event_regrowth.lua b/scripts/components/event_regrowth.lua new file mode 100644 index 0000000..d7f3a3a --- /dev/null +++ b/scripts/components/event_regrowth.lua @@ -0,0 +1,220 @@ +-------------------------------------------------------------------------- +--[[ EventRegrowth class definition ]] +-------------------------------------------------------------------------- + +return Class(function(self, inst) + + assert(TheWorld.ismastersim, "event_regrowth should not exist on client") + + require "map/terrain" + + -------------------------------------------------------------------------- + --[[ Constants ]] + -------------------------------------------------------------------------- + + local DEBUG = true + local DEBUG_TELE = true + 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 BASE_RADIUS = 20 + local EXCLUDE_RADIUS = 2 + local JITTER_RADIUS = 10 + local MIN_PLAYER_DISTANCE = 40 -- this is our "outer" sleep radius + + -------------------------------------------------------------------------- + --[[ Member variables ]] + -------------------------------------------------------------------------- + + --Public + self.inst = inst + + --Private + local regrowth_table_populated_by_mod = false + local regrowth_table = {} + local entity_list = {} + + -------------------------------------------------------------------------- + --[[ Private member functions ]] + -------------------------------------------------------------------------- + + local function EntityDeathEventHandler(ent) + if entity_list[ent.prefab] == nil then + entity_list[ent.prefab] = {} + end + local position = ent:GetPosition() + + table.insert(entity_list[ent.prefab], position) + ent:RemoveEventCallback("onremove", EntityDeathEventHandler, nil) + + if DEBUG then + print("[EventRegrowth] Entity of type ", ent.prefab, " was removed at ", position) + end + end + + local function TestForRegrow(x, y, z, tile) + if TheWorld.Map:GetTileAtPoint(x, y, z) ~= tile then + -- keep things in their biome (more or less) + return false + end + + local ents = TheSim:FindEntities(x,y,z, EXCLUDE_RADIUS) + if #ents > 0 then + -- Too dense + return false + end + + if IsAnyPlayerInRange(x,y,z, MIN_PLAYER_DISTANCE, nil) then + return false + end + + 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 false + end + return true + 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 theta = math.random() * 2 * PI + local radius = math.random() * JITTER_RADIUS + local x = x + radius * math.cos(theta) + local z = z - radius * math.sin(theta) + + 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 + + local function HookAllEntities() + while next(Ents) == nil do + end + local count = 0 + for k, v in pairs(Ents) do + if regrowth_table[v.prefab] ~= nil then + v:RemoveEventCallback("onremove", EntityDeathEventHandler, nil) + v:ListenForEvent("onremove", EntityDeathEventHandler, nil) + count = count + 1 + end + end + if DEBUG then + print("[EventRegrowth] Hooked ", count, " entities.") + end + end + + -------------------------------------------------------------------------- + --[[ Public member functions ]] + -------------------------------------------------------------------------- + function self:FinishModConfig() + regrowth_table_populated_by_mod = true + end + + function self:RegisterRegrowth(prefab, product) + if regrowth_table[prefab] == nil then + -- avoid duplicate registration + regrowth_table[prefab] = product + HookAllEntities() + end + + if DEBUG then + print("[EventRegrowth] Registered ", product ," for ", prefab) + end + end + + -------------------------------------------------------------------------- + --[[ Initialization ]] + -------------------------------------------------------------------------- + + inst:DoPeriodicTask(UPDATE_PERIOD, function() self:LongUpdate(UPDATE_PERIOD) end) + + inst:DoPeriodicTask(99, HookAllEntities, 0) + -------------------------------------------------------------------------- + --[[ Update ]] + -------------------------------------------------------------------------- + + 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 + + for prefab in pairs(entity_list) do + if entity_list[prefab] == nil or #entity_list[prefab] == 0 then + -- only do meaningful work + else + if DEBUG then + print("[EventRegrowth] Regrowing ", prefab, "...") + end + if regrowth_table[prefab] == nil then + -- if we don't have it registered, discard + entity_list[prefab] = nil + if DEBUG then + print("[EventRegrowth] Discarded") + end + else + for i = #entity_list[prefab],1,-1 do + if DEBUG then + print("[EventRegrowth] Spawning at location", entity_list[prefab][i]) + end + local attempts = 0 + while attempts < RETRY_PER_PREFAB do + local success = TryRegrowth(prefab, regrowth_table[prefab], entity_list[prefab][i]) + attempts = attempts + 1 + + if success then + print("[EventRegrowth] Succeeded after ", attempts, " attempts.") + -- we respawned this guy, remove from the list + table.remove(entity_list[prefab], i) + break + end + end + + if DEBUG and attempts == RETRY_PER_PREFAB then + print("[EventRegrowth] Failed after ", attempts, " attempts.") + end + end + end + end + end + end + + -------------------------------------------------------------------------- + --[[ Save/Load ]] + -------------------------------------------------------------------------- + + function self:OnSave() + + end + + function self:OnLoad(data) + + end + + -------------------------------------------------------------------------- + --[[ End ]] + -------------------------------------------------------------------------- + +end) + \ No newline at end of file diff --git a/scripts/components/natural_regrowth.lua b/scripts/components/natural_regrowth.lua index 6449914..0fb59b5 100644 --- a/scripts/components/natural_regrowth.lua +++ b/scripts/components/natural_regrowth.lua @@ -1,7 +1,7 @@ -------------------------------------------------------------------------- --[[ NaturalRegrowth class definition ]] -- A modified version of the original desolationspawner.lua --- It acts as a standalone regrowth manager and is in dependent 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) -- Klei has copyright over existing code used in this file. -- by lolo Jan. 2018. @@ -16,12 +16,13 @@ return Class(function(self, inst) -------------------------------------------------------------------------- --[[ Constants ]] -------------------------------------------------------------------------- - local RETRY_PER_PREFAB = 5 -- retry 5 times for each prefab - local DEBUG = true + local RETRY_PER_PREFAB = 10 -- retry 5 times for each prefab + local DEBUG = false + local DEBUG_TELE = false local UPDATE_PERIOD = 31 -- less likely to update on the same frame as others local BASE_RADIUS = 20 - local EXCLUDE_RADIUS = 3 - local MIN_PLAYER_DISTANCE = 64 * 1.2 -- this is our "outer" sleep radius + local EXCLUDE_RADIUS = 2 + local MIN_PLAYER_DISTANCE = 40 -- this is our "outer" sleep radius -------------------------------------------------------------------------- --[[ Member variables ]] @@ -85,7 +86,10 @@ return Class(function(self, inst) end if DEBUG then - print("[RegrowthEx] Spawned a ",product," for prefab ",prefab," at ", "(", x,0,z, ")", " in ", area) + print("[NaturalRegrowth] Spawned a ",product," for prefab ",prefab," at ", "(", x,0,z, ")", " in ", area) + end + + if DEBUG_TELE then c_teleport(x,0,z) end @@ -153,27 +157,36 @@ return Class(function(self, inst) for prefab in pairs(area_data) do if DEBUG then - print("[RegrowthEx] Regrowing ", prefab, "...") + print("[NaturalRegrowth] Regrowing ", prefab, "...") end local areas = area_data[prefab] - local rand = math.random(1, #areas) - local attempts = 0 - while attempts < RETRY_PER_PREFAB do - local success = TryRegrowth(areas[rand], prefab, regrowth_table[prefab]) - attempts = attempts + 1 + if regrowth_table[prefab] == nil then + if DEBUG then + print("[NaturalRegrowth] Discarded") + end + area_data[prefab] = nil + else + local rand = math.random(1, #areas) + local attempts = 0 - if success then - print("[RegrowthEx] Succeeded after ", attempts, " attempts.") - break + while attempts < RETRY_PER_PREFAB do + local success = TryRegrowth(areas[rand], prefab, regrowth_table[prefab]) + attempts = attempts + 1 + + if success then + if DEBUG then + print("[NaturalRegrowth] Succeeded after ", attempts, " attempts.") + end + break + end + end + + if DEBUG and attempts == RETRY_PER_PREFAB then + print("[NaturalRegrowth] Failed after ", attempts, " attempts.") end end - - if DEBUG and attempts == RETRY_PER_PREFAB then - print("[RegrowthEx] Failed after ", attempts, " attempts.") - end - end end