function cartographer.create_map(x, z, w, h, filled, detail, scale) local id = _cartographer.next_map_id; local map = { id = id, x = x, z = z, w = w, h = h, detail = detail, scale = scale, fill = {}, markers = {}, }; _cartographer.maps[id] = map; if filled then cartographer.fill(map, x, z, w, h); end _cartographer.next_map_id = _cartographer.next_map_id + 1; return id; end function cartographer.get_map(id) return _cartographer.maps[id]; end function cartographer.resize_map(id, w, h) local map = cartographer.get_map(id); if map ~= nil and w >= map.w and h >= map.h then map.w = w; map.h = h; end end function cartographer.fill(map, x, z, w, h) if map == nil then return; end for i = math.max(x, map.x),math.min(x + w - 1, map.x + map.w),1 do for j = math.max(z, map.z),math.min(z + h - 1, map.z + map.h),1 do map.fill[(i - map.x) + ((j - map.z) * map.w)] = map.detail; end end end function cartographer.set_marker(map, x, z, marker) if map == nil or x < map.x or x > map.x + map.w or z < map.z or z > map.z + map.h then return; end if not map.markers[x] then map.markers[x] = { [z] = marker, }; else map.markers[x][z] = marker; end end function cartographer.get_marker(map, x, z) if map == nil or x < map.x or x > map.x + map.w or z < map.z or z > map.z + map.h or not map.markers[x] then return nil; end return map.markers[x][z]; end -- Fill in the local area of a map around a position -- id: A map ID -- x: The x position, in world coordinates -- z: The z position, in world coordinates function cartographer.fill_local(id, x, z) local map = cartographer.get_map(id); x, z = cartographer.to_map_coordinates(map, x, z); -- TODO: Adjust size to match map scale if map and x >= map.x - 2 and x <= map.x + map.w + 1 and z >= map.z - 2 and z <= map.z + map.h + 1 then cartographer.fill(map, x - 2, z - 2, 5, 5); end end -- Convert a position in world coordinates to the given map's coordinate system -- map: The map to use as reference, or nil to use the default size (map scale of 1) -- x: The x position, in world coordinates -- z: The z position, in world coordinates -- -- Returns The converted x and z coordinates function cartographer.to_map_coordinates(map, x, z) if not map or map.scale == 0 then return tochunk(x), tochunk(z); end return math.floor(tochunk(x) / map.scale + 0.5), math.floor(tochunk(z) / map.scale + 0.5); end -- Periodically-called function to fill in maps and queue chunks for manual -- scanning local function fill_loop() -- Fill in all player-held maps for _,p in ipairs(minetest.get_connected_players()) do local inventory = p:get_inventory(); local pos = p:get_pos(); if pos.y > -10 then for i = 1,inventory:get_size("main") do local stack = inventory:get_stack("main", i); if stack:get_name() == "cartographer:map" then cartographer.fill_local(stack:get_meta():get_int("cartographer:map_id"), pos.x, pos.z); end end for i = -2,2 do for j = -2,2 do local adjusted_pos = { x = pos.x + fromchunk(i), y = pos.y, z = pos.z + fromchunk(j), } cartographer.queue_region(adjusted_pos); end end end end for _ = 1,10 do cartographer.scan_regions(); end minetest.after(5, fill_loop); end minetest.after(5, fill_loop); -- Register a biome with textures to display -- name: A string containing the biome name -- textures: A table of texture names. -- These should correspond with detail levels, -- any detail level past the length of the table will return the last texture -- (Optional) min_height: The minimum Y position where this biome data should be used -- (Optional) max_height: The maximum Y position where this biome data should be used function cartographer.register_biome(name, textures, min_height, max_height) _cartographer.biome_lookup[#_cartographer.biome_lookup + 1] = { name = name, textures = textures, min_height = min_height, max_height = max_height, }; end -- Format marker ids to allow their use as formspec element ids. -- We're mostly concerned with guarding against the : character because it is -- common for ids and has an alternate meaning in formspecs. -- id: The id to format -- -- Returns the formatted id local function format_marker_id(id) return id:gsub(":", "_"); end -- Find the marker data for a given id -- id: The id to search for -- -- Returns the marker data, or nil if not found function cartographer.get_marker_data(id) if not id then return nil; end id = format_marker_id(id); for _,marker in pairs(_cartographer.marker_lookup) do if marker.id == id then return marker; end end return nil; end -- Register a marker with textures to display -- id: A string containing the id of the marker -- name: A string containing the displayedname of the marker -- textures: A table of texture names. -- These should correspond with detail levels, -- any detail level past the length of the table will return the last texture function cartographer.register_marker(id, name, textures) if not id then return nil; end id = format_marker_id(id); local existing_marker = cartographer.get_marker_data(id); if existing_marker then existing_marker.name = name; existing_marker.textures = textures; else _cartographer.marker_lookup[#_cartographer.marker_lookup+1] = { id = id, name = name, textures = textures, }; end end function cartographer.is_filled(map, x, z) if map == nil then return false; end -- minetest.chat_send_all(tostring(x).. ", " .. tostring(z) .. "(" .. tostring(x + (z * map.w)) .."): " .. tostring(map.fill[x + (z * map.w)])); return map.fill[(x - map.x) + ((z - map.z) * map.w)] ~= nil; end -- Get an entry from a list for a given detail level -- textures: An array of textures -- detail: The detail level -- -- Returns the entry at detail, or the last entry if detail is out-of-bounds function cartographer.detail_texture(textures, detail) return textures[math.min(detail, #textures)]; end -- Get the texture name (minus index/extension) for the given biome, height, and detail level. -- name: A string containing the biome name -- height: A number representing the Y position of the biome -- detail: The detail level -- Returns a string with a texture name, or nil if no matching biome entry was found. function cartographer.get_biome_texture(name, height, detail) for _,biome in ipairs(_cartographer.biome_lookup) do if biome.name == name and (not biome.min_height or height >= biome.min_height) and (not biome.max_height or height <= biome.max_height) then return cartographer.detail_texture(biome.textures, detail); end end return nil; end -- Get the texture name (minus extension) for the given marker and detail level. -- id: A string containing the marker id -- detail: The detail level -- Returns a string with a texture name, or nil if no matching marker was found. function cartographer.get_marker_texture(id, detail) if not id then return nil; end id = format_marker_id(id); local marker = cartographer.get_marker_data(id); if marker then return marker.textures[math.min(detail, #marker.textures)]; end return nil; end -- Play a feedback sound localized on the given player -- sound: The sound to play -- player: The player who triggered the sound function cartographer.map_sound(sound, player) minetest.sound_play(sound, { pos=player:get_pos(), max_hear_distance=5 }, true); end