1 local S
= minetest
.get_translator("findbiome")
3 local mod_biomeinfo
= minetest
.get_modpath("biomeinfo") ~= nil
4 local mg_name
= minetest
.get_mapgen_setting("mg_name")
5 local water_level
= tonumber(minetest
.get_mapgen_setting("water_level"))
7 -- Calculate the maximum playable limit
8 local mapgen_limit
= tonumber(minetest
.get_mapgen_setting("mapgen_limit"))
9 local chunksize
= tonumber(minetest
.get_mapgen_setting("chunksize"))
10 local playable_limit
= math
.max(mapgen_limit
- (chunksize
+ 1) * 16, 0)
15 -- Resolution of search grid in nodes.
17 -- Number of points checked in the square search grid (edge * edge).
18 local checks
= 128 * 128
26 {x
= 0, y
= 0, z
= 1},
27 {x
= -1, y
= 0, z
= 0},
28 {x
= 0, y
= 0, z
= -1},
29 {x
= 1, y
= 0, z
= 0},
32 -- Returns true if pos is within the world boundaries
33 local function is_in_world(pos
)
34 return not (math
.abs(pos
.x
) > playable_limit
or math
.abs(pos
.y
) > playable_limit
or math
.abs(pos
.z
) > playable_limit
)
37 -- Checks if pos is within the biome's boundaries. If it isn't, places pos inside the boundaries.
38 local function adjust_pos_to_biome_limits(pos
, biome_id
)
39 local bpos
= table.copy(pos
)
40 local biome_name
= minetest
.get_biome_name(biome_id
)
41 local biome
= minetest
.registered_biomes
[biome_name
]
43 minetest
.log("error", "[findbiome] adjust_pos_to_biome_limits non-existing biome!")
46 local axes
= {"y", "x", "z"}
47 local out_of_bounds
= false
51 if biome
[ax
.."_min"] then
52 min = biome
[ax
.."_min"]
56 if biome
[ax
.."_max"] then
57 max = biome
[ax
.."_max"]
63 if bpos
[ax
] < min then
67 bpos
[ax
] = math
.max(bpos
[ax
] + 8, -playable_limit
)
70 if bpos
[ax
] > max then
74 bpos
[ax
] = math
.min(bpos
[ax
] - 8, playable_limit
)
78 return bpos
, out_of_bounds
81 -- Find the special default biome
82 local function find_default_biome()
83 local all_biomes
= minetest
.registered_biomes
85 for b
, biome
in pairs(all_biomes
) do
86 biome_count
= biome_count
+ 1
88 -- Trivial case: No biomes registered, default biome is everywhere.
89 if biome_count
== 0 then
90 local y
= minetest
.get_spawn_level(0, 0)
94 return { x
= 0, y
= y
, z
= 0 }
97 -- Just check a lot of random positions
98 -- It's a crappy algorithm but better than nothing.
100 pos
.x
= math
.random(-playable_limit
, playable_limit
)
101 pos
.y
= math
.random(-playable_limit
, playable_limit
)
102 pos
.z
= math
.random(-playable_limit
, playable_limit
)
103 local biome_data
= minetest
.get_biome_data(pos
)
104 if biome_data
and minetest
.get_biome_name(biome_data
.biome
) == "default" then
111 local function find_biome(pos
, biomes
)
112 pos
= vector
.round(pos
)
113 -- Pos: Starting point for biome checks. This also sets the y co-ordinate for all
114 -- points checked, so the suitable biomes must be active at this y.
122 local success
= false
126 -- Get next position on square search spiral
127 local function next_pos()
128 if edge_dist
== edge_len
then
130 dir_ind
= dir_ind
+ 1
134 dir_step
= dir_step
+ 1
135 edge_len
= math
.floor(dir_step
/ 2) + 1
138 local dir
= dirs
[dir_ind
]
139 local move
= vector
.multiply(dir
, res
)
141 edge_dist
= edge_dist
+ 1
143 return vector
.add(pos
, move
)
147 local function search()
150 for iter
= 1, checks
do
151 local biome_data
= minetest
.get_biome_data(pos
)
152 -- Sometimes biome_data is nil
153 local biome
= biome_data
and biome_data
.biome
154 for id_ind
= 1, #biome_ids
do
155 local biome_id
= biome_ids
[id_ind
]
156 pos
= adjust_pos_to_biome_limits(pos
, biome_id
)
157 local spos
= table.copy(pos
)
158 if biome
== biome_id
then
159 local good_spawn_height
= pos
.y
<= water_level
+ 16 and pos
.y
>= water_level
160 local spawn_y
= minetest
.get_spawn_level(spos
.x
, spos
.z
)
162 spawn_pos
= {x
= spos
.x
, y
= spawn_y
, z
= spos
.z
}
163 elseif not good_spawn_height
then
164 spawn_pos
= {x
= spos
.x
, y
= spos
.y
, z
= spos
.z
}
165 elseif attempt
>= 2 then
166 spawn_pos
= {x
= spos
.x
, y
= spos
.y
, z
= spos
.z
}
169 local adjusted_pos
, outside
= adjust_pos_to_biome_limits(spawn_pos
, biome_id
)
170 if is_in_world(spawn_pos
) and not outside
then
179 attempt
= attempt
+ 1
183 local function search_v6()
184 if not mod_biomeinfo
then return
187 for iter
= 1, checks
do
188 local found_biome
= biomeinfo
.get_v6_biome(pos
)
189 for i
= 1, #biomes
do
190 local searched_biome
= biomes
[i
]
191 if found_biome
== searched_biome
then
192 local spawn_y
= minetest
.get_spawn_level(pos
.x
, pos
.z
)
194 spawn_pos
= {x
= pos
.x
, y
= spawn_y
, z
= pos
.z
}
195 if is_in_world(spawn_pos
) then
208 if mg_name
== "v6" then
209 success
= search_v6()
211 -- Table of suitable biomes
214 local id
= minetest
.get_biome_id(biomes
[i
])
218 table.insert(biome_ids
, id
)
222 return spawn_pos
, success
226 local mods_loaded
= false
227 minetest
.register_on_mods_loaded(function()
231 -- Regiver chat commands
233 minetest
.register_chatcommand("findbiome", {
234 description
= S("Find and teleport to biome"),
235 params
= S("<biome>"),
236 privs
= { debug
= true, teleport
= true },
237 func
= function(name
, param
)
238 if not mods_loaded
then
241 local player
= minetest
.get_player_by_name(name
)
243 return false, S("No player.")
245 local pos
= player
:get_pos()
246 local invalid_biome
= true
247 if mg_name
== "v6" then
248 if not mod_biomeinfo
then
249 return false, S("Not supported. The “biomeinfo” mod is required for v6 mapgen support!")
251 local biomes
= biomeinfo
.get_active_v6_biomes()
253 if param
== biomes
[b
] then
254 invalid_biome
= false
259 if param
== "default" then
260 local biome_pos
= find_default_biome()
262 player
:set_pos(biome_pos
)
263 return true, S("Biome found at @1.", minetest
.pos_to_string(biome_pos
))
265 return false, S("No biome found!")
268 local id
= minetest
.get_biome_id(param
)
270 invalid_biome
= false
273 if invalid_biome
then
274 return false, S("Biome does not exist!")
276 local biome_pos
, success
= find_biome(pos
, {param
})
278 player
:set_pos(biome_pos
)
279 return true, S("Biome found at @1.", minetest
.pos_to_string(biome_pos
))
281 return false, S("No biome found!")
286 minetest
.register_chatcommand("listbiomes", {
287 description
= S("List all biomes"),
289 privs
= { debug
= true },
290 func
= function(name
, param
)
291 if not mods_loaded
then
296 if mg_name
== "v6" then
297 if not mod_biomeinfo
then
298 return false, S("Not supported. The “biomeinfo” mod is required for v6 mapgen support!")
300 biomes
= biomeinfo
.get_active_v6_biomes()
304 for k
,v
in pairs(minetest
.registered_biomes
) do
305 table.insert(biomes
, k
)
310 return true, S("No biomes.")
314 minetest
.chat_send_player(name
, biomes
[b
])