Expand formlib & distinguish items by wear in market summary.

This commit is contained in:
Jesse McDonald 2017-06-03 16:40:32 -05:00
父節點 2a5a726cc0
當前提交 808b3cbf04
共有 5 個檔案被更改,包括 220 行新增161 行删除

19
atm.lua
查看文件

@ -33,7 +33,7 @@ local function wire_fs(fs, p_name)
fs:size(4,5)
if balance then
-- To prevent duplicates
-- To detect duplicate/stale form submission
fs:field(-100, -100, 0,0, "trans_id", "", unique())
fs:label(0.50,0.325, "Balance: " .. balance)
@ -79,13 +79,12 @@ local function log_fs(fs, p_name)
fs:label(0,0, "Transaction Log")
fs("tablecolumns[text;text]")
fs:element("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))
fs(","):escape_list(entry.Time, entry.Message)
end
fs("]")
fs:button(6,7.5, 2,1, "logout", "Log Out")
@ -100,9 +99,6 @@ local function main_menu_fs(fs, p_name)
end
local trans_ids = {}
local function show_atm_form(fs_fn, p_name, ...)
local fs = formlib.Builder()
fs_fn(fs, p_name, ...)
@ -110,15 +106,18 @@ local function show_atm_form(fs_fn, p_name, ...)
end
local trans_ids = {}
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= atm_form then return end
if fields.quit then return true end
local p_name = player:get_player_name()
local this_id = fields.trans_id
local this_id = tonumber(fields.trans_id)
if this_id and this_id == trans_ids[p_name] then
if this_id and trans_ids[p_name] and this_id <= trans_ids[p_name] then
-- Ignore duplicate/stale form submittal
return true
end

查看文件

@ -26,19 +26,15 @@ 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
fs(sep)
fs:escape(row.Amount, " ", row.Item)
if row.Wear > 0 then
fs:escape(" (", wear_string(row.Wear), ")")
fs:textlist(x,y, w,h, "result_list", function(add_row)
for i, row in ipairs(results) do
local wear_suffix = nil
if row.Wear > 0 then
wear_suffix = " (" .. wear_string(row.Wear) .. ")"
end
add_row(row.Amount, " ", row.Item, wear_suffix)
end
sep = ","
end
fs("]")
end)
end

查看文件

@ -16,13 +16,13 @@ end)({})
local init_query = [=[
BEGIN TRANSACTION;
CREATE TABLE if not exists Credit
CREATE TABLE IF NOT EXISTS Credit
(
Owner TEXT PRIMARY KEY NOT NULL,
Balance INTEGER NOT NULL
);
CREATE TABLE if not exists Log
CREATE TABLE IF NOT EXISTS Log
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Recipient TEXT NOT NULL,
@ -30,7 +30,7 @@ CREATE TABLE if not exists Log
Message TEXT NOT NULL
);
CREATE TABLE if not exists Orders
CREATE TABLE IF NOT EXISTS Orders
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Poster TEXT NOT NULL,
@ -43,7 +43,7 @@ CREATE TABLE if not exists Orders
Rate INTEGER NOT NULL CHECK(Rate > 0)
);
CREATE TABLE if not exists Inbox
CREATE TABLE IF NOT EXISTS Inbox
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Recipient TEXT NOT NULL,
@ -52,44 +52,49 @@ CREATE TABLE if not exists Inbox
Amount INTEGER NOT NULL CHECK(Amount > 0)
);
CREATE INDEX if not exists index_log
CREATE INDEX IF NOT EXISTS index_log
ON Log (Recipient, Time);
CREATE INDEX if not exists index_orders
CREATE INDEX IF NOT EXISTS index_orders
ON Orders (Exchange, Type, Item, Rate, Wear, Time);
CREATE INDEX if not exists index_own_orders
CREATE INDEX IF NOT EXISTS index_own_orders
ON Orders (Poster, Time);
CREATE INDEX if not exists index_inbox
CREATE INDEX IF NOT EXISTS index_inbox
ON Inbox (Recipient, Item, Wear);
CREATE VIEW if not exists distinct_items AS
SELECT DISTINCT Item FROM Orders;
CREATE VIEW IF NOT EXISTS distinct_items AS
SELECT DISTINCT Item, Wear FROM Orders;
CREATE VIEW if not exists market_summary AS
CREATE VIEW IF NOT EXISTS market_summary AS
SELECT
distinct_items.Item,
distinct_items.Item AS Item,
distinct_items.Wear AS Wear,
(
SELECT sum(Orders.Amount) FROM Orders
SELECT SUM(Orders.Amount) FROM Orders
WHERE Orders.Item = distinct_items.Item
AND Orders.Wear >= distinct_items.Wear
AND Orders.Type = "buy"
),
) AS Buy_Volume,
(
SELECT max(Orders.Rate) FROM Orders
SELECT MAX(Orders.Rate) FROM Orders
WHERE Orders.Item = distinct_items.Item
AND Orders.Wear >= distinct_items.Wear
AND Orders.Type = "buy"
),
) AS Buy_Max,
(
SELECT sum(Orders.Amount) FROM Orders
SELECT SUM(Orders.Amount) FROM Orders
WHERE Orders.Item = distinct_items.Item
AND Orders.Wear <= distinct_items.Wear
AND Orders.Type = "sell"
),
) AS Sell_Volume,
(
SELECT min(Orders.Rate) FROM Orders
SELECT MIN(Orders.Rate) FROM Orders
WHERE Orders.Item = distinct_items.Item
AND Orders.Wear <= distinct_items.Wear
AND Orders.Type = "sell"
)
) AS Sell_Min
FROM distinct_items;
@ -1241,14 +1246,8 @@ function ex_methods.market_summary(self)
local stmt = self.stmts.summary_stmt
local res = {}
for a in stmt:rows() do
table.insert(res, {
item_name = a[1],
buy_volume = a[2],
buy_max = a[3],
sell_volume = a[4],
sell_min = a[5],
})
for a in stmt:nrows() do
table.insert(res, a)
end
stmt:reset()

查看文件

@ -9,26 +9,40 @@ local function is_integer(x)
return math.floor(x) == x
end
local function wear_string(wear)
if wear > 0 then
return "-" .. math.ceil(100 * wear / 65535) .. "%"
else
return "----"
end
end
local summary_fs = ""
local function mk_summary_fs()
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")
fs:tablecolumns("text", "text", "text", "text", "text", "text", "text")
fs:table(0,0, 11.75,9, "summary_table", function(add_row)
add_row("Item",
"Description",
"Wear",
"Buy Vol",
"Buy Max",
"Sell Vol",
"Sell Min")
local all_items = minetest.registered_items
for i, row in ipairs(exchange:market_summary()) do
local def = all_items[row.item_name] or {}
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
fs("]")
local all_items = minetest.registered_items
for i, row in ipairs(exchange:market_summary()) do
local def = all_items[row.Item] or {}
add_row(row.Item,
def.description or "Unknown Item",
wear_string(row.Wear),
row.Buy_Volume or 0,
row.Buy_Max or "N/A",
row.Sell_Volume or 0,
row.Sell_Min or "N/A")
end
end)
summary_fs = tostring(fs)
end
@ -58,10 +72,6 @@ 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.
@ -108,30 +118,20 @@ end)
local main_form = "global_exchange:exchange_main"
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")
fs:tablecolumns("text", "text", "text", "text", "text", "text", "text")
fs:table(x,y, w,h, name, function(add_row)
add_row("Poster", "Type", "Item",
"Description",
"Wear", "Amount", "Rate")
local all_items = minetest.registered_items
for i, row in ipairs(results) do
local def = all_items[row.Item] or {}
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(",---")
local all_items = minetest.registered_items
for i, row in ipairs(results) do
local def = all_items[row.Item] or {}
add_row(row.Poster, row.Type, row.Item,
def.description or "Unknown Item",
wear_string(row.Wear), row.Amount, row.Rate)
end
fs(",", formlib.escape(row.Amount))
fs(",", formlib.escape(row.Rate))
end
local sel_num = math.max(0, tonumber(selected) or 0)
fs(";", sel_num + 1, "]")
end, math.max(0, tonumber(selected) or 0) + 1)
end
local function mk_main_market_fs(fs, p_name, state)
@ -141,22 +141,13 @@ end
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(",---")
fs:tablecolumns("text", "text", "text", "text")
fs:table(x,y, w,h, "order_book", function(add_row)
add_row("Type", "Rate", "Wear", "Amount")
for _,row in ipairs(order_book) do
add_row(row.Type, row.Rate, wear_string(row.Wear), row.Amount)
end
fs(",", formlib.escape(row.Amount))
end
fs(";1]")
end, 1)
end
local function mk_main_buy_fs(fs, p_name, state)
@ -167,14 +158,11 @@ local function mk_main_buy_fs(fs, p_name, state)
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:dropdown(9,1, 3, "buy_wear", function(add_item)
for _,v in ipairs(wear_levels) do
add_item(v.text)
end
end, wear.index)
fs:field(9.35,2.40, 2.9,1, "buy_price", "Bid (ea.)", state.buy_price, false)
@ -241,14 +229,11 @@ local function mk_main_fs(fs, p_name, err_str, success)
fs:size(12,10)
fs:bgcolor("#606060", false)
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]")
fs:tabheader(0,0.65, "tab", function(add_tab)
for _,tab in ipairs(main_tabs) do
add_tab(tab.text)
end
end, state.tab or 1, false, true)
local bal = exchange:get_balance(p_name)
fs:label(0,0.37, "Balance: " .. bal)

查看文件

@ -2,83 +2,163 @@ local formlib = {}
local builder_methods = {}
function formlib.escape(x)
return minetest.formspec_escape(tostring(x or ""))
if x == nil then return "" end
return minetest.formspec_escape(tostring(x))
end
function formlib.bool(x)
-- nil and false are returned as-is, everything else maps to true
return x and true
end
function builder_methods.append(fs, ...)
for i=1,select("#", ...) do
local x = select(i, ...)
if x ~= nil then table.insert(fs, tostring(x)) end
end
return fs
end
function builder_methods.escape(fs, ...)
for _,x in ipairs({...}) do
if x then fs(formlib.escape(x)) end
for i=1,select("#", ...) do
local x = select(i, ...)
if x ~= nil then table.insert(fs, formlib.escape(x)) end
end
return fs
end
function builder_methods.escape_list(fs, ...)
for i=1,select("#", ...) do
local x = select(i, ...)
if i > 1 then fs(",") end
fs:escape(x)
end
return fs
end
function builder_methods.escape_groups(fs, ...)
for i=1,select("#", ...) do
local group = select(i, ...)
if i > 1 then fs(";") end
if type(group) == "table" then
fs:escape_list(unpack(group))
else
fs:escape(group)
end
end
return fs
end
function builder_methods.element(fs, name, ...)
return fs(name, "["):escape_groups(...):append("]")
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]")
if fixed == nil then
return fs:element("size", {w,h})
else
fs("size[", w, ",", h, "]")
return fs:element("size", {w,h, formlib.bool(fixed)})
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]")
if fullscreen == nil then
return fs:element("bgcolor", {color})
else
fs("bgcolor[", formlib.escape(color), "]")
return fs:element("bgcolor", {color}, {formlib.bool(fullscreen)})
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), "]")
return fs:element("list", {inv_loc}, {inv_list}, {x,y}, {w,h}, {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), "]")
return fs:element("button", {x,y}, {w,h}, {name}, {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), "]")
return fs:element("item_image_button", {x,y}, {w,h}, {item}, {name}, {text})
end
function builder_methods.label(fs, x,y, text)
fs("label[", x, ",", y, ";", formlib.escape(text), "]")
return fs:element("label", {x,y}, {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]")
fs:element("field", {x,y}, {w,h}, {name}, {label}, {default})
if close_on_enter ~= nil then
fs:element("field_close_on_enter", {name}, {formlib.bool(close_on_enter)})
end
end
function builder_methods.container(fs, x,y, sub_fn, ...)
fs("container[", x, ",", y, "]")
sub_fn(fs, ...)
fs("container_end[]")
return fs
end
function builder_methods.box(fs, x,y, w,h, color)
fs("box[", x, ",", y, ";", w, ",", h, ";", formlib.escape(color), "]")
return fs:element("box", {x,y}, {w,h}, {color})
end
function builder_methods.dropdown(fs, x,y, w, name, body_fn, selected_idx)
fs("dropdown["):escape_groups({x,y}, {w}, {name}):append(";")
local first = true
local results = { body_fn(function(...)
if first then first = false else fs(",") end
fs:escape(...)
end) }
if selected_idx ~= nil then fs(";"):escape(selected_idx) end
return fs("]"), unpack(results)
end
function builder_methods.tabheader(fs, x,y, name, body_fn, current_tab, transparent, draw_border)
fs("tabheader["):escape_groups({x,y}, {name}):append(";")
local first = true
local results = { body_fn(function(...)
if first then first = false else fs(",") end
fs:escape(...)
end) }
fs(";"):escape_groups({current_tab}, {formlib.bool(transparent)}, {formlib.bool(draw_border)})
return fs("]"), unpack(results)
end
function builder_methods.textlist(fs, x,y, w,h, name, body_fn, selected_idx, transparent)
fs("textlist["):escape_groups({x,y}, {w,h}, {name}):append(";")
local first = true
local results = { body_fn(function(...)
if first then first = false else fs(",") end
fs:escape(...)
end) }
fs(";"):escape_groups({selected_idx}, {formlib.bool(transparent)})
return fs("]"), unpack(results)
end
function builder_methods.tableoptions(fs, ...)
return fs:element("tableoptions", ...)
end
function builder_methods.tablecolumns(fs, ...)
return fs:element("tablecolumns", ...)
end
function builder_methods.table(fs, x,y, w,h, name, body_fn, selected_idx)
fs("table["):escape_groups({x,y}, {w,h}, {name}):append(";")
local first = true
local results = { body_fn(function(...)
if first then first = false else fs(",") end
fs:escape_list(...)
end) }
if selected_idx ~= nil then fs(";"):escape(selected_idx) end
return fs("]"), unpack(results)
end
function builder_methods.container(fs, x,y, sub_fn, ...)
fs:element("container", {x,y})
local results = { sub_fn(fs, ...) }
return fs("container_end[]"), unpack(results)
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,
__call = builder_methods.append,
__tostring = table.concat,
}