forked from your-land-mirror/yl_speak_up
389 lines
15 KiB
Lua
389 lines
15 KiB
Lua
|
|
-- Which diffrent types of effects are available?
|
|
-- -> The following fields are part of an effect/result:
|
|
-- r_id the ID/key of the effect/result
|
|
-- r_type selected from values_what; the staffs allow to use other
|
|
-- types like "function" or "give_item" etc. - but that is not
|
|
-- supported here (cannot be edited or created; only be shown)
|
|
-- r_value used to store the subtype of r_type
|
|
--
|
|
-- a state/variable:
|
|
-- r_variable name of a variable the player has *write* access to;
|
|
-- dropdown list with allowed options
|
|
-- r_operator selected from values_operator
|
|
-- r_var_cmp_value can be set freely by the player (the variable will be
|
|
-- set to this value)
|
|
--
|
|
-- a block in the world:
|
|
-- r_pos a position in the world; determined by asking the player
|
|
-- to punch the block
|
|
-- r_node (follows from r_pos)
|
|
-- r_param2 (follows from r_pos)
|
|
--
|
|
-- a craft receipe:
|
|
-- r_value the expected craft result
|
|
-- r_craft_grid array containing the stacks in the 9 craft grid fields in string form
|
|
--
|
|
-- on_failure:
|
|
-- r_value alternate target dialog if the action failed
|
|
--
|
|
-- chat_all:
|
|
-- r_value chat message sent to all players
|
|
--
|
|
-- Unlike in preconditions, trade (the trade action already happened) and
|
|
-- inventory actions are not supported as effects.
|
|
--
|
|
|
|
-- some helper lists for creating the formspecs and evaulating
|
|
-- the player's answers:
|
|
|
|
-- general direction of what could make up an effect
|
|
local check_what = {
|
|
"- please select -",
|
|
"an internal state (i.e. of a quest)", -- 2
|
|
"a block somewhere", -- 3
|
|
"NPC crafts soemthing", -- 4
|
|
"go to other dialog if the action (i.e. trade) failed", -- 5
|
|
"send a chat message to all players", -- 6
|
|
}
|
|
|
|
-- how to store these as r_type in the precondition:
|
|
local values_what = {"", "state", "block", "craft", "on_failure", "chat_all"}
|
|
|
|
-- unlike in the preconditions, the "I cannot punch it" option is
|
|
-- not offered here - because the player (and later the NPC) needs
|
|
-- to be able to build at this position
|
|
local check_block = {
|
|
"- please select -", -- 1
|
|
"If there is air: Place a block so that it looks like now.", -- 2
|
|
"If there is a block: Dig it.", -- 3
|
|
-- TODO: not sure if punching a block (as an npc) is possible without bugs
|
|
"Punch the block.", -- 4
|
|
-- TODO: not sure if right-clicking a block (as an npc) is possible without bugs
|
|
"Right-click the block.", -- 5
|
|
}
|
|
|
|
-- how to store these as p_value (the actual node data gets stored as p_node, p_param2 and p_pos):
|
|
local values_block = {"", "place", "dig", "punch", "right-click"}
|
|
|
|
-- comparison operators for variables
|
|
local check_operator = {
|
|
"- please select -", -- 1
|
|
"set to value:", -- 2
|
|
"is no longer needed (unset)", -- 3
|
|
}
|
|
|
|
-- how to store these as r_value (the actual variable is stored in r_variable, and the value in r_new_value):
|
|
local values_operator = {"", "set_to", "unset"}
|
|
|
|
|
|
-- get the list of variables the player has *write* access to
|
|
yl_speak_up.get_sorted_player_var_list_write_access = function(pname)
|
|
local var_list = {}
|
|
-- some values - like hour of day or HP of the player - can be read in
|
|
-- a precondition but not be modified
|
|
-- get the list of variables the player can *write*
|
|
local tmp = yl_speak_up.get_quest_variables_with_write_access(pname)
|
|
-- sort that list (the dropdown formspec element returns just an index)
|
|
table.sort(tmp)
|
|
for i, v in ipairs(tmp) do
|
|
table.insert(var_list, v)
|
|
end
|
|
return var_list
|
|
end
|
|
|
|
|
|
-- returns a human-readable text as description of the effects
|
|
-- (as shown in the edit options dialog and in the edit effect formspec)
|
|
yl_speak_up.show_effect = function(r)
|
|
if(not(r.r_type) or r.r_type == "") then
|
|
return "(nothing): Nothing to do. No effect."
|
|
elseif(r.r_type == "give_item") then
|
|
return "give_item: Add \""..tostring(r.r_value).."\" to the player's inventory."
|
|
elseif(r.r_type == "take_item") then
|
|
return "take_item: Take \""..tostring(r.r_value).."\" from the player's inventory."
|
|
elseif(r.r_type == "move") then
|
|
return "move: Move the player to "..tostring(r.r_value).."."
|
|
elseif(r.r_type == "function") then
|
|
return "function: execute \""..tostring(r.r_value).."\"."
|
|
elseif(r.r_type == "trade") then
|
|
return "trade: obsolete (now defined as an action)"
|
|
elseif(r.r_type == "dialog") then
|
|
return "Switch to dialog \""..tostring(r.r_value).."\"."
|
|
elseif(r.r_type == "state") then
|
|
if(not(r.r_operator)) then
|
|
return "Error: Operator not defined."
|
|
elseif(r.r_operator == "set_to") then
|
|
return "set VARIABLE[ "..tostring(r.r_variable).." ] to value \""..
|
|
tostring(r.r_var_cmp_value).."\""
|
|
elseif(r.r_operator == "unset") then
|
|
return "discard VARIABLE[ "..tostring(r.r_variable).." ] (unset)"
|
|
else
|
|
return "ERROR: Wrong operator \""..tostring(r.r_operator).."\" for "..
|
|
"VARIABLE[ "..tostring(r.r_variable).." ]"
|
|
end
|
|
elseif(r.r_type == "block") then
|
|
if(not(r.r_pos) or type(r.r_pos) ~= "table"
|
|
or not(r.r_pos.x) or not(r.r_pos.y) or not(r.r_pos.z)) then
|
|
return "ERROR: r.r_pos is "..minetest.serialize(r.r_pos)
|
|
elseif(r.r_value == "place") then
|
|
return "Place \""..tostring(r.r_node).."\" with param2: "..tostring(r.r_param2)..
|
|
" at "..minetest.pos_to_string(p.p_pos).."."
|
|
elseif(r.r_value == "dig") then
|
|
return "Dig the block at "..minetest.pos_to_string(r.r_pos).."."
|
|
elseif(r.r_value == "punch") then
|
|
return "Punch the block at "..minetest.pos_to_string(r.r_pos).."."
|
|
elseif(r.r_value == "right-click") then
|
|
return "Right-click the block at "..minetest.pos_to_string(r.r_pos).."."
|
|
else
|
|
return "ERROR: Don't know what to do with the block at "..
|
|
minetest.pos_to_string(r.r_pos)..": \""..tostring(r.r_value).."\"?"
|
|
end
|
|
elseif(r.r_type == "craft") then
|
|
if(not(r.r_value) or not(r.r_craft_grid)) then
|
|
return "ERROR: Crafting not configured correctly."
|
|
end
|
|
-- TODO: check here if the craft receipe is broken?
|
|
return "Craft \""..tostring(r.r_value).."\" from "..table.concat(r.r_craft_grid, ", ").."."
|
|
elseif(r.r_type == "on_failure") then
|
|
return "If the action (i.e. trade) failed, go to dialog \""..tostring(r.r_value).. "\"."
|
|
elseif(r.r_type == "chat_all") then
|
|
return "Send chat message: \""..tostring(r.r_value).."\""
|
|
end
|
|
-- fallback
|
|
return tostring(r.r_value)
|
|
end
|
|
|
|
|
|
-- executes an effect/result r for the player and npc n_id;
|
|
-- returns true on success (relevant for on_failure)
|
|
-- TODO: in edit mode, nothing gets executed except perhaps dialog
|
|
yl_speak_up.execute_effect = function(player, n_id, o_id, r)
|
|
if(not(r.r_type) or r.r_type == "") then
|
|
-- nothing to do
|
|
return true
|
|
elseif(r.r_type == "auto" or r.r_type == "trade") then
|
|
-- these effects don't do anything
|
|
return true
|
|
elseif(r.r_type == "dialog") then
|
|
-- TODO: how to handle target dialog?
|
|
return true
|
|
elseif(r.r_type == "function") then
|
|
-- this can only be set and edited with the staff
|
|
return yl_speak_up.eval_and_execute_function(player, r, "r_")
|
|
-- this can only be set and edited with the staff
|
|
elseif(r.r_type == "give_item") then
|
|
if(not(r.r_value)) then
|
|
return false
|
|
end
|
|
local item = ItemStack(r.r_value)
|
|
if(not(minetest.registered_items[item:get_name()])) then
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"give_item: "..tostring(item:get_name()).." unknown.")
|
|
return false
|
|
end
|
|
local r = player:get_inventory():add_item("main", item)
|
|
if(not(r)) then
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"give_item: "..tostring(item:get_name()).." failed.")
|
|
return false
|
|
end
|
|
return true
|
|
-- this can only be set and edited with the staff
|
|
elseif(r.r_type == "take_item") then
|
|
if(not(r.r_value)) then
|
|
return false
|
|
end
|
|
local item = ItemStack(r.r_value)
|
|
if(not(minetest.registered_items[item:get_name()])) then
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"take_item: "..tostring(item:get_name()).." unknown.")
|
|
return false
|
|
end
|
|
local r = player:get_inventory():remove_item("main", item)
|
|
if(not(r)) then
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"take_item: "..tostring(item:get_name()).." failed.")
|
|
return false
|
|
end
|
|
return true
|
|
-- this can only be set and edited with the staff
|
|
elseif(r.r_type == "move") then
|
|
-- copeid/moved here from AliasAlreadyTakens code in functions.lua
|
|
local target_pos = nil
|
|
local target_pos_valid = false
|
|
|
|
--pos like (100,20,400)
|
|
if minetest.string_to_pos(r.r_value) then
|
|
target_pos = minetest.string_to_pos(r.r_value)
|
|
target_pos_valid = true
|
|
end
|
|
|
|
--pos like 100,20,400
|
|
local maybe = string.split(r.r_value, ",")
|
|
if not target_pos_valid and maybe and tonumber(maybe[1])
|
|
and tonumber(maybe[2]) and tonumber(maybe[3]) and maybe[4] == nil and
|
|
tonumber(maybe[1]) <= 32000 and tonumber(maybe[1]) >= -32000 and
|
|
tonumber(maybe[2]) <= 32000 and tonumber(maybe[2]) >= -32000 and
|
|
tonumber(maybe[3]) <= 32000 and tonumber(maybe[3]) >= -32000 then
|
|
target_pos = {x=maybe[1],y=maybe[2],z=maybe[3]}
|
|
target_pos_valid = true
|
|
end
|
|
|
|
--pos like {x=100,y=20,z=400}
|
|
if not target_pos_valid and string.sub(r.r_value,1,1) == "{"
|
|
and string.sub(r.r_value,-1,-1) == "}" then
|
|
local might_be_pos = minetest.deserialize("return " .. r.r_value)
|
|
if tonumber(might_be_pos.x)
|
|
and tonumber(might_be_pos.x) <= 32000
|
|
and tonumber(might_be_pos.x) >= -32000
|
|
and tonumber(might_be_pos.y)
|
|
and tonumber(might_be_pos.y) <= 32000
|
|
and tonumber(might_be_pos.y) >= -32000
|
|
and tonumber(might_be_pos.z)
|
|
and tonumber(might_be_pos.z) <= 32000
|
|
and tonumber(might_be_pos.z) >= -32000 then
|
|
target_pos = might_be_pos
|
|
target_pos_valid = true
|
|
end
|
|
end
|
|
|
|
if target_pos_valid == true then
|
|
player:set_pos(target_pos)
|
|
if vector.distance(player:get_pos(),target_pos) >= 2 then
|
|
yl_speak_up.log_change(pname, n_id, tostring(r.r_id)..": "..
|
|
"Something went wrong! Player wasn't moved properly.")
|
|
end
|
|
end
|
|
|
|
-- Debug
|
|
if target_pos_valid == false then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
yl_speak_up.log_change(pname, n_id, tostring(r.r_id)..": "..
|
|
"NPC at "..minetest.pos_to_string(obj:get_pos(),0)..
|
|
" could not move player "..pname.." because the content of "..
|
|
tostring(r.r_id).." is wrong:"..dump(r.r_value))
|
|
else
|
|
yl_speak_up.log_change(pname, n_id, tostring(r.r_id)..": "..
|
|
"NPC with unknown ID or without proper object "..
|
|
" could not move player "..pname.." because the content of "..
|
|
tostring(r.r_id).." is wrong:"..dump(r.r_value))
|
|
end
|
|
return false
|
|
end
|
|
return true
|
|
|
|
-- "an internal state (i.e. of a quest)", -- 2
|
|
elseif(r.r_type == "state") then
|
|
-- TODO: implement effect "state"
|
|
-- "a block somewhere" -- 3
|
|
elseif(r.r_type == "block") then
|
|
-- TODO: implement effect "block"
|
|
-- ""NPC crafts soemthing", -- 4
|
|
elseif(r.r_type == "craft") then
|
|
if(not(r.r_craft_grid) or not(r.r_value)) then
|
|
return false
|
|
end
|
|
local input = {}
|
|
input.items = {}
|
|
-- multiple slots in the craft grid may contain the same item
|
|
local sum_up = {}
|
|
for i, v in ipairs(r.r_craft_grid) do
|
|
if(v and v ~= "") then
|
|
local stack = ItemStack(v)
|
|
-- store this for later crafting
|
|
input.items[ i ] = stack
|
|
local name = stack:get_name()
|
|
if(sum_up[ name ]) then
|
|
sum_up[ name ] = sum_up[ name ] + stack:get_count()
|
|
else
|
|
sum_up[ name ] = stack:get_count()
|
|
end
|
|
else
|
|
-- empty itemstack in this slot
|
|
input.items[ i ] = ItemStack("")
|
|
end
|
|
end
|
|
-- does the NPC have all these items in his inventory?
|
|
local npc_inv = minetest.get_inventory({type="detached",
|
|
name="yl_speak_up_npc_"..tostring(n_id)})
|
|
for k, v in pairs(sum_up) do
|
|
if(not(npc_inv:contains_item("npc_main", k.." "..v))) then
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"Crafting failed: NPC does not have "..tostring(k.." "..v))
|
|
return false
|
|
end
|
|
end
|
|
-- do these input items form a valid craft recipe?
|
|
input.method = "normal" -- normal crafting; no cooking or fuel or the like
|
|
input.width = 3
|
|
local output, decremented_input = minetest.get_craft_result(input)
|
|
if(output.item:is_empty()) then
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"Crafting failed: No output for that recipe.")
|
|
return false
|
|
end
|
|
-- the craft receipe may have changed in the meantime and yield a diffrent result
|
|
local expected_stack = ItemStack(r.r_value)
|
|
if(output.item:get_name() ~= expected_stack:get_name()
|
|
or output.item:get_count() ~= expected_stack:get_count()) then
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"Crafting failed: Diffrent output: "..tostring(output.item:to_string()))
|
|
return false
|
|
end
|
|
-- TODO: actually consume the items required, return the ones in decremented_input
|
|
yl_speak_up.debug_msg(player, n_id, o_id, tostring(r_id).." "..
|
|
"Great: Crafting is possible!")
|
|
return true
|
|
-- "go to other dialog if the action (i.e. trade) failed", -- 5
|
|
elseif(r.r_type == "on_failure") then
|
|
-- TODO: implement effect on_failure
|
|
-- "send a chat message to all players", -- 6
|
|
elseif(r.r_type == "chat_all") then
|
|
local pname = player:get_player_name()
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local text = r.r_value
|
|
-- replace $NPC_NAME$, $OWNER_NAME$, $PLAYER_NAME$ etc.
|
|
text = yl_speak_up.replace_vars_in_text(text, dialog, pname)
|
|
minetest.chat_send_all(text)
|
|
-- sending a chat message always counts as successful
|
|
return true
|
|
end
|
|
-- fallback: unkown type
|
|
return false
|
|
end
|
|
|
|
|
|
-- these are only wrapper functions for those in fs_edit_general.lua
|
|
|
|
yl_speak_up.input_fs_edit_effects = function(player, formname, fields)
|
|
return yl_speak_up.input_fs_edit_option_related(player, formname, fields,
|
|
"r_", "o_results", yl_speak_up.max_result_effects,
|
|
"(Ef)fect", "tmp_result",
|
|
"Please punch the block you want to manipulate in your effect!",
|
|
values_what, values_operator, values_block, {}, {},
|
|
check_what, check_operator, check_block, {}, {},
|
|
-- player variables with write access
|
|
yl_speak_up.get_sorted_player_var_list_write_access,
|
|
"edit_effects"
|
|
)
|
|
end
|
|
|
|
yl_speak_up.get_fs_edit_effects = function(player, table_click_result)
|
|
return yl_speak_up.get_fs_edit_option_related(player, table_click_result,
|
|
"r_", "o_results", yl_speak_up.max_result_effects,
|
|
"(Ef)fect", "tmp_result",
|
|
"What do you want to change with this effect?",
|
|
check_what, check_operator, check_block, {}, {},
|
|
-- player variables with write access
|
|
yl_speak_up.get_sorted_player_var_list_write_access,
|
|
yl_speak_up.show_effect,
|
|
"table_of_elements",
|
|
"Change the value of the following variable:", "What to do:", "New value:",
|
|
"The NPC shall do something to the block at the following position:"
|
|
)
|
|
end
|