--[[ maps – Minetest mod to render very ugly HUD maps Copyright © 2022 Nils Dagsson Moskopp (erlehmann) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Dieses Programm hat das Ziel, die Medienkompetenz der Leser zu steigern. Gelegentlich packe ich sogar einen handfesten Buffer Overflow oder eine Format String Vulnerability zwischen die anderen Codezeilen und schreibe das auch nicht dran. ]]-- -- blit an icon into the image unless its rect overlaps pixels that -- have any of the stop_colors, treating a nil pixel as transparent function tga_encoder.image:blit_icon(icon, pos, stop_colors) local x = pos.x local z = pos.z local overlap = false for i_z = 1,#icon do for i_x = 1,#icon[i_z] do local color = self.pixels[z + i_z][x + i_x][1] if stop_colors[color] then overlap = true break end end if overlap then break end end if overlap then return end for i_z = 1,#icon do for i_x = 1,#icon[i_z] do local color = icon[i_z][i_x][1] if color then self.pixels[z + i_z][x + i_x] = { color } end end end end maps = {} maps.dots = {} maps.maps = {} maps.minp = {} maps.maxp = {} maps.work = {} maps.sent = {} maps.posx = {} maps.posz = {} local size = 80 local worldpath = minetest.get_worldpath() local textures_dir = worldpath .. "/maps/" minetest.mkdir(textures_dir) maps.create_map = function(pos, player_name) local minp = vector.multiply(vector.floor(vector.divide(pos, size)), size) local maxp = vector.add(minp, vector.new(size - 1, size - 1, size - 1)) local prefix, _ = minetest.pos_to_string(maxp) prefix, _ = prefix:gsub("%(", "") prefix, _ = prefix:gsub("%)", "") prefix, _ = prefix:gsub(",", "_") local filename = prefix .. ".tga" if maps.work[filename] then return end local player = minetest.get_player_by_name(player_name) if maps.sent[filename] then maps.minp[player_name] = minp maps.maxp[player_name] = maxp player:hud_change( maps.maps[player_name], "text", filename ) return end maps.work[filename] = true player:hud_change( maps.maps[player_name], "text", "blank.png" ) local emerge_callback = function( blockpos, action, calls_remaining ) if calls_remaining > 0 then return end local pixels = {} local colormap = { { 195, 175, 140 }, -- background checkerboard light { 180, 160, 125 }, -- background checkerboard dark { 60, 35, 16 }, -- dark line { 210, 170, 130 }, -- liquid light { 135, 90, 40 }, -- liquid dark { 150, 105, 55 }, -- more liquid { 165, 120, 70 }, -- more liquid { 150, 105, 55 }, -- more liquid { 60, 35, 16 }, -- tree outline { 150, 105, 55 }, -- tree fill } for x = 1,size,1 do for z = 1,size,1 do local color = 0 + ( ( x + z ) % 2 ) pixels[z] = pixels[z] or {} pixels[z][x] = { color } end end local positions = minetest.find_nodes_in_area_under_air( minp, maxp, "group:liquid" ) for _, p in ipairs(positions) do if 14 == minetest.get_node_light(p, 0.5) then local z = p.z - minp.z + 1 local x = p.x - minp.x + 1 pixels[z][x] = { 3 } end end -- draw coastline for x = 1,size,1 do for z = 1,size,1 do if pixels[z][x][1] >= 3 then pixels[z][x] = { 3 + ( z % 2 ) } -- stripes if pixels[z][x][1] == 4 then local color = { 4 + ( ( math.floor( x / 7 ) + math.floor( 1.3 * z * z ) ) % 4 ) } pixels[z][x] = color end if z > 1 and pixels[z-1][x][1] < 3 then pixels[z-1][x] = { 2 } pixels[z][x] = { 4 } end if z < size and pixels[z+1][x][1] < 3 then pixels[z+1][x] = { 2 } pixels[z][x] = { 4 } end if x > 1 and pixels[z][x-1][1] < 3 then pixels[z][x-1] = { 2 } pixels[z][x] = { 4 } end if x < size and pixels[z][x+1][1] < 3 then pixels[z][x+1] = { 2 } pixels[z][x] = { 4 } end end end end local image = tga_encoder.image(pixels) local positions = minetest.find_nodes_in_area( minp, maxp, "group:door" ) for _, p in ipairs(positions) do local z = p.z - minp.z + 1 local x = p.x - minp.x + 1 local draw_house = ( z > 1 and z < size - 7 and x > 4 and x < size - 4 ) if draw_house then local _ = { nil } -- transparent local O = { 8 } -- outline local F = { 9 } -- filling local house = { { _, _, _, _, _, _, _ }, { _, O, O, O, O, O, _ }, { _, O, F, F, F, O, _ }, { _, O, F, F, F, O, _ }, { _, O, F, F, F, O, _ }, { _, _, O, F, O, _, _ }, { _, _, _, O, _, _, _ }, { _, _, _, _, _, _, _ }, } image:blit_icon( house, { x = x - 5, z = z - 1, }, { [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, } ) end end local positions = minetest.find_nodes_in_area_under_air( minp, maxp, { "group:leaves", "default:snow", -- snow-covered leaves } ) for _, p in ipairs(positions) do local z = p.z - minp.z + 1 local x = p.x - minp.x + 1 local node = minetest.get_node({ x=p.x, y=p.y - 4, z=p.z, }) local draw_tree = ( minetest.get_item_group( node.name, "tree" ) > 0 ) and ( z > 1 and z < size - 7 and x > 4 and x < size - 4 ) if draw_tree then local tree = {} local _ = { nil } -- transparent local O = { 8 } -- outline local F = { 9 } -- filling if nil ~= node.name:find("pine") then tree = { { _, _, _, _, _, _, _ }, { _, _, _, O, _, _, _ }, { _, O, O, O, O, O, _ }, { _, O, F, F, F, O, _ }, { _, _, O, F, O, _, _ }, { _, _, O, F, O, _, _ }, { _, _, _, O, _, _, _ }, { _, _, _, _, _, _, _ }, } else tree = { { _, _, _, _, _, _, _ }, { _, _, _, O, _, _, _ }, { _, _, _, O, _, _, _ }, { _, _, O, O, O, _, _ }, { _, O, F, F, F, O, _ }, { _, O, F, F, F, O, _ }, { _, _, O, O, O, _, _ }, { _, _, _, _, _, _, _ }, } end image:blit_icon( tree, { x = x - 4, z = z - 1, }, { [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, } ) end end local positions = minetest.find_nodes_in_area_under_air( minp, maxp, "group:grass" ) for _, p in ipairs(positions) do local z = p.z - minp.z + 1 local x = p.x - minp.x + 1 local draw_grass = ( z > 1 and z < size - 4 and x > 4 and x < size - 4 ) if draw_grass then local _ = { nil } -- transparent local G = { 9 } -- line local grass = { { _, _, _, _, _, _, _ }, { _, G, _, G, _, G, _ }, { _, G, _, G, _, G, _ }, { _, _, _, G, _, _, _ }, { _, _, _, _, _, _, _ }, } image:blit_icon( grass, { x = x - 5, z = z - 1, }, { [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, } ) end end local positions = minetest.find_nodes_in_area_under_air( minp, maxp, "group:flower" ) for _, p in ipairs(positions) do local z = p.z - minp.z + 1 local x = p.x - minp.x + 1 local draw_flower = ( z > 1 and z < size - 3 and x > 2 and x < size - 2 ) if draw_flower then local _ = { nil } -- transparent local F = { 9 } -- line local flower = { { _, _, _, }, { _, F, _, }, { _, F, _, }, { _, _, _, }, } image:blit_icon( flower, { x = x - 2, z = z - 1, }, { [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, } ) end end local filepath = textures_dir .. filename image:save( filepath, { colormap=colormap } ) minetest.dynamic_add_media( filepath, function() local player =minetest.get_player_by_name(player_name) player:hud_change( maps.maps[player_name], "text", filename ) maps.minp[player_name] = minp maps.maxp[player_name] = maxp maps.sent[filename] = true maps.work[filename] = false end ) end minetest.emerge_area( minp, maxp, emerge_callback ) end minetest.register_on_joinplayer( function(player) local player_name = player:get_player_name() local map_def = { hud_elem_type = "image", text = "blank.png", position = { x = 0.15, y = 0.90 }, alignment = { x = 0, y = -1 }, offset = { x = 0, y = 0 }, scale = { x = 4, y = 4 } } local dot_def = table.copy(map_def) maps.maps[player_name] = player:hud_add(map_def) maps.dots[player_name] = player:hud_add(dot_def) maps.minp[player_name] = { x=-32000, y=0, z=0 } maps.maxp[player_name] = { x=-32000, y=0, z=0 } end ) local time_elapsed = 0 minetest.register_globalstep( function(dtime) time_elapsed = time_elapsed + dtime if time_elapsed < ( 1 / 30 ) then return -- fps limiter end local players = minetest.get_connected_players() for _, player in pairs(players) do local pos = vector.round(player:get_pos()) local player_name = player:get_player_name() if ( pos.x == maps.posx[player_name] and pos.z == maps.posz[player_name] ) then -- continue else local minp = maps.minp[player_name] local maxp = maps.maxp[player_name] if pos.x < minp.x then maps.create_map(pos, player_name) elseif pos.x > maxp.x then maps.create_map(pos, player_name) end if pos.z < minp.z then maps.create_map(pos, player_name) elseif pos.z > maxp.z then maps.create_map(pos, player_name) end local x = (pos.x - minp.x - (size/2)) * 4 local y = (minp.z - pos.z) * 4 - 2 player:hud_change( maps.dots[player_name], "offset", { x = x, y = y, } ) maps.posx[player_name] = pos.x maps.posz[player_name] = pos.z end local marker local yaw = ( math.floor( player:get_look_horizontal() * 180 / math.pi / 45 + 0.5 ) % 8 ) * 45 if ( yaw == 0 or yaw == 90 or yaw == 180 or yaw == 270 ) then marker = "maps_arrow.tga" .. "^[makealpha:1,1,1" .. "^[transformR" .. yaw elseif ( yaw == 45 or yaw == 135 or yaw == 225 or yaw == 315 ) then marker = "maps_arrow_diagonal.tga" .. "^[makealpha:1,1,1" .. "^[transformR" .. (yaw - 45) end if marker then player:hud_change( maps.dots[player_name], "text", marker ) end end end )