You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

atm-core.lua 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. --
  2. -- Minetest lurkcoin mod - ATMs
  3. --
  4. -- © 2019 by luk3yx
  5. --
  6. local open_atms = {}
  7. local function e(text)
  8. return minetest.formspec_escape(tostring(text))
  9. end
  10. local formspecs = {}
  11. -- 0.4 compatibility
  12. lurkcoin.formspec_prepend = ''
  13. if minetest.get_modpath('default') and rawget(_G, 'default') and
  14. default.gui_bg and default.gui_bg_img and default.gui_slots then
  15. lurkcoin.formspec_prepend = default.gui_bg .. default.gui_bg_img ..
  16. default.gui_slots
  17. end
  18. -- The formspec code is based on something random I did in 2017(?) for
  19. -- lurkcoinV1, formspecs are weird and I somehow got it right™ then.
  20. local function get_formspec(name, page, params)
  21. -- The formspec template
  22. local formspec = 'size[8,9;]' .. lurkcoin.formspec_prepend ..
  23. 'label[0.5,1.75;Your balance: ' ..
  24. e(lurkcoin.bank.getbal(name)) .. 'cr.]' ..
  25. 'image_button[2,0.55;4,0.5;default_dirt.png^\\[colorize:#343434;y;' ..
  26. 'Welcome to a ' .. e(lurkcoin.server_name) .. ' ATM!]' ..
  27. 'label[0.5,2.25;Exchange rate: \194\1641.00 is equal to ' ..
  28. e(lurkcoin.exchange_rate) .. 'cr.]' ..
  29. 'image_button[1.75,1.05;4.5,0.5;default_dirt.png^\\[colorize:' ..
  30. '#343434;y; Your account: ' .. e(name) .. ']' ..
  31. 'image[0.5,0.5;1,1;default_mese_crystal.png]' ..
  32. 'image[6.5,0.5;1,1;default_mese_crystal.png]'
  33. -- Get the page formspec
  34. local page = formspecs[page] or formspecs.main
  35. if type(page) == 'string' then
  36. formspec = formspec .. page
  37. elseif type(page) == 'function' then
  38. formspec = formspec .. page(name, params)
  39. end
  40. -- Return the generated formspec
  41. return formspec
  42. end
  43. -- The formspecs (depending on installed mods)
  44. local withdrawls = false
  45. -- Payment screen
  46. function formspecs.pay(name, fields, guessed_amount)
  47. fields = fields or {}
  48. local formspec =
  49. 'field[0.8,3.5;7,1;user;User to pay;' .. e(fields.user or '') .. ']' ..
  50. 'field_close_on_enter[user;false]' ..
  51. 'field[0.8,4.8;7,1;server;Server the user is on;' ..
  52. e(fields.server or lurkcoin.server_name) .. ']' ..
  53. 'field_close_on_enter[server;false]' ..
  54. 'field[0.8,6.15;7,1;amount;Amount to pay (in cr);' ..
  55. e(fields.amount or '5.00') .. ']' ..
  56. 'field_close_on_enter[amount;false]'
  57. if fields._err then
  58. formspec = formspec ..
  59. 'label[0.5,7;' .. e(fields._err) .. ']' ..
  60. 'button[0.5,8;3.5,1;payuser;Cancel]' ..
  61. 'button[4,8;3.5,1;paysubmit;Try again]'
  62. elseif fields.paysubmit then
  63. formspec = formspec ..
  64. 'label[0.5,7;Please confirm the above values.'
  65. if fields.server ~= lurkcoin.server_name then
  66. local exc = -1
  67. if type(fields._exchange_rate) == 'number' then
  68. exc = fields._exchange_rate
  69. end
  70. if exc < 0 then
  71. lurkcoin.get_exchange_rate(fields.amount, fields.server or
  72. lurkcoin.server_name, function(data)
  73. if not data then
  74. fields._err = 'That server does not exist!'
  75. end
  76. fields._exchange_rate = data
  77. return lurkcoin.show_atm(name, 'pay', fields)
  78. end)
  79. return formspec .. '\n' .. fields.amount .. 'cr is equal to' ..
  80. ' ...\n\n(Calculating...)]'
  81. end
  82. exc = tostring(math.floor(exc * 100) / 100)
  83. formspec = formspec .. '\n' .. fields.amount .. 'cr is ' ..
  84. 'equal to \194\164' .. exc .. '.'
  85. end
  86. formspec = formspec .. ']' ..
  87. 'button[0.5,8;3.5,1;payuser;Cancel]' ..
  88. 'button[4,8;3.5,1;payconfirm;Confirm and pay]'
  89. else
  90. formspec = formspec ..
  91. 'button[0.5,7;7,1;paysubmit;Pay the user!]'
  92. if withdrawls then
  93. formspec = formspec ..
  94. 'button[0.5,8;3.5,1;home;Go back]' ..
  95. 'button_exit[4,8;3.5,1;quit;Quit]'
  96. else
  97. formspec = formspec ..
  98. 'button_exit[0.5,8;7,1;quit;Quit]'
  99. end
  100. end
  101. return formspec
  102. end
  103. -- The main formspec
  104. if minetest.get_modpath('currency') then
  105. formspecs.main =
  106. 'button[0.5,3.25;7,1;deposit;Deposit money]' ..
  107. 'field[0.8,4.87;6,1;withdraw;Withdraw money (in Mg);5.00]' ..
  108. 'field_close_on_enter[withdraw;false]' ..
  109. 'button[6.5,4.55;1,1;wd;Next >]' ..
  110. 'field[0.8,6.12;6,1;user;Pay someone;]' ..
  111. 'field_close_on_enter[user;false]' ..
  112. 'button[6.5,5.8;1,1;payuser;Next >]' ..
  113. 'button_exit[0.5,8;7,1;quit;Quit]'
  114. -- Create a withdrawls list with available currency.
  115. withdrawls = {
  116. [0.05] = 'currency:minegeld_cent_5',
  117. [0.10] = 'currency:minegeld_cent_10',
  118. [0.25] = 'currency:minegeld_cent_25',
  119. [1] = 'currency:minegeld',
  120. [5] = 'currency:minegeld_5',
  121. [10] = 'currency:minegeld_10',
  122. [20] = 'currency:minegeld_20', -- The original mod now has 20Mg notes.
  123. [50] = 'currency:minegeld_50',
  124. [100] = 'currency:minegeld_100',
  125. }
  126. -- Remove non-registered notes
  127. for id, note in pairs(withdrawls) do
  128. if not minetest.registered_items[note] then
  129. withdrawls[id] = nil
  130. end
  131. end
  132. assert(#withdrawls > 0, 'The "currency" mod did not register any notes!')
  133. -- Create a detached inventory for depositing
  134. local inv = minetest.create_detached_inventory('lurkcoin:atm_deposit', {
  135. on_put = function(inv, listname, index, stack, player)
  136. local name = stack:get_name()
  137. if name:sub(1, 17) == 'currency:minegeld' then
  138. local m = name:sub(19)
  139. if #m > 0 then
  140. if m:sub(1, 5) == 'cent_' then
  141. m = tonumber(m:sub(6))
  142. if m then m = m / 100 end
  143. else
  144. m = tonumber(m)
  145. end
  146. else
  147. m = 1
  148. end
  149. if type(m) == 'number' and m == m then
  150. m = m * stack:get_count()
  151. local pname = player:get_player_name()
  152. if not lurkcoin.bank.add(pname, m, 'Deposit') then
  153. player:get_inventory():add_item('main', stack)
  154. end
  155. core.log('action', 'Player ' .. pname .. ' deposts ' ..
  156. stack:to_string() .. ' into an ATM.')
  157. end
  158. end
  159. inv:set_list(listname, {})
  160. lurkcoin.show_atm(player:get_player_name(), 'deposit')
  161. end,
  162. allow_put = function(inv, listname, index, stack, player)
  163. local name = stack:get_name()
  164. if name:sub(1, 17) == 'currency:minegeld' then
  165. return stack:get_count()
  166. else
  167. return 0
  168. end
  169. end,
  170. allow_take = function() return 0 end,
  171. })
  172. inv:set_size("lurkcoin", 1)
  173. -- Depositing
  174. formspecs.deposit =
  175. 'list[current_player;main;0,4.85;8,1;]' ..
  176. 'list[current_player;main;0,6.08;8,3;8]' ..
  177. 'list[detached:lurkcoin:atm_deposit;lurkcoin;3.5,3;1,1;]' ..
  178. 'listring[]' ..
  179. 'image_button[0,3;3.5,1;default_dirt.png^\\[colorize:#343434;' ..
  180. 'ignore;Deposit money here →]' ..
  181. 'button[5.25,3;2,1;home;Finish]'
  182. else
  183. -- When there is no physical currency, the only thing you can do is pay
  184. -- others.
  185. formspecs.main = formspecs.pay
  186. end
  187. -- "Transaction accepted" screen
  188. function formspecs.success(name, text)
  189. return 'image[2.1,3;4.5,4.5;lurkcoin_success.png]' ..
  190. 'image_button[1,7;6,1;default_dirt.png^\\[colorize:#343434;' ..
  191. 'ignore;' .. e(text or 'Transaction sent!') .. ']' ..
  192. 'button[0.5,8;3.5,1;home;Go back]' ..
  193. 'button_exit[4,8;3.5,1;quit;Quit]'
  194. end
  195. -- "Transaction failed" screen
  196. function formspecs.error(name, text)
  197. return 'image[2.1,3;4.5,4.5;lurkcoin_error.png]' ..
  198. 'image_button[1,7;6,1;default_dirt.png^\\[colorize:#343434;' ..
  199. 'ignore;' .. e(text or 'An error has occurred!') .. ']' ..
  200. 'button[0.5,8;3.5,1;home;Go back]' ..
  201. 'button_exit[4,8;3.5,1;quit;Quit]'
  202. end
  203. -- Processing screen
  204. formspecs.processing =
  205. 'image[2.1,3;4.5,4.5;lurkcoin_processing.png]' ..
  206. 'image_button[1,7;6,1;default_dirt.png^\\[colorize:#343434;' ..
  207. 'ignore;Processing your transaction...]' ..
  208. 'image_button[1,8;6,1;default_dirt.png^\\[colorize:#343434;' ..
  209. 'ignore;This should only take a few seconds.]'
  210. -- A wrapper function
  211. function lurkcoin.show_atm(name, page, params)
  212. minetest.show_formspec(name, 'lurkcoin:atm', get_formspec(name,
  213. page, params))
  214. end
  215. minetest.register_on_player_receive_fields(function(player, formname, fields)
  216. if formname == 'lurkcoin:atm' then
  217. local name = player:get_player_name()
  218. if withdrawls then
  219. if fields.deposit then
  220. return lurkcoin.show_atm(name, 'deposit')
  221. elseif fields.wd then
  222. local amount = tonumber(fields.withdraw)
  223. if not amount or amount ~= amount or amount <= 0 then
  224. return lurkcoin.show_atm(name, 'error',
  225. 'ERROR: Invalid number!')
  226. end
  227. local bal = lurkcoin.bank.getbal(name)
  228. if amount > bal then
  229. return lurkcoin.show_atm(name, 'error',
  230. 'ERROR: You cannot afford to do that!')
  231. end
  232. local note = false
  233. for id, item in pairs(withdrawls) do
  234. if (not note or id > note) and
  235. math.floor(amount / id) * id == amount then
  236. local def = minetest.registered_items[item]
  237. if def and amount / id <= (def.stack_max or 99) then
  238. note = id
  239. end
  240. end
  241. end
  242. if not note then
  243. return lurkcoin.show_atm(name, 'error',
  244. 'ERROR: I cannot store that amount of\nmoney in a ' ..
  245. 'single stack of notes/coins!')
  246. end
  247. local stack = ItemStack({
  248. name = withdrawls[note],
  249. count = amount / note,
  250. })
  251. local inv = player:get_inventory()
  252. if not inv:room_for_item('main', stack) then
  253. return lurkcoin.show_atm(name, 'error',
  254. 'ERROR: You do not have enough inventory space!')
  255. end
  256. lurkcoin.bank.subtract(name, amount)
  257. inv:add_item('main', stack)
  258. return lurkcoin.show_atm(name, 'success')
  259. end
  260. end
  261. if fields.payconfirm and open_atms[name] and open_atms[name].pay then
  262. local data = open_atms[name]
  263. for k, v in pairs(data) do
  264. if type(v) == 'number' then fields[k] = tonumber(fields[k]) end
  265. if k ~= 'pay' and fields[k] ~= v then
  266. if k ~= 'amount' then
  267. k = 'target ' .. k
  268. end
  269. fields._err = 'The ' .. k ..
  270. ' was modified before pressing confirm!'
  271. lurkcoin.show_atm(name, 'pay', fields)
  272. return
  273. end
  274. end
  275. lurkcoin.show_atm(name, 'processing')
  276. lurkcoin.pay(name, data.user, data.server, data.amount,
  277. function(success, msg)
  278. local page = success and 'success' or 'error'
  279. return lurkcoin.show_atm(name, page, msg)
  280. end
  281. )
  282. elseif fields.payuser or fields.paysubmit or fields.payconfirm then
  283. if fields.paysubmit then
  284. local amount = tonumber(fields.amount)
  285. if not amount or amount ~= amount or amount <= 0 then
  286. fields._err = 'Invalid number!'
  287. lurkcoin.show_atm(name, 'pay', fields)
  288. return
  289. elseif lurkcoin.bank.getbal(name) - amount < 0 then
  290. fields._err = 'You cannot afford to do that!'
  291. lurkcoin.show_atm(name, 'pay', fields)
  292. return
  293. elseif fields.server then
  294. fields.server = fields.server:gsub('^ *(.-) *$', '%1')
  295. end
  296. if fields.user then
  297. fields.user = fields.user:gsub('^ *(.-) *$', '%1')
  298. end
  299. if not fields.server or fields.server == '' then
  300. fields.server = lurkcoin.server_name
  301. end
  302. open_atms[name] = {
  303. pay = true,
  304. user = fields.user or '',
  305. server = fields.server,
  306. amount = amount,
  307. }
  308. if open_atms[name].user == '' or (
  309. fields.server == lurkcoin.server_name and
  310. not lurkcoin.bank.user_exists(open_atms[name].user)
  311. ) then
  312. fields._err = 'That user does not exist!'
  313. lurkcoin.show_atm(name, 'pay', fields)
  314. return
  315. end
  316. end
  317. fields._err = nil
  318. lurkcoin.show_atm(name, 'pay', fields)
  319. elseif fields.home then
  320. lurkcoin.show_atm(name, 'main')
  321. elseif fields.quit then
  322. open_atms[name] = nil
  323. end
  324. end
  325. end)
  326. minetest.register_on_leaveplayer(function(player)
  327. -- TODO: I forgot what one register_on_leaveplayer uses and am too lazy to
  328. -- check.
  329. local name
  330. if type(player) == 'string' then
  331. name = player
  332. else
  333. name = player:get_player_name()
  334. end
  335. open_atms[name] = nil
  336. end)