init.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. local S = minetest.get_translator("orienteering")
  2. local mod_map = minetest.get_modpath("map")
  3. local orienteering = {}
  4. orienteering.playerhuds = {}
  5. orienteering.settings = {}
  6. orienteering.settings.speed_unit = S("m/s")
  7. orienteering.settings.length_unit = S("m")
  8. orienteering.settings.hud_pos = { x = 0.5, y = 0 }
  9. orienteering.settings.hud_offset = { x = 0, y = 15 }
  10. orienteering.settings.hud_alignment = { x = 0, y = 0 }
  11. local set = tonumber(minetest.settings:get("orienteering_hud_pos_x"))
  12. if set then orienteering.settings.hud_pos.x = set end
  13. set = tonumber(minetest.settings:get("orienteering_hud_pos_y"))
  14. if set then orienteering.settings.hud_pos.y = set end
  15. set = tonumber(minetest.settings:get("orienteering_hud_offset_x"))
  16. if set then orienteering.settings.hud_offset.x = set end
  17. set = tonumber(minetest.settings:get("orienteering_hud_offset_y"))
  18. if set then orienteering.settings.hud_offset.y = set end
  19. set = minetest.settings:get("orienteering_hud_alignment")
  20. if set == "left" then
  21. orienteering.settings.hud_alignment.x = 1
  22. elseif set == "center" then
  23. orienteering.settings.hud_alignment.x = 0
  24. elseif set == "right" then
  25. orienteering.settings.hud_alignment.x = -1
  26. end
  27. local o_lines = 4 -- Number of lines in HUD
  28. -- Helper function to switch between 12h and 24 mode for the time
  29. function orienteering.toggle_time_mode(itemstack, user, pointed_thing)
  30. --[[ Player attribute “orienteering:twelve”:
  31. * "true": Use 12h mode for time
  32. * "false" or unset: Use 24h mode for time ]]
  33. if user:get_attribute("orienteering:twelve") == "true" then
  34. user:set_attribute("orienteering:twelve", "false")
  35. else
  36. user:set_attribute("orienteering:twelve", "true")
  37. end
  38. orienteering.update_hud_displays(user)
  39. end
  40. local use = S("Put this tool in your hotbar to see the data it provides.")
  41. local use_time = S("Put this tool in your hotbar to make use of its functionality. Punch to toggle between 24-hour and 12-hour display for the time feature.")
  42. -- Displays height (Y)
  43. minetest.register_tool("orienteering:altimeter", {
  44. description = S("Altimeter"),
  45. _doc_items_longdesc = S("It shows you your current elevation (Y)."),
  46. _doc_items_usagehelp = use,
  47. wield_image = "orienteering_altimeter.png",
  48. inventory_image = "orienteering_altimeter.png",
  49. groups = { disable_repair = 1 },
  50. })
  51. -- Displays X and Z coordinates
  52. minetest.register_tool("orienteering:triangulator", {
  53. description = S("Triangulator"),
  54. _doc_items_longdesc = S("It shows you the coordinates of your current position in the horizontal plane (X and Z)."),
  55. _doc_items_usagehelp = use,
  56. wield_image = "orienteering_triangulator.png",
  57. inventory_image = "orienteering_triangulator.png",
  58. groups = { disable_repair = 1 },
  59. })
  60. -- Displays player yaw
  61. -- TODO: calculate yaw difference between 2 points
  62. minetest.register_tool("orienteering:compass", {
  63. description = S("Compass"),
  64. _doc_items_longdesc = S("It shows you your yaw (horizontal viewing angle) in degrees."),
  65. _doc_items_usagehelp = use,
  66. wield_image = "orienteering_compass_wield.png",
  67. inventory_image = "orienteering_compass_inv.png",
  68. groups = { disable_repair = 1 },
  69. })
  70. -- Displays player pitch
  71. -- TODO: calculate pitch difference between 2 points
  72. minetest.register_tool("orienteering:sextant", {
  73. description = S("Sextant"),
  74. _doc_items_longdesc = S("It shows you your pitch (vertical viewing angle) in degrees."),
  75. _doc_items_usagehelp = use,
  76. wield_image = "orienteering_sextant_wield.png",
  77. inventory_image = "orienteering_sextant_inv.png",
  78. groups = { disable_repair = 1 },
  79. })
  80. -- Ultimate orienteering tool: Displays X,Y,Z, yaw, pitch, time, speed and enables the minimap
  81. minetest.register_tool("orienteering:quadcorder", {
  82. description = S("Quadcorder"),
  83. _doc_items_longdesc = S("This is the ultimate orientieering tool. It shows you your coordinates (X, Y and Z), shows your yaw and pitch (horizontal and vertical viewing angles), the current time, your current speed and it enables you to access the minimap."),
  84. wield_image = "orienteering_quadcorder.png",
  85. _doc_items_usagehelp = use_time,
  86. wield_scale = { x=1, y=1, z=3.5 },
  87. inventory_image = "orienteering_quadcorder.png",
  88. groups = { disable_repair = 1 },
  89. on_use = orienteering.toggle_time_mode,
  90. })
  91. -- Displays game time
  92. minetest.register_tool("orienteering:watch", {
  93. description = S("Watch"),
  94. _doc_items_longdesc = S("It shows you the current time."),
  95. _doc_items_usagehelp = S("Put the watch in your hotbar to see the time. Punch to toggle between the 24-hour and 12-hour display."),
  96. wield_image = "orienteering_watch.png",
  97. inventory_image = "orienteering_watch.png",
  98. groups = { disable_repair = 1 },
  99. on_use = orienteering.toggle_time_mode,
  100. })
  101. -- Displays speed
  102. minetest.register_tool("orienteering:speedometer", {
  103. description = S("Speedometer"),
  104. _doc_items_longdesc = S("It shows you your current horizontal (“hor.”) and vertical (“ver.”) speed in meters per second, where one meter is the side length of a single cube."),
  105. _doc_items_usagehelp = use,
  106. wield_image = "orienteering_speedometer_wield.png",
  107. inventory_image = "orienteering_speedometer_inv.png",
  108. groups = { disable_repair = 1 },
  109. })
  110. if not mod_map then
  111. -- Enables minimap (surface)
  112. minetest.register_tool("orienteering:map", {
  113. description = S("Map"),
  114. _doc_items_longdesc = S("The map allows you to view a minimap of the area around you."),
  115. _doc_items_usagehelp = S("If you put a map in your hotbar, you will be able to access the minimap (only surface mode). By default, the minimap can be opened with [F7]."),
  116. wield_image = "orienteering_map.png",
  117. wield_scale = { x=1.5, y=1.5, z=0.15 },
  118. inventory_image = "orienteering_map.png",
  119. groups = { disable_repair = 1 },
  120. })
  121. end
  122. -- Enables minimap (radar)
  123. minetest.register_tool("orienteering:automapper", {
  124. description = S("Radar Mapper"),
  125. _doc_items_longdesc = S("The radar mapper is a device that combines a map with a radar. It unlocks both the surface mode and radar mode of the minimap."),
  126. _doc_items_usagehelp = S("If you put a radar mapper in your hotbar, you will be able to access the minimap. By default, the minimap can be opened with [F7]."),
  127. wield_image = "orienteering_automapper_wield.png",
  128. wield_scale = { x=1, y=1, z=2 },
  129. inventory_image = "orienteering_automapper_inv.png",
  130. groups = { disable_repair = 1 },
  131. })
  132. -- Displays X,Y,Z coordinates, yaw and game time
  133. minetest.register_tool("orienteering:gps", {
  134. description = S("GPS device"),
  135. _doc_items_longdesc = S("The GPS device shows you your coordinates (X, Y and Z), your yaw (horizontal viewing angle) and the time."),
  136. _doc_items_usagehelp = use_time,
  137. wield_image = "orienteering_gps_wield.png",
  138. wield_scale = { x=1, y=1, z=2 },
  139. inventory_image = "orienteering_gps_inv.png",
  140. groups = { disable_repair = 1 },
  141. on_use = orienteering.toggle_time_mode,
  142. })
  143. if minetest.get_modpath("default") ~= nil then
  144. -- Register crafts
  145. minetest.register_craft({
  146. output = "orienteering:altimeter",
  147. recipe = {
  148. {"default:glass"},
  149. {"default:steel_ingot"},
  150. {"default:steel_ingot"},
  151. }
  152. })
  153. minetest.register_craft({
  154. output = "orienteering:triangulator",
  155. recipe = {
  156. {"", "default:bronze_ingot", ""},
  157. {"default:bronze_ingot", "", "default:bronze_ingot"},
  158. }
  159. })
  160. minetest.register_craft({
  161. output = "orienteering:sextant",
  162. recipe = {
  163. {"", "default:gold_ingot", ""},
  164. {"default:gold_ingot", "default:gold_ingot", "default:gold_ingot"},
  165. }
  166. })
  167. minetest.register_craft({
  168. output = "orienteering:compass",
  169. recipe = {
  170. {"", "default:tin_ingot", ""},
  171. {"default:tin_ingot", "group:stick", "default:tin_ingot"},
  172. {"", "default:tin_ingot", ""},
  173. }
  174. })
  175. minetest.register_craft({
  176. output = "orienteering:speedometer",
  177. recipe = {
  178. {"", "default:gold_ingot", ""},
  179. {"default:steel_ingot", "group:stick", "default:steel_ingot"},
  180. {"", "default:steel_ingot", ""},
  181. }
  182. })
  183. minetest.register_craft({
  184. output = "orienteering:automapper",
  185. recipe = {
  186. {"default:gold_ingot", "default:gold_ingot", "default:gold_ingot"},
  187. {"default:mese_crystal", "default:obsidian_shard", "default:mese_crystal"},
  188. {"default:gold_ingot", "default:gold_ingot", "default:gold_ingot"}
  189. }
  190. })
  191. minetest.register_craft({
  192. output = "orienteering:gps",
  193. recipe = {
  194. { "default:gold_ingot", "orienteering:triangulator", "default:gold_ingot" },
  195. { "orienteering:compass", "default:bronze_ingot", "orienteering:watch" },
  196. { "default:tin_ingot", "orienteering:altimeter", "default:tin_ingot" }
  197. }
  198. })
  199. minetest.register_craft({
  200. output = "orienteering:quadcorder",
  201. recipe = {
  202. { "default:gold_ingot", "default:gold_ingot", "default:gold_ingot" },
  203. { "orienteering:speedometer", "default:diamond", "orienteering:automapper", },
  204. { "orienteering:sextant", "default:diamond", "orienteering:gps" }
  205. }
  206. })
  207. minetest.register_craft({
  208. output = "orienteering:watch",
  209. recipe = {
  210. { "default:copper_ingot" },
  211. { "default:glass" },
  212. { "default:copper_ingot" }
  213. }
  214. })
  215. if (not mod_map) and minetest.get_modpath("dye") then
  216. minetest.register_craft({
  217. output = "orienteering:map",
  218. recipe = {
  219. { "default:paper", "default:paper", "default:paper" },
  220. { "default:paper", "dye:black", "default:paper" },
  221. { "default:paper", "default:paper", "default:paper" },
  222. }
  223. })
  224. end
  225. end
  226. function orienteering.update_automapper(player)
  227. if mod_map then
  228. if orienteering.tool_active(player, "orienteering:automapper") or orienteering.tool_active(player, "orienteering:quadcorder") or minetest.settings:get_bool("creative_mode") then
  229. player:hud_set_flags({minimap_radar = true})
  230. else
  231. player:hud_set_flags({minimap_radar = false})
  232. end
  233. else
  234. if orienteering.tool_active(player, "orienteering:automapper") or orienteering.tool_active(player, "orienteering:quadcorder") or minetest.settings:get_bool("creative_mode") then
  235. player:hud_set_flags({minimap = true, minimap_radar = true})
  236. elseif orienteering.tool_active(player, "orienteering:map") then
  237. player:hud_set_flags({minimap = true, minimap_radar = false})
  238. else
  239. player:hud_set_flags({minimap = false, minimap_radar = false})
  240. end
  241. end
  242. end
  243. -- Checks whether a certain orienteering tool is “active” and ready for use
  244. function orienteering.tool_active(player, item)
  245. -- Requirement: player carries the tool in the hotbar
  246. local inv = player:get_inventory()
  247. local hotbar = player:hud_get_hotbar_itemcount()
  248. for i=1, hotbar do
  249. if inv:get_stack("main", i):get_name() == item then
  250. return true
  251. end
  252. end
  253. return false
  254. end
  255. function orienteering.init_hud(player)
  256. orienteering.update_automapper(player)
  257. local name = player:get_player_name()
  258. orienteering.playerhuds[name] = {}
  259. for i=1, o_lines do
  260. orienteering.playerhuds[name]["o_line"..i] = player:hud_add({
  261. hud_elem_type = "text",
  262. text = "",
  263. position = orienteering.settings.hud_pos,
  264. offset = { x = orienteering.settings.hud_offset.x, y = orienteering.settings.hud_offset.y + 20*(i-1) },
  265. alignment = orienteering.settings.hud_alignment,
  266. number = 0xFFFFFF,
  267. scale= { x = 100, y = 20 },
  268. })
  269. end
  270. end
  271. function orienteering.update_hud_displays(player)
  272. local toDegrees=180/math.pi
  273. local name = player:get_player_name()
  274. local gps, altimeter, triangulator, compass, sextant, watch, speedometer, quadcorder
  275. if orienteering.tool_active(player, "orienteering:gps") then
  276. gps = true
  277. end
  278. if orienteering.tool_active(player, "orienteering:altimeter") then
  279. altimeter = true
  280. end
  281. if orienteering.tool_active(player, "orienteering:triangulator") then
  282. triangulator = true
  283. end
  284. if orienteering.tool_active(player, "orienteering:compass") then
  285. compass = true
  286. end
  287. if orienteering.tool_active(player, "orienteering:sextant") then
  288. sextant = true
  289. end
  290. if orienteering.tool_active(player, "orienteering:watch") then
  291. watch = true
  292. end
  293. if orienteering.tool_active(player, "orienteering:speedometer") then
  294. speedometer = true
  295. end
  296. if orienteering.tool_active(player, "orienteering:quadcorder") then
  297. quadcorder = true
  298. end
  299. local str_pos, str_angles, str_time, str_speed
  300. local pos = vector.round(player:get_pos())
  301. if (altimeter and triangulator) or gps or quadcorder then
  302. str_pos = S("Coordinates: X=@1, Y=@2, Z=@3", pos.x, pos.y, pos.z)
  303. elseif altimeter then
  304. str_pos = S("Height: Y=@1", pos.y)
  305. elseif triangulator then
  306. str_pos = S("Coordinates: X=@1, Z=@2", pos.x, pos.z)
  307. else
  308. str_pos = ""
  309. end
  310. local yaw = player:get_look_horizontal()*toDegrees
  311. local pitch = player:get_look_vertical()*toDegrees
  312. if ((compass or gps) and sextant) or quadcorder then
  313. str_angles = S("Yaw: @1°, pitch: @2°", string.format("%.1f", yaw), string.format("%.1f", pitch))
  314. elseif compass or gps then
  315. str_angles = S("Yaw: @1°", string.format("%.1f", yaw))
  316. elseif sextant then
  317. str_angles = S("Pitch: @1°", string.format("%.1f", pitch))
  318. else
  319. str_angles = ""
  320. end
  321. local time = minetest.get_timeofday()
  322. if watch or gps or quadcorder then
  323. local totalminutes = time * 1440
  324. local minutes = totalminutes % 60
  325. local hours = math.floor((totalminutes - minutes) / 60)
  326. minutes = math.floor(minutes)
  327. local twelve = player:get_attribute("orienteering:twelve") == "true"
  328. if twelve then
  329. if hours == 12 and minutes == 0 then
  330. str_time = S("Time: noon")
  331. elseif hours == 0 and minutes == 0 then
  332. str_time = S("Time: midnight")
  333. else
  334. local hours12 = math.fmod(hours, 12)
  335. if hours12 == 0 then hours12 = 12 end
  336. if hours >= 12 then
  337. str_time = S("Time: @1:@2 p.m.", string.format("%i", hours12), string.format("%02i", minutes))
  338. else
  339. str_time = S("Time: @1:@2 a.m.", string.format("%i", hours12), string.format("%02i", minutes))
  340. end
  341. end
  342. else
  343. str_time = S("Time: @1:@2", string.format("%02i", hours), string.format("%02i", minutes))
  344. end
  345. else
  346. str_time = ""
  347. end
  348. if speedometer or quadcorder then
  349. local speed_hor, speed_ver
  350. local v
  351. local attach = player:get_attach()
  352. if attach == nil then
  353. v = player:get_player_velocity()
  354. else
  355. v = attach:get_velocity()
  356. if not v then
  357. v = player:get_player_velocity()
  358. end
  359. end
  360. speed_ver = v.y
  361. v.y = 0
  362. speed_hor = vector.length(v)
  363. local u = orienteering.settings.speed_unit
  364. str_speed = S("Speed: hor.: @1 @2, vert.: @3 @4", string.format("%.1f", speed_hor), u, string.format("%.1f", speed_ver), u)
  365. else
  366. str_speed = ""
  367. end
  368. local strs = { str_pos, str_angles, str_time, str_speed }
  369. local line = 1
  370. for i=1, o_lines do
  371. if strs[i] ~= "" then
  372. player:hud_change(orienteering.playerhuds[name]["o_line"..line], "text", strs[i])
  373. line = line + 1
  374. end
  375. end
  376. for l=line, o_lines do
  377. player:hud_change(orienteering.playerhuds[name]["o_line"..l], "text", "")
  378. end
  379. end
  380. minetest.register_on_newplayer(orienteering.init_hud)
  381. minetest.register_on_joinplayer(orienteering.init_hud)
  382. minetest.register_on_leaveplayer(function(player)
  383. orienteering.playerhuds[player:get_player_name()] = nil
  384. end)
  385. local updatetimer = 0
  386. minetest.register_globalstep(function(dtime)
  387. updatetimer = updatetimer + dtime
  388. if updatetimer > 0.1 then
  389. local players = minetest.get_connected_players()
  390. for i=1, #players do
  391. orienteering.update_automapper(players[i])
  392. orienteering.update_hud_displays(players[i])
  393. end
  394. updatetimer = updatetimer - dtime
  395. end
  396. end)
  397. if minetest.get_modpath("awards") ~= nil and minetest.get_modpath("default") ~= nil then
  398. awards.register_achievement("orienteering_quadcorder", {
  399. title = S("Master of Orienteering"),
  400. description = S("Craft a quadcorder."),
  401. icon = "orienteering_quadcorder.png",
  402. trigger = {
  403. type = "craft",
  404. item = "orienteering:quadcorder",
  405. target = 1
  406. }
  407. })
  408. end