swr2.0/
swr2.0/area/
swr2.0/boards/
swr2.0/clans/
swr2.0/doc/
swr2.0/planets/
swr2.0/spacecraft/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "mud.h"

/*
 * Local functions.
 */
void talk_channel( CHAR_DATA * ch, const char *argument,
    int channel, const char *verb );
const char *drunk_speech( const char *argument, CHAR_DATA * ch );

void sound_to_room( const ROOM_INDEX_DATA * room, const char *argument )
{
  CHAR_DATA *vic = NULL;

  if( room == NULL )
    return;

  for( vic = room->first_person; vic; vic = vic->next_in_room )
    if( !IS_NPC( vic ) && IS_SET( vic->act, PLR_SOUND ) )
      send_to_char( argument, vic );
}

void do_beep( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim = NULL;
  char arg[MAX_STRING_LENGTH];
  char buf[MAX_STRING_LENGTH];

  argument = one_argument( argument, arg );

  REMOVE_BIT( ch->deaf, CHANNEL_TELLS );

  if( !IS_NPC( ch )
      && ( IS_SET( ch->act, PLR_SILENCE )
	|| IS_SET( ch->act, PLR_NO_TELL ) ) )
  {
    send_to_char( "You can't do that.\r\n", ch );
    return;
  }

  if( arg[0] == '\0' )
  {
    send_to_char( "Beep who?\r\n", ch );
    return;
  }

  if( ( victim = get_char_world( ch, arg ) ) == NULL
      || ( IS_NPC( victim ) && victim->in_room != ch->in_room )
      || ( !NOT_AUTHED( ch ) && NOT_AUTHED( victim ) && !IS_IMMORTAL( ch ) ) )
  {
    send_to_char( "They aren't here.\r\n", ch );
    return;
  }

  if( !character_has_comlink( ch ) )
  {
    send_to_char( "You need a comlink to do that!\r\n", ch );
    return;
  }

  if( !character_has_comlink( victim ) && !IS_IMMORTAL( ch ) )
  {
    send_to_char( "They don't seem to have a comlink!\r\n", ch );
    return;
  }

  if( NOT_AUTHED( ch ) && !NOT_AUTHED( victim ) && !IS_IMMORTAL( victim ) )
  {
    send_to_char( "They can't hear you because you are not authorized.\r\n",
	ch );
    return;
  }

  if( !IS_NPC( victim ) && ( victim->switched ) && IS_IMMORTAL( ch ) )
  {
    send_to_char( "That player is switched.\r\n", ch );
    return;
  }
  else if( !IS_NPC( victim ) && ( !victim->desc ) )
  {
    send_to_char( "That player is link-dead.\r\n", ch );
    return;
  }

  if( IS_SET( victim->deaf, CHANNEL_TELLS )
      && ( !IS_IMMORTAL( ch ) || ( get_trust( ch ) < get_trust( victim ) ) ) )
  {
    act( AT_PLAIN, "$E has $S tells turned off.", ch, NULL, victim,
	TO_CHAR );
    return;
  }

  if( !IS_NPC( victim ) && ( IS_SET( victim->act, PLR_SILENCE ) ) )
  {
    send_to_char
      ( "That player is silenced.  They will receive your message but can not respond.\r\n",
	ch );
  }

  if( !IS_IMMORTAL( ch ) && !IS_AWAKE( victim ) )
  {
    act( AT_PLAIN, "$E can't hear you.", ch, 0, victim, TO_CHAR );
    return;
  }

  if( victim->desc		/* make sure desc exists first  -Thoric */
      && victim->desc->connected == CON_EDITING && IS_IMMORTAL( ch ) )
  {
    act( AT_PLAIN,
	"$E is currently in a writing buffer.  Please try again in a few minutes.",
	ch, 0, victim, TO_CHAR );
    return;
  }

  ch_printf( ch, "&WYou beep %s: %s\r\n\a", victim->name, argument );
  send_to_char( "\a", victim );

  snprintf( buf, MAX_STRING_LENGTH, "%s beeps: '$t'", ch->name );

  act( AT_WHITE, buf, ch, argument, victim, TO_VICT );
}

const char *drunk_speech( const char *argument, CHAR_DATA * ch )
{
  const char *arg = argument;
  static char buf[MAX_INPUT_LENGTH * 2];
  char buf1[MAX_INPUT_LENGTH * 2];
  short drunk;
  char *txt;
  char *txt1;

  if( IS_NPC( ch ) || !ch->pcdata )
    return argument;

  drunk = ch->pcdata->condition[COND_DRUNK];

  if( drunk <= 0 )
    return ( char * ) argument;

  buf[0] = '\0';
  buf1[0] = '\0';

  if( !argument )
  {
    bug( "Drunk_speech: NULL argument", 0 );
    return "";
  }

  /*
     if ( *arg == '\0' )
     return (char *) argument;
     */

  txt = buf;
  txt1 = buf1;

  while( *arg != '\0' )
  {
    if( toupper( ( int ) *arg ) == 'S' )
    {
      if( number_percent() < ( drunk * 2 ) )	/* add 'h' after an 's' */
      {
	*txt++ = *arg;
	*txt++ = 'h';
      }
      else
      {
	*txt++ = *arg;
      }
    }
    else if( toupper( ( int ) *arg ) == 'X' )
    {
      if( number_percent() < ( drunk * 2 / 2 ) )
      {
	*txt++ = 'c', *txt++ = 's', *txt++ = 'h';
      }
      else
      {
	*txt++ = *arg;
      }
    }
    else if( number_percent() < ( drunk * 2 / 5 ) )	/* slurred letters */
    {
      int slurn = number_range( 1, 2 );
      short currslur = 0;

      while( currslur < slurn )
	*txt++ = *arg, currslur++;
    }
    else
    {
      *txt++ = *arg;
    }

    arg++;
  }

  *txt = '\0';

  txt = buf;

  while( *txt != '\0' )		/* Let's mess with the string's caps */
  {
    if( number_percent() < ( 2 * drunk / 2.5 ) )
    {
      if( isupper( ( int ) *txt ) )
	*txt1 = ( char ) tolower( ( int ) *txt );
      else if( islower( ( int ) *txt ) )
	*txt1 = ( char ) toupper( ( int ) *txt );
      else
	*txt1 = *txt;
    }
    else
    {
      *txt1 = *txt;
    }

    txt1++, txt++;
  }

  *txt1 = '\0';
  txt1 = buf1;
  txt = buf;

  while( *txt1 != '\0' )	/* Let's make them stutter */
  {
    if( *txt1 == ' ' )	/* If there's a space, then there's gotta be a */
    {			/* along there somewhere soon */

      while( *txt1 == ' ' )	/* Don't stutter on spaces */
	*txt++ = *txt1++;

      if( ( number_percent() < ( 2 * drunk / 4 ) ) && *txt1 != '\0' )
      {
	int offset = number_range( 0, 2 );
	short pos = 0;

	while( *txt1 != '\0' && pos < offset )
	  *txt++ = *txt1++, pos++;

	if( *txt1 == ' ' )	/* Make sure not to stutter a space after */
	{		/* the initial offset into the word */
	  *txt++ = *txt1++;
	  continue;
	}

	pos = 0;
	offset = number_range( 2, 4 );

	while( *txt1 != '\0' && pos < offset )
	{
	  *txt++ = *txt1;
	  pos++;

	  if( *txt1 == ' ' || pos == offset )	/* Make sure we don't stick */
	  {		/* A hyphen right before a space */
	    txt1--;
	    break;
	  }
	  *txt++ = '-';
	}

	if( *txt1 != '\0' )
	  txt1++;
      }
    }
    else
    {
      *txt++ = *txt1++;
    }
  }

  *txt = '\0';

  return buf;
}

static void broadcast_channel_default( CHAR_DATA * ch, const char *verb,
    const char *argument, char *buf, size_t len )
{
  set_char_color( AT_GOSSIP, ch );
  ch_printf( ch, "(%s) %s: %s\r\n", verb, ch->name, argument );
  snprintf( buf, len, "(%s) %s: $t",
      verb, IS_IMMORTAL( ch ) ? "$n" : ch->name );
}

static void broadcast_channel_clantalk( CHAR_DATA * ch, const char *verb,
    const char *argument, char *buf, size_t len )
{
  set_char_color( AT_CLAN, ch );
  ch_printf( ch, "Over the organizations private network you say, '%s'\r\n",
      argument );
  snprintf( buf, len, "%s speaks over the organizations network, '$t'",
      IS_IMMORTAL( ch ) ? "$n" : ch->name );
}

static void broadcast_channel_ship( CHAR_DATA * ch, const char *verb,
    const char *argument, char *buf, size_t len )
{
  set_char_color( AT_SHIP, ch );
  ch_printf( ch, "You tell the ship, '%s'\r\n", argument );
  snprintf( buf, len, "%s says over the ships com system, '$t'",
      IS_IMMORTAL( ch ) ? "$n" : ch->name );
}

static void broadcast_channel_yell( CHAR_DATA * ch, const char *verb,
    const char *argument, char *buf, size_t len )
{
  set_char_color( AT_GOSSIP, ch );
  ch_printf( ch, "You %s, '%s'\r\n", verb, argument );
  snprintf( buf, len, "%s %ss, '$t'",
      IS_IMMORTAL( ch ) ? "$n" : ch->name, verb );
}

static void broadcast_channel_newbie( CHAR_DATA * ch, const char *verb,
    const char *argument, char *buf, size_t len )
{
  set_char_color( AT_OOC, ch );
  ch_printf( ch, "(NEWBIE) %s: %s\r\n", ch->name, argument );
  snprintf( buf, len, "(NEWBIE) %s: $t", IS_IMMORTAL( ch ) ? "$n" : ch->name );
}

static void broadcast_channel_ooc( CHAR_DATA * ch, const char *verb,
    const char *argument, char *buf, size_t len )
{
  short position = ch->position;
  set_char_color( AT_OOC, ch );
  snprintf( buf, len, "(OOC) %s: $t", IS_IMMORTAL( ch ) ? "$n" : ch->name );
  ch->position = POS_STANDING;
  act( AT_OOC, buf, ch, argument, NULL, TO_CHAR );
  ch->position = position;
}

static void broadcast_channel_immtalk( CHAR_DATA * ch, const char *verb,
    const char *argument, char *buf, size_t len )
{
  short position = ch->position;
  snprintf( buf, len, "%s> $t", IS_IMMORTAL( ch ) ? "$n" : ch->name );
  ch->position = POS_STANDING;
  act( AT_IMMORT, buf, ch, argument, NULL, TO_CHAR );
  ch->position = position;
}

bool character_has_comlink( const CHAR_DATA * ch )
{
  bool ch_comlink = FALSE;

  if( IS_IMMORTAL( ch ) )
  {
    ch_comlink = TRUE;
  }
  else
  {
    if( get_obj_type_char( ch, ITEM_COMLINK ) )
    {
      ch_comlink = TRUE;
    }
  }

  return ch_comlink;
}

bool channel_needs_comlink( int channel )
{
  bool needs_comlink = FALSE;

  if( channel != CHANNEL_YELL
      && channel != CHANNEL_IMMTALK
      && channel != CHANNEL_OOC
      && channel != CHANNEL_NEWBIE
      && channel != CHANNEL_SHIP && channel != CHANNEL_SYSTEM )
  {
    needs_comlink = TRUE;
  }

  return needs_comlink;
}

/*
 * Generic channel function.
 */
void talk_channel( CHAR_DATA * ch, const char *argument, int channel,
    const char *verb )
{
  char buf[MAX_STRING_LENGTH];
  DESCRIPTOR_DATA *d = NULL;
  short position = 0;
  CLAN_DATA *clan = NULL;
  PLANET_DATA *planet = NULL;

  if( channel_needs_comlink( channel ) && !character_has_comlink( ch ) )
  {
    send_to_char( "You need a comlink to do that!\r\n", ch );
    return;
  }

  if( IS_NPC( ch ) && channel == CHANNEL_CLAN )
  {
    send_to_char( "Mobs can't be in clans.\r\n", ch );
    return;
  }

  if( channel == CHANNEL_PNET &&
      ( !ch->in_room->area
	|| ( planet = ch->in_room->area->planet ) == NULL ) )
  {
    send_to_char( "Planet Net only works on planets...\r\n", ch );
    return;
  }

  if( channel == CHANNEL_CLAN )
    clan = ch->pcdata->clan;

  if( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) )
  {
    if( ch->master )
      send_to_char( "I don't think so...\r\n", ch->master );
    return;
  }

  if( argument[0] == '\0' )
  {
    snprintf( buf, MAX_STRING_LENGTH, "%s what?\r\n", verb );
    buf[0] = UPPER( buf[0] );
    send_to_char( buf, ch );	/* where'd this line go? */
    return;
  }

  if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_SILENCE ) )
  {
    ch_printf( ch, "You can't %s.\r\n", verb );
    return;
  }

  REMOVE_BIT( ch->deaf, channel );

  switch ( channel )
  {
    default:
      broadcast_channel_default( ch, verb, argument, buf, MAX_STRING_LENGTH );
      break;

    case CHANNEL_CLANTALK:
      broadcast_channel_clantalk( ch, verb, argument, buf, MAX_STRING_LENGTH );
      break;

    case CHANNEL_SHIP:
      broadcast_channel_ship( ch, verb, argument, buf, MAX_STRING_LENGTH );
      break;

    case CHANNEL_YELL:
      broadcast_channel_yell( ch, verb, argument, buf, MAX_STRING_LENGTH );
      break;

    case CHANNEL_NEWBIE:
      broadcast_channel_newbie( ch, verb, argument, buf, MAX_STRING_LENGTH );
      break;

    case CHANNEL_OOC:
      broadcast_channel_ooc( ch, verb, argument, buf, MAX_STRING_LENGTH );
      break;

    case CHANNEL_IMMTALK:
      broadcast_channel_immtalk( ch, verb, argument, buf, MAX_STRING_LENGTH );
      break;
  }

  for( d = first_descriptor; d; d = d->next )
  {
    CHAR_DATA *och = d->original ? d->original : d->character;
    CHAR_DATA *vch = d->character;

    if( d->connected == CON_PLAYING
	&& vch != ch && !IS_SET( och->deaf, channel ) )
    {
      const char *sbuf = argument;

      if( channel_needs_comlink( channel )
	  && !character_has_comlink( ch ) )
      {
	continue;
      }

      if( channel == CHANNEL_IMMTALK && !IS_IMMORTAL( och ) )
	continue;

      if( channel == CHANNEL_YELL && ch->in_room != vch->in_room )
	continue;

      if( channel == CHANNEL_PNET )
      {
	if( !vch->in_room
	    || !vch->in_room->area
	    || ( vch->in_room->area->planet != planet ) )
	  continue;
      }

      if( channel == CHANNEL_CLAN
	  && ( IS_NPC( vch )
	    || !vch->pcdata->clan || vch->pcdata->clan != clan ) )
	continue;

      if( channel == CHANNEL_SYSTEM )
      {
	SHIP_DATA *ship = ship_from_cockpit( ch->in_room );
	SHIP_DATA *target = NULL;

	if( !ship )
	  continue;

	if( !vch->in_room )
	  continue;

	target = ship_from_cockpit( vch->in_room );

	if( !target )
	  continue;

	if( channel == CHANNEL_SYSTEM )
	  if( target->starsystem != ship->starsystem )
	    continue;
      }

      if( channel == CHANNEL_SHIP )
      {
	SHIP_DATA *ship = ship_from_room( ch->in_room );
	SHIP_DATA *target = NULL;

	if( !ship )
	  continue;

	if( !vch->in_room )
	  continue;

	target = ship_from_room( vch->in_room );

	if( !target )
	  continue;

	if( target != ship )
	  continue;
      }

      position = vch->position;

      if( channel != CHANNEL_YELL )
	vch->position = POS_STANDING;

      MOBtrigger = FALSE;

      if( channel == CHANNEL_IMMTALK )
	act( AT_IMMORT, buf, ch, sbuf, vch, TO_VICT );
      else if( channel == CHANNEL_OOC || channel == CHANNEL_NEWBIE )
	act( AT_OOC, buf, ch, sbuf, vch, TO_VICT );
      else if( channel == CHANNEL_SHIP )
	act( AT_SHIP, buf, ch, sbuf, vch, TO_VICT );
      else if( channel == CHANNEL_CLAN )
	act( AT_CLAN, buf, ch, sbuf, vch, TO_VICT );
      else
	act( AT_GOSSIP, buf, ch, sbuf, vch, TO_VICT );

      vch->position = position;
    }
  }
}

void to_channel( const char *argument, int channel,
    const char *verb, short level )
{
  char buf[MAX_STRING_LENGTH];
  DESCRIPTOR_DATA *d;

  if( !first_descriptor || argument[0] == '\0' )
    return;

  snprintf( buf, MAX_STRING_LENGTH, "%s: %s\r\n", verb, argument );

  for( d = first_descriptor; d; d = d->next )
  {
    CHAR_DATA *och = d->original ? d->original : d->character;
    CHAR_DATA *vch = d->character;

    if( !och || !vch )
      continue;

    if( !IS_IMMORTAL( vch )
	&& ( channel == CHANNEL_LOG || channel == CHANNEL_COMM ) )
      continue;

    if( d->connected == CON_PLAYING
	&& !IS_SET( och->deaf, channel ) && get_trust( vch ) >= level )
    {
      set_char_color( AT_LOG, vch );
      send_to_char( buf, vch );
      set_char_color( AT_PLAIN, vch );
    }
  }
}

void do_gnet( CHAR_DATA * ch, char *argument )
{
  talk_channel( ch, argument, CHANNEL_GNET, "GNET" );
}

void do_pnet( CHAR_DATA * ch, char *argument )
{
  talk_channel( ch, argument, CHANNEL_PNET, "PNET" );
}

void do_shiptalk( CHAR_DATA * ch, char *argument )
{
  SHIP_DATA *ship = ship_from_cockpit( ch->in_room );

  if( !ship )
  {
    send_to_char( "&RYou must be in the cockpit of a ship to do that!\r\n",
	ch );
    return;
  }

  talk_channel( ch, argument, CHANNEL_SHIP, "shiptalk" );
}

void do_systemtalk( CHAR_DATA * ch, char *argument )
{
  SHIP_DATA *ship = ship_from_cockpit( ch->in_room );

  if( !ship )
  {
    send_to_char( "&RYou must be in the cockpit of a ship to do that!\r\n",
	ch );
    return;
  }

  talk_channel( ch, argument, CHANNEL_SYSTEM, "Systemtalk" );
}

void do_ooc( CHAR_DATA * ch, char *argument )
{
  talk_channel( ch, argument, CHANNEL_OOC, "ooc" );
}

void do_clantalk( CHAR_DATA * ch, char *argument )
{
  if( NOT_AUTHED( ch ) )
  {
    send_to_char( "Huh?\r\n", ch );
    return;
  }

  if( IS_NPC( ch ) || !ch->pcdata->clan )
  {
    send_to_char( "Huh?\r\n", ch );
    return;
  }

  talk_channel( ch, argument, CHANNEL_CLAN, "clantalk" );
}

void do_newbiechat( CHAR_DATA * ch, char *argument )
{
  talk_channel( ch, argument, CHANNEL_NEWBIE, "newbiechat" );
}

void do_yell( CHAR_DATA * ch, char *argument )
{
  if( NOT_AUTHED( ch ) )
  {
    send_to_char( "Huh?\r\n", ch );
    return;
  }

  talk_channel( ch, drunk_speech( argument, ch ), CHANNEL_YELL, "yell" );
}



void do_immtalk( CHAR_DATA * ch, char *argument )
{
  if( NOT_AUTHED( ch ) )
  {
    send_to_char( "Huh?\r\n", ch );
    return;
  }

  talk_channel( ch, argument, CHANNEL_IMMTALK, "immtalk" );
}


void do_say( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *vch = NULL;
  int actflags = 0;

  if( argument[0] == '\0' )
  {
    send_to_char( "Say what?\r\n", ch );
    return;
  }

  actflags = ch->act;

  if( IS_NPC( ch ) )
    REMOVE_BIT( ch->act, ACT_SECRETIVE );

  for( vch = ch->in_room->first_person; vch; vch = vch->next_in_room )
  {
    const char *sbuf = argument;

    if( vch == ch )
      continue;

    sbuf = drunk_speech( sbuf, ch );

    MOBtrigger = FALSE;
    act( AT_SAY, "$n says '$t'", ch, sbuf, vch, TO_VICT );
  }

  /*    MOBtrigger = FALSE;
	act( AT_SAY, "$n says '$T'", ch, NULL, argument, TO_ROOM ); */
  ch->act = actflags;
  MOBtrigger = FALSE;
  act( AT_SAY, "You say '$T'", ch, NULL, drunk_speech( argument, ch ),
      TO_CHAR );
  mprog_speech_trigger( argument, ch );

  if( char_died( ch ) )
    return;

  oprog_speech_trigger( argument, ch );

  if( char_died( ch ) )
    return;

  rprog_speech_trigger( argument, ch );
}



void do_tell( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  char buf[MAX_INPUT_LENGTH];
  CHAR_DATA *victim = NULL;
  short position = 0;
  CHAR_DATA *switched_victim = NULL;

  if( IS_SET( ch->deaf, CHANNEL_TELLS ) && !IS_IMMORTAL( ch ) )
  {
    act( AT_PLAIN, "You have tells turned off... try chan +tells first", ch,
	NULL, NULL, TO_CHAR );
    return;
  }

  if( !IS_NPC( ch )
      && ( IS_SET( ch->act, PLR_SILENCE )
	|| IS_SET( ch->act, PLR_NO_TELL ) ) )
  {
    send_to_char( "You can't do that.\r\n", ch );
    return;
  }

  argument = one_argument( argument, arg );

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

  if( ( victim = get_char_world( ch, arg ) ) == NULL
      || ( IS_NPC( victim ) && victim->in_room != ch->in_room )
      || ( !NOT_AUTHED( ch ) && NOT_AUTHED( victim ) && !IS_IMMORTAL( ch ) ) )
  {
    send_to_char( "They aren't here.\r\n", ch );
    return;
  }

  if( ch == victim )
  {
    send_to_char( "You have a nice little chat with yourself.\r\n", ch );
    return;
  }

  if( victim->in_room != ch->in_room && !IS_IMMORTAL( ch ) )
  {
    if( !character_has_comlink( ch ) )
    {
      send_to_char( "You need a comlink to do that!\r\n", ch );
      return;
    }

    if( !character_has_comlink( victim ) )
    {
      send_to_char( "They don't seem to have a comlink!\r\n", ch );
      return;
    }
  }

  if( NOT_AUTHED( ch ) && !NOT_AUTHED( victim ) && !IS_IMMORTAL( victim ) )
  {
    send_to_char
      ( "They can't hear you because you have not yet finished the academy.\r\n",
	ch );
    return;
  }

  if( !IS_NPC( victim ) && ( victim->switched )
      && ( IS_IMMORTAL( ch ) )
      && !IS_SET( victim->switched->act, ACT_POLYMORPHED )
      && !IS_AFFECTED( victim->switched, AFF_POSSESS ) )
  {
    send_to_char( "That player is switched.\r\n", ch );
    return;
  }
  else if( !IS_NPC( victim ) && ( victim->switched )
      && ( IS_SET( victim->switched->act, ACT_POLYMORPHED )
	|| IS_AFFECTED( victim->switched, AFF_POSSESS ) ) )
  {
    switched_victim = victim->switched;
  }
  else if( !IS_NPC( victim ) && ( !victim->desc ) )
  {
    send_to_char( "That player is link-dead.\r\n", ch );
    return;
  }

  if( !IS_NPC( victim ) && ( IS_SET( victim->act, PLR_AFK ) ) )
  {
    send_to_char( "That player is afk.\r\n", ch );
    return;
  }

  if( IS_SET( victim->deaf, CHANNEL_TELLS )
      && ( !IS_IMMORTAL( ch ) || ( get_trust( ch ) < get_trust( victim ) ) ) )
  {
    act( AT_PLAIN, "$E has $S tells turned off.", ch, NULL, victim,
	TO_CHAR );
    return;
  }

  if( !IS_NPC( victim ) && ( IS_SET( victim->act, PLR_SILENCE ) ) )
  {
    send_to_char
      ( "That player is silenced.  They will receive your message but can not respond.\r\n",
	ch );
  }

  if( !IS_IMMORTAL( ch ) && !IS_AWAKE( victim ) )
  {
    act( AT_PLAIN, "$E can't hear you.", ch, 0, victim, TO_CHAR );
    return;
  }

  if( victim->desc		/* make sure desc exists first  -Thoric */
      && victim->desc->connected == CON_EDITING && IS_IMMORTAL( ch ) )
  {
    act( AT_PLAIN,
	"$E is currently in a writing buffer.  Please try again in a few minutes.",
	ch, 0, victim, TO_CHAR );
    return;
  }

  if( switched_victim )
    victim = switched_victim;

  snprintf( buf, MAX_STRING_LENGTH, "You tell %s '$t'", victim->name );
  act( AT_TELL, buf, ch, argument, victim, TO_CHAR );
  position = victim->position;
  victim->position = POS_STANDING;
  snprintf( buf, MAX_STRING_LENGTH, "%s tells you '$t'", ch->name );
  act( AT_TELL, buf, ch, argument, victim, TO_VICT );
  victim->position = position;
  victim->reply = ch;
  mprog_speech_trigger( argument, ch );
}

void do_reply( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  CHAR_DATA *victim = NULL;
  short position = 0;
  REMOVE_BIT( ch->deaf, CHANNEL_TELLS );

  if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_SILENCE ) )
  {
    send_to_char( "Your message didn't get through.\r\n", ch );
    return;
  }

  if( ( victim = ch->reply ) == NULL )
  {
    send_to_char( "They aren't here.\r\n", ch );
    return;
  }

  if( !IS_NPC( victim ) && ( victim->switched )
      && can_see( ch, victim ) && IS_IMMORTAL( ch ) )
  {
    send_to_char( "That player is switched.\r\n", ch );
    return;
  }
  else if( !IS_NPC( victim ) && ( !victim->desc ) )
  {
    send_to_char( "That player is link-dead.\r\n", ch );
    return;
  }

  if( !IS_NPC( victim ) && ( IS_SET( victim->act, PLR_AFK ) ) )
  {
    send_to_char( "That player is afk.\r\n", ch );
    return;
  }

  if( IS_SET( victim->deaf, CHANNEL_TELLS )
      && ( !IS_IMMORTAL( ch ) || ( get_trust( ch ) < get_trust( victim ) ) ) )
  {
    act( AT_PLAIN, "$E has $S tells turned off.", ch, NULL, victim,
	TO_CHAR );
    return;
  }

  if( !IS_IMMORTAL( ch ) && !IS_AWAKE( victim ) )
  {
    act( AT_PLAIN, "$E can't hear you.", ch, 0, victim, TO_CHAR );
    return;
  }

  if( victim->in_room != ch->in_room && !IS_IMMORTAL( ch ) )
  {
    if( !character_has_comlink( ch ) )
    {
      send_to_char( "You need a comlink to do that!\r\n", ch );
      return;
    }

    if( !character_has_comlink( victim ) )
    {
      send_to_char( "They don't seem to have a comlink!\r\n", ch );
      return;
    }
  }

  snprintf( buf, MAX_STRING_LENGTH, "You tell %s '$t'", victim->name );
  act( AT_TELL, buf, ch, argument, victim, TO_CHAR );
  position = victim->position;
  victim->position = POS_STANDING;
  snprintf( buf, MAX_STRING_LENGTH, "%s tells you '$t'", ch->name );
  act( AT_TELL, buf, ch, argument, victim, TO_VICT );
  victim->position = position;
  victim->reply = ch;
}



void do_emote( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  const char *plast = NULL;
  int actflags = ch->act;

  if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_NO_EMOTE ) )
  {
    send_to_char( "You can't show your emotions.\r\n", ch );
    return;
  }

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

  if( IS_NPC( ch ) )
    REMOVE_BIT( ch->act, ACT_SECRETIVE );

  for( plast = argument; *plast != '\0'; plast++ )
    ;

  strcpy( buf, argument );

  if( isalpha( ( int ) plast[-1] ) )
    strcat( buf, "." );

  MOBtrigger = FALSE;
  act( AT_ACTION, "$n $T", ch, NULL, buf, TO_ROOM );
  MOBtrigger = FALSE;
  act( AT_ACTION, "$n $T", ch, NULL, buf, TO_CHAR );
  ch->act = actflags;
}

void do_bug( CHAR_DATA * ch, char *argument )
{
  if( !argument || argument[0] == '\0' )
  {
    send_to_char( "What do you want to submit as a bug?\n\r", ch );
    return;
  }

  append_file( ch, BUG_FILE, argument );
  send_to_char( "Ok.  Thanks.\n\r", ch );
}

void do_typo( CHAR_DATA * ch, char *argument )
{
  if( !argument || argument[0] == '\0' )
  {
    send_to_char( "What do you want to submit as an idea?\n\r", ch );
    return;
  }

  append_file( ch, TYPO_FILE, argument );
  send_to_char( "Ok.  Thanks.\r\n", ch );
}

void do_quit( CHAR_DATA * ch, char *argument )
{
  size_t x = 0, y = 0;

  if( IS_NPC( ch ) && IS_SET( ch->act, ACT_POLYMORPHED ) )
  {
    send_to_char( "You can't quit while polymorphed.\r\n", ch );
    return;
  }

  if( IS_NPC( ch ) )
    return;

  if( ch->position == POS_FIGHTING )
  {
    set_char_color( AT_RED, ch );
    send_to_char( "No way! You are fighting.\r\n", ch );
    return;
  }

  if( ch->position < POS_STUNNED )
  {
    set_char_color( AT_BLOOD, ch );
    send_to_char( "You're not DEAD yet.\r\n", ch );
    return;
  }

  if( auction->item != NULL
      && ( ( ch == auction->buyer ) || ( ch == auction->seller ) ) )
  {
    send_to_char
      ( "Wait until you have bought/sold the item on auction.\r\n", ch );
    return;
  }

  if( !IS_IMMORTAL( ch ) && ch->in_room
      && !IS_SET( ch->in_room->room_flags, ROOM_HOTEL ) && !NOT_AUTHED( ch ) )
  {
    send_to_char( "You may not quit here.\r\n", ch );
    send_to_char
      ( "You will have to find a safer resting place such as a hotel...\r\n",
	ch );
    send_to_char( "Maybe you could HAIL a speeder.\r\n", ch );
    return;
  }

  set_char_color( AT_WHITE, ch );
  send_to_char
    ( "Your surroundings begin to fade as a mystical swirling vortex of colors\r\nenvelops your body... When you come to, things are not as they were.\r\n\r\n",
      ch );
  act( AT_SAY, "A strange voice says, 'We await your return, $n...'", ch,
      NULL, NULL, TO_CHAR );
  act( AT_BYE, "$n has left the game.", ch, NULL, NULL, TO_ROOM );
  set_char_color( AT_GREY, ch );

  snprintf( log_buf, MAX_STRING_LENGTH, "%s has quit.", ch->name );
  quitting_char = ch;
  save_char_obj( ch );
  save_home( ch );
  saving_char = NULL;

  /*
   * After extract_char the ch is no longer valid!
   */
  extract_char( ch, TRUE );

  for( x = 0; x < MAX_WEAR; x++ )
    for( y = 0; y < MAX_LAYERS; y++ )
      save_equipment[x][y] = NULL;

  /* don't show who's logging off to leaving player */
  log_string_plus( log_buf, LOG_COMM );
}

void do_ansi( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    send_to_char( "ANSI ON or OFF?\r\n", ch );
    return;
  }

  if( ( strcmp( arg, "on" ) == 0 ) || ( strcmp( arg, "ON" ) == 0 ) )
  {
    SET_BIT( ch->act, PLR_ANSI );
    set_char_color( AT_WHITE + AT_BLINK, ch );
    send_to_char( "ANSI ON!!!\r\n", ch );
    return;
  }

  if( ( strcmp( arg, "off" ) == 0 ) || ( strcmp( arg, "OFF" ) == 0 ) )
  {
    REMOVE_BIT( ch->act, PLR_ANSI );
    send_to_char( "Okay... ANSI support is now off\r\n", ch );
    return;
  }
}

void do_save( CHAR_DATA * ch, char *argument )
{
  if( IS_NPC( ch ) && IS_SET( ch->act, ACT_POLYMORPHED ) )
  {
    send_to_char( "You can't save while polymorphed.\r\n", ch );
    return;
  }

  if( IS_NPC( ch ) )
    return;

  if( NOT_AUTHED( ch ) )
  {
    send_to_char
      ( "You can't save untill after you've graduated from the acadamey.\r\n",
	ch );
    return;
  }

  save_char_obj( ch );
  save_home( ch );
  saving_char = NULL;
  send_to_char( "Ok.\r\n", ch );
}

/*
 * Something from original DikuMUD that Merc yanked out.
 * Used to prevent following loops, which can cause problems if people
 * follow in a loop through an exit leading back into the same room
 * (Which exists in many maze areas)			-Thoric
 */
bool circle_follow( const CHAR_DATA * ch, const CHAR_DATA * victim )
{
  const CHAR_DATA *tmp = NULL;

  for( tmp = victim; tmp; tmp = tmp->master )
    if( tmp == ch )
      return TRUE;

  return FALSE;
}

void do_follow( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim;

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    send_to_char( "Follow whom?\r\n", ch );
    return;
  }

  if( ( victim = get_char_room( ch, arg ) ) == NULL )
  {
    send_to_char( "They aren't here.\r\n", ch );
    return;
  }

  if( IS_AFFECTED( ch, AFF_CHARM ) && ch->master )
  {
    act( AT_PLAIN, "But you'd rather follow $N!", ch, NULL, ch->master,
	TO_CHAR );
    return;
  }

  if( victim == ch )
  {
    if( !ch->master )
    {
      send_to_char( "You already follow yourself.\r\n", ch );
      return;
    }

    stop_follower( ch );
    return;
  }

  if( circle_follow( ch, victim ) )
  {
    send_to_char( "Following in loops is not allowed... sorry.\r\n", ch );
    return;
  }

  if( ch->master )
    stop_follower( ch );

  add_follower( ch, victim );
}

void add_follower( CHAR_DATA * ch, CHAR_DATA * master )
{
  if( ch->master )
  {
    bug( "Add_follower: non-null master.", 0 );
    return;
  }

  ch->master = master;
  ch->leader = NULL;

  if( can_see( master, ch ) )
    act( AT_ACTION, "$n now follows you.", ch, NULL, master, TO_VICT );

  act( AT_ACTION, "You now follow $N.", ch, NULL, master, TO_CHAR );
}

void stop_follower( CHAR_DATA * ch )
{
  if( !ch->master )
  {
    bug( "Stop_follower: null master.", 0 );
    return;
  }

  if( IS_AFFECTED( ch, AFF_CHARM ) )
  {
    REMOVE_BIT( ch->affected_by, AFF_CHARM );
    affect_strip( ch, gsn_charm_person );
  }

  if( can_see( ch->master, ch ) )
    act( AT_ACTION, "$n stops following you.", ch, NULL, ch->master,
	TO_VICT );

  act( AT_ACTION, "You stop following $N.", ch, NULL, ch->master, TO_CHAR );

  ch->master = NULL;
  ch->leader = NULL;
}

void die_follower( CHAR_DATA * ch )
{
  CHAR_DATA *fch = NULL;

  if( ch->master )
    stop_follower( ch );

  ch->leader = NULL;

  for( fch = first_char; fch; fch = fch->next )
  {
    if( fch->master == ch )
      stop_follower( fch );
    if( fch->leader == ch )
      fch->leader = fch;
  }
}

void do_order( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  char argbuf[MAX_INPUT_LENGTH];
  CHAR_DATA *victim = NULL;
  CHAR_DATA *och = NULL;
  CHAR_DATA *och_next = NULL;
  bool found = FALSE;
  bool fAll = FALSE;
  int toomany = 0;

  strcpy( argbuf, argument );
  argument = one_argument( argument, arg );

  if( arg[0] == '\0' || argument[0] == '\0' )
  {
    send_to_char( "Order whom to do what?\r\n", ch );
    return;
  }

  if( IS_AFFECTED( ch, AFF_CHARM ) )
  {
    send_to_char( "You feel like taking, not giving, orders.\r\n", ch );
    return;
  }

  if( !str_cmp( arg, "all" ) )
  {
    fAll = TRUE;
    victim = NULL;
  }
  else
  {
    fAll = FALSE;

    if( ( victim = get_char_room( ch, arg ) ) == NULL )
    {
      send_to_char( "They aren't here.\r\n", ch );
      return;
    }

    if( victim == ch )
    {
      send_to_char( "Aye aye, right away!\r\n", ch );
      return;
    }

    if( !IS_AFFECTED( victim, AFF_CHARM ) || victim->master != ch )
    {
      send_to_char( "Do it yourself!\r\n", ch );
      return;
    }
  }

  found = FALSE;

  for( och = ch->in_room->first_person; och; och = och_next )
  {
    och_next = och->next_in_room;

    if( IS_AFFECTED( och, AFF_CHARM )
	&& och->master == ch
	&& ( fAll || och == victim ) && !IS_IMMORTAL( och ) )
    {
      found = TRUE;
      act( AT_ACTION, "$n orders you to '$t'.", ch, argument, och,
	  TO_VICT );
      interpret( och, argument );
    }

    if( toomany++ > 100 )
      break;
  }

  if( found )
  {
    snprintf( log_buf, MAX_STRING_LENGTH, "%s: order %s.", ch->name, argbuf );
    log_string_plus( log_buf, LOG_NORMAL );
    send_to_char( "Ok.\r\n", ch );
    WAIT_STATE( ch, 12 );
  }
  else
  {
    send_to_char( "You have no followers here.\r\n", ch );
  }
}

void do_group( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim = 0;

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    CHAR_DATA *gch = 0;
    CHAR_DATA *leader = ch->leader ? ch->leader : ch;
    set_char_color( AT_GREEN, ch );
    ch_printf( ch, "%s's group:\r\n", PERS( leader, ch ) );

    for( gch = first_char; gch; gch = gch->next )
    {
      if( is_same_group( gch, ch ) )
      {
	set_char_color( AT_DGREEN, ch );
	ch_printf( ch, "%s\r\n", capitalize( PERS( gch, ch ) ) );
      }
    }

    return;
  }

  if( !strcmp( arg, "disband" ) )
  {
    CHAR_DATA *gch;
    int count = 0;

    if( ch->leader || ch->master )
    {
      send_to_char
	( "You cannot disband a group if you're following someone.\r\n",
	  ch );
      return;
    }

    for( gch = first_char; gch; gch = gch->next )
    {
      if( is_same_group( ch, gch ) && ( ch != gch ) )
      {
	gch->leader = NULL;
	gch->master = NULL;
	count++;
	send_to_char( "Your group is disbanded.\r\n", gch );
      }
    }

    if( count == 0 )
      send_to_char( "You have no group members to disband.\r\n", ch );
    else
      send_to_char( "You disband your group.\r\n", ch );

    return;
  }

  if( !strcmp( arg, "all" ) )
  {
    CHAR_DATA *rch;
    int count = 0;

    for( rch = ch->in_room->first_person; rch; rch = rch->next_in_room )
    {
      if( ch != rch
	  && !IS_NPC( rch )
	  && rch->master == ch
	  && !ch->master && !ch->leader && !is_same_group( rch, ch ) )
      {
	rch->leader = ch;
	count++;
      }
    }

    if( count == 0 )
    {
      send_to_char( "You have no eligible group members.\r\n", ch );
    }
    else
    {
      act( AT_ACTION, "$n groups $s followers.", ch, NULL, victim,
	  TO_ROOM );
      send_to_char( "You group your followers.\r\n", ch );
    }

    return;
  }

  if( ( victim = get_char_room( ch, arg ) ) == NULL )
  {
    send_to_char( "They aren't here.\r\n", ch );
    return;
  }

  if( ch->master || ( ch->leader && ch->leader != ch ) )
  {
    send_to_char( "But you are following someone else!\r\n", ch );
    return;
  }

  if( victim->master != ch && ch != victim )
  {
    act( AT_PLAIN, "$N isn't following you.", ch, NULL, victim, TO_CHAR );
    return;
  }

  if( is_same_group( victim, ch ) && ch != victim )
  {
    victim->leader = NULL;
    act( AT_ACTION, "$n removes $N from $s group.", ch, NULL, victim,
	TO_NOTVICT );
    act( AT_ACTION, "$n removes you from $s group.", ch, NULL, victim,
	TO_VICT );
    act( AT_ACTION, "You remove $N from your group.", ch, NULL, victim,
	TO_CHAR );
    return;
  }

  victim->leader = ch;
  act( AT_ACTION, "$N joins $n's group.", ch, NULL, victim, TO_NOTVICT );
  act( AT_ACTION, "You join $n's group.", ch, NULL, victim, TO_VICT );
  act( AT_ACTION, "$N joins your group.", ch, NULL, victim, TO_CHAR );
}

/*
 * 'Split' originally by Gnort, God of Chaos.
 */
void do_split( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *gch = NULL;
  int members = 0;
  int amount = 0;
  int share = 0;
  int extra = 0;

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    send_to_char( "Split how much?\r\n", ch );
    return;
  }

  amount = atoi( arg );

  if( amount < 0 )
  {
    send_to_char( "Your group wouldn't like that.\r\n", ch );
    return;
  }

  if( amount == 0 )
  {
    send_to_char( "You hand out zero credits, but no one notices.\r\n",
	ch );
    return;
  }

  if( ch->gold < amount )
  {
    send_to_char( "You don't have that many credits.\r\n", ch );
    return;
  }

  members = 0;

  for( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
  {
    if( is_same_group( gch, ch ) )
      members++;
  }


  if( ( IS_SET( ch->act, PLR_AUTOGOLD ) ) && ( members < 2 ) )
    return;

  if( members < 2 )
  {
    send_to_char( "Just keep it all.\r\n", ch );
    return;
  }

  share = amount / members;
  extra = amount % members;

  if( share == 0 )
  {
    send_to_char( "Don't even bother, cheapskate.\r\n", ch );
    return;
  }

  ch->gold -= amount;
  ch->gold += share + extra;

  set_char_color( AT_GOLD, ch );
  ch_printf( ch,
      "You split %d credits.  Your share is %d credits.\r\n",
      amount, share + extra );

  snprintf( buf, MAX_STRING_LENGTH, "$n splits %d credits. Your share is %d credits.",
      amount, share );

  for( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
  {
    if( gch != ch && is_same_group( gch, ch ) )
    {
      act( AT_GOLD, buf, ch, NULL, gch, TO_VICT );
      gch->gold += share;
    }
  }
}

void do_gtell( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *gch = NULL;

  if( argument[0] == '\0' )
  {
    send_to_char( "Tell your group what?\r\n", ch );
    return;
  }

  if( IS_SET( ch->act, PLR_NO_TELL ) )
  {
    send_to_char( "Your message didn't get through!\r\n", ch );
    return;
  }

  /*
   * Note use of send_to_char, so gtell works on sleepers.
   */
  for( gch = first_char; gch; gch = gch->next )
  {
    if( is_same_group( gch, ch ) )
    {
      set_char_color( AT_GTELL, gch );
      ch_printf( gch, "%s tells the group '%s'.\r\n", ch->name,
	  argument );
    }
  }
}

/*
 * It is very important that this be an equivalence relation:
 * (1) A ~ A
 * (2) if A ~ B then B ~ A
 * (3) if A ~ B  and B ~ C, then A ~ C
 */
bool is_same_group( const CHAR_DATA * ach, const CHAR_DATA * bch )
{
  if( ach->leader )
    ach = ach->leader;

  if( bch->leader )
    bch = bch->leader;

  return ach == bch;
}

/*
 * this function sends raw argument over the AUCTION: channel
 * I am not too sure if this method is right..
 */

void talk_auction( const char *argument )
{
  DESCRIPTOR_DATA *d = NULL;
  char buf[MAX_STRING_LENGTH];

  snprintf( buf, MAX_STRING_LENGTH, "Auction: %s", argument );	/* last %s to reset color */

  for( d = first_descriptor; d; d = d->next )
  {
    CHAR_DATA *original = d->original ? d->original : d->character;	/* if switched */
    if( ( d->connected == CON_PLAYING )
	&& !IS_SET( original->deaf, CHANNEL_AUCTION )
	&& !NOT_AUTHED( original ) )
      act( AT_GOSSIP, buf, original, NULL, NULL, TO_CHAR );
  }
}