cartographer/map_api.lua
2020-06-10 19:29:06 -04:00

230 lines
6.2 KiB
Lua

local map_data, chunk, scanner, marker_lookup = ...;
local Map = {};
Map.__index = Map;
for _,loaded_map in ipairs(map_data.maps) do
setmetatable(loaded_map, Map);
end
function Map.resize(self, w, h)
if w >= self.w and h >= self.h then
self.w = w;
self.h = h;
-- FIXME: Is this really correct? Seems questionable.
end
end
function Map.fill_area(self, x, z, w, h)
for i = math.max(x, self.x),math.min(x + w - 1, self.x + self.w),1 do
for j = math.max(z, self.z),math.min(z + h - 1, self.z + self.h),1 do
self.fill[(i - self.x) + ((j - self.z) * self.w)] = self.detail;
end
end
end
function Map.set_marker(self, x, z, marker)
if x < self.x or x > self.x + self.w or z < self.z or z > self.z + self.h then
return;
end
if not self.markers[x] then
self.markers[x] = {
[z] = marker,
};
else
self.markers[x][z] = marker;
end
end
function Map.get_marker(self, x, z)
if x < self.x or x > self.x + self.w or z < self.z or z > self.z + self.h or not self.markers[x] then
return nil;
end
return self.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 Map.fill_local(self, x, z)
x, z = self:to_coordinates(x, z);
-- TODO: Adjust size to match map scale
if x >= self.x - 2 and x <= self.x + self.w + 1 and z >= self.z - 2 and z <= self.z + self.h + 1 then
self:fill_area(x - 2, z - 2, 5, 5);
end
end
-- Convert a position in world coordinates to the given map's coordinate system
-- self: The map to use as reference
-- x: The x position, in world coordinates
-- z: The z position, in world coordinates
--
-- Returns The converted x and z coordinates
function Map.to_coordinates(self, x, z)
if self.scale == 0 then
return chunk.to(x), chunk.to(z);
end
return math.floor(chunk.to(x) / self.scale + 0.5), math.floor(chunk.to(z) / self.scale + 0.5);
end
function Map.is_filled(self, x, z)
return self.fill[(x - self.x) + ((z - self.z) * self.w)] ~= nil;
end
local maps = {
create = function(x, z, w, h, filled, detail, scale)
local id = map_data.next_map_id;
local map = {
id = id,
x = x,
z = z,
w = w,
h = h,
detail = detail,
scale = scale,
fill = {},
markers = {},
};
setmetatable(map, Map);
map_data.maps[id] = map;
if filled then
map:fill(map, x, z, w, h);
end
map_data.next_map_id = map_data.next_map_id + 1;
return id;
end,
get = function(id)
return map_data.maps[id];
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);
local map = maps.get(stack:get_meta():get_int("cartographer:map_id"));
if map then
map:fill_local(pos.x, pos.z);
end
end
for i = -2,2 do
for j = -2,2 do
local adjusted_pos = {
x = pos.x + chunk.from(i),
y = pos.y,
z = pos.z + chunk.from(j),
}
scanner.queue_region(adjusted_pos);
end
end
end
end
for _ = 1,10 do
scanner.scan_regions();
end
minetest.after(5, fill_loop);
end
minetest.after(5, fill_loop);
-- 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(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
marker_lookup[#marker_lookup+1] = {
id = id,
name = name,
textures = textures,
};
end
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 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
return maps;