bank_accounts/computer.lua

265 lines
13 KiB
Lua

--[[
Teller Computer Node
--------------------
This file defines the Teller Computer node, which allows players with
the 'bank_teller' privilege to manage other players' accounts.
--]]
-- Uses the global 'pos_info' variable defined in atm.lua for consistency.
pos_data = {} -- Kept for historical reasons, but pos_info is used.
-- Shows the main form for the teller.
-- @param player: The teller player object.
-- @param pos: The position of the computer node.
-- @param customer_name: The name of the customer being managed.
local function show_teller_form(player, pos, customer_name)
local list_name = "nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z
local data = {balance = 0, credit = 0}
if customer_name and customer_name ~= "" and bank_accounts.player_has_account(customer_name) then
data = bank_accounts.get_account_data(customer_name)
end
local formspec = "size[8,9.5]" ..
"label[0,-.25;"..S("Deposit:").."]" ..
"list["..list_name..";ones;0,.25;1,1]" .. "list["..list_name..";fives;1.25,.25;1,1]" .. "list["..list_name..";tens;2.5,.25;1,1]" ..
"label[0.25,1.2;1 MG]" .. "label[1.5,1.2;5 MG]" .. "label[2.75,1.2;10 MG]" ..
"field[4,.5;4,1;playername;"..S("Player:")..";"..minetest.formspec_escape(customer_name or "").."]" ..
"field[.3,2.25;4,1;withdrawal;"..S("Withdraw:")..";]" ..
"field[.3,3.25;4,1;credit_debt;"..S("Credit Payment:")..";]" ..
"label[.5,4.5;"..S("Balance: @1", string.format("%.2f", data.balance).." MG").."]" ..
"label[.5,4.75;"..S("Credit Debt: @1", string.format("%.2f", data.credit).." MG").."]" ..
"button_exit[5.5,1;2,1;stats;"..S("Show Account").."]" ..
"button_exit[5.5,2;2,1;statement;"..S("Account Statement").."]" ..
"button_exit[5.5,3;2,1;wipe;"..S("Wipe Account").."]" ..
"button_exit[5.5,4;2,1;reset_pin;"..S("Reset PIN").."]" ..
"button_exit[3,5.5;2,1;exit;"..S("Cancel").."]" ..
"button_exit[5.5,5.5;2,1;enter;"..S("Enter").."]" ..
"list[current_player;main;0,7;8,2.5;]"
minetest.show_formspec(player:get_player_name(), "bank_accounts:teller", formspec)
end
-- Shows the account statement form for a given customer.
local function show_statement_form(player, pos, customer_name, account_type)
local data = bank_accounts.get_account_data(customer_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)))
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:statement@" .. customer_name
local formspec = "size[13,9]" ..
"label[0,0;"..S("Account Statement for @1",customer_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:get_player_name(), form_name, formspec)
end
-- Node definition for the Teller Computer.
minetest.register_node("bank_accounts:teller_computer", {
description = S("Bank Teller's Computer"),
drawtype = "mesh",
mesh = "computer.obj",
paramtype = "light",
paramtype2 = "facedir",
light_source = 5,
tiles = { {name="computer.png"},{name="computer_screen.png"} },
groups = {cracky=3, crumbly=3, oddly_breakable_by_hand=2},
selection_box = { type = "fixed", fixed = {{-.5,-.5,-.5,.5,.4,.2}} },
collision_box = { type = "fixed", fixed = {{-.5,-.5,-.5,.5,.4,.2}} },
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("ones", 1)
inv:set_size("fives", 1)
inv:set_size("tens", 1)
end,
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
pos_info = pos
local player_name = player:get_player_name()
if player:get_player_control().aux1 and minetest.check_player_privs(player_name, {server=true}) then
minetest.show_formspec(player_name, "bank_accounts:admin_teller",
"size[8,4]" ..
"field[.5,.5;4,1;search;"..S("Search:")..";]" ..
"button_exit[4.5,.22;2,1;search_button;"..S("Search").."]" ..
"label[.5,1.25;"..S("Player:").."]".. "label[3.5,1.25;"..S("Seized:").."]")
elseif minetest.check_player_privs(player_name, {bank_teller=true}) then
show_teller_form(player, pos, "")
else
minetest.chat_send_player(player_name, S("[Bank] Insufficient privileges."))
end
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if listname=="ones" and stack:get_name()~="currency:minegeld" then return 0 end
if listname=="fives" and stack:get_name()~="currency:minegeld_5" then return 0 end
if listname=="tens" and stack:get_name()~="currency:minegeld_10" then return 0 end
return stack:get_count()
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local inv=minetest.get_meta(pos):get_inventory()
local stack=inv:get_stack(from_list, from_index)
if to_list=="ones" and stack:get_name()~="currency:minegeld" then return 0 end
if to_list=="fives" and stack:get_name()~="currency:minegeld_5" then return 0 end
if to_list=="tens" and stack:get_name()~="currency:minegeld_10" then return 0 end
return count
end,
})
-- Central handler for all Teller and Admin forms.
minetest.register_on_player_receive_fields(function(player, formname, fields)
if not formname:find("bank_accounts:teller") and not formname:find("bank_accounts:admin_teller") and not formname:find("bank_accounts:statement") then
return
end
local player_name = player:get_player_name()
local pos = pos_info
if not pos then
return
end
-- Admin search form.
if formname == "bank_accounts:admin_teller" then
if fields.search_button and fields.search and fields.search ~= "" then
local search_name=fields.search
local status
if not bank_accounts.player_has_account(search_name) then
status=S("No Account")
elseif minetest.check_player_privs(search_name,{seized=true}) then
status=S("Yes")
else
status=S("No")
end
minetest.show_formspec(player_name,formname,
"size[8,4]"..
"field[.5,.5;4,1;search;"..S("Search:")..";"..minetest.formspec_escape(search_name).."]"..
"button_exit[4.5,.22;2,1;search_button;"..S("Search").."]"..
"label[.5,1.25;"..S("Player:").."]".."label[3.5,1.25;"..S("Seized:").."]"..
"label[.5,1.75;"..minetest.formspec_escape(search_name).."]".."label[3.5,1.75;"..status.."]")
end
return
end
-- Main teller form.
if formname == "bank_accounts:teller" then
local customer_name = fields.playername
-- This validation logic is quirky but proven to be stable in the user's environment.
if (not customer_name or customer_name=="") and not fields.exit then
minetest.chat_send_player(player_name, S("[Bank] Must enter a player name."))
return
end
if (customer_name and customer_name~="" and not bank_accounts.player_has_account(customer_name)) and not fields.exit then
minetest.chat_send_player(player_name, S("[Bank] Invalid player name entered."))
return
end
if fields.stats then
show_teller_form(player, pos, customer_name)
elseif fields.statement then
show_statement_form(player, pos, customer_name, "balance")
elseif fields.wipe then
bank_accounts.set_balance(customer_name, 0, "Teller Wipe", S("Teller: @1", player_name), player_name)
show_teller_form(player, pos, customer_name)
elseif fields.reset_pin then
bank_accounts.set_pin(customer_name, "0000")
show_teller_form(player, pos, customer_name)
elseif fields.enter then
local meta=minetest.get_meta(pos)
local inv=meta:get_inventory()
-- Deposit
local total_deposit=inv:get_stack("ones",1):get_count()+inv:get_stack("fives",1):get_count()*5+inv:get_stack("tens",1):get_count()*10
if total_deposit>0 then
bank_accounts.add_balance(customer_name,total_deposit,"Teller Deposit",S("Teller: @1",player_name),player_name)
end
-- Withdrawal
local withdrawal = tonumber(fields.withdrawal)
if withdrawal and withdrawal>0 then
if withdrawal%1~=0 then
minetest.chat_send_player(player_name,S("[Bank] Withdrawal amount must be a whole number."))
elseif bank_accounts.get_balance(customer_name)>=withdrawal then
bank_accounts.add_balance(customer_name,-withdrawal,"Teller Withdrawal",S("Teller: @1",player_name),player_name)
local tens=math.floor(withdrawal/10);local rem=withdrawal%10;local fives=math.floor(rem/5);local ones=rem%5
player:get_inventory():add_item("main",{name="currency:minegeld_10",count=tens})
player:get_inventory():add_item("main",{name="currency:minegeld_5",count=fives})
player:get_inventory():add_item("main",{name="currency:minegeld",count=ones})
else
minetest.chat_send_player(player_name,S("[Bank] Player has insufficient funds for withdrawal."))
end
end
-- Credit Payment
local credit_payment = normalize_and_tonumber(fields.credit_debt)
if credit_payment and credit_payment>0 then
if bank_accounts.get_balance(customer_name)>=credit_payment then
if bank_accounts.get_credit(customer_name)>=credit_payment then
bank_accounts.add_balance(customer_name,-credit_payment,"Teller Credit Payment",S("Teller: @1",player_name),player_name)
bank_accounts.add_credit(customer_name,-credit_payment,"Teller Credit Payment",S("Teller: @1",player_name),player_name)
else
minetest.chat_send_player(player_name,S("[Bank] Player does not have that much credit debt."))
end
else
minetest.chat_send_player(player_name,S("[Bank] Player has insufficient funds for credit payment."))
end
end
inv:set_stack("ones",1,nil)
inv:set_stack("fives",1,nil)
inv:set_stack("tens",1,nil)
show_teller_form(player,pos,customer_name)
end
end
-- Statement form.
if formname:find("bank_accounts:statement@") then
local customer_name=formname:match("bank_accounts:statement@(.*)")
if not customer_name then
return
end
if fields.view_balance then
show_statement_form(player, pos, customer_name, "balance")
elseif fields.view_credit then
show_statement_form(player, pos, customer_name, "credit")
elseif fields.back then
show_teller_form(player, pos, customer_name)
end
end
end)