1
0
mirror of https://github.com/mt-mods/biome_lib.git synced 2025-06-29 06:40:44 +02:00

28 Commits

Author SHA1 Message Date
53f55cdb4d make sure a listed block is indeed loaded before writing to it
(in case the mapgen takes long enough on a server that players
have wandered off and the server has unloaded blocks)

for each block queued, make sure it and all 6 of its neighbors are
actually loaded before populating, else move the block to the end of the
queue.
2021-04-05 12:46:06 -04:00
8ecb401309 fix a couple of missing = signs 2021-04-05 12:44:21 -04:00
9ed4858518 take the integer of that last division I added
always round up.  otherwise it'll break the `for` loop
if a mod supplies a value not divisible by 25 :-)
2021-04-01 09:02:41 -04:00
26dbbb5a67 since we split to mapblocks now,
biome.max_count as received from mods has to be scaled appropriately
as mods expect this value to be relative to the usual
chunk size of 80x80 = 6400 nodes' horizontal area.
2021-04-01 08:32:45 -04:00
ec0a0f0c3b add some debugging messages 2021-04-01 07:18:28 -04:00
e92361675f remove the old-plantslib-api compat thing 2021-04-01 06:24:09 -04:00
7f1fec6ae0 add compatibility for games other than minetest_game 2021-04-01 05:47:22 -04:00
f569bb1fbd fix outdated "plantlife" message
and some redundancy
2021-04-01 05:03:29 -04:00
f2a807b814 make debug mode a proper minetest.conf setting 2021-04-01 05:02:07 -04:00
9b7705c380 move the "registered N actions on M surfaces" message to debug 2021-04-01 04:59:57 -04:00
c9f6235815 Add a function to print active/pending block counts to debug 2021-04-01 04:50:03 -04:00
ddd88613e0 trim some blank lines 2021-04-01 04:34:07 -04:00
27cd07cb36 Fix overlap in the split-to-blocks feature 2021-04-01 04:33:53 -04:00
ac8738d837 use mod.conf instead of depends.txt 2021-03-29 19:56:09 -04:00
0005af6022 fix my name 2021-03-29 19:53:26 -04:00
228296411e split generated map chunks into mapblock-sized pieces as they're logged
to reduce lag/latency at a tiny cost of overall throughput.
2021-03-29 05:19:52 -04:00
353ca0cbd4 optimize some blocklist/actionlist checks 2021-03-28 23:10:31 -04:00
d65c72d48b don't need the old non-air-above find nodes call anymore
only very old minetest engine lacks the air-above api call
2021-03-25 19:27:38 -04:00
10a1089767 add minimum minetest version key for contentdb 2020-06-03 13:00:01 -04:00
5a910875af fix deprecated function call 2020-04-15 16:59:40 -04:00
a96f015ce9 properly-limit abm-based growth to just the proper surfaces
(if no surface list is given, just grow on anything)
2019-05-22 20:02:30 -04:00
d72e15f1d0 get rid of old junglegrass -> dry shrub "corner case" 2019-05-22 19:28:13 -04:00
29899f2d36 Fix call to nil (function) vector.copy
Either vector.new or table.copy
NOT LIKE THIS
2019-01-07 19:37:16 +01:00
0837ff7fd2 Sync with world temperature if available 2019-01-02 23:08:07 +01:00
ff9ce531d6 Cleanup ABM callback 2019-01-02 22:29:59 +01:00
fa304f9d18 Fixes 2019-01-02 22:06:07 +01:00
b7d69a8487 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
2019-01-02 20:35:39 +01:00
f056d6d76e add descriptive names to ABM calls
can be specified by label= in the biome table
else biome_lib will try to make up something useful
fix #1
2018-12-01 10:51:18 -05:00
7 changed files with 647 additions and 396 deletions

59
API.txt
View File

@ -72,6 +72,7 @@ biome = {
---- most likely want to use at least some of these to limit how and ---- most likely want to use at least some of these to limit how and
---- where your objects are spawned. ---- where your objects are spawned.
label = string, -- set this to identify the ABM for Minetest's profiler
avoid_nodes = {table}, -- same meaning as savoid, above avoid_nodes = {table}, -- same meaning as savoid, above
avoid_radius = num, -- same as sradius avoid_radius = num, -- same as sradius
seed_diff = num, -- The Perlin seed difference value passed to the seed_diff = num, -- The Perlin seed difference value passed to the
@ -342,14 +343,18 @@ into something else over time. This function has no return value, and accepts
a biome definition table as the only parameter. These are defined like so: a biome definition table as the only parameter. These are defined like so:
options = { options = {
grow_plant = "string", -- Name of the node to be grown into something label = string, -- set this to identify the ABM for Minetest's
-- else. This value is passed to the ABM as the -- profiler. If not set, biome_lib will set it to
-- "nodenames" parameter, so it is the plants -- "biome_lib grow_plants(): " appended with the node
-- themselves that are the ABM trigger, rather than -- in grow_plant (or the first item if it's a table)
grow_plant = "string" or {table}, -- Name(s) of the node(s) to be grown
-- into something else. This value is passed to the
-- ABM as the "nodenames" parameter, so the plants
-- themselves are the ABM trigger, rather than
-- the ground they spawned on. A plant will only grow -- the ground they spawned on. A plant will only grow
-- if the node above it is air. Can also be a table, -- if the node above it is air. If you use a table,
-- but note that all nodes referenced therein will be -- note that all nodes referenced therein will be
-- grown into the same object. -- grown into the same final object.
grow_delay = num, -- Passed as the ABM "interval" parameter, as with grow_delay = num, -- Passed as the ABM "interval" parameter, as with
-- spawning. -- spawning.
grow_chance = num, -- Passed as the ABM "chance" parameter. grow_chance = num, -- Passed as the ABM "chance" parameter.
@ -583,3 +588,43 @@ And this particular one is mapped slightly differently from the others:
(Note the +150 and +50 offsets) (Note the +150 and +50 offsets)
==================
Default game nodes
==================
Although this project was intended to be used with minetest_game, it can be
configured to work with something else instead. All you need to do is provide
the names of the nodes in your game you want biome_lib's internals to use.
Put these settings in your game's minetest.conf (or your client's own config,
if desired). You'll need to set all of them.
biome_lib_default_grow_through_nodes
Comma-separated list of things that a spawned node is allowed to grow
through. Air is always added to whatever you specify.
Default: air, default:snow
biome_lib_default_water_nodes
Comma-separated list of nodes that should be treated as water for the sake
of looking for neighboring "wet" ground.
Default: default:water_source, default:water_flowing,
default:river_water_source, default:river_water_flowing
biome_lib_default_wet_surfaces
Comma-separated list of nodes that should be considered "wet" if one of
the aforementioned water nodes is nearby.
Default: default:dirt, default:dirt_with_grass, default:sand
biome_lib_default_grow_nodes
Comma-separated list of nodes that something must be sitting on to be
able to actively change from one thing to another (such as a sapling
growing into a tree), to be used for ALL growable nodes, if the calling
mod doesn't provide its own lists.
Default: "default:dirt_with_grass"
biome_lib_default_ground_nodes
Comma-separated list of nodes to use as the "root" of something that can
gradually climb up a wall, to be used for ALL such nodes, if the calling
mod doesn't provide its own lists.
Default: "default:dirt_with_grass"

View File

@ -12,7 +12,7 @@ Both mapgen-based spawning and ABM-based spawning is supported. Growing code is
It is primarily intended for mapgen v6, but it should work fine when used with mapgen v7. It is primarily intended for mapgen v6, but it should work fine when used with mapgen v7.
**Dependencies**: default from minetest_game **Dependencies:** nothing, but if you don't use `minetest_game`, you'll need to supply some settings (see API.txt).
**Recommends**: [Plantlife Modpack](https://github.com/minetest-mods/plantlife_modpack), **Recommends**: [Plantlife Modpack](https://github.com/minetest-mods/plantlife_modpack),
[More Trees](https://github.com/minetest-mods/moretrees) [More Trees](https://github.com/minetest-mods/moretrees)

View File

@ -1,3 +0,0 @@
default
intllib?

90
growth.lua Normal file
View File

@ -0,0 +1,90 @@
local time_scale = ...
-- The growing ABM
function biome_lib.check_surface(name, nodes)
if not nodes then return true end
if type(nodes) == "string" then return nodes == name end
if nodes.set and nodes[name] then
return true
else
for _, n in ipairs(nodes) do
if name == n then return true end
end
end
return false
end
function biome_lib:grow_plants(opts)
local options = opts
options.height_limit = options.height_limit or 5
options.ground_nodes = options.ground_nodes or biome_lib.default_ground_nodes
options.grow_nodes = options.grow_nodes or biome_lib.default_grow_nodes
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 biome_lib.default_grow_through_nodes[n_top.name]
and (not options.need_wall or (options.need_wall and walldir)) then
if 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 biome_lib.check_surface(n_bot.name, options.grow_nodes) then
if 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
})
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

551
init.lua
View File

@ -1,4 +1,4 @@
-- Biome library mod by Vanessa Ezekowitz -- Biome library mod by VanessaE
-- --
-- I got the temperature map idea from "hmmmm", values used for it came from -- I got the temperature map idea from "hmmmm", values used for it came from
-- Splizard's snow mod. -- Splizard's snow mod.
@ -9,8 +9,6 @@
biome_lib = {} biome_lib = {}
biome_lib.air = {name = "air"} biome_lib.air = {name = "air"}
plantslib = setmetatable({}, { __index=function(t,k) print("Use of deprecated function:", k) return biome_lib[k] end })
biome_lib.blocklist_aircheck = {} biome_lib.blocklist_aircheck = {}
biome_lib.blocklist_no_aircheck = {} biome_lib.blocklist_no_aircheck = {}
@ -32,6 +30,41 @@ biome_lib.total_no_aircheck_calls = 0
biome_lib.queue_run_ratio = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or 100 biome_lib.queue_run_ratio = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or 100
local function tableize(s)
return string.split(string.trim(string.gsub(s, " ", "")))
end
local c1 = minetest.settings:get("biome_lib_default_grow_through_nodes")
biome_lib.default_grow_through_nodes = {["air"] = true}
if c1 then
for _, i in ipairs(tableize(c1)) do
biome_lib.default_grow_through_nodes[i] = true
end
else
biome_lib.default_grow_through_nodes["default:snow"] = true
end
local c2 = minetest.settings:get("biome_lib_default_water_nodes")
biome_lib.default_water_nodes = {}
if c2 then
for _, i in ipairs(tableize(c2)) do
biome_lib.default_water_nodes[i] = true
end
else
biome_lib.default_water_nodes["default:water_source"] = true
biome_lib.default_water_nodes["default:water_flowing"] = true
biome_lib.default_water_nodes["default:river_water_source"] = true
biome_lib.default_water_nodes["default:river_water_flowing"] = true
end
local c3 = minetest.settings:get("biome_lib_default_wet_surfaces")
local c4 = minetest.settings:get("biome_lib_default_ground_nodes")
local c5 = minetest.settings:get("biome_lib_default_grow_nodes")
biome_lib.default_wet_surfaces = c3 and tableize(c3) or {"default:dirt", "default:dirt_with_grass", "default:sand"}
biome_lib.default_ground_nodes = c4 and tableize(c4) or {"default:dirt_with_grass"}
biome_lib.default_grow_nodes = c5 and tableize(c5) or {"default:dirt_with_grass"}
-- Boilerplate to support localized strings if intllib mod is installed. -- Boilerplate to support localized strings if intllib mod is installed.
local S local S
if minetest.global_exists("intllib") then if minetest.global_exists("intllib") then
@ -45,12 +78,12 @@ else
end end
biome_lib.intllib = S biome_lib.intllib = S
local DEBUG = false --... except if you want to spam the console with debugging info :-) local DEBUG = minetest.settings:get_bool("biome_lib_debug", false)
function biome_lib:dbg(msg) function biome_lib:dbg(msg)
if DEBUG then if DEBUG then
print("[Plantlife] "..msg) print("[Biome Lib] "..msg)
minetest.log("verbose", "[Plantlife] "..msg) minetest.log("verbose", "[Biome Lib] "..msg)
end end
end end
@ -84,6 +117,22 @@ biome_lib.perlin_humidity = PerlinNoise(humidity_seeddiff, humidity_octaves, hum
-- Local functions -- Local functions
local function get_biome_data(pos, perlin_fertile)
local fertility = perlin_fertile:get_2d({x=pos.x, y=pos.z})
if type(minetest.get_biome_data) == "function" then
local data = minetest.get_biome_data(pos)
if data then
return fertility, data.heat / 100, data.humidity / 100
end
end
local temperature = biome_lib.perlin_temperature:get2d({x=pos.x, y=pos.z})
local humidity = biome_lib.perlin_humidity:get2d({x=pos.x+150, y=pos.z+50})
return fertility, temperature, humidity
end
function biome_lib:is_node_loaded(node_pos) function biome_lib:is_node_loaded(node_pos)
local n = minetest.get_node_or_nil(node_pos) local n = minetest.get_node_or_nil(node_pos)
if (not n) or (n.name == "ignore") then if (not n) or (n.name == "ignore") then
@ -109,7 +158,7 @@ function biome_lib:set_defaults(biome)
biome.near_nodes_size = biome.near_nodes_size or 0 biome.near_nodes_size = biome.near_nodes_size or 0
biome.near_nodes_count = biome.near_nodes_count or 1 biome.near_nodes_count = biome.near_nodes_count or 1
biome.rarity = biome.rarity or 50 biome.rarity = biome.rarity or 50
biome.max_count = biome.max_count or 5 biome.max_count = biome.max_count or 125
if biome.check_air ~= false then biome.check_air = true end if biome.check_air ~= false then biome.check_air = true end
-- specific to abm spawner -- specific to abm spawner
@ -198,21 +247,27 @@ function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model)
end end
end end
function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes, checkair) -- Function to check whether a position matches the given biome definition
-- Returns true when the surface can be populated
biome_lib:set_defaults(biome) local function populate_single_surface(biome, pos, perlin_fertile_area, checkair)
-- filter stage 1 - find nodes from the supplied surfaces that are within the current biome.
local in_biome_nodes = {}
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 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}) if math.random(1, 100) <= biome.rarity then
local noise3 = biome_lib.perlin_humidity:get2d({x=pos.x+150, y=pos.z+50}) return
end
local fertility, temperature, humidity = get_biome_data(pos, perlin_fertile_area)
local pos_biome_ok = pos.y >= biome.min_elevation and pos.y <= biome.max_elevation
and fertility > biome.plantlife_limit
and temperature <= biome.temp_min and temperature >= biome.temp_max
and humidity <= biome.humidity_min and humidity >= 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 biome_surfaces_string = dump(biome.surface)
local surface_ok = false local surface_ok = false
@ -231,24 +286,61 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
end end
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 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 surface_ok = true
end end
if surface_ok if not surface_ok then
and pos.y >= biome.min_elevation return -- Surface does not match the given node group/name
and pos.y <= biome.max_elevation end
and noise1 > biome.plantlife_limit
and noise2 <= biome.temp_min if checkair and minetest.get_node(p_top).name ~= "air" then
and noise2 >= biome.temp_max return
and noise3 <= biome.humidity_min end
and noise3 >= biome.humidity_max
and (not checkair or minetest.get_node(p_top).name == "air") if biome.below_nodes and
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) not string.find(dump(biome.below_nodes),
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) minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name
and math.random(1,100) > biome.rarity ) then
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) ) return -- Node below does not match
then 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)
local items_added = 0
biome_lib:set_defaults(biome)
-- filter stage 1 - find nodes from the supplied surfaces that are within the current biome.
local in_biome_nodes = {}
local perlin_fertile_area = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale)
for i = 1, #snodes do
local pos = vector.new(snodes[i])
if populate_single_surface(biome, pos, perlin_fertile_area, checkair) then
in_biome_nodes[#in_biome_nodes + 1] = pos in_biome_nodes[#in_biome_nodes + 1] = pos
end end
end end
@ -257,8 +349,11 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
local num_in_biome_nodes = #in_biome_nodes local num_in_biome_nodes = #in_biome_nodes
if num_in_biome_nodes > 0 then if num_in_biome_nodes == 0 then
for i = 1, math.min(biome.max_count, num_in_biome_nodes) do return 0
end
for i = 1, math.min(math.ceil(biome.max_count/25), num_in_biome_nodes) do
local tries = 0 local tries = 0
local spawned = false local spawned = false
while tries < 2 and not spawned do while tries < 2 and not spawned do
@ -268,7 +363,9 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
end end
local p_top = { x = pos.x, y = pos.y + 1, z = pos.z } 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 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 if biome.delete_above then
minetest.swap_node(p_top, biome_lib.air) 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) minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z}, biome_lib.air)
@ -325,15 +422,30 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
tries = tries + 1 tries = tries + 1
end end
end end
if spawned then items_added = items_added + 1 end
end end
return items_added
end end
-- make sure the target block and all 6 neighbors are actually loaded.
local function confirm_block_surroundings(pos)
return minetest.get_node_or_nil(pos)
and minetest.get_node_or_nil({ x=pos.x-16, y=pos.y, z=pos.z })
and minetest.get_node_or_nil({ x=pos.x+16, y=pos.y, z=pos.z })
and minetest.get_node_or_nil({ x=pos.x, y=pos.y-16, z=pos.z })
and minetest.get_node_or_nil({ x=pos.x, y=pos.y+16, z=pos.z })
and minetest.get_node_or_nil({ x=pos.x, y=pos.y, z=pos.z-16 })
and minetest.get_node_or_nil({ x=pos.x, y=pos.y, z=pos.z+16 })
end end
-- Primary mapgen spawner, for mods that can work with air checking enabled on -- Primary mapgen spawner, for mods that can work with air checking enabled on
-- a surface during the initial map read stage. -- a surface during the initial map read stage.
function biome_lib:generate_block_with_air_checking() function biome_lib:generate_block_with_air_checking(shutdown)
if #biome_lib.blocklist_aircheck > 0 then if not biome_lib.blocklist_aircheck[1] then
return
end
local minp = biome_lib.blocklist_aircheck[1][1] local minp = biome_lib.blocklist_aircheck[1][1]
local maxp = biome_lib.blocklist_aircheck[1][2] local maxp = biome_lib.blocklist_aircheck[1][2]
@ -343,42 +455,33 @@ function biome_lib:generate_block_with_air_checking()
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 -- read it into the block cache
if type(minetest.find_nodes_in_area_under_air) == "function" then -- use newer API call
biome_lib.surface_nodes_aircheck.blockhash = biome_lib.surface_nodes_aircheck.blockhash =
minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck) 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.
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
end
end
biome_lib.actioncount_aircheck.blockhash = 1 biome_lib.actioncount_aircheck.blockhash = 1
if #biome_lib.surface_nodes_aircheck.blockhash > 0 then
biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp).." added, with "..#biome_lib.surface_nodes_aircheck.blockhash.." surface nodes detected.")
end
elseif not shutdown and not confirm_block_surroundings(minp) and #biome_lib.blocklist_aircheck > 1 then
biome_lib.blocklist_aircheck[#biome_lib.blocklist_aircheck+1] = biome_lib.blocklist_aircheck[1]
table.remove(biome_lib.blocklist_aircheck, 1)
biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp).." had an unloaded neighbor, moved it to the end of the queue.")
else else
if biome_lib.actioncount_aircheck.blockhash <= #biome_lib.actionslist_aircheck then if biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash] then
-- [1] is biome, [2] is node/function/model -- [1] is biome, [2] is node/function/model
biome_lib:populate_surfaces( local added = biome_lib:populate_surfaces(
biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][1], biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][1],
biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][2], biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][2],
biome_lib.surface_nodes_aircheck.blockhash, true) biome_lib.surface_nodes_aircheck.blockhash, true)
if added > 0 then
biome_lib:dbg("Ran biome_lib:populate_surfaces for block at "..minetest.pos_to_string(minp)..
". Entry #"..biome_lib.actioncount_aircheck.blockhash.." added "..added.." items.")
end
biome_lib.actioncount_aircheck.blockhash = biome_lib.actioncount_aircheck.blockhash + 1 biome_lib.actioncount_aircheck.blockhash = biome_lib.actioncount_aircheck.blockhash + 1
else else
if biome_lib.surface_nodes_aircheck.blockhash then
table.remove(biome_lib.blocklist_aircheck, 1) table.remove(biome_lib.blocklist_aircheck, 1)
biome_lib.surface_nodes_aircheck.blockhash = nil biome_lib.surface_nodes_aircheck.blockhash = nil
end biome_lib.actioncount_aircheck.blockhash = nil
end
end end
end end
end end
@ -386,8 +489,10 @@ end
-- Secondary mapgen spawner, for mods that require disabling of -- Secondary mapgen spawner, for mods that require disabling of
-- checking for air during the initial map read stage. -- checking for air during the initial map read stage.
function biome_lib:generate_block_no_aircheck() function biome_lib:generate_block_no_aircheck(shutdown)
if #biome_lib.blocklist_no_aircheck > 0 then if not biome_lib.blocklist_no_aircheck[1] then
return
end
local minp = biome_lib.blocklist_no_aircheck[1][1] local minp = biome_lib.blocklist_no_aircheck[1][1]
local maxp = biome_lib.blocklist_no_aircheck[1][2] local maxp = biome_lib.blocklist_no_aircheck[1][2]
@ -395,83 +500,75 @@ function biome_lib:generate_block_no_aircheck()
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
biome_lib.surface_nodes_no_aircheck.blockhash = biome_lib.surface_nodes_no_aircheck.blockhash =
minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck) minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck)
biome_lib.actioncount_no_aircheck.blockhash = 1 biome_lib.actioncount_no_aircheck.blockhash = 1
elseif not shutdown and not confirm_block_surroundings(minp) and #biome_lib.blocklist_no_aircheck > 1 then
biome_lib.blocklist_no_aircheck[#biome_lib.blocklist_no_aircheck+1] = biome_lib.blocklist_no_aircheck[1]
table.remove(biome_lib.blocklist_no_aircheck, 1)
else else
if biome_lib.actioncount_no_aircheck.blockhash <= #biome_lib.actionslist_no_aircheck then if biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash] then
biome_lib:populate_surfaces( 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][1],
biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][2], biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][2],
biome_lib.surface_nodes_no_aircheck.blockhash, false) biome_lib.surface_nodes_no_aircheck.blockhash, false)
biome_lib.actioncount_no_aircheck.blockhash = biome_lib.actioncount_no_aircheck.blockhash + 1 biome_lib.actioncount_no_aircheck.blockhash = biome_lib.actioncount_no_aircheck.blockhash + 1
else else
if biome_lib.surface_nodes_no_aircheck.blockhash then
table.remove(biome_lib.blocklist_no_aircheck, 1) table.remove(biome_lib.blocklist_no_aircheck, 1)
biome_lib.surface_nodes_no_aircheck.blockhash = nil biome_lib.surface_nodes_no_aircheck.blockhash = nil
biome_lib.actioncount_no_aircheck.blockhash = nil
end end
end end
end end
end
end
-- "Record" the chunks being generated by the core mapgen
minetest.register_on_generated(function(minp, maxp, blockseed)
biome_lib.blocklist_aircheck[#biome_lib.blocklist_aircheck + 1] = { minp, maxp }
end)
minetest.register_on_generated(function(minp, maxp, blockseed)
biome_lib.blocklist_no_aircheck[#biome_lib.blocklist_no_aircheck + 1] = { minp, maxp }
end)
-- "Play" them back, populating them with new stuff in the process -- "Play" them back, populating them with new stuff in the process
local step_duration = tonumber(minetest.settings:get("dedicated_server_step"))
minetest.register_globalstep(function(dtime) minetest.register_globalstep(function(dtime)
if dtime < 0.2 -- don't attempt to populate if lag is already too high if dtime >= step_duration + 0.1 -- don't attempt to populate if lag is already too high
and math.random(100) <= biome_lib.queue_run_ratio or math.random(100) > biome_lib.queue_run_ratio
and (#biome_lib.blocklist_aircheck > 0 or #biome_lib.blocklist_no_aircheck > 0) then 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_start_time = minetest.get_us_time()
biome_lib.globalstep_runtime = 0 biome_lib.globalstep_runtime = 0
while (#biome_lib.blocklist_aircheck > 0 or #biome_lib.blocklist_no_aircheck > 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. and biome_lib.globalstep_runtime < 200000 do -- 0.2 seconds, in uS.
if #biome_lib.blocklist_aircheck > 0 then if #biome_lib.blocklist_aircheck > 0 then
biome_lib:generate_block_with_air_checking() biome_lib:generate_block_with_air_checking(false)
end end
if #biome_lib.blocklist_no_aircheck > 0 then if #biome_lib.blocklist_no_aircheck > 0 then
biome_lib:generate_block_no_aircheck() biome_lib:generate_block_no_aircheck(false)
end end
biome_lib.globalstep_runtime = minetest.get_us_time() - biome_lib.globalstep_start_time biome_lib.globalstep_runtime = minetest.get_us_time() - biome_lib.globalstep_start_time
end end
end
end) end)
-- Play out the entire log all at once on shutdown -- Play out the entire log all at once on shutdown
-- to prevent unpopulated map areas -- to prevent unpopulated map areas
minetest.register_on_shutdown(function() minetest.register_on_shutdown(function()
if #biome_lib.blocklist_aircheck > 0 then if #biome_lib.blocklist_aircheck == 0 then
return
end
print("[biome_lib] Stand by, playing out the rest of the aircheck mapblock log") print("[biome_lib] Stand by, playing out the rest of the aircheck mapblock log")
print("(there are "..#biome_lib.blocklist_aircheck.." entries)...") print("(there are "..#biome_lib.blocklist_aircheck.." entries)...")
while true do while #biome_lib.blocklist_aircheck > 0 do
biome_lib:generate_block_with_air_checking(0.1) biome_lib:generate_block_with_air_checking(true)
if #biome_lib.blocklist_aircheck == 0 then return end
end
end end
end) end)
minetest.register_on_shutdown(function() minetest.register_on_shutdown(function()
if #biome_lib.blocklist_no_aircheck > 0 then 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("[biome_lib] Stand by, playing out the rest of the no-aircheck mapblock log")
print("(there are "..#biome_lib.blocklist_no_aircheck.." entries)...") print("(there are "..#biome_lib.blocklist_no_aircheck.." entries)...")
while true do while #biome_lib.blocklist_no_aircheck > 0 do
biome_lib:generate_block_no_aircheck(0.1) biome_lib:generate_block_no_aircheck(true)
if #biome_lib.blocklist_no_aircheck == 0 then return end
end
end end
end) end)
@ -501,44 +598,94 @@ function biome_lib:spawn_on_surfaces(sd,sp,sr,sc,ss,sa)
biome_lib:set_defaults(biome) biome_lib:set_defaults(biome)
biome.spawn_plants_count = #(biome.spawn_plants) biome.spawn_plants_count = #(biome.spawn_plants)
local n
if type(biome.spawn_plants) == "table" then
n = "random: "..biome.spawn_plants[1]..", ..."
else
n = biome.spawn_plants
end
biome.label = biome.label or "biome_lib spawn_on_surfaces(): "..n
minetest.register_abm({ minetest.register_abm({
nodenames = biome.spawn_surfaces, nodenames = biome.spawn_surfaces,
interval = biome.interval, interval = biome.interval,
chance = biome.spawn_chance, chance = biome.spawn_chance,
neighbors = biome.neighbors, neighbors = biome.neighbors,
label = biome.label,
action = function(pos, node, active_object_count, active_object_count_wider) 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_top = { x = pos.x, y = pos.y + 1, z = pos.z }
local n_top = minetest.get_node(p_top) local n_top = minetest.get_node(p_top)
local perlin_fertile_area = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale) local perlin_fertile_area = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale)
local noise1 = perlin_fertile_area:get2d({x=p_top.x, y=p_top.z})
local noise2 = biome_lib.perlin_temperature:get2d({x=p_top.x, y=p_top.z}) local fertility, temperature, humidity = get_biome_data(pos, perlin_fertile_area)
local noise3 = biome_lib.perlin_humidity:get2d({x=p_top.x+150, y=p_top.z+50})
if noise1 > biome.plantlife_limit local pos_biome_ok = pos.y >= biome.min_elevation and pos.y <= biome.max_elevation
and noise2 <= biome.temp_min and fertility > biome.plantlife_limit
and noise2 >= biome.temp_max and temperature <= biome.temp_min and temperature >= biome.temp_max
and noise3 <= biome.humidity_min and humidity <= biome.humidity_min and humidity >= biome.humidity_max
and noise3 >= biome.humidity_max and biome_lib:is_node_loaded(p_top)
and biome_lib:is_node_loaded(p_top) then
if not pos_biome_ok then
return -- Outside of biome
end
local n_light = minetest.get_node_light(p_top, nil) local n_light = minetest.get_node_light(p_top, nil)
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)) if n_light < biome.light_min or n_light > biome.light_max then
and n_light >= biome.light_min return -- Too dark or too bright
and n_light <= biome.light_max end
and (not(biome.neighbors and 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 and biome.near_nodes_count and biome.near_nodes_size) 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) if biome.avoid_nodes and biome.avoid_radius and minetest.find_node_near(
and (not(biome.air_count and biome.air_size) or #(minetest.find_nodes_in_area({x=p_top.x-biome.air_size, y=p_top.y, z=p_top.z-biome.air_size}, {x=p_top.x+biome.air_size, y=p_top.y, z=p_top.z+biome.air_size}, "air")) >= biome.air_count) p_top, biome.avoid_radius + math.random(-1.5,2), biome.avoid_nodes) then
and pos.y >= biome.min_elevation return -- Nodes to avoid are nearby
and pos.y <= biome.max_elevation end
then
if biome.neighbors and 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 -- Near neighbour nodes are not present
end
local NEAR_DST = biome.near_nodes_size
if biome.near_nodes and biome.near_nodes_count and biome.near_nodes_size and
#minetest.find_nodes_in_area(
{x=pos.x-NEAR_DST, y=pos.y-biome.near_nodes_vertical, z=pos.z-NEAR_DST},
{x=pos.x+NEAR_DST, y=pos.y+biome.near_nodes_vertical, z=pos.z+NEAR_DST},
biome.near_nodes
) < biome.near_nodes_count then
return -- Far neighbour nodes are not present
end
if (biome.air_count and biome.air_size) and
#minetest.find_nodes_in_area(
{x=p_top.x-biome.air_size, y=p_top.y, z=p_top.z-biome.air_size},
{x=p_top.x+biome.air_size, y=p_top.y, z=p_top.z+biome.air_size},
"air"
) < biome.air_count then
return -- Not enough air
end
local walldir = biome_lib:find_adjacent_wall(p_top, biome.verticals_list, biome.choose_random_wall) local walldir = biome_lib:find_adjacent_wall(p_top, biome.verticals_list, biome.choose_random_wall)
if biome.alt_wallnode and walldir then if biome.alt_wallnode and walldir then
if n_top.name == "air" then if n_top.name == "air" then
minetest.swap_node(p_top, { name = biome.alt_wallnode, param2 = walldir }) minetest.swap_node(p_top, { name = biome.alt_wallnode, param2 = walldir })
end end
else return
end
local currentsurface = minetest.get_node(pos).name local currentsurface = minetest.get_node(pos).name
if currentsurface ~= "default:water_source"
or (currentsurface == "default:water_source" and #(minetest.find_nodes_in_area({x=pos.x, y=pos.y-biome.depth_max-1, z=pos.z}, {x=pos.x, y=pos.y, z=pos.z}, {"default:dirt", "default:dirt_with_grass", "default:sand"})) > 0 ) if biome_lib.default_water_nodes[currentsurface] and
then #minetest.find_nodes_in_area(
{x=pos.x, y=pos.y-biome.depth_max-1, z=pos.z},
vector.new(pos),
biome_lib.default_wet_surfaces
) == 0 then
return -- On water but no ground nearby
end
local rnd = math.random(1, biome.spawn_plants_count) local rnd = math.random(1, biome.spawn_plants_count)
local plant_to_spawn = biome.spawn_plants[rnd] local plant_to_spawn = biome.spawn_plants[rnd]
local fdir = biome.facedir local fdir = biome.facedir
@ -565,64 +712,6 @@ function biome_lib:spawn_on_surfaces(sd,sp,sr,sc,ss,sa)
end end
end end
end end
end
end
end
end
})
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
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,
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 end
@ -637,15 +726,13 @@ function biome_lib:replace_object(pos, replacement, grow_function, walldir, seed
return return
elseif growtype == "function" then elseif growtype == "function" then
local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale) local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale)
local noise1 = perlin_fertile_area:get2d({x=pos.x, y=pos.z}) local fertility, temperature, _ = get_biome_data(pos, perlin_fertile_area)
local noise2 = biome_lib.perlin_temperature:get2d({x=pos.x, y=pos.z}) grow_function(pos, fertility, temperature, walldir)
grow_function(pos,noise1,noise2,walldir)
return return
elseif growtype == "string" then elseif growtype == "string" then
local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale) local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale)
local noise1 = perlin_fertile_area:get2d({x=pos.x, y=pos.z}) local fertility, temperature, _ = get_biome_data(pos, perlin_fertile_area)
local noise2 = biome_lib.perlin_temperature:get2d({x=pos.x, y=pos.z}) assert(loadstring(grow_function.."(...)"))(pos, fertility, temperature, walldir)
assert(loadstring(grow_function.."(...)"))(pos,noise1,noise2,walldir)
return return
elseif growtype == "nil" then elseif growtype == "nil" then
minetest.swap_node(pos, { name = replacement, param2 = walldir}) minetest.swap_node(pos, { name = replacement, param2 = walldir})
@ -655,71 +742,8 @@ function biome_lib:replace_object(pos, replacement, grow_function, walldir, seed
end end
end end
-- function to decide if a node has a wall that's in verticals_list{} dofile(biome_lib.modpath .. "/search_functions.lua")
-- returns wall direction of valid node, or nil if invalid. assert(loadfile(biome_lib.modpath .. "/growth.lua"))(time_scale)
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
-- 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 -- Check for infinite stacks
@ -738,10 +762,29 @@ function biome_lib:get_nodedef_field(nodename, fieldname)
return minetest.registered_nodes[nodename][fieldname] return minetest.registered_nodes[nodename][fieldname]
end end
print("[Biome Lib] Loaded") if DEBUG then
biome_lib.last_count_air = 0
biome_lib.last_count_no_air = 0
function biome_lib.show_pending_block_counts()
if biome_lib.last_count_air ~= #biome_lib.blocklist_aircheck
or biome_lib.last_count_no_air ~= #biome_lib.blocklist_no_aircheck then
biome_lib:dbg(string.format("Pending block counts, air: %-7i no-air: %i",
#biome_lib.blocklist_aircheck, #biome_lib.blocklist_no_aircheck))
biome_lib.last_count_air = #biome_lib.blocklist_aircheck
biome_lib.last_count_no_air = #biome_lib.blocklist_no_aircheck
end
minetest.after(1, biome_lib.show_pending_block_counts)
end
biome_lib.show_pending_block_counts()
minetest.after(0, function() minetest.after(0, function()
print("[Biome Lib] Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread") print("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread")
print("[Biome Lib] across "..#biome_lib.actionslist_aircheck.." actions with air-checking and "..#biome_lib.actionslist_no_aircheck.." actions without.") print("across "..#biome_lib.actionslist_aircheck.." actions with air-checking and "..#biome_lib.actionslist_no_aircheck.." actions without.")
end) end)
end
print("[Biome Lib] Loaded")

View File

@ -1 +1,3 @@
name = biome_lib name = biome_lib
min_minetest_version = 5.2.0
optional_depends = default, intllib

74
search_functions.lua Normal file
View File

@ -0,0 +1,74 @@
-- 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
-- "Record" the map chunks being generated by the core mapgen,
-- split into individual mapblocks to reduce lag
minetest.register_on_generated(function(minp, maxp, blockseed)
for x = 0, 4 do
local minx = minp.x + x*16
for y = 0, 4 do
local miny = minp.y + y*16
for z = 0, 4 do
local minz = minp.z + z*16
local bmin = {x=minx, y=miny, z=minz}
local bmax = {x=minx + 15, y=miny + 15, z=minz + 15}
biome_lib.blocklist_aircheck[#biome_lib.blocklist_aircheck + 1] = { bmin, bmax }
biome_lib.blocklist_no_aircheck[#biome_lib.blocklist_no_aircheck + 1] = { bmin, bmax }
end
end
end
end)