Advertisement
Guest User

Untitled

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