mirror of
https://gitea.your-land.de/Sokomine/yl_speak_up.git
synced 2025-06-17 14:18:05 +02:00
put exec_actions.lua into extra file
This commit is contained in:
parent
9a89c678f0
commit
0c2f9f0b49
657
exec_actions.lua
Normal file
657
exec_actions.lua
Normal file
@ -0,0 +1,657 @@
|
||||
-- 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
|
||||
if(this_action.a_on_failure) then
|
||||
-- go back to the same dialog
|
||||
if(not(dialog.n_dialogs[this_action.a_on_failure])) then
|
||||
this_action.a_on_failure = d_id
|
||||
end
|
||||
-- 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
|
||||
else
|
||||
yl_speak_up.debug_msg(player, n_id, o_id, "Action "..
|
||||
tostring(a_id).." failed, but no a_on_failure target "..
|
||||
"dialog defined. Continuing execution.")
|
||||
end
|
||||
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
|
@ -1,4 +1,5 @@
|
||||
|
||||
-- This file contains what is necessary to add/edit an action.
|
||||
--
|
||||
-- Which diffrent types of actions are available?
|
||||
-- -> The following fields are part of an action:
|
||||
-- a_id the ID/key of the action
|
||||
@ -29,8 +30,8 @@
|
||||
-- call a custom formspec ("custom"):
|
||||
-- a_value parameter for the custom function
|
||||
--
|
||||
-- a general, more complex formspec-basted puzzle ("puzzle"): not supported yet
|
||||
-- TODO: implement "puzzle" type?
|
||||
-- a general, more complex formspec-basted puzzle ("puzzle"): not supported
|
||||
-- (custom may be more helpful)
|
||||
--
|
||||
--
|
||||
-- Note: Trades are not stored as actions - they are stored in
|
||||
@ -56,65 +57,6 @@ local check_what = {
|
||||
local values_what = {"", "none", "trade", "npc_gives", "npc_wants", "text_input", "custom", "puzzle"}
|
||||
|
||||
|
||||
-- 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
|
||||
|
||||
|
||||
-- returns a human-readable text as description of the action
|
||||
-- (as shown in the edit options dialog and in the edit effect formspec)
|
||||
yl_speak_up.show_action = function(a)
|
||||
@ -143,605 +85,6 @@ yl_speak_up.show_action = function(a)
|
||||
return tostring(a.a_value)
|
||||
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
|
||||
if(this_action.a_on_failure) then
|
||||
-- go back to the same dialog
|
||||
if(not(dialog.n_dialogs[this_action.a_on_failure])) then
|
||||
this_action.a_on_failure = d_id
|
||||
end
|
||||
-- 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
|
||||
else
|
||||
yl_speak_up.debug_msg(player, n_id, o_id, "Action "..
|
||||
tostring(a_id).." failed, but no a_on_failure target "..
|
||||
"dialog defined. Continuing execution.")
|
||||
end
|
||||
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
|
||||
|
||||
|
||||
-- these are only wrapper functions for those in fs_edit_general.lua
|
||||
|
||||
yl_speak_up.input_fs_edit_actions = function(player, formname, fields)
|
||||
|
1
init.lua
1
init.lua
@ -29,6 +29,7 @@ dofile(modpath .. "fs_save_or_discard_or_back.lua")
|
||||
dofile(modpath .. "custrom_functions_you_can_override.lua")
|
||||
-- execute preconditions, actions and effects
|
||||
dofile(modpath .. "exec_eval_preconditions.lua")
|
||||
dofile(modpath .. "exec_actions.lua")
|
||||
-- some helper functions for formatting text for a formspec talbe
|
||||
dofile(modpath .. "print_as_table.lua")
|
||||
-- create i.e. a dropdown list of player names
|
||||
|
Loading…
Reference in New Issue
Block a user