Added interval for each entity. Optimized event regrowth algorithem and structure check.
This commit is contained in:
parent
165648c40a
commit
4f11d8b07e
21
LICENSE
Normal file
21
LICENSE
Normal 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.
|
@ -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 = ""
|
||||||
|
|
||||||
|
86
modmain.lua
86
modmain.lua
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user