From d6a158a6e7eb7a7c37645f145c34978ec24b7a51 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Thu, 21 Nov 2019 15:28:04 +0200 Subject: [PATCH] Towny Economy Setup --- towny/depends.txt | 1 + towny/init.lua | 2 - towny/mod.conf | 2 +- towny/settingtypes.txt | 20 +-- towny_eco/api.lua | 19 +++ towny_eco/depends.txt | 2 + towny_eco/description.txt | 1 + towny_eco/init.lua | 18 +++ towny_eco/mod.conf | 4 + towny_eco/mods/currency.lua | 289 ++++++++++++++++++++++++++++++++++++ towny_eco/settingtypes.txt | 6 + 11 files changed, 344 insertions(+), 20 deletions(-) create mode 100644 towny_eco/api.lua create mode 100644 towny_eco/depends.txt create mode 100644 towny_eco/description.txt create mode 100644 towny_eco/init.lua create mode 100644 towny_eco/mod.conf create mode 100644 towny_eco/mods/currency.lua create mode 100644 towny_eco/settingtypes.txt diff --git a/towny/depends.txt b/towny/depends.txt index c4c0ea8..b7c156d 100644 --- a/towny/depends.txt +++ b/towny/depends.txt @@ -1,2 +1,3 @@ areas? protector? +currency? diff --git a/towny/init.lua b/towny/init.lua index bb897de..00fe892 100644 --- a/towny/init.lua +++ b/towny/init.lua @@ -1,8 +1,6 @@ -- A township system for Minetest servers. -- The MIT License - 2019 Evert "Diamond" Prants --- TODO: Protection on HUD --- TODO: Nations -- TODO: Economy local modpath = minetest.get_modpath(minetest.get_current_modname()) diff --git a/towny/mod.conf b/towny/mod.conf index 3221210..39d207f 100644 --- a/towny/mod.conf +++ b/towny/mod.conf @@ -1,3 +1,3 @@ name = towny description = A township system for Minetest servers. -optional_depends = areas,protector +optional_depends = areas,protector,currency diff --git a/towny/settingtypes.txt b/towny/settingtypes.txt index 4f38e64..6a2934e 100644 --- a/towny/settingtypes.txt +++ b/towny/settingtypes.txt @@ -1,13 +1,11 @@ -# General -########## +[General] # Town metadata and region storage engine # Recommended to keep as modstorage. towny_storage_engine (Storage engine) enum modstorage modstorage,flatfile -# Claims settings -################## +[Claims] # Towny claim diameter towny_claim_size (Claim size) int 16 @@ -27,19 +25,7 @@ towny_distance (Max town claims) int 80 # Recommended to be kept as true, may cause issues with claims otherwise towny_prevent_protector (Prevent protectors from being placed in a town) bool true -# Command settings -################### +[Command] # If true, players must be invited into towns (No direct joining) towny_invite (Invite-based membership) bool true - -# Economy settings -################### - -towny_eco (Enable economy) bool false -towny_tax (Allow taxation) bool true - -# Units depend on economy mod used in server -towny_create_cost (Town creation cost) int 10000 -towny_claim_cost (Town claim block cost) int 1000 -towny_upkeep_cost (Town daily upkeep cost, multiplied by member count) int 0 diff --git a/towny_eco/api.lua b/towny_eco/api.lua new file mode 100644 index 0000000..5364a45 --- /dev/null +++ b/towny_eco/api.lua @@ -0,0 +1,19 @@ + +-------------- +-- Abstract -- +-------------- + +-- Get currency name/description +function towny.eco.get_currency() return "" end + +-- Format the number appropriately +function towny.eco.format_number(number) return number end + +-- Get player's balance +function towny.eco.get_player_balance(player) return 0 end + +-- Charge a player +function towny.eco.charge_player(player, amount) return 0 end + +-- Pay a player +function towny.eco.pay_player(player, amount) return false end diff --git a/towny_eco/depends.txt b/towny_eco/depends.txt new file mode 100644 index 0000000..6ccca44 --- /dev/null +++ b/towny_eco/depends.txt @@ -0,0 +1,2 @@ +towny +currency? diff --git a/towny_eco/description.txt b/towny_eco/description.txt new file mode 100644 index 0000000..9e99f24 --- /dev/null +++ b/towny_eco/description.txt @@ -0,0 +1 @@ +Towny Economy support diff --git a/towny_eco/init.lua b/towny_eco/init.lua new file mode 100644 index 0000000..7d108fb --- /dev/null +++ b/towny_eco/init.lua @@ -0,0 +1,18 @@ +-- A township system for Minetest servers. +-- The MIT License - 2019 Evert "Diamond" Prants + +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +towny.eco = { + enabled = false, + create_cost = tonumber(minetest.settings:get("towny_create_cost")) or 10000, + claim_cost = tonumber(minetest.settings:get("towny_claim_cost")) or 1000, + upkeep_cost = tonumber(minetest.settings:get("towny_upkeep_cost")) or 0, + taxable = minetest.settings:get_bool("towny_tax", true) +} + +dofile(modpath.."/api.lua") + +if minetest.get_modpath("currency") ~= nil then + dofile(modpath.."/mods/currency.lua") +end diff --git a/towny_eco/mod.conf b/towny_eco/mod.conf new file mode 100644 index 0000000..5cd7db5 --- /dev/null +++ b/towny_eco/mod.conf @@ -0,0 +1,4 @@ +name = towny_eco +description = Towny Economy support +depends = towny +optional_depends = currency diff --git a/towny_eco/mods/currency.lua b/towny_eco/mods/currency.lua new file mode 100644 index 0000000..bf36729 --- /dev/null +++ b/towny_eco/mods/currency.lua @@ -0,0 +1,289 @@ +-- 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(" - Give money to a player") .. "\n" .. + b("/money request") .. c(" - 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 diff --git a/towny_eco/settingtypes.txt b/towny_eco/settingtypes.txt new file mode 100644 index 0000000..9b71ba0 --- /dev/null +++ b/towny_eco/settingtypes.txt @@ -0,0 +1,6 @@ +towny_tax (Allow taxation) bool true + +# Units depend on economy mod used in server +towny_create_cost (Town creation cost) int 10000 +towny_claim_cost (Town claim block cost) int 1000 +towny_upkeep_cost (Town daily upkeep cost, multiplied by member count) int 0