forked from Sokomine/yl_speak_up
added trade interface via buy button
This commit is contained in:
parent
0d88093a45
commit
d5fe15a7c7
314
fs_trade_via_buy_button.lua
Normal file
314
fs_trade_via_buy_button.lua
Normal file
@ -0,0 +1,314 @@
|
||||
|
||||
-- buy-button-based trading from trade list: one item(stack) for another item(stack)
|
||||
|
||||
-- helper function
|
||||
yl_speak_up.get_trade_item_desc = function(item)
|
||||
local stack = ItemStack(item)
|
||||
local def = minetest.registered_items[stack:get_name()]
|
||||
if(def and def.description) then
|
||||
return minetest.formspec_escape(tostring(stack:get_count()).."x "..def.description)
|
||||
end
|
||||
return minetest.formspec_escape(tostring(stack:get_count()).."x "..stack:get_name())
|
||||
end
|
||||
|
||||
|
||||
-- helper function; also used by trade_list.lua
|
||||
yl_speak_up.get_sorted_trade_id_list = function(dialog)
|
||||
-- make sure all fields exist
|
||||
yl_speak_up.setup_trade_limits(dialog)
|
||||
local keys = {}
|
||||
for k, v in pairs(dialog.trades) do
|
||||
if(k ~= "limits" and k ~= "") then
|
||||
-- structure of the indices: sell name amount for name amount
|
||||
local parts = string.split(k, " ")
|
||||
if(parts and #parts == 6 and parts[4] == "for"
|
||||
and v.pay and v.pay[1] ~= "" and v.pay[1] == parts[5].." "..parts[6]
|
||||
and v.buy and v.buy[1] ~= "" and v.buy[1] == parts[2].." "..parts[3]
|
||||
and minetest.registered_items[parts[5]]
|
||||
and minetest.registered_items[parts[2]]
|
||||
and tonumber(parts[6]) > 0
|
||||
and tonumber(parts[3]) > 0) then
|
||||
table.insert(keys, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(keys)
|
||||
return keys
|
||||
end
|
||||
|
||||
|
||||
yl_speak_up.input_trade_via_buy_button = function(player, formname, fields)
|
||||
local pname = player:get_player_name()
|
||||
|
||||
if(fields.buy_directly) then
|
||||
local trade_id = yl_speak_up.speak_to[pname].trade_id
|
||||
local error_msg = yl_speak_up.check_trade_via_buy_button(player, trade_id, true)
|
||||
|
||||
if(error_msg ~= "OK") then
|
||||
yl_speak_up.show_fs(player, "msg", {
|
||||
input_to = "yl_speak_up:trade_via_buy_button",
|
||||
formspec = "size[6,2.5]"..
|
||||
"label[0.2,-0.2;"..error_msg.."\nTrade aborted.]"..
|
||||
"button[2,1.5;1,0.9;back_from_error_msg;Back]"})
|
||||
return
|
||||
end
|
||||
yl_speak_up.show_fs(player, "trade_via_buy_button", trade_id)
|
||||
return
|
||||
end
|
||||
|
||||
-- scroll through the trades with prev/next buttons
|
||||
if(fields.prev_trade or fields.next_trade) then
|
||||
local pname = player:get_player_name()
|
||||
local n_id = yl_speak_up.speak_to[pname].n_id
|
||||
local dialog = yl_speak_up.speak_to[pname].dialog
|
||||
local keys = yl_speak_up.speak_to[pname].trade_id_list or {}
|
||||
local trade_id = yl_speak_up.speak_to[pname].trade_id
|
||||
local idx = math.max(1, table.indexof(keys, trade_id))
|
||||
if(fields.prev_trade) then
|
||||
idx = idx - 1
|
||||
elseif(fields.next_trade) then
|
||||
idx = idx + 1
|
||||
end
|
||||
if(idx > #keys) then
|
||||
idx = 1
|
||||
elseif(idx < 1) then
|
||||
idx = #keys
|
||||
end
|
||||
yl_speak_up.speak_to[pname].trade_id = keys[idx]
|
||||
-- this is another trade; count from 0 again
|
||||
yl_speak_up.speak_to[pname].trade_done = nil
|
||||
end
|
||||
-- TODO: delete button if(fields.delete_trade_via_buy_button) then
|
||||
|
||||
-- show the trade list
|
||||
if(fields.back_to_trade_list or fields.quit
|
||||
or not(yl_speak_up.speak_to[pname].trade_id)) then
|
||||
yl_speak_up.show_fs(player, "trade_list")
|
||||
return
|
||||
end
|
||||
|
||||
-- show the trade formspec again
|
||||
yl_speak_up.show_fs(player, "trade_via_buy_button", yl_speak_up.speak_to[pname].trade_id)
|
||||
end
|
||||
|
||||
|
||||
-- helper function
|
||||
-- if do_trade is false: check only if the trade would be possible and return
|
||||
-- error message if not; return "OK" when trade is possible
|
||||
-- if do_trade is true: if possible, execute the trade; return the same as above
|
||||
yl_speak_up.check_trade_via_buy_button = function(player, trade_id, do_trade)
|
||||
local pname = player:get_player_name()
|
||||
local n_id = yl_speak_up.speak_to[pname].n_id
|
||||
local dialog = yl_speak_up.speak_to[pname].dialog
|
||||
-- make sure all necessary table entries exist
|
||||
yl_speak_up.setup_trade_limits(dialog)
|
||||
local this_trade = dialog.trades[trade_id]
|
||||
-- the players' inventory
|
||||
local player_inv = player:get_inventory()
|
||||
-- the NPCs' inventory
|
||||
local npc_inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)})
|
||||
if(not(this_trade)) then
|
||||
return "Trade not found."
|
||||
elseif(not(player_inv)) then
|
||||
return "Couldn't find player's inventory."
|
||||
elseif(not(npc_inv)) then
|
||||
return "Couldn't find the NPC's inventory."
|
||||
end
|
||||
-- store which trade we're doing
|
||||
yl_speak_up.speak_to[pname].trade_id = trade_id
|
||||
|
||||
-- what the player pays to the npc:
|
||||
local pay_stack = ItemStack(dialog.trades[ trade_id ].pay[1])
|
||||
-- what the npc sells and the player buys:
|
||||
local buy_stack = ItemStack(dialog.trades[ trade_id ].buy[1])
|
||||
local npc_name = minetest.formspec_escape(dialog.n_npc)
|
||||
|
||||
-- can the NPC provide his part?
|
||||
if(not(npc_inv:contains_item("npc_main", buy_stack))) then
|
||||
return "Out of stock" --"Sorry. "..npc_name.." ran out of stock.\nPlease come back later."
|
||||
-- has the NPC room for the payment?
|
||||
elseif(not(npc_inv:room_for_item("npc_main", pay_stack))) then
|
||||
return npc_name.." has no room left!"
|
||||
-- return "Sorry. "..npc_name.." ran out of inventory space.\n"..
|
||||
-- "There is no room to store your payment!"
|
||||
end
|
||||
|
||||
-- are there any limits which we have to take into account?
|
||||
local min_storage = dialog.trades.limits.sell_if_more[ buy_stack:get_name() ]
|
||||
local max_storage = dialog.trades.limits.buy_if_less[ pay_stack:get_name() ]
|
||||
if((min_storage and min_storage > 0)
|
||||
or (max_storage and max_storage < 10000)) then
|
||||
local counted_npc_inv = yl_speak_up.count_npc_inv(n_id)
|
||||
local stock_pay = counted_npc_inv[ pay_stack:get_name() ] or 0
|
||||
local stock_buy = counted_npc_inv[ buy_stack:get_name() ] or 0
|
||||
-- trade limit: is enough left after the player buys the item?
|
||||
if( min_storage and min_storage > stock_buy - buy_stack:get_count()) then
|
||||
return "Sorry. "..npc_name.." currently does not want to\nsell that much."..
|
||||
" Current stock: "..tostring(stock_buy)..
|
||||
" (min: "..tostring(min_storage).."). Perhaps later?"
|
||||
-- trade limit: make sure the bought amount does not exceed the desired maximum
|
||||
elseif(max_storage and max_storage < stock_pay + pay_stack:get_count()) then
|
||||
return "Sorry. "..npc_name.." currently does not want to\n"..
|
||||
"buy that much."..
|
||||
" Current stock: "..tostring(stock_pay)..
|
||||
" (max: "..tostring(max_storage).."). Perhaps later?"
|
||||
end
|
||||
end
|
||||
-- can the player pay?
|
||||
if(not(player_inv:contains_item("main", pay_stack))) then
|
||||
-- both slots will remain empty
|
||||
return "You can't pay the price."
|
||||
elseif(not(player_inv:room_for_item("main", buy_stack))) then
|
||||
-- the player has no room for the sold item; give a warning
|
||||
return "You don't have enough free inventory\nspace to store your purchase."
|
||||
end
|
||||
|
||||
-- was it a dry run to check if the trade is possible?
|
||||
if(not(do_trade)) then
|
||||
return "OK"
|
||||
end
|
||||
|
||||
-- actually do the trade
|
||||
local payment = player_inv:remove_item("main", pay_stack)
|
||||
local sold = npc_inv:remove_item("npc_main", buy_stack)
|
||||
-- used items cannot be sold as there is no fair way to indicate how
|
||||
-- much they are used
|
||||
if(payment:get_wear() > 0 or sold:get_wear() > 0) then
|
||||
-- revert the trade
|
||||
player_inv:add_item("main", payment)
|
||||
npc_inv:add_item("npc_main", sold)
|
||||
return "Sorry. "..npc_name.." accepts only undammaged items."
|
||||
end
|
||||
player_inv:add_item("main", sold)
|
||||
npc_inv:add_item("npc_main", payment)
|
||||
-- save the inventory of the npc so that the payment does not get lost
|
||||
yl_speak_up.save_npc_inventory( n_id )
|
||||
-- store for statistics how many times the player has executed this trade
|
||||
-- (this is also necessary to switch to the right target dialog when
|
||||
-- dealing with dialog options trades)
|
||||
if(not(yl_speak_up.speak_to[pname].trade_done)) then
|
||||
yl_speak_up.speak_to[pname].trade_done = 0
|
||||
end
|
||||
yl_speak_up.speak_to[pname].trade_done = yl_speak_up.speak_to[pname].trade_done + 1
|
||||
-- log the trade
|
||||
yl_speak_up.log_change(pname, n_id,
|
||||
"bought "..tostring(pay_stack:to_string())..
|
||||
" for "..tostring(buy_stack:to_string()))
|
||||
return "OK"
|
||||
end
|
||||
|
||||
|
||||
-- trade for a player (the owner of the NPC): one item(stack) for another
|
||||
-- trade by clicking on the "buy" button instead of moving inventory items around
|
||||
-- checks if payment and buying is possible
|
||||
yl_speak_up.get_fs_trade_via_buy_button = function(player, trade_id)
|
||||
local pname = player:get_player_name()
|
||||
local n_id = yl_speak_up.speak_to[pname].n_id
|
||||
local dialog = yl_speak_up.speak_to[pname].dialog
|
||||
-- make sure all necessary table entries exist
|
||||
yl_speak_up.setup_trade_limits(dialog)
|
||||
local this_trade = dialog.trades[trade_id]
|
||||
-- the players' inventory
|
||||
local player_inv = player:get_inventory()
|
||||
-- the NPCs' inventory
|
||||
local npc_inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)})
|
||||
if(not(this_trade) or not(player_inv) or not(npc_inv)) then
|
||||
return yl_speak_up.trade_fail_fs
|
||||
end
|
||||
-- what the player pays to the npc:
|
||||
local pay = dialog.trades[ trade_id ].pay[1]
|
||||
-- what the npc sells and the player buys:
|
||||
local buy = dialog.trades[ trade_id ].buy[1]
|
||||
local pay_name = yl_speak_up.get_trade_item_desc(pay)
|
||||
local buy_name = yl_speak_up.get_trade_item_desc(buy)
|
||||
local npc_name = minetest.formspec_escape(dialog.n_npc)
|
||||
-- get the number of the trade
|
||||
local keys = yl_speak_up.speak_to[pname].trade_id_list or {}
|
||||
local idx = math.max(1, table.indexof(keys, trade_id))
|
||||
|
||||
-- the common formspec, shared by actual trade and configuration
|
||||
-- no listring here as that would make things more complicated
|
||||
local formspec = { -- "size[8.5,8]"..
|
||||
yl_speak_up.show_fs_simple_deco(10, 8.8)..
|
||||
"container[0.75,0]"..
|
||||
"label[4.35,1.4;", npc_name, " sells:]",
|
||||
"list[current_player;main;0.2,4.55;8,1;]",
|
||||
"list[current_player;main;0.2,5.78;8,3;8]",
|
||||
-- "label[7.0,0.2;Offer ", tostring(idx), "/", tostring(#keys), "]",
|
||||
-- "label[2.5,0.7;Trading with ", npc_name, "]",
|
||||
"label[1.5,0.7;Offer no. ",
|
||||
tostring(idx), "/", tostring(#keys),
|
||||
" from ", npc_name, "]",
|
||||
"label[1.5,1.4;You pay:]",
|
||||
-- show images of price and what is sold so that the player knows what
|
||||
-- it costs and what he will get even if the trade is not possible at
|
||||
-- that moment
|
||||
-- "item_image[2.1,2.0;1,1;",
|
||||
"item_image_button[2.1,1.9;1.2,1.2;",
|
||||
tostring(pay),
|
||||
";pay_item_img;",
|
||||
"]",
|
||||
-- "item_image[5.1,2.0;1,1;",
|
||||
-- tostring(buy),
|
||||
"item_image_button[5.1,1.9;1.2,1.2;",
|
||||
tostring(buy),
|
||||
";buy_item_img;",
|
||||
"]",
|
||||
"label[1.5,3.0;",
|
||||
pay_name,
|
||||
"]",
|
||||
"label[4.35,3.0;",
|
||||
buy_name,
|
||||
"]",
|
||||
"image[3.5,2.0;1,1;gui_furnace_arrow_bg.png^[transformR270]",
|
||||
-- go back to the trade list
|
||||
"button[0.2,0.0;8.0,1.0;back_to_trade_list;Back to list]"..
|
||||
"tooltip[back_to_trade_list;Click here once you've traded enough with this "..
|
||||
"NPC and want to get back to the trade list.]"
|
||||
}
|
||||
|
||||
-- show edit button for the owner if in edit_mode
|
||||
if(yl_speak_up.may_edit_npc(player, n_id)) then
|
||||
-- for trades in trade list: allow delete (new trades can easily be added)
|
||||
-- allow delete for trades in trade list even if not in edit mode
|
||||
-- (entering edit mode for that would be too much work)
|
||||
table.insert(formspec,
|
||||
"button[0.2,2.0;1.2,0.9;delete_trade_via_buy_button;Delete]"..
|
||||
"tooltip[delete_trade_via_buy_button;"..
|
||||
"Delete this trade. You can do so only if\n"..
|
||||
"you can edit the NPC as such (i.e. own it).]")
|
||||
end
|
||||
|
||||
-- dry-run: test if the trade can be done
|
||||
local trade_possible = yl_speak_up.check_trade_via_buy_button(player, trade_id, false)
|
||||
if(trade_possible == "OK") then
|
||||
local buy_str = "Buy"
|
||||
local trade_done = yl_speak_up.speak_to[pname].trade_done
|
||||
if(trade_done and trade_done > 0) then
|
||||
buy_str = "Buy again. Bought: "..tostring(trade_done).."x"
|
||||
end
|
||||
table.insert(formspec, "button[0.2,3.5;8.0,1.0;buy_directly;")
|
||||
-- "button[6.5,2.0;1.7,0.9;buy_directly;Buy]"..
|
||||
table.insert(formspec, buy_str)
|
||||
table.insert(formspec, "]")
|
||||
table.insert(formspec, "tooltip[buy_directly;Click here in order to buy.]")
|
||||
else
|
||||
-- set a red background color in order to alert thep layer to the error
|
||||
table.insert(formspec, "style_type[button;bgcolor=#FF4444]"..
|
||||
"button[0.2,3.5;8.0,1.0;back_from_error_msg;")
|
||||
-- table.insert(formspec, "label[0.5,3.5;")
|
||||
table.insert(formspec, trade_possible)
|
||||
table.insert(formspec, ']')
|
||||
-- set the background color for the next buttons back to our normal one
|
||||
table.insert(formspec, 'style_type[button;bgcolor=#a37e45]')
|
||||
-- table.insert(formspec, "label[6.5,2.0;Trade not\npossible.]")
|
||||
end
|
||||
|
||||
table.insert(formspec, "container_end[]"..
|
||||
"real_coordinates[true]"..
|
||||
"button[0.5,1.9;0.8,2.0;prev_trade;<]"..
|
||||
"button[11.7,1.9;0.8,2.0;next_trade;>]"..
|
||||
"tooltip[prev_trade;Show previous trade offer]"..
|
||||
"tooltip[next_trade;Show next trade offer]")
|
||||
return table.concat(formspec, '')
|
||||
end
|
2
init.lua
2
init.lua
@ -87,6 +87,8 @@ dofile(modpath .. "fs_trade_limit.lua")
|
||||
dofile(modpath .. "fs_edit_trade_limit.lua")
|
||||
-- trade one item(stack) against one other item(stack)
|
||||
dofile(modpath .. "trade_simple.lua")
|
||||
-- just click on a button to buy items from the trade list
|
||||
dofile(modpath .. "fs_trade_via_buy_button.lua")
|
||||
-- easily accessible list of all trades the NPC offers
|
||||
dofile(modpath .. "trade_list.lua")
|
||||
-- as the name says: list which npc acesses a variable how and in which context
|
||||
|
12
show_fs.lua
12
show_fs.lua
@ -45,6 +45,10 @@ minetest.register_on_player_receive_fields( function(player, formname, fields)
|
||||
elseif formname == "yl_speak_up:do_trade_simple" then
|
||||
yl_speak_up.input_do_trade_simple(player, formname, fields)
|
||||
return true
|
||||
-- handled in fs_trade_via_buy_button.lua
|
||||
elseif formname == "yl_speak_up:trade_via_buy_button" then
|
||||
yl_speak_up.input_trade_via_buy_button(player, formname, fields)
|
||||
return true
|
||||
-- (as above - also handled in trade_simple.lua)
|
||||
elseif formname == "yl_speak_up:add_trade_simple" then
|
||||
yl_speak_up.input_add_trade_simple(player, formname, fields)
|
||||
@ -296,6 +300,14 @@ yl_speak_up.show_fs = function(player, fs_name, param)
|
||||
yl_speak_up.show_fs_ver(pname, "yl_speak_up:do_trade_simple",
|
||||
yl_speak_up.get_fs_trade_simple(player, param), 1)
|
||||
|
||||
elseif(fs_name == "trade_via_buy_button") then
|
||||
-- the optional parameter param is the trade_id
|
||||
if(not(param) and yl_speak_up.speak_to[pname]) then
|
||||
param = yl_speak_up.speak_to[pname].trade_id
|
||||
end
|
||||
yl_speak_up.show_fs_ver(pname, "yl_speak_up:trade_via_buy_button",
|
||||
yl_speak_up.get_fs_trade_via_buy_button(player, param), 1)
|
||||
|
||||
elseif(fs_name == "add_trade_simple") then
|
||||
-- the optional parameter param is the trade_id
|
||||
if(not(param) and yl_speak_up.speak_to[pname]) then
|
||||
|
@ -43,7 +43,7 @@ yl_speak_up.input_trade_list = function(player, formname, fields)
|
||||
-- the "_" is necessary for the price button so that offer and price
|
||||
-- button can each have their tooltip *and* lead to the same dialog
|
||||
if(fields[ k ] or fields[ k.."_" ]) then
|
||||
yl_speak_up.show_fs(player, "trade_simple", k)
|
||||
yl_speak_up.show_fs(player, "trade_via_buy_button", k)
|
||||
return
|
||||
end
|
||||
end
|
||||
@ -108,11 +108,9 @@ yl_speak_up.get_fs_trade_list = function(player, show_dialog_option_trades)
|
||||
|
||||
-- the order in which the trades appear shall not change each time;
|
||||
-- but lua cannot sort the keys of a table by itself...
|
||||
local sorted_trades = {}
|
||||
for k in pairs(dialog.trades) do
|
||||
table.insert(sorted_trades, k)
|
||||
end
|
||||
table.sort(sorted_trades)
|
||||
-- this function can be found in fs_trade_via_button.lua
|
||||
local sorted_trades = yl_speak_up.get_sorted_trade_id_list(dialog)
|
||||
yl_speak_up.speak_to[pname].trade_id_list = sorted_trades
|
||||
|
||||
for i, k in ipairs(sorted_trades) do
|
||||
local v = dialog.trades[ k ]
|
||||
@ -157,6 +155,7 @@ yl_speak_up.get_fs_trade_list = function(player, show_dialog_option_trades)
|
||||
-- show the price label only when the offer is in stock
|
||||
table.insert(formspec, "label[0,1.9;->]"..
|
||||
"label[0,4.4;Price:]\ncontainer_end[]")
|
||||
|
||||
end
|
||||
col = col + 1
|
||||
if(col >= yl_speak_up.trade_max_cols) then
|
||||
@ -201,7 +200,7 @@ yl_speak_up.get_fs_trade_list = function(player, show_dialog_option_trades)
|
||||
text, text,
|
||||
true, nil, nil, pname_for_old_fs)
|
||||
-- show a list of how much the NPC will buy and sell
|
||||
text = "Do not buy or sell more than what I will tell you."
|
||||
text = "Limits: Do not buy or sell more than what I will tell you."
|
||||
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
||||
"trade_limit",
|
||||
text, text,
|
||||
|
Loading…
Reference in New Issue
Block a user