init.lua 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. local worldpath = minetest.get_worldpath()
  2. local S = minetest.get_translator("named_waypoints")
  3. named_waypoints = {}
  4. local test_interval = 5
  5. local player_huds = {} -- Each player will have a table of [position_hash] = hud_id pairs in here
  6. local waypoint_defs = {} -- the registered definition tables
  7. local waypoint_areastores = {} -- areastores containing waypoint data
  8. local inventory_string = "inventory"
  9. local hotbar_string = "hotbar"
  10. local wielded_string = "wielded"
  11. --waypoint_def = {
  12. -- default_name = , -- a string that's used if a waypoint's data doesn't have a "name" property
  13. -- default_color = , -- if not defined, defaults to 0xFFFFFFFF
  14. -- visibility_requires_item = , -- item, if not defined then nothing is required
  15. -- visibility_item_location = , -- "inventory", "hotbar", "wielded" (defaults to inventory if not provided)
  16. -- visibility_volume_radius = , -- If not defined, HUD waypoints will not be shown.
  17. -- visibility_volume_height = , -- if defined, then visibility check is done in a cylindrical volume rather than a sphere
  18. -- discovery_requires_item = ,-- item, if not defined then nothing is required
  19. -- discovery_item_location = ,-- -- "inventory", "hotbar", "wielded" (defaults to inventory if not provided)
  20. -- discovery_volume_radius = , -- radius within which a waypoint can be auto-discovered by a player. "discovered_by" property is used in waypoint_data to store discovery info
  21. -- discovery_volume_height = , -- if defined, then discovery check is done in a cylindrical volume rather than a sphere
  22. -- on_discovery = function(player, pos, waypoint_data, waypoint_def) -- use "named_waypoints.default_discovery_popup" for a generic discovery notification
  23. --}
  24. named_waypoints.register_named_waypoints = function(waypoints_type, waypoints_def)
  25. waypoint_defs[waypoints_type] = waypoints_def
  26. player_huds[waypoints_type] = {}
  27. local areastore_filename = worldpath.."/named_waypoints_".. waypoints_type ..".txt"
  28. local area_file = io.open(areastore_filename, "r")
  29. local areastore = AreaStore()
  30. if area_file then
  31. area_file:close()
  32. areastore:from_file(areastore_filename)
  33. end
  34. waypoint_areastores[waypoints_type] = areastore
  35. end
  36. local function save(waypoints_type)
  37. local areastore_filename = worldpath.."/named_waypoints_".. waypoints_type ..".txt"
  38. local areastore = waypoint_areastores[waypoints_type]
  39. if areastore then
  40. areastore:to_file(areastore_filename)
  41. else
  42. minetest.log("error", "[named_waypoints] attempted to save areastore for unregistered type " .. waypoints_type)
  43. end
  44. end
  45. -- invalidates a hud marker at a specific location
  46. local function remove_hud_marker(waypoints_type, pos)
  47. local waypoint_def = waypoint_defs[waypoints_type]
  48. if not waypoint_def.visibility_volume_radius then
  49. -- if there's no visibility, there won't be any hud markers to remove
  50. return
  51. end
  52. local target_hash = minetest.hash_node_position(pos)
  53. local waypoints_for_this_type = player_huds[waypoints_type]
  54. for player_name, waypoints in pairs(waypoints_for_this_type) do
  55. local player = minetest.get_player_by_name(player_name)
  56. if player then
  57. for pos_hash, hud_id in pairs(waypoints) do
  58. if pos_hash == target_hash then
  59. player:hud_remove(hud_id)
  60. waypoints[pos_hash] = nil
  61. break
  62. end
  63. end
  64. end
  65. end
  66. end
  67. local function add_waypoint(waypoints_type, pos, waypoint_data, update_existing)
  68. assert(type(waypoint_data) == "table")
  69. local areastore = waypoint_areastores[waypoints_type]
  70. if not areastore then
  71. minetest.log("error", "[named_waypoints] attempted to add waypoint for unregistered type " .. waypoints_type)
  72. return false
  73. end
  74. local existing_area = areastore:get_areas_for_pos(pos, false, true)
  75. local id = next(existing_area)
  76. if id and not update_existing then
  77. return false -- already exists
  78. end
  79. local data
  80. if id then
  81. data = minetest.deserialize(existing_area[id].data)
  82. for k,v in pairs(waypoint_data) do
  83. data[k] = v
  84. end
  85. areastore:remove_area(id)
  86. remove_hud_marker(waypoints_type, pos)
  87. else
  88. data = waypoint_data
  89. end
  90. local waypoint_def = waypoint_defs[waypoints_type]
  91. if not (data.name or waypoint_def.default_name) then
  92. minetest.log("error", "[named_waypoints] Waypoint of type " .. waypoints_type .. " at "
  93. .. minetest.pos_to_string(pos) .. " was missing a name field in its data " .. dump(data)
  94. .. " and its type definition has no default to fall back on.")
  95. return false
  96. end
  97. areastore:insert_area(pos, pos, minetest.serialize(data), id)
  98. save(waypoints_type)
  99. return true
  100. end
  101. named_waypoints.add_waypoint = function(waypoints_type, pos, waypoint_data)
  102. if not waypoint_data then
  103. waypoint_data = {}
  104. end
  105. return add_waypoint(waypoints_type, pos, waypoint_data, false)
  106. end
  107. named_waypoints.update_waypoint = function(waypoints_type, pos, waypoint_data)
  108. return add_waypoint(waypoints_type, pos, waypoint_data, true)
  109. end
  110. named_waypoints.get_waypoint = function(waypoints_type, pos)
  111. local areastore = waypoint_areastores[waypoints_type]
  112. local existing_area = areastore:get_areas_for_pos(pos, false, true)
  113. local id = next(existing_area)
  114. if not id then
  115. return nil -- nothing here
  116. end
  117. return minetest.deserialize(existing_area[id].data)
  118. end
  119. -- returns a list of tables with the values {pos=, data=}
  120. named_waypoints.get_waypoints_in_area = function(waypoints_type, minp, maxp)
  121. local areastore = waypoint_areastores[waypoints_type]
  122. local areas = areastore:get_areas_in_area(minp, maxp, true, true, true)
  123. local returnval = {}
  124. for id, data in pairs(areas) do
  125. table.insert(returnval, {pos=data.min, data=minetest.deserialize(data.data)})
  126. end
  127. return returnval
  128. end
  129. named_waypoints.remove_waypoint = function(waypoints_type, pos)
  130. local areastore = waypoint_areastores[waypoints_type]
  131. local existing_area = areastore:get_areas_for_pos(pos, false, true)
  132. local id = next(existing_area)
  133. if not id then
  134. return false -- nothing here
  135. end
  136. areastore:remove_area(id)
  137. remove_hud_marker(waypoints_type, pos)
  138. save(waypoints_type)
  139. return true
  140. end
  141. local function add_hud_marker(waypoints_type, player, player_name, pos, label, color)
  142. local waypoints_for_this_type = player_huds[waypoints_type]
  143. local waypoints = waypoints_for_this_type[player_name] or {}
  144. local pos_hash = minetest.hash_node_position(pos)
  145. if waypoints[pos_hash] then
  146. -- already exists
  147. return
  148. end
  149. waypoints_for_this_type[player_name] = waypoints
  150. color = color or 0xFFFFFF
  151. local hud_id = player:hud_add({
  152. hud_elem_type = "waypoint",
  153. name = label,
  154. text = "m",
  155. number = color,
  156. world_pos = pos})
  157. waypoints[pos_hash] = hud_id
  158. end
  159. local grouplen = #"group:"
  160. local function test_items(player, item, location)
  161. if not item then
  162. return true
  163. end
  164. location = location or inventory_string
  165. local group
  166. if item:sub(1,grouplen) == "group:" then
  167. group = item:sub(grouplen+1)
  168. end
  169. if location == inventory_string then
  170. local player_inv = player:get_inventory()
  171. if group then
  172. for _, itemstack in pairs(player_inv:get_list("main")) do
  173. if minetest.get_item_group(itemstack:get_name(), group) > 0 then
  174. return true
  175. end
  176. end
  177. elseif player_inv:contains_item("main", ItemStack(item)) then
  178. return true
  179. end
  180. elseif location == hotbar_string then
  181. local player_inv = player:get_inventory()
  182. if group then
  183. for i = 1,8 do
  184. local hot_item = player_inv:get_Stack("main", i)
  185. if minetest.get_item_group(hot_item:get_name(), group) > 0 then
  186. return true
  187. end
  188. end
  189. else
  190. local hot_required = ItemStack(item)
  191. for i = 1, 8 do
  192. local hot_item = player_inv:get_stack("main", i)
  193. if hot_item:get_name() == hot_required:get_name() and hot_item:get_count() >= hot_required:get_count() then
  194. return true
  195. end
  196. end
  197. end
  198. elseif location == wielded_string then
  199. local wielded_item = player:get_wielded_item()
  200. if group then
  201. return minetest.get_item_group(wielded_item:get_name(), group) > 0
  202. else
  203. local wielded_required = ItemStack(item)
  204. if wielded_item:get_name() == wielded_required:get_name() and wielded_item:get_count() >= wielded_required:get_count() then
  205. return true
  206. end
  207. end
  208. else
  209. minetest.log("error", "[named_waypoints] Illegal inventory location " .. location .. " to test for an item.")
  210. end
  211. return false
  212. end
  213. local function test_range(player_pos, waypoint_pos, volume_radius, volume_height)
  214. if not volume_radius then
  215. return false
  216. end
  217. if volume_height then
  218. if math.abs(player_pos.y - waypoint_pos.y) > volume_height then
  219. return false
  220. end
  221. return math.sqrt(
  222. ((player_pos.x - waypoint_pos.x)*(player_pos.x - waypoint_pos.x))+
  223. ((player_pos.z - waypoint_pos.z)*(player_pos.z - waypoint_pos.z))) <= volume_radius
  224. else
  225. return vector.distance(player_pos, waypoint_pos) <= volume_radius
  226. end
  227. end
  228. -- doesn't test for discovery status being lost, it is assumed that waypoints are
  229. -- rarely ever un-discovered once discovered.
  230. local function remove_distant_hud_markers(waypoint_type)
  231. local waypoint_def = waypoint_defs[waypoint_type]
  232. local vis_radius = waypoint_def.visibility_volume_radius
  233. if not vis_radius then
  234. -- if there's no visibility, there won't be any hud markers to remove
  235. return
  236. end
  237. local waypoints_for_this_type = player_huds[waypoint_type]
  238. local players_to_remove = {}
  239. local vis_inv = waypoint_def.visibility_requires_item
  240. local vis_loc = waypoint_def.visibility_item_location
  241. local vis_height = waypoint_def.visibility_volume_height
  242. for player_name, waypoints in pairs(waypoints_for_this_type) do
  243. local player = minetest.get_player_by_name(player_name)
  244. if player then
  245. local waypoints_to_remove = {}
  246. local player_pos = player:get_pos()
  247. for pos_hash, hud_id in pairs(waypoints) do
  248. local pos = minetest.get_position_from_hash(pos_hash)
  249. if not (test_items(player, vis_inv, vis_loc)
  250. and test_range(player_pos, pos, vis_radius, vis_height)) then
  251. table.insert(waypoints_to_remove, pos_hash)
  252. player:hud_remove(hud_id)
  253. end
  254. end
  255. for _, pos_hash in ipairs(waypoints_to_remove) do
  256. waypoints[pos_hash] = nil
  257. end
  258. if not next(waypoints) then -- player's waypoint list is empty, remove it
  259. table.insert(players_to_remove, player_name)
  260. end
  261. else
  262. table.insert(players_to_remove, player_name)
  263. end
  264. end
  265. for _, player_name in ipairs(players_to_remove) do
  266. player_huds[player_name] = nil
  267. end
  268. end
  269. local function get_range_box(pos, volume_radius, volume_height)
  270. if volume_height then
  271. return {x = pos.x - volume_radius, y = pos.y - volume_height, z = pos.z - volume_radius},
  272. {x = pos.x + volume_radius, y = pos.y + volume_height, z = pos.z + volume_radius}
  273. else
  274. return vector.subtract(pos, volume_radius), vector.add(pos, volume_radius)
  275. end
  276. end
  277. local elapsed = 0
  278. minetest.register_globalstep(function(dtime)
  279. elapsed = elapsed + dtime
  280. if elapsed < test_interval then
  281. return
  282. end
  283. elapsed = 0
  284. local connected_players = minetest.get_connected_players()
  285. for waypoint_type, waypoint_def in pairs(waypoint_defs) do
  286. local vis_radius = waypoint_def.visibility_volume_radius
  287. local disc_radius = waypoint_def.discovery_volume_radius
  288. if vis_radius or disc_radius then
  289. local areastore = waypoint_areastores[waypoint_type]
  290. local dirty_areastore = false
  291. local vis_height = waypoint_def.visibility_volume_height
  292. local vis_inv = waypoint_def.visibility_requires_item
  293. local vis_loc = waypoint_def.visibility_item_location
  294. local disc_height = waypoint_def.discovery_volume_height
  295. local disc_inv = waypoint_def.discovery_requires_item
  296. local disc_loc = waypoint_def.discovery_item_location
  297. local on_discovery = waypoint_def.on_discovery
  298. local default_color = waypoint_def.default_color
  299. local default_name = waypoint_def.default_name
  300. for _, player in ipairs(connected_players) do
  301. local player_pos = player:get_pos()
  302. local player_name = player:get_player_name()
  303. if disc_radius then
  304. local min_discovery_edge, max_discovery_edge = get_range_box(player_pos, disc_radius, disc_height)
  305. local potentially_discoverable = areastore:get_areas_in_area(min_discovery_edge, max_discovery_edge, true, true, true)
  306. for id, area_data in pairs(potentially_discoverable) do
  307. local pos = area_data.min
  308. local data = minetest.deserialize(area_data.data)
  309. local discovered_by = data.discovered_by or {}
  310. if not discovered_by[player_name] and
  311. test_items(player, disc_inv, disc_loc)
  312. and test_range(player_pos, pos, disc_radius, disc_height) then
  313. discovered_by[player_name] = true
  314. data.discovered_by = discovered_by
  315. areastore:remove_area(id)
  316. areastore:insert_area(pos, pos, minetest.serialize(data), id)
  317. if on_discovery then
  318. on_discovery(player, pos, data, waypoint_def)
  319. end
  320. dirty_areastore = true
  321. end
  322. end
  323. end
  324. if vis_radius then
  325. local min_visual_edge, max_visual_edge = get_range_box(player_pos, vis_radius, vis_height)
  326. local potentially_visible = areastore:get_areas_in_area(min_visual_edge, max_visual_edge, true, true, true)
  327. for id, area_data in pairs(potentially_visible) do
  328. local pos = area_data.min
  329. local data = minetest.deserialize(area_data.data)
  330. local discovered_by = data.discovered_by
  331. if (not disc_radius or (discovered_by and discovered_by[player_name])) and
  332. test_items(player, vis_inv, vis_loc)
  333. and test_range(player_pos, pos, vis_radius, vis_height) then
  334. add_hud_marker(waypoint_type, player, player_name, pos,
  335. data.name or default_name, data.color or default_color)
  336. end
  337. end
  338. end
  339. end
  340. if dirty_areastore then
  341. save(waypoint_type)
  342. end
  343. remove_distant_hud_markers(waypoint_type)
  344. end
  345. end
  346. end)
  347. -- Use this as a definition's on_discovery for a generic popup and sound alert
  348. named_waypoints.default_discovery_popup = function(player, pos, data, waypoint_def)
  349. local player_name = player:get_player_name()
  350. local discovery_name = data.name or waypoint_def.default_name
  351. local discovery_note = S("You've discovered @1", discovery_name)
  352. local formspec = "formspec_version[2]" ..
  353. "size[5,2]" ..
  354. "label[1.25,0.75;" .. minetest.formspec_escape(discovery_note) ..
  355. "]button_exit[1.0,1.25;3,0.5;btn_ok;".. S("OK") .."]"
  356. minetest.show_formspec(player_name, "named_waypoints:discovery_popup", formspec)
  357. minetest.chat_send_player(player_name, discovery_note)
  358. minetest.log("action", "[named_waypoints] " .. player_name .. " discovered " .. discovery_name)
  359. minetest.sound_play({name = "named_waypoints_chime01", gain = 0.25}, {to_player=player_name})
  360. end
  361. ------------------------------------------------------------------------------------------------------------------
  362. --- Admin commands
  363. local formspec_state = {}
  364. local function get_formspec(player_name)
  365. local player = minetest.get_player_by_name(player_name)
  366. local player_pos = player:get_pos()
  367. local state = formspec_state[player_name] or {}
  368. formspec_state[player_name] = state
  369. state.row_index = state.row_index or 1
  370. local formspec = {
  371. "formspec_version[2]"
  372. .."size[8,9]"
  373. .."button_exit[7.0,0.25;0.5,0.5;close;X]"
  374. .."label[0.5,0.6;Type:]dropdown[1.25,0.5;2,0.25;type_select;"
  375. }
  376. local types = {}
  377. local i = 0
  378. local dropdown_selected_index
  379. for waypoint_type, def in pairs(waypoint_defs) do
  380. i = i + 1
  381. if not state.selected_type then
  382. state.selected_type = waypoint_type
  383. end
  384. if state.selected_type == waypoint_type then
  385. dropdown_selected_index = i
  386. end
  387. table.insert(types, waypoint_type)
  388. end
  389. local selected_def = waypoint_defs[state.selected_type]
  390. formspec[#formspec+1] = table.concat(types, ",") .. ";"..dropdown_selected_index.."]"
  391. formspec[#formspec+1] = "tablecolumns[text;text;text]table[0.5,1.0;7,4;waypoint_table;"
  392. local areastore = waypoint_areastores[state.selected_type]
  393. if not areastore then
  394. return ""
  395. end
  396. local areas_by_id = areastore:get_areas_in_area({x=-32000, y=-32000, z=-32000}, {x=32000, y=32000, z=32000}, true, true, true)
  397. local areas = {}
  398. for id, area in pairs(areas_by_id) do
  399. area.id = id
  400. table.insert(areas, area)
  401. end
  402. table.sort(areas, function(area1, area2)
  403. local dist1 = vector.distance(area1.min, player_pos)
  404. local dist2 = vector.distance(area2.min, player_pos)
  405. return dist1 < dist2
  406. end)
  407. local selected_area = areas[state.row_index]
  408. if not selected_area then
  409. state.row_index = 1
  410. end
  411. local selected_name = ""
  412. local selected_data_string = ""
  413. state.selected_id = nil
  414. state.selected_pos = nil
  415. for i, area in ipairs(areas) do
  416. if i == state.row_index then
  417. state.selected_id = area.id
  418. state.selected_pos = area.min
  419. selected_area = area
  420. selected_data_string = selected_area.data
  421. local selected_data = minetest.deserialize(selected_data_string)
  422. selected_name = minetest.formspec_escape(selected_data.name or selected_def.default_name or "unnamed")
  423. end
  424. local pos = area.min
  425. local data_string = area.data
  426. local data = minetest.deserialize(data_string)
  427. formspec[#formspec+1] = minetest.formspec_escape(data.name or selected_def.default_name or "unnamed")
  428. ..","..minetest.formspec_escape(minetest.pos_to_string(pos))
  429. ..",".. minetest.formspec_escape(data_string)
  430. formspec[#formspec+1] = ","
  431. end
  432. formspec[#formspec] = ";"..state.row_index.."]" -- don't use +1, this overwrites the last ","
  433. state.selected_pos = state.selected_pos or {x=0,y=0,z=0}
  434. formspec[#formspec+1] = "container[0.5,5.25]"
  435. .."label[0,0.15;X]field[0.25,0;1,0.25;pos_x;;"..state.selected_pos.x.."]"
  436. .."label[1.5,0.15;Y]field[1.75,0;1,0.25;pos_y;;"..state.selected_pos.y.."]"
  437. .."label[3.0,0.15;Z]field[3.25,0;1,0.25;pos_z;;"..state.selected_pos.z.."]"
  438. .."container_end[]"
  439. formspec[#formspec+1] = "textarea[0.5,5.75;7,2.25;waypoint_data;;".. minetest.formspec_escape(selected_data_string) .."]"
  440. formspec[#formspec+1] = "container[0.5,8.25]"
  441. .."button[0,0;1,0.5;teleport;"..S("Teleport").."]button[1,0;1,0.5;save;"..S("Save").."]"
  442. .."button[2,0;1,0.5;rename;"..S("Rename").."]field[3,0;2,0.5;waypoint_name;;" .. selected_name .."]"
  443. .."button[5,0;1,0.5;create;"..S("New").."]button[6,0;1,0.5;delete;"..S("Delete").."]"
  444. .."container_end[]"
  445. return table.concat(formspec)
  446. end
  447. minetest.register_chatcommand("named_waypoints", {
  448. description = S("Open server controls for named_waypoints"),
  449. func = function(name, param)
  450. if not minetest.check_player_privs(name, {server = true}) then
  451. minetest.chat_send_player(name, S("This command is for server admins only."))
  452. return
  453. end
  454. minetest.show_formspec(name, "named_waypoints:server_controls", get_formspec(name))
  455. end,
  456. })
  457. minetest.register_on_player_receive_fields(function(player, formname, fields)
  458. if formname ~= "named_waypoints:server_controls" then
  459. return
  460. end
  461. if fields.close then
  462. return
  463. end
  464. local player_name = player:get_player_name()
  465. if not minetest.check_player_privs(player_name, {server = true}) then
  466. minetest.chat_send_player(player_name, S("This command is for server admins only."))
  467. return
  468. end
  469. local refresh = false
  470. local state = formspec_state[player_name]
  471. if fields.type_select then
  472. formspec_state[player_name].selected_type = fields.type_select
  473. refresh = true
  474. end
  475. if fields.waypoint_table then
  476. local table_event = minetest.explode_table_event(fields.waypoint_table)
  477. if table_event.type == "CHG" then
  478. formspec_state[player_name].row_index = table_event.row
  479. refresh = true
  480. end
  481. end
  482. if fields.save then
  483. local deserialized = minetest.deserialize(fields.waypoint_data)
  484. local pos_x = tonumber(fields.pos_x)
  485. local pos_y = tonumber(fields.pos_y)
  486. local pos_z = tonumber(fields.pos_z)
  487. if deserialized and pos_x and pos_y and pos_z and state.selected_id then
  488. local areastore = waypoint_areastores[state.selected_type]
  489. local pos = vector.floor({x=pos_x, y=pos_y, z=pos_z})
  490. areastore:remove_area(state.selected_id)
  491. areastore:insert_area(pos, pos,
  492. fields.waypoint_data, state.selected_id)
  493. save(state.selected_type)
  494. remove_hud_marker(state.selected_type, state.selected_pos)
  495. minetest.chat_send_player(player_name, S("Waypoint updated."))
  496. else
  497. minetest.chat_send_player(player_name, S("Invalid syntax."))
  498. end
  499. refresh = true
  500. end
  501. if fields.delete then
  502. local areastore = waypoint_areastores[state.selected_type]
  503. areastore:remove_area(state.selected_id)
  504. save(state.selected_type)
  505. remove_hud_marker(state.selected_type, state.selected_pos)
  506. refresh = true
  507. end
  508. if fields.create then
  509. local pos = player:get_pos()
  510. local areastore = waypoint_areastores[state.selected_type]
  511. local existing_area = areastore:get_areas_for_pos(pos, false, true)
  512. local id = next(existing_area)
  513. if id then
  514. minetest.chat_send_player(player_name, S("There's already a waypoint there."))
  515. return
  516. end
  517. areastore:insert_area(pos, pos, minetest.serialize({}))
  518. save(state.selected_type)
  519. refresh = true
  520. end
  521. if fields.rename then
  522. local areastore = waypoint_areastores[state.selected_type]
  523. local area = areastore:get_area(state.selected_id, true, true)
  524. local data = minetest.deserialize(area.data)
  525. data.name = fields.waypoint_name
  526. areastore:remove_area(state.selected_id)
  527. areastore:insert_area(area.min, area.min, minetest.serialize(data), state.selected_id)
  528. save(state.selected_type)
  529. remove_hud_marker(state.selected_type, state.selected_pos)
  530. minetest.chat_send_player(player_name, S("Waypoint updated."))
  531. end
  532. if fields.teleport then
  533. player:set_pos(state.selected_pos)
  534. end
  535. if refresh then
  536. minetest.show_formspec(player_name, "named_waypoints:server_controls", get_formspec(player_name))
  537. end
  538. end)
  539. local function set_all_discovered(player_name, waypoint_type, state)
  540. local waypoint_list = named_waypoints.get_waypoints_in_area(waypoint_type,
  541. {x=-32000, y=-32000, z=-32000}, {x=32000, y=32000, z=32000})
  542. for id, waypoint in pairs(waypoint_list) do
  543. waypoint.data.discovered_by = waypoint.data.discovered_by or {}
  544. waypoint.data.discovered_by[player_name] = state
  545. named_waypoints.update_waypoint(waypoint_type, waypoint.pos, waypoint.data)
  546. end
  547. end
  548. minetest.register_chatcommand("named_waypoints_discover_all", {
  549. description = S("Set all waypoints of a type as discovered by you"),
  550. params = S("waypoint type"),
  551. privs = {["server"]=true},
  552. func = function(name, param)
  553. if param == "" or waypoint_defs[param] == nil then
  554. minetest.chat_send_player(name, S("Please provide a valid waypoint type as a parameter"))
  555. return
  556. end
  557. set_all_discovered(name, param, true)
  558. end,
  559. })
  560. minetest.register_chatcommand("named_waypoints_undiscover_all", {
  561. description = S("Set all waypoints of a type as not discovered by you"),
  562. params = S("waypoint type"),
  563. privs = {["server"]=true},
  564. func = function(name, param)
  565. if param == "" or waypoint_defs[param] == nil then
  566. minetest.chat_send_player(name, S("Please provide a valid waypoint type as a parameter"))
  567. return
  568. end
  569. set_all_discovered(name, param, nil)
  570. end,
  571. })