Advertisement
Guest User

Untitled

a guest
Apr 6th, 2021
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.41 KB | None | 0 0
  1. -- Biome library mod by VanessaE
  2. --
  3. -- I got the temperature map idea from "hmmmm", values used for it came from
  4. -- Splizard's snow mod.
  5. --
  6.  
  7. -- Various settings - most of these probably won't need to be changed
  8.  
  9. biome_lib = {}
  10. biome_lib.air = {name = "air"}
  11.  
  12. biome_lib.block_log = {}
  13.  
  14. biome_lib.actionslist_aircheck = {}
  15. biome_lib.actionslist_no_aircheck = {}
  16.  
  17. biome_lib.surfaceslist_aircheck = {}
  18. biome_lib.surfaceslist_no_aircheck = {}
  19.  
  20. biome_lib.modpath = minetest.get_modpath("biome_lib")
  21.  
  22. local function tableize(s)
  23.     return string.split(string.trim(string.gsub(s, " ", "")))
  24. end
  25.  
  26. local c1 = minetest.settings:get("biome_lib_default_grow_through_nodes")
  27. biome_lib.default_grow_through_nodes = {["air"] = true}
  28. if c1 then
  29.     for _, i in ipairs(tableize(c1)) do
  30.         biome_lib.default_grow_through_nodes[i] = true
  31.     end
  32. else
  33.     biome_lib.default_grow_through_nodes["default:snow"] = true
  34. end
  35.  
  36. local c2 = minetest.settings:get("biome_lib_default_water_nodes")
  37. biome_lib.default_water_nodes = {}
  38. if c2 then
  39.     for _, i in ipairs(tableize(c2)) do
  40.         biome_lib.default_water_nodes[i] = true
  41.     end
  42. else
  43.     biome_lib.default_water_nodes["default:water_source"] = true
  44.     biome_lib.default_water_nodes["default:water_flowing"] = true
  45.     biome_lib.default_water_nodes["default:river_water_source"] = true
  46.     biome_lib.default_water_nodes["default:river_water_flowing"] = true
  47. end
  48.  
  49. local c3 = minetest.settings:get("biome_lib_default_wet_surfaces")
  50. local c4 = minetest.settings:get("biome_lib_default_ground_nodes")
  51. local c5 = minetest.settings:get("biome_lib_default_grow_nodes")
  52.  
  53. biome_lib.default_wet_surfaces = c3 and tableize(c3) or {"default:dirt", "default:dirt_with_grass", "default:sand"}
  54. biome_lib.default_ground_nodes = c4 and tableize(c4) or {"default:dirt_with_grass"}
  55. biome_lib.default_grow_nodes =   c5 and tableize(c5) or {"default:dirt_with_grass"}
  56.  
  57. -- Boilerplate to support localized strings if intllib mod is installed.
  58. local S
  59. if minetest.global_exists("intllib") then
  60.     if intllib.make_gettext_pair then
  61.         S = intllib.make_gettext_pair()
  62.     else
  63.         S = intllib.Getter()
  64.     end
  65. else
  66.     S = function(s) return s end
  67. end
  68. biome_lib.intllib = S
  69.  
  70. local DEBUG = minetest.settings:get_bool("biome_lib_debug", false)
  71.  
  72. function biome_lib:dbg(msg)
  73.     if DEBUG then
  74.         print("[Biome Lib] "..msg)
  75.         minetest.log("verbose", "[Biome Lib] "..msg)
  76.     end
  77. end
  78.  
  79. biome_lib.plantlife_seed_diff = 329 -- needs to be global so other mods can see it
  80.  
  81. local perlin_octaves = 3
  82. local perlin_persistence = 0.6
  83. local perlin_scale = 100
  84.  
  85. local temperature_seeddiff = 112
  86. local temperature_octaves = 3
  87. local temperature_persistence = 0.5
  88. local temperature_scale = 150
  89.  
  90. local humidity_seeddiff = 9130
  91. local humidity_octaves = 3
  92. local humidity_persistence = 0.5
  93. local humidity_scale = 250
  94.  
  95. local time_scale = 1
  96. local time_speed = tonumber(minetest.settings:get("time_speed"))
  97.  
  98. if time_speed and time_speed > 0 then
  99.     time_scale = 72 / time_speed
  100. end
  101.  
  102. --PerlinNoise(seed, octaves, persistence, scale)
  103.  
  104. biome_lib.perlin_temperature = PerlinNoise(temperature_seeddiff, temperature_octaves, temperature_persistence, temperature_scale)
  105. biome_lib.perlin_humidity = PerlinNoise(humidity_seeddiff, humidity_octaves, humidity_persistence, humidity_scale)
  106.  
  107. -- Local functions
  108.  
  109. local function get_biome_data(pos, perlin_fertile)
  110.     local fertility = perlin_fertile:get_2d({x=pos.x, y=pos.z})
  111.  
  112.     if type(minetest.get_biome_data) == "function" then
  113.         local data = minetest.get_biome_data(pos)
  114.         if data then
  115.             return fertility, data.heat / 100, data.humidity / 100
  116.         end
  117.     end
  118.  
  119.     local temperature = biome_lib.perlin_temperature:get2d({x=pos.x, y=pos.z})
  120.     local humidity = biome_lib.perlin_humidity:get2d({x=pos.x+150, y=pos.z+50})
  121.  
  122.     return fertility, temperature, humidity
  123. end
  124.  
  125. function biome_lib:is_node_loaded(node_pos)
  126.     local n = minetest.get_node_or_nil(node_pos)
  127.     if (not n) or (n.name == "ignore") then
  128.         return false
  129.     end
  130.     return true
  131. end
  132.  
  133. function biome_lib:set_defaults(biome)
  134.     biome.seed_diff = biome.seed_diff or 0
  135.     biome.min_elevation = biome.min_elevation or -31000
  136.     biome.max_elevation = biome.max_elevation or 31000
  137.     biome.temp_min = biome.temp_min or 1
  138.     biome.temp_max = biome.temp_max or -1
  139.     biome.humidity_min = biome.humidity_min or 1
  140.     biome.humidity_max = biome.humidity_max or -1
  141.     biome.plantlife_limit = biome.plantlife_limit or 0.1
  142.     biome.near_nodes_vertical = biome.near_nodes_vertical or 1
  143.  
  144. -- specific to on-generate
  145.  
  146.     biome.neighbors = biome.neighbors or biome.surface
  147.     biome.near_nodes_size = biome.near_nodes_size or 0
  148.     biome.near_nodes_count = biome.near_nodes_count or 1
  149.     biome.rarity = biome.rarity or 50
  150.     biome.max_count = biome.max_count or 125
  151.     if biome.check_air ~= false then biome.check_air = true end
  152.  
  153. -- specific to abm spawner
  154.     biome.seed_diff = biome.seed_diff or 0
  155.     biome.light_min = biome.light_min or 0
  156.     biome.light_max = biome.light_max or 15
  157.     biome.depth_max = biome.depth_max or 1
  158.     biome.facedir = biome.facedir or 0
  159. end
  160.  
  161. local function search_table(t, s)
  162.     for i = 1, #t do
  163.         if t[i] == s then return true end
  164.     end
  165.     return false
  166. end
  167.  
  168. -- register the list of surfaces to spawn stuff on, filtering out all duplicates.
  169. -- separate the items by air-checking or non-air-checking map eval methods
  170.  
  171. function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model)
  172.  
  173.     -- if calling code passes an undefined node for a surface or
  174.     -- as a node to be spawned, don't register an action for it.
  175.  
  176.     if type(nodes_or_function_or_model) == "string"
  177.       and string.find(nodes_or_function_or_model, ":")
  178.       and not minetest.registered_nodes[nodes_or_function_or_model] then
  179.         biome_lib:dbg("Warning: Ignored registration for undefined spawn node: "..dump(nodes_or_function_or_model))
  180.         return
  181.     end
  182.  
  183.     if type(nodes_or_function_or_model) == "string"
  184.       and not string.find(nodes_or_function_or_model, ":") then
  185.         biome_lib:dbg("Warning: Registered function call using deprecated string method: "..dump(nodes_or_function_or_model))
  186.     end
  187.  
  188.     if biomedef.check_air == false then
  189.         biome_lib:dbg("Register no-air-check mapgen hook: "..dump(nodes_or_function_or_model))
  190.         biome_lib.actionslist_no_aircheck[#biome_lib.actionslist_no_aircheck + 1] = { biomedef, nodes_or_function_or_model }
  191.         local s = biomedef.surface
  192.         if type(s) == "string" then
  193.             if s and (string.find(s, "^group:") or minetest.registered_nodes[s]) then
  194.                 if not search_table(biome_lib.surfaceslist_no_aircheck, s) then
  195.                     biome_lib.surfaceslist_no_aircheck[#biome_lib.surfaceslist_no_aircheck + 1] = s
  196.                 end
  197.             else
  198.                 biome_lib:dbg("Warning: Ignored no-air-check registration for undefined surface node: "..dump(s))
  199.             end
  200.         else
  201.             for i = 1, #biomedef.surface do
  202.                 local s = biomedef.surface[i]
  203.                 if s and (string.find(s, "^group:") or minetest.registered_nodes[s]) then
  204.                     if not search_table(biome_lib.surfaceslist_no_aircheck, s) then
  205.                         biome_lib.surfaceslist_no_aircheck[#biome_lib.surfaceslist_no_aircheck + 1] = s
  206.                     end
  207.                 else
  208.                     biome_lib:dbg("Warning: Ignored no-air-check registration for undefined surface node: "..dump(s))
  209.                 end
  210.             end
  211.         end
  212.     else
  213.         biome_lib:dbg("Register with-air-checking mapgen hook: "..dump(nodes_or_function_or_model))
  214.         biome_lib.actionslist_aircheck[#biome_lib.actionslist_aircheck + 1] = { biomedef, nodes_or_function_or_model }
  215.         local s = biomedef.surface
  216.         if type(s) == "string" then
  217.             if s and (string.find(s, "^group:") or minetest.registered_nodes[s]) then
  218.                 if not search_table(biome_lib.surfaceslist_aircheck, s) then
  219.                     biome_lib.surfaceslist_aircheck[#biome_lib.surfaceslist_aircheck + 1] = s
  220.                 end
  221.             else
  222.                 biome_lib:dbg("Warning: Ignored with-air-checking registration for undefined surface node: "..dump(s))
  223.             end
  224.         else
  225.             for i = 1, #biomedef.surface do
  226.                 local s = biomedef.surface[i]
  227.                 if s and (string.find(s, "^group:") or minetest.registered_nodes[s]) then
  228.                     if not search_table(biome_lib.surfaceslist_aircheck, s) then
  229.                         biome_lib.surfaceslist_aircheck[#biome_lib.surfaceslist_aircheck + 1] = s
  230.                     end
  231.                 else
  232.                     biome_lib:dbg("Warning: Ignored with-air-checking registration for undefined surface node: "..dump(s))
  233.                 end
  234.             end
  235.         end
  236.     end
  237. end
  238.  
  239. -- Function to check whether a position matches the given biome definition
  240. -- Returns true when the surface can be populated
  241.  
  242. local function populate_single_surface(biome, pos, perlin_fertile_area, checkair)
  243.     local p_top = { x = pos.x, y = pos.y + 1, z = pos.z }
  244.  
  245.     if math.random(1, 100) <= biome.rarity then
  246.         return
  247.     end
  248.  
  249.     local fertility, temperature, humidity = get_biome_data(pos, perlin_fertile_area)
  250.  
  251.     local pos_biome_ok = pos.y >= biome.min_elevation and pos.y <= biome.max_elevation
  252.         and fertility > biome.plantlife_limit
  253.         and temperature <= biome.temp_min and temperature >= biome.temp_max
  254.         and humidity <= biome.humidity_min and humidity >= biome.humidity_max
  255.    
  256.     if not pos_biome_ok then
  257.         return -- Y position mismatch, outside of biome
  258.     end
  259.  
  260.     local biome_surfaces_string = dump(biome.surface)
  261.     local surface_ok = false
  262.  
  263.     if not biome.depth then
  264.         local dest_node = minetest.get_node(pos)
  265.         if string.find(biome_surfaces_string, dest_node.name) then
  266.             surface_ok = true
  267.         else
  268.             if string.find(biome_surfaces_string, "group:") then
  269.                 for j = 1, #biome.surface do
  270.                     if string.find(biome.surface[j], "^group:")
  271.                       and minetest.get_item_group(dest_node.name, biome.surface[j]) then
  272.                         surface_ok = true
  273.                         break
  274.                     end
  275.                 end
  276.             end
  277.         end
  278.     elseif not string.find(biome_surfaces_string,
  279.             minetest.get_node({ x = pos.x, y = pos.y-biome.depth-1, z = pos.z }).name) then
  280.         surface_ok = true
  281.     end
  282.    
  283.     if not surface_ok then
  284.         return -- Surface does not match the given node group/name
  285.     end
  286.  
  287.     if checkair and minetest.get_node(p_top).name ~= "air" then
  288.         return
  289.     end
  290.  
  291.     if biome.below_nodes and
  292.             not string.find(dump(biome.below_nodes),
  293.                 minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name
  294.             ) then
  295.         return -- Node below does not match
  296.     end
  297.  
  298.     if biome.ncount and
  299.             #minetest.find_nodes_in_area(
  300.                 {x=pos.x-1, y=pos.y, z=pos.z-1},
  301.                 {x=pos.x+1, y=pos.y, z=pos.z+1},
  302.                 biome.neighbors
  303.             ) <= biome.ncount then
  304.         return -- Not enough similar biome nodes around
  305.     end
  306.  
  307.     if biome.near_nodes and
  308.             #minetest.find_nodes_in_area(
  309.                 {x=pos.x-biome.near_nodes_size, y=pos.y-biome.near_nodes_vertical, z=pos.z-biome.near_nodes_size},
  310.                 {x=pos.x+biome.near_nodes_size, y=pos.y+biome.near_nodes_vertical, z=pos.z+biome.near_nodes_size},
  311.                 biome.near_nodes
  312.             ) < biome.near_nodes_count then
  313.         return -- Long distance neighbours do not match
  314.     end
  315.  
  316.     -- Position fits into given biome
  317.     return true
  318. end
  319.  
  320. function biome_lib.populate_surfaces(biome, nodes_or_function_or_model, snodes, checkair)
  321.     local items_added = 0
  322.  
  323.     biome_lib:set_defaults(biome)
  324.  
  325.     -- filter stage 1 - find nodes from the supplied surfaces that are within the current biome.
  326.  
  327.     local in_biome_nodes = {}
  328.     local perlin_fertile_area = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale)
  329.  
  330.     for i = 1, #snodes do
  331.         local pos = vector.new(snodes[i])
  332.         if populate_single_surface(biome, pos, perlin_fertile_area, checkair) then
  333.             in_biome_nodes[#in_biome_nodes + 1] = pos
  334.         end
  335.     end
  336.  
  337.     -- filter stage 2 - find places within that biome area to place the plants.
  338.  
  339.     local num_in_biome_nodes = #in_biome_nodes
  340.  
  341.     if num_in_biome_nodes == 0 then
  342.         return 0
  343.     end
  344.  
  345.     for i = 1, math.min(math.ceil(biome.max_count/25), num_in_biome_nodes) do
  346.         local tries = 0
  347.         local spawned = false
  348.         while tries < 2 and not spawned do
  349.             local pos = in_biome_nodes[math.random(1, num_in_biome_nodes)]
  350.             if biome.spawn_replace_node then
  351.                 pos.y = pos.y-1
  352.             end
  353.             local p_top = { x = pos.x, y = pos.y + 1, z = pos.z }
  354.  
  355.             if not (biome.avoid_nodes and biome.avoid_radius
  356.                     and minetest.find_node_near(p_top, biome.avoid_radius
  357.                     + math.random(-1.5,2), biome.avoid_nodes)) then
  358.                 if biome.delete_above then
  359.                     minetest.swap_node(p_top, biome_lib.air)
  360.                     minetest.swap_node({x=p_top.x, y=p_top.y+1, z=p_top.z}, biome_lib.air)
  361.                 end
  362.  
  363.                 if biome.delete_above_surround then
  364.                     minetest.swap_node({x=p_top.x-1, y=p_top.y, z=p_top.z}, biome_lib.air)
  365.                     minetest.swap_node({x=p_top.x+1, y=p_top.y, z=p_top.z}, biome_lib.air)
  366.                     minetest.swap_node({x=p_top.x,   y=p_top.y, z=p_top.z-1}, biome_lib.air)
  367.                     minetest.swap_node({x=p_top.x,   y=p_top.y, z=p_top.z+1}, biome_lib.air)
  368.  
  369.                     minetest.swap_node({x=p_top.x-1, y=p_top.y+1, z=p_top.z}, biome_lib.air)
  370.                     minetest.swap_node({x=p_top.x+1, y=p_top.y+1, z=p_top.z}, biome_lib.air)
  371.                     minetest.swap_node({x=p_top.x,   y=p_top.y+1, z=p_top.z-1}, biome_lib.air)
  372.                     minetest.swap_node({x=p_top.x,   y=p_top.y+1, z=p_top.z+1}, biome_lib.air)
  373.                 end
  374.  
  375.                 if biome.spawn_replace_node then
  376.                     minetest.swap_node(pos, biome_lib.air)
  377.                 end
  378.  
  379.                 local objtype = type(nodes_or_function_or_model)
  380.  
  381.                 if objtype == "table" then
  382.                     if nodes_or_function_or_model.axiom then
  383.                         biome_lib:generate_tree(p_top, nodes_or_function_or_model)
  384.                         biome_lib:dbg("An L-tree was spawned at "..minetest.pos_to_string(p_top))
  385.                         spawned = true
  386.                     else
  387.                         local fdir = nil
  388.                         if biome.random_facedir then
  389.                             fdir = math.random(biome.random_facedir[1], biome.random_facedir[2])
  390.                         end
  391.                         local n=nodes_or_function_or_model[math.random(#nodes_or_function_or_model)]
  392.                         minetest.swap_node(p_top, { name = n, param2 = fdir })
  393.                         biome_lib:dbg("Node \""..n.."\" was randomly picked from a list and placed at "..minetest.pos_to_string(p_top))
  394.                         spawned = true
  395.                     end
  396.                 elseif objtype == "string" and
  397.                   minetest.registered_nodes[nodes_or_function_or_model] then
  398.                     local fdir = nil
  399.                     if biome.random_facedir then
  400.                         fdir = math.random(biome.random_facedir[1], biome.random_facedir[2])
  401.                     end
  402.                     minetest.swap_node(p_top, { name = nodes_or_function_or_model, param2 = fdir })
  403.                     biome_lib:dbg("Node \""..nodes_or_function_or_model.."\" was placed at "..minetest.pos_to_string(p_top))
  404.                     spawned = true
  405.                 elseif objtype == "function" then
  406.                     nodes_or_function_or_model(pos)
  407.                     biome_lib:dbg("A function was run on surface node at "..minetest.pos_to_string(pos))
  408.                     spawned = true
  409.                 elseif objtype == "string" and pcall(loadstring(("return %s(...)"):
  410.                     format(nodes_or_function_or_model)),pos) then
  411.                     spawned = true
  412.                     biome_lib:dbg("An obsolete string-specified function was run on surface node at "..minetest.pos_to_string(p_top))
  413.                 else
  414.                     biome_lib:dbg("Warning: Ignored invalid definition for object "..dump(nodes_or_function_or_model).." that was pointed at {"..dump(pos).."}")
  415.                 end
  416.             else
  417.                 tries = tries + 1
  418.             end
  419.         end
  420.         if spawned then items_added = items_added + 1 end
  421.     end
  422.     return items_added
  423. end
  424.  
  425. -- Primary log read-out/mapgen spawner
  426.  
  427. local function confirm_block_surroundings(p)
  428.     local n=minetest.get_node_or_nil(p)
  429.     if not n or n.name == "ignore" then return false end
  430.  
  431.     for x = -32,32,64 do -- step of 64 causes it to only check the 8 corner blocks
  432.         for y = -32,32,64 do
  433.             for z = -32,32,64 do
  434.                 local pos = {x=p.x + x, y=p.y + y, z=p.z + z}
  435.                 local n=minetest.get_node_or_nil(pos)
  436.                 if not n or n.name == "ignore" then return false end
  437.             end
  438.         end
  439.     end
  440.     return true
  441. end
  442.  
  443. function biome_lib.generate_block(shutting_down)
  444.     if not biome_lib.block_log[1] then return end -- the block log is empty
  445.  
  446.     local minp =        biome_lib.block_log[1][1]
  447.     local maxp =        biome_lib.block_log[1][2]
  448.     local airflag =     biome_lib.block_log[1][3]
  449.     local pos_hash =    minetest.hash_node_position(minp)
  450.  
  451.     if not biome_lib.pos_hash then -- we need to read the maplock and get the surfaces list
  452.         biome_lib.pos_hash = {}
  453.         if not confirm_block_surroundings(minp) and not shutting_down then -- if any neighbors appear not to be loaded, move this block to the end of the queue
  454.             biome_lib.block_log[#biome_lib.block_log + 1] = biome_lib.block_log[1]
  455.             table.remove(biome_lib.block_log, 1)
  456.             biome_lib.pos_hash = nil
  457.                 biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp)..
  458.                     " had a neighbor not fully emerged, moved it to the end of the queue.")
  459.             return
  460.         else
  461.             biome_lib.pos_hash.surface_node_list = airflag
  462.                 and minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck)
  463.                 or minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck)
  464.             biome_lib.pos_hash.action_index = 1
  465.             if #biome_lib.pos_hash.surface_node_list > 0 then
  466.                 biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp)..
  467.                     " has "..#biome_lib.pos_hash.surface_node_list..
  468.                     " surface nodes to work on (airflag="..dump(airflag)..")")
  469.             end
  470.         end
  471.     elseif not (airflag and biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index])
  472.       and not (not airflag and biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index]) then
  473.         -- the block is finished, remove it
  474.         if #biome_lib.pos_hash.surface_node_list > 0 then
  475.             biome_lib:dbg("Deleted mapblock "..minetest.pos_to_string(minp).." from the block log")
  476.         end
  477.         table.remove(biome_lib.block_log, 1)
  478.         biome_lib.pos_hash = nil
  479.     else
  480.         -- below, [1] is biome, [2] is the thing to be added
  481.         local added = 0
  482.         if airflag then
  483.             if biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index] then
  484.                 added = biome_lib.populate_surfaces(
  485.                             biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][1],
  486.                             biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][2],
  487.                             biome_lib.pos_hash.surface_node_list, true)
  488.                 biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1
  489.             end
  490.         else
  491.             if biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index] then
  492.                 added = biome_lib.populate_surfaces(
  493.                             biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][1],
  494.                             biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][2],
  495.                             biome_lib.pos_hash.surface_node_list, false)
  496.                 biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1
  497.             end
  498.         end
  499.         if added > 0 then
  500.             biome_lib:dbg("biome_lib.populate_surfaces ran on mapblock at "..
  501.                 minetest.pos_to_string(minp)..".  Entry #"..
  502.                 (biome_lib.pos_hash.action_index-1).." added "..added.." items.")
  503.         end
  504.     end
  505. end
  506.  
  507. -- "Play" them back, populating them with new stuff in the process
  508.  
  509. local rr = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or -100
  510.  
  511. biome_lib.queue_run_ratio = 100 - rr
  512. biome_lib.entries_per_step = math.max(-rr, 1)
  513.  
  514. minetest.register_globalstep(function(dtime)
  515.     if math.random(100) > biome_lib.queue_run_ratio then return end
  516.     for s = 1, biome_lib.entries_per_step do
  517.         biome_lib.generate_block()
  518.     end
  519. end)
  520.  
  521. -- Play out the entire log all at once on shutdown
  522. -- to prevent unpopulated map areas
  523.  
  524. minetest.register_on_shutdown(function()
  525.     if #biome_lib.block_log == 0 then
  526.         return
  527.     end
  528.  
  529.     print("[biome_lib] Stand by, playing out the rest of the mapblock log")
  530.     print("(there are "..#biome_lib.block_log.." entries)...")
  531.     while #biome_lib.block_log > 0 do
  532.         biome_lib.generate_block(true)
  533.     end
  534. end)
  535.  
  536. -- The spawning ABM
  537.  
  538. function biome_lib:spawn_on_surfaces(sd,sp,sr,sc,ss,sa)
  539.  
  540.     local biome = {}
  541.  
  542.     if type(sd) ~= "table" then
  543.         biome.spawn_delay = sd  -- old api expects ABM interval param here.
  544.         biome.spawn_plants = {sp}
  545.         biome.avoid_radius = sr
  546.         biome.spawn_chance = sc
  547.         biome.spawn_surfaces = {ss}
  548.         biome.avoid_nodes = sa
  549.     else
  550.         biome = sd
  551.     end
  552.  
  553.     if biome.spawn_delay*time_scale >= 1 then
  554.         biome.interval = biome.spawn_delay*time_scale
  555.     else
  556.         biome.interval = 1
  557.     end
  558.  
  559.     biome_lib:set_defaults(biome)
  560.     biome.spawn_plants_count = #(biome.spawn_plants)
  561.  
  562.     local n
  563.     if type(biome.spawn_plants) == "table" then
  564.         n = "random: "..biome.spawn_plants[1]..", ..."
  565.     else
  566.         n = biome.spawn_plants
  567.     end
  568.     biome.label = biome.label or "biome_lib spawn_on_surfaces(): "..n
  569.  
  570.     minetest.register_abm({
  571.         nodenames = biome.spawn_surfaces,
  572.         interval = biome.interval,
  573.         chance = biome.spawn_chance,
  574.         neighbors = biome.neighbors,
  575.         label = biome.label,
  576.         action = function(pos, node, active_object_count, active_object_count_wider)
  577.             local p_top = { x = pos.x, y = pos.y + 1, z = pos.z }  
  578.             local n_top = minetest.get_node(p_top)
  579.             local perlin_fertile_area = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale)
  580.  
  581.             local fertility, temperature, humidity = get_biome_data(pos, perlin_fertile_area)
  582.  
  583.             local pos_biome_ok = pos.y >= biome.min_elevation and pos.y <= biome.max_elevation
  584.                 and fertility > biome.plantlife_limit
  585.                 and temperature <= biome.temp_min and temperature >= biome.temp_max
  586.                 and humidity <= biome.humidity_min and humidity >= biome.humidity_max
  587.                 and biome_lib:is_node_loaded(p_top)
  588.  
  589.             if not pos_biome_ok then
  590.                 return -- Outside of biome
  591.             end
  592.  
  593.             local n_light = minetest.get_node_light(p_top, nil)
  594.             if n_light < biome.light_min or n_light > biome.light_max then
  595.                 return -- Too dark or too bright
  596.             end
  597.  
  598.             if biome.avoid_nodes and biome.avoid_radius and minetest.find_node_near(
  599.                     p_top, biome.avoid_radius + math.random(-1.5,2), biome.avoid_nodes) then
  600.                 return -- Nodes to avoid are nearby
  601.             end
  602.  
  603.             if biome.neighbors and biome.ncount and
  604.                     #minetest.find_nodes_in_area(
  605.                         {x=pos.x-1, y=pos.y, z=pos.z-1},
  606.                         {x=pos.x+1, y=pos.y, z=pos.z+1},
  607.                         biome.neighbors
  608.                     ) <= biome.ncount then
  609.                 return -- Near neighbour nodes are not present
  610.             end
  611.  
  612.             local NEAR_DST = biome.near_nodes_size
  613.             if biome.near_nodes and biome.near_nodes_count and biome.near_nodes_size and
  614.                     #minetest.find_nodes_in_area(
  615.                         {x=pos.x-NEAR_DST, y=pos.y-biome.near_nodes_vertical, z=pos.z-NEAR_DST},
  616.                         {x=pos.x+NEAR_DST, y=pos.y+biome.near_nodes_vertical, z=pos.z+NEAR_DST},
  617.                         biome.near_nodes
  618.                     ) < biome.near_nodes_count then
  619.                 return -- Far neighbour nodes are not present
  620.             end
  621.  
  622.             if (biome.air_count and biome.air_size) and
  623.                     #minetest.find_nodes_in_area(
  624.                         {x=p_top.x-biome.air_size, y=p_top.y, z=p_top.z-biome.air_size},
  625.                         {x=p_top.x+biome.air_size, y=p_top.y, z=p_top.z+biome.air_size},
  626.                         "air"
  627.                     ) < biome.air_count then
  628.                 return -- Not enough air
  629.             end
  630.  
  631.             local walldir = biome_lib:find_adjacent_wall(p_top, biome.verticals_list, biome.choose_random_wall)
  632.             if biome.alt_wallnode and walldir then
  633.                 if n_top.name == "air" then
  634.                     minetest.swap_node(p_top, { name = biome.alt_wallnode, param2 = walldir })
  635.                 end
  636.                 return
  637.             end
  638.  
  639.             local currentsurface = minetest.get_node(pos).name
  640.  
  641.             if biome_lib.default_water_nodes[currentsurface] and
  642.                     #minetest.find_nodes_in_area(
  643.                         {x=pos.x, y=pos.y-biome.depth_max-1, z=pos.z},
  644.                         vector.new(pos),
  645.                         biome_lib.default_wet_surfaces
  646.                     ) == 0 then
  647.                 return -- On water but no ground nearby
  648.             end
  649.  
  650.             local rnd = math.random(1, biome.spawn_plants_count)
  651.             local plant_to_spawn = biome.spawn_plants[rnd]
  652.             local fdir = biome.facedir
  653.             if biome.random_facedir then
  654.                 fdir = math.random(biome.random_facedir[1],biome.random_facedir[2])
  655.             end
  656.             if type(biome.spawn_plants) == "string" then
  657.                 assert(loadstring(biome.spawn_plants.."(...)"))(pos)
  658.             elseif not biome.spawn_on_side and not biome.spawn_on_bottom and not biome.spawn_replace_node then
  659.                 if n_top.name == "air" then
  660.                     minetest.swap_node(p_top, { name = plant_to_spawn, param2 = fdir })
  661.                 end
  662.             elseif biome.spawn_replace_node then
  663.                 minetest.swap_node(pos, { name = plant_to_spawn, param2 = fdir })
  664.  
  665.             elseif biome.spawn_on_side then
  666.                 local onside = biome_lib:find_open_side(pos)
  667.                 if onside then
  668.                     minetest.swap_node(onside.newpos, { name = plant_to_spawn, param2 = onside.facedir })
  669.                 end
  670.             elseif biome.spawn_on_bottom then
  671.                 if minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
  672.                     minetest.swap_node({x=pos.x, y=pos.y-1, z=pos.z}, { name = plant_to_spawn, param2 = fdir} )
  673.                 end
  674.             end
  675.         end
  676.     })
  677. end
  678.  
  679. -- Function to decide how to replace a plant - either grow it, replace it with
  680. -- a tree, run a function, or die with an error.
  681.  
  682. function biome_lib:replace_object(pos, replacement, grow_function, walldir, seeddiff)
  683.     local growtype = type(grow_function)
  684.     if growtype == "table" then
  685.         minetest.swap_node(pos, biome_lib.air)
  686.         biome_lib:grow_tree(pos, grow_function)
  687.         return
  688.     elseif growtype == "function" then
  689.         local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale)
  690.         local fertility, temperature, _ = get_biome_data(pos, perlin_fertile_area)
  691.         grow_function(pos, fertility, temperature, walldir)
  692.         return
  693.     elseif growtype == "string" then
  694.         local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale)
  695.         local fertility, temperature, _ = get_biome_data(pos, perlin_fertile_area)
  696.         assert(loadstring(grow_function.."(...)"))(pos, fertility, temperature, walldir)
  697.         return
  698.     elseif growtype == "nil" then
  699.         minetest.swap_node(pos, { name = replacement, param2 = walldir})
  700.         return
  701.     elseif growtype ~= "nil" and growtype ~= "string" and growtype ~= "table" then
  702.         error("Invalid grow function "..dump(grow_function).." used on object at ("..dump(pos)..")")
  703.     end
  704. end
  705.  
  706. dofile(biome_lib.modpath .. "/search_functions.lua")
  707. assert(loadfile(biome_lib.modpath .. "/growth.lua"))(time_scale)
  708.  
  709. -- Check for infinite stacks
  710.  
  711. if minetest.get_modpath("unified_inventory") or not minetest.settings:get_bool("creative_mode") then
  712.     biome_lib.expect_infinite_stacks = false
  713. else
  714.     biome_lib.expect_infinite_stacks = true
  715. end
  716.  
  717. -- read a field from a node's definition
  718.  
  719. function biome_lib:get_nodedef_field(nodename, fieldname)
  720.     if not minetest.registered_nodes[nodename] then
  721.         return nil
  722.     end
  723.     return minetest.registered_nodes[nodename][fieldname]
  724. end
  725.  
  726. if DEBUG then
  727.     biome_lib.last_count = 0
  728.  
  729.     function biome_lib.show_pending_block_count()
  730.         if biome_lib.last_count ~= #biome_lib.block_log then
  731.             biome_lib:dbg("Pending block count: "..#biome_lib.block_log)
  732.             biome_lib.last_count = #biome_lib.block_log
  733.         end
  734.         minetest.after(1, biome_lib.show_pending_block_count)
  735.     end
  736.  
  737.     biome_lib.show_pending_block_count()
  738.  
  739.     minetest.after(0, function()
  740.         print("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread")
  741.         print("across "..#biome_lib.actionslist_aircheck.." actions with air-checking and "..#biome_lib.actionslist_no_aircheck.." actions without.")
  742.     end)
  743.  
  744. end
  745.  
  746. print("[Biome Lib] Loaded")
  747.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement