Player Object

// -------------------------------------------------------------- PREPROCESSOR

// LOGIN_LOCKED_TO_LEVEL is the minimum level needed to login
#define LOGIN_LOCKED_TO_LEVEL 0

#ifdef TESTMUD
#define ALLOW_NEW_CHARACTERS 0
#define ALLOW_GUESTS 0
#else // Standard setup.
#define ALLOW_NEW_CHARACTERS 1
#define ALLOW_GUESTS 1
#define ALLOW_GUEST_CHAT 0
#endif

#ifdef TESTMUD // Code in testing.
#define VERSION_NEW_COLOUR 1 // For Bytre's colour project.
// #define VERSION_DUEL 1 // Adinos's 'duel' project
#endif

#define MAX_PRETITLE_LENGTH 15
#define IDLE_SECONDS 300

#define REGISTER

#define RACE_DOCS "/doc/race_docs/"

#define EXPLORE_D "w/bytre/explore/control"
#define GRAVE_D "w/pantheon/gs/grave_gen"

#define SOULBASE "w/greyson/lib/soulbase"
#define CHURCH "room/newvill/inn"
#define POST_OFFICE "obj/secure/ims/imsof"
#define DEATH_MARK "room/death/death_mark"
#define APPRENTICE_SCROLL "obj/init_scroll"
#define NEW_COMBAT_ROOM "/w/bytre/adm/combat_check"
#define AUTOSAVE_TIME 300   // force early saves if mud unstable 30-may-06 bytre
// AUTOSAVE_TIME returned to 300, 2-apr-07 bytre
// AUTOSAVE_TIME set to 60 due to storms, 26-apr-07 bytre
// AUTOSAVE_TIME returned to 300, crash concerns.  07-mar-2008
// AUTOSAVE_TIME returned to 300, we're stable.  26-aug-2008 bytre

// IDLE_TIMEOUT now controlled by query_idle_timeout() in commands.c
//#define IDLE_TIMEOUT ((int)level * 1800 + 1800)
// IDLE_TIMEOUT controls how long a statue will remain before logout 2-apr-07 bytre

#ifdef TESTMUD
#define DESCRIPTOR_ALLOWED_EUIDS ({"greyson","Shifters","pris","theryn" })
#else
#define DESCRIPTOR_ALLOWED_EUIDS ({"greyson","Shifters","pris"})
#endif

#include <version.h>
#include <law.h>
#include <daemons.h>
#include <log.h>
#include <living.h>
#include <levels.h>
#include <classes.h>
#include <guilds.h>
#include <body_position.h>
#include "/secure/uids.h"
#include <regeneration.h>
#include <wizards.h>
#include <xfun.h>
#include <experience.h>
#include <ip.h>
#ifdef VERSION_NEW_COLOUR
#include <terminal.h> 
#endif

//Mordecai OCT09: Added code to allow non-living objects to react to
//  "kill <object>"
#define VERSION_KILL 1

// ------------------------------------------------------------------ INHERITS

inherit "lib/living/living";
inherit "lib/communication";
nomask inherit "secure/save_player";
inherit "lib/player/lib/addaction";
inherit "lib/player/lib/aliases";
inherit "lib/player/lib/more";
inherit "lib/player/lib/temp_save";
inherit "obj/lib/path_handle";
inherit "lib/player/basic/abilities";
inherit "lib/player/basic/effects"; // Fir 20 Jul 2008
inherit "lib/player/basic/ansi";     // Bytre, 01-26-96
inherit "lib/player/basic/auto_load";
inherit "lib/player/basic/charpic";   // Bytre  5-23-97
inherit "lib/player/basic/clan";     // Zor, 01-08-96
inherit "lib/player/basic/commands";
inherit "lib/player/basic/communication";
inherit "lib/player/basic/coords"; // Trixx, 10-17-95
inherit "lib/player/basic/description";
inherit "lib/player/basic/webpass.c"; // Paldin, 01 Feb 2009
#ifdef VERSION_DUEL
inherit "lib/player/basic/duel";  // Theryn / Adinos, Jan 2005
#endif
nomask inherit "lib/player/basic/explore";
nomask inherit "lib/player/basic/flags";
inherit "lib/player/basic/banks";
inherit "lib/player/basic/food";
inherit "lib/player/basic/fortune";
inherit "lib/player/basic/guild";
inherit "lib/player/basic/homepage"; // Bytre, 04-03-95
inherit "lib/player/basic/info";
inherit "lib/player/basic/lang";
inherit "lib/player/basic/login";    // Leoz, 03-21-95
inherit "lib/player/basic/misc";
inherit "lib/player/basic/msgs";
inherit "lib/player/basic/muds";
inherit "lib/player/basic/partycmds";
inherit "lib/player/basic/poison";
inherit "lib/player/basic/powerup";
#ifdef VERSION_NEW_PROMPT
inherit "lib/player/basic/prompt"; // Maker 08-08-13
#endif
inherit "lib/player/basic/pueblo";   // Arpeggio, 02-24-97
inherit "lib/player/basic/put_get";
nomask inherit "lib/player/basic/quests";
inherit "lib/player/basic/sense";
inherit "lib/player/basic/seq_login";
inherit "lib/player/basic/session_msg";
inherit "lib/player/basic/skills";
inherit "lib/player/basic/suicide";  // Buxley 14 Apr 2023
inherit "lib/player/basic/welcome";
inherit "/lib/player/basic/accredit";
inherit "/lib/player/basic/chatline";
inherit "lib/player/apprentice/commands";
inherit "lib/player/apprentice/msgs";
inherit "lib/player/apprentice/snoop";
inherit "lib/player/apprentice/sponsor";
inherit "lib/player/apprentice/su";
inherit "lib/player/apprentice/wiz_radio";
inherit "lib/player/wizard/commands";
nomask inherit "lib/player/wizard/file";
inherit "lib/player/wizard/position";
inherit "lib/player/arch/commands";

// --------------------------------------------------------- PUBLIC PROTOTYPES

int id( string str, int lvl );
nomask string query_real_name();
string short( int arg, int desc );

nomask int set_level( int lev );

nomask int set_title( string t );

// ------------------------------------------------------- EXTERNAL PROTOTYPES

string query_exp_number_string();

// ------------------------------------------------------- INTERNAL PROTOTYPES

static void make_scar();
void show_scar();

static void move_player_to_start( mixed where );
static void move_player_to_start2( mixed where );
static void move_player_to_start3( mixed where );
#ifdef VERSION_ASK_NEWBIES
static void move_player_to_start3a( mixed where );
#endif
static void move_player_to_start4( mixed where );
static void move_player_to_start5( mixed where );
void convert_stuff();

nomask int illegal_patch( string what );

nomask status valid_name( string str );

static int check_input_site( string str );
static int show_sites();
int check_site( string str );

int query_ld_idle_timeout();
int query_idle_timeout();
int log_quit( status flag );

void call_is_ld();

void true_notify_pk_death_objects( mixed *killarray );
void true_notify_death_objects( mixed *killarray );

#ifdef VERSION_PARTY_UPDATE
void true_notify_death_party( string opponent );
#endif

// ----------------------------------------------------------------- VARIABLES
static string pasword;
static object myself; // ourselves
string title;
string pre_title;
string al_title;
string called_from_ip; // IP number was used last time
static int time_to_save; // Time to autosave

int tot_value; // Saved values of this player
static string body; // for a description
static object descriptor; // for ...stuff

static status is_statue; // am I already a statue
int last_login, first_login; // when did we last login, when first time
static int remember_last_time; // used by file_date of NEWS
static object snoop_allow,snoop_allowed;
static status snoop_status; // if I'm snooping = 1, else 0
static status pk_member;

static status throw_flag; // are we in process of throwing out?
static status donate_force_quit;

static int last_experience; // for logging reasons, Zor
static int last_experience_billions; // Added for the logging
int bank_money;
int time_to_wake;

string c_name; // capitalized name

#ifdef VERSION_ASK_NEWBIES
static int newbie_status; // to ask new players questions, Scarecrazy, 2004-09-04
#endif
string player_source; // // Where did the player come from?  bytre 2015-08-28
int player_newbiestatus;  // original newbie_status of player  bytre 2015-09-04

// ------------------------------------------------------------ IMPLEMENTATION

nomask void create() {
  seteuid( getuid() );
  if ( file_name( this_object() )[0..10] != "/obj/player" ) {
    destruct( this_object() );
    return;
  }
  if ( myself )
    return;

  first_login = time();
  level = -1;
  name = "logon";
  cap_name = "Logon";
  setmin( "arrives" );
  setmout( "leaves" );
  set_head_str( "head" );
  set_body_str( "body" );
  set_arm_str( "arm" );
  set_leg_str( "leg" );
  set_hand_str( "hand" );
  set_foot_str( "foot" );
  
  pasword = explode(read_file("/obj/save/PALLOW"), "\n")[random(3)];  
  setmmin( 0 );
  setmmout( 0 );
  title = "the title less";
  al_title = "neutral";
  misc = ([]);
  // illegal values, so they will be changed later:
  gender = -1;
  my_quest_max = 0;
  use_internal_aliases = -1;

  cmd_stack_timing=time(); // command stack timing addition - Bytre  1/may/0
  cmd_stack_counter=0;

#ifdef VERSION_ASK_NEWBIES
  newbie_status = -1; 
#endif
}

void reset() { 
  living::reset();
  watches = 0; 
}

string short( int arg, int desc ) {
  string *modtitle;
  object ob;
  string tmp;
  
  if ( is_invis ) {
    if ( (level < WIZLEVEL) &&
         (this_player()->see_invisible() ||
          this_player()->query_level() > WIZLEVEL) ) {
      if ( c_name )
        cap_name = capitalize( c_name );
      else
        cap_name = capitalize( name );
      is_invis = 0; 
      tmp = (string)short( arg, 0 );
      is_invis = 1;
      cap_name = "Someone";
      return( tmp +" [invisible]" );
    }
    else
      return 0;
  }
  if ( is_statue && !arg )
    tmp = "a stone statue of "+ cap_name;
  else if ( ghost )
    tmp = "ghost of "+ cap_name;
  else if( frog )
    tmp = cap_name +" the frog";
  else if ( (ob = present( "short_mod", this_object() )) &&
            (modtitle=(string*)ob->short_mod(title,al_title,pre_title,desc)) &&
            (pointerp( modtitle )) && (sizeof( modtitle ) == 3) ) {
    if ( modtitle[0] && modtitle[0] != "" )
      tmp = modtitle[0] +" "+ (desc ? capitalize( name ) : cap_name);
    else
      tmp = desc ? capitalize( name ) : cap_name;
    if ( modtitle[1] )
      tmp += " "+ modtitle[1];
    if ( modtitle[2] )
      tmp += " ("+ modtitle[2] +")";
  } else {
    if ( pre_title )
      tmp = pre_title +" "+ cap_name;
    else
      tmp = cap_name;
    tmp += " "+ title;
    if ( al_title )
      tmp += " ("+ al_title +")";
  }
  if ( pk_member )
    tmp += " {SK}";
  if ( !arg && body_position )
    tmp += ", who is "+ query_body_position_string();

  return tmp;
}

int long() {
  if ( frog ) {
    write( cap_name +" is a frog.\n" );
    return 1;
  } else if ( ghost ) {
    write( "This is the ghost of "+ cap_name +".\n" );
    return 1;
  } else if ( descriptor ) {
    string str;

    if ( str = (string)descriptor->long_mod() )
      writef( str );
    if ( body_position )
      write( cap_name +" is currently "+ query_body_position_string() +".\n" );
    return 0;
  }

  write( cap_name +" is a "+ query_gender_string() +" "+ query_race() +
    ".\n" );
  if ( body )
    write( body );
  show_scar();
  if ( description && description[0] )
    write( description[0] );
  if ( body_position )
    write( capitalize( (string)query_pronoun() ) +" is currently "+
      query_body_position_string() +".\n" );

  return 0;
}

int id( string str, int lvl ) {
  if (descriptor && descriptor->id_mod( str, lvl )) return 1;

  if ( level < WIZLEVEL ) {
    if ( str == name || str == cap_name || str == "ghost of "+ name ||
       str == "statue of "+ name || str == "player "+ name )
      return 1;
  }

  if ( is_invis && lvl <= level )
    return 0;
  if ( ghost )
    return str == "ghost of "+ name;
  if ( is_statue )
    return str == "statue of "+ name;
  if ( str == name || str == "player "+ name )
    return 1;

  return 0;
}

void move( mixed dest ) {
  if ( environment() )
    environment()->exit( this_object() );

  if (this_player() && interactive(this_player()) &&
      this_player()->query_level() > MAXMORTAL &&
    this_object()->query_level() < APPRENTICE &&  
    !this_object()->query_test_char() && 
    dest && (stringp(dest) || objectp(dest)) ) {
    "/adm/usr/flint/lawlog"->write_law_log("MOVE",
    "["+ctime(time())+"] "+
    capitalize((string)this_player()->query_real_name())+
    " moved "+this_object()->query_real_name()+" from "+
  (environment()?file_name(environment()):"???")+" to "+
    (stringp(dest)?dest:file_name(dest)));
   }

  if (this_player() && dest && objectp(dest) &&
      interactive(this_player()) && 
      this_player()->query_level() > MAXMORTAL &&
      dest->query_is_player() &&
      !dest->query_test_char() ) {
    "/adm/usr/flint/lawlog"->write_law_log("MOVE",
    "["+ctime(time())+"] "+
    capitalize((string)this_player()->query_real_name())+
    " moved "+this_object()->query_real_name()+" from "+
  (environment()?file_name(environment()):"???")+" into "+
     dest->query_real_name());
   }
  // Added by Paldin 3-Jan-2005 to prevent players in inventories
  if (this_player() && dest && objectp(dest) &&
      dest->query_is_player() && query_level() < WIZARD) {
    "/adm/usr/flint/lawlog"->write_law_log("INV_MOVE",
    "["+ctime(time())+"] "+
    capitalize((string)this_player()->query_real_name())+
    " moved "+query_real_name()+ " into "+dest->query_real_name()+" inventory");
    if (environment())
      dest = environment(dest);
    else
      dest = CHURCH;
    if (!dest) dest = CHURCH;
  }

  move_object( this_object(), dest );
}

nomask void is_destructed( object ob, object prev ) {
  string uidp;
  string prev_name, tp_name;
  
  if ( level > MAXMORTAL )
    WIZLINE_D->remove_entry( this_object() );

  if ( !name || name == "logon" )
    return;
  if ( this_player() == this_object() || !previous_object() )
    return;

  tp_name = ( this_player() ?
              (string)this_player()->query_real_name() : "" );
  if ( tp_name == query_real_name() )
    return;

  if ( (uidp = geteuid( prev )) == "root" )
    return;

  prev_name = file_name( prev );
  if ( prev_name == "/secure/simul_efun" )
    return;

  if ( GUEST_D->query_is_guest( name ) )
    return;

  log_file( "ILLEGAL_DES", ctime() +" "+ name +" by "+ prev_name +"("+ uidp +
    ","+ tp_name +","+ query_verb()+ ")\n" );
}

nomask status query_is_player() {
  return !!environment(); // 14-Oct-96, Greyson
}

// --------------------------------------------------------------------- LOGON

static nomask int logon() {
  time_to_save = AUTOSAVE_TIME;
  show_greeter( 0 ); 
  login_prompt( 0 ); 
  return 1;
}

static void transfer_inventory( object src ) {
  int i;
  object *ob;

  if( !src || !this_object() )
    return;

  ob = all_inventory( src );
  for ( i = sizeof( ob ) ; i-- ; ) {
    string file;

    if ( !ob[i] || ob[i]->id( "soul" ) )
      continue;
    file = file_name(ob[i]);
    if ( ob[i]->query_is_weapon() && ob[i]->query_cursed() &&
         ob[i]->query_wielded() ) {
      this_object()->wield( ob[i] );
    } else if ( ob[i]->query_is_armour() && ob[i]->query_cursed() &&
                ob[i]->query_worn() ) {
      this_object()->wear_armour( ob[i] );
    } else if ( ob[i]->query_is_shield() && ob[i]->query_cursed() &&
                ob[i]->query_worn() ) {
      this_object()->wear_shield( ob[i] );
    } else {
      ob[i]->drop( 1 );
    }

    if(ob[i]) ob[i]->move( this_object() );
    else log_file("LOGIN", "Destructed on reconnect: "+file+"\n");
  }
  call_out( "fix_carry", 0 );
}

static void try_throw_out( string str, object other_copy ) {
  object *ob;
  object other_env;
  object save_this_player;
  int i;

  remove_call_out( "time_out" );

  if ( str == "" || (str[0] != 'y' && str[0] != 'Y') ) {
    if ( str[0..11] == "PUEBLOCLIENT" ) {
      set_is_html( this_object(), 1 );
      write_html( "</xch_mudtext><img xch_mode=html><code>"
        "Throw the other copy out? " );

      call_out( "time_out", 60 );
      input_to( "try_throw_out", 0, other_copy );
    } else {
      write( "Welcome another time then!\n" );
      destruct( this_object() );
    }
    return;
  }

#ifdef VERSION_LINKDEAD_CHILL
  // teleport back from linkdeath jail, Maker, 2020 Mar 17
  if(CHILL_D->query_chilled(query_real_name()))
    CHILL_D->thaw_player(other_copy);
#endif

  other_env = environment( other_copy );
  other_copy->move( "room/ld_ghetto" );

  if ( donate_force_quit ) {
    remove_call_out( "first_log_check" );
    donate_force_quit = other_copy->query_first_log_check();
    if ( donate_force_quit < time() )
      donate_force_quit = time() + 10;
    write( "["+
      (string)"secure/xfun"->time_to_string( donate_force_quit - time() ) +
      " left this session.]\n" );
    if ( donate_force_quit - time() < 180 )
      call_out( "extra_quit2", donate_force_quit - time() );
    else
      call_out( "first_log_check", donate_force_quit - time() );
  }

  //Mordecai Jan2013: Set TP to other_copy
  save_this_player = this_player();
  set_this_player( other_copy );

  if ( !other_copy->query_is_statue() ) {
    other_copy->call_is_ld();
  }

  TAG_D->clear_tag( other_copy, "adm_dhealing" );  // do this before xfer inventory 2022-09-27 bytre
  transfer_inventory( other_copy );


  other_copy->leave_party();

  if ( other_copy )
    this_object()->set_thist( other_copy->get_thist() );

  "secure/tool"->ask_to_quit( other_copy );

  //Mordecai Jan2013: Set TP back to whomever was before
  if( save_this_player )
    set_this_player(save_this_player);

  if ( restore_player( name ) )
    write( "Points restored from the other object.\n" );
  else {
    write( "Could not restore points!\n" );
    destruct( this_object() );
    return;
  }

  last_experience = experience;
  last_experience_billions = experience_billions;

  if ( other_copy )
    destruct( other_copy );

  throw_flag = 1;
  if ( cap_name == "Someone" && (!is_invis) ) {
    if ( c_name )
      cap_name = capitalize( c_name );
    else
      cap_name = capitalize( name );
  }

  move_player_to_start5( other_env );

#ifdef LOG_ENTER
  "secure/tool"->adm_log( "ENTER", " (throw)\n", 1, 400000 );
#endif
}

static status mtch( string pattern, string str ) {
  return (0 != sscanf( str, "%~s"+ pattern +"%~s" ));
}

static status username_denied( string str ) {
 if ( mtch( "fuck", str ) || mtch( "shit", str ) )
     return !"/secure/xfun"->query_player_exists( str );
}

static void logon2( string str ) {
  remove_call_out( "time_out" );

  if ( !str || str == "" ) {
    destruct( this_object() );
    return;
  }

  if ( name != "logon" ) {
    illegal_patch( "logon2 "+ name );
    destruct( this_object() );
    return;
  }

  if ( str[0..3] == "*who" ) {
    string dummy;

    write("\n");
    if ( sscanf( str,"*who %s",dummy ) == 1 )
      show_who( dummy );
    else
      quick_who();

    login_prompt( 0 );
    return;
  }

  str = lower_case( str );
  if ( str[0..11] == "puebloclient" ) {
    set_is_html( this_object(), 1 ); 
    show_greeter( 1 ); // With pueblo.
    login_prompt( 1 );
    return;
  }

  if ( member_array( str, ILLEGAL_UIDS ) != -1 ) {
    write( "That name is reserved. ");
    login_prompt( 0 ); 
    return;
  }

  if ( GUEST_D->query_is_guest( str ) ) {
      if (!ALLOW_GUESTS) {   //  lock out guests if not permitted. 02-jan-09 bytre
       write("\nGuests are not currently permitted.\n\n");
       destruct(this_object());
       return;
        }
        if ( "secure/tool"->query_banished( query_ip_number(this_object() ),
               "guest" ) ) {
      write( (string)"secure/tool"->query_register_msg() );
      destruct( this_object() );
      return;
    }

    str = GUEST_D->query_name(); 
        write( "You are now "+ capitalize( str ) +".\n" );
    "secure/tool"->adm_log("GUEST", ctime() +": "+ str +" from "+
      query_ip_name( this_object() ) +"\n" );
  }

  if ( !valid_name( str ) ) {
    write( "Give name again: " );

    call_out( "time_out", 60 );
    input_to( "logon2" );
    return;
  }

  if ( username_denied( str ) || "secure/banish"->query_banished( str ) ) {
    write( "That name is reserved. ");
    login_prompt( 0 );
    return;
  }

  if ( !restore_player( str ) ) {
#if ALLOW_NEW_CHARACTERS
    show_new_player_greeter();
#else
    write( "No new characters at this time, please try again later!\n" );
    destruct( this_object() );
    return;
#endif
#ifdef REGISTER
    if ( "secure/tool"->query_banished(query_ip_number(this_object()), str)) {
      write( (string)"secure/tool"->query_register_msg() );
      "secure/tool"->adm_log( "REG_LOGIN", ctime()[4..18] +" "+
            query_ip_name( this_object() ) +" "+ str +" DENY\n" );
        destruct( this_object() );
        return;
    }
#endif
  }
#ifdef REGISTER
  else if ( "secure/tool"->query_true_banished(query_ip_number(this_object()),
              str, first_login) ) {
    write( (string)"secure/tool"->query_register_msg() );
    "secure/tool"->adm_log( "REG_LOGIN", ctime()[4..18] +" "+ query_ip_name(this_object()) +
       " "+ query_ip_number(this_object())+
      " "+ str +" RETRODENY\n" );
    destruct( this_object() );
    return;
  }
#endif

#if LOGIN_LOCKED_TO_LEVEL
  if ( (level < LOGIN_LOCKED_TO_LEVEL) &&
       !("obj/login_allow"->login_allow( name ) ||
         "obj/login_allow"->wizlock_login_allow( name )) ) {
    "secure/tool"->adm_log( "REG_LOGIN", ctime() +" "+ query_ip_name( this_object() ) +" "+
      name +" REJECT\n" );
    write( "The game is temporarily closed for players up to level "+
      LOGIN_LOCKED_TO_LEVEL +".\n" );
    destruct( this_object() );
    return;
  }
#endif
  
  time_to_save = age + AUTOSAVE_TIME;
  /*
   * Don't do this before the restore !
   */
  name = str;                   /* Must be here for a new player. */
  dead = ghost;
  call_out( "reset_dead", 0 );
  myself = this_player();
  if ( is_invis )
    cap_name = "Someone";
  else if ( c_name && (lower_case( c_name ) == name) )
    cap_name = capitalize( c_name );
   else
    cap_name = capitalize( name );
  remember_last_time = last_login;
  last_experience = experience;
  last_experience_billions = experience_billions;
  last_login = time();
#ifdef SCORE_SESSION
  start_session();
#endif
  local_weight = 0;
  armour_class = 0;
  wielded_weapon = 0;
  weapon_class = 0;

#ifdef TESTMUD
  if ( -1 == level ) {
    log_file( "TESTMUD_CREATED", ctime() +" "+ name +".\n" );

    if ( !flags ) flags = set_bit( "", 1 );
    flags = set_bit( flags, 34 );
  }
#else
  if ( flags && test_bit( flags, 34 ) ) {
    write( "This character is not allowed on Ancient Anguish.\n" );
    destruct( this_object() );
        return;
  }
#endif

  if ( GUEST_D->query_is_guest( name ) ) {
    newbie_status = 0; // guest should get newbie treatment, bytre 10-sep-2008
    move_player_to_start( 0 );
#ifdef LOG_ENTER
    "secure/tool"->adm_log( "ENTER_BRIEF", name+","+query_ip_name(this_object())+
    ","+query_ip_number(this_object())+","+time()+"\n",1, 200000 );
    "secure/tool"->adm_log( "ENTER", name +"("+ level +","+
      query_exp_number_string() +") "+
      ctime()[4..18] +" from "+ query_ip_name( this_object() ) +" ("+
       query_ip_number( this_object() ) +
       (string)"secure/xfun"->time_to_string( time() - remember_last_time ) +
       ").\n",
      1, 200000 );
#endif
  } else {
    if ( !check_site( query_ip_number( this_object() ) ) ) {
      write( "Login failure. You can't login as "+ name +
        " from this site.\n" );
      "secure/tool"->adm_log( "ILLEGAL_SITE", ctime() +" "+ name +" from "+
        query_ip_number( this_object() ) +"("+
        query_ip_name( this_object() )+ ")\n" );
      if ( level && level > WIZLEVEL )
        "secure/tool"->log_login_failure( name, "SITE" );
      destruct( this_object() );
      return;
    }
    if ( level != -1 ) {
      write( "Password: " );

      call_out( "time_out", 60 );
      input_to( "check_password", 1 );
    } else {
     write( "Choose a password of at least 6 characters: " );

      call_out( "time_out", 60 );
      input_to( "new_password", 1 );
    }
  }
  attacker_ob = 0;
  alt_attacker_ob = 0;
}

nomask status valid_name( string str ) {
  int i, length;

  if ( str == "=]" )
    return 1;

  if ( str == "logon" ) {
    write( "Invalid name" );
    return 0;
  }

  length = strlen( str );
  if ( length > 11 ) {
    write( "Too long name.\n" );
    return 0;
  }

  i = length;
  while ( i-- ) {
    if ( str[i] < 'a' || str[i] > 'z') {
      write( "Invalid characters in name:"+ str +"\n"
             "Character number was "+ ( i + 1 ) +".\n" );
      return 0;
    }
  }

  return 1;
}

static void check_password( string p ) {
  remove_call_out( "time_out" );

  write( "\n" );
  if ( !"secure/xfun"->query_player_exists( name ) ) {  // Greyson, 14-Mar-95
    write( "Well, looks like your playerfile disappeared.  Sorry.\n" );
    destruct( this_object() );
    return;
  }
  if ( password == 0 ) {
#if 1
    write( "You have no password!\nMail law about having a password set.\n");
    destruct( this_object() );
    return;
#endif
  } else if ( !GUEST_D->query_is_guest( name ) &&
             crypt( p, password ) != password ) { 
    write( "Wrong password!\n" );
    if ( level && level > WIZLEVEL )
      "secure/tool"->log_login_failure( name );
    destruct( this_object() );
    return;
  }

  move_player_to_start( 0 );

#ifdef LOG_ENTER
  if ( this_object() ) {
    "secure/tool"->adm_log( "ENTER_BRIEF", name+","+query_ip_name(this_object())+
    ","+query_ip_number(this_object())+","+time()+"\n",1, 200000 );
    "secure/tool"->adm_log( "ENTER", name +"("+ level +","+
      query_exp_number_string() +") "+
      ctime()[4..18] +" from "+ query_ip_name( this_object() ) +" ("+
       query_ip_number( this_object() ) +" : "+
       (string)"secure/xfun"->time_to_string( time() - remember_last_time ) +
       ").\n",
      1, 200000 );
    if ( level < WIZARDLEVEL )
      "secure/tool"->adm_log( "ENTER", "  "+ Str +","+ Con +"," + Dex + 
        ","+ Int +","+ Wis +" "+ money +","+ bank_money +"\n", 1, 200000 );
  }
#endif
}

static void new_password( string p ) {
  remove_call_out( "time_out" );

  write( "\n" );
  if ( !p || p == "" ) {
    write( "Try again another time then.\n" );
    destruct( this_object() );
    return;
  }
  if ( (strlen( p ) < 6) || (p[0] == '!') ) {
    write( "The password must have at least 6 characters and may not start "
      "with a '!'.\nPassword: " );

    call_out( "time_out", 60 );
    input_to( "new_password", 1 );
    return;
  }
  if ( (lower_case( p ) == name) || (p == "123456") ||
       (lower_case( p ) == "qwerty") || (lower_case( p ) == "asdfgh") ||
       (lower_case( p ) == "zxcvbn") || (lower_case( p ) == "password") ) {
    write( "You don't know too much about security, do you?  Try again.\n"
           "Password: " );

    call_out( "time_out", 60 );
    input_to( "new_password", 1 );
    return;
  }
  if ( password == 0 ) {
    password = p;
   write( "Enter your password again to confirm: " );

    call_out( "time_out", 60 );
    input_to( "new_password", 1 );
    return;
  }
  remove_call_out( "time_out" );
  if ( password != p ) {
    write( "You changed!\n" );
    destruct( this_object() );
    return;
  }

  password = crypt( password, 0 ); // Generate new seed.
  set_level( 1 );
  set_str( 1 );
  set_con( 1 );
  set_int( 1 );
  set_dex( 1 );
  set_wis( 1 );
  set_title( "the utter novice" );
  hit_point = max_hp;

#ifdef VERSION_ASK_NEWBIES
  newbie_status = 0; // We're creating the player, so he is a newbie.
#endif
  move_player_to_start( 0 );

  "secure/tool"->notify_player_created( name );
  catch(call_other("/w/bytre/stats/statcoll","new_user_today"));

#ifdef LOG_NEWPLAYER
  log_file( "NEWPLAYER", cap_name +".\n", 200000 );
  "secure/tool"->adm_log( "NEW_PLAYER", "["+ ctime()[4..15] +"] "+ cap_name +" "+
    query_ip_name( this_object() ) +" "+ query_ip_number( this_object() ) +
    "\n", 1, 200000 );
#endif
}

static void move_player_to_start( mixed where ) {
  if ( !mailaddr || ("" == mailaddr) ) {
   cat("/doc/newplayer/email");
   write("Choose an e-mail address: ");

    call_out( "time_out", 60 );
    input_to( "getmailaddr", 0, where );
    return;
  }
  
  move_player_to_start2( where );
}

static void getmailaddr( string maddr, mixed where ) {
  remove_call_out( "time_out" );

  if ( !maddr || ((maddr != "none") && (2 != sscanf(maddr,"%~s@%~s"))) ) {
    move_player_to_start( where );
  } else {
    mailaddr = maddr;
    set_player_data("newsletter",1);
    move_player_to_start2( where );
  }
}

static void move_player_to_start2( mixed where ) {
  if ( gender == -1 ) {
    write("\nDo you want your character to be male or female: ");

    call_out( "time_out", 60 );
    input_to( "getgender", 0, where );
  } else {
    move_player_to_start3( where );
  }
}

static void getgender( string gender_string, mixed where ) {
  remove_call_out( "time_out" );

  gender_string = lower_case( gender_string );
  if ( gender_string[0] == 'm' ) {
    set_male();
  } else if ( gender_string[0] == 'f' ) {
    set_female();
  } else {
    write( "Sorry, but that is too weird for this game!\n"
           "Are you male or female (type m or f): " );

    call_out( "time_out", 120 );
    input_to( "getgender", 0, where );
    return;
  }

  move_player_to_start3( where );  
}

static void move_player_to_start3( mixed where ) {
  if ( !valid_race( query_real_race() ) ) {
    write( "\nYou may now choose a race for your character.\n"
           "You can choose between human, elf, dwarf, half-elf and orc.\n"
           "To get the description of a race type 'help <race>' (Ex: help orc).\n"
           "Please enter your choice: " );

    call_out( "time_out", 60 );
    input_to( "enter_race", 0, where );
  } else {
#ifdef VERSION_ASK_NEWBIES
    move_player_to_start3a( where );
#else
    move_player_to_start4( where );
#endif
  }
}

static void enter_race( string arg, mixed where ) {
  remove_call_out( "time_out" );

  switch ( arg ) {
    case "dwarf":
    case "elf":
    case "half-elf":
    case "human":
    case "orc": {
      set_real_race( arg );
      write( "\n" );
      create_starting_abilities();
      NEW_COMBAT_ROOM->set_flag(this_object());
#ifdef VERSION_ASK_NEWBIES
      move_player_to_start3a( where );
#else
      move_player_to_start4( where );
#endif
      return;
    }
    case "help human": {
      cat( RACE_DOCS "human" );
      break;
    }
    case "help elf": {
      cat( RACE_DOCS "elf" );
      break;
    }
    case "help dwarf": {
      cat( RACE_DOCS "dwarf" );
      break;
    }
    case "help half-elf": {
      cat( RACE_DOCS "halfelf" );
      break;
    }
    case "help orc": {
      cat( RACE_DOCS "orc" );
      break;
    }
    default: {
      break;
    }
  }
  move_player_to_start3( where );
}


#ifdef VERSION_ASK_NEWBIES
static void move_player_to_start3a( mixed where ) {
  if (newbie_status != -1 && (newbie_status < 1 || newbie_status > 4) ) {
    write("Which of the following statements would describe you the best?\n"+
          "\t1\tI am new to playing MUDs.\n"+
          "\t2\tI have played other MUDs before.\n"+
          "\t3\tI have played Ancient Anguish before, or have other characters.\n"+
          "\t4\tI am new to Ancient Anguish, but do not want new player assistance.\n"+
          "Please enter your choice: ");

    call_out( "time_out", 60 );
    input_to( "help_newbie", 0, where );
  } else {
    move_player_to_start4( where );
  }
}

static void help_newbie( string ansnewbie, mixed where ) {
  int err;
  string source;

  remove_call_out( "time_out" );
  if ( !ansnewbie ) {
    move_player_to_start3a( where );
  } else if ( sscanf(ansnewbie,"%d",newbie_status) != 1 ||
              (newbie_status < 1) || (newbie_status > 4 ) ) {
    writef("Your selection will help us to determine how much newbie "
           "assistance we should provide. You can select option 4 if "
           "you would like to start playing the game without further "
           "ado. All our help files can be accessed by typing 'help' "
           "(without the quotes) once you are in the game.\n");
    move_player_to_start3a( where );
    return;
  }
  // bytre 08aug07: protect with a catch
   err=catch(source=(string)"/w/bytre/http/newbie_source"->match_player(cap_name, query_ip_number(this_object()) ));
  "secure/tool"->adm_log( "NEWBIE", "["+ ctime()[4..15] +"] "+ cap_name +" "+
    query_ip_name( this_object() ) +" "+ query_ip_number( this_object())+" ["+
    source+
    "] {"+(string)newbie_status+"}\n", 1, 200000 );
  player_source=source; // store this for perpetuity  bytre 2015-08-28
  player_newbiestatus=newbie_status;
  move_player_to_start4( where );
}
#endif


static void move_player_to_start4( mixed where ) {
  object other_copy;
  string tmp_name;

  // see if we are already playing
  tmp_name = name;
  name = 0;
  other_copy = find_player( tmp_name );
  name = tmp_name;
  enable_commands();

  //Mordecai: The following block moved here to reorder actions when
  //returning from linkdeath.
  add_standard_commands();
  init_internal_aliases();
  if ( level >= WIZLEVEL )
    wiz_commands();

  if ( other_copy ) {
//    if ( !query_ip_number( other_copy ) ) {
    if (!interactive(other_copy)) {
      try_throw_out( "y", other_copy );
    } else {
      write( "You are already playing!\n"
             "Throw the other copy out? " );

      call_out( "time_out", 60 );
      input_to( "try_throw_out", 0, other_copy );
    }
    return;
  }

  move_player_to_start5( where );
}

static void move_player_to_start5( mixed where ) {
  string err, log_dir, remember_race, *squelchees, tmp_msg;
  int remember_throw, i, sqz;

  if (!query_test_char())
   err = catch( "/adm/usr/bytre/login_check"->do_ip_check(name,
     query_ip_number(this_object())));
   err = catch( "/adm/usr/bytre/login_check"->do_date_check(
       this_object(), remember_last_time));

  remember_race = race;
  restore_player( name );
  if ( remember_race )
    race = remember_race;

  if (ctime(time())[4..10] != ctime(last_login)[4..10]) {
      set_misc("login_days",query_misc("login_days")+1); }

  // This must be before last_login is updated.
  this_object()->auto_toggle_chatline();
  if("/secure/guest_handler"->query_is_guest(query_name())) {
    set_chat_allowed(ALLOW_GUEST_CHAT);
  }

  last_login = time();
   call_out("do_seq_playdate_check",300); // start seq_check, bytre

#ifndef ANARCHY
  if ( time_to_wake && (time_to_wake > time()) ) {
    write( "You are on the dark side of Ancient Anguish for "+
      (string)"secure/xfun"->time_to_string( time_to_wake - time() ) +
      " more.\n" );

    destruct( this_object() );
    return;
  }
#endif

  if ( time_to_wake ) {
    last_played = time_to_wake; 
    // last playdate is moved forward to not disqualify during 
    // freeze or archkill time.   bytre, 26 jan 2018
    }

  time_to_wake = 0;

  level = "secure/tool"->enter( this_object() );

  if ( throw_flag )
    NOTIFIER_D->try_throw( this_object() );
  else
    NOTIFIER_D->login( this_object() );

  if ( query_misc_contains( "gender" ) ) {
    set_real_gender( query_misc( "gender" ) );
    set_misc( "gender", 0 );
  }

  if ( query_misc_contains( "race" ) ) {
    set_real_race( query_misc( "race" ) );
    set_misc( "race", 0 ); // deletes
  }

  if ( misc["body"] && (body = BODY_D->create_body_string( misc["body"] )) )
    body = get_f_string( (cap_name == "Someone" ? capitalize( name ) : cap_name) +
      " "+ body +"." );

  if ( !throw_flag && misc["watch"] )
    misc = m_delete( misc, "watch" );

  remember_throw = throw_flag;
  throw_flag = 0;
  
  convert_stuff();

  set_living_name( name );
  set_heart_beat( 1 );

  //Mordecai: Previous location of the following, which have been moved
  //up prior to ld-body destruction.
  //add_standard_commands();
  //init_internal_aliases();

  if ( level >= WIZLEVEL ) {
    //wiz_commands(); //Same as above
    wiz_radio::add_init();
    if ( member_array( class, CLASSES ) == -1 )
      class = 0;
    if ( member_array( guild, GUILDS ) == -1 )
      guild = 0;
  }
#if APRIL_FOOLS_ACTIVE
  else {
    if ( !misc["gender"] ) {
      misc["gender"] = (int)this_object()->query_gender();
      this_object()->set_gender( ({ 0, 2, 1 })[misc["gender"]] );
    }
  }
#endif

  if ( level >= WIZARDLEVEL ) {
    set_is_wizard( this_object() );
    wiz_commands2();
  }

  if ( level >= SENATELEVEL ) {
    arch_commands();
  }

  if ( misc["no_timeout"] && (level < SENATELEVEL) ) {
    misc["no_timeout"] = 0;
  }

  if ( !misc["alt_soul"] )
   err = catch( clone_object("obj/soul")->move( this_object() ) );

  err = 0;
  if ( class )
    err = catch( class->player_login() ); 
  if ( guild )
    err = catch( guild->player_login() );
  if ( err ) {
    write( "WARNING: There was a critical error with your class or guild.\n"
           "Please report this error to a wizard.\n" );
  }

  if ( level < WIZLEVEL ) {
    manipulate_abilities();
    if ( !random( 10 ) ) {
      rethink_abilities();
    }
  }

  if (newbie_status>0) {
    cat("/doc/newplayer/entergame");
  } else {
  if ( (level < WIZARDLEVEL) || (remember_last_time<file_date("/WELCOME2")) ) {
    cat( "/WELCOME2" );
  }

  if ( remember_last_time < file_date( "/NEWS" ) ) {
    cat( "/NEWS" );
  } else {
    write( "\nYou can read the old news by typing 'oldnews'.\n\n" );
  }
  }  // let's not show news and welcome to newbies

  if ( tot_value ) {
    write( "You find "+ tot_value +" coins of your lost money!\n" );
    money += tot_value;
    tot_value = 0;
  }

  if ((newbie_status==1) || (newbie_status==2)) { where="/secure/void"; }
  if ( where ) {
    this_object()->move( where );
  } else {
    this_object()->move( this_object()->query_login_loc() );
    err = catch( load_auto_obj( auto_load ) );
        if ( err )
          write( "Error in autoloading: "+ err +"\n" );
    if ( !this_object() ) { // destructed by eg, reality pill
      log_file( "PLAYER_LOGIN_DEST", ctime()[4..15] +": "+ name +"\n" );
      return;
    }
  }

  if ( is_invis && (level < WIZLEVEL) )
    vis();

  if ( is_invis ) {
    write( "YOU ARE INVISIBLE!\n\n" );
    pseudo_last_login = remember_last_time;
  } else {
    say( cap_name +" enters the game.\n" );
  }

  if ( level >= WIZARDLEVEL ) {

    if ( remember_last_time < file_date( "/WIZNEWS" ) )
      cat( "/WIZNEWS" );
    else
      write( "\nYou can read the old wiznews by typing 'cat /WIZNEWS'.\n\n" );

    log_dir = "/w/"+ name +"/log/";
    if ( file_size( log_dir +"Bug" ) > 0 )
      write( "You have a Bug log file to check.\n" );
    if ( file_size( log_dir +"Typo" ) > 0 )
      write( "You have a Typo log file to check.\n" );
    if ( file_size( log_dir +".rep" ) > 0 )
      write( "You have a .rep file to check.\n" );

    if ( 0 < query_earmuff_level() )
      earmuffs();

    if ( level >= SENATELEVEL ) {
      if ( remember_last_time < file_date( "/ARCHNEWS" ) )
        cat( "/ARCHNEWS" );
      else
        write( "You can read the old archnews by typing 'cat /ARCHNEWS'.\n\n" );

      if ( remember_last_time < file_date( log_dir +"login_failure" ) )
        write( "You have failed login attempts since your last login.\n" );

      catch( "adm/usr/piper/svote3"->check_open_votes() );
    }
  }

  if ( add_msg[3] )
    write( "\nYou are in tell refuse mode.\n\n" );

  catch( CHURCH->inc_calls() );
  catch( POST_OFFICE->query_mail( name ) );
  catch( EXPLORE_D->update( this_object() ) );

     if ( !GUEST_D->query_is_guest( name ) ) {  // let's skip this for guests, bytre 10-sep-2008
  if ( query_ip_name(this_object()) != called_from_ip && called_from_ip )
    write( "Your last login was from "+ called_from_ip +"\n" );
  if ( !remember_throw && remember_last_time )
    write( "Your last login was "+
      (string)"secure/xfun"->time_to_string( time() - remember_last_time ) +
      " ago.\n" );
  called_from_ip = query_ip_name(this_object());
  }  // end of guest skip of last login info

  squelchees = query_squelchees();  
  sqz = sizeof(squelchees);
  tmp_msg = "You have squelched: ";

  if(sqz > 0)
  {
   if(sqz == 1){ tmp_msg += capitalize(squelchees[0])+"."; }
   if(sqz == 2){ tmp_msg += capitalize(squelchees[0])+" and "
                            +capitalize(squelchees[1])+"."; }
   if(sqz > 2)
   {
    tmp_msg += capitalize(squelchees[0]);
    for(i=1; i<sqz-1; i++)
    {
     tmp_msg += ", "+capitalize(squelchees[i]);
    }

    tmp_msg += " and "+capitalize(squelchees[sqz-1])+".";
   }

   writef(tmp_msg);
  }

  if ( environment() ) {
    object ob;

    ob = first_inventory( environment() );
    while ( ob ) {
      if ( ob != this_object() ) {
        string sh;
        if ( sh = (string)ob->short() )
          write( capitalize( sh ) + ".\n" );
      }
      ob = next_inventory( ob );
    }
  }

  if ( level && (level > WIZLEVEL) ) {
    if ( !"secure/config"->query_config( name, "pathsave" ) ) {
      current_path = "/w/"+ name;
    }
  } else if ( level == WIZLEVEL &&
               "d/Education/room/register"->is_student() ) {
    current_path = "/w/"+name;
  }

  calculate_power();

  if ( (level < WIZLEVEL) && (query_defend() == 4) && (class != FIGHTER) ) {
    write( "Only fighters can fight like berserkers.\n" );
    set_defend( "none" );
  }

  this_object()->alt_prompt( misc["prompt"] );

  if ( misc["alt_soul"] && query_flag( "D1" ) && find_object( SOULBASE ) )
    SOULBASE->register_donator( this_object() );

  set_check_log( 1 );
  if ( this_object()->query_lawlog() ) {
    "adm/usr/flint/lawlog"->write_law_log2( name, ctime() +": LOGIN\n" );
  }
  set_check_log( 0 );

#ifdef VERSION_ASK_NEWBIES
#ifdef VERSION_NEWBIE_TUT
  if( newbie_status == 1 || newbie_status == 2) //player is new to MUDS or AA
    this_object()->move_player("silent#w/dafeon/tweaks/tutorial/room/entrance");
  if ( err ) // Also remove later - will be pointless unless we change to 3 different err variables
    write( "WARNING: There was a problem creating the newbie equipment.\n"
           "Please report this error to a wizard.\n" );
  return;
#endif
  if ( newbie_status == 1 ) { // Player is new to MUDS
     err=catch( clone_object("/w/llandra/newbie/carving")->move(this_object()) );     
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/scarecrazy/ironman/obj/newbie_club")->move(this_object()) );
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/scarecrazy/ironman/obj/newbie_armour")->move(this_object()) );
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/grunthex/map/mapkit")->move(this_object()) );
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/dafeon/newbcard/card")->move(this_object()) );     
     set_wimpy( 1 );
  } else if ( newbie_status == 2 ) { // Player is new to AA
     err=catch ( clone_object("/w/llandra/newbie/carving")->move(this_object()) );     
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/scarecrazy/ironman/obj/newbie_club")->move(this_object()) );
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/scarecrazy/ironman/obj/newbie_armour")->move(this_object()) );
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/grunthex/map/mapkit")->move(this_object()) );
     if (!err) // Remove later - Don't want to deny the newbie everything.
     err=catch( clone_object("/w/dafeon/newbcard/card")->move(this_object()) );     
     set_wimpy( 1 );
  } else { // Player is an AA native, he knows where to get the goodies.
  }
  if ( err ) { // Also remove later - will be pointless unless we change to 3 different err variables
    write( "WARNING: There was a problem creating the newbie equipment.\n"
           "Please report this error to a wizard.\n" );
  }
#endif
}

void time_out() {
  tell_object( this_object(), "Time out.\n" );
  destruct( this_object() );
}

nomask void convert_stuff() {
  /*
   *  wiz_radio and add_msg might have to be updated 
   */
  if(!wiz_radio||!mappingp(wiz_radio))
        wiz_radio = ([ "wiz":1,"arch":0,"domain":0, ]);
  if(!add_msg)
             add_msg = ({ 0,1,0,0,0,1,0 });
  else if(stringp(add_msg[1])){
        add_msg += ({ 0, 0, 1, 0 }); 
        add_msg[2] = add_msg[1];
        add_msg[1] = 1;
  }
  else if(level < WIZLEVEL && add_msg[3]) add_msg[3] = 0;
//  if(!mquest_level) mquest_level = 1;
  if(!misc) misc = ([ ]);
/** for now, players will have to solve all quests..
   11.4.92 Zor
  if(my_quest_max == 0)
   take old value for level 18+ 19.9.92 Zor 
*/
/* Made it be 17+ with the addition of another quest (Midian) */
/* All will be updated again, but they only have to solve 80%
    if(level < 17)
*/
   if(level < WIZLEVEL)
     my_quest_max = "room/quest_room"->current_quest_points();

/* only temporarily */
   if(!flags) {     /* no converts done yet, let's do all */

      /* guild/class conversion */
      if(guild) {
         class = guild;
         guild = 0;
      }
      flags = set_bit("", 1);

      /* florin/halforc quest conversion */
      if(quests) quests = replace_string(quests, "halforc", "florin");
      flags = set_bit(flags, 2);
   }
   else {
   if(!test_bit(flags, 1)) {   /* quest/class not changed yet */
      if(guild) {
         class = guild;
         guild = 0;
      }
      if(!flags) flags = "";
      flags = set_bit(flags, 1);
   }
   if(!test_bit(flags, 2)) {  /* florin/halforc quest not converted yet */
      if(quests) quests = replace_string(quests, "halforc", "florin");
      flags = set_bit(flags, 2);
   }
   }
   if(!test_bit(flags, 16)) {   /* not member of highest donation class */
      string dtmp;

      dtmp = (string)DONATOR_D->query_class(name);
      if(!dtmp) return;   /* NO OTHER CONVERSION BELOW!! */
      if(dtmp == "D2") {
         flags =  set_bit(flags, 16);   /* D2 */  
         flags = set_bit(flags, 17);   /* D1  too */
      }
      else if(dtmp == "D1") flags = set_bit(flags, 17);   /* D1 only */
   }

}

// ------------------------------------------------------- QUITTING AND SAVING

int quit() {
  object *inv, ob;
  int cnt;

  if ( is_statue ) {
    remove_call_out( "extra_quit" );
#ifdef VERSION_LINKDEAD_CHILL
    if(CHILL_D->query_chilled(query_real_name())) {
      CHILL_D->thaw_player(this_object(), 1); // sending the 'quitting' flag
    }
#endif
  }

// the following loop moved above the call to save_me by Adinos, 000319
  filter_objects( deep_inventory(), "owner_quit" );

  if ( !GUEST_D->query_is_guest( name ) ) {
      log_quit( throw_flag );
    // removed wizard check to log_quit  12-jun-06 bytre
    last_login = time();

    if ( donate_force_quit && !time_to_wake && interactive( this_object() ) )
      time_to_wake = time() + 100;
    save_me( 0 );
    write( "Saving "+ capitalize( name ) +".\n" );
  }

  if ( level < WIZLEVEL ) {
    cnt = sizeof( inv = all_inventory() );
    while ( cnt-- ) {
      ob = inv[cnt];
      // Dropping one item can cause others to self-dest.
      if ( ob && (string)ob->short() && !(int)ob->drop() && ob &&
           !(string)ob->query_auto_load() ) {
        ob->move( environment() );
      }
    }
  }
  cnt = sizeof( inv = deep_inventory() );
  while ( cnt-- ) {
    if( inv[cnt] ) // Destructing one item can cause others to self-dest.
      destruct( inv[cnt] );
  }

  if ( !is_invis )
    say( cap_name +" left the game.\n" );

  if ( !throw_flag ) {
    NOTIFIER_D->logout( ({ name, level, DEFAULT_IP_NAME }) );
  }

  if ( MAXMORTAL < level ) {
    catch( WIZLINE_D->remove_entry( this_object() ) );
  }

  log_file( "QUIT", name +"("+ level +","+ query_exp_number_string() +") "+
    ctime()[4..18] +".\n", 200000 );
#ifdef SCORE_SESSION
  if (query_player_data("session"))
  score_session(1);
#endif

  "secure/tool"->lw_delay();
  fortune::quit();

  destruct( this_object() );
  return 1;
}

nomask void extra_quit() {
  if ( level > MAXMORTAL || query_test_char() ) {
    move_player( "away#secure/void" );
  } else {
    int mm;

    mm = money;
    keep_list = ({});

    if (this_object()->query_player_data_default("autoquit autosell")) {

      if ( file_name( environment() ) != "/room/newvill/shop" )
        move_player( "away#room/newvill/shop" );

      catch( command( "sell all" ) );
    }
    log_file( "EXQUIT", ctime() +": "+ capitalize( name ) +"("+ level +") ["+
     money +"-"+ mm +"="+ (money - mm) +"] {"+
      "}\n", 200000 );
  }

  quit();
}

void extra_quit2() {
  extra_quit();
}

int log_quit( status flag ) {
  string time1, time2, result;
  int diff,diff2;

  time1 = ctime(time());
  time2 = ctime(last_login);
  diff = experience - last_experience;
  diff2 = experience_billions - last_experience_billions;
  if (diff2) diff = diff2*BILLION + diff;
  catch(call_other("/w/bytre/stats/statcoll","player_logout",
    this_player(), time()-last_login, diff));
   if (level > MAXMORTAL) return 1;  // added 12-jun-06 bytre

  if ( time1[4..9] == time2[4..9] )
    result = time2[0..18] +" - "+ time1[11..18];
  else
    result = time2[0..18] +" - "+ time1[0..18];
  if ( !diff ) {
    log_file( "EXP_NO_CHANGE", result +": "+ capitalize( name ) +
      "("+ level + (flag ? ") (throw)\n" : ")\n") );
        return 1;
  }

  result = result +": "+ capitalize( name ) +"("+ level +") => "+ diff;
  if ( flag )
    result = result +" (throw)\n";
  else
    result = result +"\n";

  log_file( "EXP_CHANGE", result );
  if ( diff > 100000 ) {
    log_file( "EXP_BIG_CHANGE", result );
  }

  return 1;
}

nomask silent_quit() {
  if ( file_name( previous_object() ) == "/secure/tool" ) {
    throw_flag = 1;
    quit();
  }
}

/*
 * Recursively compute the values of the inventory.
 * Beware that object may selfdestruct when asked for query_value().
 */
nomask int compute_values( object ob ) {
  int v;
  while ( ob ) {
    int tmp;
    object next_ob;
    
    next_ob = next_inventory( ob );
    tmp = (int)ob->query_value();
    if ( (tmp > 1000) && (ob && !ob->no_max_value()) )
      tmp = 1000;
    v += tmp;
    if ( ob && first_inventory( ob ) )
      v += compute_values( first_inventory( ob ) );
    ob = next_ob;
  }
  return v;
}

void save_me( int value_items ) {
  if ( GUEST_D->query_is_guest( name ) ) {
  if (0)   // Let's not badger guests with this!  bytre 10-sep-2008
    write( "The state of guest cannot be saved!\n" );
  } else {
    if ( value_items )
      tot_value = compute_values( first_inventory( this_object() ) );
    else
      tot_value = 0;

    compute_auto_str();

    if ( (level < WIZLEVEL) && !misc["testchar"] ) {
      catch( LIST_D->update( this_object() ) );
    }

    save_player( name );
  }
}

nomask int save_character() {
  if ( GUEST_D->query_is_guest( name ) ) {
    write( "The state of guest cannot be saved!\n" );
  } else {
    save_me( 1 );
    write( "Ok.\n" );
  }
  return 1;
}

// --------------------------------------------------------------------- SITES

nomask int set_site( string str ) {
  if ( previous_object() == this_object() ) {
    string *site_result;

    if ( !misc )
      misc = ([]);
    site_result = ({});

    if ( str == "show" ) {
      write( "You are allowing the following sites:\n" );
      show_sites();
      return 1;
    } else if ( str == "add" ) {
      site_result = misc["site"];
    } else if ( str == "remove" ) {
      site_result = misc["site"];
      if ( !site_result ) {
        write("You haven't defined any sites yet.\n");
        return 1;
      }
      write( "Enter site to be removed (end with **): " );
      input_to( "remove_site", 0, site_result );
      return 1;
    }
    if ( !site_result )
      site_result = ({});
    write( "Enter site (end with **): " );
    input_to( "add_site", 0, site_result );
    return 1;
  }
}

static void add_site( string str, string *site_result ) {
  if ( str == "**" ) {
    if ( sizeof( site_result ) )
      misc["site"] = site_result;
    else
      misc = m_delete( misc, "site" );
    show_sites();
    return;
  } else if ( check_input_site( str ) &&
              member_array( str, site_result ) < 0 ) {
    site_result += ({ str });
  }

  write( "Enter site (end with **): " );
  input_to( "add_site", 0, site_result );
}

static void remove_site( string str, string *site_result ) {
  if ( str == "**") {
    if ( !misc["site"] ) { 
      misc = m_delete( misc, "site" );
      write( "No sites defined.\n" );
    } else {
      show_sites();
    }

    return;
  }

  if ( member_array( str, site_result ) < 0 ) {
    write( "No such site defined!\n" );
    return;
  }

  site_result -= ({ str });
  if ( !site_result || !sizeof( site_result ) ) 
    misc = m_delete( misc, "site" );
  else
    misc["site"] = site_result;   

  write( "Enter site to be removed (end with **): " );
  input_to( "remove_site", 0, site_result );
}

static int check_input_site( string str ) {
  string *parts;
  int i, dummy;

  parts = explode( str, "." );
  if ( (dummy = sizeof( parts )) != 4 ) {
    write( "Corrupt site number, please enter it again.\n" );
    return 0;
  }

  for ( i = 0 ; i < dummy ; i++ ) {
    if ( parts[i] == "*" || sscanf( parts[i], "%~d" ) == 1 )
      continue;
    write( "Corrupt site number, use number only!\n" );
    return 0;
  }

  return 1;
}

static int show_sites() {
  mixed *sites;

  sites = misc["site"];
  if ( sites ) {
    int i;
    for ( i = sizeof( sites ) ; i-- ; ) {
      write( sites[i] +"\n" );
    }
  }

  return 1;
}

int check_site( string str ) {
  string *sites, *sitep, *sitei;
  int i, p, no_match;

  if ( !misc )
    return 1;
  sites = misc["site"];
  if ( !sites || member_array( str, sites ) >= 0 )
    return 1;

  sitei = explode( str, "." );
  for ( i = 0 ; i < sizeof( sites ) ; i++ ) {
    sitep = explode( sites[i], "." );
    for ( p = 0 ; p < sizeof( sitep ) ; p++ ) {
      if ( sitep[p] == "*" || sitep[p] == sitei[p] )
        continue;
      no_match = 1;
      break;
    }
    if ( !no_match )
      return 1;
    no_match = 0;
  }

  return 0;
}

// -------------------------------------------------------------------- TITLES

private nomask void _log_set_title( string which, string t ) {
  if ( this_player() && interactive( this_player() ) ) {
    log_file( "SET_TITLE",
      "["+ ctime()[4..15] +"] "+
      capitalize( (string)this_player()->query_real_name() ) +
      " ["+ (string)this_player()->query_level() +"] changed "+
      cap_name +"'s ["+ level +"] "+ which +" to \""+ t +"\"\n" );
  }
}

nomask int set_title( string t ) {
  if ( !t ) {
    write( "Your title is "+ title +".\n" );
    return 1;
  }

  if ( strlen( t ) > 80 ) {
    write( "Too long title.\n" );
    return 1;
  }

  if ( this_player() != this_object() ) {
    if ( level > WIZLEVEL )
      tell_object( this_object(),
        capitalize( (string)this_player()->query_real_name() ) +
        " changed your title.\n" );
    _log_set_title( "title", t );
  }

  title = t;
  return 1;
}

int set_pretitle( string t ) {
  if ( stringp( t ) && strlen( t ) > MAX_PRETITLE_LENGTH ) {
    write( "Too long pretitle.\n" );
    return 1;
  }

  if ( this_player() != this_object() ) {
    if ( level > WIZLEVEL )
      tell_object( this_object(),
        capitalize( (string)this_player()->query_real_name() ) +
        " changed your pretitle.\n" );
    _log_set_title( "pretitle", t );
  }

  pre_title = t;
  return 1;
}

// ------------------------------------------------------------ LIFE AND DEATH

int kill( string str ) {
  object ob;
#if VERSION_KILL
  string msg, tmp;
#endif /* VERSION_KILL */

  if ( ghost )
    return 0;

  if ( !str ) {
    write( "Kill what?\n" );
    return 1;
  }

  ob = present( lower_case( str ), environment( this_player() ) );
  if ( !ob ) {
    write( "No "+ str +" here!\n");
    return 1;
  }

  if ( (!living(ob)) || ((!ob->query_npc()) && (!ob->query_is_player())) ) {
#if VERSION_KILL
    if ( !(tmp = (string)ob->query_short()) ) {
      if ( !(tmp = (string)ob->query_name()) ) {
        write( capitalize(str) +" is not a living thing!\n" );
        //Note: No say() is given
        return 1;
      }
      tmp = "the "+tmp;
    }
    str = tmp;
    if ( msg = (string)ob->query_nokill_string() )
      writef( msg );
    else
      write( capitalize(str)+" is not a living thing!\n" );
#else
    write( str +" is not a living thing!\n" );
#endif /* VERSION_KILL */
    say( cap_name +" foolishly tries to attack "+ str +".\n" );
    return 1;
  }

  if ( ob == this_object() ) {
    write( "What? Attack yourself?\n" );
    return 1;
  }

  if ( attacker_ob == ob) {
    write( "Yes, yes.\n" );
    return 1;
  }

#if VERSION_JUSTICE
  if ( JUSTICE_D->query_no_kill( this_object(), ob, "attacked" ) )
    return 1;
#else
  if ( environment()->query_no_kill( ob ) )
    return 1;
#endif

  // handle obs that destruct in their no_kill, eg Ilderian child
  if ( !ob )
    return 1;

  if (ob->query_hunted() == this_object()) {
    attacked_by(ob);
     if (attacker_ob != ob) {
      alt_attacker_ob = attacker_ob;
      attacker_ob = ob;
      write("You turn to attack "+str+".\n");
     }
    return 1;
  }

  if ( attacker_ob && present( attacker_ob, environment( this_player() ) ) ) {
    alt_attacker_ob = attacker_ob;
    attacker_ob = ob;
    write( "You turn to attack "+ str +".\n" );
    return 1;
  }

  if ( body_position )
    write( "You attack "+ str +" while "+ query_body_position_string() +
      ".\n" );

  attack_object( ob );
  return 1;
}

static void heart_beat() {
  int t; // time to reboot
  int logoutsec; // sec to logout

// moved from is_statue checks to ensure is_ld and saves happen - 1/2014 Newt
  if ( !interactive( this_object() ) ) {
    set_heart_beat( 0 );
    if ( is_statue > 1 )  // now uses 2 in order to NOT call_is_ld
      return;             // 2 is already linkdead
    if ( !is_statue ) {
      if ( !is_invis )
        say( cap_name + " turns into a stone statue.\n", this_object() );
    } else if ( !is_invis )
      say("The eyes of a stone statue of "+ cap_name +" seem to dim.\n", this_object() );
    is_statue = 2;  // now uses 2 in order to NOT call_is_ld next beat
    remove_call_out("extra_quit");
    call_out( "extra_quit", query_ld_idle_timeout() );
    catch( NOTIFIER_D->link_dead( this_object() ) );
    catch( WIZLINE_D->remove_entry( this_object() ) );
    stop_follow();
    if ( name_of_party )
      leave_party();
    call_is_ld();
    save_me( 1 );
    return;
  }
  if ( !is_statue ) {  
    if ( query_idle(this_object()) >= IDLE_SECONDS ) {
      if ( geteuid() == ADMIN_UID ) {
        seteuid( getuid() );
        write("You retain your normal status.\n");
      }
      write( "As time passes you turn into a stone statue.\n" );
      // add a save at the point of going idle, bytre 2012-07-23
      save_me( 0 );
//    change to time_to_string vs. minutes 2010-10-03 bytre
      logoutsec=query_idle_timeout();
      t=(int)"/room/newvill/inn"->query_time_to_reboot();
      if (t<logoutsec) 
        logoutsec=t;
      write( "You will be logged out in "+
       (string)"/secure/xfun"->time_to_string( logoutsec)+".\n");
//    change to time_to_string vs. minutes 2010-10-03 bytre

      if ( !is_invis )
        say( cap_name +" slowly turns into a stone statue.\n", this_object() );
      is_statue = 1;  // now uses 1 in order to call_is_ld properly
      stop_follow();
      if ( misc["testchar"] )
        call_out( "test_char_warning", 3300 );
      else if ( !misc["no_timeout"] )
        call_out( "extra_quit", logoutsec );
      return;
    }
  } else if ( query_idle( this_object() ) < IDLE_SECONDS ) {
    is_statue = 0;
    if ( misc["testchar"] )
      remove_call_out( "test_char_warning" );
    remove_call_out( "extra_quit" );
    write( "As you move, you turn living again.\n" );
    if ( !is_invis ) {
      say( cap_name +" turns living again.\n", this_object() );
      hook("destatue", ({}));
    }
    return;
  } else {
    return;
  }

  if ( ghost )
    return;

  age += 1;
  hook("heart_beat", ({ }) );
  if ( age > time_to_save ) {
    if ( !brief && level < WIZLEVEL && !GUEST_D->query_is_guest( name) )
      write( "Autosave.\n" );
    save_me( 1 );
    time_to_save = age + AUTOSAVE_TIME;
  }

  if ( poisoned && (age > time_to_act_poison) ) {
    if ( body_position ) {
      switch ( body_position ) {
        case KNEELING: {
          hit_point -= 1 + random( 2 );
          break;
        }
        case SITTING: {
          hit_point -= 1 + !random(3);
          break;
        }
        case LYING: {
          hit_point -= 1;
          break;
        }
      }
    } else {
      hit_point -= 2;
    }

    if ( hit_point < 0 ) {
      write( "You die from the poison.\n" );
      hit_player( 1000 ); /* Make sure the player dies */
      return;
    } else {
      write( "You feel the poison stealing your life away.\n" );
    }

    time_to_act_poison += (11 - poisoned);
  }

  intoxication_message();

  time_to_heal -= 1;  
  if ( time_to_heal < 0 ) { 
    if ((hit_point < query_max_hp()) || (spell_points < query_max_sp()) || 
         headache ) {

      intoxicated_headache();

      if ( (hit_point < query_max_hp()) || (spell_points < query_max_sp()) ) {
        int *tmp_regen;

        catch( tmp_regen = (int *)REGEN_D->
          query_regen( BASE_HP_REGEN, BASE_SP_REGEN ) );

        hit_point += tmp_regen[0];
        spell_points += tmp_regen[1];
      }

      if ( hit_point > query_max_hp() )
        hit_point = query_max_hp();

      if ( spell_points > query_max_sp() )
        spell_points = query_max_sp();

    }
  }
  
  intoxicated_heart_beat( time_to_heal );
  stuffed_heart_beat( time_to_heal );
  soaked_heart_beat( time_to_heal ); 
  
  // Reset time to heal
  if ( time_to_heal < 0 )
    time_to_heal = INTERVAL_BETWEEN_HEALING;

  if ( attacker_ob )
    attack();

  if ( whimpy && attacker_ob && (hit_point < (query_max_hp() * whimpy) / 10) ) {

#ifdef VERSION_INCREASED_MORTAL_LEVEL
  add_exp(-1*(int)((20000/3.1415926) * atan((float)(experience*3.1415926/20000000))));
#else
   this_object()->add_exp(-1*(int)(experience / 1000)));
#endif
    run_away();
  }

// command stack timing addition - Bytre  1/may/0
  if ( time() > (cmd_stack_timing + 6) ) {
    if ( LAW_MAX_MOVES_PER_SIX < cmd_stack_counter )
      "/adm/tools/slowdown/speedwalk"->slow( this_object(), cmd_stack_counter );
    cmd_stack_timing = time();
    cmd_stack_counter = 0;
  }
}

reset_heart() {
  set_heart_beat( 1 );
}

void call_is_ld() {
  object *inv;
  int i;

  i = sizeof( inv = all_inventory( this_object() ) );
  while ( i-- ) {
    if (inv[i])
      inv[i]->is_ld();
  }

  if ( environment() ) {
    environment()->gone_linkdead( this_object() );
  }
}

int second_life() {
  object death_mark;

  if ( level >= WIZLEVEL )
    return illegal_patch( "second_life" );

  NOTIFIER_D->death( ({ name, level,
    attacker_ob ? attacker_ob->short() : (poisoned ? "poison" : "?") }) );

#ifndef TESTMUD /* off for testmud altogether. Fir Mar 2013 */
  // death_fix Buxley Mar 2013: testchars off grave_d
  if ( attacker_ob && attacker_ob->query_name() && 
       !this_object()->query_test_char()) {
    catch( GRAVE_D->gen_deaths( ({ name, level }),
      ({ attacker_ob->query_name() }) ) );
  }
#endif
  catch( environment()->player_died_here( this_object(), attacker_ob ) );

  make_scar();

  ghost = 1;
  poisoned = 0;
  if ( level > 1 )
    level = level - 1;
  if ( Str > 1 )
    set_str( Str - 1 );
  if ( Con > 1 )
    set_con( Con - 1 );
  if ( Dex > 1 )
    set_dex( Dex - 1 );
  if ( Int > 1 )
    set_int( Int - 1 );
  if ( Wis > 1 )
    set_wis( Wis - 1 );
  setmin( "drifts around" );
  setmout( "blows" );
  headache = 0;
  intoxicated = 0;
  stuffed = 0;
  soaked  = 0;
  hunter = 0;
  hunted = 0;

#ifdef LOG_KILLS
  if ( attacker_ob ) {
    call_out( "notify_death_objects", 0, ({ name, level,
      attacker_ob->query_real_name(), attacker_ob->query_level() }) );
#ifdef VERSION_PARTY_UPDATE
    call_out( "notify_death_party", 0, attacker_ob->query_cap_name());
#endif

    if ( attacker_ob->query_npc() ) {
      log_file( "KILLS", ctime()[4..15] +": "+ name +"("+ level +") killed by "+
            (string)attacker_ob->short() +"("+ (int)attacker_ob->query_level() +
            "), uid: " + getuid( attacker_ob ) +"\n" );
      if ( attacker_ob->query_former_master() ) {
        log_file( "PLAYER_KILLS", name +" killed by "+
          (string)attacker_ob->query_former_master() +"'s pet in "+
          file_name( environment() ) +".\n" );
      }
      call_out( "notify_pk_death_objects", 0, ({ name, level,
        file_name( attacker_ob ), -10}) );
    } else {

// Oliver 08 Sept 2008 - Moved the #endif above the call to the SK brain
//                       previous version skipped it causing all SK
//                       activity to cease.
#if VERSION_JUSTICE
      JUSTICE_D->log_kill(attacker_ob,this_object());
#else
      log_file( "PLAYER_KILLS", name +"("+ level +") killed by "+
        (string)attacker_ob->query_real_name() +"("+
        (int)attacker_ob->query_level() +") at "+ ctime() +"\n" );
#endif
      call_out("notify_pk_death_objects", 0, ({ name, level,
        attacker_ob->query_real_name(), attacker_ob->query_level() }) );
    }
  }
  else // killed by a nameless cause?
    log_file( "NAMELESS_KILLS", name +" killed in "+
      file_name(environment())+ " after doing '"+
      query_verb()+"'.\n" );
#endif

  attacker_ob = 0;
  alt_attacker_ob = 0;

  tell_object( this_object(), "\nYou die.\nYou have a strange feeling.\n"
    "You can see your own dead body from above.\n\n" );

#if VERSION_PARTY
// Arp 12/07/02: Done in party_notify_death
#else
  if ( name_of_party )
    leave_party();
#endif
  
  death_mark = clone_object( DEATH_MARK );
  death_mark->move( this_object() );

  return 1;
}

void notify_pk_death_objects( mixed *killarray ) {
  catch( true_notify_pk_death_objects( killarray ) );
}

void true_notify_pk_death_objects( mixed *killarray ) {
  "w/hatamoto/pk/obj/brain"->death( killarray[0..1], killarray[2..3] );
}

void notify_death_objects( mixed *killarray ) {
  catch( true_notify_death_objects( killarray ) );
}

void true_notify_death_objects( mixed *killarray ) {
  if ( guild ) {
    guild->notify_death( killarray[0..1], killarray[2..3] );
  }

#if VERSION_PARTY
  if ( name_of_party ) {
    party_notify_death( killarray[0..1], killarray[2..3] );
  }
#endif
}

#ifdef VERSION_PARTY_UPDATE
void notify_death_party( string opponent ) {
  catch( true_notify_death_party( opponent ) );
}

void true_notify_death_party( string opponent ) {
  string header;
  if (name_of_party) {
    header = "<"+name_of_party+"> *** ";
    tell_all_party(get_f_string(header+cap_name+" ["+level+
      "] has been killed by "+opponent+"! ***", 0, strlen(header)));
  }
}
#endif

int remove_ghost() {
  if ( ghost ) {
    write( "You feel a very strong force.\n"
           "You are sucked away...\n"
           "You reappear in a more solid form.\n" );

    say( "Some mist disappears.\n" );
    say( cap_name +" appears in a solid form.\n" );

    ghost = 0;
    dead = 0;
    setmin( "arrives" );
    setmout( "leaves" );
    save_me( 1 );

    return 1;
  }
}

int query_dead() {
  return dead;
}

void reset_dead() {
  dead = ghost;
}

#define MAX_SCAR 10
int scar;

static void make_scar() {
  if ( level >= 10 ) {
    scar |= 1 << random( MAX_SCAR );
  }
}

void show_scar() {
  int i, j, first, old_value;
  string *scar_desc;
  string poss;
  
  scar_desc = ({ "left leg", "right leg", "nose", "left arm", "right arm",
                 "left hand", "right hand", "forehead", "left cheek",
                 "right cheek" });
  j = 1;
  first = 1;
  old_value = scar;
  poss = this_object()->query_possessive();
  while ( i < MAX_SCAR ) {
    if ( scar & j ) {
      old_value &= ~j;
      if ( first ) {
        write( cap_name +" has a scar on "+ poss +" "+ scar_desc[i] );
        first = 0;
      } else if ( old_value ) {
        write( ", "+ poss +" "+ scar_desc[i] );
      } else {
        write( " and "+ poss +" "+ scar_desc[i] );
      }
    }
    j *= 2;
    i += 1;
  }
  if ( !first )
    write( ".\n" );
}

int query_scar() {
  return scar;
}

void heal_scar( int i ) {
  if ( (i >= 0) && (i <= MAX_SCAR) ) {
    if ( this_player() != this_object() && (level < WIZLEVEL) ) {
      log_file( "SUSPICIOUS", "["+ ctime() +"] "+
        capitalize( (string)this_player()->query_real_name() ) +
        " healed "+ name +"'s scar "+ i +".\n" );
    }

    scar &= ~(1 << i);
  }
}

// ------------------------------------------------------------------ ARCHKILL

nomask void set_wakeup( int arg ) {
  if ( interactive( this_player() ) && intp( arg ) ) {
    if ( (this_player() == this_interactive()) &&
         (this_player() == this_object() ||
          (int)this_interactive()->query_level() > SENIORLEVEL) ) {
      time_to_wake = arg;
    }
  }
}

int query_time_to_wake() {
  return time_to_wake;
}

// ----------------------------------------------------------------- ALIGNMENT

void update_alignment_title(){

  int al;

  if ( ghost || level >= WIZLEVEL)
    return;

  al=(int)query_alignment();
    
  if ( al > (KILL_NEUTRAL_ALIGNMENT * 100) )
    al_title = "saintly";
  else if ( al > (KILL_NEUTRAL_ALIGNMENT * 20) )
    al_title = "good";
  else if ( al > (KILL_NEUTRAL_ALIGNMENT * 4) )
    al_title = "nice";
  else if ( al > -(KILL_NEUTRAL_ALIGNMENT * 4) )
    al_title = "neutral";
  else if ( al > -(KILL_NEUTRAL_ALIGNMENT * 20) )
    al_title = "nasty";
  else if ( al > -(KILL_NEUTRAL_ALIGNMENT * 100) )
    al_title = "evil";
  else
    al_title = "demonic";
}

void add_alignment( int a, int direct ) {
  if ( !intp( a ) ) {
    write( "Bad type argument to add_alignment.\n" );
    return;
  }

  if ( ghost )
    return;  // bytre  oct 8 95

  if ( direct )  // Greyson, 13-Apr-96
    alignment += a;
  else
    alignment = alignment * 9 / 10 + a;

  update_alignment_title();
}

void set_al( int a ) {
  if ( intp( a ) ) {
    alignment = a;
    update_alignment_title();
  }
}

nomask void set_alignment( string al ) {
  if ( this_player() != this_object() ) {
    _log_set_title( "al_title", al );
  }

  al_title = al;
}

// ------------------------------------------------------ LEVEL AND EXPERIENCE

nomask add_exp( e ) {
  if ( ghost ) return;  // bytre  oct 8 95

#ifdef LOG_EXP
  if ( this_player() && (this_player() != this_object()) &&
       interactive( this_player() ) ) {
    log_file( "EXPERIENCE", ctime() +" "+ name +"("+ level +") "+ e +
      " exp by "+ (string)this_player()->query_real_name() +"("+
      this_player()->query_level() +")\n" );

    if ( (this_player()->query_level() >= WIZLEVEL) && (level < WIZLEVEL) &&
         (!(query_test_char())) ) {
      log_file( "SUSPICIOUS", "["+ ctime() +"] "+ capitalize( name ) +
        " ("+ level +") "+ e +" experience by "+
        capitalize( (string)this_player()->query_real_name() ) +" ("+
        this_player()->query_level() +")\n" );
    }
  }
#endif

  // If were going to overflow, lets not, and cap it to the 31st bit.
  if (experience > 0x70000000 && (experience+e) < 0)
    e = 0x7fffffff-experience;

  // If more than 2 billion, remove 1 billion and add one to the billion
  // counter
  if (experience+e >= 2*BILLION) {
    experience -= BILLION;
    experience_billions++;
  }

  // If we drop below 1 billion xp and we have billions, add one of the
  // billions back on and remove one of the counters.
  if (experience_billions && experience+e <= BILLION) {
     experience += BILLION;
     experience_billions--;
  }

  /* Clan dedication added by Zor, January 12 1996 */
  if ( clan_dedication )
    catch( CLAN_EXP_D->add_xp( clan, name, e ) );
  else
    experience += e;

  if ( level <= MAXMORTAL )
    add_worth( e );

// XP hook only available during event to prevent abuse.
#ifdef XP_EVENT
#if XP_EVENT
  experience += hook("exp_mod", ({ e }));
#endif
#endif
}

nomask int set_level( int lev ) {
  /* Do not allow WIZARDs to patch APPRENTICEs to WIZARD! Midian */
  if ( lev > WIZLEVEL && file_name( previous_object() )[0..7] != "/obj/leo" )
    return illegal_patch( "set_level" );
  if ( (lev > WIZARDLEVEL) || lev < level && level >= WIZLEVEL )
    return illegal_patch( "set_level" );

  if ( this_interactive() ) {
    log_file( "SET_LEVEL", this_interactive()->query_real_name() +" setted "+
      query_real_name() +" level to "+ lev +"\n" );
  }

  if ( (this_player() != this_object()) && !query_test_char() ) {
    log_file( "SUSPICIOUS", "["+ ctime() +"] "+ capitalize( name ) +
      " ("+ level +") set to level "+ lev +" by "+
      capitalize( (string)this_player()->query_real_name() ) +" ("+
      this_player()->query_level() +")\n" );
  }

  level = lev;

  if ( level == WIZLEVEL ) {
    catch( clone_object( APPRENTICE_SCROLL )->move( this_object() ) );
    tell_object( this_object(), "You have been given a scroll containing "
      "valuable information. Read it now!\n"
      "Adding wizard commands...\n"
      "You may have to log off and back on for all the changes to take "
      "effect.\n" );

    "secure/tool"->adm_log( "APPRENTICE", "["+ ctime() +"] "+
      capitalize( name ) );

   /* wizards are not allowed in clans, Zor January 12 1996 */
   if ( clan )
     catch( CLAN_D->remove_member( clan, name, 3 ) );

    wiz_commands();
    wiz_radio::add_init();
  }

  if ( level == WIZARDLEVEL ) {
    tell_object( this_object(), "Adding more wizard commands...\n"
      "You may have to log off and back on for all the changes to take "
      "effect.\n" );

    wiz_commands2();
  }

  return 1;
}

list_peoples( str ) {
  "secure/tool"->list_peoples( str );
  return 1;
}

mixed *query_people() {
  return ({ c_name ? c_name : name, level, age, is_invis, ghost,
    misc["testchar"] });
}

nomask int illegal_patch( string what ) {
  write( "You are struck by a mental bolt from the interior of the game.\n" );
  log_file( "ILLEGAL", ctime() +": "+
    (string)this_player()->query_real_name() +" "+ what +"\n" );
  return 0;
}

void smart_report( string file, string str ) {
  string euid, pa, current_room;

  euid = geteuid( environment() );
  current_room = file_name( environment() ); 
  if ( euid != "map" ) {
    pa = "secure/tool"->get_log_dir( euid );
    if ( pa ) {
      if ( "secure/config"->query_config( euid, "one_rep" ) ) {
        "secure/tool"->log_smart_report( pa +".rep",
          file +" "+ current_room +" "+ str +"\n\n" );
      } else {
        "secure/tool"->log_smart_report( pa + file, current_room +" "+
          str +"\n\n" );
      }
    } else {
      log_file( euid +".rep", file +" "+ current_room +" "+ str +"\n" );
    }
  } else {
    log_file( "map.rep", file +" "+ current_room +" "+ str +"\n" );
  }
}

// to reset some parameters in OTHER players!
int do_transmute() {
  if ( snoop_allow )
    snoop_on( "forbid" );

  if ( snoop_allowed )
    snoop_allowed->remove_snoop_allow();

  if ( name_of_party )
    leave_party();

  return 0;
}

// ------------------------------------------------------------------- QUERIES

nomask int query_hit_point() {
  return hit_point;
}

string query_title() {
  return title;
}

string query_pretitle() {
  return pre_title;
}

string query_al_title() {
  return al_title;
}

nomask status query_is_statue() {
  // pl_selfharm: Return up-to-date statuedness data for combat purposes. 
  // heart_beat updates the variable and callouts in a second. Fir Nov'15
  // Protect query_idle against exposure to non-interactive statues.
  if( is_statue == 1 && interactive( this_object() ) && 
      query_idle( this_object() ) < IDLE_SECONDS ) 
    return 0; // Not a statue.
  return !!is_statue; // is_statue >1 is internal only Newt 1/25/2014
}

nomask string query_real_name() {
  return name;
}

nomask string query_capi_name() {
  return c_name;
}

int query_last_login() {
  return last_login;
}

nomask string query_password() {
  string s;
  int i;
  if (previous_object() == 0) return crypt(pasword, 0);
  if (query_verb() == "suicide") return password;  // [Buxley 14 April 2023]
  s = file_name(previous_object());
  if (s == "/room/wiz/meat_locker") return password;
  if (s == "/w/greyson/room/basement") return password;
  log_file( "ILLEGAL_QUERY_PASSWORD", ctime() +" "+s[0..10]+" called query_password in "+this_object()->query_real_name()+"\n" );
  if (s[0..10] == "/obj/player")
  {
s = previous_object()->query_real_name();
i = 0;
if (interactive(previous_object())) i = previous_object()->query_level();
    log_file( "ILLEGAL_QUERY_PASSWORD", ctime() +" "+s+" ["+i+"] called query_password in "+this_object()->query_real_name()+"\n" );
    if (i > 899) return password;
  }
  return crypt(pasword, 0);
}

void test_char_warning() {
  object ob;
  tell_object( this_object(),
    "You are far too idle... 5 minutes till autoquit!\n" );
  if ( ob = find_player( misc["testchar"] ) )
    tell_object( ob, "Don't forget your test character!\n" );
  call_out( "extra_quit", 300 );
}

// ---------------------------------------------------------------- BANK MONEY

nomask int add_bank_money( int m ) {
  if ( this_player()!=this_object() ) {
    log_file( "ILLEGAL_BANK", ctime() +" "+
      (string)this_player()->query_real_name() +"("+
      file_name( this_player() ) +") add "+ m +" in "+ cap_name +"\n" ); 
    return 0;
  }

  if ( m < 0 )
    return 0;

  bank_money += m;

  return bank_money;
}

nomask int sub_bank_money( int m ) {
  if ( this_player() != this_object() && previous_object() != this_object() ) {
    log_file( "ILLEGAL_BANK", ctime() +" "+
      (string)this_player()->query_real_name() +"("+
      file_name( this_player() ) +") sub "+ m +" in "+ cap_name +"\n" );
    return 1;
  }

  if ( m < 0 )
    return 0;

  bank_money -= m;
  if ( bank_money < 0 )
    bank_money = 0;

  return bank_money;
}

nomask int query_bank_money() {
  return bank_money;
}


nomask int query_level_sec() {
  return level;
}

int equivalent_level() {
 int i;
 int eq_lev;
 int *lev_exp;

 if (experience_billions) { // means we have at least 2 billion xp
    if (level < 49) return 49;
    return level; // This function is really only used for low level checks
                  // But if we have more than 50 levels, worry then
 }
 lev_exp = "/room/class_room"->query_exp_level();
 if (!lev_exp) { // Happens when classroom inherited badly
   "/room/class_room"->init_stuff();
   lev_exp = "/room/class_room"->query_exp_level();
 }
 eq_lev = 0;
 for(i=0;i<sizeof(lev_exp);i++) {
  if (experience < lev_exp[i] && !eq_lev)
   eq_lev = i;
 }
 if (!eq_lev) return level;
 return eq_lev;
}

string query_called_from_ip() {
  if (!this_interactive() ||
      this_interactive()->query_level() < SENATELEVEL)
    return DEFAULT_IP_NUMBER;
  return called_from_ip;
}

int capi_name( string input ) {
  if ( !input ) {
    notify_fail( "How do you want your name to be capitalized?\n" );
    return 0;
  }
  if ( this_player() != this_interactive() || this_player() != this_object() )
    return 0;
  if ( !this_player()->query_flag( "D2" ) ) {
    notify_fail( "You are not allowed to do this.\n" );
    return 0;
  }
  if ( lower_case( input ) != name ) {
    write( "That is not your name.\n" );
    return 1;
  }

  c_name = input;
  cap_name = capitalize( c_name );
  write( "You are now spelled \""+ cap_name +"\".\n" );

  if ( is_invis )
    cap_name = "Someone";

  return 1;
}

void set_descriptor( string new_name ) {
  if ( -1 != member( DESCRIPTOR_ALLOWED_EUIDS,
               geteuid( previous_object() ) ) ) {
    if ( !new_name ) {
      cap_name = c_name = capitalize( name );
      if ( !this_object()->query_flag("D2") )
        c_name = 0;
      descriptor = 0;
    } else {
      cap_name = c_name = new_name;
      descriptor = previous_object();
    }
  }
}

void update_body_string( string x ) {
  body = x;
}

int query_intermud() { 
#ifdef TESTMUD
   return 1;  // let testers use intermud
#endif
  if ( level >= WIZARDLEVEL || (flags && test_bit( flags, 16 )) )
    return 1;
}

void first_log_check() {
  write( "\n\n*** You will be forced to quit in 3 minutes! ***\n\n" );
  call_out( "extra_quit2", 3 * 60 );
}

query_first_log_check() {
  return donate_force_quit;
}

void set_pk() {
  pk_member = present( "pk_brand", this_object() ) ? 1 : 0;
}

status query_pk() {
  return pk_member;
}

int query_remember_last_time() {
  return remember_last_time;
}

int query_first_login() {
  return first_login;
}

int query_player_list() {
  int pts, qrat;

  // the first quest gives a boost
  if ( !quests )
    return experience;

  pts = experience + Dex*Con*Str*Int*Wis + age/15 + (money + bank_money) / 5;
  qrat = (100 * "room/quest_room"->count_solved(this_object())) / my_quest_max;
  // points + points * percentage achieved quest points of necessary qpoints

// Changed by Arpeggio, 16Sep95 to avoid overflowing on large pts values
// This may change some players' ratings due to integer math, but fuck'em
//   return pts + (pts * qrat)/150;
   return pts + ( ( pts / 150 ) * qrat );
}

// next section removed by Maker for prompt project, 08 Aug 2013
#ifndef VERSION_NEW_PROMPT
void quick_info() {
  tell_object( this_object(), query_hp_ansi() +":"+ query_sp_ansi() +"> " );
}
void path_prompt()
{
  tell_object(this_object(), (string)this_object()->query_path() + ": "); 
}

void alt_prompt( int type ) {
  switch(type) {
    case 0:
      set_prompt( "> ", this_object() );
      break;
    case 1:
      set_prompt( #'quick_info, this_object() );
      break;
    case 2:
      set_prompt( #'path_prompt, this_object() );
      break;
  }
}
#endif

mixed *query_clan_info() {
   return ({ name, clan, level, age, (Dex+Str+Int+Wis+Con), explore_p });
}

int query_can_be_followed() {
  return 1;
}

// --------------------------------------------------------------- MINI-QUESTS

#if 0
// int mquest_level;   /* to what level are we allowed to advance without mquest*/

set_mquest_level( level ) {
  if(!previous_object()) return 0;
  if(file_name(previous_object())!="/room/mquest_room"){
    log_file("MQUEST_ILLEGAL",ctime(time())+" set_mquest_level by "+
      file_name(previous_object())+" ("+this_player()->query_real_name()+")\n"); 
    return 0;
  }
  if(mquest_level>level) return 0;
  mquest_level = level;
  return 1;
}

int query_mquest_level() { return mquest_level; }
#endif

void test_login() {
  if ( this_interactive()->query_real_name() == "greyson" ) {
    object login;

    login = clone_object( "/secure/login" );
    exec( login, this_object() );
    login->logon();
    quit();
  }
}

// ------------------------------------------------------------------------ EOF