functions.lua 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  1. -- This function is responsible for triggering the update of vending
  2. -- machines when a chest inventory is modified.
  3. chest_api.update_vending = function(pos)
  4. local vendors = {}
  5. local node = nil
  6. local valid_vendor = function(name)
  7. local names = {
  8. "easyvend:vendor",
  9. "easyvend:vendor_on",
  10. "easyvend:depositor",
  11. "easyvend:depositor_on",
  12. }
  13. for k, v in ipairs(names) do
  14. if v == name then
  15. return true
  16. end
  17. end
  18. end
  19. local orig_pos = table.copy(pos)
  20. -- Search upwards.
  21. pos.y = pos.y + 1
  22. node = minetest.get_node_or_nil(pos)
  23. while node ~= nil do
  24. if easyvend.traversable_node_types[node.name] then
  25. if valid_vendor(node.name) then
  26. vendors[#vendors+1] = {pos=table.copy(pos), node=table.copy(node)}
  27. end
  28. else
  29. break
  30. end
  31. pos.y = pos.y + 1
  32. node = minetest.get_node_or_nil(pos)
  33. end
  34. pos = table.copy(orig_pos)
  35. -- Search downwards.
  36. pos.y = pos.y - 1
  37. node = minetest.get_node_or_nil(pos)
  38. while node ~= nil do
  39. if easyvend.traversable_node_types[node.name] then
  40. if valid_vendor(node.name) then
  41. vendors[#vendors+1] = {pos=table.copy(pos), node=table.copy(node)}
  42. end
  43. else
  44. break
  45. end
  46. pos.y = pos.y - 1
  47. node = minetest.get_node_or_nil(pos)
  48. end
  49. -- Perform vending checks!
  50. for k, v in ipairs(vendors) do
  51. easyvend.machine_check(v.pos, v.node)
  52. --minetest.chat_send_all("# Server: Machine check!")
  53. end
  54. end
  55. function chest_api.sort_chest(pos)
  56. local meta = minetest.get_meta(pos)
  57. local inv = meta:get_inventory()
  58. local inlist = inv:get_list("main")
  59. local typecnt = {}
  60. local typekeys = {}
  61. for _, st in ipairs(inlist) do
  62. if not st:is_empty() then
  63. local n = st:get_name()
  64. local k = string.format("%s", n)
  65. if not typecnt[k] then
  66. typecnt[k] = {st}
  67. table.insert(typekeys, k)
  68. else
  69. table.insert(typecnt[k], st)
  70. end
  71. end
  72. end
  73. table.sort(typekeys)
  74. inv:set_list("main", {})
  75. for _, k in ipairs(typekeys) do
  76. for _, item in ipairs(typecnt[k]) do
  77. inv:add_item("main", item)
  78. end
  79. end
  80. end
  81. local function get_share_names(meta)
  82. local share_names = meta:get_string("share_names") or ""
  83. if share_names ~= "" then
  84. local tb = minetest.parse_json(share_names)
  85. if tb and type(tb) == "table" then
  86. -- Count how many entries the share table has, and return that as second value.
  87. local c = 0
  88. for k, v in pairs(tb) do
  89. c = c + 1
  90. end
  91. return tb, c
  92. end
  93. end
  94. return {}, 0
  95. end
  96. chest_api.get_chest_formspec = function(name, desc, pos)
  97. local spos = pos.x .. "," .. pos.y .. "," .. pos.z
  98. local defgui = default.gui_bg ..
  99. default.gui_bg_img ..
  100. default.gui_slots
  101. local formspec
  102. local meta = minetest.get_meta(pos)
  103. local inv = meta:get_inventory()
  104. -- Permit grandfathering of old shared ironside chests.
  105. local shares, sharecount = get_share_names(meta)
  106. -- Obtain hooks into the trash mod's trash slot inventory.
  107. local ltrash, mtrash = trash.get_listname()
  108. local itrash = trash.get_iconname()
  109. -- Locked or unlocked gold chest.
  110. if string.find(name, "gold") then
  111. formspec = "size[12,10]" .. defgui ..
  112. "list[nodemeta:" .. spos .. ";main;0,1.3;12,4;]" ..
  113. "list[current_player;main;1,5.85;8,1;]" ..
  114. "list[current_player;main;1,7.08;8,3;8]" ..
  115. "listring[nodemeta:" .. spos .. ";main]" ..
  116. "listring[current_player;main]" ..
  117. "label[0,0;" .. desc .. "]" ..
  118. default.get_hotbar_bg(1, 5.85)
  119. -- Trash icon.
  120. .. "list[" .. ltrash .. ";" .. mtrash .. ";10,5.85;1,1;]" ..
  121. "image[10,5.85;1,1;" .. itrash .. "]"
  122. -- Locked gold chest.
  123. if string.find(name, "locked") then
  124. local chest_name = meta:get_string("chest_name") or ""
  125. formspec = formspec .. "button[10,0;2,1;rename;Rename]" ..
  126. "field[8.25,0.45;2,0.75;name;;]" ..
  127. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  128. end
  129. -- Locked or unlocked diamond chest.
  130. elseif string.find(name, "diamond") then
  131. formspec = "size[12,11]" .. defgui ..
  132. "list[nodemeta:" .. spos .. ";main;0,1.3;12,5;]" ..
  133. "list[current_player;main;1,6.85;8,1;]" ..
  134. "list[current_player;main;1,8.08;8,3;8]" ..
  135. "listring[nodemeta:" .. spos .. ";main]" ..
  136. "listring[current_player;main]" ..
  137. "label[0,0;" .. desc .. "]" ..
  138. default.get_hotbar_bg(1, 6.85)
  139. -- Trash icon.
  140. .. "list[" .. ltrash .. ";" .. mtrash .. ";10,6.85;1,1;]" ..
  141. "image[10,6.85;1,1;" .. itrash .. "]"
  142. -- Locked diamond chest.
  143. if string.find(name, "locked") then
  144. local chest_name = meta:get_string("chest_name") or ""
  145. formspec = formspec .. "button[10,0;2,1;rename;Rename]" ..
  146. "field[8.25,0.45;2,0.75;name;;]" ..
  147. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  148. end
  149. -- Locked or unlocked mithril chest.
  150. elseif string.find(name, "mithril") then
  151. formspec = "size[14,11]" .. defgui ..
  152. "list[nodemeta:" .. spos .. ";main;0,1.3;14,5;]" ..
  153. "list[current_player;main;2,6.85;8,1;]" ..
  154. "list[current_player;main;2,8.08;8,3;8]" ..
  155. "listring[nodemeta:" .. spos .. ";main]" ..
  156. "listring[current_player;main]" ..
  157. "label[0,0;" .. desc .. "]" ..
  158. default.get_hotbar_bg(2, 6.85)
  159. -- Trash icon.
  160. .. "list[" .. ltrash .. ";" .. mtrash .. ";11,6.85;1,1;]" ..
  161. "image[11,6.85;1,1;" .. itrash .. "]"
  162. -- Locked mithril chest.
  163. if string.find(name, "locked") then
  164. local chest_name = meta:get_string("chest_name") or ""
  165. formspec = formspec .. "button[12,0;2,1;rename;Rename]" ..
  166. "field[10.25,0.45;2,0.75;name;;]" ..
  167. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  168. end
  169. -- Locked silver chest. (This chest is shareable.) Grandfather in old ironside chests.
  170. elseif (string.find(name, "silver") and string.find(name, "locked")) or sharecount > 0 then
  171. local chest_name = meta:get_string("chest_name") or ""
  172. formspec = "size[10,10]" .. defgui ..
  173. "list[nodemeta:" .. spos .. ";main;0,1.3;8,4;]" ..
  174. "list[current_player;main;0,5.85;8,1;]" ..
  175. "list[current_player;main;0,7.08;8,3;8]" ..
  176. "listring[nodemeta:" .. spos .. ";main]" ..
  177. "listring[current_player;main]" ..
  178. "label[0,0;" .. desc .. "]" ..
  179. default.get_hotbar_bg(0, 5.85) ..
  180. "button[6,0;2,1;rename;Rename]" ..
  181. "field[4.25,0.45;2,0.75;name;;]" ..
  182. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]" ..
  183. "button[8,1.2;2,1;doshare;Share Chest]" ..
  184. "button[8,2.2;2,1;unshare;Unshare]"
  185. -- Trash icon.
  186. .. "list[" .. ltrash .. ";" .. mtrash .. ";9,5.85;1,1;]" ..
  187. "image[9,5.85;1,1;" .. itrash .. "]"
  188. --formspec = formspec .. "textlist[8,1.26;1.70,3;sharelist;Item ##1,Item ##2,Item ##3]"
  189. -- Locked/unlocked copper, iron, or silver chests.
  190. elseif string.find(name, "iron") or
  191. string.find(name, "copper") or
  192. string.find(name, "silver") then
  193. local locked = string.find(name, "locked")
  194. if locked then
  195. formspec = "size[9,10]"
  196. else
  197. formspec = "size[8,10]"
  198. end
  199. formspec = formspec .. defgui ..
  200. "list[nodemeta:" .. spos .. ";main;0,1.3;8,4;]" ..
  201. "list[current_player;main;0,5.85;8,1;]" ..
  202. "list[current_player;main;0,7.08;8,3;8]" ..
  203. "listring[nodemeta:" .. spos .. ";main]" ..
  204. "listring[current_player;main]" ..
  205. "label[0,0;" .. desc .. "]" ..
  206. default.get_hotbar_bg(0, 5.85)
  207. -- Locked copper or iron chest.
  208. -- (If chest was locked silver, then another if-statement already handled it.)
  209. -- Iron locked chests with existing shares are grandfathered in.
  210. if locked then
  211. local chest_name = meta:get_string("chest_name") or ""
  212. formspec = formspec .. "button[6,0;2,1;rename;Rename]" ..
  213. "field[4.25,0.45;2,0.75;name;;]" ..
  214. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  215. -- Trash icon.
  216. .. "list[" .. ltrash .. ";" .. mtrash .. ";8,1.3;1,1;]" ..
  217. "image[8,1.3;1,1;" .. itrash .. "]"
  218. else
  219. -- Trash icon. This also applies to the unlocked silver chest!
  220. formspec = formspec
  221. .. "list[" .. ltrash .. ";" .. mtrash .. ";7,0;1,1;]" ..
  222. "image[7,0;1,1;" .. itrash .. "]"
  223. end
  224. -- Default chest or woodchest (old version). We MUST preserve the formspec for the old chest version!
  225. elseif inv:get_size("main") == 8*4 and (name:find("woodchest") or name:find("^chests:chest_")) then
  226. formspec = "size[8,9]" .. defgui ..
  227. "list[nodemeta:" .. spos .. ";main;0,0.3;8,4;]" ..
  228. "list[current_player;main;0,4.85;8,1;]" ..
  229. "list[current_player;main;0,6.08;8,3;8]" ..
  230. "listring[nodemeta:" .. spos .. ";main]" ..
  231. "listring[current_player;main]" ..
  232. default.get_hotbar_bg(0, 4.85)
  233. -- Locked/unlocked non-metalic chest (new version). Should have 8*3 size inventory.
  234. else
  235. formspec = "size[9,8]" .. defgui ..
  236. "list[nodemeta:" .. spos .. ";main;0,0.3;8,3;]" ..
  237. "list[current_player;main;0,3.85;8,1;]" ..
  238. "list[current_player;main;0,5.08;8,3;8]" ..
  239. "listring[nodemeta:" .. spos .. ";main]" ..
  240. "listring[current_player;main]" ..
  241. default.get_hotbar_bg(0, 3.85)
  242. -- Trash icon.
  243. .. "list[" .. ltrash .. ";" .. mtrash .. ";8,0.3;1,1;]" ..
  244. "image[8,0.3;1,1;" .. itrash .. "]"
  245. end
  246. return formspec
  247. end
  248. local function get_chest_name(meta)
  249. local cname = meta:get_string("chest_name") or ""
  250. if cname == "" then
  251. return "this chest"
  252. else
  253. return "<" .. cname .. ">"
  254. end
  255. end
  256. local function add_share_name(meta, name, pname)
  257. if name == "" then return end -- Failsafe.
  258. name = rename.grn(name)
  259. local share_names, share_count = get_share_names(meta)
  260. if not share_names[name] then
  261. minetest.chat_send_player(pname, "# Server: Player <" .. rename.gpn(name) .. "> will now have access to " .. get_chest_name(meta) .. ".")
  262. end
  263. share_names[name] = 1
  264. local str = minetest.write_json(share_names)
  265. meta:set_string("share_names", str)
  266. end
  267. local function del_share_name(meta, name, pname)
  268. if name == "" then return end -- Failsafe.
  269. name = rename.grn(name)
  270. local share_names, share_count = get_share_names(meta)
  271. if share_names[name] then
  272. minetest.chat_send_player(pname, "# Server: Player <" .. rename.gpn(name) .. "> can no longer access " .. get_chest_name(meta) .. ".")
  273. end
  274. share_names[name] = nil
  275. local str = minetest.write_json(share_names) -- Returns nil for empty table?
  276. meta:set_string("share_names", str)
  277. end
  278. -- Generate a share formspec. We need the chest metadata.
  279. chest_api.get_share_formspec = function(pos, meta)
  280. local node = minetest.get_node(pos)
  281. local nn = node.name
  282. local desc = minetest.reg_ns_nodes[nn].description
  283. local cname = meta:get_string("chest_name") or ""
  284. local formspec
  285. local defgui = default.gui_bg ..
  286. default.gui_bg_img ..
  287. default.gui_slots
  288. formspec = "size[8,5]" .. defgui ..
  289. "label[0,0;" .. minetest.formspec_escape(utility.get_short_desc(desc)) .. "]" ..
  290. "label[0,0.35;Label: <" .. minetest.formspec_escape(cname) .. ">]" ..
  291. "button[6,0;2,1;unshare;Unshare]" ..
  292. "button_exit[6,4;2,1;quit;Close]" ..
  293. "button[2.5,2.14;2,1.02;addname;Add Name]" ..
  294. "button[2.5,3.14;2,1.02;delname;Remove Name]" ..
  295. "label[0,1.71;Add or remove access grants:]" ..
  296. "field[0.27,2.46;2.5,1;addname_field;;]" ..
  297. "field[0.27,3.46;2.5,1;delname_field;;]" ..
  298. "label[0,4;Tip: any locked chest can be shared with a key.]"
  299. formspec = formspec ..
  300. "textlist[5,1;2.8,2.9;sharelist;"
  301. local share_names, share_count = get_share_names(meta)
  302. for k, v in pairs(share_names) do
  303. formspec = formspec .. minetest.formspec_escape(rename.gpn(k)) .. ","
  304. end
  305. formspec = string.gsub(formspec, ",$", "") -- Remove trailing comma.
  306. formspec = formspec .. "]"
  307. return formspec
  308. end
  309. -- This function is only used to check if a player is allowed to take/move/put
  310. -- from a chest inventory, or is allowed to open the formspec.
  311. local function has_locked_chest_privilege(pos, name, meta, player)
  312. if minetest.check_player_privs(player, "protection_bypass") then
  313. return true
  314. end
  315. if player:get_player_name() == meta:get_string("owner") then
  316. -- Owner can access the node to any time
  317. return true
  318. end
  319. -- Locked silver chests have sharing functionality. Remember to grandfather in old shared ironside chests.
  320. if name:find("iron") or name:find("silver") then
  321. local share_names, share_count = get_share_names(meta)
  322. if (string.find(name, "silver") and string.find(name, "locked")) or share_count > 0 then
  323. if share_names[player:get_player_name()] then
  324. return true
  325. end
  326. end
  327. end
  328. -- Is player wielding the right key?
  329. local item = player:get_wielded_item()
  330. if item:get_name() == "key:key" or item:get_name() == "key:chain" then
  331. local key_meta = item:get_meta()
  332. if key_meta:get_string("secret") == "" then
  333. local key_oldmeta = item:get_metadata()
  334. if key_oldmeta == "" or not minetest.parse_json(key_oldmeta) then
  335. return false
  336. end
  337. key_meta:set_string("secret", minetest.parse_json(key_oldmeta).secret)
  338. item:set_metadata("")
  339. end
  340. return meta:get_string("key_lock_secret") == key_meta:get_string("secret")
  341. end
  342. -- Is chest open?
  343. do
  344. local node = minetest.get_node(pos)
  345. if string.find(node.name, "_open$") then
  346. -- Player must be near enough to the chest.
  347. if vector.distance(pos, player:get_pos()) < 6 then
  348. return true
  349. end
  350. end
  351. end
  352. return false
  353. end
  354. local function chest_lid_obstructed(pos)
  355. local above = { x = pos.x, y = pos.y + 1, z = pos.z }
  356. local def = minetest.reg_ns_nodes[minetest.get_node(above).name]
  357. if not def then
  358. return true
  359. end
  360. -- allow ladders, signs, wallmounted things and torches to not obstruct
  361. if def.drawtype == "airlike" or
  362. def.drawtype == "signlike" or
  363. def.drawtype == "torchlike" or
  364. ((def.drawtype == "nodebox" or def.drawtype == "mesh") and def.paramtype2 == "wallmounted") then
  365. return false
  366. end
  367. return true
  368. end
  369. chest_api.open_chests = chest_api.open_chests or {}
  370. local open_chests = chest_api.open_chests
  371. local function open_chest(def, pos, node, clicker)
  372. -- Player name.
  373. local pname = clicker:get_player_name()
  374. -- Chest basename.
  375. local name = def._chest_basename
  376. -- Delay before opening chest formspec.
  377. local admin = (gdac.player_is_admin(pname) and gdac_invis.is_invisible(pname))
  378. local open_delay = (not admin and 0.2 or 0)
  379. -- Don't play sound or open chest, if opener is admin and not invisible.
  380. if not admin then
  381. local meta = minetest.get_meta(pos)
  382. local last_oiled = meta:get_int("oiled_time")
  383. if (os.time() - last_oiled) > math.random(0, 60*60*24*30) then
  384. -- Play sound, open chest.
  385. ambiance.sound_play(def.sound_open, pos, 0.5, 20)
  386. else
  387. -- Oiled chests open faster.
  388. open_delay = 0
  389. end
  390. if not chest_lid_obstructed(pos) then
  391. minetest.swap_node(pos,
  392. { name = name .. "_open",
  393. param2 = node.param2 })
  394. end
  395. end
  396. minetest.after(open_delay, minetest.show_formspec,
  397. clicker:get_player_name(),
  398. "default:chest",
  399. chest_api.get_chest_formspec(name, def.description, pos))
  400. open_chests[pname] = {
  401. pos = pos,
  402. sound = def.sound_close,
  403. swap = name .. "_closed",
  404. orig = name .. "_open",
  405. }
  406. end
  407. local function close_chest(pn, pos, node, swap, sound)
  408. -- Don't play sound or open chest, if opener is admin and not invisible.
  409. local admin = (gdac.player_is_admin(pn) and gdac_invis.is_invisible(pn))
  410. open_chests[pn] = nil
  411. -- Skip sorting, sound, and chest-closing, if player is invisible admin.
  412. if admin then
  413. return
  414. end
  415. for k, v in pairs(open_chests) do
  416. if vector.equals(v.pos, pos) then
  417. return -- This checks if someone else has chest open.
  418. end
  419. end
  420. -- Play sound, close chest.
  421. minetest.after(0.2, minetest.swap_node, pos, {
  422. name = swap,
  423. param2 = node.param2
  424. })
  425. local meta = minetest.get_meta(pos)
  426. local last_oiled = meta:get_int("oiled_time")
  427. if (os.time() - last_oiled) > math.random(0, 60*60*24*30) then
  428. ambiance.sound_play(sound, pos, 0.5, 20)
  429. end
  430. -- Mithril chests auto-sort when closed.
  431. -- Thus they are always already sorted when opened.
  432. if string.find(swap, "mithril") then
  433. chest_api.sort_chest(pos)
  434. end
  435. end
  436. function chest_api.on_leaveplayer(player, timeout)
  437. if not player or not player:is_player() then return end
  438. local pn = player:get_player_name()
  439. if open_chests[pn] then
  440. local pos = open_chests[pn].pos
  441. local node = minetest.get_node(pos)
  442. local sound = open_chests[pn].sound
  443. local swap = open_chests[pn].swap
  444. close_chest(pn, pos, node, swap, sound)
  445. end
  446. end
  447. function chest_api.on_dieplayer(player)
  448. if not player or not player:is_player() then return end
  449. local pn = player:get_player_name()
  450. if open_chests[pn] then
  451. local pos = open_chests[pn].pos
  452. local node = minetest.get_node(pos)
  453. local sound = open_chests[pn].sound
  454. local swap = open_chests[pn].swap
  455. close_chest(pn, pos, node, swap, sound)
  456. end
  457. end
  458. function chest_api.on_player_receive_fields(player, formname, fields)
  459. -- This function is valid for these callbacks only.
  460. if formname ~= "default:chest" and formname ~= "default:chest_share" then
  461. return -- Continue handling callbacks.
  462. end
  463. if not player then
  464. return true
  465. end
  466. local pn = player:get_player_name()
  467. -- Anticheat check.
  468. if not open_chests[pn] then
  469. minetest.chat_send_player(pn, "# Server: No chest opened.")
  470. return true -- Abort.
  471. end
  472. local pos = open_chests[pn].pos
  473. local node = minetest.get_node(pos)
  474. local nn = node.name
  475. local sound = open_chests[pn].sound
  476. local swap = open_chests[pn].swap
  477. local orig = open_chests[pn].orig
  478. local meta = minetest.get_meta(pos)
  479. local owner = meta:get_string("owner") or "" -- Only locked chests have owners.
  480. -- Failsafe.
  481. if nn ~= orig and nn ~= swap then
  482. minetest.chat_send_player(pn, "# Server: Error: 0xDEADBEEF (bad node)!")
  483. return true -- Abort.
  484. end
  485. if fields.rename then
  486. -- Anitcheat check.
  487. if (string.find(nn, "copper") or string.find(nn, "diamond") or
  488. string.find(nn, "iron") or
  489. string.find(nn, "silver") or
  490. string.find(nn, "gold") or
  491. string.find(nn, "mithril")) and string.find(nn, "locked") then
  492. if owner == pn or gdac.player_is_admin(pn) then
  493. minetest.chat_send_player(pn, "# Server: Chest name updated.")
  494. local new_name = (fields.name or "")
  495. new_name = new_name:trim()
  496. meta:set_string("chest_name", new_name)
  497. local owner = meta:get_string("owner") or ""
  498. local dname = rename.gpn(owner)
  499. local cname = meta:get_string("chest_name") or ""
  500. if cname == "" then
  501. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  502. else
  503. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  504. end
  505. local desc = minetest.reg_ns_nodes[nn].description
  506. minetest.show_formspec(pn, "default:chest", chest_api.get_chest_formspec(nn, desc, pos))
  507. else
  508. minetest.chat_send_player(pn, "# Server: You cannot relabel this chest.")
  509. end
  510. else
  511. minetest.chat_send_player(pn, "# Server: This chest does not have labeling functionality.")
  512. end
  513. end
  514. if fields.doshare then -- Button on main formspec.
  515. -- Permit grandfathering of old shared ironside chests.
  516. local shares, sharecount = get_share_names(meta)
  517. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  518. if owner == pn or gdac.player_is_admin(pn) then
  519. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  520. else
  521. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  522. end
  523. else
  524. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  525. end
  526. end
  527. if fields.unshare then -- This button is on the sharing and main formspecs.
  528. -- Permit grandfathering of old shared ironside chests.
  529. local shares, sharecount = get_share_names(meta)
  530. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  531. if owner == pn or gdac.player_is_admin(pn) then
  532. meta:set_string("share_names", nil)
  533. minetest.chat_send_player(pn, "# Server: All share grants revoked for " .. get_chest_name(meta) .. ".")
  534. -- Refresh sharing formspec only if already being displayed.
  535. -- The main formspec doesn't need updating.
  536. if formname == "default:chest_share" then
  537. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  538. end
  539. else
  540. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  541. end
  542. else
  543. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  544. end
  545. end
  546. if fields.addname and fields.addname_field ~= "" then -- Sharing formspec only.
  547. -- Permit grandfathering of old shared ironside chests.
  548. local shares, sharecount = get_share_names(meta)
  549. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  550. if owner == pn or gdac.player_is_admin(pn) then
  551. add_share_name(meta, fields.addname_field, pn)
  552. -- The sharing formspec is being displayed. Refresh it.
  553. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  554. else
  555. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  556. end
  557. else
  558. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  559. end
  560. end
  561. if fields.delname then -- Sharing formspec only.
  562. -- Permit grandfathering of old shared ironside chests.
  563. local shares, sharecount = get_share_names(meta)
  564. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  565. if owner == pn or gdac.player_is_admin(pn) then
  566. del_share_name(meta, fields.delname_field, pn)
  567. -- The sharing formspec is being displayed. Refresh it.
  568. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  569. else
  570. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  571. end
  572. else
  573. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  574. end
  575. end
  576. if not fields.quit then
  577. return true
  578. end
  579. -- Close chest.
  580. close_chest(pn, pos, node, swap, sound)
  581. return true
  582. end
  583. function chest_api.protected_on_construct(pos)
  584. local meta = minetest.get_meta(pos)
  585. meta:set_string("infotext", "Locked Chest")
  586. meta:set_string("owner", "")
  587. local inv = meta:get_inventory()
  588. local name = minetest.get_node(pos).name
  589. if string.find(name, "gold") then
  590. inv:set_size("main", 12*4)
  591. elseif string.find(name, "diamond") then
  592. inv:set_size("main", 12*5)
  593. elseif string.find(name, "mithril") then
  594. inv:set_size("main", 14*5)
  595. elseif name:find("woodchest") or name:find("^chests:chest_") then
  596. inv:set_size("main", 8*3)
  597. else
  598. inv:set_size("main", 8*4)
  599. end
  600. end
  601. function chest_api.protected_after_place_node(pos, placer)
  602. local meta = minetest.get_meta(pos)
  603. local owner = placer:get_player_name() or ""
  604. local dname = rename.gpn(owner)
  605. meta:set_string("owner", owner)
  606. meta:set_string("rename", dname)
  607. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  608. meta:set_string("formspec", nil)
  609. end
  610. function chest_api.protected_can_dig(pos, player)
  611. local meta = minetest.get_meta(pos)
  612. local owner = meta:get_string("owner") or ""
  613. local pname = player:get_player_name()
  614. local inv = meta:get_inventory()
  615. -- Only chest owners can dig shared locked chests.
  616. -- If chests is owned by the server, or by no one, then anyone can dig.
  617. return inv:is_empty("main") and (owner == pname or owner == "" or owner == "server")
  618. end
  619. function chest_api.protected_allow_metadata_inventory_move(
  620. pos, from_list, from_index, to_list, to_index, count, player)
  621. local meta = minetest.get_meta(pos)
  622. local name = minetest.get_node(pos).name
  623. if not has_locked_chest_privilege(pos, name, meta, player) then
  624. return 0
  625. end
  626. return count
  627. end
  628. function chest_api.protected_allow_metadata_inventory_put(
  629. pos, listname, index, stack, player)
  630. local meta = minetest.get_meta(pos)
  631. local name = minetest.get_node(pos).name
  632. if not has_locked_chest_privilege(pos, name, meta, player) then
  633. return 0
  634. end
  635. return stack:get_count()
  636. end
  637. function chest_api.protected_allow_metadata_inventory_take(
  638. pos, listname, index, stack, player)
  639. local meta = minetest.get_meta(pos)
  640. local name = minetest.get_node(pos).name
  641. if not has_locked_chest_privilege(pos, name, meta, player) then
  642. return 0
  643. end
  644. return stack:get_count()
  645. end
  646. function chest_api.protected_on_rightclick(pos, node, clicker)
  647. local meta = minetest.get_meta(pos)
  648. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  649. local name = def._chest_basename
  650. if not has_locked_chest_privilege(pos, name, meta, clicker) then
  651. ambiance.sound_play("default_chest_locked", pos, 1.0, 20)
  652. return
  653. end
  654. local meta = minetest.get_meta(pos)
  655. local owner = meta:get_string("owner") or ""
  656. local dname = rename.gpn(owner)
  657. local cname = meta:get_string("chest_name") or ""
  658. if cname == "" then
  659. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  660. else
  661. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  662. end
  663. -- Upgrade inventory size.
  664. if string.find(name, "gold") then
  665. local inv = meta:get_inventory()
  666. local inv_sz = inv:get_size("main")
  667. if inv_sz ~= 12*4 then
  668. inv:set_size("main", 12*4)
  669. end
  670. elseif string.find(name, "mithril") then
  671. local inv = meta:get_inventory()
  672. local inv_sz = inv:get_size("main")
  673. if inv_sz ~= 14*5 then
  674. inv:set_size("main", 14*5)
  675. end
  676. end
  677. -- Do NOT update inventory sides for old woodchests/default chests.
  678. -- They are relied on by too many players. Only new chests will have smaller inventories.
  679. open_chest(def, pos, node, clicker)
  680. end
  681. function chest_api.protected_on_key_use(pos, player)
  682. local node = minetest.get_node(pos)
  683. local def = minetest.reg_ns_nodes[node.name]
  684. local name = def._chest_basename
  685. -- Failsafe.
  686. if node.name ~= name .. "_closed" then return end
  687. if not player or not player:is_player() then return end
  688. local meta = minetest.get_meta(pos)
  689. local secret = meta:get_string("key_lock_secret")
  690. local itemstack = player:get_wielded_item()
  691. local key_meta = itemstack:get_meta()
  692. if key_meta:get_string("secret") == "" then
  693. local key_oldmeta = itemstack:get_metadata()
  694. if key_oldmeta == "" or not minetest.parse_json(key_oldmeta) then
  695. return false
  696. end
  697. key_meta:set_string("secret", minetest.parse_json(key_oldmeta).secret)
  698. itemstack:set_metadata("")
  699. end
  700. if secret ~= key_meta:get_string("secret") then
  701. minetest.chat_send_player(player:get_player_name(), "# Server: Key does not fit lock!")
  702. return
  703. end
  704. local owner = meta:get_string("owner") or ""
  705. local dname = rename.gpn(owner)
  706. local cname = meta:get_string("chest_name") or ""
  707. if cname == "" then
  708. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  709. else
  710. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  711. end
  712. -- Upgrade inventory size.
  713. if string.find(name, "gold") then
  714. local inv = meta:get_inventory()
  715. local inv_sz = inv:get_size("main")
  716. if inv_sz ~= 12*4 then
  717. inv:set_size("main", 12*4)
  718. end
  719. elseif string.find(name, "mithril") then
  720. local inv = meta:get_inventory()
  721. local inv_sz = inv:get_size("main")
  722. if inv_sz ~= 14*5 then
  723. inv:set_size("main", 14*5)
  724. end
  725. end
  726. -- Do NOT update inventory sides for old woodchests/default chests.
  727. -- They are relied on by too many players. Only new chests will have smaller inventories.
  728. open_chest(def, pos, node, player)
  729. end
  730. function chest_api.protected_on_skeleton_key_use(pos, player, newsecret)
  731. local meta = minetest.get_meta(pos)
  732. local owner = meta:get_string("owner")
  733. local pname = player:get_player_name()
  734. -- verify placer is owner of lockable chest
  735. if not gdac.player_is_admin(pname) then
  736. if owner ~= pname then
  737. minetest.record_protection_violation(pos, pname)
  738. minetest.chat_send_player(pname, "# Server: You do not own this chest.")
  739. return nil
  740. end
  741. end
  742. local secret = meta:get_string("key_lock_secret")
  743. if secret == "" then
  744. secret = newsecret
  745. meta:set_string("key_lock_secret", secret)
  746. end
  747. return secret, "a locked chest", owner
  748. end
  749. function chest_api.protected_on_rename_check(pos)
  750. local meta = minetest.get_meta(pos)
  751. local owner = meta:get_string("owner")
  752. -- Nobody placed this block.
  753. if owner == "" then
  754. return
  755. end
  756. local dname = rename.gpn(owner)
  757. meta:set_string("rename", dname)
  758. local label = meta:get_string("chest_name") or ""
  759. if label == "" then
  760. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  761. else
  762. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. label .. ">")
  763. end
  764. end
  765. function chest_api.public_on_construct(pos)
  766. local meta = minetest.get_meta(pos)
  767. local name = minetest.get_node(pos).name
  768. meta:set_string("infotext", "Unlocked Chest")
  769. local inv = meta:get_inventory()
  770. if string.find(name, "gold") then
  771. inv:set_size("main", 12*4)
  772. elseif string.find(name, "diamond") then
  773. inv:set_size("main", 12*5)
  774. elseif string.find(name, "mithril") then
  775. inv:set_size("main", 14*5)
  776. elseif name:find("woodchest") or name:find("^chests:chest_") then
  777. inv:set_size("main", 8*3)
  778. else
  779. inv:set_size("main", 8*4)
  780. end
  781. end
  782. function chest_api.public_can_dig(pos,player)
  783. local meta = minetest.get_meta(pos);
  784. local inv = meta:get_inventory()
  785. return inv:is_empty("main")
  786. end
  787. function chest_api.public_on_rightclick(pos, node, clicker)
  788. local meta = minetest.get_meta(pos)
  789. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  790. local name = def._chest_basename
  791. meta:set_string("infotext", "Unlocked Chest")
  792. meta:set_string("formspec", nil)
  793. -- Upgrade inventory size.
  794. if string.find(name, "gold") then
  795. local inv = meta:get_inventory()
  796. local inv_sz = inv:get_size("main")
  797. if inv_sz ~= 12*4 then
  798. inv:set_size("main", 12*4)
  799. end
  800. elseif string.find(name, "mithril") then
  801. local inv = meta:get_inventory()
  802. local inv_sz = inv:get_size("main")
  803. if inv_sz ~= 14*5 then
  804. inv:set_size("main", 14*5)
  805. end
  806. end
  807. -- Do NOT update inventory sides for old woodchests/default chests.
  808. -- They are relied on by too many players. Only new chests will have smaller inventories.
  809. open_chest(def, pos, node, clicker)
  810. end
  811. function chest_api.on_receive_fields(pos, formname, fields, sender)
  812. if not sender or not sender:is_player() then return end
  813. -- convert chest by removing the formspec and renaming the inv
  814. -- the order here is chosen to minimize the chance of two conversion
  815. -- attempts running at the same time from racing.
  816. -- after this function runs, it destroys the formspec in the metadata
  817. -- and will never be called again for this chest.
  818. local meta = minetest.get_meta(pos)
  819. meta:set_string("formspec", nil)
  820. -- We do not attempt to switch the inventory.
  821. --local inv = meta:get_inventory()
  822. --local list = inv:get_list("main")
  823. --inv:set_list("main", nil)
  824. --inv:set_size("default:chest", 8*4)
  825. --inv:set_list("default:chest", list)
  826. end
  827. function chest_api.on_metadata_inventory_move(
  828. pos, from_list, from_index, to_list, to_index, count, player)
  829. minetest.log("action", player:get_player_name() ..
  830. " moves stuff in chest at " .. minetest.pos_to_string(pos))
  831. if string.find(minetest.get_node(pos).name, "locked") then
  832. minetest.after(0, function() chest_api.update_vending(pos) end)
  833. end
  834. end
  835. function chest_api.on_metadata_inventory_put(pos, listname, index, stack, player)
  836. minetest.log("action", player:get_player_name() ..
  837. " moves " .. stack:get_name() ..
  838. " to chest at " .. minetest.pos_to_string(pos))
  839. if string.find(minetest.get_node(pos).name, "locked") then
  840. minetest.after(0, function() chest_api.update_vending(pos) end)
  841. end
  842. end
  843. function chest_api.on_metadata_inventory_take(pos, listname, index, stack, player)
  844. minetest.log("action", player:get_player_name() ..
  845. " takes " .. stack:get_name() ..
  846. " from chest at " .. minetest.pos_to_string(pos))
  847. if string.find(minetest.get_node(pos).name, "locked") then
  848. minetest.after(0, function() chest_api.update_vending(pos) end)
  849. end
  850. end
  851. function chest_api.on_blast(pos)
  852. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  853. local name = def._chest_basename
  854. local drops = {}
  855. default.get_inventory_drops(pos, "main", drops)
  856. drops[#drops+1] = name .. "_closed"
  857. minetest.remove_node(pos)
  858. return drops
  859. end