dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// saymote.cpp - saymote system written by Kalahn, Idea from Kirion.
/***************************************************************************
 * 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.                                                              *
 **************************************************************************/
/*
The player types:

saymote >kalahn [with a bow] greetings [and, then, smiling] how have you been?

and Kalahn would see it:

Kirion says to you with a bow: 'greetings' and, then, smiling: 'how have you been?'

Translating the said parts according to the language of the
speaker/listener.

Also supports [[ and ]] to allow a player to actually put '[' and ']' codes 
in their says
*/
#include "include.h" // dawn standard includes

/**************************************************************************/
// Kal - June 1999
void saymote( language_data *language, char_data *ch, char *argument, int sayflags)
{
	int i;

	if(IS_SET(sayflags, SAYFLAG_ONLY_REMEMBERED_CHAR) 
		&& (!IS_NPC(ch)|| !ch->mprog_target))
	{
		ch->printlnf("rsay '%s' not processed because you arent remembering anyone.", argument);
		return;
	}

	bool say_with_emotes=true;
	if(IS_SET(sayflags, SAYFLAG_NO_SAYMOTE)){
		say_with_emotes=false;
	}
	bool convert_colour=false;
	if(IS_SET(sayflags, SAYFLAG_CONVERT_COLOUR)){
		convert_colour=true;
	}
	
    static int rpmon=0;
	const int MAXBUFCOUNT=8;

	char	tochar_sayask_text[30], 
			toall_sayask_text[30], 
			tovictim_sayask_text[30];
	char splitbuf[MAXBUFCOUNT][(MIL*2)+3];	// (MIL*2)+3 to prevent $$$$... 
											// causing buffer overrun
	tovictim_sayask_text[0]='\0';
	char_data *victim= NULL; // if victim is true we have a sayto target

	// clear our buffers
	for(i=0; i<MAXBUFCOUNT; i++){
		splitbuf[i][0]='\0';
	};

	// *** idiot checks
    if ( IS_NULLSTR(argument) )
    {
        ch->println("Say what?");
        return;
    }
	// dont allow messages that could be defrauding
	if(check_defrauding_argument(ch, argument)){
		return;
	}
	// prevent potential buffer overruns
    if (count_char(argument,'$')>10) 
    {
        ch->println("You can't have more than 10 $'s in a saymote.");
        return;
    }
    if (count_char(argument,'[')>10) 
    {
        ch->println("You can't have more than 10 ['s in a say/saymote.");
        return;
    }
    if (count_char(argument,']')>10) 
    {
        ch->println("You can't have more than 10 ]'s in a say/saymote.");
        return;
    }

	if ( !IS_NPC(ch) 
		&& !IS_SET(language->flags, LANGFLAG_NO_SKILL_REQUIRED) 
		&& language->gsn>=0 
		&& get_skill(TRUE_CH(ch), language->gsn)<1 )
	{
		ch->printlnf("You have no knowledge of any %s words.", language->name);            
		return;
	}

	// wiznet RPMONITOR
    if (!IS_NPC(ch) && !IS_OOC(ch)) 
    {
        rpmon++;
        if (rpmon>=20)
        {
			int rplevel;
			char buf2[MSL];
			rplevel = (IS_TRUSTED(ch,LEVEL_IMMORTAL)?MAX_LEVEL:get_trust(ch));

            sprintf (buf2, "Wiznet rpmonitor: %s says '%s'`x", 
				ch->name, argument);
            wiznet(buf2,ch,NULL,WIZ_RPMONITOR,0,rplevel);
            if (rpmon>=23) rpmon=0;
        }
    }

	if(!IS_NPC(ch))
    {
		ch->pcdata->did_ic=true;
        if(is_room_private_to_char(ch->in_room, ch))
		{
            ch->pcdata->did_ooc=true;
            ch->pcdata->did_ic=false;
        }
	}

    // trim off any leading blanks
	argument = ltrim_string(argument);

	// anti autorps abuse system
	if(!IS_NPC(ch) && IS_IC(ch))
	{
		++ch->pcdata->say_index%=RPS_AUDIT_SIZE;
		free_string(ch->pcdata->saycheck[ch->pcdata->say_index]);
		ch->pcdata->saycheck[ch->pcdata->say_index]=str_dup(argument);
		ch->pcdata->saytimes[ch->pcdata->say_index]=current_time;
	}

	// Check for the nospeak flag, Cel Nov 99
	if(IS_SET(ch->in_room->room_flags, ROOM_NOSPEAK)
	&& !HAS_CONFIG(ch, CONFIG_HOLYSPEECH)) {
		ch->println("You feel the air escape your lungs, and yet... no sound.");
		return;
	}

	// *** Find if we have a sayto victim
	if (argument[0]=='>')
	{
		char to_arg[MIL];
		argument++; 
	    argument = one_argument( argument, to_arg);
	    victim = get_char_room( ch, to_arg);
		if (!victim)
		{
			ch->printlnf("You can't find '%s' to 'say to'",to_arg);
			return;
		}

		if (victim==ch)
		{
			ch->println("You can't direct your says at yourself.");
			return;
		}

		if (victim->position ==POS_SLEEPING)
		{
			ch->printlnf("%s is asleep.",icapitalize(PERS(victim, ch)));
			return;
		}

		if ( IS_NULLSTR(argument) )
		{
			ch->printlnf( "Say what to %s?", PERS(victim,ch));
			return;
		}
	}

	// parse the text
	int splitbuf_index=0;// even index = say, odd = emote
	bool in_emote=false; // true when we get a '[' but haven't yet seen a ']'
	char *pstr;
	char *writestr=&splitbuf[0][0]; // where the next character will go
	bool trim_space=true; // used to trim the leading space off something

	for(pstr=argument; !IS_NULLSTR(pstr); pstr++){
		// trim of a single space from the start of each type
		if(trim_space && *pstr==' '){	
			continue;
		}
		trim_space=false;

		// $ are converted to $$ and then converted back to $ by act_new()
		if(*pstr=='$'){
			// double up the character into the appropriate buffer
			*writestr++='$';
			*writestr++='$';
			continue;
		};

		// ` is converted to `` if convert colour is enabled
		if(convert_colour && *pstr=='`'){
			// double up the character into the appropriate buffer
			*writestr++='`';
			*writestr++='`';
			continue;
		};

		// convert [[ into [
		if(say_with_emotes){
			if(*pstr=='[' && *(pstr+1)=='[' ){
				*writestr++='[';
				pstr++;
				continue;
			}

			// convert ]] into ]
			if(*pstr==']' && *(pstr+1)==']' ){
				*writestr++=']';
				pstr++;
				continue;
			}
		}
		
		// check for state changing characters
		if(say_with_emotes && *pstr=='['){ // start emote
			if(in_emote){
				ch->printf("You can't have a [ character in a say after a [.\r\n"
					"  (to close the emote use a ]).\r\n"
					"   See `=Chelp saymote`x.\r\n");
				ch->wrapln("hint: you can also disable the automated use of saymote "
					"for the say command by typing `=Cautosaymote`x");
				return;
			};

			splitbuf_index++;
			if(splitbuf_index>=MAXBUFCOUNT){
				ch->printlnf("There is a limit of %d alternating says + emotes in a saymote.",
					MAXBUFCOUNT);
				return;
			};
			
			in_emote=true;
			*writestr='\0';	// terminate the buffer we were working on
			//trim off last character in a say if it is a space
			if(is_space(*(writestr-1))){
				*(writestr-1)='\0'; // terminate the buffer trimming the last single
									// space if necessary from the say
			}
			
			writestr=&splitbuf[splitbuf_index][0];
			continue; // just switched into emote mode, go onto next character
		}
		
		if(say_with_emotes && *pstr==']'){ // close emote 
			if(!in_emote){ 
				ch->println("You can't have a ] character before a matching [.\r\n"
					"  (to start an emote inside a say use a [).\r\n"
					"   See `=Chelp saymote`x.\r\n"
					"   To say an actual [ character, use [[.");
				ch->wrapln("hint: you can also disable the automated use of saymote "
					"for the say command by typing `=Cautosaymote`x");
				return;
			};

			splitbuf_index++;
			if(splitbuf_index>=MAXBUFCOUNT){
				ch->printlnf("There is a limit of %d alternating says + emotes in a saymote.",
					MAXBUFCOUNT);
				return;
			};
	
			in_emote=false;
			trim_space=true;// enable inital space trimming for next say
			*writestr='\0'; // terminate the buffer we were working on
			writestr=&splitbuf[splitbuf_index][0];
			continue; // just switched into say mode, go onto next character
		}

		// copy the character into the appropriate buffer
		*writestr++=*pstr;
	}
	// check that there isn't an open emote
	if(in_emote){
		ch->printf("You can't leave an open emote in a saymote.\r\n"
			"   See `=Chelp saymote`x.\r\n");
		ch->wrapln("hint: you can also disable the automated use of saymote "
			"for the say command by typing `=Cautosaymote`x");
		return;
	}
	*writestr='\0'; // terminate the buffer we were working on
	
	// *** decide if is relating to asking/saying and sayto
	bool question=false;
	bool exclaim=false;
	for(i=0; i<=splitbuf_index; i+=2){
		if(IS_NULLSTR(splitbuf[i])){
			continue;
		}
		char *trimmedtext=rtrim_string(splitbuf[i]);

		if(trimmedtext[str_len(trimmedtext)-1]=='?'){
			question=true;
		}else if(trimmedtext[str_len(trimmedtext)-1]=='!'){
			exclaim=true;
		}
		break;
	}
	
	if (question){ // it is a question therefore asking
		if(victim){						
			strcpy(tochar_sayask_text,"ask $N");
			strcpy(toall_sayask_text,"asks $N");
			strcpy(tovictim_sayask_text,"`#`Wasks you`&");
		}else{
			strcpy(tochar_sayask_text,"ask");
			strcpy(toall_sayask_text,"asks");
		}
	}
	else if (exclaim){
		if(victim){						
			strcpy(tochar_sayask_text,"exclaim to $N");
			strcpy(toall_sayask_text,"exclaims to $N");
			strcpy(tovictim_sayask_text,"`#`Wexclaims to you`&");
		}else{
			strcpy(tochar_sayask_text,"exclaim");
			strcpy(toall_sayask_text,"exclaims");
		}
	}
	else
	{ // saying
		if(victim){						
			strcpy(tochar_sayask_text,"say to $N");
			strcpy(toall_sayask_text,"says to $N");
			strcpy(tovictim_sayask_text,"`#`Wsays to you`&");
		}else{
			strcpy(tochar_sayask_text,"say");
			strcpy(toall_sayask_text,"says");
		}
	}

	char format_buffer[MSL*2]; // *2 because the translator can expand text
	int format_index;
	char buf[MSL*2];
	char wizitext[20];
	bool langshow;
	if(GAMESETTING3(GAMESET3_LANGUAGE_NAME_NOT_IN_SAYS) 
		|| language == language_unknown){
		langshow=false;
	}else{
		langshow=true;
	}

	int max_invislevel=INVIS_LEVEL(ch);
	
	if(victim){
		max_invislevel=UMAX(INVIS_LEVEL(victim),max_invislevel);
	}
	if(max_invislevel){
		sprintf(wizitext,"[Wizi %d] ", max_invislevel);
	}else{
		wizitext[0]='\0';
	}
	bool blind=false, deaf=false;

    // translate the language to all/intended people? in the room
    for( char_data *to= ch->in_room->people; to ; to = to->next_in_room )
    {
		if(IS_SET(sayflags, SAYFLAG_ONLY_REMEMBERED_CHAR) && to!=ch->mprog_target){
			continue;
		}

        if (
            (!IS_NPC(to) && to->desc == NULL) // linkdead or switched out of
             || ch==to						  // self	
             || (IS_UNSWITCHED_MOB(to) && !IS_SET(to->act,ACT_MOBLOG))  // non switched mob 
																		// without a moblog
			 || !IS_AWAKE(to) ){    // asleep
			continue;
		}

		// players can't see saymotes by wizi characters, or said to 
		// wizi characters
		if (!IS_TRUSTED(to, max_invislevel)){
			continue;
		}

		// set the deaf and blind for quick lookup
		if(IS_IC(to)){
			deaf=(is_affected(to, gsn_deafness ));

			blind=(IS_AFFECTED( to, AFF_BLIND ) || is_affected( to, gsn_blindness ));

			// someone that is deaf and blind gets nothing sent to them if IC
			if(deaf && blind)
				continue;
		}

		sprintf(format_buffer,"%s%s%s %s",
			(IS_IMMORTAL(to)?wizitext:""),			// [Wizi %d]
			IS_OOC(ch)?"`B(OOC) `x":"`x",			// (OOC)
			PERS(ch, to),							// ch->name/ch->short_descr
			to==victim?tovictim_sayask_text:toall_sayask_text); // say/ask

		// if there are more parts process them
		for(format_index=0; format_index<=splitbuf_index; format_index++){
			if(IS_NULLSTR(splitbuf[format_index]))
				continue;
			if(format_index%2==0){ // says are even
				if(deaf){
					sprintf(buf," `S<%s's lips move>`x", PERS(ch,to));
					strcat(format_buffer,buf);
				}else{
					sprintf(&format_buffer[str_len(format_buffer)]," `x'`%c", ch->saycolour);					
					translate_language(language, langshow, ch, to, splitbuf[format_index], buf);
					strcat(format_buffer, buf);
					strcat(format_buffer,"`x'");// reset colour
				}
				langshow=false;
			}else{ //emotes are odd
				if(!blind){
					sprintf(&format_buffer[str_len(format_buffer)]," `%c", ch->motecolour);
					strcat(format_buffer,splitbuf[format_index]);
					strcat(format_buffer,"`x");// reset colour
				}
			}
		}		
		RECORD_TO_REPLAYROOM=true;
		act( format_buffer, to, NULL, victim, TO_CHAR );
		RECORD_TO_REPLAYROOM=false;
	} // end of translation loop

	
	{	// format what is sent to the actual speaker
		// because this goes thru act() we don't need to put the [Wizi %d] on
		if(IS_OOC(ch)){
			if(IS_SET(language->flags,LANGFLAG_REVERSE_TEXT)){
				sprintf(format_buffer,"`B(OOC) `xYou %s [reversing language]", tochar_sayask_text);
			}else{
				sprintf(format_buffer,"`B(OOC) `xYou %s", tochar_sayask_text);
			}
		}else{
			if(GAMESETTING3(GAMESET3_LANGUAGE_NAME_NOT_IN_SAYS) 
				|| language == language_unknown)
			{
				sprintf(format_buffer,"`x%sYou %s", 
					(IS_IMMORTAL(ch)?wizitext:""), tochar_sayask_text);					
			}else{
				sprintf(format_buffer,"`x%sYou %s in %s",
					(IS_IMMORTAL(ch)?wizitext:""), tochar_sayask_text, language->name);
			}
		}

		// if there are more parts process them
		for(format_index=0; format_index<=splitbuf_index; format_index++){
			if(IS_NULLSTR(splitbuf[format_index]))
				continue;
			if(format_index%2==0){ // says are even
				sprintf(&format_buffer[str_len(format_buffer)]," `x'`%c", ch->saycolour);
				strcat(format_buffer,splitbuf[format_index]);
				strcat(format_buffer,"`x'");// reset colour
			}else{ //emotes are odd
				sprintf(&format_buffer[str_len(format_buffer)]," `%c", ch->motecolour);
				strcat(format_buffer,splitbuf[format_index]);
				strcat(format_buffer,"`x");// reset colour
			}
		}		
		RECORD_TO_REPLAYROOM=true;
		act( format_buffer, ch, NULL, victim, TO_CHAR );
		RECORD_TO_REPLAYROOM=false;
	}

	int number_in_room=ch->in_room->number_in_room;
	// note: number_in_room is used to prevent a mobprog on two mobs in 
	// a room creating an endless loop by removing themself from
	// the room and putting themselves back in the room - Kal, June 01

	// handle mobprog speech and act triggers
    if ( !IS_NPC(ch) && !IS_SET(sayflags, SAYFLAG_ONLY_REMEMBERED_CHAR) )
    {
		char_data *mob_next;
        char_data *mob= ch->in_room->people; 
        for ( ; mob && --number_in_room>=0; mob = mob_next )
        {
            mob_next = mob->next_in_room;
			if(!IS_NPC(mob)){
				continue;
			}
            if (HAS_TRIGGER( mob, TRIG_SPEECH ) 
				|| HAS_TRIGGER( mob, TRIG_ACT ) 
				|| (HAS_TRIGGER( mob, TRIG_SAYTO) && victim==mob))
			{
				for(format_index=0; format_index<=splitbuf_index; format_index++){
					if(IS_NULLSTR(splitbuf[format_index]))
						continue;
					if(format_index%2==0){ // says are even
						if(HAS_TRIGGER( mob, TRIG_SAYTO))
						{
							mp_act_trigger( splitbuf[format_index], mob, 
								ch, NULL, NULL, TRIG_SAYTO );
						}
						if(HAS_TRIGGER( mob, TRIG_SPEECH ))
						{
							mp_act_trigger( splitbuf[format_index], mob, 
								ch, NULL, NULL, TRIG_SPEECH );
						}
					}else{ //emotes are odd
						if(HAS_TRIGGER( mob, TRIG_ACT )){
							mp_act_trigger( splitbuf[format_index], mob, 
								ch, NULL, NULL, TRIG_ACT );
						}
					}
				}		
			}
        }
    }
}
/**************************************************************************/