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;