Guest User

Untitled

a guest
Apr 6th, 2021
40
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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.     for x = -32,32,32 do
  429.         for y = -32,32,32 do
  430.             for z = -32,32,32 do
  431.                 local pos = {x=p.x + x, y=p.y + y, z=p.z + z}
  432.                 if not minetest.get_node_or_nil(pos)
  433.                   or minetest.get_node_or_nil(pos).name == "ignore" then
  434.                     return false
  435.                 end
  436.             end
  437.         end
  438.     end
  439.     return true
  440. end
  441.  
  442. function biome_lib.generate_block(shutting_down)
  443.     if not biome_lib.block_log[1] then return end -- the block log is empty
  444.  
  445.     local minp =        biome_lib.block_log[1][1]
  446.     local maxp =        biome_lib.block_log[1][2]
  447.     local airflag =     biome_lib.block_log[1][3]
  448.     local pos_hash =    minetest.hash_node_position(minp)
  449.  
  450.     if not biome_lib.pos_hash then -- we need to read the maplock and get the surfaces list
  451.         biome_lib.pos_hash = {}
  452.         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
  453.             biome_lib.block_log[#biome_lib.block_log + 1] = biome_lib.block_log[1]
  454.             table.remove(biome_lib.block_log, 1)
  455.             biome_lib.pos_hash = nil
  456.                 biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp)..
  457.                     " had a neighbor not fully emerged, moved it to the end of the queue.")
  458.             return
  459.         else
  460.             biome_lib.pos_hash.surface_node_list = airflag
  461.                 and minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck)
  462.                 or minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck)
  463.             biome_lib.pos_hash.action_index = 1
  464.             if #biome_lib.pos_hash.surface_node_list > 0 then
  465.                 biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp)..
  466.                     " has "..#biome_lib.pos_hash.surface_node_list..
  467.                     " surface nodes to work on (airflag="..dump(airflag)..")")
  468.             end
  469.         end
  470.     elseif not (airflag and biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index])
  471.       and not (not airflag and biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index]) then
  472.         -- the block is finished, remove it
  473.         if #biome_lib.pos_hash.surface_node_list > 0 then
  474.             biome_lib:dbg("Deleted mapblock "..minetest.pos_to_string(minp).." from the block log")
  475.         end
  476.         table.remove(biome_lib.block_log, 1)
  477.         biome_lib.pos_hash = nil
  478.     else
  479.         -- below, [1] is biome, [2] is the thing to be added
  480.         local added = 0
  481.         if airflag then
  482.             if biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index] then
  483.                 added = biome_lib.populate_surfaces(
  484.                             biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][1],
  485.                             biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][2],
  486.                             biome_lib.pos_hash.surface_node_list, true)
  487.                 biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1
  488.             end
  489.         else
  490.             if biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index] then
  491.                 added = biome_lib.populate_surfaces(
  492.                             biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][1],
  493.                             biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][2],
  494.                             biome_lib.pos_hash.surface_node_list, false)
  495.                 biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1
  496.             end
  497.         end
  498.         if added > 0 then
  499.             biome_lib:dbg("biome_lib.populate_surfaces ran on mapblock at "..
  500.                 minetest.pos_to_string(minp)..".  Entry #"..
  501.                 (biome_lib.pos_hash.action_index-1).." added "..added.." items.")
  502.         end
  503.     end
  504. end
  505.  
  506. -- "Play" them back, populating them with new stuff in the process
  507.  
  508. local rr = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or -100
  509.  
  510. biome_lib.queue_run_ratio = 100 - rr
  511. biome_lib.entries_per_step = math.max(-rr, 1)
  512.  
  513. minetest.register_globalstep(function(dtime)
  514.     if math.random(100) > biome_lib.queue_run_ratio then return end
  515.     for s = 1, biome_lib.entries_per_step do
  516.         biome_lib.generate_block()
  517.     end
  518. end)
  519.  
  520. -- Play out the entire log all at once on shutdown
  521. -- to prevent unpopulated map areas
  522.  
  523. minetest.register_on_shutdown(function()
  524.     if #biome_lib.block_log == 0 then
  525.         return
  526.     end
  527.  
  528.     print("[biome_lib] Stand by, playing out the rest of the mapblock log")
  529.     print("(there are "..#biome_lib.block_log.." entries)...")
  530.     while #biome_lib.block_log > 0 do
  531.         biome_lib.generate_block(true)
  532.     end
  533. end)
  534.  
  535. -- The spawning ABM
  536.  
  537. function biome_lib:spawn_on_surfaces(sd,sp,sr,sc,ss,sa)
  538.  
  539.     local biome = {}
  540.  
  541.     if type(sd) ~= "table" then
  542.         biome.spawn_delay = sd  -- old api expects ABM interval param here.
  543.         biome.spawn_plants = {sp}
  544.         biome.avoid_radius = sr
  545.         biome.spawn_chance = sc
  546.         biome.spawn_surfaces = {ss}
  547.         biome.avoid_nodes = sa
  548.     else
  549.         biome = sd
  550.     end
  551.  
  552.     if biome.spawn_delay*time_scale >= 1 then
  553.         biome.interval = biome.spawn_delay*time_scale
  554.     else
  555.         biome.interval = 1
  556.     end
  557.  
  558.     biome_lib:set_defaults(biome)
  559.     biome.spawn_plants_count = #(biome.spawn_plants)
  560.  
  561.     local n
  562.     if type(biome.spawn_plants) == "table" then
  563.         n = "random: "..biome.spawn_plants[1]..", ..."
  564.     else
  565.         n = biome.spawn_plants
  566.     end
  567.     biome.label = biome.label or "biome_lib spawn_on_surfaces(): "..n
  568.  
  569.     minetest.register_abm({
  570.         nodenames = biome.spawn_surfaces,
  571.         interval = biome.interval,
  572.         chance = biome.spawn_chance,
  573.         neighbors = biome.neighbors,
  574.         label = biome.label,
  575.         action = function(pos, node, active_object_count, active_object_count_wider)
  576.             local p_top = { x = pos.x, y = pos.y + 1, z = pos.z }  
  577.             local n_top = minetest.get_node(p_top)
  578.             local perlin_fertile_area = minetest.get_perlin(biome.seed_diff, perlin_octaves, perlin_persistence, perlin_scale)
  579.  
  580.             local fertility, temperature, humidity = get_biome_data(pos, perlin_fertile_area)
  581.  
  582.             local pos_biome_ok = pos.y >= biome.min_elevation and pos.y <= biome.max_elevation
  583.                 and fertility > biome.plantlife_limit
  584.                 and temperature <= biome.temp_min and temperature >= biome.temp_max
  585.                 and humidity <= biome.humidity_min and humidity >= biome.humidity_max
  586.                 and biome_lib:is_node_loaded(p_top)
  587.  
  588.             if not pos_biome_ok then
  589.                 return -- Outside of biome
  590.             end
  591.  
  592.             local n_light = minetest.get_node_light(p_top, nil)
  593.             if n_light < biome.light_min or n_light > biome.light_max then
  594.                 return -- Too dark or too bright
  595.             end
  596.  
  597.             if biome.avoid_nodes and biome.avoid_radius and minetest.find_node_near(
  598.                     p_top, biome.avoid_radius + math.random(-1.5,2), biome.avoid_nodes) then
  599.                 return -- Nodes to avoid are nearby
  600.             end
  601.  
  602.             if biome.neighbors and biome.ncount and
  603.                     #minetest.find_nodes_in_area(
  604.                         {x=pos.x-1, y=pos.y, z=pos.z-1},
  605.                         {x=pos.x+1, y=pos.y, z=pos.z+1},
  606.                         biome.neighbors
  607.                     ) <= biome.ncount then
  608.                 return -- Near neighbour nodes are not present
  609.             end
  610.  
  611.             local NEAR_DST = biome.near_nodes_size
  612.             if biome.near_nodes and biome.near_nodes_count and biome.near_nodes_size and
  613.                     #minetest.find_nodes_in_area(
  614.                         {x=pos.x-NEAR_DST, y=pos.y-biome.near_nodes_vertical, z=pos.z-NEAR_DST},
  615.                         {x=pos.x+NEAR_DST, y=pos.y+biome.near_nodes_vertical, z=pos.z+NEAR_DST},
  616.                         biome.near_nodes
  617.                     ) < biome.near_nodes_count then
  618.                 return -- Far neighbour nodes are not present
  619.             end
  620.  
  621.             if (biome.air_count and biome.air_size) and
  622.                     #minetest.find_nodes_in_area(
  623.                         {x=p_top.x-biome.air_size, y=p_top.y, z=p_top.z-biome.air_size},
  624.                         {x=p_top.x+biome.air_size, y=p_top.y, z=p_top.z+biome.air_size},
  625.                         "air"
  626.                     ) < biome.air_count then
  627.                 return -- Not enough air
  628.             end
  629.  
  630.             local walldir = biome_lib:find_adjacent_wall(p_top, biome.verticals_list, biome.choose_random_wall)
  631.             if biome.alt_wallnode and walldir then
  632.                 if n_top.name == "air" then
  633.                     minetest.swap_node(p_top, { name = biome.alt_wallnode, param2 = walldir })
  634.                 end
  635.                 return
  636.             end
  637.  
  638.             local currentsurface = minetest.get_node(pos).name
  639.  
  640.             if biome_lib.default_water_nodes[currentsurface] and
  641.                     #minetest.find_nodes_in_area(
  642.                         {x=pos.x, y=pos.y-biome.depth_max-1, z=pos.z},
  643.                         vector.new(pos),
  644.                         biome_lib.default_wet_surfaces
  645.                     ) == 0 then
  646.                 return -- On water but no ground nearby
  647.             end
  648.  
  649.             local rnd = math.random(1, biome.spawn_plants_count)
  650.             local plant_to_spawn = biome.spawn_plants[rnd]
  651.             local fdir = biome.facedir
  652.             if biome.random_facedir then
  653.                 fdir = math.random(biome.random_facedir[1],biome.random_facedir[2])
  654.             end
  655.             if type(biome.spawn_plants) == "string" then
  656.                 assert(loadstring(biome.spawn_plants.."(...)"))(pos)
  657.             elseif not biome.spawn_on_side and not biome.spawn_on_bottom and not biome.spawn_replace_node then
  658.                 if n_top.name == "air" then
  659.                     minetest.swap_node(p_top, { name = plant_to_spawn, param2 = fdir })
  660.                 end
  661.             elseif biome.spawn_replace_node then
  662.                 minetest.swap_node(pos, { name = plant_to_spawn, param2 = fdir })
  663.  
  664.             elseif biome.spawn_on_side then
  665.                 local onside = biome_lib:find_open_side(pos)
  666.                 if onside then
  667.                     minetest.swap_node(onside.newpos, { name = plant_to_spawn, param2 = onside.facedir })
  668.                 end
  669.             elseif biome.spawn_on_bottom then
  670.                 if minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
  671.                     minetest.swap_node({x=pos.x, y=pos.y-1, z=pos.z}, { name = plant_to_spawn, param2 = fdir} )
  672.                 end
  673.             end
  674.         end
  675.     })
  676. end
  677.  
  678. -- Function to decide how to replace a plant - either grow it, replace it with
  679. -- a tree, run a function, or die with an error.
  680.  
  681. function biome_lib:replace_object(pos, replacement, grow_function, walldir, seeddiff)
  682.     local growtype = type(grow_function)
  683.     if growtype == "table" then
  684.         minetest.swap_node(pos, biome_lib.air)
  685.         biome_lib:grow_tree(pos, grow_function)
  686.         return
  687.     elseif growtype == "function" then
  688.         local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale)
  689.         local fertility, temperature, _ = get_biome_data(pos, perlin_fertile_area)
  690.         grow_function(pos, fertility, temperature, walldir)
  691.         return
  692.     elseif growtype == "string" then
  693.         local perlin_fertile_area = minetest.get_perlin(seeddiff, perlin_octaves, perlin_persistence, perlin_scale)
  694.         local fertility, temperature, _ = get_biome_data(pos, perlin_fertile_area)
  695.         assert(loadstring(grow_function.."(...)"))(pos, fertility, temperature, walldir)
  696.         return
  697.     elseif growtype == "nil" then
  698.         minetest.swap_node(pos, { name = replacement, param2 = walldir})
  699.         return
  700.     elseif growtype ~= "nil" and growtype ~= "string" and growtype ~= "table" then
  701.         error("Invalid grow function "..dump(grow_function).." used on object at ("..dump(pos)..")")
  702.     end
  703. end
  704.  
  705. dofile(biome_lib.modpath .. "/search_functions.lua")
  706. assert(loadfile(biome_lib.modpath .. "/growth.lua"))(time_scale)
  707.  
  708. -- Check for infinite stacks
  709.  
  710. if minetest.get_modpath("unified_inventory") or not minetest.settings:get_bool("creative_mode") then
  711.     biome_lib.expect_infinite_stacks = false
  712. else
  713.     biome_lib.expect_infinite_stacks = true
  714. end
  715.  
  716. -- read a field from a node's definition
  717.  
  718. function biome_lib:get_nodedef_field(nodename, fieldname)
  719.     if not minetest.registered_nodes[nodename] then
  720.         return nil
  721.     end
  722.     return minetest.registered_nodes[nodename][fieldname]
  723. end
  724.  
  725. if DEBUG then
  726.     biome_lib.last_count = 0
  727.  
  728.     function biome_lib.show_pending_block_count()
  729.         if biome_lib.last_count ~= #biome_lib.block_log then
  730.             biome_lib:dbg("Pending block count: "..#biome_lib.block_log)
  731.             biome_lib.last_count = #biome_lib.block_log
  732.         end
  733.         minetest.after(1, biome_lib.show_pending_block_count)
  734.     end
  735.  
  736.     biome_lib.show_pending_block_count()
  737.  
  738.     minetest.after(0, function()
  739.         print("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread")
  740.         print("across "..#biome_lib.actionslist_aircheck.." actions with air-checking and "..#biome_lib.actionslist_no_aircheck.." actions without.")
  741.     end)
  742.  
  743. end
  744.  
  745. print("[Biome Lib] Loaded")
  746.  
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×