tfe-1.0/area/
tfe-1.0/files/
tfe-1.0/logs/
tfe-1.0/logs/immortal/
tfe-1.0/logs/mob/
tfe-1.0/logs/object/
tfe-1.0/logs/player/
tfe-1.0/logs/room/
tfe-1.0/notes/clans/
tfe-1.0/player/
tfe-1.0/prev/
tfe-1.0/prev/area/
tfe-1.0/prev/player/
tfe-1.0/prev/rooms/
tfe-1.0/rooms/
tfe-1.0/src-gc/
tfe-1.0/src-msvc/
tfe-1.0/src-unix/
tfe-1.0/www/
tfe-1.0/www/html/
#include "sys/types.h"
#include <ctype.h>
#include "stdio.h"
#include "define.h"
#include "struct.h"


int max_language = MAX_LANGUAGE-LANG_PRIMAL;

flag_data lang_flags = { "Languages", &skill_table[LANG_PRIMAL].name,
  &skill_table[LANG_PRIMAL+1].name, &max_language };


/*
 *   LOCAL FUNCTIONS
 */


const char*  say_verb       ( char_data*, char_data* );
void         say_routine    ( char_data*, char*, int );
void         add_tell       ( tell_data*&, const char*, const char*,
                              int = -1 );
void         display        ( tell_data*&, char_data*, char*,
                              char_data* = NULL );
void         garble_string  ( char*, char*, int );
void         tell_message   ( char_data*, char*, char_data*, int = 0 );


path_func   hear_yell;

tell_data*  ooc_history  = NULL;
tell_data*  atalk_history = NULL;


/*
 *   TELL CLASS AND SUPPORT ROUTINES
 */


Tell_Data :: Tell_Data( const char* pers, const char* txt,
  int tongue )
{
  record_new( sizeof( tell_data ), MEM_TELL );

  name     = alloc_string( pers, MEM_TELL );
  message  = alloc_string( txt, MEM_TELL ); 
  language = tongue; 
  *name    = toupper( *name );
  next     = NULL;
}


Tell_Data :: ~Tell_Data( )
{
  record_delete( sizeof( tell_data ), MEM_TELL );

  free_string( name, MEM_TELL );
  free_string( message, MEM_TELL );
}


void display( tell_data*& list, char_data* ch, char* text,
  char_data* victim )
{
  char         tmp  [ MAX_STRING_LENGTH ];
  int            i;
  tell_data*  tell;

  if( list == NULL ) {
    if( &list == &ooc_history ) {
      send( ch, "There have been no OOC's since the mud was started.\n\r" );
      }
    else if( victim == NULL || victim == ch ) {
      send( ch, "You haven't heard any %s since logging in.\n\r", text );
      }
    else {
      send( ch, "%s hasn't heard any %s since logging in.\n\r",
        victim, text );
      }
    return;
    }

  sprintf( tmp, "--- " );
  for( i = 0; i < int( strlen( text ) ); i++ )
    tmp[i+4] = toupper( text[i] );
  sprintf( &tmp[ strlen( text )+4 ], " ---" );

  page_centered( ch, tmp );

  for( tell = list; tell != NULL; tell = tell->next ) {
    if( tell->language != -1 )
      sprintf( tmp, "[%s >> %s]\n\r%s%s", tell->name,
        skill_table[tell->language].name, tell->message,
        tell->next == NULL ? "" : "\n\r" );
    else
      sprintf( tmp, "[%s]\n\r%s%s", tell->name,
        tell->message, tell->next == NULL ? "" : "\n\r" );

    page( ch, tmp );
    }

  return;
}


void add_tell( tell_data*& list, const char* pers, const char* message,
  int language )
{
  tell_data*  tell;

  tell = new tell_data( pers, message, language );

  append( list, tell );

  if( count( list ) > 10 ) {
    tell = list;
    list = tell->next;
    delete tell;
    }

  return;
} 


/*
 *   LANGUAGE ROUTINES
 */


void do_language( char_data *ch, char *argument )
{
  int i;

  if( is_confused_pet( ch ) || ch->pcdata == NULL )
    return;

  if( ch->pcdata->speaking < LANG_PRIMAL
    || ch->pcdata->speaking >= MAX_LANGUAGE )
    ch->pcdata->speaking = LANG_PRIMAL; 

  if( *argument == '\0' ) {
    send( ch, "You are currently speaking %s.\n\rType 'lang <lang\
 name>' to switch to another language.\n\r",
      skill_table[ch->pcdata->speaking].name );
    return;
    }

  for( i = LANG_PRIMAL; i < MAX_LANGUAGE; i++ )
    if( fmatches( argument, skill_table[i].name ) ) {
      if( i == LANG_PRIMAL && ch->shdata->level < LEVEL_APPRENTICE ) {
        send( ch, "Only immortals can speak in Primal.\n\r" );
        return;
        }
      if( ch->shdata->skill[i] == UNLEARNT
        && !is_set( ch->affected_by, AFF_TONGUES ) ) {
        send( ch, "You don't know that language.\n\r" );
        return;
        }
      ch->pcdata->speaking = i;
      send( ch, "You will now speak in %s.\n\r", skill_table[i].name );
      return;
      }

  send( ch, "Unknown language.\n\r" );
  return;
}


int get_language( char_data* ch, int i )
{
  if( i < LANG_PRIMAL || i >= MAX_LANGUAGE )
    return 0;

  if( ch->pcdata == NULL || is_set( ch->affected_by, AFF_TONGUES ) )
    return 10;

  return ch->shdata->skill[i];
}


void garble_string( char* output, char* input, int skill )
{
  skill = (200-10*skill)*10*skill;

  for( ; *input != '\0'; input++, output++ ) {
    if( number_range( 0, 10000 ) > skill ) {
      if( ispunct( *input ) || isspace( *input ) ) 
        *output = *input;
      else if( ( *input > 90 ) && ( *input < 97 ) )
        *output = *input-10;
      else
        *output = number_range( 'a', 'z' );
      }
    else 
      *output = *input;
    }
      
  *output = '\0';
  return;
}


char* slang( char_data* ch, int language )
{
  if( !is_set( ch->pcdata->pfile->flags, PLR_LANG_ID ) )
    return empty_string;

  if( get_language( ch, language ) == 0 )
    return " (in an unknown tongue)";

  char* tmp = static_string( );

  sprintf( tmp, " (in %s)", skill_table[ language ].name );
 
  return tmp;
}


bool munchkin( char_data* ch, char* text )
{
  int length =  strlen( text );
  int  punct =  0;
   
  for( int i = 0; i < length; i++ )    
    if( text[i] == '?' || text[i] == '!' )
      punct++; 

  if( punct > 3 && length/punct < 20 ) { 
    send( ch, "Excessive punctuation is sure sign that you shouldn't be heard from, please\n\rsurpress your munchkin tendencies.\n\r" );
    return TRUE;
    }

  return FALSE;
}


/*
 *   CHANNEL SUPPORT ROUTINES
 */


inline bool hear_channels( player_data* pc )
{
  return( pc->position != POS_EXTRACTED && ( pc->link == NULL
    || pc->link->connected == CON_PLAYING ) );
}


bool subtract_gsp( player_data* ch, const char* text, int cost )
{
  if( get_trust( ch ) < LEVEL_ARCHITECT ) {
    if( ch->gossip_pts < cost ) {
      send( ch, "%s requires %s gossip point%s.\n\r",
        text, number_word( cost ), cost == 1 ? "" : "s" );
      return FALSE;
      }
    ch->gossip_pts -= cost;
    }

  return TRUE;
}


bool can_talk( char_data* ch, const char* string )
{
  if( !is_set( ch->affected_by, AFF_SILENCE ) ) 
    return TRUE;

  if( string != NULL ) 
    fsend( ch, "You attempt to %s, but oddly fail to make any noise.",
      string );

  return FALSE;
}


/*
 *   STANDARD CHANNELS ROUTINES
 */


void do_ooc( char_data* ch, char* argument )
{
  char               buf  [ MAX_STRING_LENGTH ];
  char               tmp  [ MAX_STRING_LENGTH ];
  player_data*    pc;
  player_data*    victim;
  int         max_length;
  int             length;

  if( is_mob( ch ) )
    return;

  pc = player( ch );

  if( *argument == '\0' ) {
    display( ooc_history, ch, "OOC CHANNEL" );
    return;
    }

  if( toggle( ch, argument, "OOC channel",
    ch->pcdata->pfile->flags, PLR_OOC ) )
    return;

  if( !is_set( ch->pcdata->pfile->flags, PLR_OOC ) ) {
    send( ch, "You have OOC off.\n\r" );
    return;
    }

  if( munchkin( pc, argument ) )
    return;

  if( !subtract_gsp( pc, "Using OOC", 1 ) )
    return;
  
  sprintf( buf, "[OOC] %s:", ch->real_name( ) );
  length = strlen( buf );
  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[ length < max_length ] );

  for( int i = 0; i < player_list; i++ ) {
    victim = player_list[i];
    if( hear_channels( victim )
      && is_set( victim->pcdata->pfile->flags, PLR_OOC )
      && !victim->Filtering( ch ) ) 
      send_color( victim, COLOR_OOC, buf );
    }

  add_tell( ooc_history, ch->real_name( ), tmp ); 
}


void do_chant( char_data* ch, char* argument )
{
  char             buf  [ MAX_STRING_LENGTH ];
  char             tmp  [ MAX_STRING_LENGTH ];
  player_data*      pc;
  int         language;
  int           length;
  int       max_length;

  if( is_mob( ch ) )
    return;

  pc = player( ch );

  if( ch->pcdata->religion == REL_NONE ) {
    send( ch, "Only those with a religion can chant.\n\r" );
    return;
    }

  if( *argument == '\0' ) {
    display( pc->chant, ch, "Chant" );
    return;
    }

  if( toggle( ch, argument, "Chant",
    ch->pcdata->pfile->flags, PLR_CHANT ) )
    return;

  if( !is_set( ch->pcdata->pfile->flags, PLR_CHANT ) ) {
    send( ch, "You have chant turned off.\n\r" );
    return;
    }

  language = ch->pcdata->speaking;
  garble_string( buf, argument, get_language( ch, language ) );
  format_tell( tmp, buf );
  max_length = 70-strlen( tmp );

  sprintf( buf, "You chant:%s%s", 7 < max_length ? "" : "\n\r", 
    &tmp[7 < max_length] );
  send( ch, buf );

  for( int i = 0; i < player_list; i++ ) {
    pc = player_list[i];
    if( hear_channels( pc ) 
      && pc->pcdata->religion == ch->pcdata->religion ) {
      add_tell( pc->chant, ch->Seen_Name( pc ), tmp, language ); 
      if( is_set( pc->pcdata->pfile->flags, PLR_CHANT ) ) {
        sprintf( buf, "%s chants:", ch->Seen_Name( pc ) );
        length = strlen( buf );
        sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
          &tmp[length < max_length] );
        send( pc, buf );
        }
      }
    }
}


void do_chat( char_data* ch, char* argument )
{
  char             buf  [ MAX_STRING_LENGTH ];
  char             tmp  [ MAX_STRING_LENGTH ];
  player_data*  victim;
  player_data*      pc;
  int         language;
  int       max_length;
  int           length;

  if( is_mob( ch ) )
    return;

  pc = player( ch );

  if( *argument == '\0' ) {
    display( pc->chat, ch, "chats" );
    return;
    }
 
  if( toggle( ch, argument, "Chat channel", 
    ch->pcdata->pfile->flags, PLR_CHAT ) )
    return;

  if( !is_set( ch->pcdata->pfile->flags, PLR_CHAT ) ) {
    send( "You have chat turned off.\n\r", ch );
    return;
    }

  if( !subtract_gsp( pc, "Chatting", 1 ) )
    return;

  language = ch->pcdata->speaking;
  garble_string( buf, argument, get_language( ch, language ) );

  format_tell( tmp, buf );

  max_length = 70-strlen( tmp );

  sprintf( buf, "You chat%s:", slang( ch, language ) );
  length = strlen( buf );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );
  send_color( ch, COLOR_CHAT, buf );

  for( int i = 0; i < player_list; i++ ) {
    victim = player_list[i];
    if( hear_channels( victim ) && are_allied( ch, victim ) ) { 
      add_tell( victim->chat, who_name( victim, ch ), tmp, language ); 
      if( victim != ch && victim->link != NULL
        && is_set( victim->pcdata->pfile->flags, PLR_CHAT )
        && !victim->Filtering( ch ) ) {
        sprintf( buf, "%s chats%s:", who_name( victim, ch ),
          slang( victim, language ) );
        *buf = toupper( *buf ); 
        length = strlen( buf );
        sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
          &tmp[length < max_length] );
        send_color( victim, COLOR_CHAT, buf );
        }
      }
    }

  return;
}


void do_gossip( char_data* ch, char* argument )
{
  char              buf  [ MAX_STRING_LENGTH ];
  char              tmp  [ MAX_STRING_LENGTH ];
  player_data*       pc;
  player_data*   victim;
  int          language;
  int        max_length;
  int            length;

  if( is_mob( ch ) ) 
    return;

  pc = player( ch );

  if( *argument == '\0' ) {
    display( pc->gossip, ch, "gossips" );
    return;
    }

  if( toggle( ch, argument, "Gossip channel", 
    ch->pcdata->pfile->flags, PLR_GOSSIP ) ) 
    return;

  if( !is_set( ch->pcdata->pfile->flags, PLR_GOSSIP ) ) {
    send( "You have gossip turned off.\n\r", ch );
    return;
    }

  if( munchkin( pc, argument ) )
    return;

  if( !subtract_gsp( pc, "Using gossip", 10 ) )
    return;

  language = ch->pcdata->speaking;
  garble_string( buf, argument, get_language( ch, language ) );
  format_tell( tmp, buf );

  max_length = 70-strlen( tmp );

  sprintf( buf, "You gossip%s:", slang( ch, language ) );
  length = strlen( buf );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );
  send_color( ch, COLOR_GOSSIP, buf );

  for( int i = 0; i < player_list; i++ ) {
    victim = player_list[i];
    if( hear_channels( victim ) ) {
      add_tell( victim->gossip, who_name( victim, ch ), tmp, language ); 
      if( victim != ch && victim->link != NULL
        && is_set( victim->pcdata->pfile->flags, PLR_GOSSIP )
        && !victim->Filtering( ch ) ) {
        sprintf( buf, "%s gossips%s:", who_name( victim, ch ),
          slang( victim, language ) );
        *buf = toupper( *buf ); 
        length = strlen( buf );
        sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
          &tmp[length < max_length] );
        send_color( victim, COLOR_GOSSIP, buf );
        }
      }
    }
}


void do_atalk( char_data* ch, char* argument )
{
  char               buf  [ MAX_STRING_LENGTH ];
  char               tmp  [ MAX_STRING_LENGTH ];
  player_data*    pc;
  player_data*    victim;
  int         max_length;
  int             length;

  if( is_mob( ch ) )
    return;

  pc = player( ch );

  if( *argument == '\0' ) {
    display( atalk_history, ch, "AUCTION CHANNEL" );
    return;
    }

  if( toggle( ch, argument, "Auction channel",
    ch->pcdata->pfile->flags, PLR_ATALK ) )
    return;

  if( !is_set( ch->pcdata->pfile->flags, PLR_ATALK ) ) {
    send( ch, "You have ATALK off.\n\r" );
    return;
    }

  if( !subtract_gsp( pc, "Using ATALK", 10 ) )
    return;
  
  sprintf( buf, "[ATALK] %s:", ch->real_name( ) );
  length = strlen( buf );
  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[ length < max_length ] );

  for( int i = 0; i < player_list; i++ ) {
    victim = player_list[i];
    if( hear_channels( victim )
      && is_set( victim->pcdata->pfile->flags, PLR_ATALK )
      && !victim->Filtering( ch ) ) 
      send_color( victim, COLOR_AUCTION, buf );
    }

  add_tell( atalk_history, ch->real_name( ), tmp ); 
}


/*
 *   IMMORTAL CHANNELS
 */


void do_avatar( char_data* ch, char* argument )
{
  char              buf  [ MAX_STRING_LENGTH ];
  char              tmp  [ MAX_STRING_LENGTH ];
  player_data*   pc;
  wizard_data*   imm  = NULL;
  int            length;
  int        max_length;

  if( *argument == '\0' ) {
    imm = (wizard_data*) ch;
    display( imm->avatar, ch, "avatar channel" );
    return;
    }

  if( toggle( ch, argument, "Avatar channel", 
    ch->pcdata->pfile->flags, PLR_AVATAR ) )
    return;
        
  if( !is_set( ch->pcdata->pfile->flags, PLR_AVATAR ) ) {
    send( "You have the avatar channel turned off.\n\r", ch );
    return;
    }   

  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );

  sprintf( buf, "%s --", ch->real_name( ) );
  *buf   = toupper( *buf ); 
  length = strlen( buf );

  for( int i = 0; i < player_list; i++ ) {
    pc = player_list[i];
    if( hear_channels( pc ) && ( imm = wizard( pc ) ) != NULL
      && has_permission( imm, PERM_AVATAR_CHAN ) ) {
      add_tell( imm->avatar, ch->real_name( ), tmp ); 
      if( is_set( imm->pcdata->pfile->flags, PLR_AVATAR ) 
        && !imm->Filtering( ch ) ) {
        sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
          &tmp[length < max_length] );
        send( imm, buf );
        }
      }
    }
        
  return;
}


void do_buildchan( char_data* ch, char* argument )
{
  char              buf  [ MAX_STRING_LENGTH ];
  char              tmp  [ MAX_STRING_LENGTH ];
  player_data*   pc;
  wizard_data*   imm  = NULL;
  int        max_length;
  int            length;

  if( *argument == '\0' ) {
    imm = (wizard_data*) ch;
    display( imm->build_chan, ch, "builder channel" );
    return;
    }

  if( toggle( ch, argument, "Creator channel", 
    ch->pcdata->pfile->flags, PLR_BUILDCHAN ) )
    return;
        
  if( !is_set( ch->pcdata->pfile->flags, PLR_BUILDCHAN ) ) {
    send( ch, "You have the creator channel turned off.\n\r" );
    return;
    }   

  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );

  sprintf( buf, "%s:", ch->descr->name );
  *buf   = toupper( *buf ); 
  length = strlen( buf );

  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );

  for( int i = 0; i < player_list; i++ ) {
    pc = player_list[i];
    if( hear_channels( pc ) && ( imm = wizard( pc ) ) != NULL
      && has_permission( imm, PERM_BUILD_CHAN ) ) {
      add_tell( imm->build_chan, ch->descr->name, tmp, -1 ); 
      if( is_set( imm->pcdata->pfile->flags, PLR_BUILDCHAN ) )
        send( buf, imm );
      }
    }
        
  return;
}


void do_immtalk( char_data* ch, char* argument )
{
  char                buf  [ MAX_STRING_LENGTH ];
  char                tmp  [ MAX_STRING_LENGTH ];
  player_data*     pc;
  wizard_data*     imm  = NULL;
  int          max_length;
  int              length;

  if( *argument == '\0' ) {
    imm = (wizard_data*) ch;
    display( imm->imm_talk, ch, "imm_talk" );
    return;
    }

  if( toggle( ch, argument, "Immortal channel", 
    ch->pcdata->pfile->flags, PLR_IMMCHAN ) )
    return;
        
  if( !is_set( ch->pcdata->pfile->flags, PLR_IMMCHAN ) ) {
    send( ch, "You have the immortal channel turned off.\n\r" );
    return;
    }   

  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );

  sprintf( buf, "[%s]", ch->descr->name );
  *buf   = toupper( *buf ); 
  length = strlen( buf );

  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );

  for( int i = 0; i < player_list; i++ ) {
    pc = player_list[i];
    if( hear_channels( pc ) && ( imm = wizard( pc ) ) != NULL
      && has_permission( imm, PERM_IMM_CHAN ) ) {
      add_tell( imm->imm_talk, ch->descr->name, tmp ); 
      if( is_set( imm->pcdata->pfile->flags, PLR_IMMCHAN ) ) 
        send( buf, imm );
      }
    }
        
  return;
}


void do_god( char_data* ch, char* argument )
{
  char                buf  [ MAX_STRING_LENGTH ];
  char                tmp  [ MAX_STRING_LENGTH ];
  player_data*     pc;
  wizard_data*     imm  = NULL;
  int          max_length;
  int              length;

  if( *argument == '\0' ) {
    imm = (wizard_data*) ch;
    display( imm->god_talk, ch, "God Channel" );
    return;
    }

  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );

  sprintf( buf, "{%s}", ch->descr->name );
  *buf   = toupper( *buf ); 
  length = strlen( buf );

  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );

  for( int i = 0; i < player_list; i++ ) {
    pc = player_list[i];
    if( hear_channels( pc ) && ( imm = wizard( pc ) ) != NULL
      && has_permission( imm, PERM_GOD_CHAN ) ) {
      add_tell( imm->god_talk, ch->descr->name, tmp ); 
      send( buf, imm );
      }
    }

  return;
}


/*
 *   YELL ROUTINE
 */
 

void do_yell( char_data* ch, char* argument )
{
  char              buf  [ MAX_STRING_LENGTH ];
  char              tmp  [ MAX_STRING_LENGTH ];
  char_data*        rch;
  player_data*       pc;
  int          language;
  int        max_length;
  int            length;

  if( is_confused_pet( ch ) )
    return;

  pc = player( ch );

  if( *argument == '\0' ) {
    if( pc != NULL )
      display( pc->yell, ch, "yells" );
    return;
    }

  if( !can_talk( ch, "yell" ) )
    return;

  if( ch->pcdata != NULL ) {
    language = ch->pcdata->speaking;
    garble_string( buf, argument, get_language( ch, language ) );
    format_tell( tmp, buf );
    }
  else {
    language = LANG_PRIMAL;
    format_tell( tmp, argument );
    }

  max_length = 70-strlen( tmp );

  if( ch->pcdata != NULL ) {
    sprintf( buf, "You yell%s:", slang( ch, language ) );
    length = strlen( buf );
    sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
      &tmp[length < max_length] );
    send( ch, buf );
    }

  if( pc != NULL )
    add_tell( pc->yell, ch->descr->name, tmp, language ); 

  for( int i = 0; i < *ch->array; i++ ) 
    if( ( rch = character( ch->array->list[i] ) ) != NULL 
      && rch != ch ) 
      hear_yell( rch, ch, tmp );

  exec_range( ch, 8, hear_yell, tmp );

  return;
}


const char* yell_name( char_data* ch, char_data* victim,
  int dir, int distance )
{
  char* tmp;

  if( distance == 0 ) {
    if( ch->Seen( victim ) ) 
      return ch->Seen_Name( victim );
    else
      return "someone nearby";
    }
  
  if( distance > 5 ) 
    return "someone far away";

  tmp = static_string( );
  sprintf( tmp, "someone %s", dir_table[dir].where );

  return tmp;
}


void hear_yell( char_data* victim, char_data* ch,
  char* message, int dir, int distance )
{
  char                 buf  [ MAX_STRING_LENGTH ];
  char                 tmp  [ MAX_STRING_LENGTH ];
  int               length;
  int           max_length;
  player_data*          pc  = player( victim );
  const char*         name;
  int             language;

  if( pc == NULL || !can_hear( victim ) ) 
    return;

  name       = yell_name( ch, victim, dir, distance );
  max_length = 70-strlen( message );
  language   = ch->pcdata != NULL ? ch->pcdata->speaking : LANG_PRIMAL;

  garble_string( tmp, message, get_language( victim, language ) );
  add_tell( pc->yell, name, tmp, language ); 

  sprintf( buf, "%s yells%s:", name,
    slang( victim, language ) );
  *buf = toupper( *buf ); 
  length = strlen( buf );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );
  send( victim, buf );

  return;
}


/*
 *   SAY ROUTINES
 */


void do_say( char_data* ch, char* argument )
{
  char              buf  [ MAX_STRING_LENGTH ];
  char              tmp  [ MAX_STRING_LENGTH ];
  char              arg  [ MAX_STRING_LENGTH ];
  char_data*     victim;
  player_data*       pc  = player( ch );
  int          language;
  int            length;
  int        max_length;

  if( is_confused_pet( ch ) )
    return;

  if( *argument == '\0' ) {
    if( pc != NULL ) 
      display( pc->say, ch, "says" );
    return;
    }

  if( !can_talk( ch, "speak" ) )
    return;

  if( ch->pcdata != NULL ) {
    language = ch->pcdata->speaking;
    garble_string( buf, argument, get_language( ch, language ) );
    format_tell( tmp, buf );
    }
  else {
    language = LANG_PRIMAL;
    format_tell( tmp, argument );
    }

  max_length = 70-strlen( tmp );

  if( ch->pcdata != NULL ) {
    if( is_set( ch->pcdata->pfile->flags, PLR_SAY_REPEAT ) ) {
      sprintf( buf, "You %s%s:", say_verb( ch, ch ), 
        slang( ch, language ) );
      length = strlen( buf );
      sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
        &tmp[length < max_length] );
      send( buf, ch );
      }
    else
      send( ch, "Ok.\n\r" );
    if( pc != NULL )
      add_tell( pc->say, ch->descr->name, tmp, language ); 
    }

  for( int i = 0; i < *ch->array; i++ ) {
    if( ( victim = character( ch->array->list[i] ) ) == NULL 
      || ch == victim || ( pc = player( victim ) ) == NULL
      || !can_hear( victim ) )
      continue;
    victim->improve_skill( language );
    sprintf( buf, "%s %s%s:", ch->Name( victim ),
      say_verb( ch, victim ), slang( victim, language ) );
    *buf = toupper( *buf );
    length = strlen( buf );
    garble_string( arg, tmp, get_language( victim, language ) );
    sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
      &arg[length < max_length] );
    send_color( victim, COLOR_SAYS, buf );
    add_tell( pc->say, ch->Name( victim ), arg, language ); 
    }

  return;
}


const char* say_verb( char_data* ch, char_data* victim )
{
  if( ch->shdata->race == RACE_LIZARD ) {
    return( ch == victim ? "hiss" : "hisses" );
    }

  return( ch == victim ? "say" : "says" );
}


/*
 *   SHOUT ROUTINE
 */


void do_shout( char_data* ch, char* argument )
{
  char             buf  [ MAX_STRING_LENGTH ];
  char             tmp  [ MAX_STRING_LENGTH ];
  player_data*  pc;
  player_data*  victim;
  int           length;
  int       max_length;

  if( is_mob( ch ) )
    return;

  pc = player( ch );

  if( *argument == '\0' ) {
    display( pc->shout, ch, "shouts" );
    return;
    }

  if( is_set( ch->pcdata->pfile->flags, PLR_NO_SHOUT ) ) {
    send( ch, "You have been forbidden from shouting by the gods.\n\r" );
    return;
    }

  if( *argument == '\0' ) {
    send( "Shout what?\n\r", ch );
    return;
    }

  if( !subtract_gsp( pc, "Shouting", 500 ) )
    return;

  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );

  sprintf( buf, "You shout:%s%s", 10 < max_length ? "" : "\n\r", 
    &tmp[10 < max_length] );
  send( buf, ch );

  add_tell( pc->shout, ch->descr->name, tmp, -1 ); 

  for( int i = 0; i < player_list; i++ ) {
    victim = player_list[i];
    if( victim != ch && hear_channels( victim ) ) {
      add_tell( victim->shout, who_name( victim, ch ),tmp, -1 ); 
      sprintf( buf, "%s shouts:", who_name( victim, ch ) );
      *buf = toupper( *buf ); 
      length = strlen( buf );
      sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
        &tmp[length < max_length] );
      send( victim, buf );
      }
    }

  return;
}


/*
 *   TELL ROUTINES
 */


void tell_message( char_data* ch, char* msg, char_data* victim, int i )
{
  char tmp  [ THREE_LINES ];

  sprintf( tmp,   "++ " );  
  sprintf( tmp+3, msg, victim->descr->name, i );
  strcat( tmp, " ++\n\r\n\r" );

  tmp[3] = toupper( tmp[3] );

  send( ch, tmp );
}


void process_tell( char_data* ch, char_data* victim, char* argument )
{
  char             buf  [ 3*MAX_STRING_LENGTH ];
  char             tmp  [ 3*MAX_STRING_LENGTH ];
  player_data*      pc;
  int           length;
  int       max_length;

  if( ch->pcdata != NULL && victim->pcdata != NULL ) {
    if( victim->Filtering( ch ) ) {
      fsend( ch, "%s is filtering you - please leave %s in peace.",
        victim->descr->name, victim->Him_Her( ) );
      return; 
      }
    if( ch->Filtering( victim ) ) {
      fsend( ch, "You are filtering %s and only a chebucto would want to\
 converse with someone they are filtering.", victim );
      return;
      } 
    }

  if( ( pc = player( victim ) ) == NULL
    && victim->link != NULL )
    pc = victim->link->player;

  if( victim->pcdata != NULL ) {
    if( victim->link == NULL && pc->switched == NULL ) 
      tell_message( ch, "%s is link dead", victim );
    else if( victim->timer+30 < current_time )
      tell_message( ch, "%s has been idle for %d seconds",
        victim, current_time-victim->timer );
    else if( victim->array != ch->array && opponent( victim ) != NULL )
      tell_message( ch, "%s is in battle", victim );
    }

  format_tell( tmp, argument );
  max_length = 70-strlen( tmp );

  if( ch->pcdata != NULL ) {
    if( is_set( ch->pcdata->pfile->flags, PLR_SAY_REPEAT ) ) {
      sprintf( buf, "You tell %s:", victim->Seen_Name( ch ) );
      length = strlen( buf );
      sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
        &tmp[length < max_length] );
      send( ch, buf );
      }
    else
      send( ch, "Ok.\n\r" );
    }

  if( pc == NULL )
    return;

  sprintf( buf, "%s tells %s:", ch->Seen_Name( victim ),
    victim != pc || pc->switched == NULL
    ? "you" : pc->descr->name );
  *buf = toupper( *buf );
  length = strlen( buf );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );

  if( ch->pcdata == NULL ) {
    convert_to_ansi( victim, buf, tmp );
    send( victim, tmp );
    return;
    } 

  if( !is_set( &victim->status, STAT_REPLY_LOCK ) ) 
    victim->reply = ch;

  if( victim == pc && pc->switched != NULL )
    victim = pc->switched;

  send_color( victim, COLOR_TELLS, buf );
  add_tell( pc->tell, ch->Seen_Name( victim ), tmp );
}


void do_tell( char_data *ch, char *argument )
{
  char             arg  [ MAX_STRING_LENGTH ];
  char_data*    victim;
  player_data*  pc  = player( ch );

  if( is_confused_pet( ch ) )
    return;

  if( *argument == '\0' ) {
    if( pc != NULL )
      display( pc->tell, ch, "tells" );
    return;
    }

  argument = one_argument( argument, arg );

  if( *arg == '\0' || *argument == '\0' ) {
    send( ch, "Tell whom what?\n\r" );
    return;
    }

  in_character = FALSE;

  if( ( victim = one_character( ch, arg, "tell",
    (thing_array*) &player_list ) ) == NULL )
    return;

  if( ch == victim ) {
    send( ch, "Talking to yourself is pointless.\n\r" );
    return;
    }

  if( ch->pcdata != NULL && victim->pcdata != NULL ) {
    if( victim->Ignoring( ch ) ) {
      fsend( ch, "%s has ignore set to level %d and you cannot tell to %s.",
        victim->descr->name, 
        level_setting( &victim->pcdata->pfile->settings, SET_IGNORE ),
        victim->Him_Her( ) );
      return;
      }
    }

  process_tell( ch, victim, argument );
}


void do_reply( char_data* ch, char* argument )
{
  char*   noone_msg  = "Noone has told to you or the person who last told\
 to you has quit.\n\r";
  char_data* victim  = ch->reply;

  in_character = FALSE;
 
  if( *argument == '\0' ) {
    if( ch->reply == NULL ) {
      send( ch, "Reply points to noone.\n\r" );
      }
    else {
      send( ch, "Reply %s to %s.\n\r",
        is_set( &ch->status, STAT_REPLY_LOCK ) ? "is locked" : "points",
        victim->Seen_Name( ch ) );
      }
    return;
    }

  if( !strcasecmp( argument, "lock" ) ) {
    if( victim == NULL ) { 
      send( ch, noone_msg );
      }
    else if( is_set( &ch->status, STAT_REPLY_LOCK ) ) {
      send( ch, "Your reply is already locked to %s.\n\r",
        victim->Seen_Name( ch ) );
      }
    else {
      set_bit( &ch->status, STAT_REPLY_LOCK );
      send( ch, "Reply locked to %s.\n\r", victim->Seen_Name( ch ) );    
      }
    return;
    }

  if( !strcasecmp( argument, "unlock" ) ) {
    if( !is_set( &ch->status, STAT_REPLY_LOCK ) ) {
      send( ch, "Your reply is already unlocked.\n\r" );
      }
    else {
      send( ch, "Reply unlocked.\n\r" );
      remove_bit( &ch->status, STAT_REPLY_LOCK );
      }
    return;
    }

  if( victim == NULL ) {
    send( ch, noone_msg );
    return;
    }

  process_tell( ch, victim, argument );
}


/*
 *   WHISPER/TO
 */


void trigger_say( char_data* ch, char_data* mob, char* argument )
{
  mprog_data* mprog;

  for( mprog = mob->species->mprog; mprog != NULL; mprog = mprog->next ) 
    if( mprog->trigger == MPROG_TRIGGER_TELL
      && ( is_name( argument, mprog->string )
      || *mprog->string == '\0' ) ) {
      var_ch   = ch;
      var_mob  = mob;
      var_arg  = argument;
      var_room = Room( ch->array->where );
      execute( mprog );
      return;
      }
}


void ic_tell( char_data* ch, char* argument, char* verb )
{ 
  char              buf  [ MAX_STRING_LENGTH ];
  char              tmp  [ MAX_STRING_LENGTH ];
  char              arg  [ MAX_STRING_LENGTH ];
  char_data*     victim;
  player_data*       pc;
  int          language;
  int            length;
  int        max_length;

  argument = one_argument( argument, arg );

  if( ( victim = one_character( ch, arg, verb,
    ch->array ) ) == NULL )
    return;
 
  if( *argument == '\0' ) {
    send( ch, "%s what to %s?\n\r", verb, victim );
    return;
    }

  if( ch == victim ) {
    send( ch, "%sing something to yourself does nothing useful.\n\r",
      verb );
    return;
    }

  if( !IS_AWAKE( victim ) ) {
    send( ch, "They are not in a state to hear you.\n\r" );
    return;
    }

  /*
  fsend_seen( ch, "%s whispers something to %s.\n\r", ch, victim );
  */

  if( ch->pcdata != NULL ) {
    language = ch->pcdata->speaking;
    garble_string( buf, argument, get_language( ch, language ) );
    format_tell( tmp, buf );
    }
  else {
    language = LANG_PRIMAL;
    format_tell( tmp, argument );
    }

  max_length = 70-strlen( tmp );

  if( ch->pcdata != NULL ) {
    if( is_set( ch->pcdata->pfile->flags, PLR_SAY_REPEAT ) ) {
      sprintf( buf, "You %s to %s%s:", verb, victim->Name( ch ), 
        slang( ch, language ) );
      length = strlen( buf );
      sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
        &tmp[length < max_length] );
      send( ch, buf );
      }
    else
      send( ch, "Ok.\n\r" );
    }

  if( ( pc = player( victim ) ) == NULL ) {
    trigger_say( ch, victim, argument );
    return;
    }

  sprintf( buf, "%s %ss to you%s:", ch->Name( victim ),
    verb, slang( victim, language ) );
  buf[0] = toupper( buf[0] );
  length = strlen( buf );
  garble_string( arg, tmp, get_language( victim, language ) );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &arg[length < max_length] );
  send_color( victim, COLOR_SAYS, buf );
  add_tell( pc->whisper, ch->Seen_Name( pc ), arg ); 
}


void do_to( char_data* ch, char* argument )
{
  player_data* pc;

  if( is_confused_pet( ch ) )
    return;

  if( *argument == '\0' ) {
    if( ( pc = player( ch ) ) != NULL )
      display( pc->to, ch, "To" );
    return;
    }

  ic_tell( ch, argument, "say" );
}


void do_whisper( char_data* ch, char* argument )
{  
  player_data*  pc  = player( ch );

  if( is_confused_pet( ch ) )
    return;

  if( *argument == '\0' ) {
    if( pc != NULL )  
      display( pc->whisper, ch, "whispers" );
    return;
    }

  ic_tell( ch, argument, "whisper" );
}


/*
 *   EMOTE ROUTINE
 */


void do_emote( char_data *ch, char *argument )
{
  char_data*    rch;
  bool        space;

  if( ch->pcdata != NULL
    && is_set( ch->pcdata->pfile->flags, PLR_NO_EMOTE ) ) {
    send( ch, "You can't show your emotions.\n\r" );
    return;
    }

  if( *argument == '\0' ) {
    send( ch, "Emote what?\n\r" );
    return;
    }

  space = strncmp( argument, "'s ", 3 ) && strncmp( argument, ", ", 2 );

  for( int i = 0; i < *ch->array; i++ ) {
    if( ( rch = character( ch->array->list[i] ) ) == NULL 
      || rch->position <= POS_SLEEPING )
      continue; 
    fsend( rch, "%s%s%s\n\r",
      ch->Name( rch ), space ? " " : "", argument );
    }

  return;
}


/*
 *   GROUP TELL
 */


void do_gtell( char_data* ch, char* argument )
{
  char              tmp  [ MAX_STRING_LENGTH ];
  char              buf  [ MAX_STRING_LENGTH ];
  char_data*     leader;
  player_data*       pc  = player( ch );
  int          language;
  int            length;
  int        max_length;

  if( is_mob( ch ) )
    return;

  if( *argument == '\0' ) {
    display( pc->gtell, ch, "group tells" );
    return;
    }

  if( is_set( ch->pcdata->pfile->flags, PLR_NO_TELL ) ) {
    send( ch, "You are banned from using tell.\n\r" );
    return;
    }

  if( ( leader = group_leader( ch ) ) == NULL ) {
    send( ch, "You aren't in a group.\n\r" );
    return;
    }

  language = ch->pcdata->speaking;
  garble_string( buf, argument, get_language( ch, language ) );

  format_tell( tmp, buf );

  max_length = 70-strlen( tmp );

  sprintf( buf, "You tell your group%s:", 
    slang( ch, language ) );
  length = strlen( buf );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );
  send( buf, ch );

  for( int i = 0; i < player_list; i++ ) {
    pc = player_list[i];
    if( pc->position != POS_EXTRACTED && ( pc->link == NULL
      || pc->link->connected == CON_PLAYING )
      && leader == group_leader( pc ) ) {
      add_tell( pc->gtell, ch->Seen_Name( pc ), tmp, language );
      if( pc != ch && pc->link != NULL ) {
        sprintf( buf, "%s tells the group%s:", ch->Seen_Name( pc ),
          slang( pc, language ) );
        buf[0] = toupper( buf[0] ); 
        length = strlen( buf );
        sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
          &tmp[length < max_length] );
        send_color( pc, COLOR_GTELL, buf );
        }
      }
    }

  return;
}


/*
 *   CLAN TELL
 */


void do_ctell( char_data* ch, char* argument )
{
  char               buf  [ MAX_STRING_LENGTH ];
  char               tmp  [ MAX_STRING_LENGTH ];
  int           language;
  int         max_length;
  int             length;
  player_data*    pc  = player( ch );

  if( is_mob( ch ) )
    return;

  if( ch->pcdata->pfile->clan == NULL ) {
    send( ch, "You aren't in a clan.\n\r" );
    return;
    }

  if( *argument == '\0' ) {
    display( pc->ctell, ch, "clan tells" );
    return;
    }

  if( toggle( ch, argument, "Clan channel", 
    ch->pcdata->pfile->flags, PLR_CTELL ) )
    return;

  if( !is_set( ch->pcdata->pfile->flags, PLR_CTELL ) ) {
    send( ch, "You have ctell turned off.\n\r" );
    return;
    }

  language = ch->pcdata->speaking;
  garble_string( buf, argument, get_language( ch, language ) );

  format_tell( tmp, buf );

  max_length = 70-strlen( tmp );

  sprintf( buf, "You tell the clan%s:", slang( ch, language ) );
  length = strlen( buf );
  sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
    &tmp[length < max_length] );
  send_color( ch, COLOR_CTELL, buf );

  for( int i = 0; i < player_list; i++ ) {
    pc = player_list[i];
    if( pc->In_Game( )
      && pc->pcdata->pfile->clan == ch->pcdata->pfile->clan ) {
      add_tell( pc->ctell, ch->Seen_Name( pc ), tmp ); 
      if( pc != ch && pc->link != NULL
        && is_set( pc->pcdata->pfile->flags, PLR_CTELL ) ) {
        sprintf( buf, "%s ctells%s:", ch->Seen_Name( pc ),
          slang( pc, language ) );
        *buf = toupper( *buf ); 
        length = strlen( buf );
        sprintf( &buf[length], "%s%s", length < max_length ? "" : "\n\r", 
          &tmp[length < max_length] );
        send_color( pc, COLOR_CTELL, buf );
        }
      }
    }

  return;
}


/*
 *   REVIEW COMMAND
 */


void do_review( char_data* ch, char* argument )
{
  char              arg  [ MAX_INPUT_LENGTH ];
  player_data*   victim;

  argument = one_argument( argument, arg );

  if( *arg == '\0' ) {
    send( ch, "For whom do you wish to review recent conversation?\n\r" );
    return;
    }

  if( ( victim = (player_data*) one_character( ch, arg, "review", 
    (thing_array*) &player_list ) ) == NULL )
    return;

  if( ch == victim ) {
    send( ch,
      "There are simplier ways to review your own conversations.\n\r" );
    return;
    }

  if( get_trust( victim ) >= get_trust( ch ) ) {
    send( ch, "You are unable to review %s's conversations.\n\r", victim );
    return;
    }

  if( *argument == '\0' ) {
    send( ch, "Which conversation channel do you want to review?\n\r" );
    return;
    }

  if( matches( argument, "say" ) ) {
    display( victim->say, ch, "says", victim );
    return;
    }

  if( matches( argument, "tell" ) ) {
    display( victim->tell, ch, "tells", victim );
    return;
    }

  send( ch, "Unknown history type - see help review.\n\r" );

  return;
}