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