easyvend.lua 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327
  1. if not minetest.global_exists("easyvend") then easyvend = {} end
  2. easyvend.modpath = minetest.get_modpath("easyvend")
  3. -- Localize for performance.
  4. local math_floor = math.floor
  5. local traversable_node_types = easyvend.traversable_node_types
  6. local registered_chests = easyvend.registered_chests
  7. local currency_types = {}
  8. local initial_currency = 1
  9. for k, v in ipairs(currency.note_names) do
  10. table.insert(currency_types, v)
  11. end
  12. -- Maximum price that can be configured on a vendor or depositor.
  13. local maxcost = 1000000
  14. local slots_max = 30
  15. -- Allow for other mods to register custom chests
  16. easyvend.register_chest = function(node_name, inv_list, meta_owner)
  17. easyvend.registered_chests[node_name] = { inv_list = inv_list, meta_owner = meta_owner }
  18. easyvend.traversable_node_types[node_name] = true
  19. end
  20. -- Partly a wrapper around contains_item, but does special treatment if the item
  21. -- is a tool. Basically checks whether the items exist in the supplied inventory
  22. -- list. If check_wear is true, only counts items without wear.
  23. easyvend.check_and_get_items = function(inventory, listname, itemtable, check_wear)
  24. local itemstring = itemtable.name
  25. local minimum = itemtable.count
  26. if check_wear == nil then check_wear = false end
  27. local get_items = {}
  28. -- Tool workaround
  29. if minetest.registered_tools[itemstring] ~= nil then
  30. local count = 0
  31. for i=1,inventory:get_size(listname) do
  32. local stack = inventory:get_stack(listname, i)
  33. if stack:get_name() == itemstring then
  34. if not check_wear or stack:get_wear() == 0 then
  35. count = count + 1
  36. table.insert(get_items, {id=i, item=stack})
  37. if count >= minimum then
  38. return true, get_items
  39. end
  40. end
  41. end
  42. end
  43. return false
  44. else
  45. -- Normal Minetest check
  46. return inventory:contains_item(listname, ItemStack(itemtable))
  47. end
  48. end
  49. easyvend.free_slots = function(inv, listname)
  50. local size = inv:get_size(listname)
  51. local free = 0
  52. for i=1,size do
  53. local stack = inv:get_stack(listname, i)
  54. if stack:is_empty() then
  55. free = free + 1
  56. end
  57. end
  58. return free
  59. end
  60. easyvend.buysell = function(nodename)
  61. local buysell = nil
  62. if ( nodename == "easyvend:depositor" or nodename == "easyvend:depositor_on" ) then
  63. buysell = "buy"
  64. elseif ( nodename == "easyvend:vendor" or nodename == "easyvend:vendor_on" ) then
  65. buysell = "sell"
  66. end
  67. return buysell
  68. end
  69. easyvend.is_active = function(nodename)
  70. if ( nodename == "easyvend:depositor_on" or nodename == "easyvend:vendor_on" ) then
  71. return true
  72. elseif ( nodename == "easyvend:depositor" or nodename == "easyvend:vendor" ) then
  73. return false
  74. else
  75. return nil
  76. end
  77. end
  78. easyvend.set_formspec = function(pos)
  79. local meta = minetest.get_meta(pos)
  80. local node = minetest.get_node(pos)
  81. local description = utility.get_short_desc(minetest.reg_ns_nodes[node.name].description);
  82. local number = meta:get_int("number")
  83. local cost = meta:get_int("cost")
  84. local itemname = meta:get_string("itemname")
  85. local bg = ""
  86. local configmode = meta:get_int("configmode") == 1
  87. if minetest.get_modpath("default") then
  88. bg = default.formspec.get_form_colors() .. default.formspec.get_form_image() .. default.formspec.get_slot_colors()
  89. end
  90. local numbertext, costtext, buysellbuttontext
  91. local itemcounttooltip = "Item count"
  92. local buysell = easyvend.buysell(node.name)
  93. if buysell == "sell" then
  94. numbertext = "Offered Item"
  95. costtext = "Price"
  96. buysellbuttontext = "Buy"
  97. elseif buysell == "buy" then
  98. numbertext = "Requested Item"
  99. costtext = "Payment"
  100. buysellbuttontext = "Sell"
  101. else
  102. return
  103. end
  104. local status = meta:get_string("status")
  105. if status == "" then status = "Unknown." end
  106. local message = meta:get_string("message")
  107. if message == "" then message = "No message." end
  108. local status_image
  109. if node.name == "easyvend:vendor_on" or node.name == "easyvend:depositor_on" then
  110. status_image = "easyvend_status_on.png"
  111. else
  112. status_image = "easyvend_status_off.png"
  113. end
  114. -- TODO: Expose number of items in stock
  115. local formspec = "size[8,7.3;]"
  116. .. bg
  117. .."label[3,-0.2;" .. minetest.formspec_escape(description) .. "]"
  118. .."image[7.5,0.2;0.5,1;" .. status_image .. "]"
  119. .."textarea[2.8,0.2;4.5,1.5;;Status: " .. minetest.formspec_escape(status) .. ";]"
  120. .."textarea[2.8,1.3;4.5,1.5;;Message: " .. minetest.formspec_escape(message) .. ";]"
  121. .."label[0,-0.15;"..numbertext.."]"
  122. .."label[0,1.2;"..costtext.."]"
  123. .."list[current_player;main;0,3.5;8,4;]"
  124. local machine_currency = meta:get_string("machine_currency")
  125. if configmode then
  126. local wear = "false"
  127. if meta:get_int("wear") == 1 then wear = "true" end
  128. formspec = formspec
  129. .."image[7,1.5;1,1;gui_clu_grey.png;]"
  130. .."item_image_button[0,1.65;1,1;" .. machine_currency .. ";easyvend_currency_image;]"
  131. .."list[context;item;0,0.35;1,1;]"
  132. .."list[context;upgrade_slot;7,1.5;1,1;]"
  133. .."listring[current_player;main]"
  134. .."listring[context;item]"
  135. .."field[1.3,0.65;1.5,1;number;;" .. number .. "]"
  136. .."tooltip[number;"..itemcounttooltip.."]"
  137. .."field[1.3,1.95;1.5,1;cost;;" .. cost .. "]"
  138. .."tooltip[cost;"..itemcounttooltip.."]"
  139. .."button[6,2.8;2,0.5;save;Confirm]"
  140. .."tooltip[save;Confirm configuration and activate machine (only for owner)]"
  141. local weartext, weartooltip
  142. if buysell == "buy" then
  143. weartext = "Buy worn tools"
  144. weartooltip = "If disabled, only tools in perfect condition will be bought from sellers (only settable by owner)"
  145. else
  146. weartext = "Sell worn tools"
  147. weartooltip = "If disabled, only tools in perfect condition will be sold (only settable by owner)"
  148. end
  149. if minetest.registered_tools[itemname] ~= nil then
  150. formspec = formspec .."checkbox[2,2.4;wear;"..minetest.formspec_escape(weartext)..";"..wear.."]"
  151. .."tooltip[wear;"..minetest.formspec_escape(weartooltip).."]"
  152. end
  153. else
  154. formspec = formspec
  155. .."item_image_button[0,1.65;1,1;" .. machine_currency .. ";easyvend_currency_image;]"
  156. .."item_image_button[0,0.35;1,1;"..itemname..";item_image;]"
  157. .."label[1,1.85;×" .. cost .. "]"
  158. .."label[1,0.55;×" .. number .. "]"
  159. .."button[6,2.8;2,0.5;config;Configure]"
  160. if buysell == "sell" then
  161. formspec = formspec .. "tooltip[config;Configure offered items and price (only for owner)]"
  162. else
  163. formspec = formspec .. "tooltip[config;Configure requested items and payment (only for owner)]"
  164. end
  165. formspec = formspec .."button[0,2.8;2,0.5;buysell;"..buysellbuttontext.."]"
  166. if minetest.registered_tools[itemname] ~= nil then
  167. local weartext
  168. if meta:get_int("wear") == 0 then
  169. if buysell == "buy" then
  170. weartext = "Only intact tools are bought."
  171. else
  172. weartext = "Only intact tools are sold."
  173. end
  174. else
  175. if buysell == "sell" then
  176. weartext = "Warning: Might sell worn tools."
  177. else
  178. weartext = "Worn tools are bought, too."
  179. end
  180. end
  181. if weartext ~= nil then
  182. formspec = formspec .."textarea[2.3,2.6;3,1;;"..minetest.formspec_escape(weartext)..";]"
  183. end
  184. end
  185. end
  186. meta:set_string("formspec", formspec)
  187. end
  188. easyvend.machine_disable = function(pos, node, playername)
  189. if node.name == "easyvend:vendor_on" then
  190. easyvend.sound_disable(pos)
  191. minetest.swap_node(pos, {name="easyvend:vendor", param2 = node.param2})
  192. return true
  193. elseif node.name == "easyvend:depositor_on" then
  194. easyvend.sound_disable(pos)
  195. minetest.swap_node(pos, {name="easyvend:depositor", param2 = node.param2})
  196. return true
  197. else
  198. if playername ~= nil then
  199. easyvend.sound_error(playername)
  200. end
  201. return false
  202. end
  203. end
  204. easyvend.machine_enable = function(pos, node)
  205. if node.name == "easyvend:vendor" then
  206. easyvend.sound_setup(pos)
  207. minetest.swap_node(pos, {name="easyvend:vendor_on", param2 = node.param2})
  208. return true
  209. elseif node.name == "easyvend:depositor" then
  210. easyvend.sound_setup(pos)
  211. minetest.swap_node(pos, {name="easyvend:depositor_on", param2 = node.param2})
  212. return true
  213. else
  214. return false
  215. end
  216. end
  217. easyvend.upgrade_currency = function(pos, meta, old_currency, old_cost)
  218. if old_currency == "default:gold_ingot" then
  219. -- Upgrade gold to currency at 1 to 25. This is a fixed exchange rate.
  220. meta:set_string("machine_currency", "currency:minegeld_5")
  221. meta:set_int("cost", math_floor((old_cost * 25) / 5))
  222. return ("currency:minegeld_5"), math_floor((old_cost * 25) / 5)
  223. end
  224. return old_currency, old_cost
  225. end
  226. easyvend.machine_check = function(pos, node)
  227. local active = true
  228. local status = "Ready."
  229. local meta = minetest.get_meta(pos)
  230. local machine_owner = meta:get_string("owner")
  231. local itemname = meta:get_string("itemname")
  232. local number = meta:get_int("number")
  233. local check_wear = meta:get_int("wear") == 0
  234. local inv = meta:get_inventory()
  235. local itemstack = inv:get_stack("item", 1)
  236. local buysell = easyvend.buysell(node.name)
  237. if inv:get_size("upgrade_slot") == 0 then
  238. inv:set_size("upgrade_slot", 1)
  239. end
  240. local have_clu = inv:contains_item("upgrade_slot", "techcrafts:control_logic_unit")
  241. local machine_currency = meta:get_string("machine_currency")
  242. local cost = meta:get_int("cost")
  243. -- If the machine uses a depreciated currency, this will upgrade it using a fixed exchange rate.
  244. machine_currency, cost = easyvend.upgrade_currency(pos, meta, machine_currency, cost)
  245. local chest_pos_remove, chest_error_remove, chest_pos_add, chest_error_add
  246. if buysell == "sell" then
  247. -- Vending machine.
  248. chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, true)
  249. chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, false)
  250. else
  251. -- Depositing machine.
  252. chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, true)
  253. chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, false)
  254. end
  255. if chest_pos_remove and chest_pos_add then
  256. local rchest, rchestdef, rchest_meta, rchest_inv
  257. rchest = minetest.get_node(chest_pos_remove)
  258. rchestdef = registered_chests[rchest.name]
  259. rchest_meta = minetest.get_meta(chest_pos_remove)
  260. rchest_inv = rchest_meta:get_inventory()
  261. local checkstack, checkitem
  262. if buysell == "buy" then
  263. checkitem = machine_currency
  264. else
  265. checkitem = itemname
  266. end
  267. local stock = 0
  268. -- Count stock
  269. -- FIXME: Ignore tools with bad wear level
  270. for i=1,rchest_inv:get_size(rchestdef.inv_list) do
  271. checkstack = rchest_inv:get_stack(rchestdef.inv_list, i)
  272. if checkstack:get_name() == checkitem then
  273. stock = stock + checkstack:get_count()
  274. end
  275. end
  276. meta:set_int("stock", stock)
  277. if not itemstack:is_empty() then
  278. local number_stack_max = itemstack:get_stack_max()
  279. local maxnumber = number_stack_max * slots_max
  280. if not(number >= 1 and number <= maxnumber and cost >= 1 and cost <= maxcost) then
  281. active = false
  282. if buysell == "sell" then
  283. status = "Invalid item count or price."
  284. else
  285. status = "Invalid item count or payment."
  286. end
  287. end
  288. else
  289. active = false
  290. status = "Awaiting configuration by owner."
  291. end
  292. else
  293. active = false
  294. meta:set_int("stock", 0)
  295. if chest_error_remove == "no_chest" and chest_error_add == "no_chest" then
  296. status = "No storage; machine needs to be connected with a locked chest."
  297. elseif chest_error_remove == "not_owned" or chest_error_add == "not_owned" then
  298. status = "Storage can’t be accessed because it is owned by a different person!"
  299. elseif chest_error_remove == "no_stock" then
  300. if buysell == "sell" then
  301. status = "The vending machine has insufficient materials!"
  302. else
  303. status = "The depositing machine is out of money!"
  304. end
  305. elseif chest_error_add == "no_space" then
  306. status = "No room in the machine’s storage!"
  307. else
  308. status = "Unknown error!"
  309. end
  310. end
  311. if meta:get_int("configmode") == 1 then
  312. active = false
  313. status = "Awaiting configuration by owner."
  314. end
  315. if currency.is_currency(itemname) then
  316. status = "Cannot treat currency as a directly saleable item!"
  317. active = false
  318. end
  319. -- If the currency type is depreciated, then this warning overrides all others.
  320. if not currency.is_currency(machine_currency) then
  321. status = "Machine uses a depreciated currency standard!"
  322. active = false
  323. end
  324. meta:set_string("status", status)
  325. itemname=itemstack:get_name()
  326. meta:set_string("itemname", itemname)
  327. -- Inform remote market system of any changes.
  328. depositor.update_info(pos, machine_owner, itemname, number, cost, machine_currency, buysell, active)
  329. local change
  330. if node.name == "easyvend:vendor" or node.name == "easyvend:depositor" then
  331. if active then change = easyvend.machine_enable(pos, node) end
  332. elseif node.name == "easyvend:vendor_on" or node.name == "easyvend:depositor_on" then
  333. if not active then
  334. change = easyvend.machine_disable(pos, node)
  335. local machine_type = "vendor"
  336. if node.name:find("depositor") then
  337. machine_type = "depositor"
  338. end
  339. if have_clu then
  340. -- Don't send email if shop was disabled because it entered config mode.
  341. if meta:get_int("configmode") ~= 1 then
  342. easyvend.record_disable({
  343. type = "disable",
  344. owner = machine_owner,
  345. machine_type = machine_type,
  346. item = itemname,
  347. pos = vector.copy(pos),
  348. })
  349. end
  350. end
  351. end
  352. end
  353. local current_node = minetest.get_node(pos)
  354. meta:set_string("infotext", easyvend.make_infotext(pos, current_node.name, machine_owner, cost, number, itemname))
  355. easyvend.set_formspec(pos)
  356. return change
  357. end
  358. easyvend.on_receive_fields_config = function(pos, formname, fields, sender)
  359. local node = minetest.get_node(pos)
  360. local meta = minetest.get_meta(pos)
  361. local inv_self = meta:get_inventory()
  362. local itemstack = inv_self:get_stack("item",1)
  363. local buysell = easyvend.buysell(node.name)
  364. if fields.config then
  365. meta:set_int("configmode", 1)
  366. local was_active = easyvend.is_active(node.name)
  367. if was_active then
  368. meta:set_string("message", "Configuration mode activated; machine disabled.")
  369. else
  370. meta:set_string("message", "Configuration mode activated.")
  371. end
  372. easyvend.machine_check(pos, node)
  373. return
  374. end
  375. if not fields.save then
  376. return
  377. end
  378. local number = fields.number
  379. local cost = fields.cost
  380. number = tonumber(number)
  381. cost = tonumber(cost)
  382. local itemname=""
  383. local number_stack_max = 0
  384. if itemstack and not itemstack:is_empty() then
  385. itemname = itemstack:get_name()
  386. number_stack_max = itemstack:get_stack_max()
  387. end
  388. local oldnumber = meta:get_int("number")
  389. local oldcost = meta:get_int("cost")
  390. local maxnumber = number_stack_max * slots_max
  391. if ( itemstack == nil or itemstack:is_empty() ) then
  392. meta:set_string("status", "Awaiting configuration by owner.")
  393. meta:set_string("message", "No item specified.")
  394. easyvend.sound_error(sender:get_player_name())
  395. easyvend.set_formspec(pos)
  396. return
  397. elseif ( number == nil or number < 1 or number > maxnumber ) then
  398. if maxnumber > 1 then
  399. meta:set_string("message", string.format("Invalid item count; must be between 1 and %d!", maxnumber))
  400. else
  401. meta:set_string("message", "Invalid item count; must be exactly 1!")
  402. end
  403. meta:set_int("number", oldnumber)
  404. easyvend.sound_error(sender:get_player_name())
  405. easyvend.set_formspec(pos)
  406. return
  407. elseif ( cost == nil or cost < 1 or cost > maxcost ) then
  408. if maxcost > 1 then
  409. meta:set_string("message", string.format("Invalid cost; must be between 1 and %d!", maxcost))
  410. else
  411. meta:set_string("message", "Invalid cost; must be exactly 1!")
  412. end
  413. meta:set_int("cost", oldcost)
  414. easyvend.sound_error(sender:get_player_name())
  415. easyvend.set_formspec(pos)
  416. return
  417. end
  418. meta:set_int("number", number)
  419. meta:set_int("cost", cost)
  420. meta:set_string("itemname", itemname)
  421. meta:set_int("configmode", 0)
  422. meta:set_string("message", "Configuration successful.")
  423. local change = easyvend.machine_check(pos, node)
  424. if not change then
  425. if (node.name == "easyvend:vendor_on" or node.name == "easyvend:depositor_on") then
  426. easyvend.sound_setup(pos)
  427. else
  428. easyvend.sound_disable(pos)
  429. end
  430. end
  431. end
  432. easyvend.make_infotext = function(pos, nodename, owner, cost, number, itemstring)
  433. local dname = rename.gpn(owner)
  434. local d = ""
  435. if itemstring == nil or itemstring == "" or number == 0 or cost == 0 then
  436. if easyvend.buysell(nodename) == "sell" then
  437. d = string.format("Inactive Vending Machine (Owned by <%s>!)", dname)
  438. else
  439. d = string.format("Inactive Depositing Machine (Owned by <%s>!)", dname)
  440. end
  441. return d
  442. end
  443. local iname = minetest.registered_items[itemstring].description
  444. if iname == nil then iname = itemstring end
  445. iname = utility.get_short_desc(iname)
  446. local printitem, printcost
  447. if number == 1 then
  448. printitem = iname
  449. else
  450. printitem = string.format("%d×%s", number, iname)
  451. end
  452. local meta = minetest.get_meta(pos)
  453. local machine_currency = meta:get_string("machine_currency")
  454. if currency.is_currency(machine_currency) then
  455. printcost = currency.get_stack_value(machine_currency, cost) .. " Minegeld"
  456. else
  457. printcost = "Depreciated Currency!"
  458. end
  459. if nodename == "easyvend:vendor_on" then
  460. d = string.format("Vending Machine (Owned by <%s>!)\nSelling: %s\nPrice: %s", dname, printitem, printcost)
  461. elseif nodename == "easyvend:vendor" then
  462. d = string.format("Inactive Vending Machine (Owned by <%s>!)\nSelling: %s\nPrice: %s", dname, printitem, printcost)
  463. elseif nodename == "easyvend:depositor_on" then
  464. d = string.format("Depositing Machine (Owned by <%s>!)\nBuying: %s\nPayment: %s", dname, printitem, printcost)
  465. elseif nodename == "easyvend:depositor" then
  466. d = string.format("Inactive Depositing Machine (Owned by <%s>!)\nBuying: %s\nPayment: %s", dname, printitem, printcost)
  467. end
  468. return d
  469. end
  470. easyvend.execute_trade = function(pos, sender, player_inv, pin, vendor_inv, iin, remote_tax)
  471. local sendername = sender:get_player_name()
  472. local meta = minetest.get_meta(pos)
  473. local meta_inv = meta:get_inventory()
  474. if meta_inv:get_size("upgrade_slot") == 0 then
  475. meta_inv:set_size("upgrade_slot", 1)
  476. end
  477. local have_clu = meta_inv:contains_item("upgrade_slot", "techcrafts:control_logic_unit")
  478. local node = minetest.get_node(pos)
  479. local number = meta:get_int("number")
  480. local cost = meta:get_int("cost")
  481. local itemname=meta:get_string("itemname")
  482. local item=meta_inv:get_stack("item", 1)
  483. local check_wear = meta:get_int("wear") == 0 and minetest.registered_tools[itemname] ~= nil
  484. local buysell = easyvend.buysell(node.name)
  485. local number_stack_max = item:get_stack_max()
  486. local maxnumber = number_stack_max * slots_max
  487. if ( number == nil or number < 1 or number > maxnumber ) or
  488. ( cost == nil or cost < 1 or cost > maxcost ) or
  489. ( itemname == nil or itemname=="") then
  490. meta:set_string("status", "Invalid item count or price!")
  491. easyvend.machine_disable(pos, node, sendername)
  492. easyvend.set_formspec(pos)
  493. return
  494. end
  495. local machine_currency = meta:get_string("machine_currency")
  496. local machine_owner = meta:get_string("owner")
  497. -- Check currency.
  498. if not currency.is_currency(machine_currency) then
  499. easyvend.sound_error(sendername)
  500. minetest.chat_send_player(sendername, "# Server: Shop at " .. rc.pos_to_namestr(pos) .. " uses a depreciated currency, attempting to upgrade!")
  501. minetest.chat_send_player(sendername, "# Server: If this happens, try to use the shop again and it may work if nothing else is wrong.")
  502. easyvend.machine_check(pos, node)
  503. --meta:set_string("status", "Machine uses a depreciated currency standard!")
  504. --easyvend.machine_disable(pos, node, sendername)
  505. --easyvend.set_formspec(pos)
  506. return
  507. end
  508. -- Cannot sell or buy currency directly.
  509. if currency.is_currency(itemname) then
  510. meta:set_string("status", "Cannot treat currency as a directly saleable item!")
  511. easyvend.machine_disable(pos, node, sendername)
  512. easyvend.set_formspec(pos)
  513. return
  514. end
  515. local chest_pos_remove, chest_error_remove, chest_pos_add, chest_error_add
  516. if buysell == "sell" then
  517. -- Vending.
  518. chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, true)
  519. chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, false)
  520. else
  521. -- Depositing.
  522. chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, true)
  523. chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, false)
  524. end
  525. if chest_pos_remove ~= nil and chest_pos_add ~= nil and sender and sender:is_player() then
  526. -- Since we'll be modifying chest inventories, update nearby vending machines when we're done.
  527. minetest.after(0, chest_api.update_vending, chest_pos_add)
  528. minetest.after(0, chest_api.update_vending, chest_pos_remove)
  529. local rchest = minetest.get_node(chest_pos_remove)
  530. local rchestdef = registered_chests[rchest.name]
  531. local rchest_meta = minetest.get_meta(chest_pos_remove)
  532. local rchest_inv = rchest_meta:get_inventory()
  533. local achest = minetest.get_node(chest_pos_add)
  534. local achestdef = registered_chests[achest.name]
  535. local achest_meta = minetest.get_meta(chest_pos_add)
  536. local achest_inv = achest_meta:get_inventory()
  537. -- If passing a target inventory, redirect operations to it.
  538. -- This also indicates whether this is a remote trade executed via market.
  539. local vchest_inv = achest_inv
  540. local vchest_name = achestdef.inv_list
  541. if vendor_inv and iin then
  542. vchest_inv = vendor_inv
  543. vchest_name = iin
  544. end
  545. local stack = {name=itemname, count=number, wear=0, metadata=""}
  546. local price = currency.get_stack_value(machine_currency, cost)
  547. local chest_has, player_has, chest_free, player_free, chest_out, player_out
  548. local msg = ""
  549. if buysell == "sell" then
  550. -- Vending.
  551. local pricewithtax = price
  552. if vendor_inv then
  553. pricewithtax = currency.calculate_tax(price, 1, remote_tax)
  554. end
  555. chest_has, chest_out = easyvend.check_and_get_items(rchest_inv, rchestdef.inv_list, stack, check_wear)
  556. player_has = currency.has_cash_amount(player_inv, pin, pricewithtax)
  557. chest_free = currency.room_for_cash(vchest_inv, vchest_name, price)
  558. player_free = player_inv:room_for_item(pin, stack)
  559. if chest_has and player_has and chest_free and player_free then
  560. if number <= number_stack_max then
  561. easyvend.machine_enable(pos, node)
  562. -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
  563. if check_wear then
  564. rchest_inv:set_stack(rchestdef.inv_list, chest_out[1].id, "")
  565. player_inv:add_item(pin, chest_out[1].item)
  566. else
  567. stack = rchest_inv:remove_item(rchestdef.inv_list, stack)
  568. player_inv:add_item(pin, stack)
  569. end
  570. -- Transfer cash.
  571. currency.remove_cash(player_inv, pin, pricewithtax)
  572. currency.add_cash(vchest_inv, vchest_name, price)
  573. -- Deliver tax to the colonial government.
  574. currency.record_tax_income(pricewithtax - price)
  575. meta:set_string("message", "Item bought.")
  576. easyvend.sound_vend(pos)
  577. easyvend.machine_check(pos, node)
  578. local remote_str = ""
  579. if vendor_inv then
  580. remote_str = " remotely"
  581. end
  582. minetest.log("action", sendername .. remote_str .. " bought " .. number .. " " ..
  583. itemname .. " for " .. price .. " minegeld from vending machine owned by " ..
  584. machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (pricewithtax - price))
  585. if have_clu then
  586. easyvend.record_purchase({
  587. type = "sell",
  588. owner = machine_owner,
  589. player = sendername,
  590. count = number,
  591. item = itemname,
  592. price = price,
  593. pos = vector.copy(pos),
  594. })
  595. end
  596. else
  597. -- Large item counts (multiple stacks)
  598. local numberstacks = math.modf(number / number_stack_max)
  599. local numberremainder = math.fmod(number, number_stack_max)
  600. local numberfree = numberstacks
  601. if numberremainder > 0 then numberfree = numberfree + 1 end
  602. if not player_free and easyvend.free_slots(player_inv, pin) < numberfree then
  603. if numberfree > 1 then
  604. msg = string.format("No room in your inventory (%d empty slots required)!", numberfree)
  605. else
  606. msg = "No room in your inventory!"
  607. end
  608. meta:set_string("message", msg)
  609. elseif not chest_free and not currency.room_for_cash(vchest_inv, vchest_name, price) then
  610. meta:set_string("status", "No room in the machine’s storage!")
  611. easyvend.machine_disable(pos, node, sendername)
  612. else
  613. -- Remember items for transfer
  614. local cheststacks = {}
  615. easyvend.machine_enable(pos, node)
  616. -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
  617. if check_wear then
  618. for o=1, #chest_out do
  619. rchest_inv:set_stack(rchestdef.inv_list, chest_out[o].id, "")
  620. end
  621. else
  622. for i=1, numberstacks do
  623. stack.count = number_stack_max
  624. table.insert(cheststacks, rchest_inv:remove_item(rchestdef.inv_list, stack))
  625. end
  626. end
  627. if numberremainder > 0 then
  628. stack.count = numberremainder
  629. table.insert(cheststacks, rchest_inv:remove_item(rchestdef.inv_list, stack))
  630. end
  631. if check_wear then
  632. for o=1, #chest_out do
  633. player_inv:add_item(pin, chest_out[o].item)
  634. end
  635. else
  636. for i=1, #cheststacks do
  637. player_inv:add_item(pin, cheststacks[i])
  638. end
  639. end
  640. -- Transfer money.
  641. currency.remove_cash(player_inv, pin, pricewithtax)
  642. currency.add_cash(vchest_inv, vchest_name, price)
  643. -- Deliver tax to the colonial government.
  644. currency.record_tax_income(pricewithtax - price)
  645. meta:set_string("message", "Item bought.")
  646. easyvend.sound_vend(pos)
  647. easyvend.machine_check(pos, node)
  648. local remote_str = ""
  649. if vendor_inv then
  650. remote_str = " remotely"
  651. end
  652. minetest.log("action", sendername .. remote_str .. " bought " .. number .. " " ..
  653. itemname .. " for " .. price .. " minegeld from vending machine owned by " ..
  654. machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (pricewithtax - price))
  655. if have_clu then
  656. easyvend.record_purchase({
  657. type = "sell",
  658. owner = machine_owner,
  659. player = sendername,
  660. count = number,
  661. item = itemname,
  662. price = price,
  663. pos = vector.copy(pos),
  664. })
  665. end
  666. end
  667. end
  668. elseif chest_has and player_has then
  669. if not player_free then
  670. msg = "No room in your inventory!"
  671. meta:set_string("message", msg)
  672. easyvend.sound_error(sendername)
  673. elseif not chest_free then
  674. msg = "No room in the machine’s storage!"
  675. meta:set_string("status", msg)
  676. easyvend.machine_disable(pos, node, sendername)
  677. end
  678. else
  679. if not chest_has then
  680. msg = "The vending machine has insufficient materials!"
  681. meta:set_string("status", msg)
  682. easyvend.machine_disable(pos, node, sendername)
  683. elseif not player_has then
  684. msg = "You can’t afford this item!"
  685. meta:set_string("message", msg)
  686. easyvend.sound_error(sendername)
  687. end
  688. end
  689. else
  690. -- Depositing.
  691. local pricewithtax = price
  692. if vendor_inv then
  693. pricewithtax = currency.calculate_tax(price, 2, remote_tax)
  694. end
  695. chest_has = currency.has_cash_amount(rchest_inv, rchestdef.inv_list, price)
  696. player_has, player_out = easyvend.check_and_get_items(player_inv, pin, stack, check_wear)
  697. chest_free = vchest_inv:room_for_item(vchest_name, stack)
  698. player_free = currency.room_for_cash(player_inv, pin, pricewithtax)
  699. if chest_has and player_has and chest_free and player_free then
  700. if number <= number_stack_max then
  701. easyvend.machine_enable(pos, node)
  702. -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
  703. if check_wear then
  704. player_inv:set_stack(pin, player_out[1].id, "")
  705. vchest_inv:add_item(vchest_name, player_out[1].item)
  706. else
  707. stack = player_inv:remove_item(pin, stack)
  708. vchest_inv:add_item(vchest_name, stack)
  709. end
  710. -- Transfer money.
  711. currency.remove_cash(rchest_inv, rchestdef.inv_list, price)
  712. currency.add_cash(player_inv, pin, pricewithtax)
  713. -- Deliver tax to the colonial government.
  714. currency.record_tax_income(price - pricewithtax)
  715. meta:set_string("status", "Ready.")
  716. meta:set_string("message", "Item sold.")
  717. easyvend.sound_deposit(pos)
  718. easyvend.machine_check(pos, node)
  719. local remote_str = ""
  720. if vendor_inv then
  721. remote_str = " remotely"
  722. end
  723. minetest.log("action", sendername .. remote_str .. " sold " .. number .. " " ..
  724. itemname .. " for " .. price .. " minegeld to depositing machine owned by " ..
  725. machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (price - pricewithtax))
  726. if have_clu then
  727. easyvend.record_deposit({
  728. type = "buy",
  729. owner = machine_owner,
  730. player = sendername,
  731. count = number,
  732. item = itemname,
  733. price = price,
  734. pos = vector.copy(pos),
  735. })
  736. end
  737. else
  738. -- Large item counts (multiple stacks)
  739. local numberstacks = math.modf(number / number_stack_max)
  740. local numberremainder = math.fmod(number, number_stack_max)
  741. local numberfree = numberstacks
  742. if numberremainder > 0 then numberfree = numberfree + 1 end
  743. if not player_free and not currency.room_for_cash(player_inv, pin, pricewithtax) then
  744. msg = "Not enough room in your inventory for payment!"
  745. meta:set_string("message", msg)
  746. easyvend.sound_error(sendername)
  747. elseif not chest_free and easyvend.free_slots(vchest_inv, vchest_name) < numberfree then
  748. meta:set_string("status", "No room in the machine’s storage!")
  749. easyvend.machine_disable(pos, node, sendername)
  750. else
  751. easyvend.machine_enable(pos, node)
  752. -- Remember removed items for transfer
  753. local playerstacks = {}
  754. -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
  755. if check_wear then
  756. for o=1, #player_out do
  757. player_inv:set_stack(pin, player_out[o].id, "")
  758. end
  759. else
  760. for i=1, numberstacks do
  761. stack.count = number_stack_max
  762. table.insert(playerstacks, player_inv:remove_item(pin, stack))
  763. end
  764. end
  765. if numberremainder > 0 then
  766. stack.count = numberremainder
  767. table.insert(playerstacks, player_inv:remove_item(pin, stack))
  768. end
  769. if check_wear then
  770. for o=1, #player_out do
  771. vchest_inv:add_item(vchest_name, player_out[o].item)
  772. end
  773. else
  774. for i=1, #playerstacks do
  775. vchest_inv:add_item(vchest_name, playerstacks[i])
  776. end
  777. end
  778. -- Transfer money.
  779. currency.remove_cash(rchest_inv, rchestdef.inv_list, price)
  780. currency.add_cash(player_inv, pin, pricewithtax)
  781. -- Deliver tax to the colonial government.
  782. currency.record_tax_income(price - pricewithtax)
  783. meta:set_string("message", "Item sold.")
  784. easyvend.sound_deposit(pos)
  785. easyvend.machine_check(pos, node)
  786. local remote_str = ""
  787. if vendor_inv then
  788. remote_str = " remotely"
  789. end
  790. minetest.log("action", sendername .. remote_str .. " sold " .. number .. " " ..
  791. itemname .. " for " .. price .. " minegeld to depositing machine owned by " ..
  792. machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (price - pricewithtax))
  793. if have_clu then
  794. easyvend.record_deposit({
  795. type = "buy",
  796. owner = machine_owner,
  797. player = sendername,
  798. count = number,
  799. item = itemname,
  800. price = price,
  801. pos = vector.copy(pos),
  802. })
  803. end
  804. end
  805. end
  806. elseif chest_has and player_has then
  807. if not player_free then
  808. msg = "No room in your inventory!"
  809. meta:set_string("message", msg)
  810. easyvend.sound_error(sendername)
  811. elseif not chest_free then
  812. msg = "No room in the machine’s storage!"
  813. meta:set_string("status", msg)
  814. easyvend.machine_disable(pos, node, sendername)
  815. end
  816. else
  817. if not player_has then
  818. msg = "You have insufficient materials!"
  819. meta:set_string("message", msg)
  820. easyvend.sound_error(sendername)
  821. elseif not chest_has then
  822. msg = "The depositing machine is out of money!"
  823. meta:set_string("status", msg)
  824. easyvend.machine_disable(pos, node, sendername)
  825. end
  826. end
  827. end
  828. else
  829. local status
  830. meta:set_int("stock", 0)
  831. if chest_error_remove == "no_chest" and chest_error_add == "no_chest" then
  832. status = "No storage; machine needs to be connected with a locked chest."
  833. elseif chest_error_remove == "not_owned" or chest_error_add == "not_owned" then
  834. status = "Storage can’t be accessed because it is owned by a different person!"
  835. elseif chest_error_remove == "no_stock" then
  836. if buysell == "sell" then
  837. status = "The vending machine has insufficient materials!"
  838. else
  839. status = "The depositing machine is out of money!"
  840. end
  841. elseif chest_error_add == "no_space" then
  842. status = "No room in the machine’s storage!"
  843. else
  844. status = "Unknown error!"
  845. end
  846. meta:set_string("status", status)
  847. easyvend.sound_error(sendername)
  848. end
  849. easyvend.set_formspec(pos)
  850. end
  851. -- Executed when player uses formspec on actual vending machine.
  852. easyvend.on_receive_fields_buysell = function(pos, formname, fields, sender)
  853. if not fields.buysell then
  854. return
  855. end
  856. return easyvend.execute_trade(pos, sender, sender:get_inventory(), "main", nil, nil, nil)
  857. end
  858. easyvend.after_place_node = function(pos, placer)
  859. local node = minetest.get_node(pos)
  860. local meta = minetest.get_meta(pos)
  861. local inv = meta:get_inventory()
  862. local player_name = placer:get_player_name()
  863. local dname = rename.gpn(player_name)
  864. inv:set_size("item", 1)
  865. inv:set_size("gold", 1)
  866. inv:set_size("upgrade_slot", 1)
  867. local machine_currency = currency_types[initial_currency]
  868. meta:set_string("machine_currency", machine_currency)
  869. meta:set_int("machine_currency_idx", initial_currency)
  870. inv:set_stack( "gold", 1, machine_currency )
  871. local d = ""
  872. if node.name == "easyvend:vendor" then
  873. d = string.format("Inactive Vending Machine (Owned by <%s>!)", dname)
  874. meta:set_int("wear", 1)
  875. elseif node.name == "easyvend:depositor" then
  876. d = string.format("Inactive Depositing Machine (Owned by <%s>!)", dname)
  877. meta:set_int("wear", 0)
  878. end
  879. meta:set_string("infotext", d)
  880. meta:set_string("status", "Awaiting configuration by owner.")
  881. meta:set_string("message", "Welcome! Please prepare the machine.")
  882. meta:set_int("number", 1)
  883. meta:set_int("cost", 1)
  884. meta:set_int("stock", -1)
  885. meta:set_int("configmode", 1)
  886. meta:set_string("itemname", "")
  887. meta:set_string("owner", player_name or "")
  888. meta:set_string("rename", dname)
  889. easyvend.set_formspec(pos)
  890. end
  891. easyvend.can_dig = function(pos, player)
  892. local meta = minetest.get_meta(pos)
  893. local inv = meta:get_inventory()
  894. if not inv:is_empty("upgrade_slot") then
  895. return false
  896. end
  897. local name = player:get_player_name()
  898. local owner = meta:get_string("owner")
  899. -- Owner can always dig shop
  900. if owner == name then
  901. return true
  902. end
  903. local chest_pos = easyvend.find_connected_chest(owner, pos)
  904. local chest, meta_chest
  905. if chest_pos then
  906. chest = minetest.get_node(chest_pos)
  907. meta_chest = minetest.get_meta(chest_pos)
  908. else
  909. return true --if no chest, enyone can dig this shop
  910. end
  911. if registered_chests[chest.name] then
  912. if player and player:is_player() then
  913. local owner_chest = meta_chest:get_string(registered_chests[chest.name].meta_owner)
  914. if name == owner_chest then
  915. return true --chest owner can also dig shop
  916. end
  917. end
  918. return false
  919. else
  920. return true --if no chest, enyone can dig this shop
  921. end
  922. end
  923. easyvend.on_receive_fields = function(pos, formname, fields, sender)
  924. local meta = minetest.get_meta(pos)
  925. local node = minetest.get_node(pos)
  926. local owner = meta:get_string("owner")
  927. local sendername = sender:get_player_name(sender)
  928. if fields.easyvend_currency_image then
  929. if meta:get_int("configmode") == 1 and sendername == owner then
  930. -- Toggle through possible banknote denominations.
  931. local idx = meta:get_int("machine_currency_idx") or initial_currency
  932. idx = idx + 1
  933. if idx > #currency_types then idx = 1 end
  934. meta:set_string("machine_currency", currency_types[idx])
  935. meta:set_int("machine_currency_idx", idx)
  936. easyvend.set_formspec(pos)
  937. end
  938. end
  939. if fields.config or fields.save or fields.usermode then
  940. if sender:get_player_name() == owner then
  941. easyvend.on_receive_fields_config(pos, formname, fields, sender)
  942. else
  943. meta:set_string("message", "Only the owner may change the configuration.")
  944. easyvend.sound_error(sendername)
  945. easyvend.set_formspec(pos)
  946. return
  947. end
  948. elseif fields.wear ~= nil then
  949. if sender:get_player_name() == owner then
  950. if fields.wear == "true" then
  951. if easyvend.buysell(node.name) == "buy" then
  952. meta:set_string("message", "Used tools are now accepted.")
  953. else
  954. meta:set_string("message", "Used tools are now for sale.")
  955. end
  956. meta:set_int("wear", 1)
  957. elseif fields.wear == "false" then
  958. if easyvend.buysell(node.name) == "buy" then
  959. meta:set_string("message", "Used tools are now rejected.")
  960. else
  961. meta:set_string("message", "Used tools won’t be sold anymore.")
  962. end
  963. meta:set_int("wear", 0)
  964. end
  965. easyvend.set_formspec(pos)
  966. return
  967. else
  968. meta:set_string("message", "Only the owner may change the configuration.")
  969. easyvend.sound_error(sendername)
  970. easyvend.set_formspec(pos)
  971. return
  972. end
  973. elseif fields.buysell then
  974. easyvend.on_receive_fields_buysell(pos, formname, fields, sender)
  975. end
  976. end
  977. easyvend.sound_error = function(playername)
  978. minetest.sound_play("easyvend_error", {to_player = playername, gain = 0.25}, true)
  979. end
  980. easyvend.sound_setup = function(pos)
  981. minetest.sound_play("easyvend_activate", {pos = pos, gain = 0.5, max_hear_distance = 12}, true)
  982. end
  983. easyvend.sound_disable = function(pos)
  984. minetest.sound_play("easyvend_disable", {pos = pos, gain = 0.9, max_hear_distance = 12}, true)
  985. end
  986. easyvend.sound_vend = function(pos)
  987. minetest.sound_play("easyvend_vend", {pos = pos, gain = 0.4, max_hear_distance = 5}, true)
  988. end
  989. easyvend.sound_deposit = function(pos)
  990. minetest.sound_play("easyvend_deposit", {pos = pos, gain = 0.4, max_hear_distance = 5}, true)
  991. end
  992. --[[ Tower building ]]
  993. easyvend.is_traversable = function(pos)
  994. local node = minetest.get_node_or_nil(pos)
  995. if (node == nil) then
  996. return false
  997. end
  998. return traversable_node_types[node.name] == true
  999. end
  1000. easyvend.neighboring_nodes = function(pos)
  1001. local check = {
  1002. {x=pos.x, y=pos.y-1, z=pos.z},
  1003. {x=pos.x, y=pos.y+1, z=pos.z},
  1004. }
  1005. local trav = {}
  1006. for i=1,#check do
  1007. if easyvend.is_traversable(check[i]) then
  1008. table.insert(trav, check[i])
  1009. end
  1010. end
  1011. return trav
  1012. end
  1013. easyvend.find_connected_chest = function(owner, pos, nodename, check_wear, amount, removing)
  1014. local nodes = easyvend.neighboring_nodes(pos)
  1015. if (#nodes < 1 or #nodes > 2) then
  1016. return nil, "no_chest"
  1017. end
  1018. -- Find the stack direction
  1019. local first = nil
  1020. local second = nil
  1021. for i=1,#nodes do
  1022. if ( first == nil ) then
  1023. first = nodes[i]
  1024. else
  1025. second = nodes[i]
  1026. end
  1027. end
  1028. local chest_pos, chest_internal
  1029. if (first ~= nil and second ~= nil) then
  1030. local dy = (first.y - second.y)/2
  1031. chest_pos, chest_internal = easyvend.find_chest(owner, pos, dy, nodename, check_wear, amount, removing)
  1032. if ( chest_pos == nil ) then
  1033. chest_pos, chest_internal = easyvend.find_chest(owner, pos, -dy, nodename, check_wear, amount, removing, chest_internal)
  1034. end
  1035. else
  1036. local dy = first.y - pos.y
  1037. chest_pos, chest_internal = easyvend.find_chest(owner, pos, dy, nodename, check_wear, amount, removing)
  1038. end
  1039. if chest_internal.chests == 0 then
  1040. return nil, "no_chest"
  1041. elseif chest_internal.chests == chest_internal.other_chests then
  1042. return nil, "not_owned"
  1043. elseif removing and chest_internal.stock < 1 then
  1044. return nil, "no_stock"
  1045. elseif not removing and chest_internal.space < 1 then
  1046. return nil, "no_space"
  1047. elseif chest_pos ~= nil then
  1048. return chest_pos
  1049. else
  1050. return nil, "unknown"
  1051. end
  1052. end
  1053. easyvend.find_chest = function(owner, pos, dy, itemname, check_wear, amount, removing, internal)
  1054. pos = {x=pos.x, y=pos.y + dy, z=pos.z}
  1055. if internal == nil then
  1056. internal = {}
  1057. internal.chests = 0
  1058. internal.other_chests = 0
  1059. internal.stock = 0
  1060. internal.space = 0
  1061. end
  1062. local node = minetest.get_node_or_nil(pos)
  1063. if ( node == nil ) then
  1064. return nil, internal
  1065. end
  1066. local chestdef = registered_chests[node.name]
  1067. if (chestdef ~= nil) then
  1068. internal.chests = internal.chests + 1
  1069. local meta = minetest.get_meta(pos)
  1070. if (owner ~= meta:get_string(chestdef.meta_owner)) then
  1071. internal.other_chests = internal.other_chests + 1
  1072. return nil, internal
  1073. end
  1074. local inv = meta:get_inventory()
  1075. if (inv ~= nil) then
  1076. if (itemname ~= nil and amount ~= nil and removing ~= nil and check_wear ~= nil) then
  1077. local chest_has, chest_free
  1078. -- We're going to query the chest to answer two questions:
  1079. -- Does the chest contain the item in the amount requested?
  1080. -- Does the chest contain free slots suitable to store the amount requested?
  1081. if currency.is_currency(itemname) then
  1082. -- Item is a fungible currency, use currency-related functions.
  1083. local value = currency.get_stack_value(itemname, amount)
  1084. chest_free = currency.room_for_cash(inv, chestdef.inv_list, value)
  1085. chest_has = currency.has_cash_amount(inv, chestdef.inv_list, value)
  1086. -- If the chest doesn't have enough space to ADD currency,
  1087. -- we can't safely remove currency, either (due to denomination splitting).
  1088. if not chest_free then
  1089. chest_has = false
  1090. end
  1091. else
  1092. -- Do regular itemstack-style check. Note: as of the current Minetest version,
  1093. -- the raw inv:room_for_item() check works with stacks over the stackmax limit.
  1094. -- The old version of this check also checked for number of free slots,
  1095. -- but that shouldn't be necessary.
  1096. local stack = {name=itemname, count=amount, wear=0, metadata=""}
  1097. chest_has = easyvend.check_and_get_items(inv, chestdef.inv_list, stack, check_wear)
  1098. chest_free = inv:room_for_item(chestdef.inv_list, stack)
  1099. end
  1100. if chest_has then
  1101. internal.stock = internal.stock + 1
  1102. end
  1103. if chest_free then
  1104. internal.space = internal.space + 1
  1105. end
  1106. if (removing and internal.stock == 0) or (not removing and internal.space == 0) then
  1107. return easyvend.find_chest(owner, pos, dy, itemname, check_wear, amount, removing, internal)
  1108. else
  1109. return pos, internal
  1110. end
  1111. end
  1112. end
  1113. elseif (node.name ~= "easyvend:vendor" and node.name~="easyvend:depositor" and node.name~="easyvend:vendor_on" and node.name~="easyvend:depositor_on") then
  1114. return nil, internal
  1115. end
  1116. return easyvend.find_chest(owner, pos, dy, itemname, check_wear, amount, removing, internal)
  1117. end
  1118. -- Pseudo-inventory handling
  1119. easyvend.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  1120. if listname=="item" then
  1121. local meta = minetest.get_meta(pos)
  1122. local owner = meta:get_string("owner")
  1123. local name = player:get_player_name()
  1124. if name == owner then
  1125. local inv = meta:get_inventory()
  1126. if stack==nil then
  1127. inv:set_stack( "item", 1, nil )
  1128. else
  1129. local sn = stack:get_name()
  1130. -- Do not permit currency denominations to be placed in this slot.
  1131. if currency.is_currency(sn) then
  1132. return 0
  1133. end
  1134. inv:set_stack("item", 1, sn)
  1135. meta:set_string("itemname", sn)
  1136. easyvend.set_formspec(pos)
  1137. end
  1138. end
  1139. end
  1140. if listname == "upgrade_slot" and stack:get_name() == "techcrafts:control_logic_unit" then
  1141. local meta = minetest.get_meta(pos)
  1142. local inv = meta:get_inventory()
  1143. if inv:get_stack("upgrade_slot", 1):get_count() == 0 then
  1144. return 1
  1145. end
  1146. end
  1147. return 0
  1148. end
  1149. easyvend.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  1150. if listname == "upgrade_slot" then
  1151. local meta = minetest.get_meta(pos)
  1152. local owner = meta:get_string("owner")
  1153. local pname = player:get_player_name()
  1154. if pname == owner then
  1155. return stack:get_count()
  1156. end
  1157. end
  1158. return 0
  1159. end
  1160. easyvend.allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  1161. return 0
  1162. end