Guest User

Untitled

a guest
Apr 6th, 2021
43
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.     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.  
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.

×