An advanced-ish waypoints mod for Minetest.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

297 lines
9.8KB

  1. --
  2. -- Minetest advmarkers SSCSM
  3. --
  4. -- Copyright © 2019 by luk3yx
  5. -- License: https://git.minetest.land/luk3yx/advmarkers/src/branch/master/LICENSE.md
  6. --
  7. advmarkers = {}
  8. local data = {}
  9. assert(not sscsm.restrictions or not sscsm.restrictions.chat_messages,
  10. 'The advmarkers SSCSM needs to be able to send chat messages!')
  11. -- Convert positions to/from strings
  12. local function pos_to_string(pos)
  13. if type(pos) == 'table' then
  14. pos = minetest.pos_to_string(vector.round(pos))
  15. end
  16. if type(pos) == 'string' then
  17. return pos
  18. end
  19. end
  20. local function string_to_pos(pos)
  21. if type(pos) == 'string' then
  22. pos = minetest.string_to_pos(pos)
  23. end
  24. if type(pos) == 'table' then
  25. return vector.round(pos)
  26. end
  27. end
  28. -- Run remote command
  29. -- This is easier to do with chatcommands because servers cannot send mod
  30. -- channel messages to specific clients.
  31. local csm_key = string.char(1) .. 'ADVMARKERS_SSCSM' .. string.char(1)
  32. local function run_remote_command(cmd, param)
  33. local msg = csm_key
  34. if cmd then
  35. msg = msg .. cmd
  36. if param then msg = msg .. param end
  37. end
  38. minetest.run_server_chatcommand('wp', msg)
  39. end
  40. -- Display a waypoint
  41. function advmarkers.display_waypoint(name)
  42. name = tostring(name)
  43. if data['marker-' .. name] then
  44. run_remote_command('0', tostring(name))
  45. return true
  46. else
  47. return false
  48. end
  49. end
  50. -- Get a waypoint
  51. function advmarkers.get_waypoint(name)
  52. return string_to_pos(data['marker-' .. tostring(name)])
  53. end
  54. advmarkers.get_marker = advmarkers.get_waypoint
  55. -- Delete a waypoint
  56. function advmarkers.delete_waypoint(name)
  57. name = tostring(name)
  58. if data['marker-' .. name] ~= nil then
  59. data['marker-' .. name] = nil
  60. run_remote_command('D', name)
  61. end
  62. end
  63. advmarkers.delete_marker = advmarkers.delete_waypoint
  64. -- Set a waypoint
  65. function advmarkers.set_waypoint(pos, name)
  66. pos = pos_to_string(pos)
  67. if not pos then return end
  68. name = tostring(name)
  69. data['marker-' .. name] = pos
  70. run_remote_command('S', pos:gsub(' ', '') .. ' ' .. name)
  71. return true
  72. end
  73. advmarkers.set_marker = advmarkers.set_waypoint
  74. -- Rename a waypoint and re-interpret the position.
  75. function advmarkers.rename_waypoint(oldname, newname)
  76. oldname, newname = tostring(oldname), tostring(newname)
  77. local pos = advmarkers.get_waypoint(oldname)
  78. if not pos or not advmarkers.set_waypoint(pos, newname) then return end
  79. if oldname ~= newname then
  80. advmarkers.delete_waypoint(oldname)
  81. end
  82. return true
  83. end
  84. advmarkers.rename_marker = advmarkers.rename_waypoint
  85. -- Import waypoints - Note that this won't import strings made by older
  86. -- versions of the CSM.
  87. function advmarkers.import(s, clear)
  88. if type(s) ~= 'table' then
  89. if s:sub(1, 1) ~= 'J' then return end
  90. s = minetest.decode_base64(s:sub(2))
  91. local success, msg = pcall(minetest.decompress, s)
  92. if not success then return end
  93. s = minetest.parse_json(msg)
  94. end
  95. -- Iterate over waypoints to preserve existing ones and check for errors.
  96. if type(s) ~= 'table' then return end
  97. if clear then data = {} end
  98. for name, pos in pairs(s) do
  99. if type(name) == 'string' and type(pos) == 'string' and
  100. name:sub(1, 7) == 'marker-' and minetest.string_to_pos(pos) and
  101. data[name] ~= pos then
  102. -- Prevent collisions
  103. local c = 0
  104. while data[name] and c < 50 do
  105. name = name .. '_'
  106. c = c + 1
  107. end
  108. -- Sanity check
  109. if c < 50 then
  110. data[name] = pos
  111. end
  112. end
  113. end
  114. end
  115. -- Get waypoint names
  116. function advmarkers.get_waypoint_names(sorted)
  117. local res = {}
  118. for name, pos in pairs(data) do
  119. if name:sub(1, 7) == 'marker-' then
  120. table.insert(res, name:sub(8))
  121. end
  122. end
  123. if sorted or sorted == nil then table.sort(res) end
  124. return res
  125. end
  126. -- Display the formspec
  127. local formspec_list = {}
  128. local selected_name = false
  129. function advmarkers.display_formspec()
  130. local formspec = 'size[5.25,8]' ..
  131. 'label[0,0;Waypoint list ' ..
  132. minetest.colorize('#888888', '(SSCSM)') .. ']' ..
  133. 'button_exit[0,7.5;1.3125,0.5;display;Display]' ..
  134. 'button[1.3125,7.5;1.3125,0.5;teleport;Teleport]' ..
  135. 'button[2.625,7.5;1.3125,0.5;rename;Rename]' ..
  136. 'button[3.9375,7.5;1.3125,0.5;delete;Delete]' ..
  137. 'textlist[0,0.75;5,6;marker;'
  138. -- Iterate over all the markers
  139. local id = 0
  140. local selected = 1
  141. formspec_list = advmarkers.get_waypoint_names()
  142. for id, name in ipairs(formspec_list) do
  143. if id > 1 then formspec = formspec .. ',' end
  144. if not selected_name then selected_name = name end
  145. if name == selected_name then selected = id end
  146. formspec = formspec .. '##' .. minetest.formspec_escape(name)
  147. end
  148. -- Close the text list and display the selected marker position
  149. formspec = formspec .. ';' .. tostring(selected) .. ']'
  150. if selected_name then
  151. local pos = advmarkers.get_marker(selected_name)
  152. if pos then
  153. pos = minetest.formspec_escape(tostring(pos.x) .. ', ' ..
  154. tostring(pos.y) .. ', ' .. tostring(pos.z))
  155. pos = 'Waypoint position: ' .. pos
  156. formspec = formspec .. 'label[0,6.75;' .. pos .. ']'
  157. end
  158. else
  159. -- Draw over the buttons
  160. formspec = formspec .. 'button_exit[0,7.5;5.25,0.5;quit;Close dialog]' ..
  161. 'label[0,6.75;No waypoints. Add one with "/add_wp".]'
  162. end
  163. -- Display the formspec
  164. return minetest.show_formspec('advmarkers-sscsm', formspec)
  165. end
  166. -- Register chatcommands
  167. local mrkr_cmd
  168. function mrkr_cmd(param)
  169. if param == '' then return advmarkers.display_formspec() end
  170. if param == '--ssm' then param = '' end
  171. minetest.run_server_chatcommand('wp', param)
  172. end
  173. sscsm.register_chatcommand('mrkr', mrkr_cmd)
  174. sscsm.register_chatcommand('wp', mrkr_cmd)
  175. sscsm.register_chatcommand('wps', mrkr_cmd)
  176. sscsm.register_chatcommand('waypoint', mrkr_cmd)
  177. sscsm.register_chatcommand('waypoints', mrkr_cmd)
  178. function mrkr_cmd(param)
  179. minetest.run_server_chatcommand('add_wp', param)
  180. run_remote_command()
  181. end
  182. sscsm.register_chatcommand('add_wp', mrkr_cmd)
  183. sscsm.register_chatcommand('add_waypoint', mrkr_cmd)
  184. sscsm.register_chatcommand('add_mrkr', mrkr_cmd)
  185. mrkr_cmd = nil
  186. -- Set the HUD
  187. minetest.register_on_formspec_input(function(formname, fields)
  188. if formname == 'advmarkers-ignore' then
  189. return true
  190. elseif formname ~= 'advmarkers-sscsm' then
  191. return
  192. end
  193. local name = false
  194. if fields.marker then
  195. local event = minetest.explode_textlist_event(fields.marker)
  196. if event.index then
  197. name = formspec_list[event.index]
  198. end
  199. else
  200. name = selected_name
  201. end
  202. if name then
  203. if fields.display then
  204. if not advmarkers.display_waypoint(name) then
  205. minetest.display_chat_message('Error displaying waypoint!')
  206. end
  207. elseif fields.rename then
  208. minetest.show_formspec('advmarkers-sscsm', 'size[6,3]' ..
  209. 'label[0.35,0.2;Rename waypoint]' ..
  210. 'field[0.3,1.3;6,1;new_name;New name;' ..
  211. minetest.formspec_escape(name) .. ']' ..
  212. 'button[0,2;3,1;cancel;Cancel]' ..
  213. 'button[3,2;3,1;rename_confirm;Rename]')
  214. elseif fields.rename_confirm then
  215. if fields.new_name and #fields.new_name > 0 then
  216. if advmarkers.rename_waypoint(name, fields.new_name) then
  217. selected_name = fields.new_name
  218. else
  219. minetest.display_chat_message('Error renaming waypoint!')
  220. end
  221. advmarkers.display_formspec()
  222. else
  223. minetest.display_chat_message(
  224. 'Please enter a new name for the waypoint.'
  225. )
  226. end
  227. elseif fields.teleport then
  228. minetest.show_formspec('advmarkers-sscsm', 'size[6,2.2]' ..
  229. 'label[0.35,0.25;' .. minetest.formspec_escape(
  230. 'Teleport to a waypoint\n - ' .. name
  231. ) .. ']' ..
  232. 'button[0,1.25;3,1;cancel;Cancel]' ..
  233. 'button_exit[3,1.25;3,1;teleport_confirm;Teleport]')
  234. elseif fields.teleport_confirm then
  235. -- Teleport with /teleport
  236. local pos = advmarkers.get_waypoint(name)
  237. if pos then
  238. minetest.run_server_chatcommand('teleport',
  239. pos.x .. ', ' .. pos.y .. ', ' .. pos.z)
  240. else
  241. minetest.display_chat_message('Error teleporting to waypoint!')
  242. end
  243. elseif fields.delete then
  244. minetest.show_formspec('advmarkers-sscsm', 'size[6,2]' ..
  245. 'label[0.35,0.25;Are you sure you want to delete this marker?]' ..
  246. 'button[0,1;3,1;cancel;Cancel]' ..
  247. 'button[3,1;3,1;delete_confirm;Delete]')
  248. elseif fields.delete_confirm then
  249. advmarkers.delete_waypoint(name)
  250. selected_name = false
  251. advmarkers.display_formspec()
  252. elseif fields.cancel then
  253. advmarkers.display_formspec()
  254. elseif name ~= selected_name then
  255. selected_name = name
  256. advmarkers.display_formspec()
  257. end
  258. elseif fields.display or fields.delete then
  259. minetest.display_chat_message('Please select a marker.')
  260. end
  261. return true
  262. end)
  263. -- Update the waypoint list
  264. minetest.register_on_receiving_chat_message(function(message)
  265. if message:sub(1, #csm_key) == csm_key then
  266. advmarkers.import(message:sub(#csm_key + 1), true)
  267. return true
  268. end
  269. end)
  270. run_remote_command()