global_exchange/exchange_machine.lua

653 lines
16 KiB
Lua
Raw Normal View History

2016-02-25 23:38:19 +01:00
local exchange = ...
local search_cooldown = 2
2016-02-26 07:07:57 +01:00
local summary_interval = 600
local summary_fs = ""
2016-02-26 07:07:57 +01:00
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"
}
2016-02-26 07:07:57 +01:00
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")
2016-02-26 07:07:57 +01:00
end
res[#res+1] = "]"
res[#res+1] = "button[3,7;2,1;back;Back]"
summary_fs = table.concat(res)
2016-02-26 07:07:57 +01:00
end
minetest.after(0, mk_summary_fs)
2016-02-26 07:07:57 +01:00
local elapsed = 0
minetest.register_globalstep(function(dtime)
elapsed = elapsed + dtime
if elapsed >= summary_interval then
mk_summary_fs()
2016-02-26 07:36:45 +01:00
elapsed = 0
2016-02-26 07:07:57 +01:00
end
end)
local summary_form = "global_exchange:summary"
local function show_summary(p_name)
minetest.show_formspec(p_name, summary_form, summary_fs)
end
2016-02-25 23:38:19 +01:00
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
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,
}
2016-02-25 23:38:19 +01:00
end)
minetest.register_on_leaveplayer(function(player)
main_state[player:get_player_name()] = nil
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
2016-02-25 23:38:19 +01:00
local function insert(str)
fs_tab[#fs_tab+1] = str
2016-02-25 23:38:19 +01:00
end
fs_tab = {
tablecolumns,
"table[" .. x .. "," .. y .. ";" .. w .. "," .. h .. ";",
"result_table;",
"Poster,Type,Item,Description,Amount,Rate"
}
2016-02-25 23:38:19 +01:00
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")
2016-02-25 23:38:19 +01:00
insert(",")
insert(tostring(row.Amount))
insert(",")
insert(tostring(row.Rate))
end
if selected and selected ~= "" then
insert(";")
insert(selected)
end
insert("]")
return table.concat(fs_tab)
end
local function mk_main_fs(p_name, new_item, err_str, success)
local state = main_state[p_name]
if not state then return end -- Should have been initialized on player join
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 ""
local bal = exchange:get_balance(p_name)
local fs
2016-02-25 23:38:19 +01:00
if bal then
fs = "label[0,0;Balance: " .. bal .. "]"
2016-02-25 23:38:19 +01:00
else
fs = "label[0.2,0.5;Use an ATM to make your account.]"
2016-02-25 23:38:19 +01:00
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]"
2016-02-25 23:38:19 +01:00
if err_str then
fs = fs .. "label[0,3;Error: " .. err_str .. "]"
end
if success then
fs = fs .. "label[0,3;Success!]"
end
return "size[8,9]" .. fs .. table_from_results(results, 0, 4, 8, 5, selected_def)
2016-02-25 23:38:19 +01:00
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))
end
-- Something similar to creative inventory
local pagemax = 1
2016-02-25 23:38:19 +01:00
-- 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)
2016-02-25 23:38:19 +01:00
local p_name = player:get_player_name()
if from_list == "main"
and to_list == "p_" .. p_name then
2016-02-25 23:38:19 +01:00
return 1
else
return 0
end
end,
allow_put = function()
return 0
end,
allow_take = function()
return 0
end,
2016-03-06 12:21:27 +01:00
on_move = function(inv, _, _, _, _, _, player)
2016-02-25 23:38:19 +01:00
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,
on_put = function(inv, listname, index, stack, player)
local p_name = player:get_player_name()
if listname == 'money' and minetest.get_item_group(stack:get_name(), 'minegeld') then
local old_balance = exchange:get_balance(p_name)
local balance = bills2balance(stack, p_name)
exchange:change_balance(p_name, balance - old_balance)
local stacks = inv:get_list('money')
local tens = math.floor(balance/10)
if tens > 0 then
inv:set_stack('money', 1, 'currency:minegeld_10 ' .. tens)
balance = balance - tens * 10
else
inv:set_stack('money', 1, '')
end
local fives = math.floor(balance/5)
if fives > 0 then
inv:set_stack('money', 2, 'currency:minegeld_5 ' .. fives)
balance = balance - fives * 5
else
inv:set_stack('money', 2, '')
end
local ones = math.floor(balance)
if ones > 0 then
inv:set_stack('money', 3, 'currency:minegeld ' .. ones)
balance = balance - ones
else
inv:set_stack('money', 3, '')
end
end
minetest.show_formspec(p_name, "global_exchange:atm_form", cash_fs(p_name)) --redraw with new balance
end,
allow_put = function(inv, listname, index, stack, player)
local name = stack:get_name()
if listname == 'money' and minetest.get_item_group(name, 'minegeld') > 0 then
return stack:get_count()
end
return 0
end,
on_take = function(inv, listname, index, stack, player)
local p_name = player:get_player_name()
if listname == 'money' and minetest.get_item_group(stack:get_name(), 'minegeld') then
local name = stack:get_name()
local count = stack:get_count()
local delta
if name == 'currency:minegeld' then
delta = count
elseif name == 'currency:minegeld_5' then
delta = count * 5
elseif name == 'currency:minegeld_10' then
delta = count * 10
end
exchange:change_balance(p_name, -delta) --let's hope it always returns success
end
minetest.show_formspec(p_name, "global_exchange:atm_form", cash_fs(p_name)) --redraw with new balance
end,
allow_take = function(inv, listname, index, stack, player)
return stack:get_count()
end,
2016-02-25 23:38:19 +01:00
})
local selectable_list,n = {},1
2016-02-25 23:38:19 +01:00
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
2016-02-25 23:38:19 +01:00
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))
2016-02-25 23:38:19 +01:00
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)
2016-02-25 23:38:19 +01:00
end)
2016-02-25 23:38:19 +01:00
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;]"
2016-02-25 23:38:19 +01:00
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]"
2016-02-25 23:38:19 +01:00
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 p_name = player:get_player_name()
2016-02-25 23:38:19 +01:00
if item_name == "" then
return false, "You must input an item"
end
2016-02-25 23:38:19 +01:00
if not minetest.registered_items[item_name] then
return false, "That item does not exist."
end
local amount = tonumber(amount_str)
local rate = tonumber(rate_str)
if not amount then
return false, "Invalid amount."
end
if not rate then
return false, "Invalid rate."
end
2016-03-12 00:33:48 +01:00
if amount > 1000 then
return false, "Max amount is 1000"
end
2016-02-25 23:38:19 +01:00
local p_inv = player:get_inventory()
local stack = ItemStack(item_name)
stack:set_count(amount)
2016-02-25 23:38:19 +01:00
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)
end
return true
end
local function handle_main(player, fields)
2016-02-25 23:38:19 +01:00
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
end
if fields.select_item then
2016-02-25 23:38:19 +01:00
show_select(p_name)
end
if fields.search then
2016-02-25 23:38:19 +01:00
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
2016-02-25 23:38:19 +01:00
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
show_main(p_name)
end
if fields.sell then
state.sell = fields.sell == "true"
2016-02-25 23:38:19 +01:00
end
if fields.post_order then
2016-02-25 23:38:19 +01:00
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
2016-02-25 23:38:19 +01:00
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)
2016-02-25 23:38:19 +01:00
if succ then
state.search_results = {}
show_main(p_name, nil, nil, true)
else
show_main(p_name, nil, err)
end
end
if fields.result_table then
2016-02-25 23:38:19 +01:00
local results = state.search_results
local event = minetest.explode_table_event(fields.result_table)
2016-02-25 23:38:19 +01:00
if event.type ~= "CHG" then
return
2016-02-25 23:38:19 +01:00
end
local index = event.row - 1
result = results[index]
2016-02-25 23:38:19 +01:00
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
2016-02-26 07:07:57 +01:00
show_summary(p_name)
end
if fields.your_orders then
2016-02-25 23:38:19 +01:00
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)
2016-02-25 23:38:19 +01:00
local p_name = player:get_player_name()
2016-02-25 23:38:19 +01:00
local pagenum = player_pages[p_name] or 1
if fields.select_prev then
2016-02-25 23:38:19 +01:00
player_pages[p_name] = math.max(1, pagenum - 1)
show_select(p_name)
elseif fields.select_next then
2016-02-25 23:38:19 +01:00
player_pages[p_name] = math.min(pagemax, pagenum + 1)
show_select(p_name)
end
end
local function handle_own_orders(player, fields)
2016-02-25 23:38:19 +01:00
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
if fields.refresh then
2016-02-25 23:38:19 +01:00
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
2016-02-25 23:38:19 +01:00
local row = results[idx]
2016-02-26 04:03:17 +01:00
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
2016-02-25 23:38:19 +01:00
2016-02-27 05:55:48 +01:00
local succ, err =
exchange:cancel_order(p_name, row.Id, row.Type, row.Item, row.Amount, row.Rate)
2016-02-25 23:38:19 +01:00
if succ then
table.remove(results, idx)
2016-02-26 04:03:17 +01:00
if row.Type == "sell" then
p_inv:add_item("main", stack)
end
2016-02-25 23:38:19 +01:00
else
-- Refresh the results, since there might have been a problem.
state.own_results = exchange:search_player_orders(p_name) or {}
end
show_own_orders(p_name, state.own_results)
end
if fields.result_table then
local event = minetest.explode_table_event(fields.result_table)
2016-02-25 23:38:19 +01:00
if event.type == "CHG" then
state.selected_index = event.row - 1
2016-02-26 04:03:17 +01:00
show_own_orders(p_name, results, event.row)
2016-02-25 23:38:19 +01:00
end
end
if fields.back then
2016-02-25 23:38:19 +01:00
show_main(p_name)
end
end
local function handle_summary(player, fields)
if fields.back then
show_main(player:get_player_name())
2016-02-26 07:07:57 +01:00
end
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
return true
end)
2016-02-25 23:38:19 +01:00
minetest.register_node("global_exchange:exchange", {
description = "Exchange",
drawtype = "nodebox",
tiles = {
"global_exchange_terminal_top.png",
"global_exchange_terminal_bottom.png",
"global_exchange_terminal_right.png",
"global_exchange_terminal_right.png^[transform4",
"global_exchange_terminal_back.png",
"global_exchange_terminal_front.png",
2016-02-25 23:38:19 +01:00
},
paramtype = "light",
paramtype2 = "facedir",
2016-02-25 23:38:19 +01:00
groups = {cracky=2},
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
{-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
}
},
--[[
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
},
]]
2016-03-06 12:21:27 +01:00
on_rightclick = function(_, _, clicker)
2016-02-25 23:38:19 +01:00
local p_name = clicker:get_player_name()
local state = main_state[p_name]
if state then
state.search_results = {}
end
show_main(p_name)
end,
})
2016-02-27 05:55:48 +01:00
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" },
}
})