emeraldbank/functions.lua
2023-08-29 21:23:30 +02:00

540 lines
20 KiB
Lua

-- Copyright (C) 2021, 2023 Ale
-- This file is part of Emeraldbank Minetest Mod.
-- Emeraldbank is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
-- Emeraldbank is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Affero General Public License for more details.
-- You should have received a copy of the GNU Affero General Public License
-- along with Emeraldbank. If not, see <https://www.gnu.org/licenses/>.
local S = core.get_translator(core.get_current_modname())
function atm.ensure_init(name)
-- Ensure the atm account for the placer specified by name exists
atm.readaccounts()
if not atm.balance[name] then
atm.balance[name] = atm.startbalance
end
end
-- banking accounts storage
function atm.readaccounts ()
local b = atm.balance
local file = io.open(atm.pth, "r")
if file then
repeat
local balance = file:read("*n")
if balance == nil then
break
end
local name = file:read("*l")
b[name:sub(2)] = balance
until file:read(0) == nil
io.close(file)
end
end
function atm.saveaccounts()
if not atm.balance then
return
end
local data = {}
for k, v in pairs(atm.balance) do
table.insert(data, string.format("%d %s\n", v, k))
end
local output = io.open(atm.pth, "w")
output:write(table.concat(data))
io.close(output)
end
function emeraldbank.update_account()
atm.readaccounts()
for _, player in ipairs(core.get_connected_players()) do
if not player or player.is_fake_player then return end
local meta = player:get_meta()
local bankemeralds = meta:get_int("emeraldbank:emerald")
local name = player:get_player_name()
if atm.balance[name] then
atm.balance[name] = math.floor(atm.balance[name] + bankemeralds)
else
atm.balance[name] = bankemeralds
end
meta:set_int("emeraldbank:emerald", 0)
-- backup
meta:set_int("emeraldbank:emerald_backup", bankemeralds)
end
atm.saveaccounts()
end
function emeraldbank.add_emeralds(player, num)
atm.readaccounts()
if not player then return false end
local meta = player:get_meta()
local name = player:get_player_name()
if num then
if atm.balance[name] then
atm.balance[name] = math.floor(atm.balance[name] + num)
else
atm.balance[name] = num
end
mcl_title.set(player, "actionbar", {text=S("Emeralds in Bank: @1", atm.balance[name]), color="yellow"})
atm.saveaccounts()
return true
end
return false
end
function emeraldbank.get_stonks(pos)
local meta = core.get_meta(pos)
local owner = meta:get_string("owner")
local player = core.get_player_by_name(owner)
local is_online = core.player_exists(owner)
local stonks = meta:get_int("stonks")
if not player or player.is_fake_player then return end
if is_online and stonks > 0 then
core.sound_play("cash", {
to_player = owner,
gain = 1.0,
fade = 0.0,
pitch = 1.0,
})
emeraldbank.add_emeralds(player, stonks)
meta:set_int("stonks", 0)
core.chat_send_player(owner, S("You've earned @1 Emeralds with your shops.", stonks))
end
end
function emeraldbank.transfer_emeralds(player1, player2, num)
atm.readaccounts()
local name = player1:get_player_name()
local name2 = player2:get_player_name()
local bankemeralds1 = atm.balance[name]
if num > 0 then
if bankemeralds1 and bankemeralds1 >= num then
core.chat_send_player(name, S("Pay Successfully! You have transferred @1 Emeralds." , num))
core.chat_send_player(name2, S("Pay Successfully! You've gotten @1 Emeralds.", num))
if core.get_modpath("irc") then
irc.say(name.." has transferred "..num.." emeralds to "..name2)
end
if core.get_modpath("yl_matterbridge") then
yl_matterbridge.send_to_bridge("", name.." has transferred "..num.." emeralds to "..name2)
end
emeraldbank.add_emeralds(player1, -num)
emeraldbank.add_emeralds(player2, num)
core.sound_play("cash", {
to_player = name2,
gain = 1.0,
fade = 0.0,
pitch = 1.0,
})
else
core.chat_send_player(name, S("Not enough Emeralds in your account"))
end
else
core.chat_send_player(name, S("Invalid pay"))
end
end
-- wire transfer data storage
function atm.read_transactions()
local file = io.open(atm.pth_wt, "r")
if file then
local data = file:read("*all")
atm.completed_transactions = minetest.deserialize(data)
end
end
function atm.write_transactions()
if not atm.completed_transactions then
return
end
local file = io.open(atm.pth_wt, "w")
local data = minetest.serialize(atm.completed_transactions)
file:write(data)
io.close(file)
end
function emeraldbank.upgrade_shop(pos)
local oldnode = core.get_node(pos)
local old_meta = core.get_meta(pos)
local old_meta_table = core.get_meta(pos):to_table()
local nodename = core.get_node(pos).name
-- set the new shop node
core.swap_node(pos, {name = "fancy_vend:player_vendor"})
-- setup the new shop node
-- Set variables for access later (for various checks, etc.)
local owner = old_meta:get_string("owner") or ""
pos.y = pos.y + 1
local above_node = minetest.get_node(pos).name
-- If node above is air or the display node, and it is not protected, attempt to place the vendor. If vendor sucessfully places, place display node above, otherwise alert the user
if (minetest.registered_nodes[above_node].buildable_to or above_node == "fancy_vend:display_node") and not minetest.is_protected(pos, owner) then
if above_node ~= "fancy_vend:display_node" then
minetest.set_node(pos, minetest.registered_nodes["fancy_vend:display_node"])
end
-- Set owner
local meta = minetest.get_meta(pos)
meta:set_string("owner", owner or "")
-- Set default meta
meta:set_string("log", minetest.serialize({"Vendor placed by "..owner,}))
emeraldbank.reset_vendor_settings(pos)
emeraldbank.refresh_vendor(pos)
else
minetest.chat_send_player(owner, S("Vendors require 2 nodes of space."))
end
if minetest.get_modpath("pipeworks") then
pipeworks.after_place(pos)
end
-- copy old metadata in new node
core.get_meta(pos):from_table(old_meta_table)
-- new node
local node = core.get_node(pos)
local meta = core.get_meta(pos)
local count = meta:get_int("count")
local price = meta:get_int("price")
local shop_item = meta:get_string("shop_item")
local minv = meta:get_inventory()
local settings = emeraldbank.get_vendor_settings(pos)
settings.input_item = "mcl_core:emerald"
settings.input_item_qty = price
settings.output_item = shop_item
settings.output_item_qty = count
emeraldbank.refresh_vendor(pos)
end
function atm.showform (player)
atm.ensure_init(player:get_player_name())
local formspec =
"size[9,8.75]"..
"label[0.5,0;"..S("Your account balance: $@1", atm.balance[player:get_player_name()]).."]" ..
"label[0.5,0.75;"..S("Deposit:").."]" ..
-- "label[0.5,0.75;1s]" ..
-- "label[1.5,0.75;5s]" ..
-- "label[2.5,0.75;10s]" ..
-- "label[3.5,0.75;50s]" ..
-- "label[4.5,0.75;100s]" ..
"label[7.5,0.75;"..S("Withdraw:").."]" ..
-- "label[6.5,0.75;1s]" ..
-- "label[7.5,0.75;5s]" ..
-- "label[8.5,0.75;10s]" ..
-- "label[9.5,0.75;50s]" ..
-- "label[10.5,0.75;100s]" ..
"item_image_button[0.5,1.25;1,1;".. "mcl_core:emerald" ..";i1;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[1.5,1.25;1,1;".. "currency:minegeld_5" ..";i5;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[2.5,1.25;1,1;".. "currency:minegeld_10" ..";i10;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[3.5,1.25;1,1;".. "currency:minegeld_50" ..";i50;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[4.5,1.25;1,1;".. "currency:minegeld_100" ..";i100;\n\n\b\b\b\b\b" .. "x1" .."]" ..
"item_image_button[7.5,1.25;1,1;".. "mcl_core:emerald" ..";i-1;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[7.5,1.25;1,1;".. "currency:minegeld_5" ..";i-5;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[8.5,1.25;1,1;".. "currency:minegeld_10" ..";i-10;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[9.5,1.25;1,1;".. "currency:minegeld_50" ..";i-50;\n\n\b\b\b\b\b" .. "x1" .."]" ..
-- "item_image_button[10.5,1.25;1,1;".. "currency:minegeld_100" ..";i-100;\n\n\b\b\b\b\b" .. "x1" .."]" ..
"item_image_button[0.5,2.25;1,1;".. "mcl_core:emerald" ..";t10;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[1.5,2.25;1,1;".. "currency:minegeld_5" ..";t50;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[2.5,2.25;1,1;".. "currency:minegeld_10" ..";t100;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[3.5,2.25;1,1;".. "currency:minegeld_50" ..";t500;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[4.5,2.25;1,1;".. "currency:minegeld_100" ..";t1000;\n\n\b\b\b\b" .. "x10" .."]" ..
"item_image_button[7.5,2.25;1,1;".. "mcl_core:emerald" ..";t-10;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[7.5,2.25;1,1;".. "currency:minegeld_5" ..";t-50;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[8.5,2.25;1,1;".. "currency:minegeld_10" ..";t-100;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[9.5,2.25;1,1;".. "currency:minegeld_50" ..";t-500;\n\n\b\b\b\b" .. "x10" .."]" ..
-- "item_image_button[10.5,2.25;1,1;".. "currency:minegeld_100" ..";t-1000;\n\n\b\b\b\b" .. "x10" .."]" ..
"item_image_button[0.5,3.25;1,1;".. "mcl_core:emerald" ..";c100;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[1.5,3.25;1,1;".. "currency:minegeld_5" ..";c500;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[2.5,3.25;1,1;".. "currency:minegeld_10" ..";c1000;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[3.5,3.25;1,1;".. "currency:minegeld_50" ..";c5000;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[4.5,3.25;1,1;".. "currency:minegeld_100" ..";c10000;\n\n\b\b\b" .. "x100" .."]" ..
"item_image_button[7.5,3.25;1,1;".. "mcl_core:emerald" ..";c-100;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[7.5,3.25;1,1;".. "currency:minegeld_5" ..";c-500;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[8.5,3.25;1,1;".. "currency:minegeld_10" ..";c-1000;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[9.5,3.25;1,1;".. "currency:minegeld_50" ..";c-5000;\n\n\b\b\b" .. "x100" .."]" ..
-- "item_image_button[10.5,3.25;1,1;".. "currency:minegeld_100" ..";c-10000;\n\n\b\b\b" .. "x100" .."]" ..
-- "button_exit[5.5,3;1,2;Quit;Quit]" ..
"button[3.5,3;2,1;wt;"..S("Wire Transfer").."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"listring[current_player;main]"
minetest.after((0.1), function(gui)
return minetest.show_formspec(player:get_player_name(), "atm.form", gui)
end, formspec)
end
-- wire transfer interface
function atm.showform_wt (player)
atm.ensure_init(player:get_player_name())
local formspec =
"size[8,6]"..
"button[5.75,0;2,1;transactions;"..S("Transactions >").."]" ..
"label[2.5,0;"..S("Wire Transfer System").."]" ..
"label[2,0.5;"..S("Your account balance: $@1", atm.balance[player:get_player_name()]).. "]" ..
"field[0.5,1.5;5,1;dstn;"..S("Recepient:")..";]"..
"field[6,1.5;2,1;amnt;"..S("Amount:")..";]"..
"field[0.5,3;7.5,1;desc;"..S("Description:")..";]"..
"button_exit[0.2,5;1,1;Quit;"..S("Quit").."]" ..
"button[4.7,5;3,1;pay;"..S("Complete the payment").."]"
minetest.after((0.1), function(gui)
return minetest.show_formspec(player:get_player_name(), "atm.form.wt", gui)
end, formspec)
end
function atm.showform_wtconf (player, dstn, amnt, desc)
atm.ensure_init(player:get_player_name())
local formspec =
"size[8,6]"..
"label[2.5,0;"..S("Wire Transfer System").."]" ..
"label[2,0.5;"..S("Your account balance: $@1", atm.balance[player:get_player_name()]).. "]" ..
"label[2.5,1;"..S("TRANSACTION SUMMARY:").."]"..
"label[0.5,1.5;"..S("Recepient:").." "..dstn.."]"..
"label[0.5,2;"..S("Amount:").." " .. amnt .. "]"..
"label[0.5,2.5;"..S("Description:").." " .. desc .. "]"..
"button_exit[0.2,5;1,1;Quit;"..S("Quit").."]" ..
"button[4.7,5;3,1;cnfrm;"..S("Confirm transfer").."]"
minetest.after((0.1), function(gui)
return minetest.show_formspec(player:get_player_name(), "atm.form.wtc", gui)
end, formspec)
end
function atm.showform_wtlist (player, tlist)
atm.ensure_init(player:get_player_name())
local textlist = ''
if not tlist then
textlist = S("no transactions registered").."\n"
else
for _, entry in ipairs(tlist) do
textlist = textlist .. entry.date .. " $" .. entry.sum .. " " .. S("from") .. " " .. entry.from .. ": " .. entry.desc .. "\n"
end
end
local formspec =
"size[8,6]"..
"button[5.75,0;2,1;transfer;"..S("< Transfer money").."]" ..
"label[2.5,0;"..S("Wire Transfer System").."]" ..
"label[2,0.5;"..S("Your account balance: $@1", atm.balance[player:get_player_name()]).. "]" ..
"textarea[0.5,1.25;7.5,4;hst;"..S("Transaction list")..";" .. textlist .. "]" ..
"button_exit[0.2,5;1,1;Quit;"..S("Quit").."]" ..
"button[4.7,5;3,1;clr;"..S("Clear transactions").."]"
minetest.after((0.1), function(gui)
return minetest.show_formspec(player:get_player_name(), "atm.form.wtl", gui)
end, formspec)
end
function emeraldbank.bank_receive_fields(player, form, pressed)
-- ATMs
if form == "atm.form" then
local n = player:get_player_name()
local transaction = { amount = 0, denomination = 0, count = 0 }
local pinv=player:get_inventory()
-- single note transactions
for _,i in pairs({1, 5, 10, 50, 100, -1, -5, -10, -50, -100}) do
if pressed["i"..i] then
transaction.amount = i
transaction.denomination = '_' .. math.abs(i)
if transaction.denomination == '_1' then
transaction.denomination = ''
end
transaction.count = ' ' .. 1
break
end
end
-- 10x banknote transactions
for _,t in pairs({10, 50, 100, 500, 1000, -10, -50, -100, -500, -1000}) do
if pressed["t"..t] then
transaction.amount = t
transaction.denomination = '_' .. math.abs(t/10)
if transaction.denomination == '_1' then
transaction.denomination = ''
end
transaction.count = ' ' .. 10
break
end
end
-- 100x banknote transactions
for _,c in pairs({100, 500, 1000, 5000, 10000, -100, -500, -1000, -5000, -10000}) do
if pressed["c"..c] then
transaction.amount = c
transaction.denomination = '_' .. math.abs(c/100)
if transaction.denomination == '_1' then
transaction.denomination = ''
end
transaction.count = ' ' .. 100
break
end
end
-- someone hit exchange button
if pressed.wt then
atm.showform_wt(player)
end
if (atm.balance[n] + transaction.amount) < 0 then
minetest.chat_send_player(n, S("Not enough money in your account"))
transaction.amount = 0
end
local item = "mcl_core:emerald" .. transaction.denomination .. transaction.count
if transaction.amount < 0 then
if pinv:room_for_item("main", item) then
pinv:add_item("main", item)
atm.balance[n] = atm.balance[n] + transaction.amount
else
minetest.chat_send_player(n, S("Not enough room in your inventory"))
end
elseif transaction.amount > 0 then
if pinv:contains_item("main", item) then
pinv:remove_item("main", item)
atm.balance[n] = atm.balance[n] + transaction.amount
else
minetest.chat_send_player(n, S("Not enough money in your inventory"))
end
end
atm.saveaccounts()
if not pressed.Quit and not pressed.quit then
atm.showform(player)
end
end
end
function emeraldbank.wt_receive_fields(player, form, pressed)
-- Wire transfer terminals
if form == "atm.form.wt" or form == "atm.form.wtc" or form == "atm.form.wtl" then
local n = player:get_player_name()
if not pressed.Quit and not pressed.quit then
if form == "atm.form.wt" and pressed.transactions then
-- transaction list (can be edited in the form, but than means nothing)
atm.read_transactions()
atm.showform_wtlist(player, atm.completed_transactions[n])
elseif form == "atm.form.wtl" and pressed.transfer then
atm.showform_wt(player)
elseif form == "atm.form.wtl" and pressed.clr then
-- clear all transactions in the player's list
atm.read_transactions()
atm.completed_transactions[n] = nil
atm.write_transactions()
minetest.chat_send_player(n, S("Your transaction history has been cleared"))
atm.showform_wtlist(player, atm.completed_transactions[n])
elseif form == "atm.form.wt" and pressed.pay then
-- perform the checks of validity for wire transfer order
-- if passed, store the data in a temporary table and show confirmation window
if not atm.balance[pressed.dstn] then
minetest.chat_send_player(n, S("The recepient <@1> is not registered in the banking system, aborting", pressed.dstn))
atm.showform_wt(player)
elseif not string.match(pressed.amnt, '^[0-9]+$') then
minetest.chat_send_player(n, S("Invalid amount <@1> : must be an integer number, aborting", pressed.amnt))
atm.showform_wt(player)
elseif atm.balance[n] < tonumber(pressed.amnt) then
minetest.chat_send_player(n, S("Your account does not have enough funds to complete this transfer, aborting"))
atm.showform_wt(player)
else
atm.pending_transfers[n] = {to = pressed.dstn, sum = tonumber(pressed.amnt), desc = pressed.desc}
atm.showform_wtconf(player, pressed.dstn, pressed.amnt, pressed.desc)
end
elseif form == "atm.form.wtc" then
-- transaction processing
atm.read_transactions()
local t = atm.pending_transfers[n]
if not t then
return
end
if not atm.completed_transactions[t.to] then
atm.completed_transactions[t.to] = {}
end
if atm.balance[n] < t.sum then
-- you can never be too paranoid about the funds availaible
minetest.chat_send_player(n, S("Your account does not have enough funds to complete this transfer, aborting"))
if not t.extern then
atm.showform_wt(player)
else
minetest.close_formspec(n, "atm.form.wtc")
end
return
end
table.insert(atm.completed_transactions[t.to], {date=os.date("%Y-%m-%d"), from=n, sum=t.sum, desc=t.desc})
atm.balance[n] = atm.balance[n] - t.sum
atm.balance[t.to] = atm.balance[t.to] + t.sum
atm.write_transactions()
atm.saveaccounts()
if minetest.get_modpath("mail") and mail.send then
mail.send({
from = "Emerald Bank",
to = t.to,
--cc = "carbon, copy",
--bcc = "blind, carbon, copy",
subject = S("Payment of @1 emeralds from @2", t.sum, n),
body = S("Good news!\n\n@1 has transferred @2 emeralds to your bank account using the Emerald Bank Transfer System.\n\nTransfer description:\n\n@3", n, t.sum, t.desc)
})
end
minetest.chat_send_player(n, S("Payment of @1 to @2 completed", t.sum, t.to))
minetest.chat_send_player(n, S("@1, thank you for choosing the Emerald Bank Transfer system", n))
if t.callback then -- run callbacks from mods
t.callback(t)
end
if t.extern == true then -- Transfer was initiated by mod
atm.pending_transfers[n] = nil
minetest.close_formspec(n, "atm.form.wtc")
return
end
atm.pending_transfers[n] = nil
atm.showform_wt(player)
end
else
-- clear the pending transaction of the player, just in case
if atm.pending_transfers[n] then
atm.pending_transfers[n] = nil
end
end
end
end