forked from Sokomine/yl_speak_up
		
	
		
			
				
	
	
		
			1019 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			1019 lines
		
	
	
		
			39 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]"
 | |
| 
 | |
| 
 | |
| -- helper function for
 | |
| -- 	yl_speak_up.input_do_trade_simple (here) and
 | |
| -- 	yl_speak_up.input_trade_via_buy_button (in fs_trade_via_buy_button.lua)
 | |
| --
 | |
| -- 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)
 | |
| --  * it is a trade associated with a dialog option and the player is in
 | |
| --    edit mode
 | |
| --  * the player has the necessary privs
 | |
| -- This option is available without having to enter edit mode first.
 | |
| yl_speak_up.delete_trade_simple = function(player, trade_id)
 | |
| 	local pname = player:get_player_name()
 | |
| 	local n_id = yl_speak_up.speak_to[pname].n_id
 | |
| 	if(not(yl_speak_up.may_edit_npc(player, n_id))) then
 | |
| 		-- not a really helpful message - but then, this should never happen (player probably cheated)
 | |
| 		return yl_speak_up.trade_fail_msg
 | |
| 	end
 | |
| 	-- get the necessary dialog data
 | |
| 	local dialog = yl_speak_up.speak_to[pname].dialog
 | |
| 	-- store d_id and o_id in order to be able to return to the right
 | |
| 	-- edit options dialog
 | |
| 	local back_to_d_id = nil
 | |
| 	local back_to_o_id = nil
 | |
| 	if(dialog and dialog.trades and trade_id
 | |
| 	  and dialog.trades[ trade_id ] and n_id) then
 | |
| 
 | |
| 		if( dialog.trades[ trade_id ].d_id
 | |
| 		  and yl_speak_up.edit_mode[pname] ~= n_id) then
 | |
| 			yl_speak_up.show_fs(player, "msg", {
 | |
| 				input_to = "yl_speak_up:do_trade_simple",
 | |
| 				formspec = "size[6,2]"..
 | |
| 					"label[0.2,-0.2;"..
 | |
| 						"Trades that are attached to dialog options\n"..
 | |
| 						"can only be deleted in edit mode. Please tell\n"..
 | |
| 						"your NPC that you are its owner and have\n"..
 | |
| 						"new commands!]"..
 | |
| 					"button[2,1.5;1,0.9;back_from_error_msg;Back]"})
 | |
| 			return
 | |
| 		end
 | |
| 		if( dialog.trades[ trade_id ].d_id ) then
 | |
| 			back_to_d_id = dialog.trades[ trade_id ].d_id
 | |
| 			back_to_o_id = dialog.trades[ trade_id ].o_id
 | |
| 		end
 | |
| 		-- log the change
 | |
| 		yl_speak_up.log_change(pname, n_id,
 | |
| 			"Trade: Deleted offer "..tostring(trade_id)..".")
 | |
| 		-- delete this particular trade
 | |
| 		dialog.trades[ 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
 | |
| 		yl_speak_up.speak_to[pname].trade_id = nil
 | |
| 		yl_speak_up.speak_to[pname].trade_done = nil
 | |
| 	end
 | |
| 	-- always return to edit options dialog if deleting a trade that belonged to one
 | |
| 	if(back_to_d_id and back_to_o_id) then
 | |
| 		yl_speak_up.show_fs(player, "edit_option_dialog",
 | |
| 			{n_id = n_id, d_id = back_to_d_id, o_id = back_to_o_id})
 | |
| 		return
 | |
| 	end
 | |
| 	-- go back showing the trade list (since we deleted this trade)
 | |
| 	yl_speak_up.show_fs(player, "trade_list")
 | |
| 	return
 | |
| end
 | |
| 
 | |
| 
 | |
| -- 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
 | |
| 		yl_speak_up.show_fs(player, "trade_list")
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- get from a dialog option trade back to the list of all these trades
 | |
| 	if(fields.show_trade_list_dialog_options) then
 | |
| 		yl_speak_up.show_fs(player, "trade_list", true)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- a new trade has been stored - show it
 | |
| 	if(fields.trade_simple_stored) then
 | |
| 		yl_speak_up.show_fs(player, "trade_simple", yl_speak_up.speak_to[pname].trade_id)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	if(fields.buy_directly) then
 | |
| 		local error_msg = yl_speak_up.do_trade_direct(player)
 | |
| 
 | |
| 		if(error_msg ~= "") then
 | |
| 			yl_speak_up.show_fs(player, "msg", {
 | |
| 				input_to = "yl_speak_up:do_trade_simple",
 | |
| 				formspec = "size[6,2]"..
 | |
| 					"label[0.2,-0.2;"..error_msg.."]"..
 | |
| 					"button[2,1.5;1,0.9;back_from_error_msg;Back]"})
 | |
| 			return
 | |
| 		end
 | |
| 		yl_speak_up.show_fs(player, "trade_simple", 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
 | |
| 			yl_speak_up.show_fs(player, "edit_option_dialog",
 | |
| 				{n_id = n_id, d_id = tr.d_id, o_id = tr.o_id})
 | |
| 			return
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	if(fields.delete_trade_simple) then
 | |
| 		yl_speak_up.delete_trade_simple(player, trade.trade_id)
 | |
| 		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
 | |
| 		yl_speak_up.show_fs(player, "add_trade_simple", trade.trade_id)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- go back to the main dialog
 | |
| 	if(fields.abort_trade_simple or fields.quit or fields.finished_trading) then
 | |
| 		-- was the action a success?
 | |
| 		local success = not(not(trade and trade.trade_done and trade.trade_done > 0))
 | |
| 		local a_id = trade.a_id
 | |
| 		local o_id = trade.o_id
 | |
| 		yl_speak_up.debug_msg(player, n_id, o_id, "Ending trade.")
 | |
| 		-- done trading
 | |
| 		yl_speak_up.speak_to[pname].target_d_id = nil
 | |
| 		yl_speak_up.speak_to[pname].trade_id = nil
 | |
| 		-- execute the next action
 | |
| 		yl_speak_up.execute_next_action(player, a_id, success)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- show this formspec again
 | |
| 	yl_speak_up.show_fs(player, "trade_simple")
 | |
| 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 "size[9,2]"..
 | |
| 			"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,9]"..
 | |
| 		"label[4.35,0.8;"..minetest.formspec_escape(dialog.n_npc).." sells:]"..
 | |
| 		"list[current_player;main;0.2,4.85;8,1;]"..
 | |
| 		"list[current_player;main;0.2,6.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[0.5,0.0;Configure trade with "..minetest.formspec_escape(dialog.n_npc)..":]"..
 | |
| 		"label[1.5,0.8;The customer pays:]"..
 | |
| 		"label[1.5,3.8;Put items in the two slots and click on \"Store trade\".]"..
 | |
| 		"label[1.5,4.2;You will get your items back when storing the trade.]"..
 | |
| 		-- annoyingly, the height value no longer works :-(
 | |
| 		"label[0.2,2.5;Item\nname:]"..
 | |
| 		"field[1.5,3.2;3,0.2;item_name_price;;]"..
 | |
| 		"label[4.35,2.5;If you don't have the item you\n"..
 | |
| 				"want to buy, then enter its item\n"..
 | |
| 				"name (i.e. default:diamond) here.]"..
 | |
| 		"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()
 | |
| 
 | |
| 	local input_to = "add_trade_simple"
 | |
| 	-- are we editing an action of the type trade?
 | |
| 	if(   yl_speak_up.speak_to[pname][ "tmp_action" ]
 | |
| 	  and yl_speak_up.speak_to[pname][ "tmp_action" ].what == 3) then
 | |
| 		input_to = "edit_actions"
 | |
| 	end
 | |
| 
 | |
| 	-- 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
 | |
| 		yl_speak_up.show_fs(player, input_to)
 | |
| 		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
 | |
| 		yl_speak_up.show_fs(player, "trade_list")
 | |
| 		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 = ""
 | |
| 		local simulated_pay = false
 | |
| 		if(pay:is_empty() and fields.item_name_price and fields.item_name_price ~= "") then
 | |
| 			pay = ItemStack(fields.item_name_price)
 | |
| 			simulated_pay = true
 | |
| 		end
 | |
| 		-- 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
 | |
| 		-- make sure we don't create items here out of thin air
 | |
| 		if(simulated_pay) then
 | |
| 			pay = ItemStack("")
 | |
| 		end
 | |
| 		-- show error message (that leads back to this formspec)
 | |
| 		if(error_msg) then
 | |
| 			yl_speak_up.show_fs(player, "msg", {
 | |
| 				input_to = "yl_speak_up:"..input_to,
 | |
| 				formspec =
 | |
| 					"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)..".")
 | |
| 		if(not(dialog.trades)) then
 | |
| 			dialog.trades = {}
 | |
| 		end
 | |
| 		-- delete the trade type result
 | |
| 		if(trade_id) then
 | |
| 			dialog.trades[ trade_id ] = nil
 | |
| 		end
 | |
| 		-- 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(not(dialog.trades)) then
 | |
| 		dialog.trades = {}
 | |
| 	end
 | |
| 	if(dialog.trades[ trade_id ] and dialog.trades[ trade_id ].d_id
 | |
| 	  and input_to ~= "edit_actions") then
 | |
| 		yl_speak_up.speak_to[pname].trade_id = trade_id
 | |
| 		-- tell the player that the new trade has been added
 | |
| 		yl_speak_up.show_fs(player, "msg", {
 | |
| 				input_to = "yl_speak_up:do_trade_simple",
 | |
| 				formspec =
 | |
| 					"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
 | |
| 		yl_speak_up.show_fs(player, "trade_list")
 | |
| 	else
 | |
| 		-- we are no longer trading
 | |
| 		yl_speak_up.speak_to[pname].trade_id = nil
 | |
| 		-- the trade has been stored or deleted successfully
 | |
| 		return true
 | |
| --		-- ..else go back to the edit options formspec (obsolete)
 | |
| --		yl_speak_up.show_fs(player, "edit_option_dialog",
 | |
| --			{n_id = n_id, d_id = d_id, o_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
 | |
| 	-- (this is also necessary to switch to the right target dialog when
 | |
| 	--  dealing with dialog options trades)
 | |
| 	yl_speak_up.trade[pname].trade_done = yl_speak_up.trade[pname].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
 | |
| 
 | |
| 
 | |
| -- try to do the trade directly - without moving items in the buy/sell inventory slot
 | |
| -- returns error_msg or "" when successful
 | |
| yl_speak_up.do_trade_direct = function(player)
 | |
| 	if(not(player)) then
 | |
| 		return "Player, where are you?"
 | |
| 	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 "No trade found!"
 | |
| 	end
 | |
| 	-- 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)})
 | |
| 	-- has the NPC the item he wants to sell?
 | |
| 	if(    not(npc_inv:contains_item("npc_main", trade.npc_gives))) then
 | |
| 		return "Sorry. This item is sold out!"
 | |
| 	-- has the NPC room for the payment?
 | |
| 	elseif(not(npc_inv:room_for_item("npc_main", trade.player_gives))) then
 | |
| 		return "Sorry. No room to store your payment!\n"..
 | |
| 			"Please try again later."
 | |
| 	-- can the player pay the price?
 | |
| 	elseif(not(player_inv:contains_item("main", trade.player_gives))) then
 | |
| 		return "You can't pay the price!"
 | |
| 	-- has the player room for the sold item?
 | |
| 	elseif(not(player_inv:room_for_item("main", trade.npc_gives))) then
 | |
| 		return "You don't have enough free inventory space.\n"..
 | |
| 			"Trade aborted."
 | |
| 	end
 | |
| 	local payment = player_inv:remove_item("main", trade.player_gives)
 | |
| 	local sold    = npc_inv:remove_item("npc_main", trade.npc_gives)
 | |
| 	-- 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 "At least one of the items that shall be traded\n"..
 | |
| 			"is dammaged. Trade aborted."
 | |
| 	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( trade.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)
 | |
| 	yl_speak_up.trade[pname].trade_done = yl_speak_up.trade[pname].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))
 | |
| 	return ""
 | |
| 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 and trade.trade_id and trade_id and trade.trade_id == trade_id) then
 | |
| 		-- nothing to do; trade is already loaded and stored
 | |
| 	elseif(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
 | |
| 
 | |
| 		yl_speak_up.setup_trade_limits(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)
 | |
| 			yl_speak_up.speak_to[pname].trade_id = trade_id
 | |
| 			-- copy the limits
 | |
| 			local stack = ItemStack(trade.npc_gives)
 | |
| 			trade.npc_gives_name = stack:get_name()
 | |
| 			trade.npc_gives_amount = stack:get_count()
 | |
| 			trade.min_storage = dialog.trades.limits.sell_if_more[ trade.npc_gives_name ]
 | |
| 			stack = ItemStack(trade.player_gives)
 | |
| 			trade.player_gives_name = stack:get_name()
 | |
| 			trade.player_gives_amount = stack:get_count()
 | |
| 			trade.max_storage = dialog.trades.limits.buy_if_less[  trade.player_gives_name ]
 | |
| 		else
 | |
| 			trade.edit_trade = true
 | |
| 		end
 | |
| 		yl_speak_up.trade[pname] = trade
 | |
| 		-- store which action we are working at
 | |
| 		trade.a_id = yl_speak_up.speak_to[pname].a_id
 | |
| 	else
 | |
| 		trade_id = yl_speak_up.speak_to[pname].trade_id
 | |
| 		trade.trade_id = trade_id
 | |
| 	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]"..
 | |
| 		yl_speak_up.show_fs_simple_deco(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 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)
 | |
| 		formspec = formspec..
 | |
| 			"button[0.2,2.0;1.2,0.9;delete_trade_simple;Delete]"..
 | |
| 			"tooltip[delete_trade_simple;"..
 | |
| 				"Delete this trade. You can do so only if\n"..
 | |
| 				"you can edit the NPC as such (i.e. own it).]"
 | |
| 		if(not(trade.trade_is_trade_list)) then
 | |
| 			-- normal back button will lead to the talk dialog or edit option dialog;
 | |
| 			-- add this second back button to go back to the list of all dialog option trades
 | |
| 			formspec = formspec..
 | |
| 				"button[0.2,1.0;2.0,0.9;show_trade_list_dialog_options;Back to list]"..
 | |
| 				"tooltip[show_trade_list_dialog_options;"..
 | |
| 					"Click here to get back to the list of all trades\n"..
 | |
| 					"associated with dialog options (like this one).]"
 | |
| 			local dialog = yl_speak_up.speak_to[pname].dialog
 | |
| 			local tr = dialog.trades[ trade_id ]
 | |
| 			if( tr and tr.d_id and tr.o_id) then
 | |
| 				formspec = formspec..
 | |
| 					"label[0.2,-0.3;This trade belongs to dialog "..
 | |
| 						minetest.formspec_escape(tostring(tr.d_id)).." option "..
 | |
| 						minetest.formspec_escape(tostring(tr.o_id))..".]"
 | |
| 			end
 | |
| 		end
 | |
| 	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
 | |
| 
 | |
| 
 | |
| 	local trade_possible_msg = "Status of trade: Unknown."
 | |
| 	local can_trade = false
 | |
| 	-- find out how much the npc has stoerd
 | |
| 	local stock_pay = 0
 | |
| 	local stock_buy = 0
 | |
| 	-- only count the inv if there actually are any mins or max
 | |
| 	if(trade.min_storage or trade.max_storage) then
 | |
| 		local n_id = yl_speak_up.speak_to[pname].n_id
 | |
| 		local counted_npc_inv = {}
 | |
| 		counted_npc_inv = yl_speak_up.count_npc_inv(n_id)
 | |
| 		stock_pay = counted_npc_inv[trade.player_gives_name] or 0
 | |
| 		stock_buy = counted_npc_inv[trade.npc_gives_name] or 0
 | |
| 	end
 | |
| 	-- 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."
 | |
| 	-- has the NPC room for the payment?
 | |
| 	elseif(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!"
 | |
| 	-- trade limit: is enough left after the player buys the item?
 | |
| 	elseif(trade.min_storage and trade.min_storage > stock_buy - trade.npc_gives_amount) then
 | |
| 		trade_possible_msg = "Sorry. "..minetest.formspec_escape(trade.npc_name)..
 | |
| 			" currently does not want to\nsell that much."..
 | |
| 			" Current stock: "..tostring(stock_buy)..
 | |
| 			" (min: "..tostring(trade.min_storage)..
 | |
| 			"). Perhaps later?"
 | |
| 	-- trade limit: make sure the bought amount does not exceed the desired maximum
 | |
| 	elseif(trade.max_storage and trade.max_storage < stock_pay + trade.player_gives_amount) then
 | |
| 		trade_possible_msg = "Sorry. "..minetest.formspec_escape(trade.npc_name)..
 | |
| 			" currently does not want to\nbuy that much."..
 | |
| 			" Current stock: "..tostring(stock_pay)..
 | |
| 			" (max: "..tostring(trade.max_storage)..
 | |
| 			"). Perhaps later?"
 | |
| 	-- trade as an action
 | |
| 	elseif(not(trade.trade_is_trade_list)) then
 | |
| 		if(trade_inv:contains_item("pay", trade.player_gives)) then
 | |
| 		-- all good so far; move the price stack to the pay slot
 | |
| 			-- move price item to the price slot
 | |
| 			local stack = player_inv:remove_item("main", trade.player_gives)
 | |
| 			trade_inv:add_item("pay", stack)
 | |
| 			trade_possible_msg = "Please take your purchase!"
 | |
| 			can_trade = true
 | |
| 		elseif(trade_inv:is_empty("pay")) then
 | |
| 			trade_possible_msg = "Please insert the right payment in the pay slot\n"..
 | |
| 				"and then take your purchase."
 | |
| 			can_trade = false
 | |
| 		else
 | |
| 			trade_possible_msg = "This is not what "..minetest.formspec_escape(trade.npc_name)..
 | |
| 				" wants.\nPlease insert the right payment!"
 | |
| 			can_trade = false
 | |
| 		end
 | |
| 	-- 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 = "This is not what "..minetest.formspec_escape(trade.npc_name)..
 | |
| 			" wants.\nPlease insert the right payment!"
 | |
| 	else
 | |
| 		trade_possible_msg = "Please insert the right payment in the pay slot\n"..
 | |
| 			"or click on \"buy\"."..
 | |
| 			"]button[6.5,2.0;1.2,0.9;buy_directly;Buy]"..
 | |
| 			"tooltip[buy_directly;"..
 | |
| 				"Click here in order to buy directly without having to insert\n"..
 | |
| 				"your payment manually into the pay slot."
 | |
| 		can_trade = true
 | |
| 	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(can_trade and trade_inv:contains_item("pay", trade.player_gives)) then
 | |
| 		trade_possible_msg = "Take the offered item(s) in order to buy them."
 | |
| 
 | |
| 		-- only new/undammaged tools, weapons and armor are accepted
 | |
| 		if(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(can_trade and 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
 | |
| 
 | |
| 	local trades_done = "Not yet traded."
 | |
| 	if(yl_speak_up.trade[pname].trade_done > 0) then
 | |
| 		trades_done = "Traded: "..tostring(yl_speak_up.trade[pname].trade_done).." time(s)"
 | |
| 	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.."]"..
 | |
| 		"label[6.0,1.5;"..trades_done.."]"
 | |
| end
 | |
| 
 | |
| 
 | |
| -- functions for handling the detached trade inventory of players
 | |
| -- these functions exist as extra functions so that they can be changed with /npc_talk_reload
 | |
| 
 | |
| -- moving of items between diffrent lists is not allowed
 | |
| yl_speak_up.trade_inv_allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
 | |
| 	if(not(player)) 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
 | |
| yl_speak_up.trade_inv_allow_put = function(inv, listname, index, stack, player)
 | |
| 	if(not(player)) 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
 | |
| 		-- check if player can edit NPC, item is undammaged and contains no metadata
 | |
| 		return yl_speak_up.inventory_allow_item(player, stack,
 | |
| 			"yl_speak_up:add_trade_simple")
 | |
| 	end
 | |
| 	-- allow putting something in in edit mode - but not otherwise
 | |
| 	if(listname == "npc_gives") then
 | |
| 		local pname = player:get_player_name()
 | |
| 		local n_id = yl_speak_up.speak_to[pname].n_id
 | |
| 		-- only in edit mode! else the NPC manages this slot
 | |
| 		if(not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id) then
 | |
| 			return 0
 | |
| 		end
 | |
| 	end
 | |
| 	return stack:get_count()
 | |
| end
 | |
| 
 | |
| yl_speak_up.trade_inv_allow_take = function(inv, listname, index, stack, player)
 | |
| 	if(not(player)) 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
 | |
| 
 | |
| yl_speak_up.trade_inv_on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
 | |
| end
 | |
| 
 | |
| yl_speak_up.trade_inv_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)
 | |
| 		yl_speak_up.show_fs(player, "trade_simple")
 | |
| 	elseif(listname == "npc_gives"
 | |
| 	    or listname == "npc_wants") then
 | |
| 		-- monitor changes in order to adjust the formspec
 | |
| 		yl_speak_up.action_inv_changed(inv, listname, index, stack, player, "put")
 | |
| 	end
 | |
| end
 | |
| 
 | |
| yl_speak_up.trade_inv_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)
 | |
| 		yl_speak_up.show_fs(player, "trade_simple")
 | |
| 	elseif(listname == "buy") then
 | |
| 		-- do the exchange
 | |
| 		yl_speak_up.do_trade_simple(player, stack:get_count())
 | |
| 		local pname = player:get_player_name()
 | |
| 		-- which trade are we talking about?
 | |
| 		local trade = yl_speak_up.trade[pname]
 | |
| 		-- when the player traded once inside an action: that action was a success;
 | |
| 		-- 	execute next action
 | |
| 		-- but only if not in edit mode
 | |
| 		if(trade and trade.trade_done > 0
 | |
| 		  and not(trade.trade_is_trade_list)
 | |
| 		  and yl_speak_up.edit_mode[pname] ~= trade.n_id) then
 | |
| 			local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
 | |
| 			-- return surplus items from the pay slot
 | |
| 			local pay = trade_inv:get_stack("pay", 1)
 | |
| 			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("pay", 1, "")
 | |
| 			end
 | |
| 			-- done trading
 | |
| 			yl_speak_up.speak_to[pname].target_d_id = nil
 | |
| 			yl_speak_up.speak_to[pname].trade_id = nil
 | |
| 			-- execute the next action
 | |
| 			yl_speak_up.execute_next_action(player, trade.a_id, true)
 | |
| 			return
 | |
| 		end
 | |
| 		-- information may require an update (NPC might now be out of stock), or
 | |
| 		-- the player can do the trade a second time
 | |
| 		yl_speak_up.show_fs(player, "trade_simple")
 | |
| 	elseif(listname == "npc_gives"
 | |
| 	    or listname == "npc_wants") then
 | |
| 		-- monitor changes in order to adjust the formspec
 | |
| 		yl_speak_up.action_inv_changed(inv, listname, index, stack, player, "take")
 | |
| 	end
 | |
| end
 | |
| 
 | |
| -- create a detached inventory for the *player* for trading with the npcs
 | |
| -- (called in minetest.register_on_joinplayer)
 | |
| yl_speak_up.player_joined_add_trade_inv = 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), {
 | |
| 		allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
 | |
| 			return yl_speak_up.trade_inv_allow_move(inv, from_list, from_index, to_list,
 | |
| 								to_index, count, player)
 | |
| 		end,
 | |
| 
 | |
| 	        allow_put = function(inv, listname, index, stack, player)
 | |
| 			return yl_speak_up.trade_inv_allow_put(inv, listname, index, stack, player)
 | |
| 		end,
 | |
| 	        allow_take = function(inv, listname, index, stack, player)
 | |
| 			return yl_speak_up.trade_inv_allow_take(inv, listname, index, stack, player)
 | |
| 		end,
 | |
| 	        on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
 | |
| 			return yl_speak_up.trade_inv_on_move(inv, from_list, from_index, to_list,
 | |
| 								to_index, count, player)
 | |
| 		end,
 | |
| 	        on_put = function(inv, listname, index, stack, player)
 | |
| 			return yl_speak_up.trade_inv_on_put(inv, listname, index, stack, player)
 | |
| 		end,
 | |
| 	        on_take = function(inv, listname, index, stack, player)
 | |
| 			return yl_speak_up.trade_inv_on_take(inv, listname, index, stack, player)
 | |
| 		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)
 | |
| 	-- for setting up actions
 | |
| 	trade_inv:set_size("npc_gives", 1)
 | |
| 	trade_inv:set_size("npc_wants", 1)
 | |
| 	-- for setting wielded items (left and right)
 | |
| 	trade_inv:set_size("wield", 2)
 | |
| end
 |