added limit handling to trade formspec

This commit is contained in:
Sokomine 2022-09-05 07:00:55 +02:00
parent e7d82c1d52
commit 0b26af1287
5 changed files with 349 additions and 1 deletions

88
fs_edit_trade_limit.lua Normal file
View File

@ -0,0 +1,88 @@
-- add or edit a trade limit
yl_speak_up.input_edit_trade_limit = function(player, formname, fields)
-- store the new limits?
if(fields and fields["store_limit"]) then
if(not(fields["item_name"])
or fields["item_name"] == ""
or not(minetest.registered_items[fields["item_name"]])) then
-- TODO: show error message and back
return
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
-- make sure all necessary entries in the trades table exist
yl_speak_up.setup_trade_limits(dialog)
local anz = tonumber(fields['SellIfMoreThan'] or "0")
if( anz and anz > 0 and anz < 10000 ) then
dialog.trades.limits.sell_if_more[ fields["item_name"] ] = anz;
yl_speak_up.log_change(pname, n_id, "sell_if_more set to "..tostring(anz)..
" for "..tostring(fields["item_name"]))
end
anz = tonumber(fields['BuyIfLessThan'] or "0")
if( anz and anz > 0 and anz < 10000 ) then
dialog.trades.limits.buy_if_less[ fields["item_name"] ] = anz;
yl_speak_up.log_change(pname, n_id, "buy_if_less set to "..tostring(anz)..
" for "..tostring(fields["item_name"]))
end
-- save these values
yl_speak_up.save_dialog(n_id, dialog)
yl_speak_up.show_fs(player, "trade_limit", {selected = fields.item_name})
return
end
-- TODO: implement delete button
-- back to the normal trade list
yl_speak_up.show_fs(player, "trade_limit", {selected = fields.item_name})
end
-- edit a trade limit or add a new one
yl_speak_up.get_fs_edit_trade_limit = function(player, selected_row)
local pname = player:get_player_name()
local items = yl_speak_up.speak_to[pname].trade_limit_items
local item_list = yl_speak_up.speak_to[pname].trade_limit_item_list
if(not(selected_row) or selected_row < 1
or not(items) or not(item_list)
or selected_row > #item_list + 1) then
-- TODO show more helpful error message
return "Error in get_fs_edit_trade_limit."
end
local selected = item_list[ selected_row - 1]
local item_data = items[ selected ]
-- 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
local def = minetest.registered_items[ selected ]
if(not(def)) then
def = {description = '- unknown item -'}
end
local formspec = {'size[8,7]',
'item_image[-0.25,2.5;2.0,2.0;', selected, ']',
'label[1.0,0.0;Set limits for buy and sell]',
'label[1.5,1.0;Description:]',
'label[4.0,1.0;', (def.description or '?'), ']',
'label[1.5,2.0;Item name:]',
--'label[3.5,1.0;', tostring( selected ), ']',
'field[4.0,2.0;4,1;item_name;;', tostring( selected ), ']',
'label[1.5,3.0;In stock:]',
'label[4.0,3.0;', tostring( item_data[1] ), ']',
'label[1.5,4.0;Sell if more than]',
'field[4.0,4.0;1.2,1.0;SellIfMoreThan;;', tostring( item_data[2] ), ']',
'label[5.0,4.0;will remain in stock.]',
'label[1.5,5.0;Buy if less than]',
'field[4.0,5.0;1.2,1.0;BuyIfLessThan;;', tostring( item_data[3] ), ']',
'label[5.0,5.0;will end up in stock.]',
'button[1.5,6.0;2,1.0;store_limit;Save]',
'button[4.5,6.0;2,1.0;back_to_limit_list;Back]'
-- TODO: delete button
}
return table.concat(formspec, '')
end

225
fs_trade_limit.lua Normal file
View File

@ -0,0 +1,225 @@
-----------------------------------------------------------------------------
-- 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

View File

@ -82,6 +82,9 @@ dofile(modpath .. "fs_initial_config.lua")
dofile(modpath .. "fs_player_offers_item.lua")
-- inventory management, trading and handling of quest items:
dofile(modpath .. "inventory.lua")
-- limit how much the NPC shall buy and sell
dofile(modpath .. "fs_trade_limit.lua")
dofile(modpath .. "fs_edit_trade_limit.lua")
-- trade one item(stack) against one other item(stack)
dofile(modpath .. "trade_simple.lua")
-- easily accessible list of all trades the NPC offers

View File

@ -49,6 +49,12 @@ minetest.register_on_player_receive_fields( function(player, formname, fields)
elseif formname == "yl_speak_up:add_trade_simple" then
yl_speak_up.input_add_trade_simple(player, formname, fields)
return true
elseif formname == "yl_speak_up:trade_limit" then
yl_speak_up.input_trade_limit(player, formname, fields)
return true
elseif formname == "yl_speak_up:edit_trade_limit" then
yl_speak_up.input_edit_trade_limit(player, formname, fields)
return true
-- handled in fs_initial_config.lua
elseif formname == "yl_speak_up:initial_config" then
yl_speak_up.input_fs_initial_config(player, formname, fields)
@ -298,6 +304,20 @@ yl_speak_up.show_fs = function(player, fs_name, param)
yl_speak_up.show_fs_ver(pname, "yl_speak_up:add_trade_simple",
yl_speak_up.get_fs_add_trade_simple(player, param), 1)
elseif(fs_name == "trade_limit") then
if(not(param)) then
param = {}
end
yl_speak_up.show_fs_ver(pname, "yl_speak_up:trade_limit",
yl_speak_up.get_fs_trade_limit(player, param.selected))
elseif(fs_name == "edit_trade_limit") then
if(not(param)) then
param = {}
end
yl_speak_up.show_fs_ver(pname, "yl_speak_up:edit_trade_limit",
yl_speak_up.get_fs_edit_trade_limit(player, param.selected_row), 1)
elseif(fs_name == "initial_config") then
if(not(param)) then
param = {}

View File

@ -19,6 +19,12 @@ yl_speak_up.input_trade_list = function(player, formname, fields)
return
end
if(fields.trade_limit) then
-- show a list of how much the NPC can buy and sell
yl_speak_up.show_fs(player, "trade_limit")
return
end
-- toggle between view of dialog option trades and trade list trades
if(fields.show_dialog_option_trades
or fields.show_trade_list) then
@ -186,7 +192,7 @@ yl_speak_up.get_fs_trade_list = function(player, show_dialog_option_trades)
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"show_trade_list",
text, text,
true, nil, nil, pname_for_old_fs)
true, nil, nil, pname_for_old_fs)
end
-- button "add trade" for those who can edit the NPC (entering edit mode is not required)
local text = "Add a new trade."
@ -194,6 +200,12 @@ yl_speak_up.get_fs_trade_list = function(player, show_dialog_option_trades)
"trade_list_add_trade",
text, text,
true, nil, nil, pname_for_old_fs)
-- show a list of how much the NPC will buy and sell
text = "Do not buy or sell more than what I will tell you."
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"trade_limit",
text, text,
true, nil, nil, pname_for_old_fs)
end
local text = "That was all. Let's continue talking."