forked from Sokomine/yl_speak_up
		
	implemented '/npc_talk list' including sorting
This commit is contained in:
		
							parent
							
								
									e2ecb7de4b
								
							
						
					
					
						commit
						79797ee27c
					
				
							
								
								
									
										281
									
								
								fs_npc_list.lua
									
									
									
									
									
								
							
							
						
						
									
										281
									
								
								fs_npc_list.lua
									
									
									
									
									
								
							| @ -9,6 +9,16 @@ yl_speak_up.npc_list_objects = {} | ||||
| -- file to store the list of NPC for the "/npc_talk list" command | ||||
| yl_speak_up.npc_list_path = minetest.get_worldpath().."/yl_speak_up_npc_list.data" | ||||
| 
 | ||||
| -- pre-calculated lines for the NPC list (only which ones are shown and how some columns | ||||
| -- are colored varies per player) | ||||
| yl_speak_up.cache_general_npc_list_lines = {} | ||||
| 
 | ||||
| -- we need to remember which NPC was shown to the player in which order; index: player name | ||||
| yl_speak_up.cache_npc_list_per_player = {} | ||||
| 
 | ||||
| -- which table column is the player using for sorting? | ||||
| yl_speak_up.sort_npc_list_per_player = {} | ||||
| 
 | ||||
| -- add/update data about NPC self with dialog dialog (optional) | ||||
| -- force_store ought to be true if something important has been changed | ||||
| yl_speak_up.update_npc_data = function(self, dialog, force_store) | ||||
| @ -32,7 +42,7 @@ yl_speak_up.update_npc_data = function(self, dialog, force_store) | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 	elseif(is_knwon) then | ||||
| 	elseif(is_known) then | ||||
| 		local old = yl_speak_up.npc_list[self.yl_speak_up.id] | ||||
| 		may_edit   = old.may_edit | ||||
| 		desc       = old.desc | ||||
| @ -47,9 +57,13 @@ yl_speak_up.update_npc_data = function(self, dialog, force_store) | ||||
| 	local pos = {} | ||||
| 	if(self.object) then | ||||
| 		pos = self.object:get_pos() | ||||
| 		pos.x = math.floor(pos.x or 0) | ||||
| 		pos.y = math.floor(pos.y or 0) | ||||
| 		pos.z = math.floor(pos.z or 0) | ||||
| 		if(pos) then | ||||
| 			pos = { x = math.floor(pos.x or 0), | ||||
| 				y = math.floor(pos.y or 0), | ||||
| 				z = math.floor(pos.z or 0)} | ||||
| 		else | ||||
| 			pos = { x=0, y=0, z=0} | ||||
| 		end | ||||
| 	end | ||||
| 	-- only store real, important properties | ||||
| 	local properties = {} | ||||
| @ -68,6 +82,8 @@ yl_speak_up.update_npc_data = function(self, dialog, force_store) | ||||
| 		trades     = trades, | ||||
| 		pos        = pos, | ||||
| 		properties = properties, | ||||
| 		created_at = created_at, | ||||
| 		muted      = self.yl_speak_up.talk, | ||||
| 	} | ||||
| 	-- the current object will change after deactivate; there is no point in storing | ||||
| 	-- it over server restart | ||||
| @ -106,6 +122,143 @@ yl_speak_up.command_npc_talk_list = function(pname, rest) | ||||
| 	if(not(pname)) then | ||||
| 		return | ||||
| 	end | ||||
| 
 | ||||
| 	-- check if there are any loaded entities handled by yl_speak_up that | ||||
| 	-- are *not* in the list yet | ||||
| 	local liste = {} | ||||
| 	for k,v in pairs(minetest.luaentities) do | ||||
| 		if(v and v.yl_speak_up and v.yl_speak_up.id) then | ||||
| 			if(not(yl_speak_up.npc_list[v.yl_speak_up.id])) then | ||||
| 				local dialog = yl_speak_up.load_dialog(v.yl_speak_up.id, nil) | ||||
| 				yl_speak_up.update_npc_data(v, dialog, false) | ||||
| 			else | ||||
| 				yl_speak_up.update_npc_data(v, nil, false) | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	-- store the updated list | ||||
| 	yl_speak_up.npc_list_store() | ||||
| 	-- update the information for display | ||||
| 	yl_speak_up.build_cache_general_npc_list_lines() | ||||
| 	-- clear the stored NPC list and calculate it anew | ||||
| 	yl_speak_up.cache_npc_list_per_player[pname] = {} | ||||
| 	-- Note: show_fs cannot be used here as that expects the player to be talking to an actual npc | ||||
| 	yl_speak_up.show_fs_ver(pname, "yl_speak_up:show_npc_list", | ||||
|                         yl_speak_up.get_fs_show_npc_list(pname, nil)) | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| -- allow to sort the npc list, display more info on one NPC etc. | ||||
| yl_speak_up.input_show_npc_list = function(player, formname, fields) | ||||
| 	local pname = player:get_player_name() | ||||
| 	if(fields.show_npc_list) then | ||||
| 		local selected = minetest.explode_table_event(fields.show_npc_list) | ||||
| 		-- sort by column | ||||
| 		if(selected.row == 1) then | ||||
| 			local old_sort = yl_speak_up.sort_npc_list_per_player[pname] or 0 | ||||
| 			-- reverse sort | ||||
| 			if(old_sort == selected.column) then | ||||
| 				yl_speak_up.sort_npc_list_per_player[pname] = -1 * selected.column | ||||
| 			else -- sort by new col | ||||
| 				yl_speak_up.sort_npc_list_per_player[pname] = selected.column | ||||
| 			end | ||||
| 			-- show the update | ||||
| 			yl_speak_up.show_fs_ver(pname, "yl_speak_up:show_npc_list", | ||||
| 		                        yl_speak_up.get_fs_show_npc_list(pname, nil)) | ||||
| 			return | ||||
| 		end | ||||
| 	end | ||||
| 	return | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| -- the entries for the "/npc_talk list" NPC list are generally the same for all | ||||
| -- - except that not all lines are shown to each player and that some | ||||
| -- lines might be colored diffrently | ||||
| yl_speak_up.build_cache_general_npc_list_lines = function() | ||||
| 
 | ||||
| 	-- small helper function to suppress the display of zeros | ||||
| 	local show_if_bigger_null = function(value, do_count) | ||||
| 		if(do_count and value) then | ||||
| 			local anz = 0 | ||||
| 			for k, v in pairs(value) do | ||||
| 				anz = anz + 1 | ||||
| 			end | ||||
| 			value = anz | ||||
| 		end | ||||
| 		if(value and value > 0) then | ||||
| 			return tostring(value) | ||||
| 		else | ||||
| 			return "" | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	-- the real priv names would be far too long | ||||
| 	local short_priv_name = { | ||||
| 	        precon_exec_lua    = 'pX', | ||||
| 		effect_exec_lua    = 'eX', | ||||
| 		effect_give_item   = 'eG', | ||||
| 		effect_take_item   = 'eT', | ||||
| 		effect_move_player = 'eM', | ||||
| 	} | ||||
| 
 | ||||
| 	yl_speak_up.cache_general_npc_list_lines = {} | ||||
| 
 | ||||
| 	for k, data in pairs(yl_speak_up.npc_list) do | ||||
| 		local data = yl_speak_up.npc_list[k] | ||||
| 		local n = (data.name or "- ? -") | ||||
| 		if(data.desc and data.desc ~= "") then | ||||
| 			n = n..', '..(data.desc or "") | ||||
| 		end | ||||
| 		-- is the NPC muted? | ||||
| 		local npc_color = (yl_speak_up.nametag_color_when_not_muted or '#FFFFFF') | ||||
| 		if(data.muted ~= nil and data.muted == false) then | ||||
| 			npc_color = (yl_speak_up.nametag_color_when_muted or '#FFFFFF') | ||||
| 		end | ||||
| 		-- is the NPC loaded? | ||||
| 		local is_loaded_color = '#777777' | ||||
| 		if(yl_speak_up.npc_list_objects[k]) then | ||||
| 			is_loaded_color = '#FFFFFF' | ||||
| 		end | ||||
| 		-- is it a generic NPC? | ||||
| 		local n_id = 'n_'..tostring(k) | ||||
| 		local is_generic = '' | ||||
| 		if(yl_speak_up.generic_dialogs[n_id]) then | ||||
| 			is_generic = 'G' | ||||
| 		end | ||||
| 		-- does the NPC have extra privs? | ||||
| 		local priv_list = '' | ||||
| 		if(yl_speak_up.npc_priv_table[n_id]) then | ||||
| 			for priv, has_it in pairs(yl_speak_up.npc_priv_table[n_id]) do | ||||
| 				priv_list = priv_list..tostring(short_priv_name[priv])..' ' | ||||
| 			end | ||||
| 		end | ||||
| 
 | ||||
| 		yl_speak_up.cache_general_npc_list_lines[k] = { | ||||
| 			id              = k, -- keep for sorting | ||||
| 			is_loaded_color = is_loaded_color, | ||||
| 			n_id            = n_id, | ||||
| 			is_generic      = is_generic, | ||||
| 			npc_color       = npc_color, -- muted or not | ||||
| 			-- npc_color is diffrent for each player | ||||
| 			n_name          = minetest.formspec_escape(n), | ||||
| 			owner           = minetest.formspec_escape(data.owner or '- ? -'), | ||||
| 			is_loaded_color = is_loaded_color, | ||||
| 			anz_trades      = show_if_bigger_null(#data.trades), | ||||
| 			anz_properties  = show_if_bigger_null(data.properties, true), | ||||
| 			anz_editors     = show_if_bigger_null(data.may_edit, true), | ||||
| 			pos             = minetest.formspec_escape(minetest.pos_to_string(data.pos)), | ||||
| 			priv_list       = priv_list, | ||||
| 		} | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| -- allow to toggle between trade entries and full log | ||||
| -- Note: takes pname instead of player(object) as first parameter | ||||
| yl_speak_up.get_fs_show_npc_list = function(pname, param) | ||||
| 	-- which NPC can the player edit? | ||||
| 	local level = 0 | ||||
| 	if(    minetest.check_player_privs(pname, {npc_master=true}) | ||||
| 	    or minetest.check_player_privs(pname, {npc_talk_master=true}) | ||||
| @ -115,32 +268,114 @@ yl_speak_up.command_npc_talk_list = function(pname, rest) | ||||
| 		level = 1 | ||||
| 	end | ||||
| 	if(level < 1) then | ||||
| 		minetest.chat_send_player(pname, "This command requires the npc_talk_owner priv or above.") | ||||
| 		return | ||||
| 		return "size[5,1]label[0,0;Error: You do not have the npc_talk_owner priv.]" | ||||
| 	end | ||||
| 
 | ||||
| 	minetest.chat_send_player(pname, "OK...building list.") | ||||
| 	local formspec_start = 'size[18,12]'.. | ||||
| --			'button[0.5,11.1;17,0.8;', | ||||
| --				back_link, | ||||
| --				']'.. | ||||
| 			'label[4.5,0.5;List of all NPC (that you can edit)]'.. | ||||
| --			'button[0.5,0.1;3,0.8;', | ||||
| --				log_type_switch, | ||||
| --				']', | ||||
| 			'tablecolumns[' .. | ||||
| 				'color;text,align=right;'..	-- the ID | ||||
| 				'color;text,align=center;'..	-- is the NPC a generic one? | ||||
| 				'color;text,align=left;'..	-- the name of the NPC | ||||
| 				'color;text,align=center;'..	-- the name of the owner of the NPC | ||||
| 				'color;text,align=right;'..	-- number of trades offered | ||||
| 				'color;text,align=right;'..	-- number of properties set | ||||
| 				'color;text,align=right;'..	-- number of people who can edit NPC | ||||
| 				'color;text,align=center;'..	-- last known position | ||||
| 				'color;text,align=center]'..	-- does he have extra privs? | ||||
| 			'table[0.1,1.0;17.8,9.8;show_npc_list;' | ||||
| 	local formspec = {} | ||||
| 
 | ||||
| 	-- sort by default by id descending? | ||||
| 
 | ||||
| 	-- check if there are any loaded entities handled by yl_speak_up that | ||||
| 	-- are *not* in the list yet | ||||
| 	local liste = {} | ||||
| 	for k,v in pairs(minetest.luaentities) do | ||||
| 		if(v and v.yl_speak_up and v.yl_speak_up.id | ||||
| 		  and not(yl_speak_up.npc_list[v.yl_speak_up.id])) then | ||||
| 			local dialog = yl_speak_up.load_dialog(v.yl_speak_up.id, nil) | ||||
| 			yl_speak_up.update_npc_data(v, dialog, true) | ||||
| 		end | ||||
| 	end | ||||
| 	-- TODO: blocks may also be talked to | ||||
| 	-- TODO desc as mouseover?, created_at? | ||||
| 	-- TODO: created_at, typ (race?)? | ||||
| 
 | ||||
| 
 | ||||
| 	local tmp_liste = {} | ||||
| 	for k, v in pairs(yl_speak_up.npc_list) do | ||||
| 	-- possible editors: dialog.n_may_edit[pname] | ||||
| 		minetest.chat_send_player(pname, tostring(k).." ".. | ||||
| 			tostring(v.name).." "..tostring(v.owner).. | ||||
| 			" "..minetest.pos_to_string(v.pos)) | ||||
| 		if(level == 2 | ||||
| 		  or (v.owner and v.owner == pname) | ||||
| 		  or (v.may_edit and v.may_edit[pname])) then | ||||
| 			table.insert(tmp_liste, k) | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	-- the columns with the colors count as well even though they can't be selected | ||||
| 	-- (don't sort the first column by n_<id> STRING - sort by <id> NUMBER) | ||||
| 	local col_names = {"id", "id", "is_generic", "is_generic", "n_name", "n_name", | ||||
| 			   "owner", "owner", "anz_trades", "anz_trades", | ||||
| 			   "anz_properties", "anz_properties", "anz_editors", "anz_editors", | ||||
| 			   "pos", "pos", "priv_list", "priv_list"} | ||||
| 	local sort_col = yl_speak_up.sort_npc_list_per_player[pname] | ||||
| 	if(not(sort_col) or sort_col == 0) then | ||||
| 		table.sort(tmp_liste) | ||||
| 	elseif(sort_col > 0) then | ||||
| 		-- it is often more helpful to sort in descending order | ||||
| 		local col_name = col_names[sort_col] | ||||
| 		table.sort(tmp_liste, function(a, b) | ||||
| 				return yl_speak_up.cache_general_npc_list_lines[a][col_name] | ||||
| 				     > yl_speak_up.cache_general_npc_list_lines[b][col_name] | ||||
| 			end) | ||||
| 	else | ||||
| 		local col_name = col_names[sort_col * -1] | ||||
| 		table.sort(tmp_liste, function(a, b) | ||||
| 				return yl_speak_up.cache_general_npc_list_lines[a][col_name] | ||||
| 				     < yl_speak_up.cache_general_npc_list_lines[b][col_name] | ||||
| 			end) | ||||
| 	end | ||||
| 
 | ||||
| 	local col_headers = {'n_id', 'G', 'Name', 'Owner', '#Tr', '#Pr', '#Ed', 'Position', 'Privs'} | ||||
| 	for i, k in ipairs(col_headers) do | ||||
| 		if(    sort_col and sort_col == (i * 2)) then | ||||
| 			table.insert(formspec, 'yellow') | ||||
| 			table.insert(formspec, 'v '..k..' v') | ||||
| 		elseif(sort_col and sort_col == (i * -2)) then | ||||
| 			table.insert(formspec, 'yellow') | ||||
| 			table.insert(formspec, '^ '..k..' ^') | ||||
| 		else | ||||
| 			table.insert(formspec, '#FFFFFF') | ||||
| 			table.insert(formspec, k) | ||||
| 		end | ||||
| 	end | ||||
| 	yl_speak_up.cache_npc_list_per_player = tmp_list | ||||
| 
 | ||||
| 	for i, k in ipairs(tmp_liste) do | ||||
| 		local data = yl_speak_up.npc_list[k] | ||||
| 		local line = yl_speak_up.cache_general_npc_list_lines[k] | ||||
| 		-- own NPC are colored green, others white | ||||
| 		local owner_color = '#FFFFFF' | ||||
| 		if(data.owner == pname) then | ||||
| 			owner_color = '#00FF00' | ||||
| 		elseif (data.may_edit and data.may_edit[pname]) then | ||||
| 			owner_color = '#FFFF00' | ||||
| 		end | ||||
| 		table.insert(formspec, line.is_loaded_color) | ||||
| 		table.insert(formspec, line.n_id) | ||||
| 		table.insert(formspec, 'orange') | ||||
| 		table.insert(formspec, line.is_generic) | ||||
| 		table.insert(formspec, line.npc_color) | ||||
| 		table.insert(formspec, line.n_name) | ||||
| 		table.insert(formspec, owner_color) -- diffrent for each player | ||||
| 		table.insert(formspec, line.owner) | ||||
| 		table.insert(formspec, line.is_loaded_color) | ||||
| 		table.insert(formspec, line.anz_trades) | ||||
| 		table.insert(formspec, line.is_loaded_color) | ||||
| 		table.insert(formspec, line.anz_properties) | ||||
| 		table.insert(formspec, owner_color) -- diffrent for each player | ||||
| 		table.insert(formspec, line.anz_editors) | ||||
| 		table.insert(formspec, line.is_loaded_color) | ||||
| 		table.insert(formspec, line.pos) | ||||
| 		table.insert(formspec, line.is_loaded_color) | ||||
| 		table.insert(formspec, line.priv_list) | ||||
| 	end | ||||
| 	table.insert(formspec, ";1]") | ||||
| 	return formspec_start..table.concat(formspec, ',') | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -60,6 +60,9 @@ yl_speak_up.input_handler = function(player, formname, fields) | ||||
| 	elseif formname == "yl_speak_up:show_log" then | ||||
| 		yl_speak_up.input_show_log(player, formname, fields) | ||||
| 		return true | ||||
| 	elseif formname == "yl_speak_up:show_npc_list" then | ||||
| 		yl_speak_up.input_show_npc_list(player, formname, fields) | ||||
| 		return true | ||||
| 	elseif formname == "yl_speak_up:edit_trade_limit" then | ||||
| 		yl_speak_up.input_edit_trade_limit(player, formname, fields) | ||||
| 		return true | ||||
| @ -342,6 +345,11 @@ yl_speak_up.show_fs = function(player, fs_name, param) | ||||
| 		yl_speak_up.show_fs_ver(pname, "yl_speak_up:show_log", | ||||
| 			yl_speak_up.get_fs_show_log(player, param.log_type)) | ||||
| 
 | ||||
| 	elseif(fs_name == "show_npc_list") then | ||||
| 		yl_speak_up.show_fs_ver(pname, "yl_speak_up:show_npc_list", | ||||
| 			yl_speak_up.get_fs_show_npc_list(player, nil)) | ||||
| 
 | ||||
| 
 | ||||
| 	elseif(fs_name == "edit_trade_limit") then | ||||
| 		if(not(param)) then | ||||
| 			param = {} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user