746 lines
29 KiB
Lua
746 lines
29 KiB
Lua
-- spimple trading: one item(stack) for another item(stack)
|
|
|
|
-- fallback message if something went wrong
|
|
yl_speak_up.trade_fail_fs = "size[6,2]"..
|
|
"label[0.2,0.5;Ups! The trade is not possible.\nPlease notify an admin.]"..
|
|
"button_exit[2,1.5;1,0.9;exit;Exit]"
|
|
|
|
|
|
-- possible inputs:
|
|
-- fields.edit_trade_simple go on to showing the add_trade_simple formspec
|
|
-- fields.abort_trade_simple, ESC, depends on context
|
|
-- fields.delete_trade_simple delete this trade
|
|
-- fields.finished_trading
|
|
-- if in edit mode: go back to edit options dialog
|
|
-- if traded at least once: go on to the target dialog
|
|
-- if not traded: go back to the original dialog
|
|
yl_speak_up.input_do_trade_simple = function(player, formname, fields)
|
|
if(not(player)) then
|
|
return 0
|
|
end
|
|
local pname = player:get_player_name()
|
|
|
|
-- which trade are we talking about?
|
|
local trade = yl_speak_up.trade[pname]
|
|
|
|
-- show the trade list
|
|
if(fields.back_to_trade_list) then
|
|
minetest.show_formspec(pname, "yl_speak_up:trade_list",
|
|
yl_speak_up.get_fs_trade_list(player))
|
|
return
|
|
end
|
|
|
|
-- a new trade has been stored - show it
|
|
if(fields.trade_simple_stored) then
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
yl_speak_up.get_fs_trade_simple(player,
|
|
yl_speak_up.speak_to[pname].trade_id))
|
|
return
|
|
end
|
|
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
-- if in edit mode: go back to the edit options dialog
|
|
if(fields.back_to_edit_options
|
|
and yl_speak_up.edit_mode[pname] == n_id and n_id) then
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local tr = dialog.trades[ trade.trade_id ]
|
|
if(tr) then
|
|
-- done trading
|
|
yl_speak_up.speak_to[pname].target_d_id = nil
|
|
yl_speak_up.speak_to[pname].trade_id = nil
|
|
-- go to the edit options dialog
|
|
minetest.show_formspec(pname, "yl_speak_up:edit_option_dialog",
|
|
yl_speak_up.get_fs_edit_option_dialog(player, n_id, tr.d_id, tr.o_id))
|
|
return
|
|
end
|
|
end
|
|
|
|
-- delete a trade; this can be done here only if..
|
|
-- * it is a trade from the trade list (not an effect of a dialog option)
|
|
-- * the player has the necessary privs
|
|
-- This option is available without having to enter edit mode first.
|
|
if(fields.delete_trade_simple
|
|
and yl_speak_up.may_edit_npc(player, trade.n_id)) then
|
|
-- get the necessary dialog data
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
if(dialog and dialog.trades and trade.trade_id
|
|
and dialog.trades[ trade.trade_id ] and
|
|
not(dialog.trades[ trade.trade_id ].d_id)) then
|
|
-- log the change
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"Trade: Deleted offer "..tostring(trade.trade_id)..".")
|
|
-- delete this particular trade
|
|
dialog.trades[ trade.trade_id ] = nil
|
|
-- actually save the dialog to disk
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
-- we are done with this trade
|
|
yl_speak_up.trade[pname] = nil
|
|
end
|
|
-- go back showing the trade list (since we deleted this trade)
|
|
minetest.show_formspec(pname, "yl_speak_up:trade_list",
|
|
yl_speak_up.get_fs_trade_list(player))
|
|
return
|
|
end
|
|
|
|
-- can the player edit this trade?
|
|
if(fields.edit_trade_simple
|
|
and (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id
|
|
and (yl_speak_up.speak_to[pname].n_id))) then
|
|
-- force edit mode for this trade
|
|
trade.edit_trade = true
|
|
end
|
|
|
|
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
|
|
local player_inv = player:get_inventory()
|
|
-- give the items from the pay slot back
|
|
local pay = trade_inv:get_stack("pay", 1)
|
|
if( player_inv:room_for_item("main", pay)) then
|
|
player_inv:add_item("main", pay)
|
|
trade_inv:set_stack("pay", 1, "")
|
|
end
|
|
-- clear the buy slot as well
|
|
trade_inv:set_stack("buy", 1, "")
|
|
|
|
-- show the edit trade formspec
|
|
if(fields.edit_trade_simple) then
|
|
minetest.show_formspec(pname, "yl_speak_up:add_trade_simple",
|
|
yl_speak_up.get_fs_add_trade_simple(player))
|
|
return
|
|
end
|
|
|
|
-- go back to the main dialog
|
|
if(fields.abort_trade_simple or fields.quit or fields.finished_trading) then
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local o_id = yl_speak_up.speak_to[pname].o_id
|
|
local target_dialog = d_id
|
|
-- if at least one successful trade has happened: show the target dialog
|
|
-- (else show the current dialog again)
|
|
if(trade and trade.trade_done > 0 and yl_speak_up.speak_to[pname].target_d_id) then
|
|
target_dialog = yl_speak_up.speak_to[pname].target_d_id
|
|
end
|
|
-- done trading
|
|
yl_speak_up.speak_to[pname].target_d_id = nil
|
|
yl_speak_up.speak_to[pname].trade_id = nil
|
|
|
|
-- if in edit mode: go back to the edit options dialog
|
|
if(yl_speak_up.edit_mode[pname] == n_id and n_id and o_id) then
|
|
-- go to the edit options dialog
|
|
minetest.show_formspec(pname, "yl_speak_up:edit_option_dialog",
|
|
yl_speak_up.get_fs_edit_option_dialog(player, n_id, d_id, o_id))
|
|
return
|
|
-- show either the current or the target dialog
|
|
elseif(target_dialog) then
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, target_dialog))
|
|
return
|
|
end
|
|
end
|
|
|
|
-- show this formspec again
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
yl_speak_up.get_fs_trade_simple(player))
|
|
end
|
|
|
|
|
|
-- simple trade: add a new trade or edit existing one (by storing a new one);
|
|
-- set trade_id to "new" if it shall be a new trade added to the trade list;
|
|
-- set trade_id to "<d_id> <o_id>" if it shall be a result/effect of a dialog option;
|
|
yl_speak_up.get_fs_add_trade_simple = function(player, trade_id)
|
|
if(not(player)) then
|
|
return yl_speak_up.trade_fail_fs
|
|
end
|
|
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
|
|
|
|
-- is this player allowed to edit the NPC and his trades? If not abort.
|
|
if(not(yl_speak_up.may_edit_npc(player, n_id)) or not(dialog) or not(dialog.n_npc)) then
|
|
return formspec..
|
|
"label[2.0,1.8;Ups! Something went wrong.]"..
|
|
"button[6.2,1.6;2.0,0.9;abort_trade_simple;Back]"
|
|
end
|
|
|
|
-- store the trade_id (so that it doesn't have to be transfered in a hidden field)
|
|
yl_speak_up.speak_to[pname].trade_id = trade_id
|
|
|
|
local delete_button =
|
|
"button[0.2,2.6;1.0,0.9;delete_trade_simple;Delete]"..
|
|
"tooltip[delete_trade_simple;Delete this trade.]"
|
|
-- no point in deleting a new trade - it doesn't exist yet
|
|
if(trade_id and trade_id == "new") then
|
|
delete_button = ""
|
|
end
|
|
return "size[8.5,8]"..
|
|
"label[4.35,0.7;"..minetest.formspec_escape(dialog.n_npc).." sells:]"..
|
|
"list[current_player;main;0.2,3.85;8,1;]"..
|
|
"list[current_player;main;0.2,5.08;8,3;8]"..
|
|
-- show the second slot of the setup inventory in the detached player's inv
|
|
"list[detached:yl_speak_up_player_"..pname..";setup;2,1.5;1,1;]"..
|
|
-- show the second slot of said inventory
|
|
"list[detached:yl_speak_up_player_"..pname..";setup;5,1.5;1,1;1]"..
|
|
"label[2.5,0.0;Configure trade with "..minetest.formspec_escape(dialog.n_npc)..":]"..
|
|
"label[1.5,0.8;The customer pays:]"..
|
|
"label[1.5,2.8;Put items in the two slots and click on \"Store trade\".]"..
|
|
"label[1.5,3.2;You will get your items back when storing the trade.]"..
|
|
"button[0.2,1.6;1.0,0.9;abort_trade_simple;Abort]"..
|
|
delete_button..
|
|
"button[6.2,1.6;2.0,0.9;store_trade_simple;Store trade]"..
|
|
"tooltip[store_trade_simple;Click here to store this as a new trade. Your\n"..
|
|
"items will be returned to you and the trade will\n"..
|
|
"will be shown the way the customer can see it.]"..
|
|
"tooltip[abort_trade_simple;Abort setting up this new trade.]"
|
|
end
|
|
|
|
|
|
-- when closing the yl_speak_up.get_fs_add_trade_simple formspec:
|
|
-- give the items back to the player (he took them from his inventory and
|
|
-- had no real chance to put them elsewhere - so there really ought to be
|
|
-- room enough)
|
|
yl_speak_up.add_trade_simple_return_items = function(player, trade_inv, pay, buy)
|
|
local player_inv = player:get_inventory()
|
|
if( pay and player_inv:room_for_item("main", pay)) then
|
|
player_inv:add_item("main", pay)
|
|
trade_inv:set_stack("setup", 1, "")
|
|
end
|
|
if( buy and player_inv:room_for_item("main", buy)) then
|
|
player_inv:add_item("main", buy)
|
|
trade_inv:set_stack("setup", 2, "")
|
|
end
|
|
end
|
|
|
|
|
|
-- the player wants to add a simple trade; handle formspec input
|
|
-- possible inputs:
|
|
-- fields.back_from_error_msg show this formspec here again
|
|
-- fields.store_trade_simple store this trade as a result and
|
|
-- go on to showing the do_trade_simple formspec
|
|
-- fields.delete_trade_simple delete this trade
|
|
-- go back to edit options dialog
|
|
-- abort_trade_simple, ESC go back to edit options dialog
|
|
-- The rest is inventory item movement.
|
|
yl_speak_up.input_add_trade_simple = function(player, formname, fields)
|
|
if(not(player)) then
|
|
return 0
|
|
end
|
|
local pname = player:get_player_name()
|
|
|
|
-- we return from showing an error message (the player may not have noticed
|
|
-- a chat message while viewing a formspec; thus, we showed a formspec message)
|
|
if(fields.back_from_error_msg) then
|
|
minetest.show_formspec(pname, "yl_speak_up:add_trade_simple",
|
|
yl_speak_up.get_fs_add_trade_simple(player))
|
|
return
|
|
end
|
|
|
|
-- which trade are we talking about?
|
|
local trade_id = yl_speak_up.speak_to[pname].trade_id
|
|
|
|
-- this also contains the inventory list "setup" where the player placed the items
|
|
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
|
|
|
|
-- fields.abort_trade_simple can be ignored as it is similar to ESC
|
|
|
|
local pay = trade_inv:get_stack("setup", 1)
|
|
local buy = trade_inv:get_stack("setup", 2)
|
|
|
|
-- clicking on abort here when adding a new trade via the trade list
|
|
-- goes back to the trade list (does not require special privs)
|
|
if(fields.abort_trade_simple and trade_id == "new") then
|
|
-- we are no longer doing a particular trade
|
|
yl_speak_up.speak_to[pname].trade_id = nil
|
|
-- return the items (setting up the trade was aborted)
|
|
yl_speak_up.add_trade_simple_return_items(player, trade_inv, pay, buy)
|
|
-- ..else go back to the edit options formspec
|
|
minetest.show_formspec(pname, "yl_speak_up:trade_list",
|
|
yl_speak_up.get_fs_trade_list(player))
|
|
return
|
|
end
|
|
-- adding a new trade via the trade list?
|
|
if(not(trade_id) and fields.store_trade_simple) then
|
|
trade_id = "new"
|
|
end
|
|
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local o_id = yl_speak_up.speak_to[pname].o_id
|
|
|
|
-- the trade can only be changed in edit mode
|
|
if((not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id)
|
|
-- exception: when adding a new trade via the trade list
|
|
-- (that is allowed without having to be in edit mode)
|
|
and not(trade_id == "new" and yl_speak_up.may_edit_npc(player, n_id))) then
|
|
-- return the items (setting up the trade was aborted)
|
|
yl_speak_up.add_trade_simple_return_items(player, trade_inv, pay, buy)
|
|
return
|
|
end
|
|
|
|
-- store the new trade
|
|
if(fields.store_trade_simple) then
|
|
local error_msg = ""
|
|
-- check for error conditions
|
|
if(pay:is_empty()) then
|
|
error_msg = "What shall the customer pay?\nWe don't give away stuff for free here!"
|
|
elseif(buy:is_empty()) then
|
|
error_msg = "What shall your NPC sell?\nCustomers won't pay for nothing!"
|
|
elseif(pay:get_wear() > 0 or buy:get_wear() > 0) then
|
|
error_msg = "Selling used items is not possible."
|
|
elseif(not(minetest.registered_items[ pay:get_name() ])
|
|
or not(minetest.registered_items[ buy:get_name() ])) then
|
|
error_msg = "Unkown items cannot be traded."
|
|
elseif(pay:get_name() == buy:get_name()) then
|
|
error_msg = "Selling *and* buying the same item\nat the same time makes no sense."
|
|
else
|
|
-- get the necessary dialog data
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
-- player_gives (pay stack):
|
|
local ps = pay:get_name().." "..tostring(pay:get_count())
|
|
-- npc_gives (buy stack):
|
|
local bs = buy:get_name().." "..tostring(buy:get_count())
|
|
local r_id = "?"
|
|
|
|
if(not(dialog.trades)) then
|
|
dialog.trades = {}
|
|
end
|
|
-- is this a trade attached to the trade list?
|
|
-- or do we have to create a new trade ID?
|
|
if(trade_id == "new") then
|
|
-- if the player adds the same trade again, the ID is reused; other
|
|
-- than that, the ID is uniq
|
|
-- (the ID is formed so that we can later easily sort the offers by
|
|
-- the name of the buy stack - which is more helpful for the player
|
|
-- than sorting by the pay stack)
|
|
trade_id = "sell "..bs.." for "..ps
|
|
-- log the change
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"Trade: Added offer "..tostring(trade_id)..".")
|
|
-- add this new trade
|
|
dialog.trades[ trade_id ] = {pay={ps},buy={bs}}
|
|
-- actually save the dialog to disk
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
-- store the newly created trade_id
|
|
yl_speak_up.speak_to[pname].trade_id = trade_id
|
|
-- all ok so far
|
|
error_msg = nil
|
|
-- storing trades that are associated with particular dialogs and options
|
|
-- requires d_id and o_id to be set
|
|
elseif(trade_id ~= "new" and (not(d_id) or not(o_id))) then
|
|
error_msg = "Internal error. o_id was not set."
|
|
else
|
|
-- record this as a change, but do not save do disk yet
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": Trade "..tostring(trade_id).." added to option "..
|
|
tostring(o_id)..".")
|
|
-- add this new trade - complete with information to which dialog and
|
|
-- to which option the trade belongs
|
|
dialog.trades[ trade_id ] = {pay={ps},buy={bs}, d_id = d_id, o_id = o_id}
|
|
-- all ok so far
|
|
error_msg = nil
|
|
end
|
|
-- do not return yet - the items still need to be given back!
|
|
end
|
|
-- show error message (that leads back to this formspec)
|
|
if(error_msg) then
|
|
minetest.show_formspec(pname, "yl_speak_up:add_trade_simple",
|
|
"size[6,2]"..
|
|
"label[0.2,0.5;"..error_msg.."]"..
|
|
"button[2,1.5;1,0.9;back_from_error_msg;Back]")
|
|
return
|
|
end
|
|
|
|
-- we need a way of deleting trades as well;
|
|
-- this affects only trades that are associated with dialogs and options;
|
|
-- trades from the trade list are deleted more directly
|
|
elseif(fields.delete_trade_simple) then
|
|
-- delete this result (if it exists)
|
|
-- get the necessary dialog data
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
-- record this as a change
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": Trade "..tostring(trade_id).." deleted from option "..
|
|
tostring(o_id)..".")
|
|
-- delete the trade type result
|
|
dialog.trades[ trade_id ] = nil
|
|
-- do not return yet - the items still need to be given back!
|
|
end
|
|
|
|
-- return the items after successful setup
|
|
yl_speak_up.add_trade_simple_return_items(player, trade_inv, pay, buy)
|
|
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
if(dialog.trades[ trade_id ] and dialog.trades[ trade_id ].d_id) then
|
|
yl_speak_up.speak_to[pname].trade_id = trade_id
|
|
-- tell the player that the new trade has been added
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
"size[6,2]"..
|
|
"label[0.2,0.5;The new trade has been configured successfully.]"..
|
|
"button[1.5,1.5;2,0.9;trade_simple_stored;Show trade]")
|
|
-- return back to trade list
|
|
elseif(not(o_id)) then
|
|
-- we are no longer trading
|
|
yl_speak_up.speak_to[pname].trade_id = nil
|
|
-- ..else go back to the edit options formspec
|
|
minetest.show_formspec(pname, "yl_speak_up:trade_list",
|
|
yl_speak_up.get_fs_trade_list(player))
|
|
else
|
|
-- we are no longer trading
|
|
yl_speak_up.speak_to[pname].trade_id = nil
|
|
-- ..else go back to the edit options formspec
|
|
minetest.show_formspec(pname, "yl_speak_up:edit_option_dialog",
|
|
yl_speak_up.get_fs_edit_option_dialog(player, n_id, d_id, o_id))
|
|
end
|
|
end
|
|
|
|
|
|
-- can this trade be made? called in allow_take
|
|
yl_speak_up.can_trade_simple = function(player, count)
|
|
if(not(player)) then
|
|
return 0
|
|
end
|
|
local pname = player:get_player_name()
|
|
-- which trade are we talking about?
|
|
local trade = yl_speak_up.trade[pname]
|
|
-- do we have all the necessary data?
|
|
if(not(trade) or trade.trade_type ~= "trade_simple") then
|
|
return 0
|
|
end
|
|
|
|
-- the player tries to take *less* items than what his payment is;
|
|
-- avoid this confusion!
|
|
if(ItemStack(trade.npc_gives):get_count() ~= count) then
|
|
return 0
|
|
end
|
|
-- buy, sell and config items need to be placed somewhere
|
|
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
|
|
-- 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(trade.n_id)})
|
|
|
|
-- is the payment in the payment slot?
|
|
if( not(trade_inv:contains_item("pay", trade.player_gives))
|
|
-- is the item to be sold in the buy slot?
|
|
or not(trade_inv:contains_item("buy", trade.npc_gives))
|
|
-- has the NPC room for the payment?
|
|
or not(npc_inv:room_for_item("npc_main", trade.player_gives))
|
|
-- has the player room for the sold item?
|
|
or not(player_inv:room_for_item("main", trade.npc_gives))) then
|
|
-- trade not possible
|
|
return 0
|
|
end
|
|
|
|
-- used items cannot be sold as there is no fair way to indicate how
|
|
-- much they are used
|
|
if( trade_inv:get_stack("pay", 1):get_wear() > 0) then
|
|
return 0
|
|
end
|
|
|
|
-- all ok; all items that are to be sold can be taken
|
|
return ItemStack(trade.npc_gives):get_count()
|
|
end
|
|
|
|
|
|
-- actually execute the trade
|
|
yl_speak_up.do_trade_simple = function(player, count)
|
|
-- can the trade be made?
|
|
if(not(yl_speak_up.can_trade_simple(player, count))) then
|
|
return
|
|
end
|
|
|
|
local pname = player:get_player_name()
|
|
-- which trade are we talking about?
|
|
local trade = yl_speak_up.trade[pname]
|
|
|
|
-- buy, sell and config items need to be placed somewhere
|
|
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
|
|
-- the NPCs' inventory
|
|
local npc_inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(trade.n_id)})
|
|
|
|
-- the NPC sells these items right now, and the player is moving it to his inventory
|
|
npc_inv:remove_item("npc_main", trade.npc_gives)
|
|
|
|
-- move price items to the NPC
|
|
local stack = trade_inv:remove_item("pay", trade.player_gives)
|
|
npc_inv:add_item("npc_main", stack)
|
|
-- save the inventory of the npc so that the payment does not get lost
|
|
yl_speak_up.save_npc_inventory( trade.n_id )
|
|
|
|
-- store for statistics how many times the player has executed this trade
|
|
trade.trade_done = trade.trade_done + 1
|
|
|
|
-- log the trade
|
|
yl_speak_up.log_change(pname, trade.n_id,
|
|
"bought "..tostring(trade.npc_gives)..
|
|
" for "..tostring(trade.player_gives))
|
|
end
|
|
|
|
|
|
-- simple trade: one item(stack) for another
|
|
-- handles configuration of new trades and showing the formspec for trades;
|
|
-- checks if payment and buying is possible
|
|
yl_speak_up.get_fs_trade_simple = function(player, trade_id)
|
|
if(not(player)) then
|
|
return yl_speak_up.trade_fail_fs
|
|
end
|
|
local pname = player:get_player_name()
|
|
-- which trade are we talking about?
|
|
local trade = yl_speak_up.trade[pname]
|
|
|
|
if(trade_id) then
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
trade = {
|
|
-- we start with the simple trade
|
|
trade_type = "trade_simple",
|
|
-- can be determined from other variables, but it is easier to store it here
|
|
n_id = n_id,
|
|
npc_name = dialog.n_npc,
|
|
-- for statistics and in order to determine which dialog to show next
|
|
trade_done = 0,
|
|
-- we need to know which option this is
|
|
target_dialog = d_id,
|
|
trade_is_trade_list = true,
|
|
trade_id = trade_id
|
|
}
|
|
if(dialog.trades[ trade_id ]) then
|
|
trade.player_gives = dialog.trades[ trade_id ].pay[1]
|
|
trade.npc_gives = dialog.trades[ trade_id ].buy[1]
|
|
trade.trade_is_trade_list = not(dialog.trades[ trade_id ].d_id)
|
|
else
|
|
trade.edit_trade = true
|
|
end
|
|
yl_speak_up.trade[pname] = trade
|
|
end
|
|
|
|
-- do we have all the necessary data?
|
|
if(not(trade) or trade.trade_type ~= "trade_simple") then
|
|
return yl_speak_up.trade_fail_fs
|
|
end
|
|
-- 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]"..
|
|
"label[4.35,0.7;"..minetest.formspec_escape(trade.npc_name).." sells:]"..
|
|
"list[current_player;main;0.2,3.85;8,1;]"..
|
|
"list[current_player;main;0.2,5.08;8,3;8]"
|
|
|
|
-- configuration of a new trade happens here
|
|
if(not(trade.player_gives) or not(trade.npc_gives) or trade.edit_trade) then
|
|
return yl_speak_up.get_fs_add_trade_simple(player, trade_id)
|
|
end
|
|
|
|
-- view for the customer when actually trading
|
|
|
|
-- buy, sell and config items need to be placed somewhere
|
|
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
|
|
-- 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(trade.n_id)})
|
|
|
|
-- show edit button for the owner if in edit_mode
|
|
if(yl_speak_up.may_edit_npc(player, trade.n_id)) then
|
|
-- for trades as part of the results/effects: allow edit
|
|
if(not(trade.trade_is_trade_list)
|
|
and (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)) then
|
|
formspec = formspec..
|
|
"button[0.2,1.6;1.0,0.9;edit_trade_simple;Edit]"..
|
|
"tooltip[edit_trade_simple;Edit this trade. You can do so only "..
|
|
"if you can edit the NPC as such (i.e. own it).]"
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local tr = dialog.trades[ trade_id ]
|
|
-- offer button to show the edit options dialog if suitable
|
|
if( tr and tr.d_id and tr.o_id) then
|
|
formspec = formspec..
|
|
"button[0.2,-0.5;4.5,0.9;back_to_edit_options;"..
|
|
minetest.formspec_escape("Back to edit dialog "..
|
|
tostring(tr.d_id).." option "..
|
|
tostring(tr.o_id)).."]"..
|
|
"tooltip[back_to_edit_options;Go back to editing the option "..
|
|
"where this trade is located.]"
|
|
end
|
|
|
|
-- 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)
|
|
elseif(trade.trade_is_trade_list) then
|
|
formspec = formspec..
|
|
"button[0.2,1.6;1.2,0.9;delete_trade_simple;Delete]"..
|
|
"tooltip[delete_trade_simple;Delete this trade. You can do so only "..
|
|
"if you can edit the NPC as such (i.e. own it).]"
|
|
end
|
|
end
|
|
|
|
local trade_possible_msg = "Status of trade: Unknown."
|
|
-- has the NPC room for the payment?
|
|
if(not(npc_inv:room_for_item("npc_main", trade.player_gives))) then
|
|
trade_possible_msg = "Sorry. "..minetest.formspec_escape(trade.npc_name)..
|
|
" ran out of inventory space.\nThere is no room to store your payment!]"
|
|
-- can the player pay?
|
|
elseif(not(player_inv:contains_item("main", trade.player_gives))) then
|
|
-- both slots will remain empty
|
|
trade_possible_msg = "You cannot pay the price."
|
|
-- is the slot for the payment empty?
|
|
elseif not(trade_inv:is_empty("pay")) then
|
|
-- both slots will remain empty
|
|
-- (the slot may already contain the right things; we'll find that out later on)
|
|
trade_possible_msg = "Please insert the right payment!"
|
|
-- all good so far; move the price stack to the pay slot
|
|
-- else
|
|
-- -- move price item to the price slot
|
|
-- local stack = player_inv:remove_item("main", trade.player_gives)
|
|
-- trade_inv:add_item("pay", stack)
|
|
else
|
|
trade_possible_msg = "Please insert the right payment in the pay slot."
|
|
end
|
|
|
|
-- make sure the sale slot is empty (we will fill it if the trade is possible)
|
|
trade_inv:set_stack("buy", 1, "")
|
|
-- after all this: does the payment slot contain the right things?
|
|
if(trade_inv:contains_item("pay", trade.player_gives)) then
|
|
trade_possible_msg = "Take the offered item(s) in order to buy them."
|
|
|
|
-- can the NPC provide his part?
|
|
if(not(npc_inv:contains_item("npc_main", trade.npc_gives))) then
|
|
trade_possible_msg = "Sorry. "..minetest.formspec_escape(trade.npc_name)..
|
|
" ran out of stock.\nPlease come back later."
|
|
-- only new/undammaged tools, weapons and armor are accepted
|
|
elseif(trade_inv:get_stack("pay", 1):get_wear() > 0) then
|
|
trade_possible_msg = "Sorry. "..minetest.formspec_escape(trade.npc_name)..
|
|
" accepts only undammaged items.]"
|
|
else
|
|
-- put a *copy* of the item(stack) that is to be sold in the sale slot
|
|
trade_inv:add_item("buy", trade.npc_gives)
|
|
end
|
|
end
|
|
|
|
if(not(player_inv:room_for_item("main", trade.npc_gives))) then
|
|
-- the player has no room for the sold item; give a warning
|
|
trade_possible_msg = "Careful! You do not seem to have enough\n"..
|
|
"free inventory space to store your purchase.]"
|
|
end
|
|
|
|
-- the functionality of the back button depends on context
|
|
if(not(trade.trade_is_trade_list)) then
|
|
-- go back to the right dialog (or forward to the next one)
|
|
formspec = formspec..
|
|
-- "button[6.2,1.6;2.0,0.9;finished_trading;Back to talk]"..
|
|
"button[0.2,0.0;2.0,0.9;finished_trading;Back to talk]"..
|
|
"tooltip[finished_trading;Click here once you've traded enough with this "..
|
|
"NPC and want to get back to talking.]"
|
|
else
|
|
-- go back to the trade list
|
|
formspec = formspec.. "button[0.2,0.0;2.0,0.9;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.]"
|
|
end
|
|
|
|
return formspec..
|
|
"label[2.5,0.0;Trading with "..minetest.formspec_escape(trade.npc_name).."]"..
|
|
"label[1.5,0.7;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,1.2;0.8,0.8;"..tostring(trade.player_gives).."]"..
|
|
"item_image[5.1,1.2;0.8,0.8;"..tostring(trade.npc_gives).."]"..
|
|
"image[3.5,2.0;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
|
|
-- show the pay slot from the detached player's trade inventory
|
|
"list[detached:yl_speak_up_player_"..pname..";pay;2,2.0;1,1;]" ..
|
|
-- show the buy slot from the same inventory
|
|
"list[detached:yl_speak_up_player_"..pname..";buy;5,2.0;1,1;]" ..
|
|
"label[1.5,3.0;"..trade_possible_msg.."]"
|
|
end
|
|
|
|
|
|
-- create a detached inventory for the *player* for trading with the npcs
|
|
minetest.register_on_joinplayer(function(player, last_login)
|
|
local pname = player:get_player_name()
|
|
|
|
-- create the detached inventory;
|
|
-- the functions for monitoring changes will be important later on
|
|
-- only the the player owning this detached inventory may access it
|
|
local trade_inv = minetest.create_detached_inventory("yl_speak_up_player_"..tostring(pname), {
|
|
-- moving of items between diffrent lists is not allowed
|
|
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
|
if(not(player) or player:get_player_name() ~= pname) then
|
|
return 0
|
|
end
|
|
if(from_list ~= to_list) then
|
|
return 0
|
|
end
|
|
return count
|
|
end,
|
|
|
|
-- these all require calling special functions, depending on context
|
|
allow_put = function(inv, listname, index, stack, player)
|
|
if(not(player) or player:get_player_name() ~= pname) then
|
|
return 0
|
|
end
|
|
-- the "buy" slot is managed by the NPC; the player only takes from it
|
|
if(listname == "buy") then
|
|
return 0
|
|
end
|
|
-- do not allow used items or items with metadata in the setup slots
|
|
-- (they can't really be traded later on anyway)
|
|
if(listname == "setup") then
|
|
if(stack:get_wear() > 0) then
|
|
minetest.chat_send_player(player:get_player_name(),
|
|
"Your NPC accepts only undammaged items. "..
|
|
"Trading dammaged items would be unfair.")
|
|
return 0
|
|
end
|
|
-- items with metadata cannot be traded
|
|
if(yl_speak_up.check_stack_has_meta(player, stack)) then
|
|
minetest.chat_send_player(player:get_player_name(),
|
|
"Your NPC cannot sell items that contain "..
|
|
"additional (meta-) data.")
|
|
return 0
|
|
end
|
|
end
|
|
return stack:get_count()
|
|
end,
|
|
allow_take = function(inv, listname, index, stack, player)
|
|
if(not(player) or player:get_player_name() ~= pname) then
|
|
return 0
|
|
end
|
|
-- can the trade be made?
|
|
if(listname == "buy") then
|
|
return yl_speak_up.can_trade_simple(player, stack:get_count())
|
|
end
|
|
return stack:get_count()
|
|
end,
|
|
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
|
end,
|
|
on_put = function(inv, listname, index, stack, player)
|
|
if(listname == "pay") then
|
|
local pname = player:get_player_name()
|
|
-- show formspec with updated information (perhaps sale is now possible)
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
yl_speak_up.get_fs_trade_simple(player))
|
|
end
|
|
end,
|
|
on_take = function(inv, listname, index, stack, player)
|
|
-- the player may have put something wrong in the payment slot
|
|
-- -> show updated formspec
|
|
if(listname == "pay") then
|
|
local pname = player:get_player_name()
|
|
-- show formspec with updated information (perhaps sale is now possible)
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
yl_speak_up.get_fs_trade_simple(player))
|
|
elseif(listname == "buy") then
|
|
yl_speak_up.do_trade_simple(player, stack:get_count())
|
|
-- information may require an update (NPC might now be out of stock), or
|
|
-- the player can do the trade a second time
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
yl_speak_up.get_fs_trade_simple(player))
|
|
end
|
|
end,
|
|
})
|
|
-- prepare the actual inventories
|
|
trade_inv:set_size("pay", 1)
|
|
trade_inv:set_size("buy", 1)
|
|
-- for setting up new simple trades
|
|
trade_inv:set_size("setup", 2*1)
|
|
end)
|