cartographer/map_formspec.lua

193 lines
7.6 KiB
Lua

-- Arguments
-- map_data: The cartographer map data table
local map_data, gui, skin = ...;
-- Constants
local TILE_SIZE = 0.25;
local TILE_OFFSET = 0.24; -- Slightly smaller than TILE_SIZE. We overlap tiles slightly to minimize seams
-- NoiseParams table for tile variations
local MAP_NOISE = {
offset = 0,
scale = 1,
spread = {x = 2, y = 2, z = 2},
seed = tonumber(minetest.get_mapgen_setting("seed")),
octaves = 2,
persist = 0.63,
lacunarity = 2.0,
flags = "defaults, absvalue",
};
-- Get the variant of the tile at a given position
-- prefix: The part of the tile texture name before the variant
-- x: The X position of the tile (in map coordinates)
-- z: The Z position of the tile (in map coordinates)
-- noise: A 2d lookup table of perlin noise. Must contain the position [x + 1][y + 1]
--
-- Returns a string in the format 'prefix.variant.png', where variant is a number from 1 to 4
local function get_variant(prefix, x, z, noise)
return string.format("%s.%d.png", prefix, math.floor(math.min(noise[x + 1][z + 1] * 3, 3)) + 1);
end
-- Generate formspec markup for a given map
-- x: The X position of the map (in map coordinates)
-- y: The Z position of the map (in map coordinates)
-- w: The width of the map (in map coordinates)
-- h: The height of the map (in map coordinates)
-- player_x: The X position of the player marker (in map coordinates)
-- player_y: The Y position of the player marker (in map coordinates)
-- detail: The detail level
-- map_scale: Integer scaling factor for displaying a zoomed-out map
-- height_mode: If true, displaces tiles by their height
-- (Optional) is_visible: Callback to determine if a tile should be drawn
-- (Optional) get_marker: Callback to get the marker for any given tile
--
-- Additional arguments are provided to is_visible / get_marker
--
-- Returns a formspec string
local function generate_map(x, y, w, h, player_x, player_y, detail, map_scale, height_mode, is_visible, get_marker, ...)
local str = "";
local noise = PerlinNoiseMap(MAP_NOISE, { x=w + 1, y=h + 1, z=1}):get_2d_map({ x=x, y=y});
for i = x,x + w,1 do
local fx = (i - x) * TILE_OFFSET;
local column = map_data.generated[i * map_scale];
for j = y + h,y,-1 do
local fy = (y + h - j) * TILE_OFFSET;
if column == nil or column[j * map_scale] == nil or (is_visible and not is_visible(..., i, j)) then
local unknown_tex = cartographer.detail_texture(skin.unknown_biome_textures, detail);
str = str .. gui.image {
x = fx,
y = fy,
w = TILE_SIZE,
h = TILE_SIZE,
image = get_variant(unknown_tex, i - x, j - y, noise),
};
else
local name = minetest.get_biome_name(column[j * map_scale].biome);
local height = column[j * map_scale].height;
local biome = cartographer.get_biome_texture(name, math.floor(height + 0.5), detail);
if biome then
local depth = math.min(math.max(math.floor(height / 8), -8), 0) * -1
height = math.max(math.min(math.floor(height / (math.max(map_scale * 0.5, 1) + 4)), 8), 0)
local mod = "";
if height > 0 then
mod = "^[colorize:white:"..tostring(height * 10)
height = height * 0.05;
if height_mode then
str = str .. gui.image {
x = fx,
y = fy - height + TILE_OFFSET,
w = TILE_SIZE,
h = height + 0.01,
image = cartographer.detail_texture(skin.cliff_textures, detail) .. ".png",
};
else
height = 0;
end
elseif depth > 0 then
mod = "^[colorize:#1f1f34:"..tostring(depth * 10)
end
str = str .. gui.image {
x = fx,
y = fy - height,
w = TILE_SIZE,
h = TILE_SIZE,
image = get_variant(biome, i - x, j - y, noise) .. mod,
};
if get_marker then
local marker = cartographer.get_marker_texture(get_marker(..., i, j), detail);
if marker then
str = str .. gui.image {
x = fx,
y = fy - height,
w = TILE_SIZE,
h = TILE_SIZE,
image = marker .. ".png",
};
end
end
if i == player_x and j == player_y then
local player_icon = cartographer.detail_texture(skin.player_icons, detail);
str = str .. gui.animated_image {
x = fx,
y = fy - height,
w = TILE_SIZE,
h = TILE_SIZE,
animation = player_icon,
};
end
else
local unknown_tex = cartographer.detail_texture(skin.unknown_biome_textures, detail);
str = str .. gui.image {
x = fx,
y = fy,
w = TILE_SIZE,
h = TILE_SIZE,
image = get_variant(unknown_tex, i - x, j - y, noise),
};
end
end
end
end
return str;
end
local map_formspec = {};
-- Get the formspec for a given map segment
-- x: The X position of the map, in map coordinates
-- y: The Y position of the map, in map coordinates
-- w: The width of the map, in map coordinates
-- h: The height of the map, in map coordinates
-- detail: The detail level of the map
-- scale: Integer scaling factor for displaying a zoomed-out map
-- height_mode: If true, displaces tiles by their height
--
-- Returns a formspec string, the width of the formspec, and the height of the
-- formspec
function map_formspec.from_coords(x, y, w, h, detail, scale, height_mode)
local formspec_width = (w + 1) * TILE_OFFSET + 0.01;
local formspec_height = (h + 1) * TILE_OFFSET + 0.01;
return gui.formspec {
w = formspec_width,
h = formspec_height,
generate_map(x - (w * 0.5), y - (h * 0.5), w, h, x, y, detail, scale, height_mode),
}, formspec_width, formspec_height;
end
-- Get the formspec for a given map table
-- map: The map to use
-- x: The X position of the player marker, in map coordinates
-- y: The Y position of the player marker, in map coordinates
-- height_mode: If true, displaces tiles by their height
--
-- Returns a formspec string, the width of the formspec, and the height of the
-- formspec
function map_formspec.from_map(map, x, y, height_mode)
local formspec_width = (map.w + 1) * TILE_OFFSET + 0.01;
local formspec_height = (map.h + 1) * TILE_OFFSET + 0.01;
return gui.formspec {
w = formspec_width,
h = formspec_height,
generate_map(map.x, map.z, map.w, map.h, x, y, map.detail, map.scale, height_mode, cartographer.is_filled, cartographer.get_marker, map),
}, formspec_width, formspec_height;
end
return map_formspec;