dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// msp.cpp - msp support, Kerenos and Kal
/***************************************************************************
 * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt                    *
 * >> A number of people have contributed to the Dawn codebase, with the   *
 *    majority of code written by Michael Garratt - www.dawnoftime.org     *
 * >> To use this source code, you must fully comply with the dawn license *
 *    in licenses.txt... In particular, you may not remove this copyright  *
 *    notice.                                                              *
 **************************************************************************/
#include "include.h"
#include "msp.h"


struct msp_table_type{ // the table using this type is sorted by MSP_TYPE
	char *base_url;			// the base url of filename (including directory)
	char *prefix_filename;	// directory it is stored in
	char *type;				// the category of sound - players can turn off types
	int default_prority;	// default priority of a sound
	int target_extra_prority;// extra priority of a sound when directed at a target
	int default_volume;		// default volume of a sound to all in room
	int target_extra_volume;// extra volume of a sound to some it is targeted at
	
};

msp_table_type msp_table[MSPT_MAX_TYPE];

struct msp_load_table_type
{
	MSP_TYPES type_index;
	const char *directory; // used to create the base_url, prefix_filename and type
	int default_prority;	// default priority of a sound
	int target_extra_prority;// extra priority of a sound when directed at a target
	int default_volume;		// default volume of a sound to all in room
	int target_extra_volume;// extra volume of a sound to some it is targeted at
};

msp_load_table_type msp_load_table_data[]=
{									  // prorities , volumes
	{	MSPT_ACTION,	MSP_ACTION_DIR,		40,	10,	60,	15	},
	{	MSPT_COMBAT,	MSP_COMBAT_DIR,		65,	20,	60,	35	},
	{	MSPT_MOBPROG,	MSP_MOBPROG_DIR,	35,	40,	50,	30	},
	{	MSPT_ROOM,		MSP_ROOM_DIR,		45,	 0,	70,	 0	},
	{	MSPT_SKILL,		MSP_SKILLS_DIR,		60,	10,	50,	35	},
	{	MSPT_SPELL,		MSP_SPELLS_DIR,		60,	10,	50,	35	},
	{	MSPT_WEATHER,	MSP_WEATHER_DIR,	25,	 0,	60,	 0	},
	{	MSPT_MAX_TYPE,	"", 0, 0, 0, 0 }// end of table marker
};


/**************************************************************************/
char * get_msptype_name(const char *word)
{
	static char rbuf[3][MIL];
	static int index;
	char *pStr;

	++index%=3; // rotate return buffer

	sprintf(rbuf[index], "%s", word);

	pStr=&rbuf[index][0];

	// convert spaces into _ characters
	// and upper to lowercase
	do{
		if (*pStr==' '){
			*pStr='_';
		}else if (*pStr=='/'){
			*pStr='_';
		}else{
			*pStr=tolower(*pStr);
		}
	}while (*(++pStr));

	// trim off any tailing _
	if(*(pStr-1)=='_'){
		*(pStr-1)='\0';
	}	
	return rbuf[index];
}
/**************************************************************************/
// change any '\' into a '/'   
void backslash_to_slash(char *word)
{
	char *pStr=word;
	do{
		if (*pStr=='\\'){
			*pStr='/';
		}
	}while (*(++pStr));
}
/**************************************************************************/
// Kal - Dec 99
void msp_load_table()
{
	#define data_table  msp_load_table_data[i]
	#define tab_element msp_table[msp_load_table_data[i].type_index]
	int i;
	for (i=0; !IS_NULLSTR(data_table.directory); i++){
		tab_element.base_url		= str_dup(MSP_URL);
		tab_element.prefix_filename	= str_dup(data_table.directory);
		tab_element.type			= str_dup(get_msptype_name(data_table.directory));
		// the prorities and volumes
		tab_element.default_prority		= data_table.default_prority;
		tab_element.target_extra_prority= data_table.target_extra_prority;
		tab_element.default_volume		= data_table.default_volume;
		tab_element.target_extra_volume	= data_table.target_extra_volume;
	}
	#undef tab_element
	#undef data_table
	logf("msp_load_table(): msp_table loaded with msp type info.");
}
/**************************************************************************/
float get_yellreduction(short sector);
/**************************************************************************/
// Sends an msp sound to everyone in the room - Kal, Dec 99
// NOTES:
// * if the volume is zero, the default volume is used for the sound type
// * repeats = number of times to play the sound
// * If surrounding_rooms is true, you can hear the sound in the 
//   surrounding rooms (go figure)
// * If the extension of the sound filename is .mid then !!MUSIC is used
void msp_to_room(MSP_TYPES type, char *filename, 
					int volume, // 1 ->100, if 0 default for type vol used 
					char_data *target,
					bool target_only_in_room,
					bool surrounding_rooms)
{
	if(IS_NULLSTR(filename)){
		return;
	}

	char msp_trigger_format_buf[MSL];
	char_data *to;
	// handle the loading of the msp_table on its first use
	static bool msp_table_needs_loading=true;
	if(msp_table_needs_loading){
		msp_load_table();
		msp_table_needs_loading=false;
	}

	// get the filename of what we are sending, check the file is on the server
	char send_filename[MSL];
	// support system if last character in the filename before the extension is a digit 
	// the sound will be randomized, with filename0.wav->filename#.wav where # is
	// the value of that digit
	int len=str_len(filename);
	if(	len>5 
		&& is_digit(filename[len-5])
		&& !str_suffix(".wav", filename) 
		)
	{
		// get a random sound file - if not found drop back to '*0.wav'
		filename[len-5]=(char)number_range('0', filename[len-5]);

		if(GAMESETTING(GAMESET_MSP_CHECK_FILEEXIST)){
			if(!file_exists(MSP_DIR "%s%s", msp_table[type].prefix_filename, filename)){
				filename[len-5]=(char)'0';
			}
		}
	}
	sprintf(send_filename, "%s%s", msp_table[type].prefix_filename, filename);
	if(GAMESETTING(GAMESET_MSP_CHECK_FILEEXIST)){
		if(!file_exists(MSP_DIR "%s", send_filename)){
			bugf("msp_to_room(): couldn't find sound to send - send_filename = '%s'",
				send_filename);
			return;
		};
	}

	// check our target and its room is valid 
	if(target==NULL || target->in_room==NULL){
		bugf("msp_to_room(): target==NULL || target->in_room==NULL! - send_filename = 'msp "DIR_SYM"%s'",
			send_filename);
		return;
	}

	// get the default volume
	if(volume==0){
		volume=msp_table[type].default_volume;
	}
	if(volume>100){
		volume=100;
	}

	//construct the unchanging parts of the msp trigger
	if(!str_suffix( ".mid", send_filename)){
		sprintf(msp_trigger_format_buf,
			"!!MUSIC(%s V=%%d L=1 T=%s", send_filename, msp_table[type].type);
	}else{
		sprintf(msp_trigger_format_buf, "!!SOUND(%s V=%%d L=1 P=%%d T=%s",
				send_filename, msp_table[type].type);
	}

	// if the url is sufficient length, has been changed from the default, we use it.
	if(!IS_NULLSTR(msp_table[type].base_url) 
		&& str_len(msp_table[type].base_url)>5
		&& strcmp(DEFAULT_MSP_URL,msp_table[type].base_url))
	{
		strcat(msp_trigger_format_buf, FORMATF(" U=%s%s", msp_table[type].base_url, send_filename));
	}
	strcat(msp_trigger_format_buf,")");

	// convert any backslashes to slashes
	backslash_to_slash(msp_trigger_format_buf);

	// send to the target
	if(CAN_HEAR_MSP(target))
	{
		flush_char_outbuffer(target);
		target->printf(msp_trigger_format_buf,
			((volume+msp_table[type].target_extra_volume)>100?
				100:(volume+msp_table[type].target_extra_volume)),
			(msp_table[type].default_prority+msp_table[type].target_extra_prority));
	}

	if(!target_only_in_room){
		// send to all in room except target
		for(to = target->in_room->people; to; to=to->next_in_room ){
			if (to!=target && CAN_HEAR_MSP( to )){
				flush_char_outbuffer(to);
				to->printf(msp_trigger_format_buf,
					volume,
					msp_table[type].default_prority);
			}
		}
	}

	if(!surrounding_rooms){
		return;
	}
	
	// do the sending to all surrounding rooms
    ROOM_INDEX_DATA *from_room=target->in_room;		
    for ( int door = 0; door < MAX_DIR; door++ )
    {
		EXIT_DATA       *pexit;
		int main_volume=volume;
		
		if ( ( pexit = from_room->exit[door] ) != NULL
			&&   pexit->u1.to_room != NULL
			&&   pexit->u1.to_room != from_room)
		{
			// get the new volume - same volume reduction as yelling 
			// - reduction should technically be logarithmic, but life goes on :)
			volume=(int)((float)main_volume * get_yellreduction(pexit->u1.to_room->sector_type));
			if(IS_SET(from_room->exit[door]->exit_info,EX_CLOSED)){
				volume=main_volume/2; // closed doors make things quieter
			}

			// send to all in room except target
			for(to = pexit->u1.to_room->people; to; to=to->next_in_room ){
				if (CAN_HEAR_MSP( to )){
					flush_char_outbuffer(to);
					to->printf(msp_trigger_format_buf,
						volume,
						msp_table[type].default_prority/2);
				}
			}
		}
    }    
}
/**************************************************************************/
// plays a skills sound if it has one set in sedit
void msp_skill_sound(char_data *target, int sn)
{	
	if(!IS_NULLSTR(skill_table[sn].msp_sound)){
		msp_to_room(MSPT_SKILL, skill_table[sn].msp_sound, 
				0,target,	false, true);
	}
}
/**************************************************************************/
void do_msp( char_data *ch, char *argument )
{
	pc_data *pcdata=TRUE_CH(ch)->pcdata; // the characters pcdata we want to work on
	if(!pcdata){
		ch->println("Players can only use this command");
		return;
	}

	if(IS_NULLSTR(argument)){
		ch->titlebar("MUD SOUND PROTOCOL OPTIONS");
		ch->println("syntax: `=Cmsp off`x  - msp is permanately off.");
		ch->wrapln("syntax: `=Cmsp auto`x - mud will attempt to automatically "
					"detect if you have mud client that supports msp, if one is detected "
					"the mud will send you msp sound triggers");
		ch->wrapln("syntax: `=Cmsp on`x  - msp is permanately on, mud will send you sound "
			"triggers even if your mud client doesn't support msp.");
		ch->printlnf("Your msp preference is currently set to %s", 
			preference_word(pcdata->preference_msp));
		if(pcdata->preference_msp==PREF_AUTOSENSE){
			ch->printlnf("Your connections msp support has %sbeen automatically detected.",
				(ch->desc && IS_SET(ch->desc->flags, CONNECTFLAG_MSP_DETECTED))?"":"not ");
		}
		ch->titlebar("");
		return;
	}

	PREFERENCE_TYPE pt;
	if(!str_prefix(argument, "off")){
		pt=PREF_OFF;
	}else if(!str_prefix(argument, "autosense")){
		pt=PREF_AUTOSENSE;
	}else if(!str_prefix(argument, "on")){
		pt=PREF_ON;
	}else{
		ch->printlnf("Unsupported msp option '%s'", argument);
		do_msp(ch,"");
		return;
	}
	if(pcdata->preference_msp==pt){
		ch->printlnf("Your msp preference is already set to %s", preference_word(pt));
		return;
	}

	ch->printlnf("msp preference changed from %s to %s", 
		preference_word(pcdata->preference_msp),
		preference_word(pt));
	pcdata->preference_msp=pt;

	msp_update_char(ch);

}
/**************************************************************************/
void msp_update_char(char_data*ch)
{
	if(!ch){
		return;
	}
	pc_data *pcdata=TRUE_CH(ch)->pcdata;
	connection_data *d=TRUE_CH(ch)->desc;
	if(!pcdata || !d){
		return;
	}

	switch(pcdata->preference_msp){
		case PREF_OFF:	
			pcdata->msp_enabled=false;
			break;
		case PREF_AUTOSENSE: 
			if(IS_SET(d->flags,CONNECTFLAG_ANSWERED_MSP)
				&& IS_SET(d->flags,CONNECTFLAG_MSP_DETECTED)){
				pcdata->msp_enabled=true;
			}else{
				pcdata->msp_enabled=false;
			}
			break;
		case PREF_ON: 
			pcdata->msp_enabled=true;
			break;
	}
}
/**************************************************************************/