Advertisement
Guest User

Untitled

a guest
Apr 6th, 2021
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.29 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.     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.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement