From b7d69a8487345b9d6c742b408b1e3315f2caf6e8 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 2 Jan 2019 20:35:39 +0100 Subject: [PATCH] Split code and tidy up Split to growth.lua and search_functions.lua New function populate_single_surface for a single position check Use 'return' to reduce indent levels --- growth.lua | 78 +++++++ init.lua | 531 +++++++++++++++++++------------------------ search_functions.lua | 53 +++++ 3 files changed, 361 insertions(+), 301 deletions(-) create mode 100644 growth.lua create mode 100644 search_functions.lua diff --git a/growth.lua b/growth.lua new file mode 100644 index 0000000..aec2e95 --- /dev/null +++ b/growth.lua @@ -0,0 +1,78 @@ +-- The growing ABM + +function biome_lib:grow_plants(opts) + + local options = opts + + options.height_limit = options.height_limit or 5 + options.ground_nodes = options.ground_nodes or { "default:dirt_with_grass" } + options.grow_nodes = options.grow_nodes or { "default:dirt_with_grass" } + options.seed_diff = options.seed_diff or 0 + + local n + + if type(options.grow_plant) == "table" then + n = "multi: "..options.grow_plant[1]..", ..." + else + n = options.grow_plant + end + + options.label = options.label or "biome_lib grow_plants(): "..n + + if options.grow_delay*time_scale >= 1 then + options.interval = options.grow_delay*time_scale + else + options.interval = 1 + end + + minetest.register_abm({ + nodenames = { options.grow_plant }, + interval = options.interval, + chance = options.grow_chance, + label = options.label, + action = function(pos, node, active_object_count, active_object_count_wider) + local p_top = {x=pos.x, y=pos.y+1, z=pos.z} + local p_bot = {x=pos.x, y=pos.y-1, z=pos.z} + local n_top = minetest.get_node(p_top) + local n_bot = minetest.get_node(p_bot) + local root_node = minetest.get_node({x=pos.x, y=pos.y-options.height_limit, z=pos.z}) + local walldir = nil + if options.need_wall and options.verticals_list then + walldir = biome_lib:find_adjacent_wall(p_top, options.verticals_list, options.choose_random_wall) + end + if (n_top.name == "air" or n_top.name == "default:snow") + and (not options.need_wall or (options.need_wall and walldir)) then + -- corner case for changing short junglegrass + -- to dry shrub in desert + if n_bot.name == options.dry_early_node and options.grow_plant == "junglegrass:short" then + minetest.swap_node(pos, { name = "default:dry_shrub" }) + + elseif options.grow_vertically and walldir then + if biome_lib:search_downward(pos, options.height_limit, options.ground_nodes) then + minetest.swap_node(p_top, { name = options.grow_plant, param2 = walldir}) + end + + elseif not options.grow_result and not options.grow_function then + minetest.swap_node(pos, biome_lib.air) + + else + biome_lib:replace_object(pos, options.grow_result, options.grow_function, options.facedir, options.seed_diff) + end + end + end + }) +end + + +-- spawn_tree() on generate is routed through here so that other mods can hook +-- into it. + +function biome_lib:generate_tree(pos, nodes_or_function_or_model) + minetest.spawn_tree(pos, nodes_or_function_or_model) +end + +-- and this one's for the call used in the growing code + +function biome_lib:grow_tree(pos, nodes_or_function_or_model) + minetest.spawn_tree(pos, nodes_or_function_or_model) +end diff --git a/init.lua b/init.lua index f79c1d7..c8777fd 100644 --- a/init.lua +++ b/init.lua @@ -198,6 +198,88 @@ function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model) end end +-- Function to check whether a position matches the given biome definition +-- Returns true when the surface can be populated + +local function populate_single_surface(biome, pos, perlin_fertile_area) + local p_top = { x = pos.x, y = pos.y + 1, z = pos.z } + local noise1 = perlin_fertile_area:get2d({x=pos.x, y=pos.z}) + local noise2 = biome_lib.perlin_temperature:get2d({x=pos.x, y=pos.z}) + local noise3 = biome_lib.perlin_humidity:get2d({x=pos.x+150, y=pos.z+50}) + + if math.random(1, 100) <= biome.rarity then + return + end + + local pos_biome_ok = pos.y >= biome.min_elevation and pos.y <= biome.max_elevation + and noise1 > biome.plantlife_limit + and noise2 <= biome.temp_min and noise2 >= biome.temp_max + and noise3 <= biome.humidity_min and noise3 >= biome.humidity_max + + if not pos_biome_ok then + return -- Y position mismatch, outside of biome + end + + local biome_surfaces_string = dump(biome.surface) + local surface_ok = false + + if not biome.depth then + local dest_node = minetest.get_node(pos) + if string.find(biome_surfaces_string, dest_node.name) then + surface_ok = true + else + if string.find(biome_surfaces_string, "group:") then + for j = 1, #biome.surface do + if string.find(biome.surface[j], "^group:") + and minetest.get_item_group(dest_node.name, biome.surface[j]) then + surface_ok = true + break + end + end + end + end + elseif not string.find(biome_surfaces_string, + minetest.get_node({ x = pos.x, y = pos.y-biome.depth-1, z = pos.z }).name) then + surface_ok = true + end + + if not surface_ok then + return -- Surface does not match the given node group/name + end + + if checkair and minetest.get_node(p_top).name ~= "air" then + return + end + + if biome.below_nodes and + not string.find(dump(biome.below_nodes), + minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name + ) then + return -- Node below does not match + end + + if biome.ncount and + #minetest.find_nodes_in_area( + {x=pos.x-1, y=pos.y, z=pos.z-1}, + {x=pos.x+1, y=pos.y, z=pos.z+1}, + biome.neighbors + ) <= biome.ncount then + return -- Not enough similar biome nodes around + end + + if biome.near_nodes and + #minetest.find_nodes_in_area( + {x=pos.x-biome.near_nodes_size, y=pos.y-biome.near_nodes_vertical, z=pos.z-biome.near_nodes_size}, + {x=pos.x+biome.near_nodes_size, y=pos.y+biome.near_nodes_vertical, z=pos.z+biome.near_nodes_size}, + biome.near_nodes + ) < biome.near_nodes_count then + return -- Long distance neighbours do not match + end + + -- Position fits into given biome + return true +end + function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes, checkair) biome_lib:set_defaults(biome) @@ -208,47 +290,8 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes, local perlin_fertile_area = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale) for i = 1, #snodes do - local pos = snodes[i] - local p_top = { x = pos.x, y = pos.y + 1, z = pos.z } - local noise1 = perlin_fertile_area:get2d({x=pos.x, y=pos.z}) - local noise2 = biome_lib.perlin_temperature:get2d({x=pos.x, y=pos.z}) - local noise3 = biome_lib.perlin_humidity:get2d({x=pos.x+150, y=pos.z+50}) - local biome_surfaces_string = dump(biome.surface) - local surface_ok = false - - if not biome.depth then - local dest_node = minetest.get_node(pos) - if string.find(biome_surfaces_string, dest_node.name) then - surface_ok = true - else - if string.find(biome_surfaces_string, "group:") then - for j = 1, #biome.surface do - if string.find(biome.surface[j], "^group:") - and minetest.get_item_group(dest_node.name, biome.surface[j]) then - surface_ok = true - break - end - end - end - end - elseif not string.find(biome_surfaces_string, minetest.get_node({ x = pos.x, y = pos.y-biome.depth-1, z = pos.z }).name) then - surface_ok = true - end - - if surface_ok - and pos.y >= biome.min_elevation - and pos.y <= biome.max_elevation - and noise1 > biome.plantlife_limit - and noise2 <= biome.temp_min - and noise2 >= biome.temp_max - and noise3 <= biome.humidity_min - and noise3 >= biome.humidity_max - and (not checkair or minetest.get_node(p_top).name == "air") - and (not biome.ncount or #(minetest.find_nodes_in_area({x=pos.x-1, y=pos.y, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, biome.neighbors)) > biome.ncount) - and (not biome.near_nodes or #(minetest.find_nodes_in_area({x=pos.x-biome.near_nodes_size, y=pos.y-biome.near_nodes_vertical, z=pos.z-biome.near_nodes_size}, {x=pos.x+biome.near_nodes_size, y=pos.y+biome.near_nodes_vertical, z=pos.z+biome.near_nodes_size}, biome.near_nodes)) >= biome.near_nodes_count) - and math.random(1,100) > biome.rarity - and (not biome.below_nodes or string.find(dump(biome.below_nodes), minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name) ) - then + local pos = vector.new(snodes[i]) + if populate_single_surface(biome, pos, perlin_fertile_area) then in_biome_nodes[#in_biome_nodes + 1] = pos end end @@ -257,73 +300,77 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes, local num_in_biome_nodes = #in_biome_nodes - if num_in_biome_nodes > 0 then - for i = 1, math.min(biome.max_count, num_in_biome_nodes) do - local tries = 0 - local spawned = false - while tries < 2 and not spawned do - local pos = in_biome_nodes[math.random(1, num_in_biome_nodes)] - if biome.spawn_replace_node then - pos.y = pos.y-1 + if num_in_biome_nodes == 0 then + return + end + + for i = 1, math.min(biome.max_count, num_in_biome_nodes) do + local tries = 0 + local spawned = false + while tries < 2 and not spawned do + local pos = in_biome_nodes[math.random(1, num_in_biome_nodes)] + if biome.spawn_replace_node then + pos.y = pos.y-1 + end + local p_top = { x = pos.x, y = pos.y + 1, z = pos.z } + + if not (biome.avoid_nodes and biome.avoid_radius + and minetest.find_node_near(p_top, biome.avoid_radius + + math.random(-1.5,2), biome.avoid_nodes)) then + if biome.delete_above then + minetest.swap_node(p_top, biome_lib.air) + minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z}, biome_lib.air) end - local p_top = { x = pos.x, y = pos.y + 1, z = pos.z } - if not (biome.avoid_nodes and biome.avoid_radius and minetest.find_node_near(p_top, biome.avoid_radius + math.random(-1.5,2), biome.avoid_nodes)) then - if biome.delete_above then - minetest.swap_node(p_top, biome_lib.air) - minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z}, biome_lib.air) - end + if biome.delete_above_surround then + minetest.swap_node({x=p_top.x-1, y=p_top.y, z=p_top.z}, biome_lib.air) + minetest.swap_node({x=p_top.x+1, y=p_top.y, z=p_top.z}, biome_lib.air) + minetest.swap_node({x=p_top.x, y=p_top.y, z=p_top.z-1}, biome_lib.air) + minetest.swap_node({x=p_top.x, y=p_top.y, z=p_top.z+1}, biome_lib.air) - if biome.delete_above_surround then - minetest.swap_node({x=p_top.x-1, y=p_top.y, z=p_top.z}, biome_lib.air) - minetest.swap_node({x=p_top.x+1, y=p_top.y, z=p_top.z}, biome_lib.air) - minetest.swap_node({x=p_top.x, y=p_top.y, z=p_top.z-1}, biome_lib.air) - minetest.swap_node({x=p_top.x, y=p_top.y, z=p_top.z+1}, biome_lib.air) + minetest.swap_node({x=p_top.x-1, y=p_top.y+1, z=p_top.z}, biome_lib.air) + minetest.swap_node({x=p_top.x+1, y=p_top.y+1, z=p_top.z}, biome_lib.air) + minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z-1}, biome_lib.air) + minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z+1}, biome_lib.air) + end - minetest.swap_node({x=p_top.x-1, y=p_top.y+1, z=p_top.z}, biome_lib.air) - minetest.swap_node({x=p_top.x+1, y=p_top.y+1, z=p_top.z}, biome_lib.air) - minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z-1}, biome_lib.air) - minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z+1}, biome_lib.air) - end + if biome.spawn_replace_node then + minetest.swap_node(pos, biome_lib.air) + end - if biome.spawn_replace_node then - minetest.swap_node(pos, biome_lib.air) - end + local objtype = type(nodes_or_function_or_model) - local objtype = type(nodes_or_function_or_model) - - if objtype == "table" then - if nodes_or_function_or_model.axiom then - biome_lib:generate_tree(p_top, nodes_or_function_or_model) - spawned = true - else - local fdir = nil - if biome.random_facedir then - fdir = math.random(biome.random_facedir[1], biome.random_facedir[2]) - end - minetest.swap_node(p_top, { name = nodes_or_function_or_model[math.random(#nodes_or_function_or_model)], param2 = fdir }) - spawned = true - end - elseif objtype == "string" and - minetest.registered_nodes[nodes_or_function_or_model] then + if objtype == "table" then + if nodes_or_function_or_model.axiom then + biome_lib:generate_tree(p_top, nodes_or_function_or_model) + spawned = true + else local fdir = nil if biome.random_facedir then fdir = math.random(biome.random_facedir[1], biome.random_facedir[2]) end - minetest.swap_node(p_top, { name = nodes_or_function_or_model, param2 = fdir }) + minetest.swap_node(p_top, { name = nodes_or_function_or_model[math.random(#nodes_or_function_or_model)], param2 = fdir }) spawned = true - elseif objtype == "function" then - nodes_or_function_or_model(pos) - spawned = true - elseif objtype == "string" and pcall(loadstring(("return %s(...)"): - format(nodes_or_function_or_model)),pos) then - spawned = true - else - biome_lib:dbg("Warning: Ignored invalid definition for object "..dump(nodes_or_function_or_model).." that was pointed at {"..dump(pos).."}") end + elseif objtype == "string" and + minetest.registered_nodes[nodes_or_function_or_model] then + local fdir = nil + if biome.random_facedir then + fdir = math.random(biome.random_facedir[1], biome.random_facedir[2]) + end + minetest.swap_node(p_top, { name = nodes_or_function_or_model, param2 = fdir }) + spawned = true + elseif objtype == "function" then + nodes_or_function_or_model(pos) + spawned = true + elseif objtype == "string" and pcall(loadstring(("return %s(...)"): + format(nodes_or_function_or_model)),pos) then + spawned = true else - tries = tries + 1 + biome_lib:dbg("Warning: Ignored invalid definition for object "..dump(nodes_or_function_or_model).." that was pointed at {"..dump(pos).."}") end + else + tries = tries + 1 end end end @@ -333,51 +380,52 @@ end -- a surface during the initial map read stage. function biome_lib:generate_block_with_air_checking() - if #biome_lib.blocklist_aircheck > 0 then + if #biome_lib.blocklist_aircheck == 0 then + return + end - local minp = biome_lib.blocklist_aircheck[1][1] - local maxp = biome_lib.blocklist_aircheck[1][2] + local minp = biome_lib.blocklist_aircheck[1][1] + local maxp = biome_lib.blocklist_aircheck[1][2] - -- use the block hash as a unique key into the surface nodes - -- tables, so that we can write the tables thread-safely. + -- use the block hash as a unique key into the surface nodes + -- tables, so that we can write the tables thread-safely. - local blockhash = minetest.hash_node_position(minp) + local blockhash = minetest.hash_node_position(minp) - if not biome_lib.surface_nodes_aircheck.blockhash then + if not biome_lib.surface_nodes_aircheck.blockhash then - if type(minetest.find_nodes_in_area_under_air) == "function" then -- use newer API call - biome_lib.surface_nodes_aircheck.blockhash = - minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck) - else - local search_area = minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_aircheck) + if type(minetest.find_nodes_in_area_under_air) == "function" then -- use newer API call + biome_lib.surface_nodes_aircheck.blockhash = + minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck) + else + local search_area = minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_aircheck) - -- search the generated block for air-bounded surfaces the slow way. + -- search the generated block for air-bounded surfaces the slow way. - biome_lib.surface_nodes_aircheck.blockhash = {} + biome_lib.surface_nodes_aircheck.blockhash = {} - for i = 1, #search_area do - local pos = search_area[i] - local p_top = { x=pos.x, y=pos.y+1, z=pos.z } - if minetest.get_node(p_top).name == "air" then - biome_lib.surface_nodes_aircheck.blockhash[#biome_lib.surface_nodes_aircheck.blockhash + 1] = pos - end + for i = 1, #search_area do + local pos = search_area[i] + local p_top = { x=pos.x, y=pos.y+1, z=pos.z } + if minetest.get_node(p_top).name == "air" then + biome_lib.surface_nodes_aircheck.blockhash[#biome_lib.surface_nodes_aircheck.blockhash + 1] = pos end end - biome_lib.actioncount_aircheck.blockhash = 1 + end + biome_lib.actioncount_aircheck.blockhash = 1 + else + if biome_lib.actioncount_aircheck.blockhash <= #biome_lib.actionslist_aircheck then + -- [1] is biome, [2] is node/function/model + biome_lib:populate_surfaces( + biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][1], + biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][2], + biome_lib.surface_nodes_aircheck.blockhash, true) + biome_lib.actioncount_aircheck.blockhash = biome_lib.actioncount_aircheck.blockhash + 1 else - if biome_lib.actioncount_aircheck.blockhash <= #biome_lib.actionslist_aircheck then - -- [1] is biome, [2] is node/function/model - biome_lib:populate_surfaces( - biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][1], - biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][2], - biome_lib.surface_nodes_aircheck.blockhash, true) - biome_lib.actioncount_aircheck.blockhash = biome_lib.actioncount_aircheck.blockhash + 1 - else - if biome_lib.surface_nodes_aircheck.blockhash then - table.remove(biome_lib.blocklist_aircheck, 1) - biome_lib.surface_nodes_aircheck.blockhash = nil - end + if biome_lib.surface_nodes_aircheck.blockhash then + table.remove(biome_lib.blocklist_aircheck, 1) + biome_lib.surface_nodes_aircheck.blockhash = nil end end end @@ -387,33 +435,34 @@ end -- checking for air during the initial map read stage. function biome_lib:generate_block_no_aircheck() - if #biome_lib.blocklist_no_aircheck > 0 then + if #biome_lib.blocklist_no_aircheck == 0 then + return + end - local minp = biome_lib.blocklist_no_aircheck[1][1] - local maxp = biome_lib.blocklist_no_aircheck[1][2] + local minp = biome_lib.blocklist_no_aircheck[1][1] + local maxp = biome_lib.blocklist_no_aircheck[1][2] - local blockhash = minetest.hash_node_position(minp) + local blockhash = minetest.hash_node_position(minp) - if not biome_lib.surface_nodes_no_aircheck.blockhash then + if not biome_lib.surface_nodes_no_aircheck.blockhash then - -- directly read the block to be searched into the chunk cache + -- directly read the block to be searched into the chunk cache - biome_lib.surface_nodes_no_aircheck.blockhash = - minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck) - biome_lib.actioncount_no_aircheck.blockhash = 1 + biome_lib.surface_nodes_no_aircheck.blockhash = + minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck) + biome_lib.actioncount_no_aircheck.blockhash = 1 + else + if biome_lib.actioncount_no_aircheck.blockhash <= #biome_lib.actionslist_no_aircheck then + biome_lib:populate_surfaces( + biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][1], + biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][2], + biome_lib.surface_nodes_no_aircheck.blockhash, false) + biome_lib.actioncount_no_aircheck.blockhash = biome_lib.actioncount_no_aircheck.blockhash + 1 else - if biome_lib.actioncount_no_aircheck.blockhash <= #biome_lib.actionslist_no_aircheck then - biome_lib:populate_surfaces( - biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][1], - biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][2], - biome_lib.surface_nodes_no_aircheck.blockhash, false) - biome_lib.actioncount_no_aircheck.blockhash = biome_lib.actioncount_no_aircheck.blockhash + 1 - else - if biome_lib.surface_nodes_no_aircheck.blockhash then - table.remove(biome_lib.blocklist_no_aircheck, 1) - biome_lib.surface_nodes_no_aircheck.blockhash = nil - end + if biome_lib.surface_nodes_no_aircheck.blockhash then + table.remove(biome_lib.blocklist_no_aircheck, 1) + biome_lib.surface_nodes_no_aircheck.blockhash = nil end end end @@ -432,21 +481,23 @@ end) -- "Play" them back, populating them with new stuff in the process minetest.register_globalstep(function(dtime) - if dtime < 0.2 -- don't attempt to populate if lag is already too high - and math.random(100) <= biome_lib.queue_run_ratio - and (#biome_lib.blocklist_aircheck > 0 or #biome_lib.blocklist_no_aircheck > 0) then - biome_lib.globalstep_start_time = minetest.get_us_time() - biome_lib.globalstep_runtime = 0 - while (#biome_lib.blocklist_aircheck > 0 or #biome_lib.blocklist_no_aircheck > 0) - and biome_lib.globalstep_runtime < 200000 do -- 0.2 seconds, in uS. - if #biome_lib.blocklist_aircheck > 0 then - biome_lib:generate_block_with_air_checking() - end - if #biome_lib.blocklist_no_aircheck > 0 then - biome_lib:generate_block_no_aircheck() - end - biome_lib.globalstep_runtime = minetest.get_us_time() - biome_lib.globalstep_start_time + if dtime >= 0.2 -- don't attempt to populate if lag is already too high + or math.random(100) > biome_lib.queue_run_ratio + or (#biome_lib.blocklist_aircheck == 0 and #biome_lib.blocklist_no_aircheck == 0) then + return + end + + biome_lib.globalstep_start_time = minetest.get_us_time() + biome_lib.globalstep_runtime = 0 + while (#biome_lib.blocklist_aircheck > 0 or #biome_lib.blocklist_no_aircheck > 0) + and biome_lib.globalstep_runtime < 200000 do -- 0.2 seconds, in uS. + if #biome_lib.blocklist_aircheck > 0 then + biome_lib:generate_block_with_air_checking() end + if #biome_lib.blocklist_no_aircheck > 0 then + biome_lib:generate_block_no_aircheck() + end + biome_lib.globalstep_runtime = minetest.get_us_time() - biome_lib.globalstep_start_time end end) @@ -454,24 +505,26 @@ end) -- to prevent unpopulated map areas minetest.register_on_shutdown(function() - if #biome_lib.blocklist_aircheck > 0 then - print("[biome_lib] Stand by, playing out the rest of the aircheck mapblock log") - print("(there are "..#biome_lib.blocklist_aircheck.." entries)...") - while true do - biome_lib:generate_block_with_air_checking(0.1) - if #biome_lib.blocklist_aircheck == 0 then return end - end + if #biome_lib.blocklist_aircheck == 0 then + return + end + + print("[biome_lib] Stand by, playing out the rest of the aircheck mapblock log") + print("(there are "..#biome_lib.blocklist_aircheck.." entries)...") + while #biome_lib.blocklist_aircheck > 0 do + biome_lib:generate_block_with_air_checking(0.1) end end) minetest.register_on_shutdown(function() - if #biome_lib.blocklist_no_aircheck > 0 then - print("[biome_lib] Stand by, playing out the rest of the no-aircheck mapblock log") - print("(there are "..#biome_lib.blocklist_no_aircheck.." entries)...") - while true do - biome_lib:generate_block_no_aircheck(0.1) - if #biome_lib.blocklist_no_aircheck == 0 then return end - end + if #biome_lib.blocklist_aircheck == 0 then + return + end + + print("[biome_lib] Stand by, playing out the rest of the no-aircheck mapblock log") + print("(there are "..#biome_lib.blocklist_no_aircheck.." entries)...") + while #biome_lib.blocklist_no_aircheck > 0 do + biome_lib:generate_block_no_aircheck(0.1) end end) @@ -581,71 +634,6 @@ function biome_lib:spawn_on_surfaces(sd,sp,sr,sc,ss,sa) }) end --- The growing ABM - -function biome_lib:grow_plants(opts) - - local options = opts - - options.height_limit = options.height_limit or 5 - options.ground_nodes = options.ground_nodes or { "default:dirt_with_grass" } - options.grow_nodes = options.grow_nodes or { "default:dirt_with_grass" } - options.seed_diff = options.seed_diff or 0 - - local n - - if type(options.grow_plant) == "table" then - n = "multi: "..options.grow_plant[1]..", ..." - else - n = options.grow_plant - end - - options.label = options.label or "biome_lib grow_plants(): "..n - - if options.grow_delay*time_scale >= 1 then - options.interval = options.grow_delay*time_scale - else - options.interval = 1 - end - - minetest.register_abm({ - nodenames = { options.grow_plant }, - interval = options.interval, - chance = options.grow_chance, - label = options.label, - action = function(pos, node, active_object_count, active_object_count_wider) - local p_top = {x=pos.x, y=pos.y+1, z=pos.z} - local p_bot = {x=pos.x, y=pos.y-1, z=pos.z} - local n_top = minetest.get_node(p_top) - local n_bot = minetest.get_node(p_bot) - local root_node = minetest.get_node({x=pos.x, y=pos.y-options.height_limit, z=pos.z}) - local walldir = nil - if options.need_wall and options.verticals_list then - walldir = biome_lib:find_adjacent_wall(p_top, options.verticals_list, options.choose_random_wall) - end - if (n_top.name == "air" or n_top.name == "default:snow") - and (not options.need_wall or (options.need_wall and walldir)) then - -- corner case for changing short junglegrass - -- to dry shrub in desert - if n_bot.name == options.dry_early_node and options.grow_plant == "junglegrass:short" then - minetest.swap_node(pos, { name = "default:dry_shrub" }) - - elseif options.grow_vertically and walldir then - if biome_lib:search_downward(pos, options.height_limit, options.ground_nodes) then - minetest.swap_node(p_top, { name = options.grow_plant, param2 = walldir}) - end - - elseif not options.grow_result and not options.grow_function then - minetest.swap_node(pos, biome_lib.air) - - else - biome_lib:replace_object(pos, options.grow_result, options.grow_function, options.facedir, options.seed_diff) - end - end - end - }) -end - -- Function to decide how to replace a plant - either grow it, replace it with -- a tree, run a function, or die with an error. @@ -675,71 +663,12 @@ function biome_lib:replace_object(pos, replacement, grow_function, walldir, seed end end --- function to decide if a node has a wall that's in verticals_list{} --- returns wall direction of valid node, or nil if invalid. -function biome_lib:find_adjacent_wall(pos, verticals, randomflag) - local verts = dump(verticals) - if randomflag then - local walltab = {} - - if string.find(verts, minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name) then walltab[#walltab + 1] = 3 end - if string.find(verts, minetest.get_node({ x=pos.x+1, y=pos.y, z=pos.z }).name) then walltab[#walltab + 1] = 2 end - if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z-1 }).name) then walltab[#walltab + 1] = 5 end - if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z+1 }).name) then walltab[#walltab + 1] = 4 end - if #walltab > 0 then return walltab[math.random(1, #walltab)] end +dofile(biome_lib.modpath .. "/search_functions.lua") +dofile(biome_lib.modpath .. "/growth.lua") - else - if string.find(verts, minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name) then return 3 end - if string.find(verts, minetest.get_node({ x=pos.x+1, y=pos.y, z=pos.z }).name) then return 2 end - if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z-1 }).name) then return 5 end - if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z+1 }).name) then return 4 end - end - return nil -end --- Function to search downward from the given position, looking for the first --- node that matches the ground table. Returns the new position, or nil if --- height limit is exceeded before finding it. - -function biome_lib:search_downward(pos, heightlimit, ground) - for i = 0, heightlimit do - if string.find(dump(ground), minetest.get_node({x=pos.x, y=pos.y-i, z = pos.z}).name) then - return {x=pos.x, y=pos.y-i, z = pos.z} - end - end - return false -end - -function biome_lib:find_open_side(pos) - if minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name == "air" then - return {newpos = { x=pos.x-1, y=pos.y, z=pos.z }, facedir = 2} - end - if minetest.get_node({ x=pos.x+1, y=pos.y, z=pos.z }).name == "air" then - return {newpos = { x=pos.x+1, y=pos.y, z=pos.z }, facedir = 3} - end - if minetest.get_node({ x=pos.x, y=pos.y, z=pos.z-1 }).name == "air" then - return {newpos = { x=pos.x, y=pos.y, z=pos.z-1 }, facedir = 4} - end - if minetest.get_node({ x=pos.x, y=pos.y, z=pos.z+1 }).name == "air" then - return {newpos = { x=pos.x, y=pos.y, z=pos.z+1 }, facedir = 5} - end - return nil -end - --- spawn_tree() on generate is routed through here so that other mods can hook --- into it. - -function biome_lib:generate_tree(pos, nodes_or_function_or_model) - minetest.spawn_tree(pos, nodes_or_function_or_model) -end - --- and this one's for the call used in the growing code - -function biome_lib:grow_tree(pos, nodes_or_function_or_model) - minetest.spawn_tree(pos, nodes_or_function_or_model) -end -- Check for infinite stacks diff --git a/search_functions.lua b/search_functions.lua new file mode 100644 index 0000000..92a82ea --- /dev/null +++ b/search_functions.lua @@ -0,0 +1,53 @@ + +-- function to decide if a node has a wall that's in verticals_list{} +-- returns wall direction of valid node, or nil if invalid. + +function biome_lib:find_adjacent_wall(pos, verticals, randomflag) + local verts = dump(verticals) + if randomflag then + local walltab = {} + + if string.find(verts, minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name) then walltab[#walltab + 1] = 3 end + if string.find(verts, minetest.get_node({ x=pos.x+1, y=pos.y, z=pos.z }).name) then walltab[#walltab + 1] = 2 end + if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z-1 }).name) then walltab[#walltab + 1] = 5 end + if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z+1 }).name) then walltab[#walltab + 1] = 4 end + + if #walltab > 0 then return walltab[math.random(1, #walltab)] end + + else + if string.find(verts, minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name) then return 3 end + if string.find(verts, minetest.get_node({ x=pos.x+1, y=pos.y, z=pos.z }).name) then return 2 end + if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z-1 }).name) then return 5 end + if string.find(verts, minetest.get_node({ x=pos.x , y=pos.y, z=pos.z+1 }).name) then return 4 end + end + return nil +end + +-- Function to search downward from the given position, looking for the first +-- node that matches the ground table. Returns the new position, or nil if +-- height limit is exceeded before finding it. + +function biome_lib:search_downward(pos, heightlimit, ground) + for i = 0, heightlimit do + if string.find(dump(ground), minetest.get_node({x=pos.x, y=pos.y-i, z = pos.z}).name) then + return {x=pos.x, y=pos.y-i, z = pos.z} + end + end + return false +end + +function biome_lib:find_open_side(pos) + if minetest.get_node({ x=pos.x-1, y=pos.y, z=pos.z }).name == "air" then + return {newpos = { x=pos.x-1, y=pos.y, z=pos.z }, facedir = 2} + end + if minetest.get_node({ x=pos.x+1, y=pos.y, z=pos.z }).name == "air" then + return {newpos = { x=pos.x+1, y=pos.y, z=pos.z }, facedir = 3} + end + if minetest.get_node({ x=pos.x, y=pos.y, z=pos.z-1 }).name == "air" then + return {newpos = { x=pos.x, y=pos.y, z=pos.z-1 }, facedir = 4} + end + if minetest.get_node({ x=pos.x, y=pos.y, z=pos.z+1 }).name == "air" then + return {newpos = { x=pos.x, y=pos.y, z=pos.z+1 }, facedir = 5} + end + return nil +end