towny/towny_eco/mods/currency.lua
2019-11-21 15:28:04 +02:00

290 lines
6.6 KiB
Lua

-- Simple "currency" mod API
local denoms = {
["currency:minegeld_cent_5"] = 0.05,
["currency:minegeld_cent_10"] = 0.10,
["currency:minegeld_cent_25"] = 0.25,
["currency:minegeld"] = 1,
["currency:minegeld_5"] = 5,
["currency:minegeld_10"] = 10,
["currency:minegeld_50"] = 50,
["currency:minegeld_100"] = 100,
}
function towny.eco.get_currency()
return "Minegeld"
end
function towny.eco.format_number(number)
return ("%.2f MG$"):format(number)
end
local function round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
-- Item-based functions
local function to_stacks(items)
local counted = {}
for _,i in pairs(items) do
if counted[i] then
counted[i] = counted[i] + 1
else
counted[i] = 1
end
end
local stacks = {}
for itm,cnt in pairs(counted) do
table.insert(stacks, ItemStack(itm .. " " .. cnt))
end
return stacks
end
local function get_closest_note(amount, notes)
local t = nil
local a = 0
for itm,val in pairs(denoms) do
local b = (notes == nil)
if notes and notes[itm] ~= nil and notes[itm] > 0 then
b = true
end
if val <= amount and a <= val and b then
t = itm
a = val
end
end
return a, t
end
local function denominate(amount)
local items = {}
local tc = amount
while tc > 0 do
local amount,item = get_closest_note(tc)
if amount == 0 then break end
tc = tc - amount
table.insert(items, item)
end
return to_stacks(items)
end
local function notes_inv(inventory)
local total = 0
for _,stack in pairs(inventory:get_list("main")) do
local value = denoms[stack:get_name()]
if value then
total = total + (value * stack:get_count())
end
end
return total
end
local function highest_note(notes, amount)
local result = nil
local tmount = 0
for note,cnt in pairs(notes) do
local nt = denoms[note]
if nt > amount and cnt > 0 then
result = note
tmount = nt
end
end
return tmount, result
end
local function take_notes(inventory, total)
-- Take an audit of all the notes in the inventory
local notes = {}
for _,stack in pairs(inventory:get_list("main")) do
local name = stack:get_name()
if denoms[name] then
if notes[name] then
notes[name] = notes[name] + stack:get_count()
else
notes[name] = stack:get_count()
end
end
end
local original = table.copy(notes)
-- Loop through, getting the highest notes first
while total > 0 do
local amount,item = get_closest_note(total,notes)
if amount == 0 then break end
if notes[item] then
notes[item] = notes[item] - 1
end
total = total - amount
end
-- If the total was not reached, try to get the next highest note
local give = 0
if total > 0 then
local nxam,note = highest_note(notes, total)
if nxam > total then
notes[note] = notes[note] - 1
give = nxam - total
total = 0
end
end
-- Couldn't take total balance
if total > 0 then
return nil
end
-- Take from inventory
for note,count in pairs(notes) do
if original[note] then
local taken = original[note] - count
if taken > 0 then
inventory:remove_item("main", ItemStack(note .. " " .. taken))
end
end
end
if give > 0 then
local stacks = denominate(give)
for _,stack in pairs(stacks) do
inventory:add_item("main", stack)
end
end
return total
end
-- Public functions
function towny.eco.get_player_balance(player)
if type(player) == "string" then
player = minetest.get_player_by_name(player)
end
if not player then return 0 end
if towny.eco.type == "item" then
return notes_inv(player:get_inventory())
else
local meta = player:get_meta()
return meta:get_float("money")
end
end
function towny.eco.charge_player(player, amount)
if type(player) == "string" then
player = minetest.get_player_by_name(player)
end
if not player then return false end
if towny.eco.type == "item" then
return take_notes(player:get_inventory(), amount)
else
local meta = player:get_meta()
local money = meta:get_float("money")
if money < amount then
return false
end
meta:set_float(money - amount)
return amount
end
end
function towny.eco.pay_player(player, amount)
if type(player) == "string" then
player = minetest.get_player_by_name(player)
end
if not player then return false end
if towny.eco.type == "item" then
local inventory = player:get_inventory()
local stacks = denominate(amount)
for _,stack in pairs(stacks) do
inventory:add_item("main", stack)
end
else
local meta = player:get_meta()
local money = meta:get_float("money")
meta:set_float(money + amount)
return true
end
end
local requests = {}
local function c(msg)
return minetest.colorize("#09b700",msg)
end
local function b(msg)
return minetest.colorize("#078c00",msg)
end
local function msg(name,msg)
minetest.chat_send_player(name,msg)
return true
end
local function fm(amount)
return towny.eco.format_number(amount)
end
local function help()
return minetest.colorize("#e5b002", "Command usage:") .. "\n" ..
b("/money") .. c(" - Check your balance") .. "\n" ..
b("/money transfer") .. c(" <player> <amount> - Give money to a player") .. "\n" ..
b("/money request") .. c(" <player> <amount> - Request money from a player") .. "\n"
end
local function money_command(name, param)
local player = minetest.get_player_by_name(name)
if not player then
return false, "Only an online player can run this command."
end
if param == "" then
return true, b("Balance: ") .. c(fm(towny.eco.get_player_balance(player)))
end
local command,target,amount = param:match("^(%a+) ([%a%d_-]+) ([%d.]+)$")
local target_player = minetest.get_player_by_name(target or "")
if command and not target then
return false, "The target player doesn't exist."
end
amount = tonumber(amount)
if command == "transfer" or command == "pay" or command == "give" then
local taken = towny.eco.charge_player(player, amount)
if not taken then
return false, "You do not have enough money."
end
towny.eco.pay_player(target_player, amount)
minetest.chat_send_player(target, c("Player %s has paid you %s!"):format(name, fm(amount)))
return true, c("Successfully given %s to %s."):format(fm(amount), target)
elseif command == "request" then
if requests[name.."-"..target] then
return false, "You have already requested money from that player."
end
requests[name.."-"..target] = true
minetest.chat_send_player(target, c("Player %s requested a total of %s from you. Run '/money pay %s %d' to pay them.")
:format(name, fm(amount), name, amount))
return true, c("Request sent.")
end
return false, help()
end
minetest.register_chatcommand("money", {
description = "View and transfer your money",
privs = {interact = true},
func = money_command
})
towny.eco.type = "item"
towny.eco.enabled = true