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"

/*
 * Externals
 */
bool check_social( CHAR_DATA * ch, const char *command, const char *argument );

/*
 * Log-all switch.
 */
bool fLogAll = FALSE;
CMDTYPE *command_hash[126];	/* hash table for cmd_table */
SOCIALTYPE *social_index[27];	/* hash table for socials   */

/*
 * Character not in position for command?
 */
bool check_pos( const CHAR_DATA * ch, short position )
{
  if( ch->position < position )
  {
    switch ( ch->position )
    {
      case POS_DEAD:
	send_to_char( "A little difficult to do when you are DEAD...\r\n",
	    ch );
	break;

      case POS_MORTAL:
      case POS_INCAP:
	send_to_char( "You are hurt far too bad for that.\r\n", ch );
	break;

      case POS_STUNNED:
	send_to_char( "You are too stunned to do that.\r\n", ch );
	break;

      case POS_SLEEPING:
	send_to_char( "In your dreams, or what?\r\n", ch );
	break;

      case POS_RESTING:
	send_to_char( "Nah... You feel too relaxed...\r\n", ch );
	break;

      case POS_SITTING:
	send_to_char( "You can't do that sitting down.\r\n", ch );
	break;

      case POS_FIGHTING:
	send_to_char( "No way!  You are still fighting!\r\n", ch );
	break;
    }

    return FALSE;
  }

  return TRUE;
}

extern char lastplayercmd[MAX_INPUT_LENGTH * 2];

/*
 * The main entry point for executing commands.
 * Can be recursively called from 'at', 'order', 'force'.
 */
void interpret( CHAR_DATA * ch, char *argument )
{
  char command[MAX_INPUT_LENGTH];
  char logline[MAX_INPUT_LENGTH];
  char logname[MAX_INPUT_LENGTH];
  TIMER *timer = NULL;
  CMDTYPE *cmd = NULL;
  int trust = 0;
  int loglvl = 0;
  bool found = FALSE;
  struct timeval time_used;
  long tmptime = 0;

  if( !ch )
  {
    bug( "interpret: null ch!", 0 );
    return;
  }

  if( !cmd )
  {
    /* Changed the order of these ifchecks to prevent crashing. */
    if( !argument || !strcmp( argument, "" ) )
    {
      bug( "interpret: null argument!", 0 );
      return;
    }

    /*
     * Strip leading spaces.
     */
    while( isspace( ( int ) *argument ) )
      argument++;
    if( argument[0] == '\0' )
      return;

    timer = get_timerptr( ch, TIMER_DO_FUN );

    /* REMOVE_BIT( ch->affected_by, AFF_HIDE ); */

    /*
     * Implement freeze command.
     */
    if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_FREEZE ) )
    {
      send_to_char( "You're totally frozen!\r\n", ch );
      return;
    }

    /*
     * Grab the command word.
     * Special parsing so ' can be a command,
     *   also no spaces needed after punctuation.
     */
    strcpy( logline, argument );
    if( !isalpha( ( int ) argument[0] ) && !isdigit( ( int ) argument[0] ) )
    {
      command[0] = argument[0];
      command[1] = '\0';
      argument++;
      while( isspace( ( int ) *argument ) )
	argument++;
    }
    else
      argument = one_argument( argument, command );

    /*
     * Look for command in command table.
     * Check for council powers and/or bestowments
     */
    trust = get_trust( ch );
    for( cmd = command_hash[LOWER( command[0] ) % 126]; cmd;
	 cmd = cmd->next )
      {
	if( !str_prefix( command, cmd->name )
	    && ( cmd->level <= trust
		 || ( !IS_NPC( ch )
		      && ch->pcdata->bestowments[0] != '\0'
		      && is_name( cmd->name, ch->pcdata->bestowments )
		      && cmd->level <= ( trust + 5 ) ) ) )
	  {
	    found = TRUE;
	    break;
	  }
      }

    /*
     * Turn off afk bit when any command performed.
     */
    if( IS_SET( ch->act, PLR_AFK ) && ( str_cmp( command, "AFK" ) ) )
    {
      REMOVE_BIT( ch->act, PLR_AFK );
      act( AT_GREY, "$n is no longer afk.", ch, NULL, NULL, TO_ROOM );
    }
  }

  /*
   * Log and snoop.
   */
  sprintf( lastplayercmd, "** %s: %s", ch->name, logline );

  if( found && cmd->log == LOG_NEVER )
    strcpy( logline, "XXXXXXXX XXXXXXXX XXXXXXXX" );

  loglvl = found ? cmd->log : (short) LOG_NORMAL;

  if( ( !IS_NPC( ch ) && IS_SET( ch->act, PLR_LOG ) )
      || fLogAll
      || loglvl == LOG_BUILD || loglvl == LOG_HIGH || loglvl == LOG_ALWAYS )
  {
    /* Added by Narn to show who is switched into a mob that executes
       a logged command.  Check for descriptor in case force is used. */
    if( ch->desc && ch->desc->original )
      sprintf( log_buf, "Log %s (%s): %s", ch->name,
	  ch->desc->original->name, logline );
    else
      sprintf( log_buf, "Log %s: %s", ch->name, logline );

    /*
     * Make it so a 'log all' will send most output to the log
     * file only, and not spam the log channel to death     -Thoric
     */
    if( fLogAll && loglvl == LOG_NORMAL
	&& ( IS_NPC( ch ) || !IS_SET( ch->act, PLR_LOG ) ) )
      loglvl = LOG_ALL;

    log_string_plus( log_buf, loglvl );
  }

  if( ch->desc && ch->desc->snoop_by )
  {
    sprintf( logname, "%s", ch->name );
    write_to_buffer( ch->desc->snoop_by, logname, 0 );
    write_to_buffer( ch->desc->snoop_by, "% ", 2 );
    write_to_buffer( ch->desc->snoop_by, logline, 0 );
    write_to_buffer( ch->desc->snoop_by, "\r\n", 2 );
  }



  if( timer )
  {
    int tempsub = ch->substate;
    ch->substate = SUB_TIMER_DO_ABORT;
    ( timer->do_fun ) ( ch, STRLIT_EMPTY );
    if( char_died( ch ) )
      return;
    if( ch->substate != SUB_TIMER_CANT_ABORT )
    {
      ch->substate = tempsub;
      extract_timer( ch, timer );
    }
    else
    {
      ch->substate = tempsub;
      return;
    }
  }

  /*
   * Look for command in skill and socials table.
   */
  if( !found )
  {
    if( !check_skill( ch, command, argument )
	&& !check_social( ch, command, argument )
#ifdef SWR2_USE_IMC
	&& !imc_command_hook( ch, command, argument )
#endif
	)
    {
      EXIT_DATA *pexit = NULL;

      /* check for an auto-matic exit command */
      if( ( pexit = find_door( ch, command, TRUE ) ) != NULL
	  && IS_SET( pexit->exit_info, EX_xAUTO ) )
      {
	if( IS_SET( pexit->exit_info, EX_CLOSED )
	    && ( !IS_AFFECTED( ch, AFF_PASS_DOOR )
	      || IS_SET( pexit->exit_info, EX_NOPASSDOOR ) ) )
	{
	  if( !IS_SET( pexit->exit_info, EX_SECRET ) )
	    act( AT_PLAIN, "The $d is closed.", ch, NULL,
		pexit->keyword, TO_CHAR );
	  else
	    send_to_char( "You cannot do that here.\r\n", ch );
	  return;
	}
	move_char( ch, pexit, 0 );
	return;
      }
      send_to_char( "Huh?\r\n", ch );
    }
    return;
  }

  /*
   * Character not in position for command?
   */
  if( !check_pos( ch, cmd->position ) )
    return;

  /* Berserk check for flee.. maybe add drunk to this?.. but too much
     hardcoding is annoying.. -- Altrag */
  if( !str_cmp( cmd->name, "flee" ) && IS_AFFECTED( ch, AFF_BERSERK ) )
  {
    send_to_char( "You aren't thinking very clearly..\r\n", ch );
    return;
  }

  /*
   * Dispatch the command.
   */
  ch->last_cmd = cmd->do_fun;
  start_timer( &time_used );
  ( *cmd->do_fun ) ( ch, argument );
  end_timer( &time_used );
  /*
   * Update the record of how many times this command has been used (haus)
   */
  update_userec( &time_used, &cmd->userec );
  tmptime = UMIN( time_used.tv_sec, 19 ) * 1000000 + time_used.tv_usec;

  /* laggy command notice: command took longer than 1.5 seconds */
  if( tmptime > 1500000 )
  {
    sprintf( log_buf, "[*****] LAG: %s: %s %s (R:%ld S:%d.%06d)", ch->name,
	cmd->name, ( cmd->log == LOG_NEVER ? "XXX" : argument ),
	ch->in_room ? ch->in_room->vnum : 0,
	( int ) ( time_used.tv_sec ), ( int ) ( time_used.tv_usec ) );
    log_string_plus( log_buf, LOG_NORMAL );
  }
}

CMDTYPE *find_command( const char *command )
{
  CMDTYPE *cmd = NULL;
  int hash = LOWER( command[0] ) % 126;

  for( cmd = command_hash[hash]; cmd; cmd = cmd->next )
    if( !str_prefix( command, cmd->name ) )
      return cmd;

  return NULL;
}

SOCIALTYPE *find_social( const char *command )
{
  SOCIALTYPE *social = NULL;
  int hash = 0;

  if( command[0] < 'a' || command[0] > 'z' )
    hash = 0;
  else
    hash = ( command[0] - 'a' ) + 1;

  for( social = social_index[hash]; social; social = social->next )
    if( !str_prefix( command, social->name ) )
      return social;

  return NULL;
}

bool check_social( CHAR_DATA * ch, const char *command,
		   const char *original_argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim = NULL;
  SOCIALTYPE *social = NULL;
  char argument[MAX_INPUT_LENGTH];
  snprintf( argument, MAX_INPUT_LENGTH, "%s", original_argument );

  if( ( social = find_social( command ) ) == NULL )
    return FALSE;

  if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_NO_EMOTE ) )
  {
    send_to_char( "You are anti-social!\r\n", ch );
    return TRUE;
  }

  switch ( ch->position )
  {
    case POS_DEAD:
      send_to_char( "Lie still; you are DEAD.\r\n", ch );
      return TRUE;

    case POS_INCAP:
    case POS_MORTAL:
      send_to_char( "You are hurt far too bad for that.\r\n", ch );
      return TRUE;

    case POS_STUNNED:
      send_to_char( "You are too stunned to do that.\r\n", ch );
      return TRUE;

    case POS_SLEEPING:
      /*
       * I just know this is the path to a 12" 'if' statement.  :(
       * But two players asked for it already!  -- Furey
       */
      if( !str_cmp( social->name, "snore" ) )
	break;
      send_to_char( "In your dreams, or what?\r\n", ch );
      return TRUE;

  }

  one_argument( argument, arg );
  victim = NULL;
  if( arg[0] == '\0' )
  {
    act( AT_SOCIAL, social->others_no_arg, ch, NULL, victim, TO_ROOM );
    act( AT_SOCIAL, social->char_no_arg, ch, NULL, victim, TO_CHAR );
  }
  else if( ( victim = get_char_room( ch, arg ) ) == NULL )
  {
    send_to_char( "They aren't here.\r\n", ch );
  }
  else if( victim == ch )
  {
    act( AT_SOCIAL, social->others_auto, ch, NULL, victim, TO_ROOM );
    act( AT_SOCIAL, social->char_auto, ch, NULL, victim, TO_CHAR );
  }
  else
  {
    act( AT_SOCIAL, social->others_found, ch, NULL, victim, TO_NOTVICT );
    act( AT_SOCIAL, social->char_found, ch, NULL, victim, TO_CHAR );
    act( AT_SOCIAL, social->vict_found, ch, NULL, victim, TO_VICT );

    if( !IS_NPC( ch ) && IS_NPC( victim )
	&& !IS_AFFECTED( victim, AFF_CHARM )
	&& IS_AWAKE( victim )
	&& !IS_SET( victim->pIndexData->progtypes, ACT_PROG ) )
    {
      switch ( number_bits( 4 ) )
      {
	case 0:
	  if( !IS_SET( ch->in_room->room_flags, ROOM_SAFE )
	      || IS_EVIL( ch ) )
	    multi_hit( victim, ch, TYPE_UNDEFINED );
	  else if( IS_NEUTRAL( ch ) )
	  {
	    act( AT_ACTION, "$n slaps $N.", victim, NULL, ch,
		TO_NOTVICT );
	    act( AT_ACTION, "You slap $N.", victim, NULL, ch, TO_CHAR );
	    act( AT_ACTION, "$n slaps you.", victim, NULL, ch,
		TO_VICT );
	  }
	  else
	  {
	    act( AT_ACTION, "$n acts like $N doesn't even exist.",
		victim, NULL, ch, TO_NOTVICT );
	    act( AT_ACTION, "You just ignore $N.", victim, NULL, ch,
		TO_CHAR );
	    act( AT_ACTION, "$n appears to be ignoring you.", victim,
		NULL, ch, TO_VICT );
	  }
	  break;

	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
	  act( AT_SOCIAL, social->others_found,
	      victim, NULL, ch, TO_NOTVICT );
	  act( AT_SOCIAL, social->char_found, victim, NULL, ch, TO_CHAR );
	  act( AT_SOCIAL, social->vict_found, victim, NULL, ch, TO_VICT );
	  break;

	case 9:
	case 10:
	case 11:
	case 12:
	  act( AT_ACTION, "$n slaps $N.", victim, NULL, ch, TO_NOTVICT );
	  act( AT_ACTION, "You slap $N.", victim, NULL, ch, TO_CHAR );
	  act( AT_ACTION, "$n slaps you.", victim, NULL, ch, TO_VICT );
	  break;
      }
    }
  }

  return TRUE;
}

void do_timecmd( CHAR_DATA * ch, char *argument )
{
  struct timeval start_time;
  struct timeval etime;
  static bool timing;
  extern CHAR_DATA *timechar;
  char arg[MAX_INPUT_LENGTH];

  send_to_char( "Timing\r\n", ch );
  if( timing )
    return;
  one_argument( argument, arg );
  if( !*arg )
  {
    send_to_char( "No command to time.\r\n", ch );
    return;
  }
  if( !str_cmp( arg, "update" ) )
  {
    if( timechar )
      send_to_char( "Another person is already timing updates.\r\n", ch );
    else
    {
      timechar = ch;
      send_to_char( "Setting up to record next update loop.\r\n", ch );
    }
    return;
  }
  set_char_color( AT_PLAIN, ch );
  send_to_char( "Starting timer.\r\n", ch );
  timing = TRUE;
  gettimeofday( &start_time, NULL );
  interpret( ch, argument );
  gettimeofday( &etime, NULL );
  timing = FALSE;
  set_char_color( AT_PLAIN, ch );
  send_to_char( "Timing complete.\r\n", ch );
  subtract_times( &etime, &start_time );
  ch_printf( ch, "Timing took %d.%06d seconds.\r\n",
      etime.tv_sec, etime.tv_usec );
  return;
}

void send_timer( struct timerset *vtime, const CHAR_DATA * ch )
{
  struct timeval ntime;
  int carry;

  if( vtime->num_uses == 0 )
    return;

  ntime.tv_sec = vtime->total_time.tv_sec / vtime->num_uses;
  carry = ( vtime->total_time.tv_sec % vtime->num_uses ) * 1000000;
  ntime.tv_usec = ( vtime->total_time.tv_usec + carry ) / vtime->num_uses;

  ch_printf( ch, "Has been used %d times this boot.\r\n", vtime->num_uses );
  ch_printf( ch, "Time (in secs): min %d.%0.6d; avg: %d.%0.6d; max %d.%0.6d"
      "\r\n", vtime->min_time.tv_sec, vtime->min_time.tv_usec,
      ntime.tv_sec,
      ntime.tv_usec, vtime->max_time.tv_sec, vtime->max_time.tv_usec );
}

void update_userec( struct timeval *time_used, struct timerset *userec )
{
  userec->num_uses++;

  if( !timerisset( &userec->min_time )
      || timercmp( time_used, &userec->min_time, < ) )
  {
    userec->min_time.tv_sec = time_used->tv_sec;
    userec->min_time.tv_usec = time_used->tv_usec;
  }

  if( !timerisset( &userec->max_time )
      || timercmp( time_used, &userec->max_time, > ) )
  {
    userec->max_time.tv_sec = time_used->tv_sec;
    userec->max_time.tv_usec = time_used->tv_usec;
  }

  userec->total_time.tv_sec += time_used->tv_sec;
  userec->total_time.tv_usec += time_used->tv_usec;

  while( userec->total_time.tv_usec >= 1000000 )
  {
    userec->total_time.tv_sec++;
    userec->total_time.tv_usec -= 1000000;
  }
}