diff --git a/modicon.tex b/modicon.tex deleted file mode 100644 index 01e48fa..0000000 Binary files a/modicon.tex and /dev/null differ diff --git a/modicon.xml b/modicon.xml deleted file mode 100644 index 122e546..0000000 --- a/modicon.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/modinfo.lua b/modinfo.lua index a65c70c..9b751b2 100644 --- a/modinfo.lua +++ b/modinfo.lua @@ -1,15 +1,103 @@ name = "Advanced World Regrowth" -description = "Advanced world regrowth" +description = "Advanced world regrowth including caves!\nSee the Steam Workshop page for more information." author = "lolo" -version = "1.0.0" +version = "0.1.0" forumthread = "" api_version = 10 -icon_atlas = "modicon.xml" -icon = "modicon.tex" +--icon_atlas = "modicon.xml" +--icon = "modicon.tex" all_clients_require_mod = false client_only_mod = false -dst_compatible = true \ No newline at end of file +dst_compatible = true + +-- Configuration Generation +local config_table = +{ + {"evergreen","Evergreen","Natural"}, + {"deciduoustree","Birchnut Tree","Natural"}, + {"marsh_tree","Spiky Tree","Natural"}, + {"twiggytree","Twiggy Tree","Natural"}, + {"marbletree","Marble Tree","Event-based"}, + {"livingtree","Totally Normal Tree","Event-based"}, + + {"berrybush","Berry Bush","Natural"}, + {"berrybush2","Spiky Berry Bush","Natural"}, + {"berrybush_juicy","Juicy Berry Bush","Natural"}, + + {"carrot_planted","Carrot","Natural"}, + {"flower","Flower","Natural"}, + {"flower_evil","Evil Flower","Event-based"}, + {"blue_mushroom","Blue Mushroom","Natural"}, + {"red_mushroom","Red Mushroom","Natural"}, + {"green_mushroom","Green Mushroom","Natural"}, + {"cactus","Cactus","Natural"}, + {"mandrake","Mandrake","Event-based"}, + + {"reeds","Reeds","Natural"}, + {"sapling","Sapling","Natural"}, + {"grass","Grass","Natural"}, + {"marsh_bush","Spiky Bush","Natural"}, + + {"rock1","Boulder","Natural"}, + {"rock2","Gold Vein","Natural"}, + {"rock_flintless","Flintless Boulder","Natural"}, + {"rock_moon","Moon Rock","Natural"}, + + {"stalagmite","Stalagmite","Natural"}, + {"stalagmite_tall","Tall Stalagmite","Natural"}, + + {"beehive","Beehive","Event-based"}, + {"wasphive","Killer Bee Hive","Event-based"}, + {"houndmound","Hound Mound","Event-based"}, + {"pighouse","Pig House","Event-based"}, + {"mermhouse","Rundown House","Event-based"}, + {"spiderden","Spider Den","Event-based"}, + {"catcoonden","Hollow Stump","Event-based"}, + {"rabbithouse","Rabbit Hutch","Event-based"}, + {"monkeypods","Splumonkey Pod","Event-based"}, + {"slurtlehole", "Slurtle Mound", "Event-based"}, + + {"fireflies","Fireflies","Event-based"}, + {"tentacle","Tentacle","Event-based"}, + {"knight","Clockwork Knight","Event-based"}, + {"bishop","Clockwork Bishop","Event-based"}, + {"rook","Clockwork Rook","Event-based"}, + {"knight_nightmare","Damaged Knight","Event-based"}, + {"bishop_nightmare","Damaged Bishop","Event-based"}, + {"rook_nightmare","Damaged Rook","Event-based"}, + + {"ruins_statue_mage","Ancient Mage Statue","Event-based"}, + {"ruins_statue_mage_nogem","Gemless Ancient Mage Statue","Event-based"}, + {"ruins_statue_head","Ancient Head Statue","Event-based"}, + {"ruins_statue_head_nogem", "Gemless Ancient Head Statue", "Event-based"} +} + +local config_options = {} + +for i = 1, #config_table do + local entry = + { + name = config_table[i][1], + label = config_table[i][2], + hover = config_table[i][3], + options = + { + { + description = "Disabled", + data = false + }, + { + description = "Enabled", + data = true + } + }, + default = true + } + config_options[#config_options+1] = entry +end + +configuration_options = config_options \ No newline at end of file diff --git a/modmain.lua b/modmain.lua index 8b210f8..006601c 100644 --- a/modmain.lua +++ b/modmain.lua @@ -24,75 +24,76 @@ end local natural = { - --plants - berrybush = 1440, - berrybush2 = 1440, - berrybush_juicy = 1440, + berrybush = 1451, + berrybush2 = 1429, + berrybush_juicy = 1429, carrot_planted = 240, - evergreen = 30, - deciduoustree = 30, + evergreen = 251, + deciduoustree = 251, marsh_tree = 480, - twiggytree = 480, - flower = 240, - flower_evil = 480, - grass = 240, - blue_mushroom = 240, + twiggytree = 491, + flower = 229, + grass = 229, + blue_mushroom = 251, red_mushroom = 240, green_mushroom = 240, reeds = 480, sapling = 240, marsh_bush = 480, - cactus = 480, - rock1 = 240, + cactus = 479, + rock1 = 229, rock2 = 240, - rock_flintless = 240, - marbletree=1440, + rock_flintless = 251, rock_moon = 480, - stalagmite = 240, + stalagmite = 489, stalagmite_tall = 240, } local event = { - houndbone = 960, - pighead = 960, - marblepillar = 1440, - livingtree = 960, - mandrake = 960, - beehive = 480, - wasphive = 960, - houndmound = 1440, + flower_evil = 480, + marbletree= 960, + livingtree = 969, + mandrake = 969, + beehive = 489, + wasphive = 969, + houndmound = 1449, 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, + spiderden = 1431, + catcoonden = 951, + tentacle = 489, + rabbithole = 471, + fireflies = 471, + knight = 1431, + bishop = 1431, + rook = 1449, + knight_nightmare = 1449, bishop_nightmare = 1440, rook_nightmare = 1440, - monkeypods = 1440, - ruins_statue_mage = 960, - ruins_statue_mage_nogem = 960, + monkeypods = 951, + ruins_statue_mage = 969, + ruins_statue_mage_nogem = 969, ruins_statue_head = 960, - ruins_statue_head_nogem = 960, - rabbithouse = 960 + ruins_statue_head_nogem = 951, + rabbithouse = 951, + slurtlehole = 951 } AddComponentPostInit("natural_regrowth", function(component) for prefab, time in pairs(natural) do - component:RegisterRegrowth(prefab, prefab, time) + if GetModConfigData(prefab) then + component:RegisterRegrowth(prefab, prefab, time) + end end + component:FinishModConfig() end) AddComponentPostInit("event_regrowth", function(component) for prefab, time in pairs(event) do - component:RegisterRegrowth(prefab, prefab, time) + if GetModConfigData(prefab) then + component:RegisterRegrowth(prefab, prefab, time) + end end component:FinishModConfig() end) diff --git a/scripts/components/event_regrowth.lua b/scripts/components/event_regrowth.lua index a86c0f5..0020057 100644 --- a/scripts/components/event_regrowth.lua +++ b/scripts/components/event_regrowth.lua @@ -15,17 +15,17 @@ return Class(function(self, inst) -------------------------------------------------------------------------- --[[ Constants ]] -------------------------------------------------------------------------- - local DEBUG = true + local DEBUG = false local DEBUG_TELE = false - local UPDATE_PERIOD = 11 + local UPDATE_PERIOD = 9 local BASE_RADIUS = 20 local EXCLUDE_RADIUS = 3 local JITTER_RADIUS = 6 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 THREADS_PER_BATCH = 3 + local THREADS_PER_BATCH_HOOK = 2 local REGROW_STATUS = { SUCCESS = 0, FAILED = 1, @@ -54,7 +54,7 @@ return Class(function(self, inst) end local position = ent:GetPosition() - table.insert(entity_list[ent.prefab], {position = position, interval = regrowth_table[ent.prefab].interval}) + entity_list[ent.prefab][#entity_list[ent.prefab]+1] = {position = position, interval = regrowth_table[ent.prefab].interval} ent:RemoveEventCallback("onremove", EntityDeathEventHandler, nil) if DEBUG then @@ -63,24 +63,25 @@ return Class(function(self, inst) end local function TestForRegrow(x, y, z, tile) + + if IsAnyPlayerInRange(x,y,z, MIN_PLAYER_DISTANCE, nil) then + return REGROW_STATUS.CACHE + end + 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 return REGROW_STATUS.FAILED end - - if inst.Map:GetTileAtPoint(x, y, z) ~= tile then - -- keep things in their biome (more or less) - return REGROW_STATUS.CACHE - end - + local ents = TheSim:FindEntities(x,y,z, EXCLUDE_RADIUS) if #ents > 0 then -- Too dense return REGROW_STATUS.CACHE end - if IsAnyPlayerInRange(x,y,z, MIN_PLAYER_DISTANCE, nil) then + if inst.Map:GetTileAtPoint(x, y, z) ~= tile then + -- keep things in their biome (more or less) return REGROW_STATUS.CACHE end @@ -211,6 +212,7 @@ return Class(function(self, inst) product = product, interval = interval } + HookEntities(prefab) end @@ -230,9 +232,9 @@ return Class(function(self, inst) -------------------------------------------------------------------------- --[[ Update ]] -------------------------------------------------------------------------- - local function RegrowPrefabTask(prefab) + local function RegrowPrefabTask(prefab, position) for i = #entity_list[prefab],1,-1 do - local success = TryRegrowth(prefab, regrowth_table[prefab].product, entity_list[prefab][i].position) + local success = TryRegrowth(prefab, regrowth_table[prefab].product, position) if success then -- remove from the list if it's success or failed @@ -273,7 +275,7 @@ return Class(function(self, inst) if entity_list[prefab][i].interval == 0 then -- different threads - inst:DoTaskInTime(delay, function() RegrowPrefabTask(prefab) end) + inst:DoTaskInTime(delay, function() RegrowPrefabTask(prefab, entity_list[prefab][i].position) end) -- try not to flood the server with threads count = count + 1 @@ -296,9 +298,15 @@ return Class(function(self, inst) 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}) + if entity_list[prefab] ~= nil then + -- could be nil (set in the event loop) + data.entities[prefab] = {} + for i = 1, #entity_list[prefab] do + data.entities[prefab][#data.entities[prefab] + 1] = {interval = entity_list[prefab][i].interval, position = entity_list[prefab][i].position} + end + if DEBUG then + print("[EventRegrowth] Saved ", #data.entities[prefab]," entities for ", prefab) + end end end return data @@ -308,9 +316,12 @@ return Class(function(self, inst) 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}) + for i = 1, #data.entities[prefab] do + entity_list[prefab][#entity_list[prefab] + 1] = {interval = data.entities[prefab][i].interval, position = data.entities[prefab][i].position} + end + if DEBUG then + print("[EventRegrowth] Loaded ", #entity_list[prefab]," entities for ", prefab) + end end end end diff --git a/scripts/components/natural_regrowth.lua b/scripts/components/natural_regrowth.lua index 75ab353..dbb288a 100644 --- a/scripts/components/natural_regrowth.lua +++ b/scripts/components/natural_regrowth.lua @@ -8,7 +8,7 @@ return Class(function(self, inst) - assert(inst.ismastersim, "natrual_regrowth should not exist on client") + assert(inst.ismastersim, "natural_regrowth should not exist on client") require "map/terrain" @@ -17,11 +17,11 @@ return Class(function(self, inst) -------------------------------------------------------------------------- local DEBUG = false local DEBUG_TELE = false - local UPDATE_PERIOD = 9 + local UPDATE_PERIOD = 11 local BASE_RADIUS = 20 local EXCLUDE_RADIUS = 3 local MIN_PLAYER_DISTANCE = 40 - local THREADS_PER_BATCH = 5 + local THREADS_PER_BATCH = 3 -------------------------------------------------------------------------- --[[ Member variables ]] -------------------------------------------------------------------------- @@ -33,6 +33,7 @@ return Class(function(self, inst) local regrowth_table = {} local area_data = {} local intervals = {} + local regrowth_table_populated_by_mod = false -------------------------------------------------------------------------- --[[ Private member functions ]] @@ -65,18 +66,7 @@ return Class(function(self, inst) return true end - local function TryRegrowth(area, prefab, product) - 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 - local x = points_x[1] - local z = points_y[1] - + local function TryRegrowth(x, y, z , prefab, product) if CanRegrow(x,0,z, product) then local instance = SpawnPrefab(product) @@ -85,7 +75,7 @@ return Class(function(self, inst) end if DEBUG then - print("[NaturalRegrowth] Spawned a ",product," for prefab ",prefab," at ", "(", x,0,z, ")", " in ", area) + print("[NaturalRegrowth] Spawned a ",product," for prefab ",prefab," at ", "(", x,0,z, ")") end if DEBUG_TELE then @@ -95,7 +85,7 @@ return Class(function(self, inst) return true else if DEBUG then - print("[NaturalRegrowth] Failed to spawn a ",product," for prefab ",prefab," at ", "(", x,0,z, ")", " in ", area) + print("[NaturalRegrowth] Failed to spawn a ",product," for prefab ",prefab," at ", "(", x,0,z, ")") end return false end @@ -111,12 +101,18 @@ return Class(function(self, inst) local function PopulateAreaData(prefab) if inst.generated == nil then - -- Still starting up, not ready yet. + -- Still starting up + return + end + + if area_data[prefab] ~= nil then + if DEBUG then + print("[NaturalRegrowth] Already populated ", 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 @@ -125,7 +121,7 @@ return Class(function(self, inst) area_data[prefab] = {} end - table.insert(area_data[prefab], id) + area_data[prefab][#area_data[prefab] + 1] = id break end end @@ -147,6 +143,10 @@ return Class(function(self, inst) -------------------------------------------------------------------------- --[[ Public member functions ]] -------------------------------------------------------------------------- + + function self:FinishModConfig() + regrowth_table_populated_by_mod = true + end function self:RegisterRegrowth(prefab, product, interval) if DEBUG then @@ -173,9 +173,32 @@ return Class(function(self, inst) --[[ Update ]] -------------------------------------------------------------------------- + -- duplicate of event_regrowth + local function GetRandomLocation(x, y, z, radius) + local theta = math.random() * 2 * PI + local radius = math.random() * radius + local x = x + radius * math.cos(theta) + local z = z - radius * math.sin(theta) + return x,y,z + end + local function RegrowPrefabTask(areas, prefab) + local success = false local rand = math.random(1, #areas) - local success = TryRegrowth(areas[rand], prefab, regrowth_table[prefab].product) + 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 @@ -183,35 +206,42 @@ return Class(function(self, inst) 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 - local areas = area_data[prefab] - - if regrowth_table[prefab] == nil then - area_data[prefab] = nil - intervals[prefab] = nil - else - if intervals[prefab] > UPDATE_PERIOD then - intervals[prefab] = intervals[prefab] - UPDATE_PERIOD + 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 - intervals[prefab] = 0 - end - - if DEBUG then - print("[NaturalRegrowth]", prefab, " has interval ", intervals[prefab]) - end + if intervals[prefab] > UPDATE_PERIOD then + intervals[prefab] = intervals[prefab] - UPDATE_PERIOD + else + intervals[prefab] = 0 + end + + if DEBUG then + print("[NaturalRegrowth]", prefab, " has interval ", intervals[prefab]) + end - if intervals[prefab] == 0 then - -- 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 + if intervals[prefab] == 0 then + -- use multiple threads? In the future a threadpool maybe? + inst:DoTaskInTime(delay, function() RegrowPrefabTask(area_data[prefab], 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 @@ -228,27 +258,44 @@ return Class(function(self, inst) data.areas[prefab] = {} for i = 1, #area_data[prefab] do - table.insert(data.areas[prefab], area_data[prefab][i]) + data.areas[prefab][#data.areas[prefab] + 1] = area_data[prefab][i] + end + + if DEBUG then + print("[NaturalRegrowth] Saved ", #data.areas[prefab]," areas for ", prefab) end end for prefab, interval in pairs(intervals) do - data.intervals[prefab] = interval + 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) + 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] = {} - end - for i = 1, #data.areas[prefab] do - table.insert(area_data[prefab], data.areas[prefab][i]) + 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) + 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) + end end end