first draft of alternate edit mode for players with npc_talk_owner priv

This commit is contained in:
Sokomine 2021-04-27 22:17:09 +02:00
parent 207cd3da43
commit e40c464af8
2 changed files with 221 additions and 16 deletions

View File

@ -1,7 +1,42 @@
-- TODO: just for the apple quest...needs to be deleted later on
yl_sokomine = {}
yl_sokomine["q_apple_explorer"] = {}
-- if player has npc_talk_owner priv AND is owner of this particular npc (second one: TODO):
-- chat option: "I am your owner. I have new orders for you.
-- -> enters edit mode
-- when edit_mode has been enabled, the following chat options are added to the options:
-- chat option: "Add new answer/option to this dialog."
-- -> adds a new option TODO
-- chat option: "That was all. I'm finished with giving you new orders. Remember them!"
-- -> ends edit mode
--
-- TODO: add the actual edit menu for options
-- can be entered from the edit, conditions and effects button;
-- style: Answer/Option: <text>
-- Lets the NPC answer with: <dialog_id> [Button GOTO target dialog]
--
-- Only offer this answer if all the following conditions are true:
-- <list of preconditions>
-- If at least one condition is not fulfilled, ..
-- <display the following text|grey out this answer|do not offer this answer>
-- <alternate answer>
-- When this answer has been selected, apply the following effects:
-- <list of effects>
--
-- [Store] [Delete this option] [Back to dialog xyz] [GOTO target diaglog]
--
-- TODO: check if security is ok (minetest.formspec_escape etc)
-- TODO: actually process changed entries
-- TODO: allow owner to mute/unmute npc (would be bad if players can already see what is going
-- to happen while the owner creates a long quest)
--###
-- Init
--###
-- store if the player is editing a particular NPC; format: yl_speak_up.edit_mode[pname] = npc_id
yl_speak_up.edit_mode = {}
function yl_speak_up.init_mob_table()
return false
end
@ -9,12 +44,14 @@ end
minetest.register_on_leaveplayer(
function(player)
yl_speak_up.speak_to[player:get_player_name()] = nil
yl_speak_up.edit_mode[player:get_player_name()] = nil
end
)
minetest.register_on_joinplayer(
function(player)
yl_speak_up.speak_to[player:get_player_name()] = nil
yl_speak_up.edit_mode[player:get_player_name()] = nil
end
)
@ -949,16 +986,67 @@ local function get_fs_talkdialog(player, n_id, d_id)
"image[15.5,0.5;4,4;",
portrait,
"]",
"hypertext[0.2,5;19.6,17.8;d_text;<normal>",
minetest.formspec_escape(active_dialog.d_text) .. "\n</normal>",
"]",
"tooltip[d_text;",
minetest.formspec_escape(active_dialog.d_text):trim(),
";#000000;#FFFFFF]",
"scrollbaroptions[min=0;max=14;smallstep=1;largestep=2;arrows=show]",
"scrollbar[0.2,24.2;0.2,7;vertical;scr0;0]",
"scroll_container[0,24;56,7;scr0;vertical;1]"
}
-- Is the player working on this particular npc?
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
-- this is used to build a list of all available dialogs for a dropdown menu in edit mode
-- (only relevant in edit mode)
local dialog_list = yl_speak_up.text_new_dialog_id
-- find the right index for the dialog_list dropdown above
local d_id_to_dropdown_index = {}
-- display the window with the text the NPC is saying
-- TODO: make name and description likewise editable?
if(edit_mode) then
-- build the list of available dialogs for the dropdown list(s)
-- if there are dialogs defined
if(dialog and dialog.n_dialogs) then
-- the first entry will be "New dialog"
local n = 1
for k, v in pairs(dialog.n_dialogs) do
dialog_list = dialog_list .. "," .. v.d_id
-- which one is the current dialog?
n = n + 1
d_id_to_dropdown_index[v.d_id] = n
end
end
local i = (d_id_to_dropdown_index[c_d_id] or 0)
table.insert(formspec, "label[0.2,4.6;Dialog:]") -- "..minetest.formspec_escape(c_d_id)..":]")
table.insert(formspec, "dropdown[3.0,4.0;5,1;d_id;"..dialog_list..";"..i..",]")
table.insert(formspec, "tooltip[3.0,4.0;5,1;Select the dialog you want to edit. Currently, dialog "..c_d_id.." is beeing displayed.;#FFFFFF;#000000]")
-- add previous/next buttons for previous/next dialog (sorted as in the dropdown menu);
-- this is just an alternate way of walking through the dialogs
for k,v in pairs(d_id_to_dropdown_index) do
if( v == i-1) then
table.insert(formspec, "button[8.5,4.0;2,0.9;dialog_"..k..";<]")
-- add a tooltip
table.insert(formspec, "tooltip[dialog_" .. k .. ";Go to previous dialog "..k..".]")
elseif(v == i+1) then
table.insert(formspec, "button[11,4.0;2,0.9;dialog_"..k..";>]")
-- add a tooltip
table.insert(formspec, "tooltip[dialog_" .. k .. ";Go to next dialog "..k..".]")
end
end
table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;")
table.insert(formspec, minetest.formspec_escape(active_dialog.d_text))
table.insert(formspec, "]")
else
table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;<normal>")
table.insert(formspec, minetest.formspec_escape(active_dialog.d_text) .. "\n</normal>")
table.insert(formspec, "]")
table.insert(formspec, "tooltip[d_text;")
table.insert(formspec, minetest.formspec_escape(active_dialog.d_text):trim())
table.insert(formspec, ";#000000;#FFFFFF]")
end
table.insert(formspec, "scrollbaroptions[min=0;max=14;smallstep=1;largestep=2;arrows=show]")
table.insert(formspec, "scrollbar[0.2,24.2;0.2,7;vertical;scr0;0]")
table.insert(formspec, "scroll_container[0,24;56,7;scr0;vertical;1]")
h = -0.8
-- Let#s sort the options by o_sort
@ -969,7 +1057,62 @@ local function get_fs_talkdialog(player, n_id, d_id)
end
for _, sb_v in pairs(sorted_buttons) do
if allowed[sb_v.o_id] == true then
-- in edit_mode: show all options
if edit_mode then
h = h + 1
-- add a button "o_<nr>:" that leads to an edit formspec for this option
table.insert(formspec, "button[1.8," .. h .. ";2,0.9;edit_option_" .. sb_v.o_id .. ";"..sb_v.o_id..":]")
-- add a tooltip "Edit target dialog, pre(C)onditions and (E)ffects for option o_<nr>"
table.insert(formspec, "tooltip[edit_option_" .. sb_v.o_id .. ";Edit target dialog, pre(C)onditions and (E)ffects for option "..sb_v.o_id..".]")
-- find the right target dialog for this option (if it exists):
local target_dialog = nil
local results = active_dialog.d_options[sb_v.o_id].o_results
-- has this option more results/effects than just switching to another dialog?
local has_other_results = false
if(results ~= nil) then
for k, v in pairs(results) do
if v.r_type == "dialog" and dialog.n_dialogs[v.r_value] ~= nil then
-- there may be more than one in the data structure
target_dialog = v.r_value
elseif v.r_type ~= "dialog" then
has_other_results = true
end
end
end
if(target_dialog) then
-- add a button "-> d_<nr>" that leads to the target dialog (if one is set)
table.insert(formspec, "button[8.5," .. h .. ";1,0.9;button_" .. sb_v.o_id .. ";->]")
-- add a tooltip "Go to target dialog d_<nr>"
table.insert(formspec, "tooltip[button_" .. sb_v.o_id .. ";Go to target dialog "..target_dialog.." that will be shown when this option ("..sb_v.o_id..") is selected.]")
-- selecting an option this way MUST NOT execute the pre(C)onditions or (E)ffects!
end
-- allow to set a new target dialog
table.insert(formspec, "dropdown[3.9,"..h..";4.7,1;d_id_"..sb_v.o_id..";"..dialog_list..";"..(d_id_to_dropdown_index[target_dialog] or "0")..",]")
-- add a tooltip "Change target dialog"
table.insert(formspec, "tooltip[3.9,"..h..";4.7,1;Change target dialog for option "..sb_v.o_id..".;#FFFFFF;#000000]")
-- are there any prerequirements?
local prereq = active_dialog.d_options[sb_v.o_id].o_prerequisites
if(prereq) then
table.insert(formspec, "button[0.5," .. h .. ";0.5,0.9;conditions_"..sb_v.o_id..";C]")
-- label: "There are pre(C)onditions required for showing this option. Display them."
table.insert(formspec, "tooltip[conditions_" .. sb_v.o_id .. ";There are pre(C)onditions required for showing this option. Display them.]")
end
if(has_other_results) then
table.insert(formspec, "button[1.1," .. h .. ";0.6,0.9;effects_"..sb_v.o_id..";Ef]")
-- label: "There are further (E)ffects (apart from switching to a new dialog) set for this option. Display them."
table.insert(formspec, "tooltip[effects_" .. sb_v.o_id .. ";There are further (E)ffects (apart from switching to a new dialog) set for this option. Display them.]")
end
-- show the actual text for the option
table.insert(formspec, "field[9.4," .. h .. ";44.9,0.9;text_option_" .. sb_v.o_id .. ";;"..sb_v.o_text_when_prerequisites_met.."]")
-- add a tooltip "Edit the text that is displayed on button o_<nr>."
table.insert(formspec, "tooltip[text_option_" .. sb_v.o_id .. ";Edit the text that is displayed on button "..sb_v.o_id..".]")
-- normal mode: show an option if the prerequirements (if any are defined) are met
elseif allowed[sb_v.o_id] == true then
h = h + 1
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. sb_v.o_id .. ";]")
table.insert(
@ -1045,6 +1188,32 @@ local function get_fs_talkdialog(player, n_id, d_id)
end
end
end
-- If in edit mode, add two new menu entries: "add new options" and "end edit mode".
if(edit_mode) then
-- chat option: "Add a new answer/option to this dialog."
h = h + 1
table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_add_option;]")
table.insert(formspec, "tooltip[button_add_option;Adds a new option to this dialog. You can delete options via the option edit menu.]")
table.insert(formspec, "label[0.7,"..(h+0.45)..";Add a new option/answer to this dialog. You can delete options via the option edit menu.]")
-- chat option: "That was all. I'm finished with giving you new orders. Remember them!"
h = h + 1
table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_end_edit_mode;]")
table.insert(formspec, "tooltip[button_end_edit_mode;Ends edit mode. From now on, your NPC will talk to you like he talks to other players. You can always give him new orders by entering edit mode again.]")
table.insert(formspec, "label[0.7,"..(h+0.45)..";That was all. I'm finished with giving you new orders. Remember them!]")
-- Offer to enter edit mode if the player has the npc_talk_owner priv AND owns the npc.
-- The npc_master priv allows to edit all NPC.
-- TODO: the player also has to be owner of that particular npc
elseif((minetest.check_player_privs(player, {npc_talk_owner=true}))
or (minetest.check_player_privs(player, {npc_master=true}))) then
-- chat option: "I am your owner. I have new orders for you.
h = h + 1
table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_start_edit_mode;]")
table.insert(formspec, "tooltip[button_start_edit_mode;Enters edit mode. In this mode, you can edit the texts the NPC says and the answers that can be given.]")
table.insert(formspec, "label[0.7,"..(h+0.45)..";I am your owner. I have new orders for you.]")
end
h = h + 1
table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_exit;]")
table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]")
@ -1495,6 +1664,29 @@ minetest.register_on_player_receive_fields(
local pname = player:get_player_name()
local o = ""
-- start edit mode (requires npc_talk_owner)
if fields.button_start_edit_mode then
if( not(minetest.check_player_privs(player, {npc_talk_owner=true}))
and not(minetest.check_player_privs(player, {npc_master=true}))) then
minetest.chat_send_player(pname, "Sorry. You do not have the npc_talk_owner or npc_master priv.")
return
end
-- TODO: check if this particular NPC is really owned by this player
-- enter edit mode with that particular NPC
yl_speak_up.edit_mode[pname] = yl_speak_up.speak_to[pname].n_id
-- start a new chat - but this time in edit mode
-- TODO: this continues where the last chat was and does not start from the beginning
minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id))
return
-- end edit mode (does not require the priv; will only switch back to normal behaviour)
elseif fields.button_end_edit_mode then
yl_speak_up.edit_mode[pname] = nil
-- start a new chat - but this time in normal mode
-- TODO: this continues where the last chat was and does not start from the beginning
minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id))
return
end
if fields.quit or fields.button_exit then
-- Formspec was quit
yl_speak_up.speak_to[pname] = nil
@ -1542,8 +1734,11 @@ minetest.register_on_player_receive_fields(
return
end
-- Is the player working on this particular npc?
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
-- Let's check if the button was among the "allowed buttons". Only those may be executed
if yl_speak_up.speak_to[pname].allowed and yl_speak_up.speak_to[pname].allowed[o] == false then
if not(edit_mode) and (yl_speak_up.speak_to[pname].allowed and yl_speak_up.speak_to[pname].allowed[o] == false) then
return
end
@ -1560,7 +1755,9 @@ minetest.register_on_player_receive_fields(
-- Let's do something if results exist
if d_option.o_results ~= nil then
for k, v in pairs(d_option.o_results) do
if v.r_type == "give_item" then
-- in edit_mode, only the switching to the next dialog is executed (we want to edit,
-- not solve quests, get/take items, teleport...)
if not(edit_mode) and v.r_type == "give_item" then
local item = ItemStack(v.r_value)
if minetest.registered_items[item:get_name()] ~= nil then
@ -1569,7 +1766,7 @@ minetest.register_on_player_receive_fields(
say("Item not found!")
end
end
if v.r_type == "take_item" then
if not(edit_mode) and v.r_type == "take_item" then
local item = ItemStack(v.r_value)
if minetest.registered_items[item:get_name()] ~= nil then
@ -1578,7 +1775,7 @@ minetest.register_on_player_receive_fields(
say("Item not found!")
end
end
if v.r_type == "move" then
if not(edit_mode) and v.r_type == "move" then
local target_pos = nil
local target_pos_valid = false
@ -1630,7 +1827,7 @@ minetest.register_on_player_receive_fields(
end
end
if v.r_type == "function" then
if not(edit_mode) and v.r_type == "function" then
local code = v.r_value
if code:byte(1) == 27 then
local obj = yl_speak_up.speak_to[pname].obj

View File

@ -4,4 +4,12 @@ local npc_master_priv_definition = {
give_to_admin = true,
}
minetest.register_privilege("npc_master", npc_master_priv_definition)
minetest.register_privilege("npc_master", npc_master_priv_definition)
local npc_talk_owner_priv_definition = {
description="Can edit the dialogs of his/her own NPCs",
give_to_singleplayer = false,
give_to_admin = true,
}
minetest.register_privilege("npc_talk_owner", npc_talk_owner_priv_definition)