From c9b464f32990c30e7b519ee4bb3a16fa82c09a89 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 3 Apr 2020 12:51:01 +0200 Subject: [PATCH] Add findbiome mod --- mods/MISC/findbiome/README.md | 23 ++ mods/MISC/findbiome/init.lua | 320 +++++++++++++++++++++ mods/MISC/findbiome/license.txt | 24 ++ mods/MISC/findbiome/locale/findbiome.de.tr | 10 + mods/MISC/findbiome/locale/template.txt | 10 + mods/MISC/findbiome/mod.conf | 3 + 6 files changed, 390 insertions(+) create mode 100644 mods/MISC/findbiome/README.md create mode 100644 mods/MISC/findbiome/init.lua create mode 100644 mods/MISC/findbiome/license.txt create mode 100644 mods/MISC/findbiome/locale/findbiome.de.tr create mode 100644 mods/MISC/findbiome/locale/template.txt create mode 100644 mods/MISC/findbiome/mod.conf diff --git a/mods/MISC/findbiome/README.md b/mods/MISC/findbiome/README.md new file mode 100644 index 000000000..04c63199a --- /dev/null +++ b/mods/MISC/findbiome/README.md @@ -0,0 +1,23 @@ +# Minetest mod: findbiome + +## Description +This is a mod to help with mod/game development for Minetest. +It adds a command (“findbiome”) to find a biome nearby and teleport you to it +and another command (“listbiomes”) to list biomes. + +Version: 1.0.1 + +## Known limitations +There's no guarantee you will always find the biome, even if it exists in the world. +This can happen if the biome is very obscure or small, but usually you should be +able to find the biome. + +If the biome could not be found, just move to somewhere else and try again. + +## Authors +- paramat (MIT License) +- Wuzzy (MIT License) + +See license.txt for license information. + +This mod is based on the algorithm of the "spawn" mod from Minetest Game 5.0.0. diff --git a/mods/MISC/findbiome/init.lua b/mods/MISC/findbiome/init.lua new file mode 100644 index 000000000..ce7fd9799 --- /dev/null +++ b/mods/MISC/findbiome/init.lua @@ -0,0 +1,320 @@ +local S = minetest.get_translator("findbiome") + +local mod_biomeinfo = minetest.get_modpath("biomeinfo") ~= nil +local mg_name = minetest.get_mapgen_setting("mg_name") +local water_level = tonumber(minetest.get_mapgen_setting("water_level")) + +-- Calculate the maximum playable limit +local mapgen_limit = tonumber(minetest.get_mapgen_setting("mapgen_limit")) +local chunksize = tonumber(minetest.get_mapgen_setting("chunksize")) +local playable_limit = math.max(mapgen_limit - (chunksize + 1) * 16, 0) + +-- Parameters +------------- + +-- Resolution of search grid in nodes. +local res = 64 +-- Number of points checked in the square search grid (edge * edge). +local checks = 128 * 128 + +-- End of parameters +-------------------- + +-- Direction table + +local dirs = { + {x = 0, y = 0, z = 1}, + {x = -1, y = 0, z = 0}, + {x = 0, y = 0, z = -1}, + {x = 1, y = 0, z = 0}, +} + +-- Returns true if pos is within the world boundaries +local function is_in_world(pos) + return not (math.abs(pos.x) > playable_limit or math.abs(pos.y) > playable_limit or math.abs(pos.z) > playable_limit) +end + +-- Checks if pos is within the biome's boundaries. If it isn't, places pos inside the boundaries. +local function adjust_pos_to_biome_limits(pos, biome_id) + local bpos = table.copy(pos) + local biome_name = minetest.get_biome_name(biome_id) + local biome = minetest.registered_biomes[biome_name] + if not biome then + minetest.log("error", "[findbiome] adjust_pos_to_biome_limits non-existing biome!") + return bpos, true + end + local axes = {"y", "x", "z"} + local out_of_bounds = false + for a=1, #axes do + local ax = axes[a] + local min, max + if biome[ax.."_min"] then + min = biome[ax.."_min"] + else + min = -playable_limit + end + if biome[ax.."_max"] then + max = biome[ax.."_max"] + else + max = playable_limit + end + min = tonumber(min) + max = tonumber(max) + if bpos[ax] < min then + out_of_bounds = true + bpos[ax] = min + if max-min > 16 then + bpos[ax] = math.max(bpos[ax] + 8, -playable_limit) + end + end + if bpos[ax] > max then + out_of_bounds = true + bpos[ax] = max + if max-min > 16 then + bpos[ax] = math.min(bpos[ax] - 8, playable_limit) + end + end + end + return bpos, out_of_bounds +end + +-- Find the special default biome +local function find_default_biome() + local all_biomes = minetest.registered_biomes + local biome_count = 0 + for b, biome in pairs(all_biomes) do + biome_count = biome_count + 1 + end + -- Trivial case: No biomes registered, default biome is everywhere. + if biome_count == 0 then + local y = minetest.get_spawn_level(0, 0) + if not y then + y = 0 + end + return { x = 0, y = y, z = 0 } + end + local pos = {} + -- Just check a lot of random positions + -- It's a crappy algorithm but better than nothing. + for i=1, 100 do + pos.x = math.random(-playable_limit, playable_limit) + pos.y = math.random(-playable_limit, playable_limit) + pos.z = math.random(-playable_limit, playable_limit) + local biome_data = minetest.get_biome_data(pos) + if biome_data and minetest.get_biome_name(biome_data.biome) == "default" then + return pos + end + end + return nil +end + +local function find_biome(pos, biomes) + pos = vector.round(pos) + -- Pos: Starting point for biome checks. This also sets the y co-ordinate for all + -- points checked, so the suitable biomes must be active at this y. + + -- Initial variables + + local edge_len = 1 + local edge_dist = 0 + local dir_step = 0 + local dir_ind = 1 + local success = false + local spawn_pos + local biome_ids + + -- Get next position on square search spiral + local function next_pos() + if edge_dist == edge_len then + edge_dist = 0 + dir_ind = dir_ind + 1 + if dir_ind == 5 then + dir_ind = 1 + end + dir_step = dir_step + 1 + edge_len = math.floor(dir_step / 2) + 1 + end + + local dir = dirs[dir_ind] + local move = vector.multiply(dir, res) + + edge_dist = edge_dist + 1 + + return vector.add(pos, move) + end + + -- Position search + local function search() + local attempt = 1 + while attempt < 3 do + for iter = 1, checks do + local biome_data = minetest.get_biome_data(pos) + -- Sometimes biome_data is nil + local biome = biome_data and biome_data.biome + for id_ind = 1, #biome_ids do + local biome_id = biome_ids[id_ind] + pos = adjust_pos_to_biome_limits(pos, biome_id) + local spos = table.copy(pos) + if biome == biome_id then + local good_spawn_height = pos.y <= water_level + 16 and pos.y >= water_level + local spawn_y = minetest.get_spawn_level(spos.x, spos.z) + if spawn_y then + spawn_pos = {x = spos.x, y = spawn_y, z = spos.z} + elseif not good_spawn_height then + spawn_pos = {x = spos.x, y = spos.y, z = spos.z} + elseif attempt >= 2 then + spawn_pos = {x = spos.x, y = spos.y, z = spos.z} + end + if spawn_pos then + local adjusted_pos, outside = adjust_pos_to_biome_limits(spawn_pos, biome_id) + if is_in_world(spawn_pos) and not outside then + return true + end + end + end + end + + pos = next_pos() + end + attempt = attempt + 1 + end + return false + end + local function search_v6() + if not mod_biomeinfo then return + false + end + for iter = 1, checks do + local found_biome = biomeinfo.get_v6_biome(pos) + for i = 1, #biomes do + local searched_biome = biomes[i] + if found_biome == searched_biome then + local spawn_y = minetest.get_spawn_level(pos.x, pos.z) + if spawn_y then + spawn_pos = {x = pos.x, y = spawn_y, z = pos.z} + if is_in_world(spawn_pos) then + return true + end + end + end + end + + pos = next_pos() + end + + return false + end + + if mg_name == "v6" then + success = search_v6() + else + -- Table of suitable biomes + biome_ids = {} + for i=1, #biomes do + local id = minetest.get_biome_id(biomes[i]) + if not id then + return nil, false + end + table.insert(biome_ids, id) + end + success = search() + end + return spawn_pos, success + +end + +local mods_loaded = false +minetest.register_on_mods_loaded(function() + mods_loaded = true +end) + +-- Regiver chat commands +do + minetest.register_chatcommand("findbiome", { + description = S("Find and teleport to biome"), + params = S(""), + privs = { debug = true, teleport = true }, + func = function(name, param) + if not mods_loaded then + return false + end + local player = minetest.get_player_by_name(name) + if not player then + return false, S("No player.") + end + local pos = player:get_pos() + local invalid_biome = true + if mg_name == "v6" then + if not mod_biomeinfo then + return false, S("Not supported. The “biomeinfo” mod is required for v6 mapgen support!") + end + local biomes = biomeinfo.get_active_v6_biomes() + for b=1, #biomes do + if param == biomes[b] then + invalid_biome = false + break + end + end + else + if param == "default" then + local biome_pos = find_default_biome() + if biome_pos then + player:set_pos(biome_pos) + return true, S("Biome found at @1.", minetest.pos_to_string(biome_pos)) + else + return false, S("No biome found!") + end + end + local id = minetest.get_biome_id(param) + if id then + invalid_biome = false + end + end + if invalid_biome then + return false, S("Biome does not exist!") + end + local biome_pos, success = find_biome(pos, {param}) + if success then + player:set_pos(biome_pos) + return true, S("Biome found at @1.", minetest.pos_to_string(biome_pos)) + else + return false, S("No biome found!") + end + end, + }) + + minetest.register_chatcommand("listbiomes", { + description = S("List all biomes"), + params = "", + privs = { debug = true }, + func = function(name, param) + if not mods_loaded then + return false + end + local biomes + local b = 0 + if mg_name == "v6" then + if not mod_biomeinfo then + return false, S("Not supported. The “biomeinfo” mod is required for v6 mapgen support!") + end + biomes = biomeinfo.get_active_v6_biomes() + b = #biomes + else + biomes = {} + for k,v in pairs(minetest.registered_biomes) do + table.insert(biomes, k) + b = b + 1 + end + end + if b == 0 then + return true, S("No biomes.") + else + table.sort(biomes) + for b=1, #biomes do + minetest.chat_send_player(name, biomes[b]) + end + return true + end + end, + }) +end diff --git a/mods/MISC/findbiome/license.txt b/mods/MISC/findbiome/license.txt new file mode 100644 index 000000000..a466aabdc --- /dev/null +++ b/mods/MISC/findbiome/license.txt @@ -0,0 +1,24 @@ +License of source code +---------------------- + +The MIT License (MIT) +Copyright (C) 2018 paramat + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +For more details: +https://opensource.org/licenses/MIT diff --git a/mods/MISC/findbiome/locale/findbiome.de.tr b/mods/MISC/findbiome/locale/findbiome.de.tr new file mode 100644 index 000000000..d62332eaf --- /dev/null +++ b/mods/MISC/findbiome/locale/findbiome.de.tr @@ -0,0 +1,10 @@ +# textdomain: findbiome +Find and teleport to biome=Ein Biom finden und hinteleportieren += +No player.=Kein Spieler +Biome does not exist!=Biom existiert nicht! +Biome found at @1.=Biom gefunden bei @1. +No biome found!=Kein Biom gefunden! +List all biomes=Alle Biome auflisten +No biomes.=Keine Biome. +Not supported. The “biomeinfo” mod is required for v6 mapgen support!=Nicht unterstützt. Die Mod „biomeinfo“ wird für Unterstützung des v6-Kartengenerators benötigt. diff --git a/mods/MISC/findbiome/locale/template.txt b/mods/MISC/findbiome/locale/template.txt new file mode 100644 index 000000000..f3dcc1958 --- /dev/null +++ b/mods/MISC/findbiome/locale/template.txt @@ -0,0 +1,10 @@ +# textdomain: findbiome +Find and teleport to biome= += +No player.= +Biome does not exist!= +Biome found at @1.= +No biome found!= +List all biomes= +No biomes.= +Not supported. The “biomeinfo” mod is required for v6 mapgen support!= diff --git a/mods/MISC/findbiome/mod.conf b/mods/MISC/findbiome/mod.conf new file mode 100644 index 000000000..a8ab4e3c1 --- /dev/null +++ b/mods/MISC/findbiome/mod.conf @@ -0,0 +1,3 @@ +name=findbiome +description=Add commands to list and find biomes +optional_depends=biomeinfo