-- computer2.lua (Fixed translation error and search layout) -- Uses the global 'pos_info' variable for consistency. -- This table will temporarily store which customer a teller is currently serving for the deposit form. local teller_current_customer = {} -- Copy currency definitions and helper functions from atm2.lua 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}, } local function round(num, numDecimalPlaces) local mult = 10^(numDecimalPlaces or 0) return math.floor(num * mult + 0.5) / mult end local function get_item_value(itemstack) local name = itemstack:get_name(); for _, c in ipairs(currency_values) do if c.name == name then return c.value end end; return 0 end local function amount_to_itemstacks(amount) local items = {}; local rem = amount; for _, c in ipairs(currency_values) do if rem >= c.value then local count = math.floor(rem / c.value); if count > 0 then table.insert(items, {name=c.name, count=count}); rem = rem - (count*c.value); rem = round(rem, 2); if rem < 0.001 then break end end end end; return items end -- Formspec for the initial customer selection screen local function show_teller2_selection_form(player, pos, search_term) local all_accounts = bank_accounts.get_all_data() local customer_list = {} for name, balance in pairs(all_accounts.balance) do if not search_term or search_term == "" or name:lower():find(search_term:lower(), 1, true) then table.insert(customer_list, minetest.formspec_escape(name)) end end table.sort(customer_list) minetest.show_formspec(player:get_player_name(), "bank_accounts:teller2_select", "size[8,9.5]".. "label[0,0;"..S("Select Customer Account").."]".. -- KORRIGIERTES LAYOUT: Label entfernt, Feld neu positioniert "field[0.29,0.85;5.8,1;search_name;;"..minetest.formspec_escape(search_term or "").."]".. "button_exit[6.2,0.55;1.8,1;search;"..S("Search").."]".. "table[0,1.8;8,7;customer_list;"..table.concat(customer_list, ",").."]" ) end -- Formspec for viewing and managing a selected customer's account local function show_teller2_customer_view(player, pos, customer_name) local data = bank_accounts.get_account_data(customer_name) minetest.show_formspec(player:get_player_name(), "bank_accounts:teller2_view@"..customer_name, "size[8,9]".. "label[0,0;"..S("Managing Account: @1", customer_name).."]".. "label[0.5,1;"..S("Balance: @1", string.format("%.2f", data.balance).." MG").."]".. "label[4.5,1;"..S("Credit Debt: @1", string.format("%.2f", data.credit).." MG").."]".. "container[0,0.5;8,1.25]".. "container_end[]".. "button_exit[0.5,2.5;3,1;deposit;"..S("Deposit").."]".. "button_exit[4.5,2.5;3,1;withdrawal;"..S("Withdrawal").."]".. "button_exit[0.5,4;3,1;pay_credit;"..S("Pay Credit Debt").."]".. "button_exit[4.5,4;3,1;statement;"..S("Account Statement").."]".. "button_exit[0.5,5.5;3,1;reset_pin;"..S("Reset PIN").."]".. "button_exit[4.5,5.5;3,1;wipe;"..S("Wipe Account").."]".. "button_exit[0.5,7;3,1;back_to_search;"..S("Back to Customer List").."]" ) end -- Formspec for the new deposit form local function show_teller2_deposit_form(player, pos, customer_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:get_player_name(), "bank_accounts:teller2_deposit@"..customer_name, "size[8,9]".. "label[0,0.5;"..S("Deposit for @1", customer_name).."]".. "label[0,1.5;"..S("Place coins and notes in the slot below.").."]".. "list["..list_name..";deposit_slot;3.5,2.5;1,1;]".. "label[0,4;"..S("Currently Deposited: @1 MG", string.format("%.2f", deposited_amount)).."]".. "button_exit[0.5,5;3,1;confirm_deposit;"..S("Deposit").."]".. "button_exit[4.5,5;3,1;return_deposit;"..S("Return").."]".. "list[current_player;main;0,6;8,3;]") end -- Formspec for the account statement 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))) -- 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:teller2_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 minetest.register_node("bank_accounts:teller_computer2", { description = S("Teller Computer Mark II"), drawtype = "mesh", mesh = "computer.obj", paramtype = "light", paramtype2 = "facedir", tiles = {"computer2.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 minetest.check_player_privs(player:get_player_name(), {bank_teller=true, server=true}) then show_teller2_selection_form(player, pos, "") else minetest.chat_send_player(player:get_player_name(), S("[Bank] Insufficient privileges.")) end 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, "") local customer_name = teller_current_customer[player:get_player_name()] if customer_name then minetest.after(0, show_teller2_deposit_form, player, pos, customer_name) end end end, }) minetest.register_on_player_receive_fields(function(player, formname, fields) if not formname:find("bank_accounts:teller2_") 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.")) end -- Customer Selection Form if formname == "bank_accounts:teller2_select" then local customer_name = fields.search_name or "" if fields.customer_list then local event = minetest.explode_table_event(fields.customer_list) if event.type == "CHG" or event.type == "DCL" then local all_accounts = bank_accounts.get_all_data() local customer_list = {} for name, _ in pairs(all_accounts.balance) do if not fields.search_name or fields.search_name == "" or name:lower():find(fields.search_name:lower(), 1, true) then table.insert(customer_list, name) end end table.sort(customer_list) customer_name = customer_list[event.row] end end if customer_name and bank_accounts.player_has_account(customer_name) then show_teller2_customer_view(player, pos, customer_name) else show_teller2_selection_form(player, pos, fields.search_name) end -- Customer View Form elseif formname:find("bank_accounts:teller2_view@") then local customer_name = formname:match("bank_accounts:teller2_view@(.*)") if not customer_name then return end if fields.back_to_search then show_teller2_selection_form(player, pos, "") elseif fields.statement then show_statement_form(player, pos, customer_name, "balance") elseif fields.reset_pin then minetest.show_formspec(player_name, "bank_accounts:teller2_reset_pin_confirm@"..customer_name, "size[8,4]".."label[1,1;"..S("Really reset PIN for @1?", customer_name).."]".."button_exit[1,3;2.5,1;yes_reset;"..S("Yes").."]".."button_exit[4.5,3;2.5,1;no;"..S("No").."]") elseif fields.wipe then minetest.show_formspec(player_name, "bank_accounts:teller2_wipe_confirm@"..customer_name, "size[8,4]".."label[1,1;"..S("Really wipe account for @1?", customer_name).."]".."button_exit[1,3;2.5,1;yes_wipe;"..S("Yes").."]".."button_exit[4.5,3;2.5,1;no;"..S("No").."]") elseif fields.deposit then meta:set_string("deposit_buffer", "0") teller_current_customer[player_name] = customer_name show_teller2_deposit_form(player, pos, customer_name) elseif fields.withdrawal then minetest.show_formspec(player_name, "bank_accounts:teller2_withdrawal@"..customer_name, "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(customer_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:teller2_pay_credit@"..customer_name, "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").."]") end -- Confirmation Forms elseif formname:find("bank_accounts:teller2_reset_pin_confirm@") then local customer_name = formname:match("bank_accounts:teller2_reset_pin_confirm@(.*)") if fields.yes_reset then if not bank_accounts.set_pin(customer_name, "0000") then on_fail() else minetest.chat_send_player(player_name, S("[Bank] Player's pin successfully reset!")) end end show_teller2_customer_view(player, pos, customer_name) elseif formname:find("bank_accounts:teller2_wipe_confirm@") then local customer_name = formname:match("bank_accounts:teller2_wipe_confirm@(.*)") if fields.yes_wipe then if not bank_accounts.set_balance(customer_name, 0, "Teller Wipe", S("Teller: @1", player_name), player_name) then on_fail() else minetest.chat_send_player(player_name, S("[Bank] Account successfully wiped!")) end end show_teller2_customer_view(player, pos, customer_name) -- Deposit Form elseif formname:find("bank_accounts:teller2_deposit@") then local customer_name = formname:match("bank_accounts:teller2_deposit@(.*)") 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(customer_name, buffer, "Teller Deposit", S("Teller: @1", player_name), player_name) then on_fail() else minetest.chat_send_player(player_name, S("[Bank] Deposited @1 for @2.", string.format("%.2f", buffer), customer_name)) end 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 if fields.quit then minetest.chat_send_player(player_name, S("[Bank] The deposit was cancelled and returned to your inventory.")) end end end meta:set_string("deposit_buffer", "0") teller_current_customer[player_name] = nil if not fields.quit then show_teller2_customer_view(player, pos, customer_name) end -- Withdrawal Forms elseif formname:find("bank_accounts:teller2_withdrawal@") then local customer_name = formname:match("bank_accounts:teller2_withdrawal@(.*)") if fields.enter then local requested_amount = normalize_and_tonumber(fields.money) if requested_amount and requested_amount > 0 and bank_accounts.get_balance(customer_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(customer_name, -dispensable_amount, "Teller Withdrawal", S("Teller: @1", player_name), player_name) 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...").."`]" .. "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}); list_elements = list_elements .. "list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";"..item.name..";"..(i-1)..",2;1,1;]" end minetest.show_formspec(player_name, "bank_accounts:teller2_withdrawal_output@"..customer_name, "size[8,4]" .. "label[0,0.5;"..S("Teller: Give these items to @1 (@2 MG):", customer_name, 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("[Bank] Insufficient funds or invalid amount.")); show_teller2_customer_view(player, pos, customer_name) end else show_teller2_customer_view(player, pos, customer_name) end elseif formname:find("bank_accounts:teller2_withdrawal_output@") then local customer_name = formname:match("bank_accounts:teller2_withdrawal_output@(.*)") if fields.take_all or fields.quit then local inv = meta:get_inventory(); 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 if fields.take_all then player:get_inventory():add_item("main", stack) else local success = bank_accounts.add_balance(customer_name, get_item_value(stack) * stack:get_count(), "Teller Withdraw Cancel", S("Cancelled by teller @1", player_name), player_name); if success then any_left = true end end inv:set_stack(currency.name, 1, "") end end if any_left and fields.quit then minetest.chat_send_player(player_name, S("[Bank] Withdrawn money was returned to the customer's account.")) end end if not fields.quit then show_teller2_customer_view(player, pos, customer_name) end -- Credit Payment Form elseif formname:find("bank_accounts:teller2_pay_credit@") then local customer_name = formname:match("bank_accounts:teller2_pay_credit@(.*)") local amount_to_pay = 0; local is_valid = false if fields.pay_rate then local credit_debt = bank_accounts.get_credit(customer_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(customer_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(customer_name) < amount_to_pay then minetest.chat_send_player(player_name, S("[Bank] Insufficient funds.")) elseif bank_accounts.get_credit(customer_name) < amount_to_pay then minetest.chat_send_player(player_name, S("[Bank] Player does not have that much credit debt.")) else if not bank_accounts.add_balance(customer_name, -amount_to_pay, "Teller Credit Payment", S("Teller: @1", player_name), player_name) then on_fail(); return end if not bank_accounts.add_credit(customer_name, -amount_to_pay, "Teller Credit Payment", S("Teller: @1", player_name), player_name) then on_fail(); return end 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("[Bank] Invalid amount.")) end show_teller2_customer_view(player, pos, customer_name) -- Statement Form elseif formname:find("bank_accounts:teller2_statement@") then local customer_name = formname:match("bank_accounts:teller2_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_teller2_customer_view(player, pos, customer_name) end end end)