forked from your-land-mirror/yl_speak_up
		
	
		
			
				
	
	
		
			756 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			756 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- helper function to make unintresting zeros in tables less visible
 | |
| local grey_if_zero = function(fs, n)
 | |
| 	if(n and n == 0) then
 | |
| 		table.insert(fs, "#444444")
 | |
| 	else
 | |
| 		table.insert(fs, "#FFFFFF")
 | |
| 	end
 | |
| 	table.insert(fs, minetest.formspec_escape(n))
 | |
| end
 | |
| 
 | |
| 
 | |
| -- small helper function
 | |
| local show_error_fs = function(player, text)
 | |
| 	yl_speak_up.show_fs(player, "msg", {
 | |
| 		input_to = "yl_speak_up:add_quest_steps",
 | |
| 		formspec = yl_speak_up.build_fs_quest_edit_error(text, "back_from_error_msg")})
 | |
| end
 | |
| 
 | |
| 
 | |
| -- find out in how many quest steps this NPC or location is used;
 | |
| -- ID can either be n_<ID> or a location p_(x,y,z)
 | |
| yl_speak_up.count_used_in_quest_steps = function(id, step_data)
 | |
| 	local used = 0
 | |
| 	for s, d in pairs(step_data or {}) do
 | |
| 		for loc_id, loc in pairs(d.where or {}) do
 | |
| 			if(loc and loc.n_id and loc.n_id == id) then
 | |
| 				used = used + 1
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 	return used
 | |
| end
 | |
| 
 | |
| 
 | |
| -- This order imposed here on the quest steps is the one in which the
 | |
| -- quest steps have to be solved - as far as we can tell (the quest
 | |
| -- may be in the process of beeing created and not logicly complete yet).
 | |
| yl_speak_up.get_sorted_quest_step_list_by_prev_step = function(pname, q_id)
 | |
| 	if(not(q_id) or not(yl_speak_up.quests[q_id]) or not(yl_speak_up.quests[q_id].step_data)) then
 | |
| 		return {}
 | |
| 	end
 | |
| 
 | |
| 	-- for now: sort alphabeticly
 | |
| 	local step_data = yl_speak_up.quests[q_id].step_data
 | |
| 	local liste = {}
 | |
| 	for k, v in pairs(step_data) do
 | |
| 		table.insert(liste, k)
 | |
| 	end
 | |
| 	table.sort(liste)
 | |
| 	return liste
 | |
| end
 | |
| 
 | |
| --[[ still belongs to the above function
 | |
| 	-- add back links (or rather: forward links to quest steps that get enabled)
 | |
| 	local connected = table.copy(step_data)
 | |
| 	for k, v in pairs(connected) do
 | |
| 		-- will store the inverse data
 | |
| 		connected[k].inv_one_step_required = {}
 | |
| 		connected[k].inv_all_steps_require = {}
 | |
| 	end
 | |
| 	for k, v in pairs(connected) do
 | |
| 		if(k and v and v.one_step_required) then
 | |
| 			for i, s in ipairs(v.one_step_required) do
 | |
| 				table.insert(connected[s].inv_one_step_required, k)
 | |
| 			end
 | |
| 		end
 | |
| 		if(k and v and v.all_steps_required) then
 | |
| 			for i, s in ipairs(v.all_steps_required) do
 | |
| 				table.insert(connected[s].inv_all_steps_required, k)
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 	-- get those quest steps that are not connected and not used yet
 | |
| 	local liste = {}
 | |
| 	for k, v in pairs(connected) do
 | |
| 		if(k and v
 | |
| 		  and #v.one_step_required == 0 and #v.all_steps_required == 0
 | |
| 		  and #v.inv_one_step_required == 0 and #v.inv_all_steps_required == 0) then
 | |
| 			table.insert(liste, k)
 | |
| 		end
 | |
| 	end
 | |
| 	-- sort alphabeticly
 | |
| 	table.sort(liste)
 | |
| 	-- remove those entries from our connection table (they're not connected anyway);
 | |
| 	-- we have already added them to the beginning of the list
 | |
| 	for i, v in ipairs(liste) do
 | |
| 		connected[v] = nil
 | |
| 	end
 | |
| 
 | |
| 
 | |
|         return liste
 | |
| end             
 | |
| --]]
 | |
| 
 | |
| 
 | |
| -- helper function: find out if a quest step is required by other quest steps
 | |
| yl_speak_up.quest_step_get_required_for_steps = function(step_data)
 | |
| 	local required_for_steps = {}
 | |
| 	for s, d in pairs(step_data) do
 | |
| 		required_for_steps[s] = {}
 | |
| 	end
 | |
| 	for s, d in pairs(step_data) do
 | |
| 		if(s and d and d.one_step_required and type(d.one_step_required) == "table") then
 | |
| 			for i, s2 in ipairs(d.one_step_required) do
 | |
| 				table.insert(required_for_steps[s2], s)
 | |
| 			end
 | |
| 		end
 | |
|                 if(s and d and d.all_steps_required and type(d.all_steps_required) == "table") then
 | |
| 			for i, s2 in ipairs(d.all_steps_required) do
 | |
| 				table.insert(required_for_steps[s2], s)
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 	return required_for_steps
 | |
| end
 | |
| 
 | |
| 
 | |
| yl_speak_up.input_fs_add_quest_steps = function(player, formname, fields)
 | |
| 	if(not(fields) or not(player)) then
 | |
| 		return
 | |
| 	end
 | |
| 	local res = yl_speak_up.player_is_working_on_quest(player)
 | |
| 	if(res.error_msg) then
 | |
| 		return show_error_fs(player, res.error_msg)
 | |
| 	end
 | |
| 	local pname = res.pname
 | |
| 	local q_id  = res.q_id
 | |
| 	local current_step = res.current_step
 | |
| 	local step_data    = res.step_data
 | |
| 	local quest        = res.quest
 | |
| 
 | |
| 	if(fields.back_from_error_msg) then
 | |
| 		yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 		return
 | |
| 	end
 | |
| 	local mode = yl_speak_up.speak_to[pname].quest_step_mode
 | |
| 	if(fields.back) then
 | |
| 		-- go back to quest overview
 | |
| 		if(mode and (mode == "manage_quest_npcs" or mode == "manage_quest_locations")) then
 | |
| 			return yl_speak_up.show_fs(player, "manage_quests")
 | |
| 		end
 | |
| 		return yl_speak_up.show_fs(player, "manage_quest_steps", current_step)
 | |
| 	end
 | |
| 
 | |
| 	-- has a quest step be selected?
 | |
| 	local work_step = nil
 | |
| 	if(fields.add_element and fields.add_element_name) then
 | |
| 		if(    mode and mode == "manage_quest_npcs") then
 | |
| 			-- manually entered an NPC ID
 | |
| 			local npc_id = fields.add_element_name or ""
 | |
| 			-- just check if it is *potentially* an NPC ID; that way NPC the quest
 | |
| 			-- creator has no write access to can be added
 | |
| 			if(string.sub(npc_id, 1, 2) ~= "n_"
 | |
| 			  or not(tonumber(string.sub(npc_id, 3)))) then
 | |
| 				return show_error_fs(player, "This is not an NPC ID. They have the form n_<id>.")
 | |
| 			end
 | |
| 			-- only npcs that are not yet added (and we store IDs without n_ prefix)
 | |
| 			local id = tonumber(string.sub(npc_id, 3))
 | |
| 			if(id and table.indexof(res.quest.npcs or {}, id) == -1) then
 | |
| 				table.insert(yl_speak_up.quests[q_id].npcs, id)
 | |
| 				yl_speak_up.save_quest(q_id)
 | |
| 			end
 | |
| 			return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 		elseif(mode and mode == "manage_quest_locations") then
 | |
| 			-- manually entered a quest location
 | |
| 			local location_id = fields.add_element_name or ""
 | |
| 			local d = yl_speak_up.player_vars["$NPC_META_DATA$"][location_id]
 | |
| 			local error_msg = ""
 | |
| 			-- the owner is not checked; that way, locations can be added where the
 | |
| 			-- quest onwer does not (yet) have write access
 | |
| 			if(string.sub(location_id, 1, 1) ~= "p") then
 | |
| 				error_msg = "This is not a location ID."
 | |
| 			elseif(not(d)) then
 | |
| 				error_msg = "Location not found."
 | |
| 			end
 | |
| 			if(error_msg ~= "") then
 | |
| 				return show_error_fs(player, error_msg)
 | |
| 			end
 | |
| 			-- only locations that are not yet added
 | |
| 			if(table.indexof(res.quest.locations or {}, location_id) == -1) then
 | |
| 				table.insert(yl_speak_up.quests[q_id].locations, location_id)
 | |
| 				yl_speak_up.save_quest(q_id)
 | |
| 			end
 | |
| 			return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 		end
 | |
| 
 | |
| 		-- create a new quest step
 | |
| 		local new_step = fields.add_element_name:trim()
 | |
| 		-- a new one shall be created
 | |
| 		local msg = yl_speak_up.quest_step_add_quest_step(pname, q_id, new_step)
 | |
| 		if(msg ~= "OK") then
 | |
| 			return show_error_fs(player, msg)
 | |
| 		end
 | |
| 		-- this will also be set if the quest step exists already; this is fine so far
 | |
| 		work_step = new_step
 | |
| 
 | |
| 	elseif(fields.add_from_available
 | |
| 	  and yl_speak_up.speak_to[pname].list_available) then
 | |
| 		-- selected a quest step from the list of available steps offered
 | |
| 		local liste = yl_speak_up.speak_to[pname].list_available
 | |
| 		local selected = minetest.explode_table_event(fields.add_from_available)
 | |
| 		if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
 | |
| 			work_step = liste[selected.row - 1]
 | |
| 		end
 | |
| 
 | |
| 	elseif(fields.add_to_npc_list
 | |
| 	  and yl_speak_up.speak_to[pname].list_available) then
 | |
| 		-- selected an NPC from the list of available NPC offered
 | |
| 		local liste = yl_speak_up.speak_to[pname].list_available
 | |
| 		local selected = minetest.explode_table_event(fields.add_to_npc_list)
 | |
| 		if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
 | |
| 			local npc_id = liste[selected.row - 1]
 | |
| 			if(table.indexof(res.quest.npcs or {}, npc_id) == -1) then
 | |
| 				table.insert(yl_speak_up.quests[q_id].npcs, npc_id)
 | |
| 				yl_speak_up.save_quest(q_id)
 | |
| 			end
 | |
| 		end
 | |
| 		return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 
 | |
| 	elseif(fields.add_to_location_list
 | |
| 	  and yl_speak_up.speak_to[pname].list_available) then
 | |
| 		-- selected a location from the list of available locations offered
 | |
| 		local liste = yl_speak_up.speak_to[pname].list_available
 | |
| 		local selected = minetest.explode_table_event(fields.add_to_location_list)
 | |
| 		if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
 | |
| 			local location_id = liste[selected.row - 1]
 | |
| 			if(table.indexof(res.quest.locations or {}, location_id) == -1) then
 | |
| 				table.insert(yl_speak_up.quests[q_id].locations, location_id)
 | |
| 				yl_speak_up.save_quest(q_id)
 | |
| 			end
 | |
| 		end
 | |
| 		return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 
 | |
| 	elseif(fields.delete_from_one_step_required and current_step and step_data[current_step]) then
 | |
| 		-- remove a quest step from the list (from one step required)
 | |
| 		local selected = minetest.explode_table_event(fields.delete_from_one_step_required)
 | |
| 		local liste = (step_data[current_step].one_step_required or {})
 | |
| 		if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
 | |
| 			table.remove(yl_speak_up.quests[q_id].step_data[current_step].one_step_required, selected.row-1)
 | |
| 		end
 | |
| 		yl_speak_up.save_quest(q_id)
 | |
| 		return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 
 | |
| 	elseif(fields.delete_from_all_steps_required and current_step and step_data[current_step]) then
 | |
| 		-- remove a quest step from the lists (from all steps required)
 | |
| 		local selected = minetest.explode_table_event(fields.delete_from_all_steps_required)
 | |
| 		local liste = (step_data[current_step].all_steps_required or {})
 | |
| 		if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
 | |
| 			table.remove(yl_speak_up.quests[q_id].step_data[current_step].all_steps_required, selected.row-1)
 | |
| 		end
 | |
| 		yl_speak_up.save_quest(q_id)
 | |
| 		return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 
 | |
| 	elseif(fields.delete_from_npc_list) then
 | |
| 		-- remove an NPC from the list of contributors
 | |
| 		local selected = minetest.explode_table_event(fields.delete_from_npc_list)
 | |
| 		local liste = (res.quest.npcs or {})
 | |
| 		if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
 | |
| 			-- *can* it be removed, or is it needed somewhere?
 | |
| 			local full_id = "n_"..tostring(liste[selected.row - 1])
 | |
| 			if(yl_speak_up.count_used_in_quest_steps(full_id, step_data) > 0) then
 | |
| 				return show_error_fs(player, "This NPC is needed for setting a quest step.")
 | |
| 			end
 | |
| 			table.remove(yl_speak_up.quests[q_id].npcs, selected.row - 1)
 | |
| 		end
 | |
| 		yl_speak_up.save_quest(q_id)
 | |
| 		return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 
 | |
| 	elseif(fields.delete_from_location_list) then
 | |
| 		-- remove a location from the list of contributors
 | |
| 		local selected = minetest.explode_table_event(fields.delete_from_location_list)
 | |
| 		local liste = (res.quest.locations or {})
 | |
| 		if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
 | |
| 			-- *can* it be removed, or is it needed somewhere?
 | |
| 			local full_id = liste[selected.row - 1]
 | |
| 			if(yl_speak_up.count_used_in_quest_steps(full_id, step_data) > 0) then
 | |
| 				return show_error_fs(player, "This location is needed for setting a quest step.")
 | |
| 			end
 | |
| 			table.remove(yl_speak_up.quests[q_id].locations, selected.row - 1)
 | |
| 		end
 | |
| 		yl_speak_up.save_quest(q_id)
 | |
| 		return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| 	end
 | |
| 
 | |
| 	if(not(work_step)) then
 | |
| 		return -- TODO
 | |
| 	elseif(mode == "embedded_select") then
 | |
| 		yl_speak_up.speak_to[t.pname].quest_step = work_step
 | |
| 		return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
 | |
| 	elseif(mode == "assign_quest_step") then
 | |
| 		-- TODO: what if there's already a step assigned?
 | |
| 		-- actually add the step
 | |
| 		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
 | |
| 		-- this saves the quest data as well if needed
 | |
| 		local msg = yl_speak_up.quest_step_add_where(pname, q_id, work_step,
 | |
| 						{n_id = n_id, d_id = d_id, o_id = o_id})
 | |
| 		if(msg ~= "OK") then
 | |
| 			return show_error_fs(player, msg)
 | |
| 		end
 | |
| 		if(not(n_id)) then
 | |
| 			return show_error_fs(player, "NPC or location not found.")
 | |
| 		end
 | |
| 		-- store the new connection in the NPC file itself (do not load generic dialogs)
 | |
| 		local dialog = yl_speak_up.load_dialog(n_id, false)
 | |
| 		if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id)) then
 | |
| 			-- ok - the tables exist, so we can store the connection
 | |
| 			dialog.n_dialogs[d_id].d_options[o_id].quest_id   = quest.var_name
 | |
| 			dialog.n_dialogs[d_id].d_options[o_id].quest_step = work_step
 | |
| 			-- write it back to disc
 | |
| 			yl_speak_up.save_dialog(n_id, dialog)
 | |
| 		else
 | |
| 			return show_error_fs(player, "Failed to save this quest step for this NPC.")
 | |
| 		end
 | |
| 		-- the player is working on the NPC - thus, the NPC may be in a modified stage
 | |
| 		-- that hasn't been written to disc yet, and we need to adjust this stage as well
 | |
| 		dialog = yl_speak_up.speak_to[pname].dialog
 | |
| 		if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id)) then
 | |
| 			-- ok - the tables exist, so we can store the connection
 | |
| 			dialog.n_dialogs[d_id].d_options[o_id].quest_id   = quest.var_name
 | |
| 			dialog.n_dialogs[d_id].d_options[o_id].quest_step = work_step
 | |
| 			yl_speak_up.speak_to[pname].dialog = dialog
 | |
| 		else
 | |
| 			return show_error_fs(player, "Failed to update NPC.")
 | |
| 		end
 | |
| 		-- show the newly created or selected step
 | |
| 		yl_speak_up.speak_to[pname].quest_step = work_step
 | |
| 		-- log the change
 | |
| 		yl_speak_up.log_change(pname, n_id,
 | |
| 			"Dialog "..tostring(d_id)..": Option "..tostring(o_id)..
 | |
| 			" will now set quest step \""..
 | |
| 			tostring(dialog.n_dialogs[d_id].d_options[o_id].quest_step)..
 | |
| 			"\" of quest \""..
 | |
| 			tostring(dialog.n_dialogs[d_id].d_options[o_id].quest_id).."\".")
 | |
| 		return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
 | |
| 	elseif(not(current_step) or not(step_data[current_step])) then
 | |
| 		return yl_speak_up.show_fs(player, "manage_quests")
 | |
| 	end
 | |
| 
 | |
| 	local required_for_steps = yl_speak_up.quest_step_get_required_for_steps(step_data)
 | |
| 
 | |
| 	-- make sure we have a sane data structure
 | |
| 	for i, s in ipairs({current_step, work_step}) do
 | |
| 		if(s and yl_speak_up.quests[q_id].step_data[s]) then
 | |
| 			if(not(yl_speak_up.quests[q_id].step_data[s].one_step_required)) then
 | |
| 				yl_speak_up.quests[q_id].step_data[s].one_step_required = {}
 | |
| 			end
 | |
| 			if(not(yl_speak_up.quests[q_id].step_data[s].all_steps_required)) then
 | |
| 				yl_speak_up.quests[q_id].step_data[s].all_steps_required = {}
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 	-- actually do the work
 | |
| 	if(mode == "add_to_one_needed") then
 | |
| 		table.insert(yl_speak_up.quests[q_id].step_data[current_step].one_step_required, work_step)
 | |
| 	elseif(mode == "add_to_all_needed") then
 | |
| 		table.insert(yl_speak_up.quests[q_id].step_data[current_step].all_steps_required, work_step)
 | |
| 	elseif(mode == "insert_after_prev_step") then
 | |
| 		-- the work_step requires what the current step used to require
 | |
| 		if(#step_data[current_step].one_step_required == 1) then
 | |
| 			-- a clear insert is possible
 | |
| 			yl_speak_up.quests[q_id].step_data[work_step].one_step_required = {
 | |
| 				step_data[current_step].one_step_required[1]}
 | |
| 			yl_speak_up.quests[q_id].step_data[current_step].one_step_required[1] = work_step
 | |
| 		else
 | |
| 			-- no useful information on what the new work_step ought to depend on;
 | |
| 			-- we just insert the new step at the first place
 | |
| 			table.insert(yl_speak_up.quests[q_id].step_data[current_step].one_step_required,
 | |
| 					1, work_step)
 | |
| 		end
 | |
| 		return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
 | |
| 	elseif(mode == "insert_before_next_step") then
 | |
| 		-- the work_step requires the current_step
 | |
| 		table.insert(yl_speak_up.quests[q_id].step_data[work_step].one_step_required, 1, current_step)
 | |
| 		-- the current step has exactly one successor? then we adjust that one
 | |
| 		if(#required_for_steps[current_step] == 1) then
 | |
| 			local next_step = required_for_steps[current_step][1]
 | |
| 			local i = table.indexof(step_data[next_step].one_step_required,  current_step)
 | |
| 			local a = table.indexof(step_data[next_step].all_steps_required, current_step)
 | |
| 			if(i > -1) then
 | |
| 				-- is it in one_step_required? -> replace current_step with work_step
 | |
| 				yl_speak_up.quests[q_id].step_data[next_step].one_step_required[i] = work_step
 | |
| 			elseif(a > -1) then
 | |
| 				-- or in all_steps_required? -> replace current_step with work_step
 | |
| 				yl_speak_up.quests[q_id].step_data[next_step].all_steps_required[i] =work_step
 | |
| 			end
 | |
| 		end
 | |
| 		return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
 | |
| 	end
 | |
| 	yl_speak_up.save_quest(q_id)
 | |
| 	return yl_speak_up.show_fs(player, "add_quest_steps")
 | |
| end
 | |
| 
 | |
| 
 | |
| -- small helper function for yl_speak_up.get_fs_add_quest_steps;
 | |
| -- lists all the quest steps found in liste in the order they occour there
 | |
| yl_speak_up.quest_step_list_show_table = function(formspec, table_specs, liste, data, required_for_steps)
 | |
| 	table.insert(formspec, "tablecolumns["..
 | |
| 		"color;text,align=right;".. -- #d.one_step_required
 | |
| 		"color;text,align=right;".. -- #d.all_steps_required
 | |
| 		"color;text,align=right;".. -- #required_for_steps (quest steps that need this one)
 | |
| 		"color;text,align=right;".. -- #where (locations/NPC that *set* this quest step)
 | |
| 		"color;text,align=left"..  -- name of quest step
 | |
| 		"]table[")
 | |
| 	table.insert(formspec, table_specs)
 | |
| 	table.insert(formspec,"#FFFFFF,(O),#FFFFFF,(A),#FFFFFF,(U),#FFFFFF,(L),#FFFFFF,Name of step:,")
 | |
| 	local tmp = {}
 | |
| 	for i, s in ipairs(liste or {}) do
 | |
| 		local d = data[s]
 | |
| 		if(not(d.one_step_required) or type(d.one_step_required) ~= "table") then
 | |
| 			d.one_step_required = {}
 | |
| 		end
 | |
| 		grey_if_zero(tmp, #d.one_step_required)
 | |
| 		if(not(d.all_steps_required) or type(d.all_steps_required) ~= "table") then
 | |
| 			d.all_steps_required = {}
 | |
| 		end
 | |
| 		grey_if_zero(tmp, #d.all_steps_required)
 | |
| 		if(not(required_for_steps[s])) then
 | |
| 			required_for_steps[s] = {}
 | |
| 		end
 | |
| 		grey_if_zero(tmp, #required_for_steps[s])
 | |
| 
 | |
| 		if(not(d.where) or type(d.where) ~= "table") then
 | |
| 			d.where = {}
 | |
| 		end
 | |
| 		local anz_where = 0
 | |
| 		for k, v in pairs(d.where) do
 | |
| 			anz_where = anz_where + 1
 | |
| 		end
 | |
| 		grey_if_zero(tmp, anz_where)
 | |
| 
 | |
| 		table.insert(tmp, "#AAFFAA")
 | |
| 		table.insert(tmp, minetest.formspec_escape(s))
 | |
| 	end
 | |
| 	table.insert(formspec, table.concat(tmp, ","))
 | |
| 	table.insert(formspec, ";]")
 | |
| end
 | |
| 
 | |
| 
 | |
| -- returns list of NPCs that pname can edit and that are not yet part of quest_npc_list
 | |
| yl_speak_up.quest_get_npc_candidate_list = function(pname, quest_npc_liste)
 | |
| 	-- build a list of candidates
 | |
| 	local npc_list = {}
 | |
| 	for k, v in pairs(yl_speak_up.npc_list) do
 | |
| 		-- only NPC that are not already added
 | |
| 		if(table.indexof(quest_npc_liste or {}, k) == -1
 | |
| 		  -- and only those that the player can edit
 | |
| 		  and (v.owner == pname or (v.may_edit and v.may_edit[pname]))) then
 | |
| 			table.insert(npc_list, k)
 | |
| 		end
 | |
| 	end
 | |
| 	table.sort(npc_list)
 | |
| 	return npc_list
 | |
| end
 | |
| 
 | |
| 
 | |
| -- lists npc that are either already added or could be added
 | |
| -- can also handle locations
 | |
| yl_speak_up.quest_npc_show_table = function(formspec, table_specs, liste, step_data, is_location_list)
 | |
| 	table.insert(formspec, "tablecolumns["..
 | |
| 		"color;text,align=right;".. -- used in this many quest steps
 | |
| 		"color;text,align=left;".. -- n_id (number, for NPC) or p_(-185,3,-146) (for locations)
 | |
| 		"color;text,align=left;".. -- owner
 | |
| 		"color;text,align=left"..  -- name of NPC
 | |
| 		"]table[")
 | |
| 	table.insert(formspec, table_specs)
 | |
| 	if(is_location_list) then
 | |
| 		table.insert(formspec,"#FFFFFF,Used:,#FFFFFF,PositionID:,#FFFFFF,Name")
 | |
| 	else
 | |
| 		table.insert(formspec,"#FFFFFF,Used:,#FFFFFF,n_id:,#FFFFFF,Name")
 | |
| 	end
 | |
| 	table.insert(formspec, minetest.formspec_escape(","))
 | |
| 	table.insert(formspec, " description:,#FFFFFF,Owner:,")
 | |
| 	local tmp = {}
 | |
| 	for i, n_id in ipairs(liste or {}) do
 | |
| 		local full_id = n_id
 | |
| 		if(not(is_location_list)) then
 | |
| 			full_id = "n_"..tostring(n_id)
 | |
| 		end
 | |
| 		grey_if_zero(tmp, yl_speak_up.count_used_in_quest_steps(full_id, step_data))
 | |
| 		-- the n_id of the NPC
 | |
| 		table.insert(tmp, "#AAFFAA")
 | |
| 		if(is_location_list) then
 | |
| 			-- this already encodes the position but contains , and ()
 | |
| 			table.insert(tmp, minetest.formspec_escape(n_id))
 | |
| 		else
 | |
| 			table.insert(tmp, "n_"..minetest.formspec_escape(n_id))
 | |
| 		end
 | |
| 		-- get information from the NPC list (see fs_npc_list.lua)
 | |
| 		local owner = "- ? -"
 | |
| 		local name  = "- ? -"
 | |
| 		if(yl_speak_up.npc_list[n_id]) then
 | |
| 			local npc = yl_speak_up.npc_list[n_id]
 | |
| 			owner = npc.owner
 | |
| 			name = (npc.name or name)
 | |
| 			if(npc.desc and npc.desc ~= "") then
 | |
| 				name = name..', '..(npc.desc or "")
 | |
| 			end
 | |
|                 end
 | |
| 		-- name and description of the NPC
 | |
| 		table.insert(tmp, "#AAFFAA")
 | |
| 		table.insert(tmp, minetest.formspec_escape(name))
 | |
| 		-- owner of the NPC
 | |
| 		table.insert(tmp, "#AAFFAA")
 | |
| 		table.insert(tmp, minetest.formspec_escape(owner))
 | |
| 	end
 | |
| 	table.insert(formspec, table.concat(tmp, ","))
 | |
| 	table.insert(formspec, ";]")
 | |
| end
 | |
| 
 | |
| 
 | |
| -- returns list of locations that pname can edit and that are not yet part of quest_location_list
 | |
| yl_speak_up.quest_get_location_candidate_list = function(pname, quest_location_liste)
 | |
| 	-- build a list of candidates of locations
 | |
| 	local location_list = {}
 | |
| 	for n_id, v in pairs(yl_speak_up.player_vars["$NPC_META_DATA$"] or {}) do
 | |
| 		-- TODO: better detection would be helpful
 | |
| 		if(string.sub(n_id, 1, 1) == "p"
 | |
| 		  -- only locations that are not yet added
 | |
| 		  and table.indexof(quest_location_liste or {}, n_id) == -1
 | |
| 		  -- and only those that the player can edit
 | |
| 		  and (v.owner == pname or (v.may_edit and v.may_edit[pname]))) then
 | |
| 			table.insert(location_list, n_id)
 | |
| 		end
 | |
| 	end
 | |
| 	table.sort(location_list)
 | |
| 	return location_list
 | |
| end
 | |
| 
 | |
| 
 | |
| -- param is unused
 | |
| yl_speak_up.get_fs_add_quest_steps = function(player, param)
 | |
| 	local res = yl_speak_up.player_is_working_on_quest(player)
 | |
| 	if(res.error_msg) then
 | |
| 		return yl_speak_up.build_fs_quest_edit_error(res.error_msg, "back")
 | |
|         end
 | |
| 	local pname = res.pname
 | |
| 	local step_data = res.step_data or {}
 | |
| 
 | |
| 
 | |
| 	-- find out if a quest step is required by other quest steps
 | |
| 	local required_for_steps = yl_speak_up.quest_step_get_required_for_steps(step_data)
 | |
| 
 | |
| 	local current_step = nil
 | |
| 	local this_step_data = nil
 | |
| 	if(pname and yl_speak_up.speak_to[pname] and yl_speak_up.speak_to[pname].quest_step) then
 | |
| 		current_step = yl_speak_up.speak_to[pname].quest_step
 | |
| 		this_step_data = step_data[current_step]
 | |
| 	end
 | |
| 	local mode = ""
 | |
| 	if(param) then
 | |
| 		mode = param
 | |
| 		yl_speak_up.speak_to[pname].quest_step_mode = param
 | |
| 	elseif(pname and yl_speak_up.speak_to[pname] and yl_speak_up.speak_to[pname].quest_step_mode) then
 | |
| 		mode = yl_speak_up.speak_to[pname].quest_step_mode
 | |
| 	end
 | |
| 
 | |
| 	local add_what = "Add a new quest step named:"
 | |
| 	if(mode == "manage_quest_npcs") then
 | |
| 		add_what = "Add the NPC with n_<id>:"
 | |
| 	elseif(mode == "manage_quest_locations") then
 | |
| 		add_what = "Add a location by entering its ID directly:"
 | |
| 	end
 | |
| 
 | |
| 	local formspec = {}
 | |
| 	if(mode and mode == "embedded_select") then
 | |
| 		table.insert(formspec, "size[30,12]container[6,0;18.5,12]")
 | |
| 		current_step = nil
 | |
| 	else
 | |
| 		table.insert(formspec, "size[18.5,17.3]")
 | |
| 	end
 | |
| 
 | |
| 	-- add back button
 | |
| 	table.insert(formspec, "button[8,0;2,0.7;back;Back]")
 | |
| 	-- show which quest we're working at
 | |
| 	table.insert(formspec, "label[0.2,1.0;Quest ID:]")
 | |
| 	table.insert(formspec, "label[3.0,1.0;")
 | |
| 	table.insert(formspec, minetest.formspec_escape(res.q_id))
 | |
| 	table.insert(formspec, "]")
 | |
| 	table.insert(formspec, "label[0.2,1.5;Quest name:]")
 | |
| 	table.insert(formspec, "label[3.0,1.5;")
 | |
| 	table.insert(formspec, minetest.formspec_escape(res.quest.name or "- unknown -"))
 | |
| 	table.insert(formspec, "]")
 | |
| 
 | |
| 	-- add new quest step
 | |
| 	table.insert(formspec, "label[0.2,2.2;")
 | |
| 	table.insert(formspec, add_what)
 | |
| 	table.insert(formspec, "]")
 | |
| 	table.insert(formspec, "button[16.1,2.4;1.2,0.7;add_element;Add]")
 | |
| 	table.insert(formspec, "field[1.0,2.4;15,0.7;add_element_name;;]")
 | |
| 
 | |
| 	local y_pos = 3.3
 | |
| 	if(current_step and mode == "insert_after_prev_step") then
 | |
| 		local prev_step = "-"
 | |
| 		if(this_step_data and this_step_data.one_step_required and #this_step_data.one_step_required > 0) then
 | |
| 			prev_step = this_step_data.one_step_required[1]
 | |
| 		end
 | |
| 		table.insert(formspec, "label[0.2,3.3;between the previous step:]")
 | |
| 		table.insert(formspec, "label[1.0,3.7;")
 | |
| 		table.insert(formspec, minetest.colorize("#AAFFAA", minetest.formspec_escape(prev_step)))
 | |
| 		table.insert(formspec, "]")
 | |
| 		table.insert(formspec, "label[0.2,4.1;and the currently selected step:]")
 | |
| 		table.insert(formspec, "label[1.0,4.5;")
 | |
| 		table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
 | |
| 		table.insert(formspec, "]")
 | |
| 		y_pos = 5.3
 | |
| 	elseif(current_step and mode == "insert_before_next_step") then
 | |
| 		local next_step = "-"
 | |
| 		if(current_step and required_for_steps[current_step] and #required_for_steps[current_step] > 0) then
 | |
| 			next_step = required_for_steps[current_step][1]
 | |
| 		end
 | |
| 		table.insert(formspec, "label[0.2,3.3;between the currently selected step:]")
 | |
| 		table.insert(formspec, "label[1.0,3.7;")
 | |
| 		table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
 | |
| 		table.insert(formspec, "]")
 | |
| 		table.insert(formspec, "label[0.2,4.1;and the next step:]")
 | |
| 		table.insert(formspec, "label[1.0,4.5;")
 | |
| 		table.insert(formspec, minetest.colorize("#AAFFAA", minetest.formspec_escape(next_step)))
 | |
| 		table.insert(formspec, "]")
 | |
| 		y_pos = 5.3
 | |
| 	elseif(current_step and mode == "add_to_one_needed") then
 | |
| 		table.insert(formspec, "label[0.2,3.3;as a requirement to the currently selected step:]")
 | |
| 		table.insert(formspec, "label[1.0,3.7;")
 | |
| 		table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
 | |
| 		table.insert(formspec, "]")
 | |
| 		table.insert(formspec, "label[0.2,4.1;so that "..
 | |
| 					minetest.colorize("#9999FF", "at least one")..
 | |
| 					" of these requirements is fulfilled:]")
 | |
| 		yl_speak_up.quest_step_list_show_table(formspec,
 | |
| 					"0.2,4.3;17.0,3.0;delete_from_one_step_required;",
 | |
| 					step_data[current_step].one_step_required,
 | |
| 					step_data, required_for_steps)
 | |
| 		table.insert(formspec, "label[0.2,7.5;(Click on an entry to delete it from the list above.)]")
 | |
| 		y_pos = 8.3
 | |
| 	elseif(current_step and mode == "add_to_all_needed") then
 | |
| 		table.insert(formspec, "label[0.2,3.3;as a requirement to the currently selected step:]")
 | |
| 		table.insert(formspec, "label[1.0,3.7;")
 | |
| 		table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
 | |
| 		table.insert(formspec, "]")
 | |
| 		table.insert(formspec, "label[0.2,4.1;so that "..
 | |
| 					minetest.colorize("#9999FF", "all")..
 | |
| 					" of these requirements are fulfilled:]")
 | |
| 		yl_speak_up.quest_step_list_show_table(formspec,
 | |
| 					"0.2,4.3;17.0,3.0;delete_from_all_steps_required;",
 | |
| 					step_data[current_step].all_steps_required,
 | |
| 					step_data, required_for_steps)
 | |
| 		table.insert(formspec, "label[0.2,7.5;(Click on an entry to delete it from the list above.)]")
 | |
| 		y_pos = 8.3
 | |
| 	-- add a quest step to an NPC or location
 | |
| 	elseif(mode == "assign_quest_step") then
 | |
| 		table.insert(formspec, "container[0,3.3;17,4]")
 | |
| 		-- what are we talking about?
 | |
| 		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
 | |
| 		-- describe where in the dialog of the NPC or location this quest step shall be set
 | |
| 		yl_speak_up.quest_step_show_where_set(pname, formspec, "which will be set by ", n_id, d_id, o_id, nil, nil)
 | |
| 		table.insert(formspec, "container_end[]")
 | |
| 		y_pos = 7.8
 | |
| 
 | |
| 	-- which NPC may contribute to the quest?
 | |
| 	elseif(mode == "manage_quest_npcs") then
 | |
| 		table.insert(formspec, "container[0,3.3;18,6]")
 | |
| 		table.insert(formspec, "label[0.2,0;so that the NPC "..
 | |
| 					minetest.colorize("#9999FF", "may contribute")..
 | |
| 					" to the quest like these NPC:]")
 | |
| 		yl_speak_up.quest_npc_show_table(formspec,
 | |
| 					"0.2,0.2;17.0,3.0;delete_from_npc_list;",
 | |
| 					res.quest.npcs or {},
 | |
| 					step_data, false)
 | |
| 		table.insert(formspec, "label[0.2,3.4;(Click on an entry to delete it from the list above.)]")
 | |
| 		local available_npcs = yl_speak_up.quest_get_npc_candidate_list(pname, res.quest.npcs or {})
 | |
| 		yl_speak_up.speak_to[pname].list_available = available_npcs
 | |
| 		table.insert(formspec, "label[0.2,4.4;or select an NPC from the list below:]")
 | |
| 		yl_speak_up.quest_npc_show_table(formspec,
 | |
| 					"0.2,4.6;17.0,6.0;add_to_npc_list;",
 | |
| 					available_npcs or {}, step_data, false)
 | |
| 		table.insert(formspec, "label[0.2,10.8;Used: Shows in how many quest steps this NPC is used.]")
 | |
| 		table.insert(formspec, "container_end[]")
 | |
| 		return table.concat(formspec, "")
 | |
| 	-- which locations may contribute to the quest?
 | |
| 	elseif(mode == "manage_quest_locations") then
 | |
| 		table.insert(formspec, "container[0,3.3;18,6]")
 | |
| 		table.insert(formspec, "label[0.2,0;so that the location "..
 | |
| 					minetest.colorize("#9999FF", "may contribute")..
 | |
| 					" to the quest like these locations:]")
 | |
| 		yl_speak_up.quest_npc_show_table(formspec,
 | |
| 					"0.2,0.2;17.0,3.0;delete_from_location_list;",
 | |
| 					res.quest.locations or {},
 | |
| 					step_data, true)
 | |
| 		table.insert(formspec, "label[0.2,3.4;(Click on an entry to delete it from the list above.)]")
 | |
| 		local available_locations = yl_speak_up.quest_get_location_candidate_list(pname, res.quest.locations or {})
 | |
| 		yl_speak_up.speak_to[pname].list_available = available_locations
 | |
| 		table.insert(formspec, "label[0.2,4.4;or select a location from the list below:]")
 | |
| 		yl_speak_up.quest_npc_show_table(formspec,
 | |
| 					"0.2,4.6;17.0,6.0;add_to_location_list;",
 | |
| 					available_locations or {}, step_data, true)
 | |
| 		table.insert(formspec, "label[0.2,10.8;Used: Shows in how many quest steps this location is used.]")
 | |
| 		table.insert(formspec, "container_end[]")
 | |
| 		return table.concat(formspec, "")
 | |
| 	end
 | |
| 
 | |
| 	-- some quest steps may not be available/may not make sense
 | |
| 	local not_available = {}
 | |
| 	if(current_step and step_data[current_step] and (not(mode) or mode ~= "assign_quest_step")) then
 | |
| 		-- steps that are already required
 | |
| 		for i, s in ipairs(step_data[current_step].one_step_required or {}) do
 | |
| 			not_available[s] = true
 | |
| 		end
 | |
| 		for i, s in ipairs(step_data[current_step].all_steps_required or {}) do
 | |
| 			not_available[s] = true
 | |
| 		end
 | |
| 		-- steps that directly require this quest step here
 | |
| 		for i, s in ipairs(required_for_steps[current_step] or {}) do
 | |
| 			not_available[s] = true
 | |
| 		end
 | |
| 	end
 | |
| 	if(current_step and (not(mode) or mode ~= "assign_quest_step")) then
 | |
| 		not_available[current_step] = true
 | |
| 	end
 | |
| 	-- build a list of candidates
 | |
| 	local available_steps = {}
 | |
| 	for k, v in pairs(step_data) do
 | |
| 		if(not(not_available[k])) then
 | |
| 			table.insert(available_steps, k)
 | |
| 		end
 | |
| 	end
 | |
| 	table.sort(available_steps)
 | |
| 	yl_speak_up.speak_to[pname].list_available = available_steps
 | |
| 
 | |
| 	table.insert(formspec, "container[0,")
 | |
| 	table.insert(formspec, tostring(y_pos))
 | |
| 	table.insert(formspec, ";30,20]")
 | |
| 
 | |
| 	table.insert(formspec, "label[0.2,0;or select an existing quest step from the list below")
 | |
| 	if(mode and mode == "embedded_select") then
 | |
| 		table.insert(formspec, minetest.colorize("#9999FF", " to display the step")..":]")
 | |
| 	else
 | |
| 		table.insert(formspec, ":]")
 | |
| 	end
 | |
| 	yl_speak_up.quest_step_list_show_table(formspec,
 | |
| 		"0.2,0.2;17,6.0;add_from_available;",
 | |
| 		available_steps,
 | |
| 		step_data, required_for_steps)
 | |
| 	table.insert(formspec, "label[0.2,6.5;Legend: The numbers show the amount of quest steps...\n"..
 | |
| 			"\t(O) from which (o)ne needs to be achieved for this quest step\n"..
 | |
| 			"\t(A) that (a)ll need to be achieved for this quest step\n"..
 | |
| 			"\t(U) that require/(u)se this quest step in some form\n"..
 | |
| 			"\t(L) Number of locations (npc/places) that "..
 | |
| 				minetest.colorize("#9999FF", "set").." this quest step]")
 | |
| 	table.insert(formspec, "container_end[]")
 | |
| 
 | |
| 	return table.concat(formspec, "")
 | |
| end
 |