init.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. -- Mod global namespace
  2. map = map or {}
  3. map.modpath = minetest.get_modpath("map")
  4. map.players = map.players or {}
  5. map.time_step = 10
  6. map.charge_time = 60*60 -- 1 hour of continuous use.
  7. -- Shall be called shortly after player joins game, to create the initial cache.
  8. -- Shall also be called whenever inventory is modified in such a way that a mapping kit is moved/added/removed.
  9. -- The cache shall be cleared whenever the player dies.
  10. -- Shall also be called whenever the player modifies their inventory in a way that a mapping kit is changed.
  11. function map.update_inventory_info(pname)
  12. local player = minetest.get_player_by_name(pname)
  13. if not player or not player:is_player() then
  14. return
  15. end
  16. if not map.players[pname] then
  17. map.players[pname] = {has_kit = false, indices={}}
  18. end
  19. local inv = player:get_inventory()
  20. if not inv then
  21. return
  22. end
  23. -- Reset list ID indices array.
  24. map.players[pname].indices = {}
  25. if inv:contains_item("main", "map:mapping_kit") or inv:contains_item("main", "map:mapping_tool") then
  26. local list = inv:get_list("main")
  27. for k, v in ipairs(list) do
  28. if not v:is_empty() then
  29. if map.is_mapping_kit(v:get_name()) then
  30. local wear = v:get_wear()
  31. -- If wear is 0, the tool is not charged, and has never been charged.
  32. -- Ignore discharged mapping kits/tools.
  33. if wear > 0 and wear < 65534 then
  34. table.insert(map.players[pname].indices, k)
  35. elseif wear == 0 and v:get_name() == "map:mapping_kit" then
  36. -- Treat mapping kit as if it is a tool with full charge.
  37. -- The item will be upgraded by the function that actually handles charge draining.
  38. table.insert(map.players[pname].indices, k)
  39. end
  40. end
  41. end
  42. end
  43. end
  44. if #(map.players[pname].indices) > 0 then
  45. -- At least one inventory slot has a mapping kit.
  46. -- Need to check if there's one in the hotbar.
  47. local has_kit = false
  48. local barmax = 8
  49. if minetest.check_player_privs(pname, "big_hotbar") then
  50. barmax = 16
  51. end
  52. for k, v in ipairs(map.players[pname].indices) do
  53. if v >= 1 and v <= barmax then
  54. has_kit = true
  55. break
  56. end
  57. end
  58. map.players[pname].has_kit = has_kit
  59. else
  60. map.players[pname].has_kit = false
  61. end
  62. -- Finally, update the HUD flags on the client.
  63. map.update_hud_flags(player)
  64. end
  65. -- Called from bones code, mainly.
  66. function map.clear_inventory_info(pname)
  67. map.players[pname] = {has_kit = false, indices={}}
  68. end
  69. function map.consume_charge()
  70. for name, data in pairs(map.players) do
  71. if data.has_kit and #data.indices > 0 then
  72. local player = minetest.get_player_by_name(name)
  73. if player and player:is_player() then
  74. local inv = player:get_inventory()
  75. if inv then
  76. local idx = inv:get_size("main") + 1
  77. -- Find first mapping kit.
  78. for _, index in ipairs(data.indices) do
  79. if index < idx then
  80. idx = index
  81. end
  82. end
  83. if idx ~= 0 then
  84. local stack = inv:get_stack("main", idx)
  85. local sn = stack:get_name()
  86. if map.is_mapping_kit(sn) then
  87. local depleted = false
  88. -- Convert nodes to tools.
  89. if sn == "map:mapping_kit" then
  90. stack = ItemStack("map:mapping_tool")
  91. stack:set_wear(1) -- Give full charge.
  92. end
  93. -- Use up charge.
  94. -- Note: we assume the tool has charge, if not, it should not have been in the cache!
  95. local wear = stack:get_wear()
  96. local increment = (65535 / map.charge_time)
  97. wear = wear + (increment * map.time_step)
  98. -- Don't let wear reach max or tool will be destroyed.
  99. if wear >= 65534 then
  100. wear = 65534
  101. depleted = true
  102. end
  103. stack:set_wear(math.floor(wear))
  104. inv:set_stack("main", idx, stack)
  105. -- If this mapping tool has no charge left, update the cache info.
  106. if depleted then
  107. map.update_inventory_info(name)
  108. end
  109. end
  110. end
  111. end
  112. end
  113. end
  114. end
  115. -- Call recursively.
  116. minetest.after(map.time_step, function() map.consume_charge() end)
  117. end
  118. -- Not called when player digs or places node, or if player picks up a dropped item.
  119. -- Is called when an item is dropped/taken from ground, or is moved/taken from chest, etc.
  120. -- Specifically, NOT called when inventory is modified by a process the player did not initiate.
  121. function map.on_player_inventory_action(player, action, inventory, info)
  122. --[[
  123. if action == "take" or action == "put" then
  124. minetest.chat_send_player("MustTest",
  125. "# Server: " .. action .. " in " .. info.index .. ", " .. info.stack:get_name() .. " " .. info.stack:get_count())
  126. end
  127. --]]
  128. if action == "put" or action == "take" then
  129. local name = info.stack:get_name()
  130. if map.is_mapping_kit(name) then
  131. local pname = player:get_player_name()
  132. map.update_inventory_info(pname)
  133. end
  134. elseif action == "move" then
  135. local pname = player:get_player_name()
  136. if not map.players[pname] then
  137. map.update_inventory_info(pname)
  138. end
  139. if info.from_list == "main" then
  140. local from = info.from_index
  141. -- If the moved from slot was listed as holding a mapping kit, need to refresh the cache.
  142. for k, v in ipairs(map.players[pname].indices) do
  143. if from == v then
  144. map.update_inventory_info(pname)
  145. break
  146. end
  147. end
  148. end
  149. if info.to_list == "main" then
  150. -- This is only called when player moves from player-inv to another player-inv.
  151. -- We have to check what item was added.
  152. local stack = inventory:get_stack("main", info.to_index)
  153. if map.is_mapping_kit(stack:get_name()) then
  154. map.update_inventory_info(pname)
  155. end
  156. end
  157. end
  158. end
  159. -- May be called with player object or player name.
  160. -- Return 'true' if the minimap is ENABLED.
  161. function map.update_hud_flags(player)
  162. if type(player) == "string" then
  163. player = minetest.get_player_by_name(player)
  164. end
  165. if not player or not player:is_player() then
  166. return
  167. end
  168. local has_kit = map.has_mapping_kit(player)
  169. local minimap_enabled = has_kit
  170. local radar_enabled = false
  171. -- Map & radar combined into same device.
  172. player:hud_set_flags({
  173. minimap = minimap_enabled,
  174. minimap_radar = minimap_enabled,
  175. })
  176. if minimap_enabled then
  177. return true
  178. end
  179. end
  180. -- May be called with either a player object or a player name.
  181. function map.has_mapping_kit(pname_or_pref)
  182. local pname = pname_or_pref
  183. if type(pname) ~= "string" then
  184. pname = pname_or_pref:get_player_name()
  185. end
  186. -- If data doesn't exist yet, create the cache.
  187. if not map.players[pname] then
  188. map.update_inventory_info(pname)
  189. end
  190. return map.players[pname].has_kit
  191. end
  192. -- Use from /lua command, mainly.
  193. function map.query(pname)
  194. if map.has_mapping_kit(pname) then
  195. minetest.chat_send_player("MustTest", "# Server: Player <" .. rename.gpn(pname) .. "> has a mapping kit!")
  196. else
  197. minetest.chat_send_player("MustTest", "# Server: Player <" .. rename.gpn(pname) .. "> does not have a mapping kit.")
  198. end
  199. end
  200. function map.on_use(itemstack, user, pointed_thing)
  201. map.update_inventory_info(user:get_player_name())
  202. end
  203. function map.on_joinplayer(player)
  204. local pname = player:get_player_name()
  205. minetest.after(3, function()
  206. map.update_inventory_info(pname)
  207. end)
  208. end
  209. function map.on_leaveplayer(player, timeout)
  210. -- Cleanup.
  211. map.players[player:get_player_name()] = nil
  212. end
  213. function map.is_mapping_kit(name)
  214. if name == "map:mapping_kit" or name == "map:mapping_tool" then
  215. return true
  216. end
  217. return false
  218. end
  219. function map.on_place(itemstack, placer, pt)
  220. if not placer or not placer:is_player() then
  221. return
  222. end
  223. if pt.type ~= "node" then
  224. return
  225. end
  226. local node = minetest.get_node(pt.under)
  227. local ndef = minetest.registered_nodes[node.name]
  228. if not ndef then
  229. return
  230. end
  231. if ndef.on_rightclick and not placer:get_player_control().sneak then
  232. return ndef.on_rightclick(pt.under, node, placer, itemstack, pt) or itemstack
  233. end
  234. local fakestack = ItemStack("map:mapping_kit")
  235. local retstack, success = minetest.item_place(fakestack, placer, pt)
  236. if success then
  237. itemstack:take_item()
  238. return itemstack
  239. end
  240. end
  241. -- Set HUD flags 'on joinplayer'
  242. if not map.run_once then
  243. local desc = "Stone Mapper Uplink\n\nAllows viewing a map of your surroundings.\nKeep in your hotbar and use with the 'minimap' key (default F9).\nMust be charged to operate."
  244. -- Mapping kit item.
  245. minetest.register_node("map:mapping_kit", {
  246. tiles = {"map_mapping_kit_tile.png"},
  247. wield_image = "map_mapping_kit.png",
  248. description = desc,
  249. inventory_image = "map_mapping_kit.png",
  250. paramtype = 'light',
  251. paramtype2 = "wallmounted",
  252. drawtype = "nodebox",
  253. sunlight_propagates = true,
  254. walkable = false,
  255. node_box = {
  256. type = "wallmounted",
  257. wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
  258. wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
  259. wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
  260. },
  261. selection_box = {type = "wallmounted"},
  262. stack_max = 1,
  263. groups = utility.dig_groups("bigitem", {flammable = 3, attached_node = 1}),
  264. sounds = default.node_sound_leaves_defaults(),
  265. drop = "map:mapping_tool",
  266. on_use = function(...)
  267. return map.on_use(...)
  268. end,
  269. })
  270. -- Tool item is required in order for wear-bar to work.
  271. minetest.register_tool("map:mapping_tool", {
  272. description = desc,
  273. inventory_image = "map_mapping_kit.png",
  274. wear_represents = "eu_charge",
  275. groups = {not_repaired_by_anvil = 1, disable_repair = 1},
  276. on_use = function(...)
  277. return map.on_use(...)
  278. end,
  279. on_place = function(...)
  280. return map.on_place(...)
  281. end,
  282. })
  283. -- Crafting.
  284. minetest.register_craft({
  285. output = "map:mapping_tool",
  286. recipe = {
  287. {"default:glass", "plastic:plastic_sheeting", "default:obsidian_shard"},
  288. {"default:steel_ingot", "techcrafts:control_logic_unit", "default:steel_ingot"},
  289. {"fine_wire:silver", "battery:battery", "dusts:diamond"},
  290. }
  291. })
  292. -- Fuel.
  293. minetest.register_craft({
  294. type = "fuel",
  295. recipe = "map:mapping_tool",
  296. burntime = 5,
  297. })
  298. minetest.register_on_player_inventory_action(function(...)
  299. return map.on_player_inventory_action(...) end)
  300. minetest.register_on_joinplayer(function(...)
  301. return map.on_joinplayer(...) end)
  302. minetest.register_on_leaveplayer(function(...)
  303. return map.on_leaveplayer(...) end)
  304. minetest.after(map.time_step, function() map.consume_charge() end)
  305. local c = "map:core"
  306. local f = map.modpath .. "/init.lua"
  307. reload.register_file(c, f, false)
  308. map.run_once = true
  309. end