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 "ctype.h"
#include "sys/types.h"
#include "stdio.h"
#include "stdlib.h"
#include "syslog.h"
#include "unistd.h"
#include "define.h"
#include "struct.h"


typedef int pfcomp_func   ( pfile_data*, pfile_data* );


pfile_data*       ident_list  [ MAX_PFILE ];
pfile_data**      pfile_list  = NULL;
pfile_data**      high_score  = NULL;
pfile_data**      death_list  = NULL;
pfile_data**       site_list  = NULL;
int                max_pfile  = 0;
int                 max_high  = 0;
int                max_death  = 0;
int             site_entries  = 0;


int          search          ( pfile_data**, int, pfile_data*, pfcomp_func );
pfcomp_func  compare_exp;
pfcomp_func  compare_death; 
  

/*
 *   PFILE_DATA CLASS
 */ 


Pfile_Data :: Pfile_Data( const char* string  )
{
  record_new( sizeof( pfile_data ), MEM_PFILE );

  name       = alloc_string( string, MEM_PFILE );
  pwd        = empty_string;
  last_host  = empty_string;
  homepage   = empty_string;

  account = NULL;
  mail    = NULL;
  clan    = NULL;

  last_on     = current_time;
  created     = current_time;

  level       = 0;
  clss        = 0;
  race        = 0;
  sex         = SEX_RANDOM;
  trust       = 0;
  bounty      = 0;
  flags[0]    = ( 1 << PLR_GOSSIP ) | ( 1 << PLR_PROMPT );
  flags[1]    = 0;
  ident       = -1;
  rank        = -1;
  guesses     = 0;

  vzero( vote, MAX_VOTE );
}


Pfile_Data :: ~Pfile_Data( )
{
  record_delete( sizeof( pfile_data ), MEM_PFILE );

  if( ident > 0 ) {
    remove_list( pfile_list, max_pfile, this );
    remove_list( site_list, site_entries, this );
    ident_list[ident] = NULL; 
    }

  if( rank >= 0 ) {
    for( int i = rank+1; i < max_high; i++ )
      high_score[i]->rank--;            
    remove( high_score, max_high, rank );
    } 

  clear_auction( this );

  if( clan != NULL ) 
    remove_member( this );

  for( int i = 0; i < max_pfile; i++ ) 
    for( int j = 0; j < MAX_VOTE; j++ ) 
      if( pfile_list[i]->vote[j] == this )
        pfile_list[i]->vote[j] = NULL;

  free_string( name,      MEM_PFILE );
  free_string( pwd,       MEM_PFILE );
  free_string( last_host, MEM_PFILE );
  free_string( homepage,  MEM_PFILE );
}


void extract( pfile_data* pfile, link_data* prev )
{
  player_data*   victim;
  link_data*       link;
  link_data*       next;
  int               pos  = 0;

  for( link = link_list; link != NULL; link = next ) {
    next = link->next; 
    if( link->pfile == pfile && link != prev ) {
      send( link,
        "\n\r\n\r+++ Character was deleted - Closing link +++\n\r" );
      close_socket( link, TRUE );
      }
    }

  for( int i = 0; i < player_list; i++ ) {
    victim = player_list[i];
    if( victim->Is_Valid( )
      && ( pos = search( victim->pcdata->recognize, pfile->ident ) ) >= 0 )
      remove( victim->pcdata->recognize, pos );
    }

  for( int i = 0; i < obj_list; i++ )
    if( obj_list[i]->owner == pfile )
      obj_list[i]->owner = NULL;

  delete pfile; 
}


void modify_pfile( char_data* ch )
{
  pfile_data* pfile = ch->pcdata->pfile;
 
  pfile->race      = ch->shdata->race;
  pfile->clss      = ch->pcdata->clss; 
  pfile->sex       = ch->sex;
  pfile->level     = ch->shdata->level;
  pfile->trust     = ch->pcdata->trust;
  pfile->exp       = ch->exp;

  if( pfile->ident < 1 || pfile->ident > MAX_PFILE ) 
    pfile->ident = assign_ident( );

  if( ident_list[pfile->ident] == NULL ) {
    add_list( pfile_list, max_pfile, pfile );
    add_list( site_list, site_entries, pfile ); 
    ident_list[pfile->ident] = pfile;

    if( ch->shdata->level < LEVEL_APPRENTICE ) {
      add_list( high_score, max_high, pfile );
      add_list( death_list, max_death, pfile );
      }
    }
  else {
    if( ident_list[pfile->ident] != pfile ) {
      roach( "Modify_Pfile: non-unique identity." );
      roach( "-- Ch = { %s, %s }",
        pfile->name, ident_list[pfile->ident]->name );
      panic( "-- Id = %d", pfile->ident );
      }
    }
}


/*
 *   SITE LIST ROUTINES
 */


int site_search( const char* word )
{
  int      min  = 0;
  int    value;
  int      mid;
  int      max;

  if( site_list == NULL )
    return -1;

  max = site_entries-1;

  for( ; ; ) {
    if( max < min )
      return -min-1;

    mid    = (max+min)/2;
    value  = rstrcasecmp( site_list[mid]->last_host, word );

    if( value == 0 ) 
      break;
    if( value < 0 )
      min = mid+1;
    else 
      max = mid-1;
    }

  for( ; mid > 0; mid-- )
    if( strcasecmp( site_list[mid-1]->last_host, word ) )
      break;

  return mid;
}


/*
 *   PLAYER NAME LIST
 */


void add_list( pfile_data**& list, int& size, pfile_data* pfile )
{
  int pos;
  int   i;

  if( &list == &site_list ) {
    pos = site_search( pfile->last_host );
    }
  else if( &list == &high_score ) {
    pos = search( high_score, max_high, pfile, compare_exp );
    if( pos < 0 )
      pos = -pos-1;
    pfile->rank = pos; 
    for( i = pos; i < max_high; i++ )
      high_score[i]->rank++;
    }
  else if( &list == &death_list ) {
    pos = search( death_list, max_death, pfile, compare_death );
    }
  else {
    if( ( pos = pntr_search( pfile_list, max_pfile, pfile->name ) ) >= 0 ) {
      roach( "Add_list: Repeated name." );
      panic( "-- Name = '%s'", pfile->name );
      }
    }

  insert( list, size, pfile, pos < 0 ? -pos-1 : pos );
}


void remove_list( pfile_data**& list, int& size, pfile_data* pfile )
{
  int  ident  = pfile->ident;
  int    pos;

  if( ident < 0 )
    return;

  if( &list == &site_list ) {
    for( pos = site_search( pfile->last_host );
      pos < site_entries && ( pos < 0 || site_list[pos] != pfile ); pos++ ) 
      if( pos < 0
        || strcasecmp( site_list[pos]->last_host, pfile->last_host ) ) {
        roach( "Remove_Site: Pfile not found!?" );
        roach( "-- Pfile = %s", pfile->name );
        roach( "--  Host = %s", pfile->last_host );
        for( pos = 0; pos < size; pos++ )
          if( site_list[pos] == pfile ) {
            remove( list, size, pos );
            roach( "Remove_Site: Pfile found at wrong position!" );
            break;
	    }          
        return;
        }
    }
  else {
    pos = pntr_search( list, size, pfile->name );
    }

  if( pos < 0 ) {
    roach( "Remove_list: Pfile not found in list!?" );
    return;
    }

  remove( list, size, pos );
}


int assign_ident( )
{
  register int i;

  for( i = 1; i < MAX_PFILE; i++ )
    if( ident_list[i] == NULL )
      return i;

  roach( "Assign_ident: ident_list full." );
  exit( 1 );
}


/*
 *   SEARCH ROUTINES
 */


pfile_data* player_arg( char*& )
{
  return NULL;
}
 

pfile_data* find_pfile( const char* name, char_data* ch )
{
  int      i;

  i = pntr_search( pfile_list, max_pfile, name );

  if( i >= 0 )
    return pfile_list[i];

  if( -i-1 < max_pfile
    && !strncasecmp( pfile_list[-i-1]->name, name, strlen( name ) ) )
    return pfile_list[-i-1];

  send( ch, "No such player exists.\n\r" );

  return NULL;
} 


pfile_data* find_pfile_exact( const char *name )
{
  int i;

  i = pntr_search( pfile_list, max_pfile, name );

  return( i < 0 ? NULL : pfile_list[i] );
}


pfile_data* find_pfile_substring( const char* name )
{
  char      tmp  [ 4 ];
  char*  search;
  int         i;

  memcpy( tmp, name, 3 );
  tmp[3] = '\0'; 
 
  if( ( i = pntr_search( pfile_list, max_pfile, tmp ) ) < 0 )
    i = -i-1;

  for( ; i < max_pfile; i++ ) {
    search = pfile_list[i]->name;
    if( strncasecmp( search, tmp, 3 ) )
      break;
    if( !strncasecmp( search, name, strlen( search ) ) )
      return pfile_list[i];
    }

  return NULL;
}


player_data* find_player( pfile_data* pfile )
{
  for( int i = 0; i < player_list; i++ )
    if( player_list[i]->pcdata->pfile == pfile
      && player_list[i]->in_room != NULL )
      return player_list[i];

  return NULL;
}


/*
 *   PLAYER FILE COMMANDS
 */


void forced_quit( player_data* ch, bool crash )
{
  char              tmp  [ TWO_LINES ];
  link_data*   link_new;
  link_data*  link_next;
  link_data*       link  = ch->link;
  wizard_data*      imm  = wizard( ch );

  if( ch->switched != NULL )
    do_return( ch->switched, "" );

  if( link != NULL ) {
    if( link->connected != CON_PLAYING ) {
      close_socket( link, TRUE );
      return;
      }    
    reset_screen( ch );
    send( link, "Thank you for visiting The Forest's Edge.\n\r" );
    }

  send_seen( ch, "%s has left the game.\n\r", ch );

  sprintf( tmp, "%s has quit.", ch->descr->name );
  info( "", ( imm != NULL && is_set( ch->pcdata->pfile->flags, PLR_WIZINVIS )
    ? imm->wizinvis : 0 ), tmp, IFLAG_LOGINS, 1, ch );

  if( !crash )
    remove_bit( ch->pcdata->pfile->flags, PLR_CRASH_QUIT ); 

  write( ch );
  reference( ch, ch->contents, -2 );

  for( link_new = link_list; link_new != NULL; link_new = link_next ) {
    link_next = link_new->next; 
    if( link_new->character != NULL && link_new != link
      && !strcmp( link_new->character->descr->name, ch->descr->name ) ) {
      write_to_buffer( link_new, "\n\r\n\rFor security reasons closing link : please reconnect.\n\r" );
      close_socket( link_new, TRUE );
      }
    }

  if( !strcasecmp( ch->descr->name, "Guest" ) ) {
    delete_file( PLAYER_DIR, ch->descr->name );
    delete ch->pcdata->pfile;
    }

  ch->Extract( );

  if( link != NULL ) 
    close_socket( link, TRUE );
}


void do_quit( char_data* ch, char* )
{
  room_data*     room;
  player_data*     pc;

  if( is_mob( ch ) )
    return;

  pc = player( ch );

  if( opponent( ch ) != NULL ) {
    send( ch, "You can't quit while fighting.\n\r" );
    return;
    }

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

  if( ch->shdata->level < LEVEL_APPRENTICE
    && can_pkill( ch, NULL, FALSE ) ) {
    send( ch, "You cannot quit in rooms which allow pkill.\n\r" );
    return;
    }

  forced_quit( pc, is_set( &pc->status, STAT_FORCED ) );
}


void do_delete( char_data* ch, char* argument )
{
  char*            tmp  = static_string( );
  player_data*  pc;

  if( is_mob( ch ) ) 
    return;

  pc = (player_data*) ch;

  if( ch->shdata->level > 5 && ch->pcdata->trust < LEVEL_BUILDER ) {
    send( ch, "Characters over level five may not delete.  Time has\
 shown that\n\rplayers often delete and then regret the action.  It also\
 serves no purpose.\n\rIf you no longer wish to play just quit and do not\
 return.\n\r" );
    return;
    }

  if( strcmp( argument, ch->pcdata->pfile->pwd ) ) {
    send( ch,
      "You must type delete <password> to delete you character.\n\r" );
    return;
    }

  send_seen( ch, "%s throws %sself on %s sword.\n\r", ch,
    ch->Him_Her( ), ch->His_Her( ) );
  send_seen( ch, "Death gratefully takes %s's spirit.\n\r", ch );

  clear_screen( ch );
  reset_screen( ch );

  send( ch, "Your character has been vaporized.\n\r" );
  send( ch, "Death is surprisingly peaceful.\n\r" );
  send( ch, "Good night.\n\r" );

  sprintf( tmp, "%s has deleted %sself.", ch->descr->name,
    ch->Him_Her( ) );
  info( tmp, LEVEL_IMMORTAL, tmp, IFLAG_LOGINS, 1, ch );

  player_log( ch, "Deleted." );
  purge( pc );
}


void purge( player_data* ch )
{
  pfile_data*  pfile  = ch->pcdata->pfile;
  
  delete_file( PLAYER_DIR,      ch->descr->name, FALSE );
  delete_file( PLAYER_PREV_DIR, ch->descr->name, FALSE );
  delete_file( BACKUP_DIR,      ch->descr->name, FALSE );
  delete_file( MAIL_DIR,        ch->descr->name, FALSE );

  if( ch->link != NULL && boot_stage == 2 ) {
    ch->link->player = NULL;
    close_socket( ch->link, TRUE );
    }

  ch->Extract( );
  extract( pfile );
}


/*
 *  SAVE
 */


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

  if( is_mob( ch ) )
    return;

  pc = (player_data*) ch;

  if( ch->save_time-180 > current_time ) { 
    send( ch, "You are saving your character too frequently - request\
 denied.\n\r" );
    }
  else {
    write( pc );
    reference( pc, ch->contents, -1 );
    send( ch, "Your character has been saved.\n\r" );
    }
}


/*
 *   INFORMATIONAL COMMANDS 
 */


void do_title( char_data* ch, char* argument )
{
  char            tmp  [ MAX_INPUT_LENGTH ];
  wizard_data*    imm;
  int           flags;

  if( is_mob( ch ) )
    return;

  if( ch->shdata->level < 10 ) {
    send( ch, "You must be at least level 10 to set your title.\n\r" );
    return;
    }

  if( !get_flags( ch, argument, &flags, "l", "Title" ) )
    return;;

  if( is_set( &flags, 0 ) && has_permission( ch, PERM_SHUTDOWN ) ) {
    if( strlen( argument ) > 13 ) {
      send( ch, "Your level title must be less than 13 characters.\n\r" );
      return;
      }
    imm = wizard( ch );
    free_string( imm->level_title, MEM_WIZARD );
    imm->level_title = alloc_string( argument, MEM_WIZARD );
    send( ch, "Your level title has been set to '%s'.\n\r", 
      argument );
    return;
    }

  if( *argument == '\0' ) {
    send( ch, "What do you want to set your title to?\n\r" );
    return;
    }

  sprintf( tmp, "%s%s", *argument == ',' || *argument == '\'' 
       ? "" : " ", argument );
  truncate( tmp, 53-strlen( ch->descr->name ) );

  free_string( ch->pcdata->title, MEM_PLAYER );
  ch->pcdata->title = alloc_string( tmp, MEM_PLAYER );

  send( ch, "Your title has been changed.\n\r" );
}


void do_password( char_data* ch, char* argument )
{
  char       arg  [ MAX_INPUT_LENGTH ];
  char**     pwd;
  int      flags;

  if( is_mob( ch ) 
    || !get_flags( ch, argument, &flags, "a", "Password" ) )
    return;

  argument = one_argument( argument, arg );

  if( *arg == '\0' || *argument == '\0' ) {
    send( "Syntax: password <old> <new>.\n\r", ch );
    return;
    }

  if( is_set( &flags, 0 ) ) {
    if( &ch->pcdata->pfile->account == NULL ) {
      send( ch, "You don't have an account to change the password of.\n\r" );
      return;
      }
    pwd = &ch->pcdata->pfile->account->pwd;
    }
  else {
    pwd = &ch->pcdata->pfile->pwd;
    }

  if( strcmp( arg, *pwd ) ) {
    send( "Wrong password.\n\r", ch );
    bug( "Changing Password: Incorrect guess - %s.", ch->descr->name );
    return;
    }

  if( strlen( argument ) < 5 ) {
    send( ch, "New password must be at least five characters long.\n\r" );
    return;
    }

  free_string( *pwd, MEM_PFILE );
  *pwd = alloc_string( argument, MEM_PFILE );

  if( is_set( &flags, 0 ) ) {
    send( ch, "Account password changed.\n\r" );
    save_accounts( );
    }
  else {
    send( ch, "Password changed.\n\r" );
    write( (player_data*) ch );
    }

  return;
}


/*
 *   HIGH SCORE ROUTINES
 */


const char* high_score_name( int i, char_data* ch )
{
  if( i < 0 || i >= max_high )
    return "---";

  return( is_incognito( high_score[i], ch ) ? "???"
    : high_score[i]->name );
}


void do_high( char_data* ch, char* argument )
{
  char       tmp  [ TWO_LINES ];
  char        r1  [ 8 ];
  char        r2  [ 8 ];
  int        who  [ 20 ];
  int     c1, c2;
  int          i;
  int       clss  = -1;
  int       race  = -1;

  if( is_confused_pet( ch ) )
    return;
  
  if( isalpha( *argument ) ) {
    for( clss = 0; clss < MAX_CLSS; clss++ )
      if( matches( argument, clss_table[clss].name ) )
        break;

    for( race = 0; race < MAX_PLYR_RACE; race++ )
      if( matches( argument, race_table[race].name ) )
        break;

    if( race == MAX_PLYR_RACE && clss == MAX_CLSS ) {
      send( ch, "Illegal syntax - see help high score.\n\r" );
      return;
      }

    c1 = max( atoi( argument ), 1 );

    for( i = c2 = 0; c2-c1 < 19 && i < max_high; i++ )
      if( ( clss == MAX_CLSS || high_score[i]->clss == clss )
        && ( race == MAX_PLYR_RACE || high_score[i]->race == race )
        && ++c2 >= c1 )
        who[c2-c1] = i;

    for( i = c2-c1+1; i < 20; i++ )
      who[i] = -1;
  
    c2 = c1+10;;
    }
  else {
    if( isdigit( *argument ) ) {
      c1 = max( min( atoi( argument ), max_high-20 ), 1 );
      c2 = c1+10;
      }
    else {
      c1 = 1;
      c2 = max( 11, min( ch->pcdata->pfile->rank-5, max_high-10 ) );
      }
    }

  send( ch, "              _         _   _  _   _   _         _  ___ \n\r" );
  send( ch, "       |_| | | _ |_|   |_  |  | | |_| |_   |  | |_   |  \n\r" );
  send( ch,"       | | | |_| | |    _| |_ |_| | \\ |_   |_ |  _|  |  \n\r" );
  send( ch, "\n\r\n\r" );

  for( i = 0; i < 10; i++ ) {
    sprintf( r1,  "[%d]", c1+i );
    sprintf( r2,  "[%d]", c2+i );
    sprintf( tmp, "%12s  %-20s %5s  %s\n\r",
      r1, high_score_name( clss == -1 ? c1+i-1 : who[i], ch ),
      r2, high_score_name( clss == -1 ? c2+i-1 : who[10+i], ch ) );
    send( tmp, ch );
    }
}   


void update_score( char_data* ch )
{
  int          pos;
  pfile_data*  pfile;

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

  pfile       = ch->pcdata->pfile;
  pfile->exp  = ch->exp;

  if( pfile->rank == -1 )
    return;

  for( pos = pfile->rank; pos > 0; pos-- ) {
    if( compare_exp( pfile, high_score[pos-1] ) <= 0 )
      break;
    high_score[pos-1]->rank++;
    swap( high_score[pos], high_score[pos-1] );
    }

  for( ; pos < max_high-2; pos++ ) {
    if( compare_exp( pfile, high_score[pos+1] ) >= 0 )
      break;
    high_score[pos+1]->rank--;
    swap( high_score[pos], high_score[pos+1] );
    }

  pfile->rank = pos;
}


/*
 *   SEARCH HIGH & DEATH LISTS
 */
 

int compare_exp( pfile_data *p1, pfile_data* p2 )
{
  if( p1->level != p2->level ) 
    return( p1->level > p2->level ? 1 : -1 );
  
  if( p1->exp < p2->exp )
    return -1;

  return( p1->exp > p2->exp );
}


int compare_death( pfile_data* p1, pfile_data* p2 )
{
  if( p1->deaths != p2->deaths ) 
    return( p1->deaths > p2->deaths ? 1 : -1 );
  
  return 0; 
}


int search( pfile_data** list, int max, pfile_data* pfile,
  pfcomp_func* compare )
{
  int     min  = 0;
  int     mid;
  int   value;

  if( list == NULL )
    return -1;

  max--;

  for( ; ; ) {
    if( max < min )
      return -min-1;

    mid    = (max+min)/2;
    value  = ( *compare )( pfile, list[mid] );

    if( value == 0 )
      break;
    if( value < 0 )
      min = mid+1;
    else 
      max = mid-1;
    }

  return mid;
}