mud_dist/area/
/*****************************************************************************
 * Chatmode.c  - Merc-based confrence system.                                *
 *                                                                           *
 * Full chat-mode setup for Merc based MUDS, or could be used separately if  *
 * you cut and pasted the needed parts from Merc code.                       *
 * -- Altrag Dalosein, Lord of the Dragons..                                 *
 *****************************************************************************/
/*$Id*/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "merc.h"


/*
 * Externals
 */
bool  check_social    args( ( CHAR_DATA *ch, char *command, char *argument ) );
extern int port;

/*
 * Locals
 */
void    start_chat_mode     args( ( DESCRIPTOR_DATA *d ) );
void    stop_chat_mode      args( ( CHAR_DATA *ch ) );
void    chat_interp         args( ( CHAR_DATA *ch, char *argument ) );
void    chat_command        args( ( CHAR_DATA *ch, char command,
				    char *argument ) );
#define CD CHAR_DATA *
CD      get_char_chat       args( ( CHAR_DATA *ch, char *argument ) );
#undef CD
void    send_room_stuff     args( ( CHAR_DATA *ch ) );
void    init_chat           args( ( void ) );
int     num_color           args( ( CHAR_DATA *ch ) );
char *  get_color           args( ( CHAR_DATA *ch ) );

struct chat_room
{
  struct chat_room *next;
  ROOM_INDEX_DATA *pRoom;
  char *invited;
};

void dispose_room( struct chat_room *room );

/*
 * Use pre-defined stuff, even though all parts aren't used for chat.
 * This eliminates the need for new functinos that are copies of old
 * functions, such as ACT or CHECK_SOCIAL.
 * -- Altrag
 */
struct chat_room *chat_rooms;
struct chat_room *last_chat_room;
CHAR_DATA *chat_list;
CHAR_DATA *old_chars;

void start_chat_mode( DESCRIPTOR_DATA *d )
{
  CHAR_DATA *ch;

  if ( !d || !d->character )
    return;

  if ( !chat_rooms )
    init_chat( );

  if ( d->original )
    do_return( d->character, "" );

  if ( d->pEdit )
  {
    d->pEdit = NULL;
    d->editor = 0;
  }

  if ( d->inEdit )
  {
    d->inEdit = NULL;
    d->editin = 0;
  }

  if ( d->character == char_list )
    char_list = char_list->next;
  else
  {
    for ( ch = char_list; ch; ch = ch->next )
      if ( ch->next == d->character )
	break;
    if ( ch )
      ch->next = d->character->next;
  }

  if ( d->character->was_in_room )
  {
    if ( !d->character->in_room )
      char_to_room( d->character, d->character->was_in_room );
  }

  if ( d->character->in_room )
  {
    d->character->was_in_room = d->character->in_room;
    char_from_room( d->character );
  }

  d->original = d->character;

  /*
   * These are the freaks who go link_dead (and lose their descriptor)
   * from inside chat mode.
   * -- Altrag
   */
  d->character->next = old_chars;
  old_chars = d->character;

  ch = alloc_mem( sizeof( *ch ) );
/*  *ch = *d->character;*/
  ch->desc = d;
  ch->name = str_dup( d->character->name );
  ch->act = d->character->act;
  ch->sex = d->character->sex;
  ch->position = POS_STANDING;
  ch->hit = 1;
  ch->max_hit = 1;

  ch->next = chat_list;
  chat_list = ch;

/*  char_to_room( ch, chat_rooms->pRoom );*/

  /*
   * These are still stored on d->original.  We don't need them here.
   * This will also stop any funky residue from the game.
   * -- Altrag
   */
/*  ch->reply = NULL;
  ch->affected = NULL;
  ch->affected2 = NULL;
  ch->carrying = NULL;
  ch->master = NULL;
  ch->leader = NULL;
  ch->fighting = NULL;
  ch->hunting = NULL;
  ch->gspell = NULL;
  ch->pcdata = NULL;
  ch->phobia = NULL;
  ch->in_room = NULL;
  ch->was_in_room = NULL;*/

  d->character = ch;
  d->connected = CON_CHATTING;
  char_to_room(ch, chat_rooms->pRoom);
  act(num_color(ch), "$n &Yhas entered the room.", ch, NULL, NULL, TO_ROOM);
  send_room_stuff( ch );
  return;
}

void stop_chat_mode( CHAR_DATA *ch )
{
  DESCRIPTOR_DATA *d;
  struct chat_room *room;

  d = ch->desc;

  act(num_color(ch), "$n &Yhas left the room.", ch, NULL, NULL, TO_ROOM);
  char_from_room( ch );

  if ( ch == chat_list )
    chat_list = ch->next;
  else
  {
    CHAR_DATA *gch;

    for ( gch = chat_list; gch; gch = gch->next )
      if ( gch->next == ch )
	break;
    if ( gch )
      gch->next = ch->next;
  }

  for ( room = chat_rooms; room; room = room->next )
    if (!str_prefix( ch->name, room->pRoom->name ) ||
	 is_name( ch->name, room->pRoom->name ))
      break;
  if ( room )
  {
    if ( room == chat_rooms )
      chat_rooms = room->next;
    else
    {
      struct chat_room *rprev;
	
      for ( rprev = chat_rooms; rprev; rprev = rprev->next )
	if ( rprev->next == room )
	  break;
      if ( rprev )
	rprev->next = room->next;
      if ( !rprev->next )
	last_chat_room = rprev;
    }
    dispose_room(room);
  }

/*  save_char_chat( ch );*/
  free_ch( ch );

  if ( !d )
  {
    return;
  }

  if ( !d->original )
  {
    close_socket( d );
    return;
  }

  d->character = d->original;
  d->original = NULL;

  if ( d->character == old_chars )
    old_chars = old_chars->next;
  else
  {
    CHAR_DATA *gch;

    for ( gch = old_chars; gch; gch = gch->next )
      if ( gch->next == d->character )
	break;
    if ( gch )
      gch->next = d->character->next;
  }

  d->character->next = char_list;
  char_list = d->character;

  if ( d->character->was_in_room )
    char_to_room(d->character, d->character->was_in_room);
  else
    char_to_room(d->character, get_room_index(ROOM_VNUM_TEMPLE) );

  d->character->was_in_room = NULL;
  d->connected = CON_PLAYING;
  do_look(d->character, "");

  return;
}

void chat_interp( CHAR_DATA *ch, char *argument )
{
  char command = 0;
  char arg[MAX_STRING_LENGTH];

  while ( isspace(*argument) )
    ++argument;

  if ( !*argument )
  {
    send_room_stuff( ch );
    return;
  }

  if ( *argument == '/' )
  {
    argument++;
    while (isspace(*argument))
      ++argument;
    command = *argument;
    argument++;
    while (isspace(*argument))
      ++argument;
  }

  if ( command )
  {
    chat_command( ch, command, argument );
    return;
  }

  argument = one_argument( argument, arg );

  if ( arg[0] == '.' && check_social( ch, arg + 1, argument ) )
    return;

  act( num_color(ch), "$n: &G$t $T", ch, arg, argument, TO_ROOM );
  send_to_char( AT_RED, "-- &CMessage sent &R--\n\r",ch);
  return;
}

void chat_command( CHAR_DATA *ch, char command, char *argument )
{
  char arg[MAX_STRING_LENGTH];
  CHAR_DATA *victim = NULL;
  struct chat_room *room = NULL;
  int rcount = 0;

  arg[0] = '\0';

  switch( UPPER(command) )
  {
  case 'Q':
    stop_chat_mode( ch );
    return;
  case 'P':
    argument = one_argument(argument, arg);
    while( isspace(*argument) )
      argument++;
    if ( !*argument )
    {
      send_to_char(C_DEFAULT, "Send what message?\n\r",ch);
      return;
    }
    if ( !(victim = get_char_chat(ch, arg)) )
    {
      send_to_char(C_DEFAULT, "They aren't here.\n\r",ch);
      return;
    }
    sprintf(arg, "(&RPRIV&C)&%s$n: &Y$t", get_color(ch) );
    act(AT_LBLUE, arg, ch, argument, victim,
       (ch == victim ? TO_CHAR : TO_VICT));
    act(AT_RED,"-- &CMessage sent only to &$t$N &R--", ch, get_color(ch),
	victim,	TO_CHAR);
    return;
  case 'J':
    argument = one_argument(argument, arg);
    if ( arg[0] == '\0' )
    {
      bool ToMain = FALSE;

      if ( !str_prefix( ch->name, ch->in_room->name ) || 
	   is_name( ch->name, ch->in_room->name ) )
	ToMain = TRUE;
      act( num_color(ch), "$n &Yhas left the room.", ch, NULL, NULL, TO_ROOM);
      char_from_room(ch);
      if ( ToMain )
      {
	char_to_room( ch, chat_rooms->pRoom );
	send_room_stuff(ch);
	act(num_color(ch), "$n &Yhas entered the room.", ch, NULL, NULL,
	    TO_ROOM);
	return;
      }
      for ( room = chat_rooms; room; room = room->next )
      {
	if ( is_name( ch->name, room->pRoom->name ) )
	{
	  char_to_room(ch, room->pRoom);
	  send_room_stuff(ch);
	  act( num_color(ch), "$n &Yhas entered the room.", ch, NULL, NULL,
	       TO_ROOM);
	  return;
	}
      }
      room = alloc_mem( sizeof( *room ) );
      room->invited = str_dup("");
      room->pRoom = alloc_mem( sizeof( *room->pRoom ) );
      room->pRoom->name = str_dup( ch->name );
      sprintf( arg, "%s'%s room.", ch->name,
	      (ch->name[strlen(ch->name)-1] == 's' ? "" : "s" ));
      room->pRoom->description = str_dup( arg );
      room->pRoom->people = NULL;
      last_chat_room->next = room;
      last_chat_room = room;
      char_to_room(ch, room->pRoom);
      send_to_char(AT_RED, "!! &CRoom Created &R!!\n\r", ch);
      send_room_stuff(ch);
      return;
    }
    if ( !str_prefix( arg, ch->name ) || is_name( arg, ch->name ) ||
	 !str_cmp( arg, "self" ) )
    {
      chat_command( ch, 'j', "" );
      return;
    }
    for ( room = chat_rooms; room; room = room->next )
      if ( !str_prefix( arg, room->pRoom->name ) ||
	    is_name( arg, room->pRoom->name ) )
      {
	act( num_color(ch), "$n &Yhas left the room.",ch, NULL, NULL, TO_ROOM);
	char_from_room(ch);
	char_to_room(ch, room->pRoom);
	send_room_stuff(ch);
	act( num_color(ch), "$n &Yhas entered the room.",ch,NULL,NULL,TO_ROOM);
	return;
      }
    send_to_char(AT_RED, "!! &WRoom does not exist &R!!\n\r",ch);
    return;
  case 'R':
    for ( room = chat_rooms; room; room = room->next )
    {
      sprintf( arg+strlen(arg), "%-16s&R%s&C\n\r", room->pRoom->name,
	       room->pRoom->description );
      rcount++;
    }
    sprintf(arg+strlen(arg), "There %s %d active room%s.\n\r",
	   (rcount == 1 ? "is" : "are"),
	    rcount,
	   (rcount == 1 ? "s" : ""));
    send_to_char( AT_LBLUE, arg, ch );
    return;
  case 'T':
    for ( room = chat_rooms; room; room = room->next )
      if ( room->pRoom == ch->in_room )
	break;
    if ( !room )
      return;
    if ( !is_name( ch->name, room->pRoom->name ) )
    {
      send_to_char(AT_GREEN, "You are not in your room.\n\r",ch);
      return;
    }
    free_string(room->pRoom->description);
    room->pRoom->description = str_dup( argument );
    act(num_color(ch), "$n has changed the topic to '$t'.",ch,
	argument, NULL, TO_ROOM );
    return;
  case 'W':
    for ( victim = chat_list; victim; victim = victim->next )
    {
      sprintf(arg+strlen(arg), "&%s%-16s&R%-13s&P%s&C\n\r", get_color(victim),
	      victim->name, victim->in_room->name,
	      victim->in_room->description);
      rcount++;
    }
    sprintf(arg+strlen(arg), "There %s %d %s in the conference.\n\r",
	   (rcount == 1 ? "is" : "are"),
	    rcount,
	   (rcount == 1 ? "person" : "people"));
    send_to_char(AT_CYAN, arg, ch);
    return;
  case 'H':
  case '?':
    send_to_char(AT_LBLUE, "/h     &GThis help screen\n\r", ch);
    send_to_char(AT_LBLUE, "/?     &GSame as /h\n\r",ch);
    send_to_char(AT_LBLUE, "/j[]   &GJoin a channel\n\r", ch);
    send_to_char(AT_LBLUE, "/p<>   &GSend a private message\n\r",ch);
    send_to_char(AT_LBLUE, "/<>    &GSame as /p\n\r",ch);
    send_to_char(AT_LBLUE, "/q     &GQuit the conference\n\r",ch);
    send_to_char(AT_LBLUE, "/r     &GList active rooms\n\r",ch);
    send_to_char(AT_LBLUE, "/t()   &GSet room topic\n\r",ch);
    send_to_char(AT_LBLUE, "/w     &GList people in conference\n\r\n\r",ch);
    send_to_char(AT_LBLUE, "[] &P= channel name; use /r for list\n\r",ch);
    send_to_char(AT_LBLUE, "<> &P= user name; use /w for list\n\r",ch);
    send_to_char(AT_LBLUE, "() &P= any character string\n\r",ch);
    return;
  default:
/*    sprintf(arg, "$n: &G/%c$t", command);
    act( num_color(ch), arg, ch, argument, NULL, TO_ROOM );
    send_to_char( AT_RED, "-- &CMessage sent &R--\n\r",ch);*/
    sprintf( arg, "%c%s", command, argument );
    chat_command( ch, 'p', arg );
    return;
  }
  return;
}

CHAR_DATA *get_char_chat( CHAR_DATA *ch, char *name )
{
  CHAR_DATA *vch;

  if ( !str_prefix( name, ch->name ) || is_name( name, ch->name ) )
    return ch;

  for ( vch = ch->in_room->people; vch; vch = vch->next_in_room )
    if ( !str_prefix( name, vch->name ) || is_name( name, vch->name ) )
      return vch;

  for ( vch = chat_list; vch; vch = vch->next )
    if ( !str_prefix( name, vch->name ) || is_name( name, vch->name ) )
      return vch;

  return NULL;
}

void send_room_stuff( CHAR_DATA *ch )
{
  int width = 0;
  CHAR_DATA *vch;
  if ( !ch->in_room )
    return;

  send_to_char( AT_WHITE, ch->in_room->name, ch );
  send_to_char( C_DEFAULT, "\n\r", ch );
  if ( ch->in_room->description[0] != '\0' )
    send_to_char(AT_YELLOW, ch->in_room->description, ch );
  send_to_char( C_DEFAULT, "\n\r", ch );
  for ( vch = ch->in_room->people; vch; vch = vch->next_in_room )
  {
    width += strlen( vch->name );
    if ( width >= 79 )
    {
      send_to_char(C_DEFAULT, "\n\r", ch );
      width = 0;
    }
    send_to_char(num_color(vch), vch->name, ch );
    send_to_char(C_DEFAULT, " ", ch );
  }
  send_to_char(C_DEFAULT, "\n\r\n\r", ch );
  return;
}

void do_conference( CHAR_DATA *ch, char *argument )
{
  if ( IS_NPC(ch) && (!ch->desc || !ch->desc->original ) )
    return;
  if ( ch->fighting || ch->position == POS_FIGHTING )
  {
    send_to_char( AT_WHITE, "No way!  You are fighting!.\n\r",ch);
    return;
  }
  if ( ch->combat_timer )
  {
    send_to_char(AT_WHITE, "You can't right now.\n\r",ch);
    return;
  }
  if ( ch->position < POS_STUNNED && ch->level < L_APP )
  {
    send_to_char(AT_WHITE, "You're not DEAD yet!\n\r",ch);
    return;
  }
  if ( IS_SET( ch->act, PLR_QUESTOR ) )
    REMOVE_BIT( ch->act, PLR_QUESTOR );
  ch->hunting = NULL;
  save_char_obj( ch, FALSE );
  start_chat_mode( ch->desc );
}

void init_chat( void )
{
  if ( chat_rooms )
    return;

  chat_rooms = alloc_mem( sizeof( *chat_rooms ) );
  chat_rooms->pRoom = alloc_mem(sizeof(*chat_rooms->pRoom));
  chat_rooms->invited = str_dup("");
  chat_rooms->pRoom->name = str_dup( "Main" );
  chat_rooms->pRoom->description = str_dup( "&GEye of the &BS&Ct&Wo&Cr&Bm "
					    "&GMain teleconference channel" );
  last_chat_room = chat_rooms;
  return;
}

int num_color( CHAR_DATA *ch )
{
  switch ( ch->sex )
  {
  case SEX_NEUTRAL:
    return AT_GREEN;
  case SEX_MALE:
    return AT_BLUE;
  case SEX_FEMALE:
    return AT_RED;
  }
  return C_DEFAULT;
}

char *get_color( CHAR_DATA *ch )
{
  switch ( ch->sex )
  {
  case SEX_NEUTRAL:
    return "G";
  case SEX_MALE:
    return "B";
  case SEX_FEMALE:
    return "R";
  }
  return "w";
}

/*
 * Update the chat_list and the old_chars lists.  Just kicks out linkdeads
 * if this becomes too much lag, you could just make a modified version
 * of check_reconnect in comm.c to check those lists as well as the char_list
 * list.  -- Altrag
 */
void chat_update( void )
{
  CHAR_DATA *ch;
  CHAR_DATA *ch_next;

  for ( ch = old_chars; ch; ch = ch_next )
  {
    ch_next = ch->next;

    if ( !ch->desc )  /* i.e. They went into chat but dropped link */
    {
      if ( ch == old_chars )
	old_chars = ch->next;
      else
      {
	CHAR_DATA *och;

	for ( och = old_chars; och; och = och->next )
	  if ( och->next == ch )
	    break;
	if ( och )
	  och->next = ch->next;
      }
      ch->next = char_list;
      char_list = ch;
      if ( ch->was_in_room )
	char_to_room( ch, ch->was_in_room );
    }
  }
  for ( ch = chat_list; ch; ch = ch_next )
  {
    ch_next = ch->next;
    if ( !ch->desc )
    {
      stop_chat_mode( ch );
    }
  }
  return;
}

void dispose_room( struct chat_room *room )
{
  CHAR_DATA *ch;
  CHAR_DATA *ch_next;

  for ( ch = room->pRoom->people; ch; ch = ch_next )
  {
    ch_next = ch->next_in_room;
    chat_command(ch, 'j', "Main");
  }
  free_string(room->pRoom->name);
  free_string(room->pRoom->description);
  free_mem(room->pRoom, sizeof(*room->pRoom));
  free_string(room->invited);
  free_mem(room, sizeof(*room));
  return;
}