325 lines
17 KiB
Lua
325 lines
17 KiB
Lua
-- atm2.lua (Doppelte Übersetzung im Kontoauszug behoben)
|
|
|
|
-- A global variable to store the position of the last used machine.
|
|
pos_info = {}
|
|
|
|
-- This table defines all valid currency items and their values.
|
|
local currency_values = {
|
|
{name = "currency:minegeld_100", value = 100},
|
|
{name = "currency:minegeld_50", value = 50},
|
|
{name = "currency:minegeld_10", value = 10},
|
|
{name = "currency:minegeld_5", value = 5},
|
|
{name = "currency:minegeld", value = 1},
|
|
{name = "currency:minegeld_cent_25", value = 0.25},
|
|
{name = "currency:minegeld_cent_10", value = 0.10},
|
|
{name = "currency:minegeld_cent_5", value = 0.05},
|
|
}
|
|
|
|
-- Helper function to round a number to a specific number of decimal places.
|
|
local function round(num, numDecimalPlaces)
|
|
local mult = 10^(numDecimalPlaces or 0)
|
|
return math.floor(num * mult + 0.5) / mult
|
|
end
|
|
|
|
-- Helper function to get the value of a single currency itemstack.
|
|
local function get_item_value(itemstack)
|
|
local name = itemstack:get_name()
|
|
for _, currency in ipairs(currency_values) do
|
|
if currency.name == name then
|
|
return currency.value
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
-- Helper function to convert a numeric amount back into a list of item stacks.
|
|
local function amount_to_itemstacks(amount)
|
|
local items = {}
|
|
local remaining_amount = amount
|
|
for _, currency in ipairs(currency_values) do
|
|
if remaining_amount >= currency.value then
|
|
local count = math.floor(remaining_amount / currency.value)
|
|
if count > 0 then
|
|
table.insert(items, {name = currency.name, count = count})
|
|
remaining_amount = remaining_amount - (count * currency.value)
|
|
remaining_amount = round(remaining_amount, 2)
|
|
if remaining_amount < 0.001 then break end
|
|
end
|
|
end
|
|
end
|
|
return items
|
|
end
|
|
|
|
-- Shows the account statement form.
|
|
local function show_atm2_statement_form(player, account_type)
|
|
local player_name = player:get_player_name()
|
|
local data = bank_accounts.get_account_data(player_name)
|
|
local history = data.history; local lines = {}; local current_total_label = ""
|
|
table.insert(lines, minetest.formspec_escape(string.format("%-11s | %-13s | %-13s | %-20s | %-20s | %s", S("Date"), S("Amount"), S("New Balance"), S("Method"), S("Purpose"), S("Partner"))))
|
|
table.insert(lines, minetest.formspec_escape("-----------------------------------------------------------------------------------------"))
|
|
if history then for _, t in ipairs(history) do if t.account == account_type then
|
|
local parts = {}; table.insert(parts, string.format("%-11s", os.date("%Y-%m-%d", t.timestamp))); table.insert(parts, string.format("%12s", string.format("%+.2f", t.amount) .. " MG")); table.insert(parts, string.format("%12s", string.format("%.2f", t.new_total).." MG")); table.insert(parts, string.format("%-20s", S(t.type)))
|
|
|
|
-- KORREKTUR: 'purpose' wird direkt angezeigt, nicht erneut übersetzt.
|
|
local purpose = t.purpose or ""
|
|
local partner = t.other or ""
|
|
|
|
if purpose ~= "" or partner ~= "" then table.insert(parts, string.format("%-20s", purpose)); if partner ~= "" then table.insert(parts, partner) end end
|
|
table.insert(lines, minetest.formspec_escape(table.concat(parts, " | ")))
|
|
end end end
|
|
if account_type == "balance" then current_total_label = S("Current Balance: @1", string.format("%.2f", data.balance) .. " MG") else current_total_label = S("Current Credit Debt: @1", string.format("%.2f", data.credit) .. " MG") end
|
|
local form_name = "bank_accounts:atm2_statement@" .. player_name
|
|
local formspec = "size[13,9]" .. "label[0,0;"..S("Account Statement for @1", player_name).."]" .. "button_exit[0,0.5;2,1;view_balance;"..S("Balance").."]" .. "button_exit[2,0.5;2,1;view_credit;"..S("Credit").."]" .. "textlist[0,1.2;13,7;statement_list;"..table.concat(lines, ",").."]".. "label[0,8.4;"..current_total_label.."]".. "button_exit[11,8.4;2,1;back;"..S("Back").."]"
|
|
minetest.show_formspec(player_name, form_name, formspec)
|
|
end
|
|
|
|
-- Shows the main menu for the ATM Mark II.
|
|
local function atm2_main_form(player, pos)
|
|
local player_name = player:get_player_name()
|
|
local data = bank_accounts.get_account_data(player_name)
|
|
local next_rate = 0
|
|
if data.credit > 0 then
|
|
next_rate = math.max(1, math.floor(data.credit * 0.04))
|
|
end
|
|
|
|
minetest.show_formspec(player_name, "bank_accounts:atm2_options",
|
|
"size[8,8]" ..
|
|
"button_exit[1,.5;2,1;withdrawal;"..S("Withdraw").."]" ..
|
|
"button_exit[1,1.5;2,1;deposit;"..S("Deposit").."]" ..
|
|
"button_exit[1,2.5;3,1;pay_credit;"..S("Pay Credit Debt").."]" ..
|
|
"label[4.5,0.8;"..S("Account Balance: @1", string.format("%.2f", data.balance) .. " MG").."]" ..
|
|
"label[4.5,1.3;"..S("Total Credit Debt: @1", string.format("%.2f", data.credit) .. " MG").."]" ..
|
|
"label[4.5,1.8;"..S("Next Rate: @1", tostring(next_rate) .. " MG").."]" ..
|
|
"button_exit[4.5,2.5;3,1;statement;"..S("Account Statement").."]" ..
|
|
"button_exit[1,3.5;3,1;credit_card;"..S("Get Credit Card").."]" ..
|
|
"button_exit[1,4.5;3,1;debit_card;"..S("Get Debit Card").."]" ..
|
|
"button_exit[5,7;2,1;exit;"..S("Close").."]")
|
|
end
|
|
|
|
-- Shows the new deposit form with a single "swallow" slot.
|
|
local function show_atm2_deposit_form(player, pos)
|
|
local player_name = player:get_player_name()
|
|
local meta = minetest.get_meta(pos)
|
|
local deposited_amount = tonumber(meta:get_string("deposit_buffer") or "0")
|
|
local list_name = "nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z
|
|
|
|
minetest.show_formspec(player_name, "bank_accounts:atm2_deposit",
|
|
"size[8,9]"..
|
|
"label[0,0.5;"..S("Place coins and notes in the slot below.").."]"..
|
|
"list["..list_name..";deposit_slot;3.5,1.5;1,1;]"..
|
|
"label[0,3;"..S("Currently Deposited: @1 MG", string.format("%.2f", deposited_amount)).."]"..
|
|
"button_exit[0.5,4;3,1;confirm_deposit;"..S("Deposit").."]"..
|
|
"button_exit[4.5,4;3,1;return_deposit;"..S("Return").."]"..
|
|
"list[current_player;main;0,5;8,4;]")
|
|
end
|
|
|
|
minetest.register_node("bank_accounts:atm2", {
|
|
description = S("ATM Mark II"),
|
|
drawtype = "mesh",
|
|
mesh = "atm.obj",
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
tiles = {"atm2_col.png"},
|
|
groups = {cracky=3, crumbly=3, oddly_breakable_by_hand=2},
|
|
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
inv:set_size("deposit_slot", 1)
|
|
for _, currency in ipairs(currency_values) do
|
|
inv:set_size(currency.name, 1)
|
|
end
|
|
end,
|
|
|
|
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
|
pos_info = pos
|
|
if itemstack:get_name() ~= "bank_accounts:atm_card" then
|
|
minetest.chat_send_player(player:get_player_name(), S("[ATM] Must use ATM card."))
|
|
return
|
|
end
|
|
minetest.show_formspec(player:get_player_name(), "bank_accounts:atm2_home",
|
|
"size[8,8]" .. "pwdfield[2,4;4,1;fourdigitpin;"..S("Four Digit Pin:").."]" ..
|
|
"button_exit[5,6;2,1;enter;"..S("Enter").."]" .. "button_exit[3,6;2,1;exit;"..S("Cancel").."]")
|
|
end,
|
|
|
|
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
if listname ~= "deposit_slot" then return end
|
|
|
|
local value = get_item_value(stack)
|
|
if value > 0 then
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local current_buffer = tonumber(meta:get_string("deposit_buffer") or "0")
|
|
meta:set_string("deposit_buffer", tostring(current_buffer + value * stack:get_count()))
|
|
inv:set_stack(listname, index, "")
|
|
minetest.after(0, show_atm2_deposit_form, player, pos)
|
|
end
|
|
end,
|
|
})
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
if not formname:find("bank_accounts:atm2") then return end
|
|
|
|
local player_name = player:get_player_name()
|
|
local pos = pos_info
|
|
if not pos then return end
|
|
local meta = minetest.get_meta(pos)
|
|
|
|
local function on_fail()
|
|
minetest.chat_send_player(player_name, S("[Bank] System is busy, please try again in a moment."))
|
|
atm2_main_form(player, pos)
|
|
end
|
|
|
|
if formname == "bank_accounts:atm2_home" then
|
|
if fields.enter then if bank_accounts.get_pin(player_name) == fields.fourdigitpin then atm2_main_form(player, pos) else minetest.chat_send_player(player_name, S("[ATM] Invalid Pin.")) end end
|
|
|
|
elseif formname == "bank_accounts:atm2_options" then
|
|
if fields.deposit then
|
|
meta:set_string("deposit_buffer", "0")
|
|
show_atm2_deposit_form(player, pos)
|
|
elseif fields.withdrawal then
|
|
minetest.show_formspec(player_name, "bank_accounts:atm2_withdrawal", "size[8,8]" .. "field[2,4;5,1;money;"..S("Amount:")..";]" .. "button_exit[3,6;2,1;exit;"..S("Cancel").."]" .. "button_exit[5,6;2,1;enter;"..S("Enter").."]")
|
|
elseif fields.pay_credit then
|
|
local credit_debt = bank_accounts.get_credit(player_name)
|
|
local next_rate = 0
|
|
if credit_debt > 0 then
|
|
next_rate = math.max(1, math.floor(credit_debt * 0.04))
|
|
end
|
|
minetest.show_formspec(player_name, "bank_accounts:atm2_pay_credit", "size[8,8]" ..
|
|
"label[0,0.5;"..S("Total Credit Debt: @1", string.format("%.2f", credit_debt).." MG").."]"..
|
|
"label[0,2;"..S("Next calculated rate:").."]"..
|
|
"button_exit[0,2.5;3.5,1;pay_rate;"..S("Pay Next Rate (@1 MG)", next_rate).."]" ..
|
|
"label[4.2,2;"..S("Or enter a custom amount:").."]" ..
|
|
"field[4.5,2.8;3.5,1;custom_amount;"..""..";]" ..
|
|
"button_exit[4.2,3.5;3.5,1;pay_custom;"..S("Pay Custom Amount").."]" ..
|
|
"button_exit[3,7;2,1;exit;"..S("Cancel").."]")
|
|
elseif fields.statement then
|
|
show_atm2_statement_form(player, "balance")
|
|
elseif fields.credit_card then
|
|
player:get_inventory():add_item("main", "bank_accounts:credit_card")
|
|
atm2_main_form(player, pos)
|
|
elseif fields.debit_card then
|
|
player:get_inventory():add_item("main", "bank_accounts:debit_card")
|
|
atm2_main_form(player, pos)
|
|
end
|
|
|
|
elseif formname == "bank_accounts:atm2_deposit" then
|
|
local buffer = tonumber(meta:get_string("deposit_buffer") or "0")
|
|
if fields.confirm_deposit then
|
|
if buffer > 0 then
|
|
if not bank_accounts.add_balance(player_name, buffer, "ATM Deposit", "", "") then on_fail(); return end
|
|
minetest.chat_send_player(player_name, S("[ATM] Deposited @1.", string.format("%.2f", buffer).." MG"))
|
|
end
|
|
elseif fields.return_deposit or fields.quit then
|
|
if buffer > 0 then
|
|
local items_to_return = amount_to_itemstacks(buffer)
|
|
for _, item in ipairs(items_to_return) do
|
|
player:get_inventory():add_item("main", item)
|
|
end
|
|
minetest.chat_send_player(player_name, S("[ATM] Your deposit was returned to your inventory."))
|
|
end
|
|
end
|
|
meta:set_string("deposit_buffer", "0")
|
|
if not fields.quit then
|
|
atm2_main_form(player, pos)
|
|
end
|
|
|
|
elseif formname == "bank_accounts:atm2_withdrawal" then
|
|
if fields.enter then
|
|
local requested_amount = normalize_and_tonumber(fields.money)
|
|
if requested_amount and requested_amount > 0 and bank_accounts.get_balance(player_name) >= requested_amount then
|
|
local items_to_dispense = amount_to_itemstacks(requested_amount)
|
|
local dispensable_amount = 0
|
|
for _, item_data in ipairs(items_to_dispense) do
|
|
for _, currency in ipairs(currency_values) do
|
|
if currency.name == item_data.name then
|
|
dispensable_amount = dispensable_amount + (currency.value * item_data.count)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if not bank_accounts.add_balance(player_name, -dispensable_amount, "ATM Withdrawal", "", "") then on_fail(); return end
|
|
local inv = meta:get_inventory()
|
|
local list_elements = ""
|
|
local warning_label = ""
|
|
if dispensable_amount < requested_amount then
|
|
warning_label = "label[0,1;`"..S("Note: Amount was rounded down to the nearest dispensable value.").."`]" .. "style[label;color=yellow]"
|
|
end
|
|
for i, item in ipairs(items_to_dispense) do
|
|
inv:set_stack(item.name, 1, {name = item.name, count = item.count})
|
|
local x_pos = i - 1
|
|
list_elements = list_elements .. "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";"..item.name..";"..x_pos..",2;1,1;]"
|
|
end
|
|
minetest.show_formspec(player_name, "bank_accounts:atm2_withdrawal_output",
|
|
"size[8,4]" ..
|
|
"label[0,0.5;"..S("Please take your money (@1 MG):", string.format("%.2f", dispensable_amount)).."]" ..
|
|
warning_label .. list_elements ..
|
|
"button_exit[2.5,3.25;3,1;take_all;"..S("Take All").."]")
|
|
else
|
|
minetest.chat_send_player(player_name, S("[ATM] Insufficient funds or invalid amount."))
|
|
atm2_main_form(player, pos)
|
|
end
|
|
else
|
|
atm2_main_form(player, pos)
|
|
end
|
|
|
|
elseif formname == "bank_accounts:atm2_withdrawal_output" then
|
|
local inv = meta:get_inventory()
|
|
local player_inv = player:get_inventory()
|
|
if fields.take_all or fields.quit then
|
|
local any_left = false
|
|
for _, currency in ipairs(currency_values) do
|
|
local stack = inv:get_stack(currency.name, 1)
|
|
if not stack:is_empty() then
|
|
player_inv:add_item("main", stack)
|
|
inv:set_stack(currency.name, 1, "")
|
|
any_left = true
|
|
end
|
|
end
|
|
if any_left and fields.quit then
|
|
minetest.chat_send_player(player_name, S("[ATM] Withdrawn money has been moved to your inventory."))
|
|
end
|
|
end
|
|
if not fields.quit then
|
|
atm2_main_form(player, pos)
|
|
end
|
|
|
|
elseif formname == "bank_accounts:atm2_pay_credit" then
|
|
local amount_to_pay = 0; local is_valid = false
|
|
if fields.pay_rate then
|
|
local credit_debt = bank_accounts.get_credit(player_name)
|
|
if credit_debt > 0 then
|
|
amount_to_pay = math.max(1, math.floor(credit_debt * 0.04))
|
|
end
|
|
is_valid = true
|
|
elseif fields.pay_custom then
|
|
amount_to_pay = normalize_and_tonumber(fields.custom_amount)
|
|
local min_payment = 0
|
|
local credit_debt = bank_accounts.get_credit(player_name)
|
|
if credit_debt > 0 then
|
|
min_payment = math.max(1, math.floor(credit_debt * 0.04))
|
|
end
|
|
if not amount_to_pay or amount_to_pay < min_payment then
|
|
minetest.chat_send_player(player_name, S("[ATM] You must pay at least the minimum monthly rate."))
|
|
else
|
|
is_valid = true
|
|
end
|
|
end
|
|
|
|
if is_valid and amount_to_pay and amount_to_pay > 0 then
|
|
if bank_accounts.get_balance(player_name) < amount_to_pay then minetest.chat_send_player(player_name, S("[ATM] Insufficient funds."))
|
|
elseif bank_accounts.get_credit(player_name) < amount_to_pay then minetest.chat_send_player(player_name, S("[ATM] You don't have that much credit debt."))
|
|
else
|
|
if not bank_accounts.add_balance(player_name, -amount_to_pay, "Rate Payment", "", "") then on_fail(); return end
|
|
if not bank_accounts.add_credit(player_name, -amount_to_pay, "Rate Payment", "", "") then on_fail(); return end
|
|
minetest.chat_send_player(player_name, S("[ATM] Paid @1 of credit debt.", string.format("%.2f", amount_to_pay).." MG"))
|
|
end
|
|
elseif fields.pay_custom and not is_valid then
|
|
elseif not fields.exit and not fields.quit and (fields.pay_rate or fields.pay_custom) then minetest.chat_send_player(player_name, S("[ATM] Invalid amount.")) end
|
|
atm2_main_form(player, pos)
|
|
|
|
elseif formname:find("bank_accounts:atm2_statement@") then
|
|
local target_name = formname:match("bank_accounts:atm2_statement@(.*)"); if not target_name then return end
|
|
if fields.back then atm2_main_form(player, pos) else show_atm2_statement_form(player, fields.view_credit and "credit" or "balance") end
|
|
end
|
|
end)
|