forked from minetest-mods/global_exchange
Fix tool wear & metadata bugs and improve the UI.
This commit is contained in:
parent
7db6fb3ca7
commit
2a5a726cc0
222
atm.lua
222
atm.lua
@ -1,119 +1,115 @@
|
||||
-- A telling machine. Call this file with the exchange argument.
|
||||
local exchange = ...
|
||||
local exchange, formlib = ...
|
||||
|
||||
local atm_form = "global_exchange:atm_form"
|
||||
|
||||
local main_menu =[[
|
||||
size[6,2]
|
||||
button[2,0;2,1;info;Account Info]
|
||||
button[4,0;2,1;wire;Wire Monies]
|
||||
button[1,1;4,1;transaction_log;Transaction Log]
|
||||
]]
|
||||
|
||||
local unique = (function(unique_num)
|
||||
return function()
|
||||
unique_num = unique_num + 1
|
||||
return unique_num
|
||||
end
|
||||
end)(0)
|
||||
|
||||
|
||||
local function logout(x,y)
|
||||
return "button[" .. x .. "," .. y .. ";2,1;logout;Log Out]"
|
||||
end
|
||||
|
||||
|
||||
local function label(x,y,text)
|
||||
return "label[" .. x .. "," .. y .. ";"
|
||||
.. minetest.formspec_escape(text) .. "]"
|
||||
end
|
||||
|
||||
local function field(x,y, w,h, name, label, default)
|
||||
return "field[" .. x .. "," .. y .. ";" .. w .. "," .. h .. ";"
|
||||
.. name .. ";" .. minetest.formspec_escape(label) .. ";"
|
||||
.. minetest.formspec_escape(default) .. "]"
|
||||
end
|
||||
|
||||
local unique_num = 1
|
||||
|
||||
local function unique()
|
||||
local ret = unique_num
|
||||
unique_num = unique_num + 1
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
local function info_fs(p_name)
|
||||
local function info_fs(fs, p_name)
|
||||
local balance = exchange:get_balance(p_name)
|
||||
|
||||
local fs
|
||||
if not balance then
|
||||
fs = label(0.5,0.5, "You don't have an account.")
|
||||
fs:size(4,3)
|
||||
|
||||
if balance then
|
||||
fs:label(0.5,0.5, "Balance: " .. balance)
|
||||
else
|
||||
fs = label(0.5,0.5, "Balance: " .. balance)
|
||||
fs:label(0.5,0.5, "You don't have an account.")
|
||||
end
|
||||
|
||||
return "size[4,3]" .. fs .. logout(0.5,2)
|
||||
fs:button(1,2, 2,1, "logout", "Log Out")
|
||||
end
|
||||
|
||||
|
||||
local function wire_fs(p_name)
|
||||
local function wire_fs(fs, p_name)
|
||||
local balance = exchange:get_balance(p_name)
|
||||
|
||||
local fs = "size[4,5]" .. logout(0,4)
|
||||
fs:size(4,5)
|
||||
|
||||
if not balance then
|
||||
return fs .. label(0.5,0.5, "You don't have an account.")
|
||||
if balance then
|
||||
-- To prevent duplicates
|
||||
fs:field(-100, -100, 0,0, "trans_id", "", unique())
|
||||
|
||||
fs:label(0.50,0.325, "Balance: " .. balance)
|
||||
fs:field(0.75,1.750, 3,1, "recipient", "Send to:", "")
|
||||
fs:field(0.75,3.000, 3,1, "amount", "Amount", "")
|
||||
|
||||
fs:button(0,4.25, 2,1, "logout", "Log Out")
|
||||
fs:button(2,4.25, 2,1, "send", "Send")
|
||||
else
|
||||
fs:button(0,4, 2,1, "logout", "Back")
|
||||
fs:label(0.5,0.5, "You don't have an account.")
|
||||
end
|
||||
|
||||
-- To prevent duplicates
|
||||
return fs .. field(-100, -100, 0,0, "trans_id", "", unique()) ..
|
||||
label(0.5,0.5, "Balance: " .. balance) ..
|
||||
field(0.5,1.5, 2,1, "recipient", "Send to:", "") ..
|
||||
field(0.5,2.5, 2,1, "amount", "Amount", "") ..
|
||||
"button[2,4;2,1;send;Send]"
|
||||
end
|
||||
|
||||
|
||||
local function send_fs(p_name, receiver, amt_str)
|
||||
local fs = "size[7,3]"
|
||||
local function send_fs(fs, p_name, receiver, amt_str)
|
||||
fs:size(10,3)
|
||||
|
||||
fs:button(4,2, 2,1, "wire", "Back")
|
||||
|
||||
local amt = tonumber(amt_str)
|
||||
local msg = nil
|
||||
|
||||
if not amt or amt <= 0 then
|
||||
return fs .. label(0.5,0.5, "Invalid transfer amount.") ..
|
||||
"button[0.5,2;2,1;wire;Back]"
|
||||
msg = "Invalid transfer amount."
|
||||
else
|
||||
local succ, err = exchange:transfer_credits(p_name, receiver, amt)
|
||||
|
||||
if not succ then
|
||||
msg = "Error: " .. err
|
||||
else
|
||||
msg = "Successfully sent " .. amt ..
|
||||
" credits to " .. receiver .. "."
|
||||
end
|
||||
end
|
||||
|
||||
local succ, err = exchange:transfer_credits(p_name, receiver, amt)
|
||||
|
||||
if not succ then
|
||||
return fs .. label(0.5,0.5, "Error: " .. err) ..
|
||||
"button[0.5,2;2,1;wire;Back]"
|
||||
end
|
||||
return fs.. label(0.5,0.5, "Successfully sent " ..
|
||||
amt .. " credits to " .. receiver) ..
|
||||
"button[0.5,2;2,1;wire;Back]"
|
||||
fs:label(0.5,0.5, msg)
|
||||
end
|
||||
|
||||
|
||||
local function log_fs(p_name)
|
||||
local res = {
|
||||
"size[8,8]label[0,0;Transaction Log]button[0,7;2,1;logout;Log Out]",
|
||||
"tablecolumns[text;text]",
|
||||
"table[0,1;8,6;log_table;Time,Message",
|
||||
}
|
||||
local function log_fs(fs, p_name)
|
||||
fs:size(14,8)
|
||||
|
||||
for i, entry in ipairs(exchange:player_log(p_name)) do
|
||||
i = i*4
|
||||
res[i] = ","
|
||||
res[i+1] = tostring(entry.Time)
|
||||
res[i+2] = ","
|
||||
res[i+3] = entry.Message
|
||||
fs:label(0,0, "Transaction Log")
|
||||
|
||||
fs("tablecolumns[text;text]")
|
||||
fs("table[0,0.75;13.75,6.75;log_table;Time,Message")
|
||||
|
||||
for _, entry in ipairs(exchange:player_log(p_name)) do
|
||||
fs(",", formlib.escape(entry.Time), ",", formlib.escape(entry.Message))
|
||||
end
|
||||
res[#res+1] ="]"
|
||||
|
||||
return table.concat(res)
|
||||
fs("]")
|
||||
|
||||
fs:button(6,7.5, 2,1, "logout", "Log Out")
|
||||
end
|
||||
|
||||
|
||||
local function main_menu_fs(fs, p_name)
|
||||
fs:size(6,2)
|
||||
fs:button(0.50,0.125, 2.5,1, "info", "Account Info")
|
||||
fs:button(3.00,0.125, 2.5,1, "wire", "Wire Monies")
|
||||
fs:button(0.50,1.125, 5.0,1, "transaction_log", "Transaction Log")
|
||||
end
|
||||
|
||||
|
||||
local trans_ids = {}
|
||||
|
||||
|
||||
local function show_atm_form(fs_fn, p_name, ...)
|
||||
local fs = formlib.Builder()
|
||||
fs_fn(fs, p_name, ...)
|
||||
minetest.show_formspec(p_name, atm_form, tostring(fs))
|
||||
end
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= atm_form then return end
|
||||
if fields.quit then return true end
|
||||
@ -129,24 +125,15 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
trans_ids[p_name] = this_id
|
||||
|
||||
if fields.logout then
|
||||
minetest.show_formspec(p_name, atm_form, main_menu)
|
||||
end
|
||||
|
||||
if fields.info then
|
||||
minetest.show_formspec(p_name, atm_form, info_fs(p_name))
|
||||
end
|
||||
|
||||
if fields.wire then
|
||||
minetest.show_formspec(p_name, atm_form, wire_fs(p_name))
|
||||
end
|
||||
|
||||
if fields.send then
|
||||
minetest.show_formspec(p_name, atm_form,
|
||||
send_fs(p_name, fields.recipient, fields.amount))
|
||||
end
|
||||
|
||||
if fields.transaction_log then
|
||||
minetest.show_formspec(p_name, atm_form, log_fs(p_name))
|
||||
show_atm_form(main_menu_fs, p_name)
|
||||
elseif fields.info then
|
||||
show_atm_form(info_fs, p_name)
|
||||
elseif fields.wire then
|
||||
show_atm_form(wire_fs, p_name)
|
||||
elseif fields.send then
|
||||
show_atm_form(send_fs, p_name, fields.recipient, fields.amount)
|
||||
elseif fields.transaction_log then
|
||||
show_atm_form(log_fs, p_name)
|
||||
end
|
||||
|
||||
return true
|
||||
@ -180,16 +167,16 @@ minetest.register_node("global_exchange:atm_bottom", {
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
{-0.5, 0.5, -0.5, -0.375, 1.125, -0.25},
|
||||
{0.375, 0.5, -0.5, 0.5, 1.125, -0.25},
|
||||
{-0.5, 0.5, -0.25, 0.5, 1.5, 0.5},
|
||||
{-0.5, 1.125, -0.4375, -0.375, 1.25, -0.25},
|
||||
{0.375, 1.125, -0.4375, 0.5, 1.25, -0.25},
|
||||
{-0.5, 1.25, -0.375, -0.375, 1.375, -0.25},
|
||||
{0.375, 1.25, -0.375, 0.5, 1.375, -0.25},
|
||||
{-0.5, 1.375, -0.3125, -0.375, 1.5, -0.25},
|
||||
{0.375, 1.375, -0.3125, 0.5, 1.5, -0.25},
|
||||
{-0.500, -0.500, -0.5000, 0.500, 0.500, 0.50},
|
||||
{-0.500, 0.500, -0.5000, -0.375, 1.125, -0.25},
|
||||
{ 0.375, 0.500, -0.5000, 0.500, 1.125, -0.25},
|
||||
{-0.500, 0.500, -0.2500, 0.500, 1.500, 0.50},
|
||||
{-0.500, 1.125, -0.4375, -0.375, 1.250, -0.25},
|
||||
{ 0.375, 1.125, -0.4375, 0.500, 1.250, -0.25},
|
||||
{-0.500, 1.250, -0.3750, -0.375, 1.375, -0.25},
|
||||
{ 0.375, 1.250, -0.3750, 0.500, 1.375, -0.25},
|
||||
{-0.500, 1.375, -0.3125, -0.375, 1.500, -0.25},
|
||||
{ 0.375, 1.375, -0.3125, 0.500, 1.500, -0.25},
|
||||
},
|
||||
},
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
@ -233,7 +220,7 @@ minetest.register_node("global_exchange:atm_bottom", {
|
||||
groups = {cracky=2, atm = 1},
|
||||
on_rightclick = function(pos, _, clicker)
|
||||
minetest.sound_play("atm_beep", {pos = pos, gain = 0.3, max_hear_distance = 5})
|
||||
minetest.show_formspec(clicker:get_player_name(), atm_form, main_menu)
|
||||
show_atm_form(main_menu_fs, clicker:get_player_name())
|
||||
end,
|
||||
})
|
||||
|
||||
@ -254,15 +241,15 @@ minetest.register_node("global_exchange:atm_top", {
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, -0.375, 0.125, -0.25},
|
||||
{0.375, -0.5, -0.5, 0.5, 0.125, -0.25},
|
||||
{-0.5, -0.5, -0.25, 0.5, 0.5, 0.5},
|
||||
{-0.5, 0.125, -0.4375, -0.375, 0.25, -0.25},
|
||||
{0.375, 0.125, -0.4375, 0.5, 0.25, -0.25},
|
||||
{-0.5, 0.25, -0.375, -0.375, 0.375, -0.25},
|
||||
{0.375, 0.25, -0.375, 0.5, 0.375, -0.25},
|
||||
{-0.5, 0.375, -0.3125, -0.375, 0.5, -0.25},
|
||||
{0.375, 0.375, -0.3125, 0.5, 0.5, -0.25},
|
||||
{-0.500, -0.500, -0.5000, -0.375, 0.125, -0.25},
|
||||
{ 0.375, -0.500, -0.5000, 0.500, 0.125, -0.25},
|
||||
{-0.500, -0.500, -0.2500, 0.500, 0.500, 0.50},
|
||||
{-0.500, 0.125, -0.4375, -0.375, 0.250, -0.25},
|
||||
{ 0.375, 0.125, -0.4375, 0.500, 0.250, -0.25},
|
||||
{-0.500, 0.250, -0.3750, -0.375, 0.375, -0.25},
|
||||
{ 0.375, 0.250, -0.3750, 0.500, 0.375, -0.25},
|
||||
{-0.500, 0.375, -0.3125, -0.375, 0.500, -0.25},
|
||||
{ 0.375, 0.375, -0.3125, 0.500, 0.500, -0.25},
|
||||
}
|
||||
},
|
||||
selection_box = {
|
||||
@ -278,10 +265,11 @@ minetest.register_node("global_exchange:atm_top", {
|
||||
minetest.register_craft( {
|
||||
output = "global_exchange:atm",
|
||||
recipe = {
|
||||
{ "default:stone", "default:stone", "default:stone" },
|
||||
{ "default:stone", "default:stone", "default:stone" },
|
||||
{ "default:stone", "default:gold_ingot", "default:stone" },
|
||||
{ "default:stone", "default:stone", "default:stone" },
|
||||
{ "default:stone", "default:stone", "default:stone" },
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_alias("global_exchange:atm", "global_exchange:atm_bottom")
|
||||
-- vim:set ts=4 sw=4 noet:
|
||||
|
@ -1,60 +1,65 @@
|
||||
|
||||
local exchange = ...
|
||||
local exchange, formlib = ...
|
||||
|
||||
local mailbox_form = "global_exchange:digital_mailbox"
|
||||
|
||||
local mailbox_contents = {}
|
||||
local selected_index = {}
|
||||
-- Map from player names to their most recent search result
|
||||
|
||||
-- Map from player names to their most recent search result
|
||||
local function get_mail(p_name)
|
||||
local mail_maybe = mailbox_contents[p_name]
|
||||
if mail_maybe then
|
||||
return mail_maybe
|
||||
else
|
||||
mailbox_contents[p_name] = {}
|
||||
return mailbox_contents[p_name]
|
||||
|
||||
if not mail_maybe then
|
||||
local _,res = exchange:view_inbox(p_name)
|
||||
mail_maybe = res or {}
|
||||
mailbox_contents[p_name] = mail_maybe
|
||||
selected_index[p_name] = math.min(selected_index[p_name] or 0, #mail_maybe)
|
||||
end
|
||||
|
||||
return mail_maybe
|
||||
end
|
||||
|
||||
|
||||
local function mk_inbox_list(results, x, y, w, h)
|
||||
local res = {
|
||||
"textlist[",
|
||||
tostring(x),
|
||||
",",
|
||||
tostring(y),
|
||||
";",
|
||||
tostring(w),
|
||||
",",
|
||||
tostring(h),
|
||||
";result_list;"
|
||||
}
|
||||
local function wear_string(wear)
|
||||
return "-" .. math.ceil(100 * wear / 65535) .. "%"
|
||||
end
|
||||
|
||||
|
||||
local function mk_inbox_list(fs, results, x, y, w, h)
|
||||
fs("textlist[", x, ",", y, ";", w, ",", h, ";result_list;")
|
||||
|
||||
local sep = nil
|
||||
for i, row in ipairs(results) do
|
||||
res[i*2+8] = row.Amount .. " " .. row.Item
|
||||
res[i*2+9] = ","
|
||||
fs(sep)
|
||||
fs:escape(row.Amount, " ", row.Item)
|
||||
if row.Wear > 0 then
|
||||
fs:escape(" (", wear_string(row.Wear), ")")
|
||||
end
|
||||
sep = ","
|
||||
end
|
||||
res[#res+1] = "]"
|
||||
|
||||
return table.concat(res)
|
||||
fs("]")
|
||||
end
|
||||
|
||||
|
||||
local function mk_mail_fs(p_name, results, err_str)
|
||||
fs = "size[6,8]" ..
|
||||
"label[0,0;Inbox]"
|
||||
local function mk_mail_fs(fs, p_name, results, err_str)
|
||||
fs:size(8,8)
|
||||
fs:label(0,0, "Inbox")
|
||||
|
||||
if err_str then
|
||||
fs = fs .. "label[3,0;Error: " .. err_str .. "]"
|
||||
fs:label(3,0, "Error: " .. err_str)
|
||||
end
|
||||
|
||||
return fs .. mk_inbox_list(results, 0, 1, 6, 6) ..
|
||||
"button[0,7;2,1;claim;Claim]"
|
||||
mk_inbox_list(fs, results, 0, 1, 7.75, 6.25)
|
||||
|
||||
fs:button(3,7.35, 2,1, "claim", "Claim")
|
||||
end
|
||||
|
||||
|
||||
local function show_mail(p_name, results, err_str)
|
||||
minetest.show_formspec(p_name, mailbox_form, mk_mail_fs(p_name, results, err_str))
|
||||
local function show_mail(p_name, err_str)
|
||||
local fs = formlib.Builder()
|
||||
mk_mail_fs(fs, p_name, get_mail(p_name), err_str)
|
||||
minetest.show_formspec(p_name, mailbox_form, tostring(fs))
|
||||
end
|
||||
|
||||
|
||||
@ -63,35 +68,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if fields.quit then return true end
|
||||
|
||||
local p_name = player:get_player_name()
|
||||
local idx = selected_index[p_name]
|
||||
|
||||
if fields.claim
|
||||
and idx then
|
||||
local row = get_mail(p_name)[idx]
|
||||
|
||||
if row then
|
||||
local stack = ItemStack(row.Item)
|
||||
stack:set_count(row.Amount)
|
||||
|
||||
local p_inv = player:get_inventory()
|
||||
if not p_inv:room_for_item("main", stack) then
|
||||
show_mail(p_name, get_mail(p_name), "Not enough room.")
|
||||
return true
|
||||
end
|
||||
|
||||
local succ, res = exchange:take_inbox(row.Id, row.Amount)
|
||||
if not succ then
|
||||
show_mail(p_name, get_mail(p_name), res)
|
||||
end
|
||||
|
||||
stack:set_count(res)
|
||||
|
||||
p_inv:add_item("main", stack)
|
||||
|
||||
table.remove(get_mail(p_name), idx)
|
||||
show_mail(p_name, get_mail(p_name))
|
||||
end
|
||||
end
|
||||
|
||||
if fields.result_list then
|
||||
local event = minetest.explode_textlist_event(fields.result_list)
|
||||
@ -101,22 +77,48 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
end
|
||||
end
|
||||
|
||||
if fields.claim then
|
||||
local idx = selected_index[p_name]
|
||||
local row = get_mail(p_name)[idx]
|
||||
|
||||
if row then
|
||||
local stack = ItemStack(row.Item)
|
||||
stack:set_count(row.Amount)
|
||||
stack:set_wear(row.Wear)
|
||||
|
||||
local p_inv = player:get_inventory()
|
||||
local leftover = p_inv:add_item("main", stack)
|
||||
local took_amount = row.Amount - leftover:get_count()
|
||||
|
||||
mailbox_contents[p_name] = nil
|
||||
|
||||
local succ, res = exchange:take_inbox(row.Id, took_amount)
|
||||
if succ then
|
||||
show_mail(p_name)
|
||||
else
|
||||
show_mail(p_name, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_node("global_exchange:mailbox", {
|
||||
description = "Digital Mailbox",
|
||||
tiles = {"global_exchange_box.png",
|
||||
tiles = {
|
||||
"global_exchange_box.png",
|
||||
"global_exchange_box.png",
|
||||
"global_exchange_box.png^global_exchange_mailbox_side.png",
|
||||
},
|
||||
is_ground_content = false,
|
||||
stack_max = 1,
|
||||
groups = {cracky=2},
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
local p_name = clicker:get_player_name()
|
||||
local _,res = exchange:view_inbox(p_name)
|
||||
mailbox_contents[p_name] = res
|
||||
minetest.show_formspec(p_name, mailbox_form, mk_mail_fs(p_name, res))
|
||||
mailbox_contents[p_name] = nil
|
||||
show_mail(p_name)
|
||||
end,
|
||||
})
|
||||
|
||||
@ -125,7 +127,8 @@ minetest.register_craft( {
|
||||
output = "global_exchange:mailbox",
|
||||
recipe = {
|
||||
{ "default:stone", "default:gold_ingot", "default:stone" },
|
||||
{ "default:stone", "default:chest", "default:stone" },
|
||||
{ "default:stone", "default:stone", "default:stone" },
|
||||
{ "default:stone", "default:chest", "default:stone" },
|
||||
{ "default:stone", "default:stone", "default:stone" },
|
||||
}
|
||||
})
|
||||
-- vim:set ts=4 sw=4 noet:
|
||||
|
929
exchange.lua
929
exchange.lua
File diff suppressed because it is too large
Load Diff
@ -1,35 +1,38 @@
|
||||
local exchange, formlib = ...
|
||||
|
||||
local exchange = ...
|
||||
local search_cooldown = 2
|
||||
local summary_interval = 600
|
||||
|
||||
local global_inv = nil
|
||||
|
||||
local function is_integer(x)
|
||||
return math.floor(x) == x
|
||||
end
|
||||
|
||||
local summary_fs = ""
|
||||
local function mk_summary_fs()
|
||||
local res = {
|
||||
"size[8,8]",
|
||||
"label[0,0;Updated Periodically]",
|
||||
"tablecolumns[text;text;text;text;text;text]",
|
||||
"table[0,1;8,6;summary_table;",
|
||||
"Item,Description,Buy Vol,Buy Max,Sell Vol,Sell Min"
|
||||
}
|
||||
local fs = formlib.Builder()
|
||||
|
||||
fs("tablecolumns[text;text;text;text;text;text]")
|
||||
fs("table[0,0;11.75,9;summary_table;")
|
||||
fs("Item,Description,Buy Vol,Buy Max,Sell Vol,Sell Min")
|
||||
|
||||
local all_items = minetest.registered_items
|
||||
for i, row in ipairs(exchange:market_summary()) do
|
||||
local n = #res+1
|
||||
res[n] = "," .. row.item_name
|
||||
local def = all_items[row.item_name] or {}
|
||||
res[n+1] = "," .. (def.description or "Unknown Item")
|
||||
res[n+2] = "," .. (row.buy_volume or 0)
|
||||
res[n+3] = "," .. (row.buy_max or "N/A")
|
||||
res[n+4] = "," .. (row.sell_volume or 0)
|
||||
res[n+5] = "," .. (row.sell_min or "N/A")
|
||||
fs(",", formlib.escape(row.item_name))
|
||||
fs(",", formlib.escape(def.description or "Unknown Item"))
|
||||
fs(",", formlib.escape(row.buy_volume or 0))
|
||||
fs(",", formlib.escape(row.buy_max or "N/A"))
|
||||
fs(",", formlib.escape(row.sell_volume or 0))
|
||||
fs(",", formlib.escape(row.sell_min or "N/A"))
|
||||
end
|
||||
|
||||
res[#res+1] = "]"
|
||||
res[#res+1] = "button[3,7;2,1;back;Back]"
|
||||
fs("]")
|
||||
|
||||
summary_fs = table.concat(res)
|
||||
summary_fs = tostring(fs)
|
||||
end
|
||||
|
||||
minetest.after(0, mk_summary_fs)
|
||||
|
||||
|
||||
@ -42,26 +45,38 @@ minetest.register_globalstep(function(dtime)
|
||||
end
|
||||
end)
|
||||
|
||||
local summary_form = "global_exchange:summary"
|
||||
local function show_summary(p_name)
|
||||
minetest.show_formspec(p_name, summary_form, summary_fs)
|
||||
|
||||
local wear_levels = {
|
||||
[1] = { index = 1, text = "New (-0%)", wear = math.floor(0.00*65535) },
|
||||
[2] = { index = 2, text = "Good (-10%)", wear = math.floor(0.10*65535) },
|
||||
[3] = { index = 3, text = "Worn (-50%)", wear = math.floor(0.50*65535) },
|
||||
[4] = { index = 4, text = "Junk (-100%)", wear = math.floor(1.00*65535) },
|
||||
}
|
||||
|
||||
-- Allow lookup by text label as well as index
|
||||
for _,v in ipairs(wear_levels) do
|
||||
wear_levels[tostring(v.text)] = v
|
||||
end
|
||||
|
||||
local function wear_string(wear)
|
||||
return "-" .. math.ceil(100 * wear / 65535) .. "%"
|
||||
end
|
||||
|
||||
|
||||
local main_state = {}
|
||||
-- ^ A per-player state for the main form. It contains these values:
|
||||
-- old_fields: Keeps track of the fields before this update, when changing
|
||||
-- things slightly
|
||||
-- search_results: The last search results the player obtained
|
||||
-- last_search_time: The last time the player did a search. Used to implement
|
||||
-- a cooldown on searches
|
||||
-- sell: A boolean whether the player has sell selected
|
||||
-- ^ A per-player state for the main form.
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
exchange:new_account(player:get_player_name()) --just to make sure
|
||||
main_state[player:get_player_name()] = {
|
||||
old_fields = {},
|
||||
search_results = {},
|
||||
last_search_time = 0,
|
||||
tab = 1,
|
||||
buy_item = "",
|
||||
buy_wear = wear_levels[1].text,
|
||||
buy_price = "",
|
||||
buy_amount = "1",
|
||||
sell_price = "",
|
||||
buy_page = 1,
|
||||
selected_index = 0,
|
||||
}
|
||||
end)
|
||||
|
||||
@ -70,268 +85,293 @@ minetest.register_on_leaveplayer(function(player)
|
||||
end)
|
||||
|
||||
|
||||
-- Something similar to creative inventory
|
||||
local pagemax = 1
|
||||
local pagewidth = 12
|
||||
local pageheight = 4
|
||||
local pageitems = pagewidth * pageheight
|
||||
local selectable_list = {}
|
||||
|
||||
-- Create inventory list after loading all mods
|
||||
minetest.after(0, function()
|
||||
for name, def in pairs(minetest.registered_items) do
|
||||
if (def.groups.not_in_creative_inventory or 0) == 0 and
|
||||
(def.description or "") ~= "" then
|
||||
selectable_list[#selectable_list + 1] = name
|
||||
end
|
||||
end
|
||||
table.sort(selectable_list)
|
||||
|
||||
pagemax = math.max(math.ceil(#selectable_list / pageitems), 1)
|
||||
end)
|
||||
|
||||
local main_form = "global_exchange:exchange_main"
|
||||
|
||||
|
||||
local tablecolumns =
|
||||
"tablecolumns[text;text;text;text;text;text]"
|
||||
|
||||
|
||||
local function table_from_results(results, x, y, w, h, selected)
|
||||
local fs_tab
|
||||
|
||||
local function insert(str)
|
||||
fs_tab[#fs_tab+1] = str
|
||||
end
|
||||
|
||||
fs_tab = {
|
||||
tablecolumns,
|
||||
"table[" .. x .. "," .. y .. ";" .. w .. "," .. h .. ";",
|
||||
"result_table;",
|
||||
"Poster,Type,Item,Description,Amount,Rate"
|
||||
}
|
||||
local function table_from_results(fs, results, name, x, y, w, h, selected)
|
||||
fs("tablecolumns[text;text;text;text;text;text;text]")
|
||||
fs("table[", x, ",", y, ";", w, ",", h, ";")
|
||||
fs(formlib.escape(name), ";")
|
||||
fs("Poster,Type,Item,Description,Wear,Amount,Rate")
|
||||
|
||||
local all_items = minetest.registered_items
|
||||
|
||||
for i, row in ipairs(results) do
|
||||
insert(",")
|
||||
insert(tostring(row.Poster))
|
||||
insert(",")
|
||||
insert(tostring(row.Type))
|
||||
insert(",")
|
||||
insert(tostring(row.Item))
|
||||
insert(",")
|
||||
local def = all_items[row.Item] or {}
|
||||
insert(def.description or "Unknown Item")
|
||||
insert(",")
|
||||
insert(tostring(row.Amount))
|
||||
insert(",")
|
||||
insert(tostring(row.Rate))
|
||||
fs(",", formlib.escape(row.Poster))
|
||||
fs(",", formlib.escape(row.Type))
|
||||
fs(",", formlib.escape(row.Item))
|
||||
fs(",", formlib.escape(def.description or "Unknown Item"))
|
||||
if row.Wear > 0 then
|
||||
fs(",", formlib.escape("-" .. math.ceil(100 * row.Wear / 65535) .. "%"))
|
||||
else
|
||||
fs(",---")
|
||||
end
|
||||
fs(",", formlib.escape(row.Amount))
|
||||
fs(",", formlib.escape(row.Rate))
|
||||
end
|
||||
|
||||
if selected and selected ~= "" then
|
||||
insert(";")
|
||||
insert(selected)
|
||||
end
|
||||
insert("]")
|
||||
|
||||
return table.concat(fs_tab)
|
||||
local sel_num = math.max(0, tonumber(selected) or 0)
|
||||
fs(";", sel_num + 1, "]")
|
||||
end
|
||||
|
||||
local function mk_main_market_fs(fs, p_name, state)
|
||||
fs(summary_fs)
|
||||
end
|
||||
|
||||
local function mk_main_fs(p_name, new_item, err_str, success)
|
||||
local function mk_main_order_book_fs(fs, p_name, x, y, w, h, item_name)
|
||||
local order_book = exchange:order_book("", item_name)
|
||||
|
||||
fs("tablecolumns[text;text;text;text]")
|
||||
fs("table[", x, ",", y, ";", w, ",", h, ";", "order_book;")
|
||||
fs("Type,Rate,Wear,Amount")
|
||||
|
||||
for _,row in ipairs(order_book) do
|
||||
fs(",", formlib.escape(row.Type))
|
||||
fs(",", formlib.escape(row.Rate))
|
||||
if row.Wear > 0 then
|
||||
fs(",", formlib.escape(wear_string(row.Wear)))
|
||||
else
|
||||
fs(",---")
|
||||
end
|
||||
fs(",", formlib.escape(row.Amount))
|
||||
end
|
||||
|
||||
fs(";1]")
|
||||
end
|
||||
|
||||
local function mk_main_buy_fs(fs, p_name, state)
|
||||
mk_main_order_book_fs(fs, p_name, 0, 0, 8.75, 3.75, state.buy_item)
|
||||
|
||||
fs:item_image_button(9,0, 1,1, "buy_item", state.buy_item)
|
||||
|
||||
fs:field(10.25,0.40, 2,1, "buy_amount", "Quantity", state.buy_amount, false)
|
||||
|
||||
local wear = wear_levels[state.buy_wear] or wear_levels[1]
|
||||
fs("dropdown[9,1;3;buy_wear;")
|
||||
local sep = nil
|
||||
for _,v in ipairs(wear_levels) do
|
||||
if sep then fs(sep) end
|
||||
fs:escape(v.text)
|
||||
sep = ","
|
||||
end
|
||||
fs(";", wear.index, "]")
|
||||
|
||||
fs:field(9.35,2.40, 2.9,1, "buy_price", "Bid (ea.)", state.buy_price, false)
|
||||
|
||||
fs:button(9,3, 3,1, "buy", "Place Bid")
|
||||
|
||||
fs:container(0,4, function()
|
||||
fs:button( 0,0.25, 1,1, "buy_left", "<<")
|
||||
fs:button( 5,0.25, 2,1, "position", state.buy_page .. "/" .. pagemax)
|
||||
fs:button(11,0.25, 1,1, "buy_right", ">>")
|
||||
|
||||
local firstitem = ((state.buy_page - 1) * pageitems)
|
||||
for y=0,(pageheight-1) do
|
||||
for x=0,(pagewidth-1) do
|
||||
local index = firstitem + (pagewidth * y) + x + 1
|
||||
if selectable_list[index] then
|
||||
fs:item_image_button(x,1.25+y, 1,1, "select_" .. index,
|
||||
selectable_list[index])
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function mk_main_sell_fs(fs, p_name, state)
|
||||
local sell_stack = global_inv:get_stack("p_" .. p_name, 1)
|
||||
local sell_item = (not sell_stack:is_empty()
|
||||
and sell_stack:get_name()) or ""
|
||||
|
||||
mk_main_order_book_fs(fs, p_name, 0, 0, 8.75, 3.75, sell_item)
|
||||
|
||||
fs:list(9,0, 1,1, "detached:global_exchange", "p_" .. p_name)
|
||||
|
||||
fs:field(9.35,2.40, 2.9,1, "sell_price", "Ask (ea.)", state.sell_price, false)
|
||||
|
||||
fs:button(9,3, 3,1, "sell", "Sell")
|
||||
|
||||
fs:box(1.9375,5.1875, 7.96875,4.03, "#00000020")
|
||||
|
||||
fs:list(2,5.25, 8,4, "current_player", "main")
|
||||
end
|
||||
|
||||
local function mk_main_own_orders_fs(fs, p_name, state)
|
||||
if not state.own_results then
|
||||
state.own_results = exchange:search_player_orders(p_name) or {}
|
||||
end
|
||||
|
||||
state.selected_index = math.min(state.selected_index or 0, #state.own_results)
|
||||
|
||||
table_from_results(fs, state.own_results, "result_table", 0, 0, 11.75, 8.5, state.selected_index)
|
||||
fs:button(4.5,8.5, 3,1, "cancel", "Cancel Order")
|
||||
end
|
||||
|
||||
local main_tabs = {
|
||||
[1] = { text = "Market", mk_fs = mk_main_market_fs },
|
||||
[2] = { text = "Buy", mk_fs = mk_main_buy_fs },
|
||||
[3] = { text = "Sell", mk_fs = mk_main_sell_fs },
|
||||
[4] = { text = "My Orders", mk_fs = mk_main_own_orders_fs },
|
||||
}
|
||||
|
||||
local function mk_main_fs(fs, p_name, err_str, success)
|
||||
local state = main_state[p_name]
|
||||
if not state then return end -- Should have been initialized on player join
|
||||
|
||||
fs:size(12,10)
|
||||
fs:bgcolor("#606060", false)
|
||||
|
||||
local old_fields = state.old_fields
|
||||
local results = state.search_results
|
||||
local item_def = new_item or old_fields.item or ""
|
||||
local amount_def = old_fields.amount or ""
|
||||
local rate_def = old_fields.rate or ""
|
||||
local sell_def = state.sell or false
|
||||
local selected_def = old_fields.selected or ""
|
||||
fs("tabheader[0,0.65;tab;")
|
||||
local sep = nil
|
||||
for _,tab in ipairs(main_tabs) do
|
||||
if sep then fs(sep) end
|
||||
fs:escape(tab.text)
|
||||
sep = ","
|
||||
end
|
||||
fs(";", state.tab or 1, ";false;true]")
|
||||
|
||||
local bal = exchange:get_balance(p_name)
|
||||
|
||||
local fs
|
||||
if bal then
|
||||
fs = "label[0,0;Balance: " .. bal .. "]"
|
||||
else
|
||||
fs = "label[0.2,0.5;Use an ATM to make your account.]"
|
||||
end
|
||||
|
||||
fs = fs .. "button[4,0;2,1;summary;Market Summary]" ..
|
||||
"button[6,0;2,1;your_orders;Your Orders]" ..
|
||||
"field[0.2,1.5;3,1;item;Item: ;" .. item_def .. "]" ..
|
||||
"field[3.2,1.5;3,1;amount;Amount: ;" .. amount_def .. "]" ..
|
||||
"button[6,1;2,1.4;select_item;Select Item]" ..
|
||||
"checkbox[5,3;sell;Sell;" .. tostring(sell_def) .. "]" ..
|
||||
"field[0.2,2.5;2,1;rate;Rate: ;" .. rate_def .. "]" ..
|
||||
"button[2,2;2,1.4;search;Search]" ..
|
||||
"button[4,2;3,1.4;post_order;Post Order]"
|
||||
fs:label(0,0.37, "Balance: " .. bal)
|
||||
|
||||
if err_str then
|
||||
fs = fs .. "label[0,3;Error: " .. err_str .. "]"
|
||||
fs:label(4,0.37, err_str)
|
||||
elseif success then
|
||||
fs:label(4,0.37, "Success!")
|
||||
end
|
||||
|
||||
if success then
|
||||
fs = fs .. "label[0,3;Success!]"
|
||||
if main_tabs[state.tab] then
|
||||
fs:container(0,1, main_tabs[state.tab].mk_fs, p_name, state)
|
||||
end
|
||||
|
||||
return "size[8,9]" .. fs .. table_from_results(results, 0, 4, 8, 5, selected_def)
|
||||
end
|
||||
|
||||
|
||||
local function show_main(p_name, new_item, err_str, success)
|
||||
minetest.show_formspec(p_name, main_form, mk_main_fs(p_name, new_item, err_str, success))
|
||||
local function show_main(p_name, err_str, success)
|
||||
local fs = formlib.Builder()
|
||||
mk_main_fs(fs, p_name, err_str, success)
|
||||
minetest.show_formspec(p_name, main_form, tostring(fs))
|
||||
end
|
||||
|
||||
|
||||
-- Something similar to creative inventory
|
||||
local pagemax = 1
|
||||
|
||||
-- Create detached inventory after loading all mods
|
||||
minetest.after(0, function()
|
||||
local inv = minetest.create_detached_inventory("global_exchange", {
|
||||
allow_move = function(inv, from_list, _, to_list, _,_, player)
|
||||
local p_name = player:get_player_name()
|
||||
|
||||
if from_list == "main"
|
||||
and to_list == "p_" .. p_name then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
allow_put = function()
|
||||
return 0
|
||||
end,
|
||||
allow_take = function()
|
||||
return 0
|
||||
end,
|
||||
on_move = function(inv, _, _, _, _, _, player)
|
||||
local p_name = player:get_player_name()
|
||||
local p_list = "p_" .. p_name
|
||||
|
||||
local item_name = inv:get_list(p_list)[1]:get_name()
|
||||
inv:set_list(p_list, {})
|
||||
inv:add_item("main", item_name)
|
||||
show_main(p_name, item_name)
|
||||
end,
|
||||
})
|
||||
|
||||
local selectable_list,n = {},1
|
||||
for name, def in pairs(minetest.registered_items) do
|
||||
if (not def.groups.not_in_creative_inventory
|
||||
or def.groups.not_in_creative_inventory == 0)
|
||||
and def.description
|
||||
and def.description ~= "" then
|
||||
selectable_list[n] = name
|
||||
n = n+1
|
||||
end
|
||||
end
|
||||
table.sort(selectable_list)
|
||||
inv:set_size("main", #selectable_list)
|
||||
for _,itemstring in ipairs(selectable_list) do
|
||||
inv:add_item("main", ItemStack(itemstring))
|
||||
end
|
||||
|
||||
pagemax = math.ceil((#selectable_list - 1) / (8 * 4))
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
-- the inventory list name is "p_"..player_name
|
||||
minetest.get_inventory({
|
||||
type="detached",
|
||||
name="global_exchange"
|
||||
}):set_size("p_" .. player:get_player_name(), 1)
|
||||
-- the inventory list name (for selling) is "p_"..player_name
|
||||
global_inv:set_size("p_" .. player:get_player_name(), 1)
|
||||
end)
|
||||
|
||||
|
||||
local select_form = "global_exchange:select_form"
|
||||
|
||||
|
||||
local function mk_select_formspec(p_name, start_i, pagenum)
|
||||
return "size[9.3,8]" ..
|
||||
"list[detached:global_exchange;main;0.3,0.5;8,4;" .. tostring(start_i) .. "]" ..
|
||||
"button[0.3,4.5;1.6,1;select_prev;<<]"..
|
||||
"button[6.7,4.5;1.6,1;select_next;>>]"..
|
||||
"label[2.0,5.55;"..tostring(math.floor(pagenum)).."/"..tostring(pagemax).."]"..
|
||||
"list[detached:global_exchange;p_" .. p_name .. ";0.3,7;1,1;]"
|
||||
end
|
||||
|
||||
|
||||
local player_pages = {}
|
||||
|
||||
|
||||
local function show_select(p_name)
|
||||
local pagenum = player_pages[p_name] or 1
|
||||
local start_i = (pagenum - 1) * 8 * 4
|
||||
|
||||
local fs = mk_select_formspec(p_name, start_i, pagenum)
|
||||
minetest.show_formspec(p_name, select_form, fs)
|
||||
end
|
||||
|
||||
|
||||
local own_form = "global_exchange:my_orders"
|
||||
|
||||
local own_state = {}
|
||||
-- ^ Per=player state for the own orders form. Contains these fields:
|
||||
-- selected_index: The selected index
|
||||
-- own_results: Results for own orders.
|
||||
|
||||
local function mk_own_orders_fs(p_name, results, selected)
|
||||
return "size[8,8]" ..
|
||||
"label[0.5,0.2;Your Orders]" ..
|
||||
"button[6,0;2,1;refresh;Refresh]" ..
|
||||
table_from_results(results, 0, 2, 8, 4.5, selected or "") ..
|
||||
"button[0,7;2,1;cancel;Cancel]" ..
|
||||
"button[3,7;2,1;back;Back]"
|
||||
end
|
||||
|
||||
|
||||
local function show_own_orders(p_name, results, selected)
|
||||
minetest.show_formspec(p_name, own_form, mk_own_orders_fs(p_name, results, selected))
|
||||
end
|
||||
|
||||
|
||||
-- Returns success, and also returns an error message if failed.
|
||||
local function post_order(player, ex_name, order_type, item_name, amount_str, rate_str)
|
||||
local function post_buy(player, ex_name, item_name, wear_str, amount_str, rate_str)
|
||||
local p_name = player:get_player_name()
|
||||
|
||||
if item_name == "" then
|
||||
if (item_name or "") == "" then
|
||||
return false, "You must input an item"
|
||||
end
|
||||
|
||||
if not minetest.registered_items[item_name] then
|
||||
elseif not minetest.registered_items[item_name] then
|
||||
return false, "That item does not exist."
|
||||
end
|
||||
|
||||
local wear_level = wear_levels[wear_str]
|
||||
if not wear_level then
|
||||
return false, "Invalid wear."
|
||||
end
|
||||
|
||||
local amount = tonumber(amount_str)
|
||||
local rate = tonumber(rate_str)
|
||||
local rate = tonumber(rate_str)
|
||||
|
||||
if not amount then
|
||||
if not amount or not is_integer(amount) or amount < 1 then
|
||||
return false, "Invalid amount."
|
||||
end
|
||||
|
||||
if not rate then
|
||||
elseif not rate or not is_integer(rate) or rate < 1 then
|
||||
return false, "Invalid rate."
|
||||
end
|
||||
|
||||
if amount > 1000 then
|
||||
return false, "Max amount is 1000"
|
||||
end
|
||||
|
||||
local p_inv = player:get_inventory()
|
||||
local stack = ItemStack(item_name)
|
||||
stack:set_count(amount)
|
||||
|
||||
if order_type == "buy" then
|
||||
if not p_inv:room_for_item("main", stack) then
|
||||
return false, "Not enough space in inventory."
|
||||
end
|
||||
|
||||
local succ, res = exchange:buy(p_name, ex_name, item_name, amount, rate)
|
||||
if not succ then
|
||||
return false, res
|
||||
end
|
||||
|
||||
stack:set_count(res)
|
||||
p_inv:add_item("main", stack)
|
||||
else
|
||||
if not p_inv:contains_item("main", stack) then
|
||||
return false, "Items not in inventory."
|
||||
end
|
||||
|
||||
local succ, res = exchange:sell(p_name, ex_name, item_name, amount, rate)
|
||||
if not succ then
|
||||
return false, res
|
||||
end
|
||||
|
||||
p_inv:remove_item("main", stack)
|
||||
local succ, res = exchange:buy(p_name, ex_name, item_name, wear_level.wear, amount, rate)
|
||||
if not succ then
|
||||
return false, res
|
||||
end
|
||||
|
||||
for _,row in ipairs(res) do
|
||||
stack:set_count(row.amount)
|
||||
stack:set_wear(row.wear)
|
||||
|
||||
local leftover = p_inv:add_item("main", stack)
|
||||
|
||||
-- Put anything that won't fit in the inventory in the player's inbox
|
||||
if not leftover:is_empty() then
|
||||
exchange:put_in_inbox(p_name, item_name, row.wear, leftover:get_count())
|
||||
end
|
||||
end
|
||||
|
||||
-- Refresh market summary "soonish"
|
||||
elapsed = math.max(elapsed, summary_interval - 5)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Returns success, and also returns an error message if failed.
|
||||
-- The item to sell is determined by the player's list in global_inv.
|
||||
local function post_sell(player, ex_name, rate_str)
|
||||
local p_name = player:get_player_name()
|
||||
local stack = global_inv:get_stack("p_" .. p_name, 1)
|
||||
|
||||
if not stack or stack:is_empty() then
|
||||
return false, "You must input an item"
|
||||
elseif not minetest.registered_items[stack:get_name()] then
|
||||
return false, "That item does not exist."
|
||||
end
|
||||
|
||||
if stack.get_meta then
|
||||
local meta = stack:get_meta()
|
||||
local def_meta = ItemStack(stack:get_name()):get_meta()
|
||||
|
||||
if not stack:get_meta():equals(def_meta) then
|
||||
return false, "Cannot sell an item with metadata."
|
||||
end
|
||||
elseif (stack:get_metadata() or "") ~= "" then
|
||||
return false, "Cannot sell an item with metadata."
|
||||
end
|
||||
|
||||
local rate = tonumber(rate_str)
|
||||
|
||||
if not rate or not is_integer(rate) or rate < 1 then
|
||||
return false, "Invalid rate."
|
||||
end
|
||||
|
||||
local item_name = stack:get_name()
|
||||
local wear = stack:get_wear()
|
||||
local amount = stack:get_count()
|
||||
|
||||
local succ, res = exchange:sell(p_name, ex_name, item_name, wear, amount, rate)
|
||||
if not succ then
|
||||
return false, res
|
||||
end
|
||||
|
||||
stack:clear()
|
||||
global_inv:set_stack("p_" .. p_name, 1, stack)
|
||||
|
||||
-- Refresh market summary "soonish"
|
||||
elapsed = math.max(elapsed, summary_interval - 5)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@ -339,185 +379,113 @@ end
|
||||
local function handle_main(player, fields)
|
||||
local p_name = player:get_player_name()
|
||||
local state = main_state[p_name]
|
||||
local old_fields = state.old_fields
|
||||
|
||||
for k, v in pairs(fields) do
|
||||
old_fields[k] = v
|
||||
local copy_fields = {
|
||||
"buy_wear",
|
||||
"buy_amount",
|
||||
"buy_price",
|
||||
"sell_price"
|
||||
}
|
||||
for _,k in ipairs(copy_fields) do
|
||||
if fields[k] then
|
||||
state[k] = fields[k]
|
||||
end
|
||||
end
|
||||
|
||||
if fields.select_item then
|
||||
show_select(p_name)
|
||||
end
|
||||
|
||||
if fields.search then
|
||||
local now = os.time()
|
||||
local last_search = state.last_search_time
|
||||
|
||||
if now - last_search < search_cooldown then
|
||||
show_main(p_name, nil, "Please wait before searching again.")
|
||||
return
|
||||
end
|
||||
|
||||
-- If the player is selling, she wants "buy" type offers.
|
||||
local order_type
|
||||
if state.sell then
|
||||
order_type = "buy"
|
||||
else
|
||||
order_type = "sell"
|
||||
end
|
||||
local item_name = fields["item"]
|
||||
|
||||
local results = exchange:search_orders("", order_type, item_name)
|
||||
state.search_results = results
|
||||
state.last_search_time = now
|
||||
|
||||
if fields.tab then
|
||||
state.tab = tonumber(fields.tab) or 1
|
||||
show_main(p_name)
|
||||
end
|
||||
|
||||
if fields.buy_left then
|
||||
state.buy_page = (((state.buy_page or 1) + ((2*pagemax-1) - 1)) % pagemax) + 1
|
||||
show_main(p_name)
|
||||
end
|
||||
|
||||
if fields.buy_right then
|
||||
state.buy_page = (((state.buy_page or 1) + ((2*pagemax-1) + 1)) % pagemax) + 1
|
||||
show_main(p_name)
|
||||
end
|
||||
|
||||
for name in pairs(fields) do
|
||||
local index = tonumber(string.match(name, "select_([0-9]+)"))
|
||||
if index and index >= 1 and index < #selectable_list then
|
||||
state.buy_item = selectable_list[index]
|
||||
show_main(p_name)
|
||||
end
|
||||
end
|
||||
|
||||
if fields.buy then
|
||||
local succ, err =
|
||||
post_buy(player, "", state.buy_item, fields.buy_wear,
|
||||
fields.buy_amount, fields.buy_price)
|
||||
if succ then
|
||||
state.buy_amount = "1"
|
||||
state.buy_price = ""
|
||||
state.own_results = nil
|
||||
show_main(p_name, nil, true)
|
||||
else
|
||||
show_main(p_name, err)
|
||||
end
|
||||
end
|
||||
|
||||
if fields.sell then
|
||||
state.sell = fields.sell == "true"
|
||||
end
|
||||
|
||||
|
||||
if fields.post_order then
|
||||
local now = os.time()
|
||||
local last_search = state.last_search_time
|
||||
|
||||
if now - last_search < search_cooldown then
|
||||
show_main(p_name, nil, "Please wait before posting.")
|
||||
return
|
||||
end
|
||||
|
||||
local order_type
|
||||
if state.sell then
|
||||
order_type = "sell"
|
||||
else
|
||||
order_type = "buy"
|
||||
end
|
||||
|
||||
local succ, err =
|
||||
post_order(player, "", order_type, fields.item, fields.amount, fields.rate)
|
||||
|
||||
local succ, err = post_sell(player, "", fields.sell_price)
|
||||
if succ then
|
||||
state.search_results = {}
|
||||
show_main(p_name, nil, nil, true)
|
||||
state.sell_price = ""
|
||||
state.own_results = nil
|
||||
show_main(p_name, nil, true)
|
||||
else
|
||||
show_main(p_name, nil, err)
|
||||
show_main(p_name, err)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if fields.result_table then
|
||||
local results = state.search_results
|
||||
local event = minetest.explode_table_event(fields.result_table)
|
||||
|
||||
if event.type ~= "CHG" then
|
||||
return
|
||||
end
|
||||
|
||||
local index = event.row - 1
|
||||
result = results[index]
|
||||
|
||||
if result then
|
||||
old_fields.amount = tostring(result.Amount)
|
||||
old_fields.rate = tostring(result.Rate)
|
||||
end
|
||||
|
||||
show_main(p_name)
|
||||
end
|
||||
|
||||
if fields.summary then
|
||||
show_summary(p_name)
|
||||
end
|
||||
|
||||
if fields.your_orders then
|
||||
if not own_state[p_name] then
|
||||
own_state[p_name] = {}
|
||||
end
|
||||
local o_state = own_state[p_name]
|
||||
|
||||
o_state.own_results = exchange:search_player_orders(p_name) or {}
|
||||
|
||||
show_own_orders(p_name, o_state.own_results)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function handle_select(player, fields)
|
||||
local p_name = player:get_player_name()
|
||||
|
||||
local pagenum = player_pages[p_name] or 1
|
||||
|
||||
if fields.select_prev then
|
||||
player_pages[p_name] = math.max(1, pagenum - 1)
|
||||
show_select(p_name)
|
||||
elseif fields.select_next then
|
||||
player_pages[p_name] = math.min(pagemax, pagenum + 1)
|
||||
show_select(p_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function handle_own_orders(player, fields)
|
||||
local p_name = player:get_player_name()
|
||||
|
||||
local state = own_state[p_name] or {}
|
||||
local results = state.own_results or {}
|
||||
local idx = state.selected_index
|
||||
local own_results = state.own_results or {}
|
||||
|
||||
if fields.refresh then
|
||||
state.own_results = exchange:search_player_orders(p_name) or {}
|
||||
show_own_orders(p_name, state.own_results)
|
||||
end
|
||||
|
||||
if fields.cancel and idx then
|
||||
local row = results[idx]
|
||||
if not row then return true end
|
||||
local p_inv = player:get_inventory()
|
||||
|
||||
local amount = row.Amount
|
||||
local item = row.Item
|
||||
local stack = ItemStack(item)
|
||||
stack:set_count(amount)
|
||||
if row.Type == "sell" then
|
||||
if not p_inv:room_for_item("main", stack) then
|
||||
show_own_orders(p_name, state.own_results, "Not enough room.")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local succ, err =
|
||||
exchange:cancel_order(p_name, row.Id, row.Type, row.Item, row.Amount, row.Rate)
|
||||
if fields.cancel and own_results[idx] then
|
||||
local succ, res = exchange:cancel_order(p_name, own_results[idx].Id)
|
||||
if succ then
|
||||
table.remove(results, idx)
|
||||
if row.Type == "sell" then
|
||||
p_inv:add_item("main", stack)
|
||||
if res.Type == "sell" then
|
||||
local p_inv = player:get_inventory()
|
||||
local stack = ItemStack(res.Item)
|
||||
stack:set_count(res.Amount)
|
||||
stack:set_wear(res.Wear)
|
||||
local leftover = p_inv:add_item("main", stack)
|
||||
|
||||
-- Put anything that won't fit in the inventory in the player's inbox
|
||||
if not leftover:is_empty() then
|
||||
exchange:put_in_inbox(p_name, res.Item, res.Wear, leftover:get_count())
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Refresh the results, since there might have been a problem.
|
||||
state.own_results = exchange:search_player_orders(p_name) or {}
|
||||
|
||||
-- Refresh market summary "soonish"
|
||||
elapsed = math.max(elapsed, summary_interval - 5)
|
||||
end
|
||||
|
||||
show_own_orders(p_name, state.own_results)
|
||||
state.own_results = nil
|
||||
show_main(p_name)
|
||||
end
|
||||
|
||||
if fields.result_table then
|
||||
local event = minetest.explode_table_event(fields.result_table)
|
||||
if event.type == "CHG" then
|
||||
state.selected_index = event.row - 1
|
||||
show_own_orders(p_name, results, event.row)
|
||||
show_main(p_name)
|
||||
end
|
||||
end
|
||||
|
||||
if fields.back then
|
||||
show_main(p_name)
|
||||
end
|
||||
end
|
||||
if fields.quit then
|
||||
-- Return the player's unsold inventory, if any
|
||||
local stack = global_inv:get_stack("p_" .. p_name, 1)
|
||||
local p_inv = player:get_inventory()
|
||||
local leftover = p_inv:add_item("main", stack)
|
||||
|
||||
-- Whatever doesn't fit in the player's inventory stays in the form.
|
||||
-- Note that any items in the form when the server exits are lost.
|
||||
global_inv:set_stack("p_" .. p_name, 1, leftover)
|
||||
|
||||
local function handle_summary(player, fields)
|
||||
if fields.back then
|
||||
show_main(player:get_player_name())
|
||||
state.own_results = nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -525,12 +493,6 @@ end
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == main_form then
|
||||
handle_main(player, fields)
|
||||
elseif formname == select_form then
|
||||
handle_select(player, fields)
|
||||
elseif formname == own_form then
|
||||
handle_own_orders(player, fields)
|
||||
elseif formname == summary_form then
|
||||
handle_summary(player, fields)
|
||||
else
|
||||
return
|
||||
end
|
||||
@ -538,6 +500,37 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
end)
|
||||
|
||||
|
||||
global_inv = minetest.create_detached_inventory("global_exchange", {
|
||||
allow_move = function(inv,from_list,from_index,to_list,to_index,count,player)
|
||||
return 0
|
||||
end,
|
||||
allow_put = function(inv,to_list,to_index,stack,player)
|
||||
local p_name = player:get_player_name()
|
||||
|
||||
if to_list == "p_" .. p_name then
|
||||
return stack:get_count()
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
allow_take = function(inv,from_list,from_index,stack,player)
|
||||
local p_name = player:get_player_name()
|
||||
|
||||
if from_list == "p_" .. p_name then
|
||||
return stack:get_count()
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
on_put = function(inv,to_list,to_index,stack,player)
|
||||
show_main(player:get_player_name())
|
||||
end,
|
||||
on_take = function(inv,from_list,from_index,stack,player)
|
||||
show_main(player:get_player_name())
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_node("global_exchange:exchange", {
|
||||
description = "Exchange Terminal",
|
||||
drawtype = "nodebox",
|
||||
@ -552,18 +545,19 @@ minetest.register_node("global_exchange:exchange", {
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2},
|
||||
is_ground_content = false,
|
||||
stack_max = 1,
|
||||
light_source = 3,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-8/16, -4/16, 3/16, 0.5, 0.5, 5/16},--screens
|
||||
{-1/16, -7/16, 5/16, 1/16, 5/16, 7/16},--screen leg
|
||||
{-3/16, -8/16, 4/16, 3/16, -7/16, 8/16},--leg platform
|
||||
{-8/16, -4/16, 3/16, 8/16, 8/16, 5/16},--screens
|
||||
{-1/16, -7/16, 5/16, 1/16, 5/16, 7/16},--screen leg
|
||||
{-3/16, -8/16, 4/16, 3/16, -7/16, 8/16},--leg platform
|
||||
{-7/16, -8/16, -8/16, 2/16, -6/16, -3/16},--keyboard
|
||||
{3/16, -8/16, -3/16, 7/16, -7/16, 3/16},--phone low
|
||||
{4/16, -7/16, -1/16, 6/16, -6/16, 3/16},--phone hi
|
||||
{2/16, -7/16, 0, 8/16, -5/16, 2/16},--phone speaker
|
||||
{ 3/16, -8/16, -3/16, 7/16, -7/16, 3/16},--phone low
|
||||
{ 4/16, -7/16, -1/16, 6/16, -6/16, 3/16},--phone hi
|
||||
{ 2/16, -7/16, 0/16, 8/16, -5/16, 2/16},--phone speaker
|
||||
}
|
||||
},
|
||||
on_rightclick = function(_, _, clicker)
|
||||
@ -581,8 +575,9 @@ minetest.register_node("global_exchange:exchange", {
|
||||
minetest.register_craft( {
|
||||
output = "global_exchange:exchange",
|
||||
recipe = {
|
||||
{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
|
||||
{ "default:mese_crystal", "default:steel_ingot", "default:diamond" },
|
||||
{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
|
||||
{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
|
||||
{ "default:mese_crystal", "default:steel_ingot", "default:diamond" },
|
||||
{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
|
||||
}
|
||||
})
|
||||
-- vim:set ts=4 sw=4 noet:
|
||||
|
92
formlib.lua
Normal file
92
formlib.lua
Normal file
@ -0,0 +1,92 @@
|
||||
local formlib = {}
|
||||
local builder_methods = {}
|
||||
|
||||
function formlib.escape(x)
|
||||
return minetest.formspec_escape(tostring(x or ""))
|
||||
end
|
||||
|
||||
function builder_methods.escape(fs, ...)
|
||||
for _,x in ipairs({...}) do
|
||||
if x then fs(formlib.escape(x)) end
|
||||
end
|
||||
end
|
||||
|
||||
function builder_methods.size(fs, w,h, fixed)
|
||||
if fixed == false then
|
||||
fs("size[", w, ",", h, ",false]")
|
||||
elseif fixed then
|
||||
fs("size[", w, ",", h, ",true]")
|
||||
else
|
||||
fs("size[", w, ",", h, "]")
|
||||
end
|
||||
end
|
||||
|
||||
function builder_methods.bgcolor(fs, color, fullscreen)
|
||||
if fullscreen == false then
|
||||
fs("bgcolor[", formlib.escape(color), ";false]")
|
||||
elseif fullscreen then
|
||||
fs("bgcolor[", formlib.escape(color), ";true]")
|
||||
else
|
||||
fs("bgcolor[", formlib.escape(color), "]")
|
||||
end
|
||||
end
|
||||
|
||||
function builder_methods.list(fs, x,y, w,h, inv_loc, inv_list, start_idx)
|
||||
fs("list[", formlib.escape(inv_loc), ";", formlib.escape(inv_list), ";",
|
||||
x, ",", y, ";", w, ",", h, ";", formlib.escape(start_idx), "]")
|
||||
end
|
||||
|
||||
function builder_methods.button(fs, x,y, w,h, name, text)
|
||||
fs("button[", x, ",", y, ";", w, ",", h, ";",
|
||||
formlib.escape(name), ";", formlib.escape(text), "]")
|
||||
end
|
||||
|
||||
function builder_methods.item_image_button(fs, x,y, w,h, name, item, text)
|
||||
fs("item_image_button[", x, ",", y, ";", w, ",", h, ";",
|
||||
formlib.escape(item), ";", formlib.escape(name), ";",
|
||||
formlib.escape(text), "]")
|
||||
end
|
||||
|
||||
function builder_methods.label(fs, x,y, text)
|
||||
fs("label[", x, ",", y, ";", formlib.escape(text), "]")
|
||||
end
|
||||
|
||||
function builder_methods.field(fs, x,y, w,h, name, label, default, close_on_enter)
|
||||
fs("field[", x, ",", y, ";", w, ",", h, ";",
|
||||
formlib.escape(name), ";", formlib.escape(label), ";",
|
||||
formlib.escape(default), "]")
|
||||
|
||||
if close_on_enter == false then
|
||||
fs("field_close_on_enter[", formlib.escape(name), ";false]")
|
||||
end
|
||||
end
|
||||
|
||||
function builder_methods.container(fs, x,y, sub_fn, ...)
|
||||
fs("container[", x, ",", y, "]")
|
||||
sub_fn(fs, ...)
|
||||
fs("container_end[]")
|
||||
end
|
||||
|
||||
function builder_methods.box(fs, x,y, w,h, color)
|
||||
fs("box[", x, ",", y, ";", w, ",", h, ";", formlib.escape(color), "]")
|
||||
end
|
||||
|
||||
local builder_meta = {
|
||||
__metatable = "protected",
|
||||
__index = builder_methods,
|
||||
__call = function(fs, ...)
|
||||
for _,x in ipairs({...}) do
|
||||
if x then table.insert(fs, tostring(x)) end
|
||||
end
|
||||
end,
|
||||
__tostring = table.concat,
|
||||
}
|
||||
|
||||
function formlib.Builder()
|
||||
local fs = {}
|
||||
setmetatable(fs, builder_meta)
|
||||
return fs
|
||||
end
|
||||
|
||||
return formlib
|
||||
-- vim:set ts=4 sw=4 noet:
|
70
init.lua
70
init.lua
@ -1,53 +1,43 @@
|
||||
|
||||
local insecure_env = minetest.request_insecure_environment()
|
||||
assert(insecure_env,
|
||||
"global_exchange needs to be trusted to run under mod security.")
|
||||
assert(insecure_env, "global_exchange needs to be trusted to run under mod security.")
|
||||
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. "/"
|
||||
|
||||
local income = tonumber(minetest.setting_get("citizens_income")) or 10
|
||||
local income_interval = 1200
|
||||
local income_msg = "You receive your citizen's income (+" .. income .. ")"
|
||||
|
||||
local next_payout = os.time() + income_interval
|
||||
|
||||
local exchange =
|
||||
assert(loadfile(modpath .. "exchange.lua"))(insecure_env).open_exchange(
|
||||
minetest.get_worldpath() .. "/global_exchange.db"
|
||||
)
|
||||
local exchange = assert(loadfile(modpath .. "exchange.lua"))(insecure_env).
|
||||
open_exchange(minetest.get_worldpath() .. "/global_exchange.db")
|
||||
|
||||
local formlib = assert(loadfile(modpath .. "formlib.lua"))()
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
exchange:close()
|
||||
exchange:close()
|
||||
end)
|
||||
|
||||
|
||||
local function check_giving()
|
||||
local now = os.time()
|
||||
if now < next_payout then
|
||||
return
|
||||
end
|
||||
|
||||
next_payout = now + income_interval
|
||||
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local p_name = player:get_player_name()
|
||||
|
||||
local succ = exchange:give_credits(p_name, income,
|
||||
"Citizen's Income (+" .. income .. ")")
|
||||
|
||||
if succ then
|
||||
minetest.chat_send_player(p_name, income_msg)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.after(5, check_giving)
|
||||
local function handle_setbalance_command(caller, name, newbalance)
|
||||
return exchange:set_balance(name, newbalance)
|
||||
end
|
||||
|
||||
minetest.after(5, check_giving)
|
||||
minetest.register_privilege("balance", {
|
||||
description = "Can use /setbalance",
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("setbalance", {
|
||||
params = "[<name>] <balance>",
|
||||
description = "set a player's trading balance",
|
||||
privs = {balance=true},
|
||||
func = function(caller, param)
|
||||
local name, balancestr = string.match(param, "([^ ]+) ([0-9]+)")
|
||||
if not name or not balancestr then
|
||||
name = caller
|
||||
balancestr = string.match(param, "([0-9]+)")
|
||||
if not balancestr then
|
||||
return false, "Invalid parameters (see /help setbalance)"
|
||||
end
|
||||
end
|
||||
return handle_setbalance_command(caller, name, tonumber(balancestr))
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
assert(loadfile(modpath .. "atm.lua"))(exchange)
|
||||
assert(loadfile(modpath .. "exchange_machine.lua"))(exchange)
|
||||
assert(loadfile(modpath .. "digital_mailbox.lua"))(exchange)
|
||||
assert(loadfile(modpath .. "atm.lua"))(exchange, formlib)
|
||||
assert(loadfile(modpath .. "exchange_machine.lua"))(exchange, formlib)
|
||||
assert(loadfile(modpath .. "digital_mailbox.lua"))(exchange, formlib)
|
||||
|
Loading…
Reference in New Issue
Block a user