-- This file contains what is necessary to execute an action. -- monitor changes to the npc_gives and npc_wants slots (in particular when editing actions) -- how: can be "put" or "take" yl_speak_up.action_inv_changed = function(inv, listname, index, stack, player, how) if(not(player)) then return end local pname = player:get_player_name() local n_id = yl_speak_up.speak_to[pname].n_id -- if not in edit mode: the player may just be normally interacting with the NPC; -- nothing to do for us here (wait for the player to click on "save") if(not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id) then return end -- is the player in the process of editing an action of the npc_gives/npc_wants type? local target_fs = "edit_actions" local data = yl_speak_up.speak_to[pname][ "tmp_action" ] if(not(data) or (data.what ~= 4 and data.what ~= 5)) then -- we are editing an action if(data) then return end -- it might be a precondition data = yl_speak_up.speak_to[pname][ "tmp_prereq" ] if(not(data) or (data.what ~= 8)) then return end target_fs = "edit_preconditions" end -- "The NPC gives something to the player (i.e. a quest item).", -- 4 -- "The player is expected to give something to the NPC (i.e. a quest item).", -- 5 if(how == "put") then data.item_node_name = stack:get_name().." "..stack:get_count() local meta = stack:get_meta() if(meta and meta:get_string("description")) then -- try to reconstruct $PLAYER_NAME$ (may not always work) local item_was_for = meta:get_string("yl_speak_up:quest_item_for") local new_desc = meta:get_string("description") if(item_was_for and item_was_for ~= "") then new_desc = string.gsub(new_desc, item_was_for, "$PLAYER_NAME$") end data.item_desc = new_desc end if(meta and meta:get_string("yl_speak_up:quest_id")) then data.item_quest_id = meta:get_string("yl_speak_up:quest_id") end elseif(how == "take" and data.what == 4) then data.item_desc = "- no item set -" data.item_node_name = "" elseif(how == "take" and data.what == 5) then data.item_desc = "- no item set -" data.item_node_name = "" end -- show the updated formspec to the player yl_speak_up.show_fs(player, target_fs, nil) -- no need to check anything more here; the real checks need to be done -- when the player presses the save/store/execute button end -- actions - in contrast to preconditions and effects - may take time -- because the player usually gets presented a formspec and needs to -- react to that; thus, we can't just execute all actions simultaneously yl_speak_up.execute_next_action = function(player, a_id, result_of_a_id) local pname = player:get_player_name() 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 local dialog = yl_speak_up.speak_to[pname].dialog yl_speak_up.debug_msg(player, n_id, o_id, "Last action: "..tostring(a_id).." returned ".. tostring(result_of_a_id)..".") local actions = {} local effects = {} local sorted_key_list = {} if(dialog and dialog.n_dialogs and dialog.n_dialogs[d_id] and dialog.n_dialogs[d_id].d_options and dialog.n_dialogs[d_id].d_options[o_id]) then -- get the actual actions actions = dialog.n_dialogs[d_id].d_options[o_id].actions -- needed later on when all actions are executed effects = dialog.n_dialogs[d_id].d_options[o_id].o_results end if(actions) then -- sort the actions so that we can execute them always in the -- same order sorted_key_list = yl_speak_up.sort_keys(actions) local nr = 0 if(not(a_id)) then -- check if the action(s) can be executed local time_now = yl_speak_up.get_time_in_seconds() -- is there a limiton how many failed attempts there can be per time? local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) local timer_data = yl_speak_up.get_variable_metadata( timer_name, "parameter", true) if(timer_data and timer_data["max_attempts"] and tonumber(timer_data["max_attempts"]) > 0 and timer_data["duration"] and tonumber(timer_data["duration"]) > 0) then local new_times = "" local times = yl_speak_up.get_quest_variable_value(pname, timer_name) local parts = string.split(times or "", " ") local count = 0 for i, p in ipairs(parts) do if(p and tonumber(p) and (tonumber(p) + tonumber(timer_data["duration"])>time_now)) then new_times = new_times.." "..p count = count + 1 end end -- all timers are expired if(count == 0) then yl_speak_up.set_quest_variable_value(pname, timer_name, nil) -- some timers are expired elseif(new_times ~= times) then yl_speak_up.set_quest_variable_value(pname, timer_name, new_times) end if(count >= tonumber(timer_data["max_attempts"])) then -- show the same dialog again, but with the failure message yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = d_id, alternate_text = timer_data[ "alternate_text" ] or yl_speak_up.standard_text_if_action_failed_too_often}) return end end -- is there a limiton how fast the action may be repeated again? timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id) timer_data = yl_speak_up.get_variable_metadata(timer_name, "parameter", true) if(timer_data and timer_data["duration"] and tonumber(timer_data["duration"]) > 0) then local last_time = yl_speak_up.get_quest_variable_value(pname, timer_name) if(last_time and tonumber(last_time) and tonumber(last_time) + tonumber(timer_data["duration"]) > time_now) then -- show the same dialog again, but with the failure message yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = d_id, alternate_text = timer_data[ "alternate_text" ] or yl_speak_up.standard_text_if_action_repeated_too_soon}) return else -- the timer has expired yl_speak_up.set_quest_variable_value(pname, timer_name, nil) end end else -- if(a_id) then nr = table.indexof(sorted_key_list, a_id) -- did the player go back? if(nr > -1 and result_of_a_id == nil) then -- set the current action to nil yl_speak_up.speak_to[pname].a_id = nil -- no option of the new dialog has been selected yet yl_speak_up.speak_to[pname].o_id = nil -- show the new dialog yl_speak_up.debug_msg(player, n_id, o_id, "Action ".. tostring(a_id).." aborted. Switching back to dialog ".. tostring(d_id)..".") yl_speak_up.speak_to[pname].o_id = nil yl_speak_up.speak_to[pname].a_id = nil yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = d_id}) return -- did the action fail? elseif(nr > -1 and not(result_of_a_id)) then -- is there a limiton how many failed attempts there can be per time? local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) local timer_data = yl_speak_up.get_variable_metadata( timer_name, "parameter", true) -- store that (another?) attempt to do the action failed if(timer_data and timer_data["max_attempts"] and tonumber(timer_data["max_attempts"])> 0 and timer_data["duration"] and tonumber(timer_data["duration"])> 0) then local times = yl_speak_up.get_quest_variable_value(pname, timer_name) if(not(times)) then times = "" end -- variables are stored as strings, not as lists yl_speak_up.set_quest_variable_value(pname, timer_name, times.." "..yl_speak_up.get_time_in_seconds()) end local this_action = actions[ sorted_key_list[ nr ]] -- if there is an on_failure target dialog: go there -- set the current action to nil yl_speak_up.speak_to[pname].a_id = nil -- no option of the new dialog has been selected yet yl_speak_up.speak_to[pname].o_id = nil -- show the new dialog yl_speak_up.debug_msg(player, n_id, o_id, "Action ".. tostring(a_id).." failed. Switching to dialog ".. tostring(this_action.a_on_failure)..".") yl_speak_up.speak_to[pname].d_id = this_action.a_on_failure yl_speak_up.speak_to[pname].o_id = nil yl_speak_up.speak_to[pname].a_id = nil yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = this_action.a_on_failure, alternate_text = this_action.alternate_text}) return end end -- get the next entry if(nr > -1 and nr < #sorted_key_list and sorted_key_list[nr + 1]) then local next_action = actions[ sorted_key_list[ nr + 1 ]] -- store which action we are currently executing yl_speak_up.speak_to[pname].a_id = next_action.a_id -- execute the next action yl_speak_up.debug_msg(player, n_id, o_id, "Executing next action ".. tostring(next_action.a_id)..".") yl_speak_up.execute_action(player, n_id, o_id, next_action) -- the player needs time to react return end end -- when all actions are executed: -- is there a limiton how fast the action may be repeated again? local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id) local timer_data = yl_speak_up.get_variable_metadata(timer_name, "parameter", true) -- store that the action was executed successfully if(timer_data and timer_data["duration"] and tonumber(timer_data["duration"]) > 0) then yl_speak_up.set_quest_variable_value(pname, timer_name, yl_speak_up.get_time_in_seconds()) end -- set the current action to nil yl_speak_up.speak_to[pname].a_id = nil yl_speak_up.debug_msg(player, n_id, o_id, "All actions have been executed successfully. ".. "Doing effects/results now.") -- execute all effects/results local res = yl_speak_up.execute_all_relevant_effects(player, effects, o_id, true) local target_dialog = res.next_dialog yl_speak_up.speak_to[pname].o_id = nil yl_speak_up.speak_to[pname].a_id = nil if(not(target_dialog) or target_dialog == "" or not(dialog.n_dialogs[target_dialog])) then target_dialog = d_id end -- the function above returns a target dialog; show that to the player yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = target_dialog, alternate_text = res.alternate_text}) end yl_speak_up.execute_action = function(player, n_id, o_id, a) if(not(a.a_type) or a.a_type == "" or a.a_type == "none") then -- no action - nothing to do return true elseif(a.a_type == "trade") then yl_speak_up.show_fs(player, "trade_simple", a.a_value) return elseif(a.a_type == "npc_gives") then yl_speak_up.show_fs(player, "action_npc_gives", a.a_value) return elseif(a.a_type == "npc_wants") then yl_speak_up.show_fs(player, "action_npc_wants", a.a_value) return elseif(a.a_type == "text_input") then -- start with an empty answer yl_speak_up.show_fs(player, "action_text_input", "") return elseif(a.a_type == "custom") then yl_speak_up.show_fs(player, "action_custom", a.a_value) return end -- fallback: unkown type return false end -- helper function; -- returns the action the player is currently faced with (or nil if none) yl_speak_up.get_action_by_player = 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 local d_id = yl_speak_up.speak_to[pname].d_id local o_id = yl_speak_up.speak_to[pname].o_id local a_id = yl_speak_up.speak_to[pname].a_id if(not(dialog) or not(d_id) or not(o_id) or not(a_id) or not(dialog.n_dialogs) or not(dialog.n_dialogs[d_id]) or not(dialog.n_dialogs[d_id].d_options) or not(dialog.n_dialogs[d_id].d_options[o_id]) or not(dialog.n_dialogs[d_id].d_options[o_id].actions) or not(dialog.n_dialogs[d_id].d_options[o_id].actions[a_id])) then return nil end return dialog.n_dialogs[d_id].d_options[o_id].actions[a_id] end -- Create the quest item by taking a raw item (i.e. a general piece of paper) out -- of the NPC's inventory, applying a description (if given) and quest id (if -- given); place the quest item in the trade inv of the player in the npc_gives slot. -- The npc_gives inv is managed mostly by the NPC, except when in edit mode. We can -- just overwrite anything old in there. -- Returns false if the creation of the quest item wasn't possible (i.e. the -- NPC had no paper left). yl_speak_up.action_quest_item_prepare = function(player) -- which action are we talking about? local a = yl_speak_up.get_action_by_player(player) if(not(a) or not(a.a_id) or not(a.a_value)) then return false end local pname = player:get_player_name() local n_id = yl_speak_up.speak_to[pname].n_id -- what shall the NPC give? local stack = ItemStack(a.a_value) -- get the inventory of the NPC local npc_inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)}) -- does the NPC have the item we are looking for? if(not(npc_inv:contains_item("npc_main", stack))) then local o_id = yl_speak_up.speak_to[pname].o_id yl_speak_up.debug_msg(player, n_id, o_id, "Action "..tostring(a.a_id)..": NPC ran out of ".. tostring(a.a_value)..".") -- just go back; the player didn't do anything wrong return nil end -- get the items from the NPCs inventory local new_stack = npc_inv:remove_item("npc_main", stack) local meta = new_stack:get_meta() -- if given: set the item stack description if(a.a_item_desc and a.a_item_desc ~= "") then local dialog = yl_speak_up.speak_to[pname].dialog -- replace $PLAYER_NAME$ etc. in quest item description meta:set_string("description", yl_speak_up.replace_vars_in_text(a.a_item_desc, dialog, pname)) end if(a.a_item_quest_id and a.a_item_quest_id ~= "") then -- which player got this quest item? meta:set_string("yl_speak_up:quest_item_for", pname) -- include the NPC id so that we know which NPC gave it meta:set_string("yl_speak_up:quest_item_from", tostring(n_id)) -- extend quest_id by NPC id so that it becomes more uniq meta:set_string("yl_speak_up:quest_id", tostring(n_id).." "..tostring(a.a_item_quest_id)) end -- put the stack in the npc_gives-slot of the trade inventory of the player -- (as that slot is managed by the NPC alone we don't have to worry about -- anything else in the slot) local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) -- actually put the stack in there trade_inv:set_stack("npc_gives", 1, new_stack) return true end -- check if the item in the npc_gives slot is the one the NPC wants yl_speak_up.action_quest_item_check = function(player) -- which action are we talking about? local a = yl_speak_up.get_action_by_player(player) if(not(a) or not(a.a_id) or not(a.a_value)) then return false end local pname = player:get_player_name() local n_id = yl_speak_up.speak_to[pname].n_id local o_id = yl_speak_up.speak_to[pname].o_id -- get the item that needs to be checked local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) local stack = trade_inv:get_stack("npc_wants", 1) -- nothing there? if(stack:is_empty()) then yl_speak_up.debug_msg(player, n_id, o_id, "Action "..tostring(a.a_id)..": No item found.") return false end local cmp = tostring(stack:get_name()).." "..(stack:get_count()) -- wrong item or wrong amount? if(cmp ~= a.a_value) then yl_speak_up.debug_msg(player, n_id, o_id, "Action "..tostring(a.a_id).. ": Wrong item given. Got: "..stack:to_string().. " Expected: "..tostring(a.a_value)..".") return false end local meta = stack:get_meta() -- the description is not checked; just the quest id (if given) if(a.a_item_quest_id and a.a_item_quest_id ~= "") then -- we don't check here if the item was given by the right NPC; -- only the quest id has to fit if(meta:get_string("yl_speak_up:quest_id") ~= a.a_item_quest_id) then yl_speak_up.debug_msg(player, n_id, o_id, "Action "..tostring(a.a_id).. ": Wrong quest item (wrong ID).") return false end -- was this quest item given to another player? if(meta:get_string("yl_speak_up:quest_item_for") ~= pname) then yl_speak_up.debug_msg(player, n_id, o_id, "Action "..tostring(a.a_id).. ": Quest item was given to ".. tostring(meta:get_string("yl_speak_up:quest_item_for")).. ", but "..tostring(pname).." gave it.") return false end end yl_speak_up.debug_msg(player, n_id, o_id, "Action "..tostring(a.a_id).. ": Quest item checked ok.") return true end -- strip the quest information from the item and give it back to the NPC; -- returns the modified stack (but also places it in the NPC's inventory) yl_speak_up.action_quest_item_take_back = function(player) -- which action are we talking about? local a = yl_speak_up.get_action_by_player(player) if(not(a) or not(a.a_id) or not(a.a_value)) then return false end local pname = player:get_player_name() local n_id = yl_speak_up.speak_to[pname].n_id -- get the item that the NPC shall take back (or accept in npc_wants) local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) local stack = trade_inv:get_stack("npc_wants", 1) -- if it was the wrong item: if(not(yl_speak_up.action_quest_item_check(player))) then local player_inv = player:get_inventory() -- give the item back to the player local remaining = player_inv:add_item("main", stack) -- very unlikely - but in case the item did not fit back into the player's inv: if(remaining and not(remaining:is_empty())) then local p = player:get_pos() -- throw it at the player minetest.add_item({x=p.x, y=p.y+1, z=p.z}, stack) end -- remove it from the trade inv slot trade_inv:set_stack("npc_wants", 1, ItemStack()) return false end -- we already checked that it is the correct item local meta = stack:get_meta() -- if given: set the item stack description if(a.a_item_desc and a.a_item_desc ~= "") then meta:set_string("description", "") end -- delete all the special IDs that where added before if(a.a_item_quest_id and a.a_item_quest_id ~= "") then meta:set_string("yl_speak_up:quest_item_for", "") meta:set_string("yl_speak_up:quest_item_from", "") meta:set_string("yl_speak_up:quest_id", "") end local npc_inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)}) -- Has the NPC room enough for the item? -- If the NPC doesn't have room, the item will be destroyed in the next step by setting -- npc_wants to an empty stack. While this may lead to some item loss, it is more important -- that the quest item was properly accepted (and discarded of) rather than worrying about -- where to put it or even giving it back and letting the quest fail. if(npc_inv:room_for_item("npc_main", stack)) then npc_inv:add_item("npc_main", stack) -- save the inventory of the NPC yl_speak_up.save_npc_inventory(n_id) end -- the NPC has accepted the item trade_inv:set_stack("npc_wants", 1, ItemStack()) return true end -- show the diffrent action-related formspecs and handle input to them -- (Note: trade is handled in trade_simple.lua) yl_speak_up.input_fs_action_npc_gives = function(player, formname, fields) -- back from error_msg? then show the formspec again if(fields.back_from_error_msg) then -- do not create a new item yl_speak_up.show_fs(player, "action_npc_gives", nil) return end local pname = player:get_player_name() local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) local a_id = yl_speak_up.speak_to[pname].a_id if(fields.npc_does_not_have_item) then -- the NPC can't supply the item - abort the action yl_speak_up.execute_next_action(player, a_id, nil) return end -- is the npc_gives inv empty? then all went as expected. -- (it does not really matter which button the player pressed in this case) if(trade_inv:is_empty("npc_gives")) then -- the NPC has given the item to the player; save the NPCs inventory local n_id = yl_speak_up.speak_to[pname].n_id yl_speak_up.save_npc_inventory(n_id) -- the action was a success; the NPC managed to give the item to the player yl_speak_up.execute_next_action(player, a_id, true) return end -- the npc_gives slot does not accept input - so we don't have to check for any misplaced items -- but if the player aborts, give the item back to the NPC if(fields.back_to_talk) then -- strip the quest item info from the stack (so that it may stack again) -- and give that (hopefully) stackable stack back to the NPC yl_speak_up.action_quest_item_take_back(player) -- the action failed yl_speak_up.execute_next_action(player, a_id, nil) return end -- else show a message to the player that he ought to take the item yl_speak_up.show_fs(player, "msg", { input_to = "yl_speak_up:action_npc_gives", formspec = "size[7,1.5]".. "label[0.2,-0.2;".. "Please take the offered item and click on \"Done\"!\n".. "If you can't take it, click on \"Back to talk\".]".. "button[2,1.0;1.5,0.9;back_from_error_msg;Back]"}) end yl_speak_up.get_fs_action_npc_gives = function(player, param) -- called for the first time; create the item the NPC wants to give if(param) then if(not(yl_speak_up.action_quest_item_prepare(player))) then local pname = player:get_player_name() local dialog = yl_speak_up.speak_to[pname].dialog -- it's not the fault of the player that the NPC doesn't have the item; -- so tell him that (the action will still fail) return "size[7,2.0]".. "label[0.2,-0.2;".. minetest.formspec_escape(dialog.n_npc or "- ? -").. " is very sorry:\n".. "The item intended for you is currently unavailable.\n".. "Please come back later!]".. "button[2,1.5;1.5,0.9;npc_does_not_have_item;Back]" end end local pname = player:get_player_name() local dialog = yl_speak_up.speak_to[pname].dialog return "size[8.5,8]".. "list[current_player;main;0.2,3.85;8,1;]".. "list[current_player;main;0.2,5.08;8,3;8]".. "button[0.2,0.0;2.0,0.9;back_to_talk;Back to talk]".. "button[4.75,1.6;1.5,0.9;finished_action;Done]".. "tooltip[back_to_talk;Click here if you don't want to (or can't)\n".. "take the offered item.]".. "tooltip[finished_action;Click here once you have taken the item and\n".. "stored it in your inventory.]".. "label[1.5,0.7;"..minetest.formspec_escape(dialog.n_npc or "- ? -").. " offers to you:]".. -- unlike the npc_gives slot - which is used for setting up the NPC - the -- npc_gives slot does not allow putting something in "list[detached:yl_speak_up_player_"..pname..";npc_gives;3.25,1.5;1,1;]" .. "label[1.5,2.7;Take the offered item and click on \"Done\" to proceed.]" end yl_speak_up.input_fs_action_npc_wants = function(player, formname, fields) -- back from error_msg? then show the formspec again if(fields.back_from_error_msg) then yl_speak_up.show_fs(player, "action_npc_wants", nil) return end local pname = player:get_player_name() local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) local a_id = yl_speak_up.speak_to[pname].a_id -- is the npc_wants inv empty and the player pressed the back to talk button? then the action failed. if(trade_inv:is_empty("npc_wants") and fields.back_to_talk) then -- the action was aborted yl_speak_up.execute_next_action(player, a_id, nil) return end -- the player tried to give something; check if it is the right thing if(not(trade_inv:is_empty("npc_wants"))) then local stack = trade_inv:get_stack("npc_wants", 1) -- check if it really is the item the NPC wanted; let the NPC take it local is_correct_item = yl_speak_up.action_quest_item_take_back(player) -- the action may have been a success or failure yl_speak_up.execute_next_action(player, a_id, is_correct_item) return end -- else show a message to the player yl_speak_up.show_fs(player, "msg", { input_to = "yl_speak_up:action_npc_wants", formspec = "size[7,1.5]".. "label[0.2,-0.2;".. "Please insert the item for the npc and click on \"Done\"!\n".. "If you don't have what he wants, click on \"Back to talk\".]".. "button[2,1.0;1.5,0.9;back_from_error_msg;Back]"}) end yl_speak_up.get_fs_action_npc_wants = function(player, param) local pname = player:get_player_name() local dialog = yl_speak_up.speak_to[pname].dialog return "size[8.5,8]".. "list[current_player;main;0.2,3.85;8,1;]".. "list[current_player;main;0.2,5.08;8,3;8]".. "button[0.2,0.0;2.0,0.9;back_to_talk;Back to talk]".. "button[4.75,1.6;1.5,0.9;finished_action;Done]".. "tooltip[back_to_talk;Click here if you don't know what item the\n".. "NPC wants or don't have the desired item.]".. "tooltip[finished_action;Click here once you have placed the item in\n".. "the waiting slot.]".. "label[1.5,0.7;"..minetest.formspec_escape(dialog.n_npc or "- ? -").. " expects something from you:]".. "list[detached:yl_speak_up_player_"..pname..";npc_wants;3.25,1.5;1,1;]" .. "label[1.5,2.7;Insert the right item and click on \"Done\" to proceed.]" end yl_speak_up.input_fs_action_text_input = function(player, formname, fields) -- back from error_msg? then show the formspec again if(fields.back_from_error_msg) then -- the error message is only shown if the input was empty yl_speak_up.show_fs(player, "action_text_input", "") return end local pname = player:get_player_name() local a_id = yl_speak_up.speak_to[pname].a_id local a = yl_speak_up.get_action_by_player(player) if(fields.back_to_talk) then -- the action was aborted yl_speak_up.execute_next_action(player, a_id, nil) return end if(fields.finished_action and fields.quest_answer and fields.quest_answer ~= "") then -- is the answer correct? -- strip leading and tailing blanks local success = not(not(fields.quest_answer and a.a_value and fields.quest_answer:trim() == a.a_value:trim())) -- the action was a either a success or failure yl_speak_up.execute_next_action(player, a_id, success) return end -- else show a message to the player yl_speak_up.show_fs(player, "msg", { input_to = "yl_speak_up:action_text_input", formspec = "size[7,1.5]".. "label[0.2,-0.2;".. "Please answer the question and click on \"Send answer\"!\n".. "If you don't know the answer, click on \"Back to talk\".]".. "button[2,1.0;1.5,0.9;back_from_error_msg;Back]"}) end yl_speak_up.get_fs_action_text_input = function(player, param) local pname = player:get_player_name() local dialog = yl_speak_up.speak_to[pname].dialog local a = yl_speak_up.get_action_by_player(player) if(not(a)) then return "" end return "size[12.0,4.5]".. "button[0.2,0.0;2.0,0.9;back_to_talk;Back to talk]".. "button[2.0,3.7;3.0,0.9;finished_action;Send answer]".. "tooltip[back_to_talk;Click here if you don't know the answer.]".. "tooltip[finished_action;Click here once you've entered the answer.]".. "label[0.2,1.2;"..minetest.formspec_escape(a.a_question or "Your answer:").."]".. "label[0.2,1.9;Answer:]".. "field[1.6,2.2;10.0,0.6;quest_answer;;"..tostring(param or "").."]".. "label[0.2,2.8;"..minetest.formspec_escape( "["..(dialog.n_npc or "- ? -").." looks expectantly at you.]").."]" end