waypoints.lua 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. local S = minetest.get_translator("unified_inventory")
  2. local F = minetest.formspec_escape
  3. local ui = unified_inventory
  4. local COUNT = 5
  5. local hud_colors = {
  6. {"#FFFFFF", 0xFFFFFF, S("White")},
  7. {"#DBBB00", 0xf1d32c, S("Yellow")},
  8. {"#DD0000", 0xDD0000, S("Red")},
  9. {"#2cf136", 0x2cf136, S("Green")},
  10. {"#2c4df1", 0x2c4df1, S("Blue")},
  11. }
  12. -- Storage compatibility code
  13. --[[
  14. Stores temporary player data (persists until player leaves)
  15. [player_name] = {
  16. [<waypoint index>] = {
  17. edit = <edit current waypoint?>,
  18. hud = <hud ID>,
  19. },
  20. [<waypoint index>] = { ... },
  21. ...
  22. }
  23. ]]
  24. local waypoints_temp = {}
  25. --[[
  26. Datastorage format (per-player):
  27. {
  28. selected = <waypoint index>,
  29. [<waypoint index>] = {
  30. name = <name or nil>
  31. world_pos = <coordinates vector>,
  32. color = <"hud_colors" index>,
  33. active = <hud show waypoint?>,
  34. display_pos = <hud display coorinates?>,
  35. },
  36. [<waypoint index>] = { ... },
  37. ...
  38. }
  39. Player metadata format:
  40. {
  41. selected = <selected number>,
  42. -- Cannot mix integer/string keys in JSON
  43. data = {
  44. [<waypoint index>] = { same as above },
  45. ...
  46. }
  47. }
  48. ]]
  49. local function set_waypoint_data(player, waypoints)
  50. local meta = player:get_meta()
  51. if not next(waypoints.data or {}) then
  52. -- Empty data. Do not save anything, or delete
  53. meta:set_string("ui_waypoints", "")
  54. else
  55. meta:set_string("ui_waypoints", minetest.write_json(waypoints))
  56. end
  57. end
  58. local function migrate_datastorage(player, waypoints)
  59. -- Copy values from old table
  60. local new_data = {
  61. selected = waypoints.selected,
  62. data = {}
  63. }
  64. for i = 1, COUNT do
  65. new_data.data[i] = waypoints[i]
  66. end
  67. set_waypoint_data(player, new_data)
  68. -- Delete values, but keep one entry so that it's saved by datastorage
  69. for k, _ in pairs(waypoints) do
  70. waypoints[k] = nil
  71. end
  72. waypoints[1] = 1
  73. end
  74. local have_datastorage = minetest.get_modpath("datastorage") ~= nil
  75. local function get_waypoint_data(player)
  76. local player_name = player:get_player_name()
  77. -- Migration step
  78. if have_datastorage then
  79. local waypoints = datastorage.get(player_name, "waypoints")
  80. if waypoints.selected then
  81. migrate_datastorage(player, waypoints)
  82. minetest.log("action", "[unified_inventory] " ..
  83. "Migrated waypoints of player: " .. player_name)
  84. end
  85. end
  86. -- Get directly from metadata
  87. local waypoints = player:get_meta():get("ui_waypoints")
  88. waypoints = waypoints and minetest.parse_json(waypoints) or {}
  89. waypoints.data = waypoints.data or {}
  90. return waypoints
  91. end
  92. ui.register_page("waypoints", {
  93. get_formspec = function(player)
  94. local player_name = player:get_player_name()
  95. local wp_info_x = ui.style_full.form_header_x + 1.25
  96. local wp_info_y = ui.style_full.form_header_y + 0.5
  97. local wp_bottom_row = ui.style_full.std_inv_y - 1
  98. local wp_buttons_rj = ui.style_full.std_inv_x + 10.1 - ui.style_full.btn_spc
  99. local wp_edit_w = ui.style_full.btn_spc * 4 - 0.1
  100. local waypoints = get_waypoint_data(player)
  101. local sel = waypoints.selected or 1
  102. local formspec = {
  103. ui.style_full.standard_inv_bg,
  104. string.format("label[%f,%f;%s]",
  105. ui.style_full.form_header_x, ui.style_full.form_header_y, F(S("Waypoints"))),
  106. "image["..wp_info_x..","..wp_info_y..";1,1;ui_waypoints_icon.png]"
  107. }
  108. local n=4
  109. -- Tabs buttons:
  110. for i = 1, COUNT do
  111. local sw="select_waypoint"..i
  112. formspec[n] = string.format("image_button[%f,%f;%f,%f;%sui_%i_icon.png;%s;]",
  113. ui.style_full.main_button_x, wp_bottom_row - (5-i) * ui.style_full.btn_spc,
  114. ui.style_full.btn_size, ui.style_full.btn_size,
  115. (i == sel) and "ui_blue_icon_background.png^" or "",
  116. i, sw)
  117. formspec[n+1] = "tooltip["..sw..";"..S("Select Waypoint #@1", i).."]"
  118. n = n + 2
  119. end
  120. local waypoint = waypoints.data[sel] or {}
  121. local temp = waypoints_temp[player_name][sel] or {}
  122. local default_name = S("Waypoint @1", sel)
  123. -- Main buttons:
  124. local btnlist = {
  125. set_waypoint = {
  126. "ui_waypoint_set_icon.png",
  127. S("Set waypoint to current location")
  128. },
  129. toggle_waypoint = {
  130. waypoint.active and "ui_on_icon.png" or "ui_off_icon.png",
  131. waypoint.active and S("Hide waypoint") or S("Show waypoint")
  132. },
  133. toggle_display_pos = {
  134. waypoint.display_pos and "ui_green_icon_background.png^ui_xyz_icon.png" or "ui_red_icon_background.png^ui_xyz_icon.png^(ui_no.png^[transformR90)",
  135. waypoint.display_pos and S("Hide coordinates") or S("Show coordinates")
  136. },
  137. toggle_color = {
  138. "ui_circular_arrows_icon.png",
  139. S("Change color of waypoint display")
  140. },
  141. rename_waypoint = {
  142. "ui_pencil_icon.png",
  143. S("Edit waypoint name")
  144. }
  145. }
  146. local x = 4
  147. for name, def in pairs(btnlist) do
  148. formspec[n] = string.format("image_button[%f,%f;%f,%f;%s;%s%i;]",
  149. wp_buttons_rj - ui.style_full.btn_spc * x, wp_bottom_row,
  150. ui.style_full.btn_size, ui.style_full.btn_size,
  151. def[1], name, sel)
  152. formspec[n+1] = "tooltip["..name..sel..";"..F(def[2]).."]"
  153. x = x - 1
  154. n = n + 2
  155. end
  156. -- Waypoint's info:
  157. formspec[n] = ("label[%f,%f;%s]"):format(
  158. wp_info_x, wp_info_y + 1.1,
  159. F(waypoint.active and S("Waypoint active") or S("Waypoint inactive"))
  160. )
  161. n = n + 1
  162. if temp.edit then
  163. formspec[n] = string.format("field[%f,%f;%f,%f;rename_box%i;;%s]",
  164. wp_buttons_rj - wp_edit_w - 0.1, wp_bottom_row - ui.style_full.btn_spc,
  165. wp_edit_w, ui.style_full.btn_size, sel, (waypoint.name or default_name))
  166. formspec[n+1] = string.format("image_button[%f,%f;%f,%f;ui_ok_icon.png;confirm_rename%i;]",
  167. wp_buttons_rj, wp_bottom_row - ui.style_full.btn_spc,
  168. ui.style_full.btn_size, ui.style_full.btn_size, sel)
  169. formspec[n+2] = "tooltip[confirm_rename"..sel..";"..F(S("Finish editing")).."]"
  170. n = n + 3
  171. end
  172. formspec[n] = string.format("label[%f,%f;%s: %s]",
  173. wp_info_x, wp_info_y+1.6, F(S("World position")),
  174. minetest.pos_to_string(waypoint.world_pos or vector.new()))
  175. formspec[n+1] = string.format("label[%f,%f;%s: %s]",
  176. wp_info_x, wp_info_y+2.10, F(S("Name")), (waypoint.name or default_name))
  177. formspec[n+2] = string.format("label[%f,%f;%s: %s]",
  178. wp_info_x, wp_info_y+2.60, F(S("HUD text color")), hud_colors[waypoint.color or 1][3])
  179. return {formspec=table.concat(formspec)}
  180. end,
  181. })
  182. ui.register_button("waypoints", {
  183. type = "image",
  184. image = "ui_waypoints_icon.png",
  185. tooltip = S("Waypoints"),
  186. hide_lite=true
  187. })
  188. local function update_hud(player, waypoints, temp, i)
  189. local waypoint = waypoints.data[i]
  190. if not waypoint then return end
  191. temp[i] = temp[i] or {}
  192. temp = temp[i]
  193. local pos = waypoint.world_pos or vector.new()
  194. local name
  195. if waypoint.display_pos then
  196. name = minetest.pos_to_string(pos)
  197. if waypoint.name then
  198. name = name..", "..waypoint.name
  199. end
  200. else
  201. name = waypoint.name or S("Waypoint @1", i)
  202. end
  203. -- Perform HUD updates
  204. if temp.hud then
  205. player:hud_remove(temp.hud)
  206. temp.hud = nil
  207. end
  208. if waypoint.active then
  209. temp.hud = player:hud_add({
  210. hud_elem_type = "waypoint",
  211. number = hud_colors[waypoint.color or 1][2] ,
  212. name = name,
  213. text = "m",
  214. world_pos = pos
  215. })
  216. end
  217. end
  218. minetest.register_on_player_receive_fields(function(player, formname, fields)
  219. if formname ~= "" then return end
  220. local player_name = player:get_player_name()
  221. local update_formspec = false
  222. local need_update_hud = false
  223. local hit = false
  224. local waypoints = get_waypoint_data(player)
  225. local temp = waypoints_temp[player_name]
  226. for i = 1, COUNT do
  227. local waypoint = waypoints.data[i] or {}
  228. if fields["select_waypoint"..i] then
  229. hit = true
  230. waypoints.selected = i
  231. update_formspec = true
  232. end
  233. if fields["toggle_waypoint"..i] then
  234. hit = true
  235. waypoint.active = not (waypoint.active)
  236. need_update_hud = true
  237. update_formspec = true
  238. end
  239. if fields["set_waypoint"..i] then
  240. hit = true
  241. local pos = vector.round(player:get_pos())
  242. waypoint.world_pos = pos
  243. need_update_hud = true
  244. update_formspec = true
  245. end
  246. if fields["rename_waypoint"..i] then
  247. hit = true
  248. temp[i] = temp[i] or {}
  249. temp[i].edit = true
  250. update_formspec = true
  251. end
  252. if fields["toggle_display_pos"..i] then
  253. hit = true
  254. waypoint.display_pos = not waypoint.display_pos
  255. need_update_hud = true
  256. update_formspec = true
  257. end
  258. if fields["toggle_color"..i] then
  259. hit = true
  260. local color = waypoint.color or 0
  261. color = color + 1
  262. if color > #hud_colors then
  263. color = 1
  264. end
  265. waypoint.color = color
  266. need_update_hud = true
  267. update_formspec = true
  268. end
  269. if fields["confirm_rename"..i] then
  270. hit = true
  271. temp[i] = temp[i] or {}
  272. temp[i].edit = false
  273. waypoint.name = fields["rename_box"..i]
  274. need_update_hud = true
  275. update_formspec = true
  276. end
  277. if hit then
  278. -- Save first
  279. waypoints.data[i] = waypoint
  280. set_waypoint_data(player, waypoints)
  281. end
  282. -- Update after
  283. if need_update_hud then
  284. update_hud(player, waypoints, temp, i)
  285. end
  286. if update_formspec then
  287. ui.set_inventory_formspec(player, "waypoints")
  288. end
  289. if hit then return end
  290. end
  291. end)
  292. minetest.register_on_joinplayer(function(player)
  293. local player_name = player:get_player_name()
  294. local waypoints = get_waypoint_data(player)
  295. waypoints_temp[player_name] = {}
  296. for i = 1, COUNT do
  297. update_hud(player, waypoints, waypoints_temp[player_name], i)
  298. end
  299. end)
  300. minetest.register_on_leaveplayer(function(player)
  301. waypoints_temp[player:get_player_name()] = nil
  302. end)