Jump to content

play sound command (ATTN BECK)


ETsneak
Go to solution Solved by BECK,

Recommended Posts

I saw on IOW server they have "!play x" where x is the name of the wav file. I'm wondering if there's some lua for this or special way to write the command block into the shrubbot config? How can this be done? Seems like it would be easier than making a custom chat menu.

Link to comment
Share on other sites

  • Subscriber

What he means is we have stuff like:  !play goodgame   or !play welose     or !play fu  or other stuff like that.   So one custom command that accepts a parameter that determines the sound that is played.

 

Our !play command is like this:

[command]
command  = ^bplay
exec     = play [i] [1]
desc     = Play a sound
syntax   = 
levels   = 0 1 2 3 4 5

But it does use a custom LUA script that configures what sounds are available and at what shrubbot level.  We did it because there is (was?) a limit of 63 custom commands and we wanted WAY more.     I suppose you could do what fanboi has with one change:

[command]
command  = ^bplay
exec     = playsound sound/misc/[1].wav
desc     = Play a sound
syntax   = 
levels   = 0 1 2 3 4 5

that might work - but then people would have to know what sounds you have in your custom pak.

 

EDIT:

Oh, and because we manage this through LUA and config files, we also have a !listsounds command that displays the sounds available for each shrubbot level.

Edited by BECK
Link to comment
Share on other sites

  • Subscriber
  • Solution

heh, you just assume :)   Petty arguments aside, I can share it here for you or anyone else who wants to do custom sounds this way.  The advantage of this is that you can restrict certain sounds by shrubbot level.  We use this as a small benefit for regulars, vip regulars and members.

 

In full disclosure, another member of IoW wrote these scripts, so I can't really help you if things aren't working or this is poorly written or wrong or causes your server to crash.  Use at your own risk!   All I know is, it works...

 

First you need formatting.lua.  I think it was supposed to be a general purpose utility script that is shared by other scripts.

function makeDisplayString(text, color, length, alignment)

	-- Initialize display string
	local displayString

	-- Get the display length of the text
	local textDisplayLength = displayLength(text)

	-- If the text display length fits in the desired display length
	if textDisplayLength <= length then

		-- Create enough padding to make the text the correct length for display
		local padding = ""
		for i = 1, length - textDisplayLength do
			padding = padding .. ' '
		end

		-- If alignment is set to centered
		if alignment == "center" and string.len(padding) >= 2 then
			-- Find the middle of the padding and put each half on one side of the string
			local paddingSplitPoint = math.floor(string.len(padding) / 2)
			displayString = string.sub(padding, 1, paddingSplitPoint) .. text .. string.sub(padding, paddingSplitPoint + 1)

		-- Otherwise, if alignment was set to right
		elseif alignment == "right" then
			-- Put the padding at the beginning of the string
			displayString = padding .. text

		else
			-- Otherwise add the padding to the end of the string (left aligned)
			displayString = text .. padding
		end

	-- If the text is too big to fit in the desired display length
	else
		-- If the display length was specified as a very low number (so can't display the text properly)
		if length <= 3 then
			-- Fill the display length with dots
			displayString = ""
			for i = 1, length do
				displayString = displayString .. '.'
			end

		-- Otherwise, the display length has enough space to show part of the text
		else
			-- Create a table to hold parts of the text (chunks beginning with a color code [if possible])
			local textTable = {}

			-- Split up the text into color coded pieces until there is no more text
			local moreText = true

			while moreText do
				-- Try to find the first occurence of a color code
				local firstColorCodeLocation = string.find(text, "%^[^%^]")

				-- Initialize text part
				local textPart

				-- If there are no color codes in the text
				if not firstColorCodeLocation then
					-- Set the text part to the entire text and specify that there is no more text to add
					textPart = text
					moreText = false

				-- Otherwise, a color code was found
				else
					-- If the first color code is not at the beginning of the text
					if firstColorCodeLocation > 1 then
						-- Set the text part to the text leading up to the first color code
						textPart = string.sub(text, 1, firstColorCodeLocation - 1)

					-- Otherwise, the first color code is at the beginning of the text
					else
						-- Attempt to find the second color code
						local secondColorCodeLocation = string.find(text, "%^[^%^]", firstColorCodeLocation + 2)

						-- If the second color code is found
						if secondColorCodeLocation then
							-- Set the text part to be the text leading up to the second color code (including the first one)
							textPart = string.sub(text, 1, secondColorCodeLocation - 1)
						else
							-- Otherwise, set the text part to the entire remaining text and specify that there is no more text to add
							textPart = text
							moreText = false
						end
					end

					-- After determining a text part for the table remove that part from the text
					text = string.sub(text, string.len(textPart) + 1)
				end

				-- Add the text part to the table
				table.insert(textTable, textPart)
			end

			-- Set the amount of text needed to fill the desired display length (not including the ellipses)
			local displayTextNeeded = length - 3

			-- Initialize the display string that will hold the display text
			displayString = ""

			-- While more text is needed
			while displayTextNeeded > 0 do
				-- Get the next text part from the table and remove the table entry for it
				local nextTextPart = textTable[1]
				table.remove(textTable, 1)

				-- If the text part begins with a color code
				if string.find(nextTextPart, "%^[^%^]") then
					-- Get the amount of text needed from the text part (or as much as possible) and update the text needed
					nextTextPart = string.sub(nextTextPart, 1, displayTextNeeded + 2)
					displayTextNeeded = displayTextNeeded - (string.len(nextTextPart) - 2)
				-- Otherwise, there is no color code in the text part
				else
					-- Get the amount of text needed from the text part (or as much as possible) and update the text needed
					nextTextPart = string.sub(nextTextPart, 1, displayTextNeeded)
					displayTextNeeded = displayTextNeeded - string.len(nextTextPart)
				end

				-- Add the text part to the display string
				displayString = displayString .. nextTextPart
			end

			-- Now that no more text is needed, add ellipses to show that it didn't fit
			displayString = displayString .. colorString("...", color)
		end
	end

	displayString = colorString(displayString, color)

	-- return the formatted string
	return displayString
end

function colorString(text, color)
	if color then
		-- If text does not begin with a color code
		if string.find(text, "%^[^%^]") ~= 1 then
			-- Add the color code to the beginning of the text
			text = '^' .. color .. text
		end
	end

	-- Return text with color code
	return text
end

function displayLength(text)
	-- Initialize the display length to the full length of the text
	local textDisplayLength = string.len(text)

	-- Loop through each color code
	for colorCode in string.gmatch(text, "%^[^%^]") do
		-- Subtract the color code characters from the display length
		textDisplayLength = textDisplayLength - string.len(colorCode)
	end

	return textDisplayLength
end

function makeLine(character, length)
	-- Initialize the line string
	local line = ""

	-- Add characters until the string is as long as specified
	for i = 1, length do
		line = line .. character
	end

	-- Return the print line
	return line
end

function trim(s, side)
	-- Initialize the number of spaces removed to 0
	local i = 0

	-- If the right side was not specified
	if side ~= 'r' then
		-- While there are spaces at the beginning of the string
		while string.find(s, "^%s") do
			-- Remove the first space and increment counter
			s = string.gsub(s, "^%s", "")
			i = i + 1
		end
	end

	-- If the left side was not specified
	if side ~= 'l' then
		-- While there are spaces at the end of the string
		while string.find(s, "%s$") do
			-- Remove the last space and increment counter
			s = string.gsub(s, "%s$", "")
			i = i + 1
		end
	end

	-- Return the trimmed string and the amount of spaces removed
	return s, i
end

function printETLine(clientNum, text)
	-- Print the specified text into the specified client's console
    if clientNum ~= "[i]" then
        et.trap_SendServerCommand(clientNum, 'print "' .. text .. '\n"')
    else 
        et.G_Print( text )
    end
end

function printMessage(clientNum, text, messageType)
	-- Print the specified text into the specified client's console
    if clientNum ~= "[i]" then
        et.trap_SendServerCommand(clientNum, messageType .. ' "' .. text .. '"')
    else
        et.G_Print( text )
    end
end

Then playsound.lua contains the command implementation

require("formatting")

-- Initialize a table to hold all possible sounds
soundTable = {}

-- Initialize a table to hold all players playsound cvar values
playsoundCvarTable = {}

function et_InitGame(levelTime, randomSeed, restart)
	-- Read sounds into soundTable
	local configFile, length = et.trap_FS_FOpenFile("playsound.cfg", et.FS_READ)

	-- If there were no errors opening the file
	if length ~= -1 then
		-- Read data from config file and close it
		local soundData = et.trap_FS_Read(configFile, length)
		et.trap_FS_FCloseFile(configFile)

		-- For each sound block in config file
		for soundBlock in string.gmatch(soundData, "[%w%-_]+[%s\n\r]+%b{}") do

			-- Get the sound command name
			local sound = string.match(soundBlock, "[%w%-_]+")

			-- Initialize parameter table for the sound command
			local soundParameterTable = {}

			-- Get level required from the sound block
			soundParameterTable["level"] = tonumber(string.sub(string.match(soundBlock, "level:[%d_]+"), 7))

			-- Get a list of sound filenames from the sound block
			local soundFilenameList = {}
			for soundFilenameLine in string.gmatch(soundBlock, "sound:[%w%-_]+.wav") do
				local soundFilename = string.sub(soundFilenameLine, 7)
				table.insert(soundFilenameList, soundFilename)
			end
			soundParameterTable["sounds"] = soundFilenameList

			-- Add the sound parameters to the sound table
			soundTable[string.lower(sound)] = soundParameterTable
		end
	end
end

function et_CvarValue(clientNum, cvar, value)
	-- Add the cvar value to the script cvar table
	playsoundCvarTable[clientNum][cvar] = value
end

function et_ClientConnect(clientNum, firstTime, isBot)
	-- Querry server to get cvar value
	et.G_QueryClientCvar(clientNum, "playsound_method")

	-- Initialize the playsound_method cvar to nil since we do not know the value yet
	local clientCvarValueTable = {}

	-- Add client to the cvar table
	playsoundCvarTable[clientNum] = clientCvarValueTable

	-- Return nil to indicate that the player was not rejected by this function
	return nil
end

function et_ClientDisconnect(clientNum)
	-- Remove the player from the playsound cvar table
	playsoundCvarTable[clientNum] = nil
end


function et_ConsoleCommand(command)
	-- If client executed play command from console
	if string.lower(et.trap_Argv(0)) == "play" then

		-- If there were at least 3 parameters (including command name)
		if et.trap_Argc() >= 3 then

			-- Get client's admin level
            local clientNum=0
            local clientLevel=0        
            if et.trap_Argv(1) == "[i]" then
                -- When CONSOLE executes this command, there is no clientNum.  So we force the value to 50
                -- Avoids a server crash, which is always good 
                clientLevel = 50
            else
                clientNum = et.trap_Argv(1)
                clientLevel = et.G_shrubbot_level(clientNum)
            end            

			-- Get sound client passed as a parameter
			local sound = et.trap_Argv(2)

			-- Get the parameter table for the requested sound
			local soundParameterTable = soundTable[string.lower(sound)]

			-- If the sound exists
			if soundParameterTable then

				-- Get the level required to play the sound
				local level = soundParameterTable["level"]
				
				-- If the client has high enough level
				if clientLevel >= level then

					-- Choose a random sound from the parameters
					local soundFilename = soundParameterTable["sounds"][math.random(# soundParameterTable["sounds"])]

					-- -- Play the sound
					-- local soundPath = "sound/playsound/" .. soundFilename
					-- -- et.trap_SendConsoleCommand(et.EXEC_NOW, soundCommand)
					-- et.G_globalSound(soundPath)

					-- Loop through the clients and play sound based on their cvar playsound_method
					for slot, clientCvarValueTable in pairs(playsoundCvarTable) do
						local playsoundMethod = clientCvarValueTable["playsound_method"] or "sound"
						if playsoundMethod == "sound" then
							-- Play the sound for the client
							local soundPath = "sound/playsound/" .. soundFilename
							local soundIndex = et.G_SoundIndex(soundPath)
							et.G_ClientSound(slot, soundIndex)
						end
						if playsoundMethod == "text" then

							local soundText = soundParameterTable["text"]
							-- Send message to the client showing the text version of the sound command
							printETLine(slot, soundText)
							printETLine(slot, "test")
						end
					end

				else
					-- Otherwise warn client that they do not have high enough level
					printETLine(clientNum, "^oplay: ^7Permission denied")
				end
			else
				-- Otherwise inform client that the sound doesn't exist
				printETLine(clientNum, "^oplay: ^7The sound you specified does not exist")
			end

		else
			-- Otherwise inform client that they need to specify a sound name
			printETLine(clientNum, "^oplay: ^7You must specify a sound name")
		end

		-- Return 1 to indicate that the command was handled by this function
		return 1
	end

	if string.lower(et.trap_Argv(0)) == "listsounds" then

		-- Initialize display variables
		local columnSeparator = " "
		local lineCharacter = '-'
		local borderColor = '9'
		local textColor = 'f'
		local columnWidth = 12
		local numberColumns = 6

        -- Get client's admin level
        local clientNum=0
        local clientLevel=0        
        if et.trap_Argv(1) == "[i]" then
            -- When CONSOLE executes this command, there is no clientNum.  So we force the value to 50
            -- Avoids a server crash, which is always good 
            clientLevel = 50
        else
            clientNum = et.trap_Argv(1)
            clientLevel = et.G_shrubbot_level(clientNum)
        end   

		-- Create a table to hold sounds available to the client
		local playerSoundTable = {}

		-- For each sound in the sound table
		for sound, parameterTable in pairs(soundTable) do
			-- If the client has high enough level
			if clientLevel >= parameterTable['level'] then
				-- Add sound to player sound table
				table.insert(playerSoundTable, sound)
			end
		end

		-- Sort player sound table in alphebetical order
		table.sort(playerSoundTable)

		-- Get number of characters that would be removed from the trimmed separator
		local trimmedSeparator, charactersRemoved = trim(columnSeparator)

		-- Determine line length based on number of columns, column width, and the separator display length
		local lineLength = (columnWidth * numberColumns) + (displayLength(columnSeparator) * (numberColumns + 1)) - charactersRemoved

		-- Print a line to act as the top border of the chart
		-- printETLine(clientNum, colorString(makeLine(lineCharacter, lineLength), borderColor))

		-- While there are sounds in the player sound table
		while # playerSoundTable > 0 do

			-- Initialize line holding sound names
			local soundsLine = colorString(trim(columnSeparator, 'l'), borderColor)

			-- For each column
			for i = 1, numberColumns do
				-- Add the sound from the beginning of the list
				soundsLine = soundsLine .. makeDisplayString(playerSoundTable[1] or '', textColor, columnWidth, "left")

				-- If this is not the last column
				if i < numberColumns then
					-- Add a separator (column border)
					soundsLine = soundsLine .. colorString(columnSeparator, borderColor)
				else
					-- Otherwise add a separator with the right side trimmed
					soundsLine = soundsLine .. colorString(trim(columnSeparator, 'r'), borderColor)
				end

				-- Remove first sound from the beginning of the table
				table.remove(playerSoundTable, 1)
			end

			-- Print the sound line followed by a border line
			printETLine(clientNum, soundsLine)
			-- printETLine(clientNum, colorString(makeLine(lineCharacter, lineLength), borderColor))
		end

		-- Return 1 to indicate that the command was handled by this function
		return 1
	end

	-- Return 0 to pass any command for standard et processing
	return 0
end

There is some stuff in here about having clients opt out of the sounds, but I don't think it was ever finished nor does it work AFAIK.    But take them as-is if you like.

 

Next, the format of the playsound.cfg file is this:

wewin
{
	level:1
	sound:the_game.wav
}
...

So you've got the command (as in !play wewin), the minimum shrubbot level required to use the sound (note all higher levels can use it), then the sound file name itself.   The sound file must be in 'sound/playsound/'  in your pk3 file, or else you can change the path coded in the playsound.lua file.  But this way, you can just edit playsound.cfg to add your sounds, the command string and level required to run it.   No end to the amount of sounds you can have.

 

Next, define the commands in your shrubbot.cfg file:

[command]
command  = ^bplay
exec     = play [i] [1]
desc     = Play a sound
syntax   = 
levels   = 0 1 2 3 4 5 

[command]
command  = ^blistsounds
exec     = listsounds [i]
desc     = List all sounds
syntax   = 
levels   = 0 1 2 3 4 5

Finally, be sure to include the LUA scripts in your et config files:

set lua_modules "playsound.lua"
Link to comment
Share on other sites

  • 11 months later...
  • 2 weeks later...
function et_ClientConnect(clientNum, firstTime, isBot)
	-- Querry server to get cvar value
	et.G_QueryClientCvar(clientNum, "playsound_method")

	-- Initialize the playsound_method cvar to nil since we do not know the value yet
	local clientCvarValueTable = {}

	-- Add client to the cvar table
	playsoundCvarTable[clientNum] = clientCvarValueTable

	-- Return nil to indicate that the player was not rejected by this function
	return nil
end

Is playsound_method full cvar name in your example? No cg_ cl_ prefixes?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...