ok hier di Fehlermeldung:
plugin_sank_sounds.sma(49) Fatal [100]: cannot read from file: "..\include\sound.inc"
Und hier der sma Inhalt:
/***************************************************************************
 * plugin_sank_sounds.sma
 * Author: Luke Sankey
 * Date: March 21, 2001 - Original hard-coded version
 * Date: July 2, 2001   - Rewrote to be text file configurable
 * Last Updated: July 8, 2001
 *
 * This plugin will read from a text file keyword/wav file combinations
 * and when a player says one of the keywords, it will trigger HL to play
 * that wav file to all players. It allows reloading of the file without
 * restarting the current level, as well as adding keyword/wav combinations
 * from the console during gameplay. If it finds it can't read the file, it
 * will automagically set file_access_read to 1 and then set it back to 0
 * when it's done.
 *
 * My most deepest thanks goes to William Bateman (aka HunteR)
 *  
http://thepit.shacknet.nu
 *  
huntercc@hotmail.com
 * For he was the one who got me motivated once again to write this plugin
 * since I don't run a server anymore. And besides that, he helped write
 * parts of it.
 *
 * For technical assistance with this plugin, please see the README.TXT file
 * that was bundled in the zip file.  When that fails (and it will, because
 * I'm the one who wrote it) then please see the adminmod forums at
 * 
http://www.adminmod.org   If they still fail you, email Bill Bateman.
 * As a very last final resort, email me: 
sank@spu.edu
 *
 * I hope you enjoy this new functionality on the old plugin_sank_sounds!
 *
 * Luke Sankey
 * 
sank@spu.edu
 ***************************************************************************/
// Functions included in this plugin
//  admin_sound_add "<keyword>;<dir\wav>"
//  admin_sound_help
//  admin_sound_off
//  admin_sound_on
//  admin_sound_reload [filename]
//  admin_sound_remove "<keyword>;[dir\wav]"
//  admin_sound_write <filename>
#include <core>
#include <console>
#include <string>
#include <admin>
#include <adminlib>
#include <sound>
#include <dead>
#define ACCESS_SOUND 512+64 // Access level for advanced sound commands.
new FILENAME[MAX_DATA_LENGTH] = "SND-LIST.CFG";	// Name of file to parse.
#define MAX_KEYWORDS 40 // Maximum number of keywords
#define MAX_RANDOM 10	// Maximum number of wavs per keyword
#define TOK_LENGTH 40	// Maximum length of keyword and wav file strings
#define NUM_PER_LINE 4	// Number of words per line from admin_sound_help
//#define DEBUG 1
/****************************************************************************/
/****************************************************************************/
/************** DO NOT MODIFY CONTENTS BELOW THIS LINE !!! ******************/
/************** DO NOT MODIFY CONTENTS BELOW THIS LINE !!! ******************/
/************** DO NOT MODIFY CONTENTS BELOW THIS LINE !!! ******************/
/****************************************************************************/
/****************************************************************************/
new STRING_VERSION[MAX_DATA_LENGTH] = "2.50.e";
/****************************************************************************/
/* Holds the number telling how many sounds a player has played             */
new SndCount[MAX_PLAYERS] = {0,...};
/* The number at which a player will get warned for playing too many sounds */
new SND_WARN = 0;
/* The number at which a player will get kicked for playing too many sounds */
new SND_KICK = 0;
/* The wav to play when a person joins the game                             */
new SND_JOIN[TOK_LENGTH] = "";
/* The wav to play when a person exits the game                             */
new SND_EXIT[TOK_LENGTH] = "";
/****************************************************************************/
/****************************************************************************/
/* First column is the indentifier of the Word-Wav Combination              */
/* Second column is the token identifier                                    */
/* Third column is the token (string)                                       */
/* Empty tokens are delimited by empty strings                              */
/****************************************************************************/
/*
[0] [TOK_LENGTH*0] ["crap"]
[0] [TOK_LENGTH*1] ["misc/awwcrap.wav"]
[0] [TOK_LENGTH*2] ["misc/awwcrap2.wav"]
[0] [TOK_LENGTH*3] [""]
[1] [TOK_LENGTH*0] ["woohoo"]
[1] [TOK_LENGTH*1] ["misc/woohoo.wav"]
[1] [TOK_LENGTH*2] [""]
[2] [TOK_LENGTH*0] ["ha ha"]
[2] [TOK_LENGTH*1] ["misc/haha.wav"]
[2] [TOK_LENGTH*2] [""]
[3] [TOK_LENGTH*0] ["doh"]
[3] [TOK_LENGTH*1] ["misc/doh.wav"]
[3] [TOK_LENGTH*2] ["misc/doh2.wav"]
[3] [TOK_LENGTH*3] ["misc/doh3.wav"]
[3] [TOK_LENGTH*4] ["misc/doh4.wav"]
[3] [TOK_LENGTH*5] [""]
[4] [TOK_LENGTH*0] [""]
...
*/
new WordWavCombo[MAX_KEYWORDS][TOK_LENGTH*(MAX_RANDOM+1)];
/****************************************************************************/
new LastSoundTime = 0; // Used for very limited spam protection
new bSoundsEnabled = 1;	// Used for admin_sound_on and admin_sound_off
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
public plugin_init()
{
	plugin_registerinfo("Sank Sounds Plugin", "Responds to certain chat messages by playing a sound.", STRING_VERSION);
	
	plugin_registercmd("say", "HandleSay", ACCESS_ALL);
	plugin_registercmd("admin_sound_add", "admin_sound_add", ACCESS_SOUND, "admin_sound_add ^"<keyword>;<dir\wav>^" : Adds a word/wav combo for the duration of the level.");
	plugin_registercmd("admin_sound_help", "admin_sound_help", ACCESS_ALL, "admin_sound_help: Lists all sounds associated with say command");
	plugin_registercmd("admin_sound_off", "admin_sound_off", ACCESS_SOUND, "admin_sound_off:  Turns off sounds.");
	plugin_registercmd("admin_sound_on", "admin_sound_on", ACCESS_SOUND, "admin_sound_on:  Turns on sounds.");
	plugin_registercmd("admin_sound_reload", "admin_sound_reload", ACCESS_SOUND, "admin_sound_reload: Re-parses sound list config file.");
	plugin_registercmd("admin_sound_remove", "admin_sound_remove", ACCESS_SOUND, "admin_sound_remove ^"<keyword>;[dir\wav]^" : Removes a word/wav combo for the duration of the level.");
	plugin_registercmd("admin_sound_write", "admin_sound_write", ACCESS_SOUND, "admin_sound_write:  Writes current sound configuration to file.");
#if defined (DEBUG)
	plugin_registercmd("admin_sound_debug", "print_matrix", ACCESS_SOUND, "admin_sound_debug");
#endif
	parse_sound_file(FILENAME);
	
	return PLUGIN_CONTINUE;
}
public plugin_connect(HLUserName, HLIP, UserIndex) 
{
	playsoundall(SND_JOIN);
	if (UserIndex >= 1 && UserIndex <= MAX_PLAYERS)
		SndCount[UserIndex] = 0;
	return PLUGIN_CONTINUE;
}
public plugin_disconnect(HLUserName, UserIndex) 
{
	if (UserIndex >= 1 && UserIndex <= MAX_PLAYERS)
		SndCount[UserIndex] = 0;
	playsoundall(SND_EXIT);  /* moeglicher Bot-Crash */
	return PLUGIN_CONTINUE;
}
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// Adds a Word/Wav combo to the list. If it is a valid line in the config
// file, then it is a valid parameter here. The only difference is you can
// only specify one .wav file at a time with this command.
//
// Usage: admin_sound_add "<keyword>;<dir\wav>"
// Usage: admin_sound_add "<setting>;<value>"
//////////////////////////////////////////////////////////////////////////////
public admin_sound_add(HLCommand, HLData, HLUserName, UserIndex)
{
	new Data[MAX_DATA_LENGTH];
	new User[MAX_NAME_LENGTH];
	new Word[TOK_LENGTH];
	new Wav[TOK_LENGTH];
	new Text[MAX_TEXT_LENGTH];
	new i;
	new j;
	convert_string(HLData, Data, MAX_DATA_LENGTH);
	convert_string(HLUserName, User, MAX_NAME_LENGTH);
	// Parse command line
	strsplit(Data, ";", Word, TOK_LENGTH, Wav, TOK_LENGTH);
	// Remove whitespace
	strtrim(Word, " ^t");
	strtrim(Wav, " ^t");
	if(strlen(Data) == 0 || strlen(Wav) == 0)
	{
		selfmessage("Invalid format.");
		selfmessage("USAGE: admin_sound_add ^"<keyword>;<dir\wav>^"");
		return PLUGIN_HANDLED;
	}
	// First look for special parameters
	if (!strcasecmp(Data, "SND_KICK"))
		SND_KICK = strtonum(Wav);
	else if (!strcasecmp(Data, "SND_WARN"))
		SND_WARN = strtonum(Wav);
	else if (!strcasecmp(Data, "SND_JOIN"))
		strcpy(SND_JOIN, Wav, TOK_LENGTH);
	else if (!strcasecmp(Data, "SND_EXIT"))
		strcpy(SND_EXIT, Wav, TOK_LENGTH);
	// Loop once for each keyword
	for(i=0; i<MAX_KEYWORDS; i++)
	{
		// If an empty string, then break this loop
		if(strlen(WordWavCombo
) == 0)
			break;
		// If we find a match, then add on the new wav data
		if(strncmp(Word, WordWavCombo, TOK_LENGTH) == 0)
		{
			// See if the wav already exists
			for(j=1; j<=MAX_RANDOM; j++)
			{
				// If an empty string, then break this loop
				if (strlen(WordWavCombo[TOK_LENGTH*j]) == 0)
					break;
				// See if this is the same as the new wav
				if(strncmp(Wav, WordWavCombo[TOK_LENGTH*j], TOK_LENGTH) == 0)
				{
					snprintf(Text, MAX_TEXT_LENGTH, "%s; %s already exists.", Word, Wav);
					selfmessage(Text);
					return PLUGIN_HANDLED;
				}
			}
			// If we reached the end, then there is no room
			if(j == MAX_RANDOM)
				selfmessage("No room for new wav. Increase MAX_RANDOM.");
			else
			{
				// Word exists, but Wav is new to the list, so add entry
				strcpy(WordWavCombo[TOK_LENGTH*j], Wav, TOK_LENGTH);
				snprintf(Text, MAX_TEXT_LENGTH, "%s successfully added to %s", Wav, Word);
				selfmessage(Text);
			}
			return PLUGIN_HANDLED;
		}
	}
	// If we reached the end, then there is no room
	if(i == MAX_KEYWORDS)
		selfmessage("No room for new Word/Wav combo. Increase MAX_KEYWORDS");
	else
	{
		// Word/Wav combo is new to the list, so make a new entry
		strcpy(WordWavCombo[TOK_LENGTH*0], Word, TOK_LENGTH);
		strcpy(WordWavCombo[TOK_LENGTH*1], Wav, TOK_LENGTH);
		snprintf(Text, MAX_TEXT_LENGTH, "%s; %s successfully added.", Word, Wav);
		selfmessage(Text);
	}
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// Instead of using admin_help, which uses static data, admin_sound_help
// always lists the most up-to-date information because the Word/Wav list can
// change  the middle of gameplay. admin_sound_help lists all admin_sound
// commands and keywords to the user.
//
// Usage: admin_sound_help
//////////////////////////////////////////////////////////////////////////////
public admin_sound_help(HLCommand,HLData,HLUserName,UserIndex)
{
	new User[MAX_NAME_LENGTH];
	new Text[MAX_TEXT_LENGTH] = "";
	new i = 0;
	convert_string(HLUserName,User,MAX_NAME_LENGTH);
	selfmessage("admin_sound_help: Shows this text.");
	selfmessage("admin_sound_on: Turns on playing of wav files with this plugin.");
	selfmessage("admin_sound_off: Turns off playing of wav files with this plugin.");
	selfmessage("admin_sound_reload [filename] : Reloads config file. Filename is optional.");
	selfmessage("admin_sound_add ^"<keyword>;<dir\wav>^" : Adds a Word/Wav combo to the sound list. Must use quotes");
	selfmessage("admin_sound_remove ^"<keyword>;[dir\wav]^" : Removes a Word/Wav combo from the list. Must use quotes");
	selfmessage("say <keyword>: Plays a sound. Keywords are listed below:");
	// Loop once for each keyword
	for(i=0; i<MAX_KEYWORDS; i++)
	{
		// If an invalid string, then break this loop
		if( (strlen(WordWavCombo) == 0) || (strlen(WordWavCombo) > TOK_LENGTH) )
			break;
		strcat(Text, WordWavCombo, MAX_TEXT_LENGTH);
		strcat(Text, " ^t ^t", MAX_TEXT_LENGTH);
		if(i % NUM_PER_LINE == NUM_PER_LINE - 1)
		{
			// We got NUM_PER_LINE on this line,
			//  so print it and start on the next line
			selfmessage(Text);
			Text[0] = 0;
		}
	}
	if(strlen(Text) != 0)
		selfmessage(Text);	
	
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// Turns off the playing of the wav files for this plugin only.
//
// Usage: admin_sound_off
//////////////////////////////////////////////////////////////////////////////
public admin_sound_off(HLCommand, HLData, HLUserName, UserIndex)
{
	bSoundsEnabled = 0;
	say("Sank Sounds Plugin has been disabled!");
	playsoundall("misc\awwcrap.wav"); /* moeglicher Bot-Crash */
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// Turns on the playing of the wav files for this plugin only.
//
// Usage: admin_sound_on
//////////////////////////////////////////////////////////////////////////////
public admin_sound_on(HLCommand, HLData, HLUserName, UserIndex)
{
	bSoundsEnabled = 1;
	say("Sank Sounds Plugin has been enabled!");
	playsoundall("misc\woohoo.wav"); /* moeglicher Bot-Crash */
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// Reloads the Word/Wav combos from filename.
//
// Usage: admin_sound_reload [filename]
//////////////////////////////////////////////////////////////////////////////
public admin_sound_reload(HLCommand, HLData, HLUserName, UserIndex)
{
	new parsefile[MAX_DATA_LENGTH];
	convert_string(HLData, parsefile, MAX_DATA_LENGTH);
	// Initialize WordWavCombo[][][] array
	new i;
	for(i = 0; i < MAX_KEYWORDS; i++)
		WordWavCombo[i][0] = 0;
	parse_sound_file(parsefile);
	
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// Removes a Word/Wav combo from the list. You must specify a keyword, but it
// is not necessary to specify a wav if you want to remove all wavs associated
// with that keyword. Surrounding quotes are required.
//
// Usage: admin_sound_remove "<keyword>;[wav]"
//////////////////////////////////////////////////////////////////////////////
public admin_sound_remove(HLCommand, HLData, HLUserName, UserIndex)
{
	new Data[MAX_DATA_LENGTH];
	new User[MAX_NAME_LENGTH];
	new Word[TOK_LENGTH];
	new Wav[TOK_LENGTH];
	new Text[MAX_TEXT_LENGTH];
	new iCurWord;
	new jCurWav;
	convert_string(HLData, Data, MAX_DATA_LENGTH);
	convert_string(HLUserName, User, MAX_NAME_LENGTH);
	// Parse command line
	strsplit(Data, ";", Word, TOK_LENGTH, Wav, TOK_LENGTH);
	// Remove whitespace
	strtrim(Word, " ^t");
	strtrim(Wav, " ^t");
	if(strlen(Data) == 0)
	{
		selfmessage("Invalid format.");
		selfmessage("USAGE: admin_sound_remove ^"<keyword>;[dir\wav]^"");
		return PLUGIN_HANDLED;
	}
	// Loop once for each keyword
	for(iCurWord=0; iCurWord<MAX_KEYWORDS; iCurWord++)
	{
		// If an empty string, then break this loop, we're at the end
		if(strlen(WordWavCombo[iCurWord]) == 0)
			break;
		// Look for a Word match
		if(strncmp(Word, WordWavCombo[iCurWord], TOK_LENGTH) == 0)
		{
			// If no wav was specified, then remove the whole word's entry
			if(strlen(Wav) == 0)
			{
				// Keep looping i, copying the next into the current
				for(; iCurWord<MAX_KEYWORDS; iCurWord++)
				{
					// If we're about to copy a string that doesn't exist,
					//  then just erase the last string instead of copying.
					if (iCurWord >= MAX_KEYWORDS-1)
					{
						// Delete the last word string 
						WordWavCombo[iCurWord][0] = 0;
						// We reached the end
						snprintf(Text, MAX_TEXT_LENGTH, "%s successfully removed.", Word);
						selfmessage(Text);
						return PLUGIN_HANDLED;
					}
					else
					{
						// Copy the next string over the current string
						for(jCurWav=0; jCurWav<TOK_LENGTH*(MAX_RANDOM+1); jCurWav++)
							WordWavCombo[iCurWord][jCurWav] = WordWavCombo[iCurWord+1][jCurWav];
					}
				}
			}
			else
			{
				// Just remove the one wav, if it exists
				for(jCurWav=1; jCurWav<=MAX_RANDOM; jCurWav++)
				{
					// If an empty string, then break this loop, we're at the end
					if (strlen(WordWavCombo[iCurWord][TOK_LENGTH*jCurWav]) == 0)
						break;
					// Look for a Wav match
					if(strncmp(Wav, WordWavCombo[iCurWord][TOK_LENGTH*jCurWav], TOK_LENGTH) == 0)
					{
						for(; jCurWav<=MAX_RANDOM; jCurWav++)
						{
							// If this is the only wav entry, then remove the entry altogether
							if ( (jCurWav == 1) && (strlen(WordWavCombo[iCurWord][TOK_LENGTH*(jCurWav+1)]) == 0) )
							{
								// Keep looping i, copying the next into the current
								for(; iCurWord<MAX_KEYWORDS; iCurWord++)
								{
									// If we're about to copy a string that doesn't exist,
									//  then just erase the last string instead of copying.
									if (iCurWord >= MAX_KEYWORDS-1)
									{
										// Delete the last word string 
										WordWavCombo[iCurWord][0] = 0;
										// We reached the end
										snprintf(Text, MAX_TEXT_LENGTH, "%s successfully removed.", Word);
										selfmessage(Text);
										return PLUGIN_HANDLED;
									}
									else
									{
										// Copy the next string over the current string
										for(jCurWav=0; jCurWav<TOK_LENGTH*(MAX_RANDOM+1); jCurWav++)
											WordWavCombo[iCurWord][jCurWav] = WordWavCombo[iCurWord+1][jCurWav];
									}
								}
							}
							// If we're about to copy a string that doesn't exist,
							//  then just erase the last string instead of copying.
							if(jCurWav >= MAX_RANDOM)
							{
								// Delete the last wav string
								WordWavCombo[iCurWord][TOK_LENGTH*jCurWav] = 0;
								// We reached the end
								snprintf(Text, MAX_TEXT_LENGTH, "%s successfully removed from %s.", Wav, Word);
								selfmessage(Text);
								return PLUGIN_HANDLED;
							}
							else
							{
								// Copy the next string over the current string
								strcpy(WordWavCombo[iCurWord][TOK_LENGTH*jCurWav], WordWavCombo[iCurWord][TOK_LENGTH*(jCurWav+1)], TOK_LENGTH);
							}
						}
					}
				}
				// We reached the end for this word, and the wav didn't exist
				snprintf(Text, MAX_TEXT_LENGTH, "%s not found.",  Wav);
				selfmessage(Text);
				return PLUGIN_HANDLED;
			}
		}
	}
	// We reached the end, and the word didn't exist
	snprintf(Text, MAX_TEXT_LENGTH, "%s not found.", Word);
	selfmessage(Text);
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// Saves the current configuration of Word/Wav combos to filename for possible
// reloading at a later time. You cannot overwrite the default file.
//
// Usage: admin_sound_write <filename>
//////////////////////////////////////////////////////////////////////////////
public admin_sound_write(HLCommand, HLData, HLUserName, UserIndex)
{
	new savefile[MAX_DATA_LENGTH];
	new User[MAX_NAME_LENGTH];
	new Text[MAX_TEXT_LENGTH];
	new TimeStamp[MAX_TEXT_LENGTH];
	new bSuccess = 1;
	new i;
	new j;
	convert_string(HLData, savefile, MAX_DATA_LENGTH);
	convert_string(HLUserName, User, MAX_NAME_LENGTH);
	servertime(TimeStamp, MAX_NUMBER_LENGTH, "%H:%M %B %d, %Y");
	// If the filename is NULL, then that's bad.
	if (strlen(savefile) == 0)
	{
		selfmessage("Sound Plugin >> You must specify a filename.");
		return PLUGIN_HANDLED;
	}
	// If the filename is the same as the default FILENAME, then that's bad.
	if (strcasecmp(savefile, FILENAME) == 0)
	{
		selfmessage("Sound Plugin >> Illegal write to default sound config file.");
		selfmessage("Sound Plugin >> Specify a different filename.");
		return PLUGIN_HANDLED;
	}
	/************ File should have the following format: **************
	# TimeStamp:		07:15 January 15, 2001
	# File created by:	[SPU]Crazy_Chevy
	
	# Important parameters:
	SND_KICK;	30
	SND_WARN;	17
	SND_JOIN;	
	SND_EXIT;	misc\comeagain.wav
	# Word/Wav combinations:
	crap;	misc\awwcrap.wav;misc\awwcrap2.wav
	woohoo;	misc\woohoo.wav
	ha ha;	misc\haha.wav
	doh;	misc\doh.wav;misc\doh2.wav;misc\doh3.wav
	******************************************************************/
	// See if we have file_access_write
	if (!resetfile(savefile))
	{
		snprintf(Text, MAX_TEXT_LENGTH, "Sound Plugin >> Cannot write to %s", savefile);
		selfmessage(Text);
		selfmessage("Sound Plugin >> Make sure file_access_write is set to 1.");
		return PLUGIN_HANDLED;
	}
	// We now assume that we can write to the file, so here we go!
	snprintf(Text, MAX_TEXT_LENGTH, "# TimeStamp:^t^t%s", TimeStamp);
	writefile(savefile, Text);
	snprintf(Text, MAX_TEXT_LENGTH, "# File created by:^t%s", User);
	writefile(savefile, Text);
	writefile(savefile, "");	// blank line
	writefile(savefile, "# Important parameters:");
	snprintf(Text, MAX_TEXT_LENGTH, "SND_KICK;^t%d", SND_KICK);
	writefile(savefile, Text);
	snprintf(Text, MAX_TEXT_LENGTH, "SND_WARN;^t%d", SND_WARN);
	writefile(savefile, Text);
	snprintf(Text, MAX_TEXT_LENGTH, "SND_JOIN;^t%s", SND_JOIN);
	writefile(savefile, Text);
	snprintf(Text, MAX_TEXT_LENGTH, "SND_EXIT;^t%s", SND_EXIT);
	writefile(savefile, Text);
	writefile(savefile, "");	// blank line
	writefile(savefile, "# Word/Wav combinations:");
	for (i = 0; i < MAX_KEYWORDS && bSuccess; i++)
	{
		// See if we reached the end
		if (strlen(WordWavCombo[i]) == 0)
			break;
		// I guess not, so format up a string to write
		// First, add the keyword
		snprintf(Text, MAX_TEXT_LENGTH, "%s;^t", WordWavCombo[i]);
		// Then add all the wavs
		for (j = 0; j < MAX_RANDOM && strlen(WordWavCombo[i][TOK_LENGTH*j]); j++)
			snprintf(Text, MAX_TEXT_LENGTH, "%s%s;", Text, WordWavCombo[i][TOK_LENGTH*j]);
		// Now write the formatted string to the file
		bSuccess = writefile(savefile, Text);
		// And loop for the next wav
	}
	snprintf(Text, MAX_TEXT_LENGTH, "Sound Plugin >> Configuration successfully written to %s.", savefile);
	selfmessage(Text);
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// This function exists in order to call parse_sound_file from a timer, which
// becomes necessary when file_access_read is set to 0.
//////////////////////////////////////////////////////////////////////////////
public parse_file_timer(Timer, Repeat, HLUser, HLParam)
{
	new param[MAX_TEXT_LENGTH];
	convert_string(HLParam, param, MAX_TEXT_LENGTH);
	parse_sound_file(param);
	return PLUGIN_HANDLED;
}
//////////////////////////////////////////////////////////////////////////////
// Parses the sound file specified by loadfile. If loadfile is empty, then
// it parses the default FILENAME.
//
// Returns 0 if parsing was successful
// Returns 1 if parsing failed
// Returns -1 otherwise
//
// Usage: admin_sound_reload <filename>
//////////////////////////////////////////////////////////////////////////////
parse_sound_file(loadfile[] = "")
{
	new bGotLine;
	new iLineNum = 0;
	new strLineBuf[MAX_TEXT_LENGTH];
	new WadOstrings[MAX_RANDOM*TOK_LENGTH]; // same as [MAX_RANDOM][TOK_LENGTH]
	new ListIndex = 0;
	new Text[MAX_TEXT_LENGTH];
	/************ File should have the following format: **************
	# Set the necessary variables
	SND_KICK;	20
	SND_WARN;	17
	SND_EXIT;	misc\comeagain.wav
	# Now give the sound list
	crap;	misc\awwcrap.wav;misc\awwcrap2.wav
	woohoo;	misc\woohoo.wav
	ha ha;	misc\haha.wav
	doh;	misc\doh.wav;misc\doh2.wav;misc\doh3.wav
	******************************************************************/
	if (strlen(loadfile) == 0)
		strcpy(loadfile, FILENAME, MAX_TEXT_LENGTH);
	if (fileexists(loadfile) > 0)
	{
		new i;
		bGotLine = readfile(loadfile, strLineBuf, iLineNum, MAX_TEXT_LENGTH);
		if (!bGotLine)
		{
			// If file access is already set correctly...
			if (getvar("file_access_read") == 1)
			{
				snprintf(Text, MAX_TEXT_LENGTH, "Sound Plugin >> Unable to read from %s file.", loadfile);
				selfmessage(Text);
				// If we set the file access last time, then set it back
				get_serverinfo("SNDSetAccess", Text, MAX_TEXT_LENGTH);
				if(strncmp(Text, "1", MAX_TEXT_LENGTH) == 0)
					exec("file_access_read 0");
				set_serverinfo("SNDSetAccess", "0"); 
				return 1;
			}
			// else set it correctly and try again
			exec("file_access_read 1");
			// flag ourselves to set it back later
			set_serverinfo("SNDSetAccess", "1"); 
			set_timer("parse_file_timer", 1, 0, loadfile);
			return -1;
		}
		// Initialize WordWavCombo[][][] array before using it
		for(i = 0; i < MAX_KEYWORDS; i++)
			WordWavCombo[i][0] = 0;
		while (bGotLine)
		{
			if (ListIndex >= MAX_KEYWORDS)
			{
				log("Sound Plugin >> Sound list truncated. Increase MAX_KEYWORDS.");
				printf("Sound Plugin >> Stopped parsing %s file.^n", loadfile);
				break;
			}
			// As long as the line isn't commented out, and isn't blank, then process it.
			if ((strncmp(strLineBuf, "#", 1) != 0) && (strncmp(strLineBuf, "//", 2) != 0) && (strlen(strLineBuf) != 0))
			{
				// Take up to MAX_RANDOM wav files for each keyword, each separated by a ';' 
				// Right now we fill the big WadOstrings[] with the information from the file.
				for(i=0; i<=MAX_RANDOM; i++)
				{
					strsep(strLineBuf, ";", WadOstrings[TOK_LENGTH*i], TOK_LENGTH, strLineBuf, MAX_TEXT_LENGTH);
				}
				// If we finished MAX_RANDOM times, and strRest still has contents
				//  then we should have a bigger MAX_RANDOM
				if(strlen(strLineBuf) != 0)
				{
					log("Sound Plugin >> Sound list partially truncated. Increase MAX_RANDOM.");
					printf("Sound Plugin >> Continuing to parse %s file.^n", loadfile);
				}
				// Now remove any spaces or tabs from around the strings -- clean them up
				for(i=0; i<MAX_RANDOM; i++)
				{
					strtrim(WadOstrings[TOK_LENGTH*i], " ^t");
				}
				// First look for special parameters
				if (!strcasecmp(WadOstrings, "SND_KICK"))
					SND_KICK = strtonum(WadOstrings[TOK_LENGTH*1]);
				else if (!strcasecmp(WadOstrings, "SND_WARN"))
					SND_WARN = strtonum(WadOstrings[TOK_LENGTH*1]);
				else if (!strcasecmp(WadOstrings, "SND_JOIN"))
					strcpy(SND_JOIN, WadOstrings[TOK_LENGTH*1], TOK_LENGTH);
				else if (!strcasecmp(WadOstrings, "SND_EXIT"))
					strcpy(SND_EXIT, WadOstrings[TOK_LENGTH*1], TOK_LENGTH);
				// If it wasn't one of those essential parameters, then it should be
				//  a Keyword/Wav combo, so we'll treat it as such by copying it from our
				//  temporary structure into our global structure, WordWavCombo[][][]
				else
				{
					// Now we must transfer the contents of WadOstrings[] to
					//  our global data structure, WordWavCombo[Index][]
					//  with a really tricky "string copy"
					for(i=0; i<MAX_RANDOM*TOK_LENGTH; i++)
					{
						WordWavCombo[ListIndex][i] = WadOstrings[i];
					}
					ListIndex++;
				}
			}
			// Initialize variables for next time  by clearing all the
			//  strings in the WadOstrings[]
			for(i = 0; i < MAX_RANDOM; i++)
			{
				WadOstrings[i*TOK_LENGTH] = 0;
			}
			// Read in the next line from the file
			bGotLine = readfile(loadfile, strLineBuf, ++iLineNum, MAX_TEXT_LENGTH);
			// This is pretty gay hacking, but here goes.  The readfile API function here
			//  has a static buffer that is 100 chars long in AM version 2.50.e and previous.
			//  A line longer than 100 chars in length looks like 2 lines to us in the script,
			//  with the first 'line' being the first 100 chars and the second 'line' being
			//  the rest of the line (given it is shorter than 100 as well).  So in order to
			//  fix this, we have to read in two lines, but only if the first has filled the
			//  first 100 chars completely. Then we add the second string to the end of the
			//  first and we get what we should've gotten the first time we did readline().
			// In AM versions later than 2.50.e, this should be removed, because I'm told
			//  the static buffer will be increased to 200, which is the MAX_TEXT_LENGTH for
			//  us anyway.
			if (strlen(strLineBuf) == 99)
			{
				bGotLine = readfile(loadfile, Text, ++iLineNum, MAX_TEXT_LENGTH);
				strcat(strLineBuf, Text, MAX_TEXT_LENGTH);
			}
		}
		// Now we have all of the data from the text file in our data structures.
		// Next we do some error checking, some setup, and we're done parsing!
		// Do some error checking on the user-input numbers
		// If SND_KICK is zero, then sounds quota is disabled.
		if (SND_KICK <= 0)
		{
			SND_KICK = 0;	// in case it was negative
			log("Sound quota disabled.");
		}
		// If SND_WARN is zero, then we can't have warning every
		//  time a keyword is said, so we default to 3 less than max
		else if (SND_WARN <= 0)
		{
			if (SND_KICK > 3)
				SND_WARN = SND_KICK - 3;
			else
				SND_WARN = SND_KICK - 1;
			log("Sound Plugin >> SND_WARN cannot be set to zero.");
			log("             >> SND_WARN set to default value.");
		}
		// And finally, if they want to warn after a person has been
		//  kicked, that's silly, so we'll fix it.
		else if (SND_KICK < SND_WARN)
		{
			if (SND_KICK > 3)
				SND_WARN = SND_KICK - 3;
			else
				SND_WARN = SND_KICK - 1;
			log("Sound Plugin >> SND_WARN cannot be higher than SND_KICK.");
			log("             >> SND_WARN set to default value.");
		}
		// Log some info for the nosey admin
		snprintf(Text, MAX_TEXT_LENGTH, "Sound quota set to %i", SND_KICK);
		log(Text);
		#if defined (DEBUG)
		print_matrix();
		printf("Sound Plugin >> Done parsing %s file.^n", loadfile);
		#endif
	}
	else // file exists returned false, meaning the file didn't exist
	{
		snprintf(Text, MAX_TEXT_LENGTH, "Sound Plugin >> Cannot find %s file.", loadfile);
		selfmessage(Text);
		return 1;
	}
	// If we set the file access last time, then set it back
	get_serverinfo("SNDSetAccess", Text, MAX_TEXT_LENGTH);
	if(strncmp(Text, "1", MAX_TEXT_LENGTH) == 0)
	{
		exec("file_access_read 0");
		set_serverinfo("SNDSetAccess", "0"); 
	}
	snprintf(Text, MAX_TEXT_LENGTH, "Sound Plugin >> %s successfully loaded.", loadfile);
	selfmessage(Text);
	return 0;
}
#if defined (DEBUG)
//////////////////////////////////////////////////////////////////////////////
// Prints out word wav combo matrix for debugging purposes. Kinda cool, even
// if you're not really debugging.
//
// Usage: admin_sound_debug
// Usage: admin_sound_reload <filename>
//////////////////////////////////////////////////////////////////////////////
public print_matrix()
{
	printf("SND_WARN: %d^n", SND_WARN);
	printf("SND_KICK: %d^n", SND_KICK);
	printf("SND_JOIN: %s^n", SND_JOIN);
	printf("SND_EXIT: %s^n", SND_EXIT);
	new i;
	new j;
	// Print out the matrix of sound data, so we got what we think we did
	for(i=0; i<MAX_KEYWORDS; i++)
	{
		if (strlen(WordWavCombo[i]) != 0)
		{
			printf("^n");
			for(j=0; j<MAX_RANDOM+1; j++)
			{
				if (strlen(WordWavCombo[i][j*TOK_LENGTH]) != 0)
					printf("[%d] [%d] ^"%s^"^n", i, j, WordWavCombo[i][j*TOK_LENGTH]);
			}
		}
	}
	return PLUGIN_HANDLED;
}
#endif
//////////////////////////////////////////////////////////////////////////////
// Returns 0 if the user is allowed to say things
// Returns 1 and kicks the user if the quota has been exceeded.
//////////////////////////////////////////////////////////////////////////////
QuotaCheck(User[], UserIndex)
{
	// If the sound limitation is disabled, then return happily.
	if (SND_KICK == 0)
		return 0;
	new Text[MAX_TEXT_LENGTH];
	new HowManyLeft = SND_KICK - SndCount[UserIndex];
	if (check_immunity(User) == 0)
	{
		if (SndCount[UserIndex] >= SND_KICK)
		{
			//playsoundall("misc\comeagain.wav"); // moved to the plugin_disconnect function
			message(User, "You were warned, but did not quit playing sounds");
			snprintf(Text, MAX_TEXT_LENGTH, "Kicked %s for saying too much", User);
			say(Text);
			kick(User);
			return 1;
		}
		else if (SndCount[UserIndex] >= SND_WARN)
		{
			messageex(User, "You have almost used up your sound quota. Stop.", print_center);
			messageex(User, "You have almost used up your sound quota. Stop.", print_console);
			snprintf(Text, MAX_TEXT_LENGTH, "You have %d left before you get kicked.", HowManyLeft);
			messageex(User, Text, print_center);
			messageex(User, Text, print_console);
		}
		// Increment their playsound count
		SndCount[UserIndex] = SndCount[UserIndex] + 1;
	}
	return 0;
}
//////////////////////////////////////////////////////////////////////////////
// Everything a person says goes through here, and we determine if we want to
// play a sound or not.
//
// Usage: say <something>
//////////////////////////////////////////////////////////////////////////////
public HandleSay(HLCommand, HLData, HLUserName, UserIndex) 
{
	// If sounds are not enabled, then skip this whole thing
	if (!bSoundsEnabled)
		return PLUGIN_CONTINUE;
	new i;
	new Speech[MAX_DATA_LENGTH];
	new User[MAX_NAME_LENGTH];
	new Text[MAX_TEXT_LENGTH];
	new ListIndex = -1;
	
	convert_string(HLData, Speech, MAX_DATA_LENGTH);
	convert_string(HLUserName, User, MAX_NAME_LENGTH);
	strstripquotes(Speech);
	if (systemtime() == LastSoundTime)
	{
		// Only one sound per second, please
		LastSoundTime = systemtime();
		return PLUGIN_CONTINUE;
	}
	// Check to see if what the player said is a trigger for a sound
	for (i=0; i<MAX_KEYWORDS; i++)
	{
		if (!strcasecmp(Speech, WordWavCombo[i]))
		{
			ListIndex = i;
		}
	}
	// If what the player said is a sound trigger, then handle it.
	if (ListIndex != -1)
	{
		if (!QuotaCheck(User, UserIndex))
		{
			new rand = random(MAX_RANDOM);
			new timeout = 25;
			new playFile[TOK_LENGTH];
			// This for loop runs around until it finds a real file to play
			for(rand = random(MAX_RANDOM); strlen(playFile) == 0; rand = random(MAX_RANDOM))
			{
				strcpy(playFile, WordWavCombo[ListIndex][(rand+1)*TOK_LENGTH], TOK_LENGTH);
				// If for some reason we never find a file
				//  then exit this infinite loop
				if (!timeout--)
				{
					printf("Could not find wav to play for %s^n", playFile);
					return PLUGIN_CONTINUE;
				}
			}
			snprintf(Text, MAX_TEXT_LENGTH, "%s :    %s", User, Speech);
			say(Text);
			if (am_i_dead(User)) {
				/* say("seit wann koennen Tote reden?"); */
				playsounddead(playFile);
			} else {			
				playsoundall(playFile);
			}
			LastSoundTime = systemtime();
			return PLUGIN_HANDLED;
		}
	}
	
	return PLUGIN_CONTINUE;
}