mcl_maps = {} local S = minetest.get_translator("mcl_maps") local modpath = minetest.get_modpath("mcl_maps") local worldpath = minetest.get_worldpath() local map_textures_path = worldpath .. "/mcl_maps/" local math_min = math.min local math_max = math.max local dynamic_add_media = minetest.dynamic_add_media minetest.mkdir(map_textures_path) local function load_json_file(name) local file = assert(io.open(modpath .. "/" .. name .. ".json", "r")) local data = minetest.parse_json(file:read()) file:close() return data end local texture_colors = load_json_file("colors") local palettes = load_json_file("palettes") local color_cache = {} local creating_maps = {} local loaded_maps = {} local c_air = minetest.get_content_id("air") function mcl_maps.create_map(pos) local minp = vector.subtract(vector.floor(pos), 64) local maxp = vector.add(minp, 127) local itemstack = ItemStack("mcl_maps:filled_map") local meta = itemstack:get_meta() local id = string.format("%.0f-%.0f", minetest.hash_node_position(minp), mcl_time.get_seconds_irl()) meta:set_string("mcl_maps:id", id) meta:set_string("mcl_maps:minp", minetest.pos_to_string(minp)) meta:set_string("mcl_maps:maxp", minetest.pos_to_string(maxp)) tt.reload_itemstack_description(itemstack) creating_maps[id] = true minetest.emerge_area(minp, maxp, function(blockpos, action, calls_remaining) if calls_remaining > 0 then return end local vm = minetest.get_voxel_manip() local emin, emax = vm:read_from_map(minp, maxp) local data = vm:get_data() local param2data = vm:get_param2_data() local offset_x, offset_y, offset_z = minp.x - emin.x, minp.y - emin.y, minp.z - emin.z local dx = emax.x - emin.x + 1 local dy = (emax.y - emin.y + 1) * dx local offset = offset_z * dy + offset_y * dx + offset_x local map_y_start = 64 * dx local map_y_limit = 127 * dx --local pixels = "" local pixels = {} local last_heightmap for x = 1, 128 do local map_x = x + offset local heightmap = {} for z = 1, 128 do local map_z = (z-1) * dy + map_x local color, height local map_y = map_z + map_y_start local map_y_limit = map_z + map_y_limit while data[map_y] ~= c_air and map_y < map_y_limit do map_y = map_y + dx end while data[map_y] == c_air and map_y > map_z do map_y = map_y - dx end local c_id = data[map_y] color = color_cache[c_id] if color == nil then local nodename = minetest.get_name_from_content_id(c_id) local def = minetest.registered_nodes[nodename] if def then local texture if def.palette then texture = def.palette elseif def.tiles then texture = def.tiles[1] if type(texture) == "table" then texture = texture.name end end if texture then texture = texture:match("([^=^%^]-([^.]+))$"):split("^")[1] end if def.palette then local palette = palettes[texture] color = palette and {palette = palette} else color = texture_colors[texture] end end end if color and color.palette then color = color.palette[param2data[map_y] + 1] else color_cache[c_id] = color or false end if color and last_heightmap then local last_height = last_heightmap[z] local y = map_y - map_z if last_height < y then color = { math_min(255, color[1] + 16), math_min(255, color[2] + 16), math_min(255, color[3] + 16), } elseif last_height > y then color = { math_max(0, color[1] - 16), math_max(0, color[2] - 16), math_max(0, color[3] - 16), } end end height = map_y - map_z heightmap[z] = height or minp.y pixels[z] = pixels[z] or {} pixels[z][x] = color or {0, 0, 0} --if not color then color = {0, 0, 0} end --pixels = pixels .. minetest.colorspec_to_bytes({r = color[1], g = color[2], b = color[3]}) end last_heightmap = heightmap end --local png = minetest.encode_png(128, 128, pixels) --local f = io.open(map_textures_path .. "mcl_maps_map_texture_" .. id .. ".png", "wb") --if not f then return end --f:write(png) --f:close() tga_encoder.image(pixels):save(map_textures_path .. "mcl_maps_map_texture_" .. id .. ".tga") creating_maps[id] = nil end) return itemstack end --local loading_maps = {} function mcl_maps.load_map(id, callback) if id == "" or creating_maps[id] then--or loading_maps[id] then return end --local texture = "mcl_maps_map_texture_" .. id .. ".png" local texture = "mcl_maps_map_texture_" .. id .. ".tga" if not loaded_maps[id] then --loading_maps[id] = true if not minetest.features.dynamic_add_media_table then -- minetest.dynamic_add_media() blocks in -- Minetest 5.3 and 5.4 until media loads dynamic_add_media(map_textures_path .. texture, function(player_name) end) loaded_maps[id] = true if callback then callback(texture) end --loading_maps[id] = nil else -- minetest.dynamic_add_media() never blocks -- in Minetest 5.5, callback runs after load dynamic_add_media(map_textures_path .. texture, function(player_name) loaded_maps[id] = true if callback then callback(texture) end --loading_maps[id] = nil end) end end if loaded_maps[id] then if callback then callback(texture) end return texture end end function mcl_maps.load_map_item(itemstack) return mcl_maps.load_map(itemstack:get_meta():get_string("mcl_maps:id")) end local function fill_map(itemstack, placer, pointed_thing) local new_stack = mcl_util.call_on_rightclick(itemstack, placer, pointed_thing) if new_stack then return new_stack end if minetest.settings:get_bool("enable_real_maps", true) then local new_map = mcl_maps.create_map(placer:get_pos()) itemstack:take_item() if itemstack:is_empty() then return new_map else local inv = placer:get_inventory() if inv:room_for_item("main", new_map) then inv:add_item("main", new_map) else minetest.add_item(placer:get_pos(), new_map) end return itemstack end end end minetest.register_craftitem("mcl_maps:empty_map", { description = S("Empty Map"), _doc_items_longdesc = S("Empty maps are not useful as maps, but they can be stacked and turned to maps which can be used."), _doc_items_usagehelp = S("Rightclick to create a filled map (which can't be stacked anymore)."), inventory_image = "mcl_maps_map_empty.png", on_place = fill_map, on_secondary_use = fill_map, stack_max = 64, }) local filled_def = { description = S("Map"), _tt_help = S("Shows a map image."), _doc_items_longdesc = S("When created, the map saves the nearby area as an image that can be viewed any time by holding the map."), _doc_items_usagehelp = S("Hold the map in your hand. This will display a map on your screen."), inventory_image = "mcl_maps_map_filled.png^(mcl_maps_map_filled_markings.png^[colorize:#000000)", stack_max = 64, groups = {not_in_creative_inventory = 1, filled_map = 1, tool = 1}, } minetest.register_craftitem("mcl_maps:filled_map", filled_def) local filled_wield_def = table.copy(filled_def) filled_wield_def.use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false filled_wield_def.visual_scale = 1 filled_wield_def.wield_scale = {x = 1, y = 1, z = 1} filled_wield_def.paramtype = "light" filled_wield_def.drawtype = "mesh" filled_wield_def.node_placement_prediction = "" filled_wield_def.range = minetest.registered_items[""].range filled_wield_def.on_place = mcl_util.call_on_rightclick filled_wield_def.groups.no_wieldview = 1 filled_wield_def._wieldview_item = "mcl_maps:empty_map" local function player_base_to_node_id(base, colorspec, sex) return base:gsub("%.", "") .. minetest.colorspec_to_colorstring(colorspec):gsub("#", "") .. sex end bases = mcl_skins.base base_colors = mcl_skins.base_color for _, base in pairs(bases) do for _, base_color in pairs(base_colors) do local node_id = player_base_to_node_id(base, base_color, "male") local texture = mcl_skins.make_hand_texture(base, base_color) local def = table.copy(filled_wield_def) def.tiles = {texture} def.mesh = "mcl_meshhand.b3d" def._mcl_hand_id = node_id minetest.register_node("mcl_maps:filled_map_" .. node_id, def) node_id = player_base_to_node_id(base, base_color, "female") def = table.copy(filled_wield_def) def.tiles = {texture} def.mesh = "mcl_meshhand_female.b3d" def._mcl_hand_id = node_id minetest.register_node("mcl_maps:filled_map_" .. node_id, def) end end local old_add_item = minetest.add_item function minetest.add_item(pos, stack) stack = ItemStack(stack) if minetest.get_item_group(stack:get_name(), "filled_map") > 0 then stack:set_name("mcl_maps:filled_map") end return old_add_item(pos, stack) end tt.register_priority_snippet(function(itemstring, _, itemstack) if itemstack and minetest.get_item_group(itemstring, "filled_map") > 0 then local id = itemstack:get_meta():get_string("mcl_maps:id") if id ~= "" then return "#" .. id, mcl_colors.GRAY end end end) minetest.register_craft({ output = "mcl_maps:empty_map", recipe = { { "mcl_core:paper", "mcl_core:paper", "mcl_core:paper" }, { "mcl_core:paper", "group:compass", "mcl_core:paper" }, { "mcl_core:paper", "mcl_core:paper", "mcl_core:paper" }, } }) minetest.register_craft({ type = "shapeless", output = "mcl_maps:filled_map 2", recipe = {"group:filled_map", "mcl_maps:empty_map"}, }) local function on_craft(itemstack, player, old_craft_grid, craft_inv) if itemstack:get_name() == "mcl_maps:filled_map" then for _, stack in pairs(old_craft_grid) do if minetest.get_item_group(stack:get_name(), "filled_map") > 0 then itemstack:get_meta():from_table(stack:get_meta():to_table()) return itemstack end end end end minetest.register_on_craft(on_craft) minetest.register_craft_predict(on_craft) local maps = {} local huds = {} minetest.register_on_joinplayer(function(player) local map_def = { hud_elem_type = "image", text = "blank.png", position = {x = 0.75, y = 0.8}, alignment = {x = 0, y = -1}, offset = {x = 0, y = 0}, scale = {x = 2, y = 2}, } local marker_def = table.copy(map_def) marker_def.alignment = {x = 0, y = 0} huds[player] = { map = player:hud_add(map_def), marker = player:hud_add(marker_def), } end) minetest.register_on_leaveplayer(function(player) maps[player] = nil huds[player] = nil end) minetest.register_globalstep(function(dtime) for _, player in pairs(minetest.get_connected_players()) do local wield = player:get_wielded_item() local texture = mcl_maps.load_map_item(wield) local hud = huds[player] if texture then local wield_def = wield:get_definition() local hand_def = player:get_inventory():get_stack("hand", 1):get_definition() if hand_def and wield_def and hand_def._mcl_hand_id ~= wield_def._mcl_hand_id then wield:set_name("mcl_maps:filled_map_" .. hand_def._mcl_hand_id) player:set_wielded_item(wield) end if texture ~= maps[player] then player:hud_change(hud.map, "text", "[combine:140x140:0,0=mcl_maps_map_background.png:6,6=" .. texture) maps[player] = texture end local pos = vector.round(player:get_pos()) local meta = wield:get_meta() local minp = minetest.string_to_pos(meta:get_string("mcl_maps:minp")) local maxp = minetest.string_to_pos(meta:get_string("mcl_maps:maxp")) local marker = "mcl_maps_player_arrow.png" if pos.x < minp.x then marker = "mcl_maps_player_dot.png" pos.x = minp.x elseif pos.x > maxp.x then marker = "mcl_maps_player_dot.png" pos.x = maxp.x end if pos.z < minp.z then marker = "mcl_maps_player_dot.png" pos.z = minp.z elseif pos.z > maxp.z then marker = "mcl_maps_player_dot.png" pos.z = maxp.z end if marker == "mcl_maps_player_arrow.png" then local yaw = (math.floor(player:get_look_horizontal() * 180 / math.pi / 90 + 0.5) % 4) * 90 marker = marker .. "^[transformR" .. yaw end player:hud_change(hud.marker, "text", marker) player:hud_change(hud.marker, "offset", {x = (6 - 140 / 2 + pos.x - minp.x) * 2, y = (6 - 140 + maxp.z - pos.z) * 2}) elseif maps[player] then player:hud_change(hud.map, "text", "blank.png") player:hud_change(hud.marker, "text", "blank.png") maps[player] = nil end end end)