diff -wuprN --exclude=*.o ./act.wizard.c ../../last/src/act.wizard.c
--- ./act.wizard.c	Thu Mar  4 22:47:46 1999
+++ ../../last/src/act.wizard.c	Tue Aug  3 05:00:05 1999
@@ -1562,23 +1562,58 @@ ACMD(do_date)
   send_to_char(buf, ch);
 }
 
+struct char_data *is_in_game(int idnum) {
+  extern struct descriptor_data *descriptor_list;
+  struct descriptor_data *i, *next_i;
 
+  for (i = descriptor_list; i; i = next_i) {
+    next_i = i->next;
+    if (GET_IDNUM(i->character) == idnum) {
+      return i->character;
+    }
+  }
+  return NULL;
+}
+
+/* altered from stock to the following:
+   last [name] [#]
+   last without arguments displays the last 10 entries.
+   last with a name only displays the 'stock' last entry.
+   last with a number displays that many entries (combines with name)
+*/
 
 ACMD(do_last)
 {
   struct char_file_u chdata;
+  int num_to_list=-1;
+  int recs,name=0;
+  char offend[30];
+  char time[128];
+  FILE *fp;
+  time_t delta;
+  struct last_entry mlast;
+  struct char_data *temp;
+
 
-  one_argument(argument, arg);
-  if (!*arg) {
-    send_to_char("For whom do you wish to search?\r\n", ch);
+  if(*argument) {
+    skip_spaces(&argument);
+    while(*argument) {
+      argument=one_argument(argument,buf);
+      if (isdigit(*buf)) {
+        num_to_list=atoi(buf);
+        if(num_to_list <=0 ) {
+          send_to_char("You must specify a number greater than 0\r\n",ch);
     return;
   }
-  if (load_char(arg, &chdata) < 0) {
-    send_to_char("There is no such player.\r\n", ch);
-    return;
+      } else {
+        strncpy(offend,buf,29);
+        name=1;
+      }
+    }
   }
-  if ((chdata.level > GET_LEVEL(ch)) && (GET_LEVEL(ch) < LVL_IMPL)) {
-    send_to_char("You are not sufficiently godly for that!\r\n", ch);
+  if(name && num_to_list == -1) {
+    if  (load_char(offend, &chdata) < 0) {
+      send_to_char("There is no such player.\r\n", ch);
     return;
   }
   sprintf(buf, "[%5ld] [%2d %s] %-12s : %-18s : %-20s\r\n",
@@ -1586,6 +1621,49 @@ ACMD(do_last)
 	  class_abbrevs[(int) chdata.chclass], chdata.name, chdata.host,
 	  ctime(&chdata.last_logon));
   send_to_char(buf, ch);
+    return;
+  } 
+
+  if(num_to_list <= 0 || num_to_list >= 100) {
+    num_to_list=10;
+  }
+    if(!(fp=fopen(LAST_FILE,"r"))) {
+    send_to_char("No entries found.\r\n",ch);
+    return;
+  }
+  fseek(fp,0L,SEEK_END);
+  recs=ftell(fp)/sizeof(struct last_entry);
+
+  sprintf(buf,"Last log\r\n");
+  while(num_to_list > 0 && recs > 0) {
+    fseek(fp,-1* (sizeof(struct last_entry)),SEEK_CUR);
+    fread(&mlast,sizeof(struct last_entry),1,fp);
+    fseek(fp,-1*(sizeof(struct last_entry)),SEEK_CUR);
+    if(!name ||(name && !strcasecmp(offend,mlast.username))) {
+      sprintf(buf,"%s%10.10s %20.20s ",buf,mlast.username,mlast.hostname);
+      sprintf(time,"%s",ctime(&mlast.time));
+      time[(strlen(time)-1)]='\0';
+      sprintf(buf,"%s%16.16s - ",buf,time);
+      if(mlast.close_type == LAST_CRASH) {
+        temp=is_in_game(mlast.idnum);
+        if(temp && mlast.punique == GET_PREF(temp)) {
+          sprintf(buf,"%sStill Playing  ",buf);
+        } else {
+          sprintf(buf,"%sCrash          ",buf);
+        }
+      } else {
+        sprintf(time,"%5.5s",ctime(&mlast.close_time)+11);
+        sprintf(buf,"%s%s  ",buf,time);
+        delta=mlast.close_time - mlast.time;
+        sprintf(buf,"%s(%5.5s) ",buf,asctime(gmtime(&delta))+11);
+      }
+      sprintf(buf,"%s\r\n",buf);
+      num_to_list--;
+    }
+    recs--;
+  }
+  fclose(fp);
+  page_string(ch->desc,buf,1);
 }
 
 
diff -wuprN --exclude=*.o ./comm.c ../../last/src/comm.c
--- ./comm.c	Thu Mar  4 21:42:32 1999
+++ ../../last/src/comm.c	Tue Aug  3 04:41:24 1999
@@ -1793,6 +1793,8 @@ void close_socket(struct descriptor_data
     /*
      * Plug memory leak, from Eric Green.
      */
+    add_llog_entry(d->character,LAST_DISCONNECT);
+
     if (PLR_FLAGGED(d->character, PLR_MAILING) && d->str) {
       if (*(d->str))
         free(*(d->str));
diff -wuprN --exclude=*.o ./db.c ../../last/src/db.c
--- ./db.c	Thu Mar  4 21:44:29 1999
+++ ../../last/src/db.c	Tue Aug  3 03:56:26 1999
@@ -2706,3 +2706,32 @@ int check_object_level(struct obj_data *
 
   return error;
 }
+
+int get_new_pref() {
+  FILE *fp;
+  char filename[1024];
+  int i;
+
+  sprintf(filename,PREF_FILE);
+  if(!(fp=fopen(filename,"r"))) {
+    touch(filename);
+    if(!(fp=fopen(filename,"w"))) {
+      return (number(1,128000));
+    }
+    fprintf(fp,"1\r\n");
+    fclose(fp);
+    return 1;
+  }
+  fscanf(fp,"%d",&i);
+  fclose(fp);
+  unlink(filename);
+  if(!(fp=fopen(filename,"w"))) {
+    log("Unable to open p-ref file for writing");
+    return i;
+  }
+  fprintf(fp,"%d\n",i+1);
+  fclose(fp);
+  return i;
+
+}
+
diff -wuprN --exclude=*.o ./db.h ../../last/src/db.h
--- ./db.h	Wed Dec  9 23:52:07 1998
+++ ../../last/src/db.h	Tue Aug  3 04:21:06 1999
@@ -92,7 +92,8 @@
 #define MAIL_FILE	LIB_ETC"plrmail" /* for the mudmail system	*/
 #define BAN_FILE	LIB_ETC"badsites" /* for the siteban system	*/
 #define HCONTROL_FILE	LIB_ETC"hcontrol"  /* for the house system	*/
-
+#define PREF_FILE	LIB_ETC"pref"	  /* next pref number		*/
+#define LAST_FILE	LIB_ETC"last"
 /* public procedures in db.c */
 void	boot_db(void);
 int	create_entry(char *name);
@@ -236,3 +237,6 @@ extern char	*OK;
 extern char	*NOPERSON;
 extern char	*NOEFFECT;
 #endif
+
+int get_new_pref();
+
diff -wuprN --exclude=*.o ./handler.c ../../last/src/handler.c
--- ./handler.c	Thu Mar  4 22:47:47 1999
+++ ../../last/src/handler.c	Tue Aug  3 05:06:29 1999
@@ -1238,7 +1238,6 @@ int generic_find(char *arg, bitvector_t 
   return (0);
 }
 
-
 /* a function to scan for "all" or "all.x" */
 int find_all_dots(char *arg)
 {
@@ -1250,3 +1249,180 @@ int find_all_dots(char *arg)
   } else
     return (FIND_INDIV);
 }
+
+const char *last_array[11] = {
+  "Connect",
+  "Enter Game",
+  "Reconnect",
+  "Takeover",
+  "Quit",
+  "Idleout",
+  "Disconnect",
+  "Shutdown",
+  "Reboot",
+  "Crash",
+  "Playing"
+};
+
+struct last_entry *find_llog_entry(int punique, int idnum,int close) {
+  FILE *fp;
+  struct last_entry mlast;
+  struct last_entry *llast;
+  int size,recs,tmp;
+
+  if(!(fp=fopen(LAST_FILE,"r"))) {
+    log("error opening last_file for reading");
+    return NULL;
+  }
+  fseek(fp,0L,SEEK_END);
+  size=ftell(fp);
+
+  /* recs = number of records in the last file */
+
+  recs = size/sizeof(struct last_entry);
+  /* we'll search last to first, since it's faster than any thing else
+        we can do (like searching for the last shutdown/etc..) */
+  for(tmp=recs-1; tmp > 0; tmp--) {
+    fseek(fp,-1*(sizeof(struct last_entry)),SEEK_CUR);
+    fread(&mlast,sizeof(struct last_entry),1,fp);
+        /*another one to keep that stepback */
+    fseek(fp,-1*(sizeof(struct last_entry)),SEEK_CUR);
+
+    if(mlast.punique == punique &&
+        mlast.idnum == idnum) {
+        /* then we've found a match */
+      CREATE(llast,struct last_entry,1);
+      memcpy(llast,&mlast,sizeof(struct last_entry));
+      fclose(fp);
+      return llast;
+    }
+        /* check for crash/reboot/etc code */
+    if(mlast.punique < 0 && close != 0) {
+      fclose(fp);
+      return NULL;
+    }
+        /*not the one we seek. next */
+  }
+        /*not found, no problem, quit */
+  fclose(fp);
+  return NULL;
+}
+
+  /* mod_llog_entry assumes that llast is accurate */
+void mod_llog_entry(struct last_entry *llast,int type) {
+  FILE *fp;
+  struct last_entry mlast;
+  int size,recs,tmp;
+
+  if(!(fp=fopen(LAST_FILE,"r+"))) {
+    log("error opening last_file for reading and writing");
+    return;
+  }
+  fseek(fp,0L,SEEK_END);
+  size=ftell(fp);
+
+  /* recs = number of records in the last file */
+
+  recs = size/sizeof(struct last_entry);
+
+  /* we'll search last to first, since it's faster than any thing else
+        we can do (like searching for the last shutdown/etc..) */
+  for(tmp=recs; tmp > 0; tmp--) {
+    fseek(fp,-1*(sizeof(struct last_entry)),SEEK_CUR);
+    fread(&mlast,sizeof(struct last_entry),1,fp);
+        /*another one to keep that stepback */
+    fseek(fp,-1*(sizeof(struct last_entry)),SEEK_CUR);
+
+    if(mlast.punique == llast->punique &&
+        mlast.idnum == llast->idnum) {
+        /* then we've found a match */
+        /* lets assume quit is inviolate, mainly because
+                disconnect is called after each of these */
+      if(mlast.close_type != LAST_QUIT &&
+          mlast.close_type != LAST_IDLEOUT &&
+          mlast.close_type != LAST_REBOOT &&
+          mlast.close_type != LAST_SHUTDOWN) {
+        mlast.close_type=type;
+      }
+      mlast.close_time=time(0);
+
+        /*write it, and we're done!*/
+      fwrite(&mlast,sizeof(struct last_entry),1,fp);
+      fclose(fp);
+      return;
+    }
+        /*not the one we seek. next */
+  }
+  fclose(fp);
+
+        /*not found, no problem, quit */
+  return;
+}
+
+void add_llog_entry(struct char_data *ch, int type) {
+  FILE *fp;
+  struct last_entry *llast;
+
+  /* so if a char enteres a name, but bad password, otherwise
+        loses link before he gets a pref assinged, we
+        won't record it */
+  if(GET_PREF(ch) <= 0) {
+    return;
+  }
+
+  /* we use the close at 0 because if we're modifying a current one,
+     we'll take the most recent one, but, if the mud has rebooted/etc
+     then they won't be _after_ that (after coming from the direction
+     that we're counting, which is last to first) */
+  llast = find_llog_entry(GET_PREF(ch), GET_IDNUM(ch),0);
+
+  if(llast == NULL) {  /* no entry found, add ..error if close! */
+    CREATE(llast,struct last_entry,1);
+    strncpy(llast->username,GET_NAME(ch),16);
+    strncpy(llast->hostname,GET_HOST(ch),128);
+    llast->idnum=GET_IDNUM(ch);
+    llast->punique=GET_PREF(ch);
+    llast->time=time(0);
+    llast->close_time=0;
+    llast->close_type=LAST_CRASH;
+
+    if(!(fp=fopen(LAST_FILE,"a"))) {
+      log("error opening last_file for appending");
+      free(llast);
+      return;
+    }
+    fwrite(llast,sizeof(struct last_entry),1,fp);
+    fclose(fp);
+    return;
+  } else {
+    /* we're modifying a found entry */
+    mod_llog_entry(llast,type);
+    free(llast);
+  }
+}
+
+/* debugging stuff, if you wanna see the whole file */
+char *list_llog_entry() {
+  FILE *fp;
+  struct last_entry llast;
+  char buffer[12800];
+  extern const char *last_array[];
+
+  if(!(fp=fopen(LAST_FILE,"r"))) {
+    log("bad things.");
+    return strdup("Error.");
+  }
+
+  sprintf(buffer,"Last log\r\n");
+
+  fread(&llast,sizeof(struct last_entry),1,fp);
+
+  while(!feof(fp)) {
+    sprintf(buffer,"%s%10s\t%s\t%d\t%s",
+        buffer,llast.username,last_array[llast.close_type],
+        llast.punique,ctime(&llast.time));
+    fread(&llast,sizeof(struct last_entry),1,fp);
+  }
+  return strdup(buffer);
+}
+
diff -wuprN --exclude=*.o ./handler.h ../../last/src/handler.h
--- ./handler.h	Thu Mar  4 22:47:47 1999
+++ ../../last/src/handler.h	Tue Aug  3 04:18:28 1999
@@ -114,3 +114,30 @@ void	remember(struct char_data *ch, stru
 int	damage(struct char_data *ch, struct char_data *victim, int dam, int attacktype);
 int	skill_message(int dam, struct char_data *ch, struct char_data *vict,
 		      int attacktype);
+
+#define LAST_CONNECT            0
+#define LAST_ENTER_GAME         1
+#define LAST_RECONNECT          2
+#define LAST_TAKEOVER           3
+#define LAST_QUIT               4
+#define LAST_IDLEOUT            5
+#define LAST_DISCONNECT         6
+#define LAST_SHUTDOWN           7
+#define LAST_REBOOT             8
+#define LAST_CRASH              9
+#define LAST_PLAYING            10
+
+struct last_entry {
+  int close_type;
+  char hostname[256];
+  char username[16];
+  time_t time;
+  time_t close_time;
+  int idnum;
+  int punique;
+};
+
+void add_llog_entry(struct char_data *ch, int type);
+struct last_entry *find_llog_entry(int punique,int idnum,int close);
+void mod_llog_entry(struct last_entry *llast,int type);
+
diff -wuprN --exclude=*.o ./interpreter.c ../../last/src/interpreter.c
--- ./interpreter.c	Thu Mar  4 21:55:38 1999
+++ ../../last/src/interpreter.c	Tue Aug  3 04:43:07 1999
@@ -1161,7 +1161,7 @@ int perform_dupe_check(struct descriptor
   struct descriptor_data *k, *next_k;
   struct char_data *target = NULL, *ch, *next_ch;
   int mode = 0;
-
+  int pref_temp=0;
   int id = GET_IDNUM(d->character);
 
   /*
@@ -1178,6 +1178,8 @@ int perform_dupe_check(struct descriptor
     if (k->original && (GET_IDNUM(k->original) == id)) {    /* switched char */
       SEND_TO_Q("\r\nMultiple login detected -- disconnecting.\r\n", k);
       STATE(k) = CON_CLOSE;
+      pref_temp=GET_PREF(k->character);
+
       if (!target) {
 	target = k->original;
 	mode = UNSWITCH;
@@ -1187,6 +1189,7 @@ int perform_dupe_check(struct descriptor
       k->character = NULL;
       k->original = NULL;
     } else if (k->character && (GET_IDNUM(k->character) == id)) {
+      pref_temp=GET_PREF(k->character);
       if (!target && STATE(k) == CON_PLAYING) {
 	SEND_TO_Q("\r\nThis body has been usurped!\r\n", k);
 	target = k->character;
@@ -1240,8 +1243,22 @@ int perform_dupe_check(struct descriptor
   }
 
   /* no target for swicthing into was found - allow login to continue */
-  if (!target)
-    return (0);
+
+  /* stupid-case rule for setting hostname on char:
+	any time you set the char's pref .. */
+  
+  if (!target) {
+    GET_HOST(d->character) = strdup(d->host); 
+    GET_PREF(d->character)=  get_new_pref();
+    return 0;
+  } else {
+    if(GET_HOST(target)) {
+      free(GET_HOST(target));
+    }
+    GET_HOST(target) = strdup(d->host);
+    GET_PREF(target)=pref_temp;
+    add_llog_entry(target,LAST_RECONNECT);
+  }
 
   /* Okay, we've found a target.  Connect d to target. */
   free_char(d->character); /* get rid of the old char */
@@ -1559,6 +1576,7 @@ void nanny(struct descriptor_data *d, ch
     break;
 
   case CON_RMOTD:		/* read CR after printing motd   */
+    add_llog_entry(d->character,LAST_CONNECT);
     SEND_TO_Q(MENU, d);
     STATE(d) = CON_MENU;
     break;
@@ -1568,6 +1586,7 @@ void nanny(struct descriptor_data *d, ch
     case '0':
       SEND_TO_Q("Goodbye.\r\n", d);
       STATE(d) = CON_CLOSE;
+      add_llog_entry(d->character,LAST_QUIT);
       break;
 
     case '1':
diff -wuprN --exclude=*.o ./limits.c ../../last/src/limits.c
--- ./limits.c	Fri Jan  8 00:16:41 1999
+++ ../../last/src/limits.c	Tue Aug  3 04:43:53 1999
@@ -374,6 +374,9 @@ void check_idling(struct char_data * ch)
       if (ch->in_room != NOWHERE)
 	char_from_room(ch);
       char_to_room(ch, 3);
+
+      add_llog_entry(ch,LAST_IDLEOUT);
+
       if (ch->desc) {
 	STATE(ch->desc) = CON_DISCONNECT;
 	/*
diff -wuprN --exclude=*.o ./objsave.c ../../last/src/objsave.c
--- ./objsave.c	Thu Mar  4 21:55:38 1999
+++ ../../last/src/objsave.c	Tue Aug  3 04:40:42 1999
@@ -1164,11 +1164,21 @@ SPECIAL(cryogenicist)
 void Crash_save_all(void)
 {
   struct descriptor_data *d;
+  extern int circle_shutdown;
+  extern int circle_reboot;
+
   for (d = descriptor_list; d; d = d->next) {
     if ((STATE(d) == CON_PLAYING) && !IS_NPC(d->character)) {
       if (PLR_FLAGGED(d->character, PLR_CRASH)) {
 	Crash_crashsave(d->character);
 	save_char(d->character, NOWHERE);
+	if(circle_shutdown) {
+	  if(circle_reboot) {
+	     add_llog_entry(d->character,LAST_REBOOT);
+          } else {
+             add_llog_entry(d->character,LAST_SHUTDOWN);
+	  }
+        }
 	REMOVE_BIT(PLR_FLAGS(d->character), PLR_CRASH);
       }
     }
diff -wuprN --exclude=*.o ./structs.h ../../last/src/structs.h
--- ./structs.h	Thu Mar  4 21:39:21 1999
+++ ../../last/src/structs.h	Tue Aug  3 03:53:20 1999
@@ -888,6 +888,8 @@ struct char_data {
 
    struct follow_type *followers;        /* List of chars followers       */
    struct char_data *master;             /* Who is char following?        */
+   long pref;				/* unique session id */
+   char *hostname;			/* hostname copy */
 };
 /* ====================================================================== */
 
diff -wuprN --exclude=*.o ./utils.h ../../last/src/utils.h
--- ./utils.h	Sat Mar 13 16:59:47 1999
+++ ../../last/src/utils.h	Tue Aug  3 04:24:23 1999
@@ -504,3 +504,5 @@ void	update_pos(struct char_data *victim
 #define CRYPT(a,b) ((char *) crypt((a),(b)))
 #endif
 
+#define GET_PREF(ch) 		(ch->pref)
+#define GET_HOST(ch)		(ch->hostname)
