forked from Sokomine/yl_speak_up
196 lines
6.6 KiB
Lua
196 lines
6.6 KiB
Lua
-----------------------------------------------------------------------------
|
|
-- limits for trading: maximum and minimum stock to keep
|
|
-----------------------------------------------------------------------------
|
|
-- sometimes players may not want the NPC to sell *all* of their stock,
|
|
-- or not let the NPC buy endless amounts of something when only a limited
|
|
-- amount is needed
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- helper function: make sure all necessary entries in the trades table exist
|
|
yl_speak_up.setup_trade_limits = function(dialog)
|
|
if(not(dialog)) then
|
|
dialog = {}
|
|
end
|
|
if(not(dialog.trades)) then
|
|
dialog.trades = {}
|
|
end
|
|
if(not(dialog.trades.limits)) then
|
|
dialog.trades.limits = {}
|
|
end
|
|
if(not(dialog.trades.limits.sell_if_more)) then
|
|
dialog.trades.limits.sell_if_more = {}
|
|
end
|
|
if(not(dialog.trades.limits.buy_if_less)) then
|
|
dialog.trades.limits.buy_if_less = {}
|
|
end
|
|
return dialog
|
|
end
|
|
|
|
|
|
-- helper function: count how many items the NPC has in his inventory
|
|
-- empty stacks are counted under the key "";
|
|
-- for other items, the amount of items of each type is counted
|
|
yl_speak_up.count_npc_inv = function(n_id)
|
|
if(not(n_id)) then
|
|
return {}
|
|
end
|
|
-- the NPC's inventory
|
|
local npc_inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)})
|
|
|
|
if(not(npc_inv)) then
|
|
return {}
|
|
end
|
|
local anz = npc_inv:get_size('npc_main')
|
|
local stored = {}
|
|
for i=1, anz do
|
|
local stack = npc_inv:get_stack('npc_main', i )
|
|
local name = stack:get_name()
|
|
local count = stack:get_count()
|
|
-- count empty stacks
|
|
if(name=="") then
|
|
count = 1
|
|
end
|
|
-- count how much of each item is there
|
|
if(not(stored[ name ])) then
|
|
stored[ name ] = count
|
|
else
|
|
stored[ name ] = stored[ name ] + count
|
|
end
|
|
end
|
|
return stored
|
|
end
|
|
|
|
|
|
-- helper function: update the items table so that it reflects a limitation
|
|
-- items is a table (list) with these entries:
|
|
-- [1] 0 in stock;
|
|
-- [2] sell if more than 0;
|
|
-- [3] buy if less than 10000;
|
|
-- [4] item is part of a trade offer
|
|
yl_speak_up.insert_trade_item_limitation = function( items, k, i, v )
|
|
if( i<1 or i>4) then
|
|
return;
|
|
end
|
|
if( not( items[ k ] )) then
|
|
-- 0 in stock; sell if more than 0; buy if less than 10000; item is part of a trade offer
|
|
items[ k ] = { 0, 0, 10000, false, #items }
|
|
end
|
|
items[ k ][ i ] = v
|
|
end
|
|
|
|
|
|
-- helper function; returns how often a trade can be done
|
|
-- stock_buy how much of the buy stack does the NPC have in storage?
|
|
-- stock_pay how much of the price stack does the NPC have in storage?
|
|
-- buy_stack stack containing the item the NPC sells
|
|
-- pay_stack stack containing the price for said item
|
|
-- min_storage how many items of the buy stack items shall the NPC keep?
|
|
-- max_storage how many items of the pay stack items can the NPC accept?
|
|
-- used in fs_trade_via_buy_button.lua and fs_trade_list.lua
|
|
yl_speak_up.get_trade_amount_available = function(stock_buy, stock_pay, buy_stack, pay_stack, min_storage, max_storage)
|
|
local stock = 0
|
|
-- the NPC shall not sell more than this
|
|
if(min_storage and min_storage > 0) then
|
|
stock_buy = math.max(0, stock_buy - min_storage)
|
|
end
|
|
stock = math.floor(stock_buy / buy_stack:get_count())
|
|
-- the NPC shall not buy more than this
|
|
if(max_storage and max_storage < 10000) then
|
|
stock_pay = math.min(max_storage - stock_pay, 10000)
|
|
stock = math.min(stock, math.floor(stock_pay / pay_stack:get_count()))
|
|
end
|
|
return stock
|
|
end
|
|
|
|
|
|
|
|
-- helper function; also used by fs_trade_list.lua
|
|
yl_speak_up.get_sorted_trade_id_list = function(dialog, show_dialog_option_trades)
|
|
-- make sure all fields exist
|
|
yl_speak_up.setup_trade_limits(dialog)
|
|
local keys = {}
|
|
if(show_dialog_option_trades) then
|
|
for k, v in pairs(dialog.trades) do
|
|
if(k ~= "limits" and k ~= "" and v.d_id) then
|
|
table.insert(keys, k)
|
|
end
|
|
end
|
|
else
|
|
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
|
|
end
|
|
table.sort(keys)
|
|
return keys
|
|
end
|
|
|
|
|
|
-- taken from trade_simple.lua:
|
|
|
|
-- 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
|
|
|
|
-- Note: That the trade cannot be deleted outside edit mode if it is the action
|
|
-- belonging to an option is checked in editor/trade_*.lua
|
|
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
|