diff --git a/exec_apply_effects.lua b/exec_apply_effects.lua index 82d68b6..0e8554e 100644 --- a/exec_apply_effects.lua +++ b/exec_apply_effects.lua @@ -32,6 +32,7 @@ end -- called by yl_speak_up.input_talk(..) +-- and also by yl_speak_up.get_fs_trade_list(..) -- -- This function is called *after* the player has clicked on an option -- and *after* any actions (i.e. trade) have been completed either diff --git a/exec_eval_preconditions.lua b/exec_eval_preconditions.lua index e31ce72..56c2651 100644 --- a/exec_eval_preconditions.lua +++ b/exec_eval_preconditions.lua @@ -403,3 +403,55 @@ yl_speak_up.eval_precondition = function(player, n_id, p, other_options_true_or_ -- fallback - unknown type return false end + + +-- helper function for yl_speak_up.eval_trade_list_preconditions +yl_speak_up.eval_precondition_npc_inv = function(p, inv, inv_name) + if(p.p_type ~= "npc_inv") then + return false + end + -- determine the right inventory + if( p.p_itemstack and p.p_value == "inv_contains") then + return inv:contains_item(inv_name, p.p_itemstack) + elseif(p.p_itemstack and p.p_value == "inv_does_not_contain") then + return not(inv:contains_item(inv_name, p.p_itemstack)) + elseif(p.p_itemstack and p.p_value == "has_room_for") then + return inv:room_for_item(inv_name, p.p_itemstack) + elseif(p.p_value == "inv_is_empty") then + return inv:is_empty(inv_name) + end + return false +end + + +-- cheaper version of eval_all_preconditions for the trade_list (d_trade); +-- returns the ID of the first option where the precondition match or nil; +-- *only* preconditions of the type "npc_inv" are evaluated! +yl_speak_up.eval_trade_list_preconditions = function(player) + local pname = player:get_player_name() + local dialog = yl_speak_up.speak_to[pname].dialog + local n_id = yl_speak_up.speak_to[pname].n_id + if(not(dialog) or not(dialog.n_dialogs) or not(dialog.n_dialogs["d_trade"]) + or not(dialog.n_dialogs["d_trade"].d_options)) then + return + end + local options = dialog.n_dialogs["d_trade"].d_options + local sorted_o_list = yl_speak_up.get_sorted_options(options, "o_sort") + + local inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)}) + local inv_name = "npc_main" + -- search through all options + for i, s_o_id in ipairs(sorted_o_list) do + local prereq = options[s_o_id].o_prerequisites + local all_ok = true + for k, p in pairs(prereq) do + if(not(yl_speak_up.eval_precondition_npc_inv(p, inv, inv_name))) then + all_ok = false + break + end + end + if(all_ok) then + return s_o_id + end + end +end diff --git a/fs_edit_options_dialog.lua b/fs_edit_options_dialog.lua index 97cb5ae..8e7718e 100644 --- a/fs_edit_options_dialog.lua +++ b/fs_edit_options_dialog.lua @@ -243,14 +243,14 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle "dropdown[16.0,-0.4;5.3,0.7;option_autoanswer;".. "by clicking on it,automaticly,randomly;"..tostring(answer_mode+1)..";]" -- (automaticly *by fulfilling the prerequirements*) - if(d_id == "d_got_item") then + if(d_id == "d_got_item" or d_id == "d_trade") then answer_mode = 1 d_option.o_autoanswer = 1 answer_text = "container[0.0,7.3]".. "label[0.2,0.0;..this option will be selected automaticly.]" end - if(answer_mode == 0 and d_id ~= "d_got_item") then + if(answer_mode == 0 and (d_id ~= "d_got_item" and d_id ~= "d_trade")) then answer_text = answer_text.. "label[1.2,0.8;A:]".. "field[1.7,0.3;19.6,0.9;text_option_"..minetest.formspec_escape(o_id)..";;".. @@ -276,10 +276,22 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle "label[1.2,0.8;This option will not be shown but will be selected automaticly if all ".. "prerequirements are fullfilled.]".. "label[1.2,1.4;The remaining options of this dialog will in this case not be evaluated.]".. - "label[1.2,2.0;The NPC will proceed as if this dialog was choosen manually.]".. - "label[1.2,2.6;This is useful for offering a diffrent start dialog depending on the ".. - "player's progress in a quest.]".. - "container_end[]" + "label[1.2,2.0;The NPC will proceed as if this option was choosen manually.]".. + "label[1.2,2.6;" + if(d_id == "d_got_item") then + answer_text = answer_text.. + "Note: This is used here to process items that the player gave to the NPC." + elseif(d_id == "d_trade") then + answer_text = answer_text.. + "Note: This is useful for refilling stock by crafting new things when ".. + "necessary, or for getting\nsupplies from a storage, or for storing ".. + "traded goods in external storage chests." + else + answer_text = answer_text.. + "This is i.e. useful for offering a diffrent start dialog depending on the ".. + "player's progress in a quest." + end + answer_text = answer_text .. "]container_end[]" elseif(answer_mode == 2) then answer_text = answer_text.. "label[1.2,0.8;One option of the dialog - for example this one - will be selected randomly.]".. @@ -384,7 +396,7 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle -- constructs the list_of_effects; may also update target_dialog and target_effect local res = yl_speak_up.get_list_of_effects_and_target_dialog_and_effect(dialog, results, pname, target_dialog, target_effect) - list_of_effects = res.list + local list_of_effects = res.list target_dialog = res.target_dialog target_effect = res.target_effect @@ -615,8 +627,14 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle "(Ef)fects will always be executed when this option here is\n".. "selected.\n".. "Please click on an (Ef)fect in order to edit or delete it!]".. - "container[0.0,3.2]".. + "label[0.4,0.4;The NPC will react to this answer with dialog:]" + if(d_id == "d_trade") then + action_text = action_text.. + "label[13.5,0.4;..by showing his trade list.]".. + "container_end[]" + else + action_text = action_text.. -- allow to change the target dialog via a dropdown menu "dropdown[10.2,0.0;3.0,0.7;d_id_"..minetest.formspec_escape(o_id)..";".. dialog_list..";"..dialog_selected..",]".. @@ -627,7 +645,6 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle -- ..and what the NPC will reply to that answer "tooltip[1.2,0.7;19.6,2.5;This is what the NPC will say next when the player has ".. "selected this answer here.]".. - "label[0.4,0.4;The NPC will react to this answer with dialog:]".. yl_speak_up.show_colored_dialog_text( dialog, -- this is either the "dialog" effect or an empty fallback @@ -639,7 +656,7 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle "", "button_edit_action_success_dialog").. "container_end[]" - + end end action_text = action_text.."container_end[]" diff --git a/fs_talkdialog.lua b/fs_talkdialog.lua index f7692a0..63ea9a6 100644 --- a/fs_talkdialog.lua +++ b/fs_talkdialog.lua @@ -413,6 +413,39 @@ yl_speak_up.get_fs_talkdialog_main_text_in_edit_mode = function( "You can also work with an alternate text here (as is done in the ".. "default setup when adding a new option here).".. "\n]") + -- static help text instead of text input field for d_trade + elseif(c_d_id == "d_trade") then + table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;".. + "Note:\nThis is a special dialog.".. + "It will be called when the player clicks on ".. + "Let's trade!.".. + "\nSome of the things listed below will be added automaticly when you add a ".. + "new option to this dialog. In most cases you may just have to edit the ".. + "precondition so that the right item(stack) is beeing ".. + "searched for, and you need to add suitable effects. The ones added ".. + "automaticly are just an example.".. + "\nNote that once the NPC found a matching precondition, it will execute the ".. + "relevant effects and present the player the trade list. Any further options ".. + "that might also fit will not be executed this time. Only one option ".. + "(or none) will be selected each time.".. + "\nThis is how it works in detail:".. + "\nEach option you add here ought to deal with one item(stack) that ".. + "the NPC might or might not have in its inventory, ".. + "i.e. default:stick 4. ".. + "Each option needs to be selected automaticly and ought to contain:".. + "\n* at least one precondition regarding ".. + "the inventory of the NPC ".. + "where you define which item(stack) is relevant for this option ".. + "(you can add multiple such preconditions for each option)".. + "\n* at least one effect regarding what the NPC shall do if the ".. + "precondition matches. In most cases, NPC crafts something, ".. + "put item from the NPC's inventory into a chest etc. or ".. + "take item from a chest etc. and put it into the NPC's inventory ".. + "will be what you are looking for. More than one effect is possible.".. + "\n* In this particular case, no target dialog needs to be selected. After ".. + "executing the effect(s), the trade list view will be shown to the ".. + "player.".. + "\n]") elseif(active_dialog and active_dialog.d_text) then table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;".. minetest.formspec_escape(active_dialog.d_text or "?").. @@ -507,6 +540,7 @@ yl_speak_up.get_fs_talkdialog_line_in_edit_mode = function( if v.r_type == "dialog" and (dialog.n_dialogs[v.r_value] ~= nil or v.r_value == "d_end" + or v.r_value == "d_trade" or v.r_value == "d_got_item") then -- there may be more than one in the data structure target_dialog = v.r_value @@ -735,6 +769,11 @@ yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id, alternate_text, rec yl_speak_up.trade[pname] = nil yl_speak_up.speak_to[pname].trade_id = nil + -- add a d_trade dialog if necessary + if(dialog and dialog.trades and dialog.n_dialogs and not(dialog.n_dialogs["d_trade"])) then + yl_speak_up.add_new_dialog(dialog, pname, "trade", "[no text]") + end + --[[ If we have an explicit call for a certain d_id, we grab it from parameters. If not, we grab in from context. When neither are present, we grab it from d_sort diff --git a/fs_trade_list.lua b/fs_trade_list.lua index d960955..3d1f5f2 100644 --- a/fs_trade_list.lua +++ b/fs_trade_list.lua @@ -124,6 +124,17 @@ yl_speak_up.get_fs_trade_list = function(player, show_dialog_option_trades) "label[0.2,0.5;Ups! This NPC lacks ID or name.]".. "button_exit[2,1.5;1,0.9;exit;Exit]" end + -- the trade list is a bit special - we need to evaluate the preconditions of the + -- options of the d_talk dialog and execute corresponding effects; this can be used + -- to refill stock from chests or craft new items to increase stock + local do_o_id = yl_speak_up.eval_trade_list_preconditions(player) + if(do_o_id) then + local effects = dialog.n_dialogs["d_trade"].d_options[do_o_id].o_results + -- the return value is of no intrest here - we won't be showing another dialog, + -- and alternate_text isn't relevant either; we just do the effects and then show + -- the trade list + local res = yl_speak_up.execute_all_relevant_effects(player, effects, do_o_id, true) + end if(not(yl_speak_up.may_edit_npc(player, n_id))) then -- do not show trades attached to dialog options for players who cannot edit the NPC diff --git a/functions.lua b/functions.lua index de653f7..11b8c60 100644 --- a/functions.lua +++ b/functions.lua @@ -302,6 +302,11 @@ yl_speak_up.add_new_option = function(dialog, pname, next_id, d_id, option_text, target_dialog = yl_speak_up.get_start_dialog_id(dialog) -- ...and this option needs to be selected automaticly dialog.n_dialogs[d_id].d_options[future_o_id].o_autoanswer = 1 + elseif(d_id == "d_trade") then + -- we really don't want to go to another dialog from here + target_dialog = "d_trade" + -- ...and this option needs to be selected automaticly + dialog.n_dialogs[d_id].d_options[future_o_id].o_autoanswer = 1 end local future_r_id = nil -- create a fitting dialog result automaticly if possible: @@ -342,6 +347,25 @@ yl_speak_up.add_new_option = function(dialog, pname, next_id, d_id, option_text, r_id = future_r_id, r_type = "deal_with_offered_item", r_value = "take_all"} + + -- the trade dialog is equally special + elseif(d_id == "d_trade") then + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites = {} + -- this is just an example + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites["p_1"] = { + p_id = "p_1", + p_type = "npc_inv", + p_value = "inv_does_not_contain", + p_inv_list_name = "npc_main", + p_itemstack = "default:stick "..tostring(100-next_id)} + future_r_id = yl_speak_up.add_new_result(dialog, d_id, future_o_id) + -- example craft + dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id] = { + r_id = future_r_id, + r_type = "craft", + r_value = "default:stick 4", + o_sort = "1", + r_craft_grid = {"default:wood", "", "", "", "", "", "", "", ""}} end return future_o_id end