yl_speak_up/fs_manage_quest_steps.lua

387 lines
16 KiB
Lua

-- returns a table with helpful information *if* the player is working on a quest;
-- else error_msg is set
yl_speak_up.player_is_working_on_quest = function(player)
if(not(player)) then
return
end
local t = {}
t.pname = player:get_player_name()
if(not(t.pname)) then
return {error_msg = "Player not found."}
end
if(not(yl_speak_up.speak_to or not(yl_speak_up.speak_to[t.pname]))) then
return {error_msg = "Player not working on a quest."}
end
t.q_id = yl_speak_up.speak_to[t.pname].q_id
if(not(t.q_id) or not(yl_speak_up.quests) or not(yl_speak_up.quests[t.q_id])) then
return {error_msg = "No quest selected or quest not found."}
end
t.quest = yl_speak_up.quests[t.q_id]
if(not(t.quest.step_data) or type(t.quest.step_data) ~= "table") then
yl_speak_up.quests[t.q_id].step_data = {}
end
-- TODO: check if the player has access to that data
t.step_data = yl_speak_up.quests[t.q_id].step_data
t.current_step = yl_speak_up.speak_to[t.pname].quest_step
if(t.quest_step and t.step_data[t.quest_step]) then
local data = t.step_data[t.quest_step]
-- make sure needed tables exist
if(not(data.one_step_required) or type(data.one_step_required) ~= "table") then
yl_speak_up.quests[t.q_id].step_data[t.current_step].one_step_required = {}
end
if(not(data.all_steps_required) or type(data.one_step_required) ~= "table") then
yl_speak_up.quests[t.q_id].step_data[t.current_step].all_steps_required = {}
end
end
-- t contains pname, q_id, quest, step_data and current_step - or error_msg
return t
end
-- for which other quest steps is this_step needed for?
yl_speak_up.quest_step_required_for = function(step_data, this_step)
-- find out the next quest step
local required_for = {}
for s, d in pairs(step_data) do
if(s and d and d.one_step_required and type(d.one_step_required) == "table"
and table.indexof(d.one_step_required, this_step) ~= -1) then
table.insert(required_for, s)
end
if(s and d and d.all_steps_required and type(d.all_steps_required) == "table"
and table.indexof(d.all_steps_required, this_step) ~= -1) then
table.insert(required_for, s)
end
end
table.sort(required_for)
return required_for
end
-- Imposing an order on the quest steps is...tricky as best as what will
-- be more important to the players will be the order in which the
-- quest steps have to be solved/done - and not an alphabetical order.
-- But we need an order here for a dropdown menu to select each
-- quest step even if it hasn't been assigned any place in the chain
-- of quest steps yet. So - alphabetical order.
yl_speak_up.get_sorted_quest_step_list = function(pname, q_id)
local quest_step_list = {}
if(not(pname) or not(yl_speak_up.speak_to[pname])) then
return {}
end
local q_id = yl_speak_up.speak_to[pname].q_id
if(q_id and yl_speak_up.quests[q_id] and yl_speak_up.quests[q_id].step_data) then
for step_id, v in pairs(yl_speak_up.quests[q_id].step_data) do
table.insert(quest_step_list, step_id)
end
end
table.sort(quest_step_list)
return quest_step_list
end
-- helper functions for yl_speak_up.input_fs_manage_quest_steps(..)
-- returns the index of the new quest step
yl_speak_up.input_fs_manage_quest_steps_add_new_entry = function(pname, entry_name)
local q_id = yl_speak_up.speak_to[pname].q_id
local res = yl_speak_up.quest_step_add_quest_step(pname, q_id, entry_name)
-- might make sense to show the error message somewhere
if(res ~= "OK") then
return res
end
-- the new entry will be somewhere in it
local quest_step_list = yl_speak_up.get_sorted_quest_step_list(pname)
return table.indexof(quest_step_list, entry_name)
end
-- helper functions for yl_speak_up.input_fs_manage_quest_steps(..)
-- returns a text describing if deleting the quest worked
yl_speak_up.input_fs_manage_quest_steps_del_old_entry = function(pname, entry_name)
local q_id = yl_speak_up.speak_to[pname].q_id
return yl_speak_up.quest_step_del_quest_step(pname, q_id, entry_name)
end
-- helper functions for yl_speak_up.input_fs_manage_quest_steps(..)
-- implements all the functions that are specific to managing quest steps and not part of
-- general item management
yl_speak_up.input_fs_manage_quest_steps_check_fields = function(player, formname, fields, quest_step_name, list_of_entries)
local pname = player:get_player_name()
if(not(quest_step_name)) then
quest_step_name = ""
end
--[[ TODO: implement some back button functionality?
if(fields and fields.show_variable) then
yl_speak_up.show_fs(player, "manage_variables", {var_name = quest_name})
return
end
--]]
-- this function didn't have anything to do
return "NOTHING FOUND"
end
-- makes use of yl_speak_up.input_fs_manage_general and is thus pretty short
yl_speak_up.input_fs_manage_quest_steps = function(player, formname, fields)
-- TODO: check if the player is allowed to access that quest
local pname = player:get_player_name()
if(fields and fields.back) then
yl_speak_up.show_fs(player, "manage_quests")
return
end
local res = yl_speak_up.player_is_working_on_quest(player)
if(not(res.error_msg) and res.current_step) then
local selected_from = nil
local list = {}
if( fields and fields.one_step_required) then
selected_from = fields.one_step_required
list = res.step_data[res.current_step].one_step_required
elseif(fields and fields.all_steps_required) then
selected_from = fields.all_steps_required
list = res.step_data[res.current_step].all_steps_required
elseif(fields and fields.next_steps_show) then
selected_from = fields.next_steps_show
list = yl_speak_up.quest_step_required_for(res.step_data, res.current_step)
end
if(selected_from) then
local selected = minetest.explode_table_event(selected_from)
if(selected and selected.row and selected.row > 0 and selected.row <= #list) then
-- show the selected quest step
yl_speak_up.speak_to[pname].quest_step = list[selected.row]
yl_speak_up.show_fs(player, "manage_quest_steps", list[selected.row])
return
end
end
-- show prev logical step
if(fields and fields.show_prev_step) then
if(#res.step_data[res.current_step].one_step_required > 0) then
yl_speak_up.show_fs(player, "manage_quest_steps",
res.step_data[res.current_step].one_step_required[1])
return
elseif(#res.step_data[res.current_step].all_steps_required > 0) then
yl_speak_up.show_fs(player, "manage_quest_steps",
res.step_data[res.current_step].all_steps_required[1])
return
end
-- show next logical step
elseif(fields and fields.show_next_step) then
local list = yl_speak_up.quest_step_required_for(res.step_data, res.current_step)
if(list and #list > 0) then
yl_speak_up.show_fs(player, "manage_quest_steps", list[1])
return
end
end
end
if(fields
and (fields.add_to_one_needed or fields.add_to_all_needed
or fields.insert_after_prev_step or fields.insert_before_next_step)) then
-- let that function sort out what to do;
-- yl_speak_up.speak_to[pname].q_id and yl_speak_up.speak_to[pname].quest_step
-- ought to be set to the current quest and step by now
if(fields.add_to_one_needed) then
yl_speak_up.speak_to[pname].quest_step_mode = "add_to_one_needed"
elseif(fields.add_to_all_needed) then
yl_speak_up.speak_to[pname].quest_step_mode = "add_to_all_needed"
elseif(fields.insert_after_prev_step) then
yl_speak_up.speak_to[pname].quest_step_mode = "insert_after_prev_step"
elseif(fields.insert_before_next_step) then
yl_speak_up.speak_to[pname].quest_step_mode = "insert_before_next_step"
else
yl_speak_up.speak_to[pname].quest_step_mode = nil
end
yl_speak_up.show_fs(player, "add_quest_steps")
return
end
local quest_step_list = yl_speak_up.get_sorted_quest_step_list(pname)
local res = yl_speak_up.input_fs_manage_general(player, formname, fields,
-- what_is_the_list_about, min_length, max_length, function_add_new_entry,
"quest step", 2, 70,
yl_speak_up.input_fs_manage_quest_steps_add_new_entry,
quest_step_list,
yl_speak_up.input_fs_manage_quest_steps_del_old_entry,
yl_speak_up.input_fs_manage_quest_steps_check_fields)
return true
end
-- small helper function for yl_speak_up.get_fs_manage_quest_steps
yl_speak_up.quest_step_show_table = function(formspec, table_specs, liste)
table.insert(formspec, "tablecolumns[color;text]table[")
table.insert(formspec, table_specs)
local tmp = {}
for k, v in pairs(liste) do
table.insert(tmp, "#AAFFAA")
table.insert(tmp, minetest.formspec_escape(v))
end
table.insert(formspec, table.concat(tmp, ","))
table.insert(formspec, ";]")
end
yl_speak_up.get_fs_manage_quest_steps = function(player, param)
-- small helper function
local em = function(text)
return minetest.colorize("#9999FF", text)
end
local res = yl_speak_up.player_is_working_on_quest(player)
if(res.error_msg) then
return "size[9,2]"..
"label[0,0;"..minetest.formspec_escape("Error: "..tostring(res.error_msg)).."]"..
"button[1.5,1.5;2,0.9;back;Back]"
end
local step_data = res.step_data
local quest_step_list = yl_speak_up.get_sorted_quest_step_list(res.pname)
if(param and param ~= "") then
local index = table.indexof(quest_step_list, param)
yl_speak_up.speak_to[res.pname].tmp_index_general = index + 1
end
local idx = yl_speak_up.speak_to[res.pname].tmp_index_general
if(idx and idx > 1) then
yl_speak_up.speak_to[res.pname].quest_step = quest_step_list[idx - 1]
end
local formspec = {}
table.insert(formspec, "size[30,12]"..
"container[6,0;18.5,12]"..
"label[0.2,1.2;A quest step is a single thing a player may do in a quest - "..
"like talking to an NPC.\n"..
"Usually not all quest steps can be done/solved at all times.]")
local selected = yl_speak_up.get_fs_manage_general(player, param,
formspec, quest_step_list,
"Create quest step",
"Create a new quest step for this quest.",
"quest step",
"Enter the name of the new quest step you want to create.\n"..
"This is an internal text shown only to yourself.\n"..
"Players cannot see the names of quest steps.",
"If you click here, the selected quest step will be deleted.\n"..
"This will only be possible if it's not used anywhere.",
"1.0")
table.insert(formspec, "container_end[]")
if(not(selected) or selected == "" or not(step_data) or not(step_data[selected])) then
return table.concat(formspec, "")
end
-- find out the next quest step
local required_for = yl_speak_up.quest_step_required_for(step_data, selected)
-- left side (previous quest step)
table.insert(formspec, "label[0.2,2.0;"..em("Required previous").."]"..
"label[0.2,2.4;quest step(s):]"..
"style[insert_before_next_step,insert_after_prev_step,"..
"add_to_one_needed,add_to_all_needed;bgcolor=blue;textcolor=yellow]"..
"container[0.1,2.7;5.6,10.8]"..
"box[0,0;5.6,8.5;#666666]"..
"button[4.6,0.0;0.94,0.7;add_to_one_needed;Edit]"..
"tooltip[add_to_one_needed;Add or remove a quest step to this list.]"..
"label[0.1,0.5;"..em("One of").." these quest steps:]")
yl_speak_up.quest_step_show_table(formspec, "0.1,0.7;5.4,2.7;one_step_required;",
step_data[selected].one_step_required or {})
table.insert(formspec, "tooltip[one_step_required;"..
"At least "..em("one of").." these previous quest steps listed ".. "here has to be\n"..
"achieved by the player who is trying to solve this quest.\n"..
"Only then can the player try to achieve this current quest\n"..
"step that you are editing here.\n"..
"If this is empty, then it's usually the/a first step of the quest.\n"..
"If there is one entry, then that entry is the previous quest step.\n"..
"If there are multiple entries, then players have alternate ways\n"..
"to achieve this current quest step here.]")
table.insert(formspec, "label[0.1,4.0;"..em("All of").." these quest steps:]"..
"button[4.6,3.5;0.94,0.7;add_to_all_needed;Edit]"..
"tooltip[add_to_all_needed;Add or remove a quest step to this list.]")
yl_speak_up.quest_step_show_table(formspec, "0.1,4.2;5.4,4.0;all_steps_required;",
step_data[selected].all_steps_required or {})
table.insert(formspec, "tooltip[all_steps_required;"..
"Sometimes there may not be a particular order in which\n"..
"quest steps ought to be solved. Imagine for example getting\n"..
"indigrents for a cake and delivering them to an NPC.\n"..
"The quest steps listed here can be done in "..em("any order")..".\n"..
"They have "..em("all").." to be achieved first in order for this current\n"..
"quest step to become available.\n"..
"Usually this list is empty.]")
table.insert(formspec, "container_end[]")
-- right side (next quest step)
table.insert(formspec, "label[24.4,2.0;Achieving this quest step]"..
"label[24.4,2.4;"..em("helps").." the quester to:]"..
"container[24.2,2.7;5.6,10.8]"..
"box[0,0;5.6,8.5;#666666]"..
"label[0.1,0.5;get these quest step(s) "..em("next")..":]")
yl_speak_up.quest_step_show_table(formspec, "0.1,0.7;5.4,7.7;next_steps_show;",
required_for or {})
table.insert(formspec, "tooltip[next_steps_show;"..
"Once the current quest step has been achieved by the\n"..
"player, the player can strive to achieve these next\n"..
"quest step(s).\n"..
"If this is empty, then it's either the/a last step (quest\n"..
"solved!) - or the quest is not properly set up.]")
table.insert(formspec, "container_end[]")
-- middle (this quest step)
table.insert(formspec, "container[6,2.7;12,10.8]"..
"label[0.2,0.5;This quest step:]"..
"dropdown[0.7,0.7;17,0.7;curr_step_show;")
table.insert(formspec, minetest.formspec_escape(selected))
table.insert(formspec, ";1;]")
table.insert(formspec, "container_end[]")
if( #step_data[selected].one_step_required > 0
or #step_data[selected].all_steps_required > 0) then
if( #step_data[selected].one_step_required
+ #step_data[selected].all_steps_required > 1) then
table.insert(formspec, "style[show_prev_step;bgcolor=red]")
end
table.insert(formspec, "button[5.6,1.7;0.6,7.0;show_prev_step;<]"..
"tooltip[show_prev_step;Show the previous step according to "..
em("your quest logic")..".\n"..
"The button turns "..minetest.colorize("#FF0000", "red")..
" if there is more than one option. In such\na case "..
"the alphabeticly first previous quest step is choosen.\n"..
"The other "..em("< Prev").." button shows the previous step "..
"in\n"..em("alphabetical order")..".]")
end
-- add buttons for inserting steps between this and the prev/next one
if(#step_data[selected].one_step_required <= 1) then
table.insert(formspec,
"button[5.6,8.7;0.6,2.6;insert_after_prev_step;+]"..
"tooltip[insert_after_prev_step;"..
"Insert a new quest step between the "..em("previous step")..
" (shown\n"..
"to the left) and the "..em("current step")..
" (shown in the middle).\n"..
"Note: This only makes sense if there's just one or no\n"..
" previous step.]")
end
if(required_for and #required_for > 0) then
if(#required_for > 1) then
table.insert(formspec, "style[show_next_step;bgcolor=red]")
end
table.insert(formspec, "button[23.8,1.7;0.6,7.0;show_next_step;>]"..
"tooltip[show_next_step;Show the next step according to "..
em("your quest logic")..".\n"..
"The button turns "..minetest.colorize("#FF0000", "red")..
" if there is more than one option. In such\na case "..
"the alphabeticly first next quest step is choosen.\n"..
"The other "..em("Next >").." button shows the next step "..
"in\n"..em("alphabetical order")..".]")
end
if(#required_for <= 1) then
table.insert(formspec,
"button[23.8,8.7;0.6,2.6;insert_before_next_step;+]"..
"tooltip[insert_before_next_step;"..
"Insert a new quest step between "..em("current step")..
" (shown\nin the middle) "..
"and the "..em("next step").." (shown to the right).\n"..
"Note: This only makes sense if there's just one or no\n"..
" next step.]")
end
return table.concat(formspec, "")
end