384 lines
14 KiB
Lua
384 lines
14 KiB
Lua
--
|
|
-- Minetest lurkcoin mod - ATMs
|
|
--
|
|
-- © 2019 by luk3yx
|
|
--
|
|
|
|
local open_atms = {}
|
|
|
|
local function e(text)
|
|
return minetest.formspec_escape(tostring(text))
|
|
end
|
|
|
|
local formspecs = {}
|
|
|
|
-- 0.4 compatibility
|
|
lurkcoin.formspec_prepend = ''
|
|
if minetest.global_exists('default') and default.gui_bg and
|
|
default.gui_bg_img and default.gui_slots then
|
|
lurkcoin.formspec_prepend = default.gui_bg .. default.gui_bg_img ..
|
|
default.gui_slots
|
|
end
|
|
|
|
local function centre_label(pos, label)
|
|
return 'image_button[' .. pos .. ';blank.png;;' .. e(label) ..
|
|
';true;false;]'
|
|
end
|
|
|
|
-- The formspec code is based on something I did in 2017(?) for lurkcoinV1,
|
|
-- formspecs are weird and I somehow got it right then.
|
|
local function get_formspec(name, page, params)
|
|
-- The formspec template
|
|
local formspec = 'size[8,9]' .. lurkcoin.formspec_prepend ..
|
|
'label[0.5,1.75;Your balance: ' ..
|
|
e(lurkcoin.bank.getbal(name)) .. 'cr.]' ..
|
|
centre_label('1,0.55;6,0.5', 'Welcome to a ' .. lurkcoin.server_name ..
|
|
'ATM!') ..
|
|
'label[0.5,2.25;Exchange rate: \194\1641.00 is equal to ' ..
|
|
e(lurkcoin.exchange_rate) .. 'cr.]' ..
|
|
centre_label('1.75,1.05;4.5,0.5', 'Your account: ' .. name) ..
|
|
'image[0.5,0.5;1,1;default_mese_crystal.png]' ..
|
|
'image[6.5,0.5;1,1;default_mese_crystal.png]'
|
|
|
|
-- Get the page formspec
|
|
page = formspecs[page] or formspecs.main
|
|
if type(page) == 'string' then
|
|
formspec = formspec .. page
|
|
elseif type(page) == 'function' then
|
|
formspec = formspec .. page(name, params)
|
|
end
|
|
|
|
-- Return the generated formspec
|
|
return formspec
|
|
end
|
|
|
|
-- The formspecs (depending on installed mods)
|
|
local withdrawls = false
|
|
|
|
-- Payment screen
|
|
function formspecs.pay(name, fields)
|
|
fields = fields or {}
|
|
local formspec =
|
|
'field[0.8,3.5;7,1;user;User to pay;' .. e(fields.user or '') .. ']' ..
|
|
'field_close_on_enter[user;false]' ..
|
|
'field[0.8,4.8;7,1;server;Server the user is on;' ..
|
|
e(fields.server or lurkcoin.server_name) .. ']' ..
|
|
'field_close_on_enter[server;false]' ..
|
|
'field[0.8,6.15;7,1;amount;Amount to pay (in cr);' ..
|
|
e(fields.amount or '5.00') .. ']' ..
|
|
'field_close_on_enter[amount;false]'
|
|
|
|
if fields._err then
|
|
formspec = formspec ..
|
|
'label[0.5,7;' .. e(fields._err) .. ']' ..
|
|
'button[0.5,8;3.5,1;payuser;Cancel]' ..
|
|
'button[4,8;3.5,1;paysubmit;Try again]'
|
|
elseif fields.paysubmit then
|
|
formspec = formspec ..
|
|
'label[0.5,7;Please confirm the above values.'
|
|
if fields.server ~= lurkcoin.server_name then
|
|
local exc = -1
|
|
if type(fields._exchange_rate) == 'number' then
|
|
exc = fields._exchange_rate
|
|
end
|
|
|
|
if exc < 0 then
|
|
lurkcoin.get_exchange_rate(fields.amount, fields.server or
|
|
lurkcoin.server_name, function(data, msg)
|
|
if not data then
|
|
fields._err = msg
|
|
end
|
|
fields._exchange_rate = data
|
|
return lurkcoin.show_atm(name, 'pay', fields)
|
|
end)
|
|
return formspec .. '\n' .. fields.amount .. 'cr is equal to' ..
|
|
' ...\n\n(Calculating...)]'
|
|
end
|
|
|
|
exc = tostring(math.floor(exc * 100) / 100)
|
|
formspec = formspec .. '\n' .. fields.amount .. 'cr is ' ..
|
|
'equal to ' .. exc .. '.'
|
|
end
|
|
formspec = formspec .. ']' ..
|
|
'button[0.5,8;3.5,1;payuser;Cancel]' ..
|
|
'button[4,8;3.5,1;payconfirm;Confirm and pay]'
|
|
else
|
|
formspec = formspec ..
|
|
'button[0.5,7;7,1;paysubmit;Pay the user!]'
|
|
|
|
if withdrawls then
|
|
formspec = formspec ..
|
|
'button[0.5,8;3.5,1;home;Go back]' ..
|
|
'button_exit[4,8;3.5,1;quit;Quit]'
|
|
else
|
|
formspec = formspec ..
|
|
'button_exit[0.5,8;7,1;quit;Quit]'
|
|
end
|
|
end
|
|
return formspec
|
|
end
|
|
|
|
-- The main formspec
|
|
if minetest.get_modpath('currency') then
|
|
formspecs.main =
|
|
'button[0.5,3.25;7,1;deposit;Deposit money]' ..
|
|
'field[0.8,4.87;6,1;withdraw;Withdraw money (in Mg);5.00]' ..
|
|
'field_close_on_enter[withdraw;false]' ..
|
|
'button[6.5,4.55;1,1;wd;Next >]' ..
|
|
'field[0.8,6.12;6,1;user;Pay someone;]' ..
|
|
'field_close_on_enter[user;false]' ..
|
|
'button[6.5,5.8;1,1;payuser;Next >]' ..
|
|
'button_exit[0.5,8;7,1;quit;Quit]'
|
|
|
|
-- Create a withdrawls list with available currency.
|
|
withdrawls = {
|
|
[0.05] = 'currency:minegeld_cent_5',
|
|
[0.10] = 'currency:minegeld_cent_10',
|
|
[0.25] = 'currency:minegeld_cent_25',
|
|
[1] = 'currency:minegeld',
|
|
[5] = 'currency:minegeld_5',
|
|
[10] = 'currency:minegeld_10',
|
|
[20] = 'currency:minegeld_20', -- The original mod now has 20Mg notes.
|
|
[50] = 'currency:minegeld_50',
|
|
[100] = 'currency:minegeld_100',
|
|
}
|
|
|
|
-- Remove non-registered notes
|
|
local remove = {}
|
|
for id, note in pairs(withdrawls) do
|
|
if not minetest.registered_items[note] then
|
|
table.insert(remove, id)
|
|
end
|
|
end
|
|
for _, id in ipairs(remove) do
|
|
withdrawls[id] = nil
|
|
end
|
|
assert(#withdrawls > 0, 'The "currency" mod did not register any notes!')
|
|
|
|
-- Create a detached inventory for depositing
|
|
local inv = minetest.create_detached_inventory('lurkcoin:atm_deposit', {
|
|
on_put = function(inv, listname, _, stack, player)
|
|
local name = stack:get_name()
|
|
if name:sub(1, 17) == 'currency:minegeld' then
|
|
local m = name:sub(19)
|
|
if #m > 0 then
|
|
if m:sub(1, 5) == 'cent_' then
|
|
m = tonumber(m:sub(6))
|
|
if m then m = m / 100 end
|
|
elseif m == 'bundle' then
|
|
m = 0.05 * 9
|
|
else
|
|
m = tonumber(m)
|
|
end
|
|
else
|
|
m = 1
|
|
end
|
|
|
|
if type(m) == 'number' and m == m then
|
|
m = m * stack:get_count()
|
|
local pname = player:get_player_name()
|
|
if not lurkcoin.bank.add(pname, m, 'Deposit') then
|
|
player:get_inventory():add_item('main', stack)
|
|
end
|
|
minetest.log('action', 'Player ' .. pname ..
|
|
' deposits ' .. tostring(m) .. 'Mg (' ..
|
|
stack:to_string() .. ') into a lurkcoin ATM.')
|
|
end
|
|
end
|
|
inv:set_list(listname, {})
|
|
lurkcoin.show_atm(player:get_player_name(), 'deposit')
|
|
end,
|
|
|
|
allow_put = function(_, _, _, stack, _)
|
|
local name = stack:get_name()
|
|
if name:sub(1, 17) == 'currency:minegeld' then
|
|
return stack:get_count()
|
|
else
|
|
return 0
|
|
end
|
|
end,
|
|
|
|
allow_take = function() return 0 end,
|
|
})
|
|
inv:set_size("lurkcoin", 1)
|
|
|
|
-- Depositing
|
|
formspecs.deposit =
|
|
'list[current_player;main;0,4.85;8,1;]' ..
|
|
'list[current_player;main;0,6.08;8,3;8]' ..
|
|
'list[detached:lurkcoin:atm_deposit;lurkcoin;3.5,3;1,1;]' ..
|
|
'listring[]' ..
|
|
centre_label('0,3;3.5,1', 'Deposit money here →') ..
|
|
'button[5.25,3;2,1;home;Finish]'
|
|
else
|
|
-- When there is no physical currency, the only thing you can do is pay
|
|
-- others.
|
|
formspecs.main = formspecs.pay
|
|
end
|
|
|
|
-- "Transaction accepted" screen
|
|
function formspecs.success(_, text)
|
|
return 'image[2.1,3;4.5,4.5;lurkcoin_success.png]' ..
|
|
centre_label('1,7;6,1', text or 'Transaction sent!') ..
|
|
'button[0.5,8;3.5,1;home;Go back]' ..
|
|
'button_exit[4,8;3.5,1;quit;Quit]'
|
|
end
|
|
|
|
-- "Transaction failed" screen
|
|
function formspecs.error(_, text)
|
|
return 'image[2.1,3;4.5,4.5;lurkcoin_error.png]' ..
|
|
centre_label('1,7;6,1', text or 'An error has occurred!') ..
|
|
'button[0.5,8;3.5,1;home;Go back]' ..
|
|
'button_exit[4,8;3.5,1;quit;Quit]'
|
|
end
|
|
|
|
-- Processing screen
|
|
formspecs.processing =
|
|
'image[2.1,3;4.5,4.5;lurkcoin_processing.png]' ..
|
|
centre_label('1,7;6,1', 'Processing your transaction...') ..
|
|
centre_label('1,8;6,1', 'This should only take a few seconds.')
|
|
|
|
-- A wrapper function
|
|
function lurkcoin.show_atm(name, page, params)
|
|
minetest.show_formspec(name, 'lurkcoin:atm', get_formspec(name,
|
|
page, params))
|
|
end
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, raw)
|
|
if formname ~= 'lurkcoin:atm' then return end
|
|
|
|
local name = player:get_player_name()
|
|
|
|
-- Do a shallow copy in case some other mod relies on "fields" not changing.
|
|
local fields = {}
|
|
for k, v in pairs(raw) do
|
|
if type(k) == 'string' and k:sub(1, 1) ~= '_' then
|
|
fields[k] = v
|
|
end
|
|
end
|
|
|
|
if withdrawls then
|
|
if fields.deposit then
|
|
return lurkcoin.show_atm(name, 'deposit')
|
|
elseif fields.wd then
|
|
local amount = tonumber(fields.withdraw)
|
|
if not amount or amount ~= amount or amount <= 0 then
|
|
return lurkcoin.show_atm(name, 'error',
|
|
'ERROR: Invalid number!')
|
|
end
|
|
local bal = lurkcoin.bank.getbal(name)
|
|
if amount > bal then
|
|
return lurkcoin.show_atm(name, 'error',
|
|
'ERROR: You cannot afford to do that!')
|
|
end
|
|
|
|
local note
|
|
for id, item in pairs(withdrawls) do
|
|
-- HACK: Multiply both amount and id by 1000 to avoid
|
|
-- floating-point bugs.
|
|
if (not note or id > note) and
|
|
(amount * 1000) % (id * 1000) == 0 then
|
|
local def = minetest.registered_items[item]
|
|
if def and amount / id <= (def.stack_max or 99) then
|
|
note = id
|
|
end
|
|
end
|
|
end
|
|
|
|
if not note then
|
|
return lurkcoin.show_atm(name, 'error',
|
|
'ERROR: I cannot store that amount of\nmoney in a ' ..
|
|
'single stack of notes/coins!')
|
|
end
|
|
|
|
local stack = ItemStack({
|
|
name = withdrawls[note],
|
|
count = (amount * 1000) / (note * 1000),
|
|
})
|
|
local inv = player:get_inventory()
|
|
if not inv:room_for_item('main', stack) then
|
|
return lurkcoin.show_atm(name, 'error',
|
|
'ERROR: You do not have enough inventory space!')
|
|
end
|
|
|
|
minetest.log('action', 'Player ' .. name .. ' withdraws ' ..
|
|
tostring(amount) .. 'Mg (' .. stack:to_string() ..
|
|
') from a lurkcoin ATM.')
|
|
lurkcoin.bank.subtract(name, amount)
|
|
inv:add_item('main', stack)
|
|
|
|
return lurkcoin.show_atm(name, 'success')
|
|
end
|
|
end
|
|
|
|
if fields.payconfirm and open_atms[name] and open_atms[name].pay then
|
|
local data = open_atms[name]
|
|
for k, v in pairs(data) do
|
|
if type(v) == 'number' then fields[k] = tonumber(fields[k]) end
|
|
if k ~= 'pay' and fields[k] ~= v then
|
|
if k ~= 'amount' then
|
|
k = 'target ' .. k
|
|
end
|
|
fields._err = 'The ' .. k ..
|
|
' was modified before pressing confirm!'
|
|
lurkcoin.show_atm(name, 'pay', fields)
|
|
return
|
|
end
|
|
end
|
|
lurkcoin.show_atm(name, 'processing')
|
|
lurkcoin.pay(name, data.user, data.server, data.amount,
|
|
function(success, msg)
|
|
local page = success and 'success' or 'error'
|
|
return lurkcoin.show_atm(name, page, msg)
|
|
end
|
|
)
|
|
elseif fields.payuser or fields.paysubmit or fields.payconfirm then
|
|
if fields.paysubmit then
|
|
local amount = tonumber(fields.amount)
|
|
if not amount or amount ~= amount or amount <= 0 then
|
|
fields._err = 'Invalid number!'
|
|
lurkcoin.show_atm(name, 'pay', fields)
|
|
return
|
|
elseif lurkcoin.bank.getbal(name) - amount < 0 then
|
|
fields._err = 'You cannot afford to do that!'
|
|
lurkcoin.show_atm(name, 'pay', fields)
|
|
return
|
|
elseif fields.server then
|
|
fields.server = fields.server:trim()
|
|
end
|
|
|
|
if fields.user then
|
|
fields.user = fields.user:trim()
|
|
end
|
|
|
|
if not fields.server or fields.server == '' then
|
|
fields.server = lurkcoin.server_name
|
|
end
|
|
|
|
open_atms[name] = {
|
|
pay = true,
|
|
user = fields.user or '',
|
|
server = fields.server,
|
|
amount = amount,
|
|
}
|
|
if open_atms[name].user == '' or (
|
|
fields.server == lurkcoin.server_name and
|
|
not lurkcoin.bank.user_exists(open_atms[name].user)
|
|
) then
|
|
fields._err = 'That user does not exist!'
|
|
lurkcoin.show_atm(name, 'pay', fields)
|
|
return
|
|
end
|
|
end
|
|
lurkcoin.show_atm(name, 'pay', fields)
|
|
elseif fields.home then
|
|
lurkcoin.show_atm(name, 'main')
|
|
elseif fields.quit then
|
|
open_atms[name] = nil
|
|
end
|
|
end)
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
open_atms[player:get_player_name()] = nil
|
|
end)
|