use waypoint_lib
							
								
								
									
										68
									
								
								init.lua
									
									
									
									
									
								
							
							
						
						| @ -5,7 +5,10 @@ local TARGET_ABOVE = false -- place waypoint inside the block or above it | ||||
| local POINT_TO_OBJECTS = false -- unimplemented | ||||
| local POINT_TO_LIQUIDS = true | ||||
| local COMPASS_RANGE = 180 | ||||
| local WAYPOINT_ICON = "waypoint.png" | ||||
| local WAYPOINT_SCALE = {x=-1/16*9,y=-1} | ||||
| 
 | ||||
| local waypoint_lib = dofile(minetest.get_modpath("waypoint_announce") .. "/waypoint_lib.lua") | ||||
| 
 | ||||
| -- internal mod state | ||||
| local player_waypoints = {} -- store a current player waypoint to see if it needs to be updated | ||||
| @ -66,16 +69,6 @@ local function get_compass_meta_owner(meta) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| -- local function set_compass_meta_pos(meta, pos) | ||||
| --     local pos_hash = minetest.hash_node_position(pos) | ||||
| --     meta:set_int("waypoint_compass:position", pos_hash) | ||||
| -- end | ||||
| 
 | ||||
| 
 | ||||
| -- local function get_compass_meta_pos(meta) | ||||
| --     return minetest.get_position_from_hash(meta:get_int("waypoint_compass:position")) | ||||
| -- end | ||||
| 
 | ||||
| local function set_waypoint_at_pointed_place(itemstack, pointed_thing) | ||||
|     if pointed_thing and pointed_thing.type == "node" then | ||||
|         local pointed_pos = TARGET_ABOVE and pointed_thing.above or pointed_thing.under | ||||
| @ -90,27 +83,11 @@ local function set_waypoint_at_pointed_place(itemstack, pointed_thing) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| -- return first thing player is pointing at | ||||
| local function raycast_crosshair(player, range) | ||||
|     local p_pos = player:get_pos() | ||||
|     local p_eye_height = player:get_properties().eye_height | ||||
|     local p_eye_pos = { x = p_pos.x, y = p_pos.y + p_eye_height, z = p_pos.z } | ||||
|     local to = vector.add(p_eye_pos, vector.multiply(player:get_look_dir(), range)) | ||||
|     local ray = minetest.raycast(p_eye_pos, to, POINT_TO_OBJECTS, POINT_TO_LIQUIDS) | ||||
|     local pointed_thing = ray:next() | ||||
|     return pointed_thing | ||||
|     -- while pointed_thing do | ||||
|     --     print(pointed_thing.type, minetest.pos_to_string(pointed_thing.under)) | ||||
|     --     pointed_thing = ray:next() | ||||
|     -- end | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| local function hide_hud_waypoint(player) | ||||
|     local player_name = player:get_player_name() | ||||
|     local hud_id = player_waypoints[player_name].hud_id | ||||
|     local hud = player_waypoints[player_name] | ||||
|     player_waypoints[player_name] = nil | ||||
|     player:hud_remove(hud_id) | ||||
|     hud:hide(player) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| @ -121,24 +98,29 @@ local function show_hud_waypoint(player, compass_item_meta) | ||||
|         -- do not show unset compass position | ||||
|         return | ||||
|     end | ||||
| 
 | ||||
|     -- Show this waypoint | ||||
|     local waypoint_name = get_compass_meta_label(compass_item_meta) | ||||
|     local waypoint_color = get_compass_meta_color(compass_item_meta) | ||||
|     local hud_id = player:hud_add({ | ||||
|             hud_elem_type = "waypoint", | ||||
|             name = waypoint_name, | ||||
|             text = "m", | ||||
|             precision= COMPASS_PRECISION, | ||||
|             number = waypoint_color, | ||||
|             world_pos = waypoint_pos, | ||||
|     }) | ||||
|     local hexcolor = ("#%06X"):format(waypoint_color) | ||||
| 
 | ||||
|     local size = {x=-9, y=-16} | ||||
|     local alignment = {x=100/9*1.47,y=100/16*1.63} -- near compass in your hand | ||||
|      | ||||
|     local waypoint = waypoint_lib.WaypointHUD:new(player, waypoint_pos, | ||||
| 						  waypoint_name, waypoint_color, | ||||
| 						  WAYPOINT_ICON, WAYPOINT_SCALE, hexcolor, | ||||
| 						  size, alignment, "arrow_%03d.png", hexcolor, 9, | ||||
| 						  true) | ||||
| 
 | ||||
|     waypoint:show(player) | ||||
|     -- store HUD elemnt id to remove it later | ||||
|     if not player_waypoints[player_name] then | ||||
|         player_waypoints[player_name] = {} | ||||
|     if player_waypoints[player_name] then | ||||
| 	minetest.log("error","[MOD] waypoint_compass: " .. | ||||
| 		     player:get_player_name() .. | ||||
| 		     " got their HUD stuck on screen?") | ||||
|     end | ||||
|     --print("hud_id add", hud_id) | ||||
|     player_waypoints[player_name].pos = waypoint_pos | ||||
|     player_waypoints[player_name].hud_id = hud_id | ||||
|     player_waypoints[player_name] = waypoint | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| @ -155,7 +137,7 @@ local function update_hud_waypoint(player, itemstack, force) | ||||
|         local waypoint_pos = get_compass_meta_pos(meta) | ||||
|         -- remove different waypoint if it exists | ||||
|         if player_waypoints[player_name] and | ||||
|             (player_waypoints[player_name].pos ~= waypoint_pos | ||||
|             (player_waypoints[player_name].point_pos ~= waypoint_pos | ||||
|              or force | ||||
|              or not get_compass_meta_is_set(meta)) then | ||||
|              | ||||
| @ -260,7 +242,7 @@ local function compass_use_callback(itemstack, user, pointed_thing) | ||||
|     if user:get_player_control()["sneak"] then | ||||
|         if pointed_thing.type == "nothing" then | ||||
|             if user and user:is_player() then | ||||
|                 pointed_thing = raycast_crosshair(user, COMPASS_RANGE) | ||||
|                 pointed_thing = waypoint_lib.raycast_crosshair(user, COMPASS_RANGE, POINT_TO_OBJECTS, POINT_TO_LIQUIDS) | ||||
|             end | ||||
|         end | ||||
|         set_waypoint_at_pointed_place(itemstack, pointed_thing) | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_000.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 801 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_010.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 932 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_020.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 933 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_030.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 900 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_040.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 939 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_050.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 925 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_060.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 935 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_070.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 887 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/arrow_080.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 898 B | 
							
								
								
									
										
											BIN
										
									
								
								textures/waypoint.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 501 B | 
							
								
								
									
										68
									
								
								waypoint.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,68 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="16.0px" | ||||
|    height="16.0px" | ||||
|    viewBox="0 0 16.0 16.0" | ||||
|    version="1.1" | ||||
|    id="SVGRoot" | ||||
|    sodipodi:docname="waypoint.svg" | ||||
|    inkscape:version="1.0.2 (e86c870879, 2021-01-15)"> | ||||
|   <defs | ||||
|      id="defs18" /> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="44.8" | ||||
|      inkscape:cx="5.8765185" | ||||
|      inkscape:cy="9.7673965" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      inkscape:document-rotation="0" | ||||
|      showgrid="true" | ||||
|      inkscape:window-width="2556" | ||||
|      inkscape:window-height="1436" | ||||
|      inkscape:window-x="1280" | ||||
|      inkscape:window-y="0" | ||||
|      inkscape:window-maximized="1"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid27" | ||||
|        dotted="true" | ||||
|        originx="0.5" | ||||
|        originy="0.5" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata21"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1"> | ||||
|     <circle | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" | ||||
|        id="path42" | ||||
|        cx="8" | ||||
|        cy="8" | ||||
|        r="7.5" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										255
									
								
								waypoint_lib.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,255 @@ | ||||
| local WAYPOINT_PRECISION = 1 -- set to 1 to show whole number or 10 for 1 decimal | ||||
| local UPDATE_INTERVAL = 0.22 | ||||
| 
 | ||||
| 
 | ||||
| -- get actual eye_pos of the player (including eye_offset) | ||||
| local function player_get_eye_pos(player) | ||||
|     local p_pos = player:get_pos() | ||||
|     local p_eye_height = player:get_properties().eye_height | ||||
|     p_pos.y = p_pos.y + p_eye_height | ||||
|     local p_eye_pos = p_pos | ||||
|     local p_eye_offset = vector.multiply(player:get_eye_offset(), 0.1) | ||||
|     local yaw = player:get_look_horizontal() | ||||
|     p_eye_pos = vector.add(p_eye_pos, vector.rotate_around_axis(p_eye_offset, {x=0,y=1,z=0}, yaw)) | ||||
|     return p_eye_pos | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| -- return first thing player is pointing at | ||||
| local function raycast_crosshair(player, range, point_to_objects, point_to_liquids) | ||||
|     local p_eye_pos = player_get_eye_pos(player) | ||||
|     local to = vector.add(p_eye_pos, vector.multiply(player:get_look_dir(), range)) | ||||
|     local ray = minetest.raycast(p_eye_pos, to, point_to_objects, point_to_liquids) | ||||
|     local pointed_thing = ray:next() | ||||
|     while pointed_thing do | ||||
|         if pointed_thing.type == "object" and pointed_thing.ref == player then | ||||
|             -- exclude the player themselves from the raycast | ||||
|             pointed_thing = ray:next() | ||||
|         else | ||||
|             return pointed_thing | ||||
|         end | ||||
|     end | ||||
|     -- FIXME return "nothing" pointed thing? | ||||
|     return nil | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| -- get position and thing that player is pointing at or nil | ||||
| local function get_pointed_position(player, range, point_to_objects, point_to_liquids) | ||||
|     local pointed_thing = raycast_crosshair(player, range, point_to_objects, point_to_liquids) | ||||
|     local pointed_pos = nil | ||||
|     if pointed_thing then | ||||
|         if pointed_thing.type == "node" then | ||||
|             -- middle between "above" and "under" | ||||
|             pointed_pos = vector.multiply(vector.add(pointed_thing.above, pointed_thing.under), 0.5) | ||||
|         elseif pointed_thing.type == "object" then | ||||
|             -- TODO point at the middle of collision box? (or not, ground may be better) | ||||
|             pointed_pos = pointed_thing.ref:get_pos() | ||||
|         end | ||||
|     end | ||||
|     return pointed_pos, pointed_thing | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| local function round(x) | ||||
|     local f = math.floor(x) | ||||
|     if (x == f) or (x % 2.0 == 0.5) then | ||||
|         return f | ||||
|     else | ||||
|         return math.floor(x + 0.5) | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| local function get_texture_rotation_for_direction(dir, quad_steps) | ||||
|     -- angle relative to player position | ||||
|     local angle = math.atan2(dir.x, dir.z) | ||||
|     local pi = math.pi | ||||
|     -- snap/round to increments and convert to degrees | ||||
|     local bias = 1/quad_steps/2 -- add bias to floor into correct quardant | ||||
|     local angle_quad = math.floor(angle/(2*pi) * 4 + bias) * (360 / 4) % 360 | ||||
|     local angle_quad_map = {[0] = "I", [90] = "R270", [180] = "R180", [270] = "R90"} | ||||
|     local step_size = 90/quad_steps | ||||
|     local angle_quad_step = (round((math.deg(angle) - angle_quad)/step_size) * step_size) % 90 | ||||
|     return angle_quad_step, angle_quad_map[angle_quad] | ||||
| end | ||||
| 
 | ||||
| -------------------------------------------------------------------------------- | ||||
| -- [[ Waypoint HUD element with distance, label an icon above it ]] -- | ||||
| local IconWaypointHUD = {} | ||||
| function IconWaypointHUD:new(point_pos, label, label_color, icon, icon_scale, icon_color) | ||||
|     local w = { | ||||
| 	hud_id_label = nil, | ||||
| 	hud_id_icon = nil, | ||||
|     } | ||||
|     self.__index = self | ||||
|     setmetatable(w, self) | ||||
|      | ||||
|     w.huddef_label = { | ||||
|             hud_elem_type = "waypoint", | ||||
|             name = label, | ||||
|             text = "m", -- distance suffix | ||||
|             precision = WAYPOINT_PRECISION, -- TODO make this configurable? | ||||
|             number = label_color, | ||||
|             world_pos = point_pos, | ||||
|             offset = {x=0,y=0}, | ||||
|             alignment = {x=0, y=1}, -- move down | ||||
|     } | ||||
| 
 | ||||
|     w.huddef_icon = { | ||||
|             hud_elem_type = "image_waypoint", | ||||
|             scale = icon_scale, | ||||
|             text = icon .. "^[multiply:" .. icon_color, | ||||
|             alignment = {x=0,y=-1}, | ||||
|             world_pos = point_pos, | ||||
|             offset = {x=0,y=0}, | ||||
|             --name = name, | ||||
|             --precision = WAYPOINT_PRECISION, | ||||
|             --number = waypoint_color, | ||||
|     } | ||||
|      | ||||
|     return w | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function IconWaypointHUD:show(player) | ||||
|     --self.player_name = player:get_player_name() | ||||
|     self.hud_id_label = player:hud_add(self.huddef_label) | ||||
|     self.hud_id_icon = player:hud_add(self.huddef_icon) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function IconWaypointHUD:hide(player) | ||||
|     player:hud_remove(self.hud_id_label) | ||||
|     player:hud_remove(self.hud_id_icon) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function IconWaypointHUD:update(player) | ||||
|     error("No reason to call IconWaypointHUD:update(), fix your code") | ||||
| end | ||||
| 
 | ||||
| -------------------------------------------------------------------------------- | ||||
| -- [[ Just an arrow HUD element. Can be turned any direction with update() ]] -- | ||||
| local CompassHUD = {} | ||||
| function CompassHUD:new(size, alignment, direction, icon_format, icon_color, quad_steps) | ||||
|     local c = { | ||||
| 	icon_format = icon_format, | ||||
| 	icon_color = icon_color, | ||||
| 	quad_steps = quad_steps, | ||||
| 	-- TODO have a callback to auto-update the direction? | ||||
|     } | ||||
|     self.__index = self | ||||
|     setmetatable(c, self) | ||||
| 
 | ||||
|     local texture = c:get_texture(direction) | ||||
| 
 | ||||
|     c.huddef_compass = { | ||||
| 	hud_elem_type = "compass", | ||||
| 	size = size, | ||||
| 	text = texture, | ||||
| 	alignment = alignment, | ||||
| 	dir = 1, | ||||
|     } | ||||
|      | ||||
|     return c | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function CompassHUD:get_texture(direction) | ||||
|     local angle_quad_step, rotation_t = get_texture_rotation_for_direction(direction, self.quad_steps) | ||||
| 
 | ||||
|     local texture = self.icon_format:format(angle_quad_step) .. "^[transform" .. rotation_t .. "^[multiply:" .. self.icon_color | ||||
|     return texture | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function CompassHUD:show(player) | ||||
|     --self.player_name = player:get_player_name() | ||||
|     self.hud_id_compass = player:hud_add(self.huddef_compass) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function CompassHUD:hide(player) | ||||
|     player:hud_remove(self.hud_id_compass) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function CompassHUD:update(player, direction) | ||||
|     player:hud_change(self.hud_id_compass, "text", self:get_texture(direction)) | ||||
| end | ||||
| 
 | ||||
| -------------------------------------------------------------------------------- | ||||
| 
 | ||||
| local WaypointHUD = {} | ||||
| function WaypointHUD:new(player, point_pos, | ||||
| 			 label, label_color, | ||||
| 			 point_icon, point_icon_scale, point_color, | ||||
| 			 size, alignment, arrow_icon_format, arrow_icon_color, quad_steps, | ||||
| 			 do_auto_update) | ||||
|     local w = { | ||||
| 	point_pos = point_pos, | ||||
| 	do_auto_update = do_auto_update, | ||||
| 	auto_update_job = nil, | ||||
|     } | ||||
|     self.__index = self | ||||
|     setmetatable(w, self) | ||||
| 
 | ||||
|     w.waypoint_hud = IconWaypointHUD:new(point_pos, label, label_color, point_icon, point_icon_scale, point_color) | ||||
|      | ||||
|     local d = vector.subtract(point_pos, player:get_pos()) | ||||
|     w.compass_hud = CompassHUD:new(size, alignment, d, arrow_icon_format, arrow_icon_color, quad_steps) | ||||
|      | ||||
|     return w | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function WaypointHUD:show(player) | ||||
|     self.waypoint_hud:show(player) | ||||
|     self.compass_hud:show(player) | ||||
|     self:update(player) -- FIXME? needed to start auto update | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function WaypointHUD:hide(player) | ||||
|     self.waypoint_hud:hide(player) | ||||
|     self.compass_hud:hide(player) | ||||
|     if self.auto_update_job then | ||||
| 	self.auto_update_job.cancel() | ||||
| 	self.auto_update_job = nil | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| function WaypointHUD:update(player) | ||||
|     local direction = vector.subtract(self.point_pos, player:get_pos()) | ||||
|     self.compass_hud:update(player, direction) | ||||
|     if self.do_auto_update then | ||||
| 	local player_name = player:get_player_name() | ||||
| 	local job = minetest.after(UPDATE_INTERVAL, | ||||
| 		       function() | ||||
| 			   local player = minetest.get_player_by_name(player_name) | ||||
| 			   if player then | ||||
| 			       self:update(player) | ||||
| 			   end | ||||
| 		       end | ||||
| 	) | ||||
| 	self.auto_update_job = job | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| -------------------------------------------------------------------------------- | ||||
| 
 | ||||
| waypoint_lib = { | ||||
|     raycast_crosshair = raycast_crosshair, | ||||
|     get_pointed_position = get_pointed_position, | ||||
|     player_hud_add_waypoint_compass = player_hud_add_waypoint_compass, | ||||
|     player_hud_update_waypoint_compass = player_hud_update_waypoint_compass, | ||||
|     IconWaypointHUD = IconWaypointHUD, | ||||
|     CompassHUD = CompassHUD, | ||||
|     WaypointHUD = WaypointHUD, | ||||
| } | ||||
| 
 | ||||
| return waypoint_lib | ||||