yl_speak_up/fs_trade_limit.lua
2022-09-05 07:00:55 +02:00

226 lines
7.2 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
-- handle input to the formspec
yl_speak_up.input_trade_limit = function(player, formname, fields)
-- the player has selected an entry - edit it
if(fields and fields["edit_trade_limit"]) then
local selection = minetest.explode_table_event( fields[ "edit_trade_limit" ])
if( selection and selection['row']
and (selection['type'] == 'DCL' or selection['type'] == 'CHG')) then
-- show edit trade limit formspec
yl_speak_up.show_fs(player, "edit_trade_limit", {selected_row = selection['row']})
return
end
end
if(fields and (fields.back or fields.quit)) then
-- back to the normal trade list
yl_speak_up.show_fs(player, "trade_list")
return
end
-- else show this formspec again
yl_speak_up.show_fs(player, "trade_limit")
end
-- show the formspec
yl_speak_up.get_fs_trade_limit = function(player, selected)
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
-- in items, existing amount and limit are collected for display
local items = {}
if(not(dialog) or not(n_id)) then
return "Error. Missing dialog when accessing trade limits."
end
-- make sure all necessary entries in the trades table exist
yl_speak_up.setup_trade_limits(dialog)
-- how many items does the NPC have alltogether?
-- there may be more than one stack of the same type; store amount
local counted_inv = yl_speak_up.count_npc_inv(n_id)
for k,v in pairs(counted_inv) do
yl_speak_up.insert_trade_item_limitation(items, k, 1, v)
end
-- items that are part of any of the trades may also be subject to limits; store item names
for trade_id, trade_data in ipairs(dialog.trades) do
if(trade_id ~= "limits") then
-- what the NPC sells may be subject to limits
local stack = ItemStack(trade_data.buy[1])
yl_speak_up.insert_trade_item_limitation(items, stack:get_name(), 4, true )
-- what the customer pays may be subject to limits as well
stack = ItemStack(trade_data.pay[1])
yl_speak_up.insert_trade_item_limitation(items, stack:get_name(), 4, true )
end
end
-- everything for which there's already a sell_if_more limit
for k,v in pairs(dialog.trades.limits.sell_if_more) do
mob_trading.insert_item_limitation( items, k, 2, v )
end
-- everything for which there's already a buy_if_less limit
for k,v in pairs(dialog.trades.limits.buy_if_less ) do
mob_trading.insert_item_limitation( items, k, 3, v )
end
-- all items for which limitations might possibly be needed have been collected;
-- now display them
local formspec = {'size[18,12]'..
'button[0.5,11.1;17,0.8;back;Back]'..
'label[7.0,0.5;List of trade limits]'..
'label[0.5,1.0;If you do not set any limits, your NPC will buy and sell as many '..
'items as his inventory allows.\n'..
'If you set \'Will sell if more than this\', your NPC '..
'will only sell if he will have enough left after the trade,\n'..
'and if you set \'Will buy if less than this\', he will '..
'only buy items as long as he will not end up with more than '..
'this.]'..
'tablecolumns[' ..
'text,align=left;'..
'color;text,align=right;'..
'color;text,align=center;'..
'text,align=right;'..
'color;text,align=center;'..
'text,align=right;'..
'color;text,align=left]'..
'table[0.1,2.3;17.8,8.5;edit_trade_limit;'..
'Description:,'..
'#FFFFFF,NPC has:,'..
'#FFFFFF,Will sell if more than this:,,'..
'#FFFFFF,Will buy if less than this:,,'..
'#EEEEEE,Item string:,'
}
-- the table event selection returns a row index - we need to translate that to our table
local item_list = {}
local row = 2
local selected_row = 1
-- TODO: sort this list in some way? alphabeticly? by limit?
for k,v in pairs( items ) do
table.insert(item_list, k)
if(selected and k == selected) then
selected_row = #item_list + 1
end
local c1 = '#FF0000'
if( v[1] > 0 ) then
c1 = '#BBBBBB'
end
local t1 = 'sell always'
local c2 = '#44EE44'
if( v[2] > 0 ) then
c2 = '#00FF00'
t1 = 'sell if more than:'
end
local t2 = 'buy always'
local c3 = '#EEEE44'
if( v[3] ~= 10000 ) then
c3 = '#FFFF00'
t2 = 'buy if less than:'
end
local desc = ''
if( k =="" ) then
desc = '<free inventory slot>'
k = '<nothing>'
elseif( minetest.registered_items[ k ]
and minetest.registered_items[ k ].description ) then
desc = minetest.registered_items[ k ].description
end
table.insert(formspec,
desc..','..
c1..','.. tostring( v[1] )..','..
c2..','..t1..','..tostring( v[2] )..','..
c3..','..t2..','..tostring( v[3] )..',#EEEEEE,'..k..',')
end
-- we need to store the table somewhere so that we know which limit is edited
yl_speak_up.speak_to[pname].trade_limit_items = items
yl_speak_up.speak_to[pname].trade_limit_item_list = item_list
table.insert(formspec, ";"..selected_row)
return table.concat(formspec, '')
end