init.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. -- Mesebox
  2. local mesebox = {}
  3. mesebox.open_meseboxs = {}
  4. mesebox.variants = {
  5. white = "White Mesebox",
  6. black = "Black Mesebox",
  7. red = "Red Mesebox",
  8. blue = "Blue Mesebox",
  9. green = "Green Mesebox",
  10. yellow = "Yellow Mesebox",
  11. orange = "Orange Mesebox",
  12. violet = "Violet Mesebox",
  13. }
  14. local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil
  15. function mesebox.get_mesebox_formspec(pos)
  16. local meta = minetest.get_meta(pos)
  17. local spos = pos.x .. "," .. pos.y .. "," .. pos.z
  18. local alias = meta:get_string("alias") and meta:get_string("alias") or meta:get_string("description")
  19. local formspec = "size[8,8.5]" ..
  20. "field[0.3,0.1;4,1;alias;;" .. alias .. "]"..
  21. "button_exit[4,-0.2;2,1;save;Update Name]"..
  22. "list[nodemeta:" .. spos .. ";main;0,0.8;8,3;]" ..
  23. "label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#fff", "Inventory")).."]"..
  24. "list[current_player;main;0,4.5;8,1;]"..
  25. default.get_hotbar_bg(0,4.5,8,1)..
  26. "list[current_player;main;0,5.7;8,3;8]"..
  27. default.get_hotbar_bg(0,5.7,8,3)..
  28. "listring[nodemeta:" .. spos .. ";main]" ..
  29. "listring[current_player;main]"
  30. return formspec
  31. end
  32. function mesebox.mesebox_lid_close(pn)
  33. local mesebox_open_info = mesebox.open_meseboxs[pn]
  34. local pos = mesebox_open_info.pos
  35. local sound = mesebox_open_info.sound
  36. local swap = mesebox_open_info.swap
  37. mesebox.open_meseboxs[pn] = nil
  38. for k, v in pairs(mesebox.open_meseboxs) do
  39. if v.pos.x == pos.x and v.pos.y == pos.y and v.pos.z == pos.z then
  40. return true
  41. end
  42. end
  43. local node = minetest.get_node(pos)
  44. minetest.after(0.2, minetest.swap_node, pos, { name = swap,
  45. param2 = node.param2 })
  46. minetest.sound_play(sound, {gain = 0.3, pos = pos,
  47. max_hear_distance = 10}, true)
  48. end
  49. function mesebox.can_open(pos)
  50. local dirs = {}
  51. dirs.n = {x = pos.x, y = pos.y, z = pos.z+1}
  52. dirs.s = {x = pos.x, y = pos.y, z = pos.z-1}
  53. dirs.w = {x = pos.x-1, y = pos.y, z = pos.z}
  54. dirs.e = {x = pos.x+1, y = pos.y, z = pos.z}
  55. local blocked = 0
  56. for _,dir in pairs(dirs) do
  57. local def = minetest.registered_nodes[minetest.get_node(dir).name]
  58. -- allow ladders, signs, wallmounted things and torches to not obstruct
  59. if def and (def.drawtype == "airlike" or
  60. def.drawtype == "signlike" or
  61. def.drawtype == "torchlike" or
  62. (def.drawtype == "nodebox" and def.paramtype2 == "wallmounted")) then
  63. else
  64. blocked = blocked +1
  65. end
  66. end
  67. return blocked < 4
  68. end
  69. minetest.register_on_player_receive_fields(function(player, formname, fields)
  70. if formname ~= "mesebox:mesebox" then
  71. return
  72. end
  73. if not player or not fields.quit then
  74. return
  75. end
  76. local pn = player:get_player_name()
  77. if not mesebox.open_meseboxs[pn] then
  78. return
  79. end
  80. if fields.alias and fields.alias ~= "" then
  81. local mesebox_open_info = mesebox.open_meseboxs[pn]
  82. local pos = mesebox_open_info.pos
  83. local meta = minetest.get_meta(pos)
  84. meta:set_string("alias", fields.alias)
  85. mesebox.update_ratio(meta)
  86. mesebox.update_infotext(meta)
  87. end
  88. mesebox.mesebox_lid_close(pn)
  89. return true
  90. end)
  91. minetest.register_on_leaveplayer(function(player)
  92. local pn = player:get_player_name()
  93. if mesebox.open_meseboxs[pn] then
  94. mesebox.mesebox_lid_close(pn)
  95. end
  96. end)
  97. function mesebox.update_ratio(meta)
  98. -- update ratio
  99. local inv = meta:get_inventory()
  100. local size = 24
  101. local count = 0
  102. for i = 1, size do
  103. if not inv:get_stack("main", i):is_empty() then count = count + 1 end
  104. end
  105. meta:set_string("ratio", "["..count.."/"..size.."]")
  106. end
  107. function mesebox.update_infotext(meta)
  108. -- update infotext
  109. meta:set_string("infotext", meta:get_string("alias") .. " " .. meta:get_string("ratio"))
  110. end
  111. function mesebox.register_mesebox(name, color, desc)
  112. local def = {}
  113. def.description = desc
  114. def.paramtype = "light"
  115. def.paramtype2 = "facedir"
  116. def.is_ground_content = false
  117. def.sunlight_propagates = false
  118. def.groups = { choppy = 2, oddly_breakable_by_hand = 3, tubedevice = 1,
  119. tubedevice_receiver = 1, mesebox = 1 }
  120. def.sounds = default.node_sound_wood_defaults()
  121. def.sound_open = "mesebox_open"
  122. def.sound_close = "mesebox_close"
  123. def.stack_max = 1
  124. def.drop = ""
  125. -- Called every time the item is placed in the world, before 'after_place_node'.
  126. def.on_construct = function(pos)
  127. -- Nothing to do here, everything is initialized in
  128. -- 'after_place_node'
  129. end
  130. def.on_rightclick = function(pos, node, clicker)
  131. -- XXX: The Scanner is currently blocking from opening so don't do this for now.
  132. -- if not mesebox.can_open(pos) then
  133. -- return
  134. -- end
  135. minetest.sound_play(def.sound_open, {gain = 0.5, pos = pos,
  136. max_hear_distance = 10}, true)
  137. minetest.swap_node(pos, { name = name .. "_open",
  138. param2 = node.param2 })
  139. minetest.after(0.2, minetest.show_formspec,
  140. clicker:get_player_name(),
  141. "mesebox:mesebox", mesebox.get_mesebox_formspec(pos))
  142. mesebox.open_meseboxs[clicker:get_player_name()] = {
  143. pos = pos, sound = def.sound_close, swap = name
  144. }
  145. end
  146. def.on_blast = function(pos)
  147. local drops = {}
  148. mesebox.get_inventory_drops(pos, "main", drops)
  149. drops[#drops+1] = name
  150. minetest.remove_node(pos)
  151. return drops
  152. end
  153. def.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  154. local name = player:get_player_name()
  155. if minetest.is_protected(pos, name) then
  156. minetest.record_protection_violation(pos, name)
  157. return 0
  158. end
  159. local group = minetest.get_item_group(stack:get_name(), "mesebox")
  160. if group == 0 or group == nil then
  161. return stack:get_count()
  162. else
  163. return 0
  164. end
  165. end
  166. def.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  167. local name = player:get_player_name()
  168. if minetest.is_protected(pos, name) then
  169. minetest.record_protection_violation(pos, name)
  170. return 0
  171. else
  172. return 99
  173. end
  174. end
  175. def.on_metadata_inventory_put = function(pos, listname, index, stack, player)
  176. local meta = minetest.get_meta(pos)
  177. mesebox.update_ratio(meta)
  178. mesebox.update_infotext(meta)
  179. end
  180. def.on_metadata_inventory_take = function(pos, listname, index, stack, player)
  181. local meta = minetest.get_meta(pos)
  182. mesebox.update_ratio(meta)
  183. mesebox.update_infotext(meta)
  184. end
  185. def.tube = {
  186. insert_object = function(pos, node, stack, direction)
  187. local meta = minetest.get_meta(pos)
  188. local inv = meta:get_inventory()
  189. return inv:add_item("main", stack)
  190. end,
  191. can_insert = function(pos, node, stack, direction)
  192. local meta = minetest.get_meta(pos)
  193. local inv = meta:get_inventory()
  194. if meta:get_int("splitstacks") == 1 then
  195. stack = stack:peek_item(1)
  196. end
  197. return inv:room_for_item("main", stack)
  198. end,
  199. input_inventory = "main",
  200. connect_sides = {left = 1, right = 1, front = 1, back = 1, bottom = 1, top = 1}
  201. }
  202. local def_opened = table.copy(def)
  203. local def_closed = table.copy(def)
  204. local pipes = ""
  205. -- XXX: Was gonna use alternative textures for pipeworks but trying a single
  206. -- more discrete design instead that indicates that pipes are supported.
  207. -- if pipeworks_enabled then
  208. -- pipes = "_pipes"
  209. -- end
  210. def_opened.tiles = {
  211. -- top, bottom, side, side, side, side
  212. color.."_mesebox_top"..pipes..".png",
  213. color.."_mesebox_top"..pipes..".png",
  214. color.."_mesebox_side_open.png",
  215. color.."_mesebox_side_open.png",
  216. color.."_mesebox_side_open.png",
  217. color.."_mesebox_side_open.png",
  218. }
  219. def_closed.tiles = {
  220. -- top, bottom, side, side, side, side
  221. color.."_mesebox_top"..pipes..".png",
  222. color.."_mesebox_top"..pipes..".png",
  223. color.."_mesebox_side"..pipes..".png",
  224. color.."_mesebox_side"..pipes..".png",
  225. color.."_mesebox_side"..pipes..".png",
  226. color.."_mesebox_side"..pipes..".png",
  227. }
  228. def_opened.drop = name
  229. def_opened.groups.not_in_creative_inventory = 1
  230. def_opened.groups.not_in_craft_guide = 1
  231. def_opened.can_dig = function()
  232. return false
  233. end
  234. def_opened.on_blast = function() end
  235. def_closed.after_place_node = function(pos, placer, itemstack, pointed_thing)
  236. local nmeta = minetest.get_meta(pos)
  237. local ninv = nmeta:get_inventory()
  238. local imeta = itemstack:get_meta()
  239. -- Need to re-construct the Node's inventory each time it's placed.
  240. ninv:set_size("main", 8*3)
  241. local data_str = imeta:get_string("data")
  242. if data_str == "" then
  243. -- This Mesebox is placed for the first time, set valid defaults.
  244. nmeta:set_string("alias", desc)
  245. nmeta:set_string("ratio", "[0/24]")
  246. else
  247. local data = minetest.deserialize(data_str)
  248. -- Move inventory from ItemStack to Node.
  249. ninv:set_list("main", data.items)
  250. -- Copy internal variables.
  251. for k,v in pairs(data.fields) do
  252. nmeta:set_string(k,v)
  253. end
  254. end
  255. mesebox.update_infotext(nmeta)
  256. if pipeworks_enabled then
  257. pipeworks.after_place(pos)
  258. end
  259. if minetest.settings:get_bool("creative_mode") then
  260. if not ninv:is_empty("main") then
  261. return nil
  262. else
  263. return itemstack
  264. end
  265. else
  266. return nil
  267. end
  268. end
  269. def_closed.after_dig_node = function(pos, oldnode, oldmetatbl, digger)
  270. -- NOTE: oldmeta is in table format
  271. if not digger then
  272. return
  273. end
  274. local inv = oldmetatbl.inventory.main
  275. -- These can be nil if the mesebox we're picking up is an older version.
  276. -- Default to reasonable values instead of crashing.
  277. if not oldmetatbl.fields or not oldmetatbl.fields.ratio or not oldmetatbl.fields.alias then
  278. oldmetatbl.fields = {
  279. ratio = "",
  280. alias = desc,
  281. }
  282. end
  283. local ratio = oldmetatbl.fields.ratio
  284. local alias = oldmetatbl.fields.alias
  285. -- Move items from the Node to the ItemStack.
  286. local items = {}
  287. for i,v in ipairs(inv) do
  288. items[i] = v:to_string()
  289. end
  290. -- Copy our internal state variables.
  291. local fields = {}
  292. for k,v in pairs(oldmetatbl.fields) do
  293. fields[k] = v
  294. end
  295. local data = {
  296. items = items,
  297. fields = fields,
  298. }
  299. local istack = ItemStack("mesebox:"..color.."_mesebox")
  300. local imeta = istack:get_meta()
  301. imeta:set_string("description", alias.." "..ratio)
  302. -- Serialize and store the Node's inventory and internal variables
  303. -- in the ItemStack's metadata so it can be retrieved later when
  304. -- it is again placed in the world.
  305. imeta:set_string("data", minetest.serialize(data))
  306. local dinv = digger:get_inventory()
  307. if dinv:room_for_item("main", istack) then
  308. dinv:add_item("main", istack)
  309. else
  310. minetest.add_item(pos, istack)
  311. end
  312. if pipeworks_enabled then
  313. pipeworks.after_dig(pos)
  314. end
  315. end
  316. minetest.register_node(name, def_closed)
  317. minetest.register_node(name .. "_open", def_opened)
  318. end
  319. for color, desc in pairs(mesebox.variants) do
  320. local name = "mesebox:" .. color .. "_mesebox"
  321. mesebox.register_mesebox(name, color, desc)
  322. minetest.register_craft({
  323. type = "shapeless",
  324. output = "mesebox:"..color.."_mesebox",
  325. recipe = { "group:mesebox", "dye:"..color }
  326. })
  327. end
  328. minetest.register_craft({
  329. output = "mesebox:yellow_mesebox 1",
  330. recipe = {
  331. { "default:mese", "furniture:lock", "default:mese" },
  332. { "group:wood", "default:mese", "group:wood" },
  333. { "default:mese", "furniture:hinge", "default:mese" },
  334. }
  335. })
  336. hopper:add_container({
  337. {"top", "group:mesebox", "main"},
  338. {"bottom", "group:mesebox", "main"},
  339. })
  340. -- Keep inventory of mesebox when changing color by crafting
  341. minetest.register_on_craft(
  342. function(itemstack, player, old_craft_grid, craft_inv)
  343. local new = itemstack:get_name()
  344. if minetest.get_item_group(itemstack:get_name(), "mesebox") ~= 1 then
  345. return
  346. end
  347. -- Search for an existing Mesebox in the crafting grid and store in 'old'.
  348. local old
  349. for i = 1, #old_craft_grid do
  350. local item = old_craft_grid[i]:get_name()
  351. if minetest.get_item_group(item, "mesebox") == 1 then
  352. old = old_craft_grid[i]
  353. break
  354. end
  355. end
  356. if old then
  357. -- Crafting Mesebox with dye
  358. local ometa = old:get_meta()
  359. local imeta = itemstack:get_meta()
  360. local data = minetest.deserialize(ometa:get_string("data"))
  361. if data then
  362. -- Update name with name from new Mesebox and transfer table.
  363. local new_desc = itemstack:get_description()
  364. data.fields.alias = new_desc
  365. ometa:set_string("data", minetest.serialize(data))
  366. imeta:from_table(ometa:to_table())
  367. imeta:set_string("description", new_desc.." "..data.fields.ratio)
  368. else
  369. -- No 'data' means this Mesebox has never been placed.
  370. -- We set defaults in 'after_place_node' when the Mesebox is placed
  371. -- into the world for the first time so leave as is here.
  372. imeta:from_table(ometa:to_table())
  373. end
  374. else
  375. -- Couldn't find existing Mesebox in the crafting grid which
  376. -- means we're crafting a new one.
  377. -- We set defaults in 'after_place_node' when the Mesebox is placed
  378. -- into the world for the first time so leave as is here.
  379. end
  380. return itemstack
  381. end
  382. )