diff -urN xpilot-3.7.1/src/collision.c xpilot-3.7.1gurk/src/collision.c
--- xpilot-3.7.1/src/collision.c	Tue Jan 27 12:38:58 1998
+++ xpilot-3.7.1gurk/src/collision.c	Mon May 18 20:43:35 1998
@@ -237,7 +381,7 @@
 {
     player	*pl = Players[ind];
 
-    pl->score += (points);
+    pl->AddScore(points);
 
     if (pl->conn != NOT_CONNECTED)
 	Send_score_object(pl->conn, points, x, y, msg);
@@ -310,7 +454,7 @@
 	    SET_BIT(pl->status, KILLED);
 	    sprintf(msg, "%s left the known universe.", pl->name);
 	    Set_message(msg);
-		sc = Rate(WALL_SCORE, pl->score);
+		sc = Rate(WALL_SCORE, pl->GetScore());
 	    SCORE(i, -sc,
 		  OBJ_X_IN_BLOCKS(pl),
 		  OBJ_Y_IN_BLOCKS(pl),
@@ -381,8 +525,8 @@
 			sprintf(msg, "%s and %s crashed.",
 				pl->name, Players[j]->name);
 			Set_message(msg);
-			sc = Rate(Players[j]->score, pl->score) / 3;
-			sc2 = Rate(pl->score, Players[j]->score) / 3;
+			sc = Rate(Players[j]->GetScore(), pl->GetScore()) / 3;
+			sc2 = Rate(pl->GetScore(), Players[j]->GetScore()) / 3;
 			Score_players(i, -sc, Players[j]->name,
 				      j, -sc2, pl->name);
 		    } else {
@@ -399,8 +543,8 @@
 			sound_play_sensors(Players[j]->pos.x,
 					   Players[j]->pos.y,
 					   PLAYER_RAN_OVER_PLAYER_SOUND);
-			pl->kills++;
-			sc = Rate(pl->score, Players[j]->score) / 3;
+			pl->AddKill();
+			sc = Rate(pl->GetScore(), Players[j]->GetScore()) / 3;
 			Score_players(i_tank_owner, sc, Players[j]->name,
 				      j, -sc, pl->name);
 		    }
@@ -419,8 +563,8 @@
 			Set_message(msg);
 			sound_play_sensors(pl->pos.x, pl->pos.y,
 					   PLAYER_RAN_OVER_PLAYER_SOUND);
-			Players[j]->kills++;
-			sc = Rate(Players[j]->score, pl->score) / 3;
+			Players[j]->AddKill();
+			sc = Rate(Players[j]->GetScore(), pl->GetScore()) / 3;
 			Score_players(j_tank_owner, sc, pl->name,
 				      i, -sc, Players[j]->name);
 		    }
@@ -467,6 +611,7 @@
 		    pl->ball = NULL;
 		    sound_play_sensors(pl->pos.x, pl->pos.y,
 				       CONNECT_BALL_SOUND);
+		    pl->grabbedBallFrame = main_loops;
 		}
 	    }
 	} else {
@@ -706,8 +851,8 @@
 			  OBJ_Y_IN_BLOCKS(pl),
 			  Players[killer]->name);
 		} else {
-		    Players[killer]->kills++;
-		    sc = Rate(Players[killer]->score, pl->score);
+		    Players[killer]->AddBallKill();
+		    sc = Rate(Players[killer]->GetScore(), pl->GetScore());
 		    Score_players(killer, sc, pl->name,
 				  ind, -sc, Players[killer]->name);
 		}
@@ -910,7 +1055,7 @@
 		 * for a low-scored-player hitting a high-scored-player's mine.
 		 * Maybe not.
 		 */
-		sc = Rate(Players[killer]->score, pl->score) / 6;
+		sc = Rate(Players[killer]->GetScore(), pl->GetScore()) / 6;
 		Score_players(killer, sc, pl->name,
 			      ind, -sc, Players[killer]->name);
 	    }
@@ -944,8 +1089,8 @@
 			      OBJ_Y_IN_BLOCKS(pl),
 			      (killer == -1) ? "[Explosion]" : pl->name);
 		    } else {
-			Players[killer]->kills++;
-			sc = Rate(Players[killer]->score, pl->score) / 3;
+			Players[killer]->AddKill();
+			sc = Rate(Players[killer]->GetScore(), pl->GetScore()) / 3;
 			Score_players(killer, sc, pl->name,
 				      ind, -sc, Players[killer]->name);
 		    }
@@ -1047,7 +1192,7 @@
 				       PLAYER_HIT_CANNONFIRE_SOUND);
 		    sprintf(msg, "%s was hit by cannonfire.", pl->name);
 		    Set_message(msg);
-		    sc = Rate(CANNON_SCORE, pl->score)/4;
+		    sc = Rate(CANNON_SCORE, pl->GetScore())/4;
 		    SCORE(ind, -sc,
 			  OBJ_X_IN_BLOCKS(pl),
 			  OBJ_Y_IN_BLOCKS(pl),
@@ -1079,8 +1224,8 @@
 			      OBJ_Y_IN_BLOCKS(pl),
 			      Players[killer]->name);
 		    } else {
-			Players[killer]->kills++;
-			sc = Rate(Players[killer]->score, pl->score);
+			Players[killer]->AddKill();
+			sc = Rate(Players[killer]->GetScore(), pl->GetScore());
 			Score_players(killer, sc, pl->name,
 				      ind, -sc, Players[killer]->name);
 		    }
@@ -1368,7 +1513,7 @@
 			    Add_fuel(&(vic->fuel), (long)ED_LASER_HIT);
 			    if (BIT(vic->used, OBJ_SHIELD) == 0) {
 				SET_BIT(vic->status, KILLED);
-				sc = Rate(pl->score, vic->score);
+				sc = Rate(pl->GetScore(), vic->GetScore());
 				Score_players(ind, sc, vic->name,
 					      victims[j].ind, -sc, pl->name);
 				sound_play_sensors(vic->pos.x, vic->pos.y,
@@ -1377,7 +1522,7 @@
 					"%s got roasted alive by %s's laser.",
 					vic->name, pl->name);
 				Set_message(msg);
-				pl->kills++;
+				pl->AddLaserKill();
 				Robot_war(victims[j].ind, ind);
 			    }
 			}
diff -urN xpilot-3.7.1/src/contact.c xpilot-3.7.1gurk/src/contact.c
--- xpilot-3.7.1/src/contact.c	Tue Jan 27 12:39:02 1998
+++ xpilot-3.7.1gurk/src/contact.c	Wed May  6 00:22:36 1998
@@ -134,9 +134,10 @@
     int			num_unpaused = 0;
 
     for (i = NumPlayers - 1; i >= 0; i--) {
-	if (Players[i]->conn != NOT_CONNECTED
-	    && BIT(Players[i]->status, PAUSE)
-	    && (team == TEAM_NOT_SET || Players[i]->team == team)) {
+	if (Players[i]->conn != NOT_CONNECTED &&
+	    BIT(Players[i]->status, PAUSE) &&
+	    (team == TEAM_NOT_SET || Players[i]->team == team) &&
+	    !Players[i]->IsAuthorized() ) {
 	    if (team == TEAM_NOT_SET) {
 		sprintf(msg,
 			"The paused \"%s\" was kicked because the game is full.",
@@ -405,7 +406,7 @@
 	 */
 
 #ifndef	SILENT
-	xpprintf("%s %s@%s asked for info about current game.\n",
+	0 && xpprintf("%s %s@%s asked for info about current game.\n",
 	       showtime(), real_name, host_addr);
 #endif
 	Sockbuf_clear(&ibuf);
diff -urN xpilot-3.7.1/src/event.c xpilot-3.7.1gurk/src/event.c
--- xpilot-3.7.1/src/event.c	Tue Jan 27 12:38:58 1998
+++ xpilot-3.7.1gurk/src/event.c	Thu May  7 03:29:59 1998
@@ -42,6 +42,7 @@
 #include "saudio.h"
 #include "bit.h"
 #include "netserver.h"
+#include "gurk.h"
 
 char event_version[] = VERSION;
 
@@ -128,9 +129,9 @@
     return (!alive);
 }
 
-int Player_lock_closest(int ind, int next)
+int Player_lock_closest(const int ind, int next)
 {
-    player *pl = Players[ind];
+    Player * const pl = Players[ind];
     int lock, i, newpl;
     DFLOAT dist, best, l;
 
@@ -153,9 +154,9 @@
 	    ((!lockOtherTeam && !team_dead(pl->team)) &&
 	    ((BIT(World.rules->mode, TEAM_PLAY) &&
 	     BIT(pl->status, PLAYING|GAME_OVER) == PLAYING && TEAM(ind, i)) ||
-	    (BIT(World.rules->mode, TEAM_PLAY) &&
-	     BIT(pl->status, PLAYING|GAME_OVER) != PLAYING && !TEAM(ind, i)))) ||
-	    BIT(Players[i]->status, PLAYING|GAME_OVER) != PLAYING)
+	     (BIT(World.rules->mode, TEAM_PLAY) &&
+	      BIT(pl->status, PLAYING|GAME_OVER) != PLAYING && !TEAM(ind, i)))) ||
+ 	    BIT(Players[i]->status, PLAYING|GAME_OVER) != PLAYING)
 	    continue;
 	l = Wrap_length(Players[i]->pos.x - pl->pos.x,
 			Players[i]->pos.y - pl->pos.y);
@@ -168,27 +169,31 @@
 	return 0;
     SET_BIT(pl->lock.tagged, LOCK_PLAYER);
     pl->lock.pl_id = Players[newpl]->id;
+
     return 1;
 }
 
 
 void Pause_player(int ind, int onoff)
 {
-    player		*pl = Players[ind];
+    player * const pl = Players[ind];
     int			i;
 
     if (onoff != 0 && !BIT(pl->status, PAUSE)) { /* Turn pause mode on */
+	pl->DetachAllBalls();
 	pl->count = 10*FPS;
 	pl->updateVisibility = 1;
 	CLR_BIT(pl->status, SELF_DESTRUCT|PLAYING);
 	SET_BIT(pl->status, PAUSE);
 	pl->mychar = 'P';
 	updateScores = true;
+	strcpy(pl->scorenode->logout, "paused");
     }
     else if (onoff == 0 && BIT(pl->status, PAUSE)) { /* Turn pause mode off */
 	if (pl->count <= 0) {
 	    bool toolate = false;
 
+	    pl->idleCount = 0; // idle
 	    CLR_BIT(pl->status, PAUSE);
 	    updateScores = true;
 	    if (BIT(pl->mode, LIMITED_LIVES)) {
@@ -204,6 +209,7 @@
 		    }
 		}
 	    }
+	    strcpy(pl->scorenode->logout, "playing");
 	    if (toolate) {
 		pl->life = 0;
 		pl->mychar = 'W';
@@ -430,7 +436,7 @@
 		break;
 
 	    case KEY_DROP_BALL:
-		Detach_ball(ind, -1);
+		pl->DetachAllBalls();
 		break;
 
 	    case KEY_FIRE_SHOT:
diff -urN xpilot-3.7.1/src/frame.c xpilot-3.7.1gurk/src/frame.c
--- xpilot-3.7.1/src/frame.c	Tue Jan 27 12:38:58 1998
+++ xpilot-3.7.1gurk/src/frame.c	Wed May  6 02:33:12 1998
@@ -1,4 +1,4 @@
-/* $Id: frame.c,v 3.77 1998/01/08 19:28:46 bert Exp $
+/* $Id: frame.c,v 3.77 1998/01/08 19:28:46 bert Exp $ -*-c++-*-
  *
  * XPilot, a multiplayer gravity war game.  Copyright (C) 1991-97 by
  *
@@ -852,7 +852,8 @@
 	if (conn == NOT_CONNECTED) {
 	    continue;
 	}
-	if (BIT(pl->status, PAUSE|GAME_OVER)) {
+	if (BIT(pl->status, PAUSE|GAME_OVER) &&
+	    !pl->IsAuthorized()) {
 	    /*
 	     * Lower the frame rate for non-playing players
 	     * to reduce network load.
@@ -893,11 +894,13 @@
 	 */
 	if (BIT(pl->lock.tagged, LOCK_PLAYER)) {
 	    if ((BIT(pl->status, (GAME_OVER|PLAYING)) == (GAME_OVER|PLAYING))
-		|| (BIT(pl->status, PAUSE) &&
-		    ((BIT(World.rules->mode, TEAM_PLAY)
-		     && pl->team != TEAM_NOT_SET
-		     && pl->team == Players[GetInd[pl->lock.pl_id]]->team)
-		    ))) {
+		||
+		( BIT(pl->status, PAUSE) &&
+		  ( ( BIT(World.rules->mode, TEAM_PLAY) &&
+		      pl->team != TEAM_NOT_SET &&
+		      pl->team == Players[GetInd[pl->lock.pl_id]]->team )
+		    ||
+		    pl->IsAuthorized())) ) {
 		ind = GetInd[pl->lock.pl_id];
 	    } else {
 		ind = i;
@@ -981,3 +984,25 @@
 	Robot_message(GetInd[pl->id], msg);
 }
 
+void
+SendMessageToAll(const char format[], ...)
+{
+    char msg[MSG_LEN * 2];
+    va_list ap;
+    va_start(ap, format);
+    vsprintf(msg, format, ap);
+    va_end(ap);
+    Set_message(msg);
+}
+
+void
+SendMessageToPlayer(Player * const pl,
+		    const char     format[], ...)
+{
+    char msg[MSG_LEN * 2];
+    va_list ap;
+    va_start(ap, format);
+    vsprintf(msg, format, ap);
+    va_end(ap);
+    Set_player_message(pl, msg);    
+}
diff -urN xpilot-3.7.1/src/global.h xpilot-3.7.1gurk/src/global.h
--- xpilot-3.7.1/src/global.h	Tue Jan 27 12:38:58 1998
+++ xpilot-3.7.1gurk/src/global.h	Tue Jun 23 20:44:57 1998
@@ -202,6 +202,10 @@
 
 extern bool		pLockServer;
 
+extern int		nBallRunsShown;
+extern bool		rankBallRuns;
+extern bool		lardBot;
+
 #endif
 
 #endif /* GLOBAL_H */
diff -urN xpilot-3.7.1/src/netserver.c xpilot-3.7.1gurk/src/netserver.c
--- xpilot-3.7.1/src/netserver.c	Tue Jan 27 12:38:59 1998
+++ xpilot-3.7.1gurk/src/netserver.c	Mon Oct 26 13:02:36 1998
@@ -157,6 +157,7 @@
 #include "setup.h"
 #undef NETSERVER_C
 #include "saudio.h"
+#include "rank.h"
 
 char netserver_version[] = VERSION;
 
@@ -190,7 +191,7 @@
 
     time(&now);
     tmp = localtime(&now);
-    sprintf(buf, "%02d %s %02d:%02d:%02d",
+    sprintf(buf, "%02d\xA0%s\xA0%02d:%02d:%02d",
 	    tmp->tm_mday, month_names[tmp->tm_mon],
 	    tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
     return buf;
@@ -950,6 +951,30 @@
  * and if this succeeds update the player information
  * to all connected players.
  */
+static void LegalizeName(char *string)
+{
+    while ( *string != '\0' ) {
+	char & ch = *string;
+	if ( ch == ' ' )
+	    ch = 0xA0;
+	else if ( ch == '\"' )
+	    ch = '\'';
+	else if ( !isprint(ch) )
+	    ch = '.';
+	string++;
+    }
+}
+
+static void LegalizeHost(char *string)
+{
+    while ( *string != '\0' ) {
+	char & ch = *string;
+	if ( !isalnum(ch) && ch != '.' )
+	    ch = '.';
+	string++;
+    }
+}
+
 static int Handle_login(int ind)
 {
     connection_t	*connp = &Conn[ind];
@@ -989,6 +1014,7 @@
     } else {
 	connp->team = TEAM_NOT_SET;
     }
+
     for (i = 0; i < NumPlayers; i++) {
 	if (strcasecmp(Players[i]->name, connp->nick) == 0) {
 	    errno = 0;
@@ -1000,9 +1026,13 @@
 	return -1;
     }
     pl = Players[NumPlayers];
+    strcpy(pl->illegalName, connp->nick);
     strcpy(pl->name, connp->nick);
     strcpy(pl->realname, connp->real);
     strcpy(pl->hostname, connp->host);
+    LegalizeName(pl->name);
+    LegalizeName(pl->realname);
+    LegalizeHost(pl->hostname);
     if (connp->team != TEAM_NOT_SET) {
 	pl->team = connp->team;
     }
@@ -1036,7 +1066,7 @@
      * Tell him about himself first.
      */
     Send_player(pl->conn, pl->id);
-    Send_score(pl->conn, pl->id, pl->score, pl->life, pl->mychar);
+    Send_score(pl->conn, pl->id, pl->GetScore(), pl->life, pl->mychar);
     Send_base(pl->conn, pl->id, pl->home_base);
     /*
      * And tell him about all the others.
@@ -1044,7 +1074,7 @@
     for (i = 0; i < NumPlayers - 1; i++) {
 	Send_player(pl->conn, Players[i]->id);
 	Send_score(pl->conn, Players[i]->id,
-		   Players[i]->score, Players[i]->life, Players[i]->mychar);
+		   Players[i]->GetScore(), Players[i]->life, Players[i]->mychar);
 	if (!IS_TANK_IND(i)) {
 	    Send_base(pl->conn, Players[i]->id, Players[i]->home_base);
 	}
@@ -1055,7 +1085,7 @@
     for (i = 0; i < NumPlayers - 1; i++) {
 	if (Players[i]->conn != NOT_CONNECTED) {
 	    Send_player(Players[i]->conn, pl->id);
-	    Send_score(Players[i]->conn, pl->id, pl->score,
+	    Send_score(Players[i]->conn, pl->id, pl->GetScore(),
 		       pl->life, pl->mychar);
 	    Send_base(Players[i]->conn, pl->id, pl->home_base);
 	}
@@ -1080,6 +1110,10 @@
 		pl->name, pl->realname, World.name, World.author);
     }
     Set_message(msg);
+    if ( getenv("XPILOTGREETING") != NULL ) {
+	sprintf(msg, "%s", getenv("XPILOTGREETING"));
+	Set_player_message(pl, msg);
+    }
 
     conn_bit = (1 << ind);
     for (i = 0; i < World.NumCannons; i++) {
@@ -1122,6 +1156,15 @@
 
     num_logins++;
 
+    pl->isAuthority = false; // authorized
+    for ( int i = 0;  // idle
+	  i < NumPlayers;
+	  i++ )
+	if ( Players[i]->mychar == ' ' ) 
+	    Players[i]->idleCount = 0;
+
+    Get_saved_score(*pl); // ranking
+
     return 0;
 }
 
@@ -1427,12 +1470,12 @@
 	return 0;
     }
     Convert_ship_2_string(pl->ship, buf, ext,
-			  (connp->version < 0x3200) ? 0x3100 : 0x3200);
+			  ((connp->version) < 0x3200) ? 0x3100 : 0x3200);
     n = Packet_printf(&connp->c,
 		      "%c%hd" "%c%c" "%s%s%s" "%S",
 		      PKT_PLAYER, pl->id,
 		      pl->team, pl->mychar,
-		      pl->name, pl->realname, pl->hostname,
+		      pl->illegalName, pl->realname, pl->hostname, // BERRA
 		      buf);
     if (connp->version > 0x3200) {
 	if (n > 0) {
@@ -1846,6 +1898,7 @@
 	pl = Players[GetInd[connp->id]];
 	memcpy(pl->last_keyv, connp->r.ptr, size);
 	connp->r.ptr += size;
+	Players[GetInd[connp->id]]->idleCount = 0; // idle
 	Handle_keyboard(GetInd[connp->id]);
     }
     if (connp->num_keyboard_updates++ && (connp->state & CONN_PLAYING)) {
@@ -2311,21 +2364,55 @@
  * If the string does not match one team or one player the message is not sent.
  * If no colon, the message is general.
  */
-static void Handle_talk(int ind, char *str)
+bool
+TalkMatchString(char * &   string,
+		const char pattern[])
+{
+    const int l = strlen(pattern);
+    if ( strncasecmp(string, pattern, l) == 0 ) {
+	string += l;
+	return true;
+    } else {
+	return false;
+    }
+}
+
+static void Handle_talk(const int ind,
+			char *    str)
 {
-    connection_t	*connp = &Conn[ind];
-    player		*pl = Players[GetInd[connp->id]];
+    connection_t * const connp = &Conn[ind];
+    Player * const       pl    = Players[GetInd[connp->id]];
     int			i, sent, team;
-	unsigned int	len;
+    unsigned int	len;
     char		*cp,
 			msg[MSG_LEN * 2];
 
+    pl->flooding += FPS/3;
+
+    if ( str[0] == '/' ) {
+	if ( TalkMatchString(str, "/say ") ) {
+	    SendMessageToAll("%s [%s]", str, pl->name);
+	    return;
+	} else if ( TalkMatchString(str, "/me ") ) {
+	    SendMessageToAll("%s %s ", pl->name, str);
+	    return;
+	}
+
+
+	else if ( TalkMatchString(str, "/lägerhyddsvägen") ) {
+	    pl->Authorize();
+	    SendMessageToPlayer(pl, "Good day Sir!");
+	    return;
+
+	} else if ( pl->IsAuthorized() ) {
+	}
+    }
+
     if ((cp = strchr (str, ':')) == NULL
 	|| cp == str
 	|| strchr("-_~)(/\\}{[]", cp[1])	/* smileys are smileys */
 	) {
-	sprintf(msg, "%s [%s]", str, pl->name);
-	Set_message(msg);
+	SendMessageToAll("%s [%s]", str, pl->name);
 	return;
     }
     *cp++ = '\0';
@@ -2350,7 +2437,7 @@
 	    Set_player_message(pl, msg);
 	}
     }
-    else if (strcasecmp(str, "god") == 0) {
+    else if (false && strcasecmp(str, "god") == 0) {
 	FILE *fp = fopen(LOGFILE, "a");
 	if (fp) {
 	    fprintf(fp,
@@ -2648,7 +2735,10 @@
 
 	motd_loops = main_loops;
 
-	if ((fd = open(SERVERMOTDFILE, O_RDONLY)) == -1) {
+	static const char XPILOTSERVERMOTD[] = "XPILOTSERVERMOTD";
+	const char * const motdfilename = (getenv(XPILOTSERVERMOTD) != 0) ?
+	    getenv(XPILOTSERVERMOTD) : SERVERMOTDFILE;
+	if ((fd = open(motdfilename, O_RDONLY)) == -1) {
 	    motd_size = 0;
 	    return -1;
 	}
diff -urN xpilot-3.7.1/src/object.h xpilot-3.7.1gurk/src/object.h
--- xpilot-3.7.1/src/object.h	Tue Jan 27 12:38:59 1998
+++ xpilot-3.7.1gurk/src/object.h	Mon May 18 21:25:44 1998
@@ -46,10 +46,14 @@
 #include "click.h"
 #endif
 
+#include "rank.h"
+
 #ifdef	_WINDOWS
 #include "../contrib/NT/xpilot/winNet.h"
 #endif
 
+#include "rules.h"
+
 /*
  * Different types of objects, including player.
  * Robots and tanks are players but have an additional bit.
@@ -155,8 +159,7 @@
 #define OBJ_X_IN_BLOCKS(obj)	((obj)->pos.bx)
 #define OBJ_Y_IN_BLOCKS(obj)	((obj)->pos.by)
 
-typedef struct _object object;
-struct _object {
+struct ObjectBase {
     byte	color;			/* Color of object */
     u_byte	dir;			/* Direction of acceleration */
     int		id;			/* For shots => id of player */
@@ -173,7 +176,11 @@
     long	status;
     modifiers	mods;			/* Modifiers to this object */
 
-    /* up to here all object types (including players!) should be the same. */
+    byte Color() const { return color; }
+    int Id() const { return id; }
+};
+
+struct object : public ObjectBase {
 
     DFLOAT	turnspeed;		/* for missiles only */
     long	fuselife;		/* Ticks left when considered fused */
@@ -190,9 +197,7 @@
     int		pl_range;		/* distance to player for collision. */
     int		pl_radius;		/* distance to player for hit. */
 
-#ifdef __cplusplus
-		_object() {}
-#endif
+    object() {}
 };
 
 
@@ -216,8 +221,7 @@
 };
 
 #define MAX_PLAYER_ECMS		8	/* Maximum simultaneous per player */
-typedef struct _ecm_info ecm_info;
-struct _ecm_info {
+struct ecm_info {
     int		count;
     int 	size[MAX_PLAYER_ECMS];
     position 	pos[MAX_PLAYER_ECMS];
@@ -237,8 +241,8 @@
 /*
  * Transporter info, this is per-player
  */
-typedef struct _trans_info trans_info;
-struct _trans_info {
+
+struct trans_info {
     int 	count,
 		pl_id;
 };
@@ -264,25 +268,7 @@
  * this makes it possible to use the same basic operations on both of them
  * (mainly used in update.c).
  */
-typedef struct player player;
-struct player {
-    byte	color;			/* Color of object */
-    u_byte	dir;			/* Direction of acceleration */
-    int		id;			/* Unique id of object */
-    objposition	pos;			/* World coordinates */
-    ipos	prevpos;		/* Previous position... */
-    vector	vel;			/* Velocity of object */
-    vector	acc;			/* Acceleration constant */
-    DFLOAT	max_speed;		/* Maximum speed of object */
-    DFLOAT	mass;			/* Mass of object (incl. cargo) */
-    int		type;			/* Type of object */
-    long	info;			/* Miscellaneous info */
-    int		life;			/* Zero is dead. One is alive */
-    int		count;			/* Miscellaneous timings */
-    long	status;			/** Status, currently **/
-    modifiers	mods;			/* Modifiers in effect */
-
-    /* up to here the player type should be the same as an object. */
+struct player : public ObjectBase {
 
     int		type_ext;		/* extended type info (tank, robot) */
 
@@ -368,6 +354,7 @@
     char	mychar;			/* Special char for player */
     char	prev_mychar;		/* Special char for player */
     char	name[MAX_CHARS];	/* Nick-name of player */
+    char	illegalName[MAX_CHARS];	/* Nick-name of player */
     char	realname[MAX_CHARS];	/* Real name of player */
     char	hostname[MAX_CHARS];	/* Hostname of client player uses */
     u_short	team;			/* What team is the player on? */
@@ -407,9 +394,177 @@
     int		player_round;		/* Divisor for player FPS calculation */
     int		player_count;		/* Player's current frame count */
 
-#ifdef __cplusplus
-		player() {}
-#endif
+    bool	isAuthority;
+    int		idleCount;		// idle
+    short	flooding;
+    ScoreNode * scorenode;
+
+    int	 grabbedBallFrame;
+    void StartBallRun() { extern int main_loops; grabbedBallFrame = main_loops; }
+    void AbortBallRun() { grabbedBallFrame = -1; }
+    void EndBallRun();
+
+    player() {}
+
+    void SetCharacter(char);
+    char GetCharacter()				const __attribute__ ((const));
+
+    void DetachBall(int);
+    void DetachAllBalls();
+
+    void AddKill();
+    void AddBallKill();
+    void AddTargetKill();
+    void AddLaserKill();
+    void ClearKills() { kills = 0; }
+    int GetKills() { return kills; }
+
+    void AddDeath();
+    int GetDeaths() { return deaths; }
+    void ClearDeaths() { if ( scorenode != 0 ) deaths = 0; }
+    void IgnoreLastDeath() { if ( scorenode != 0 ) scorenode->deaths--; }
+
+    void AddRound() { if ( scorenode != 0 ) scorenode->rounds++; }
+
+    void SetScore(int);
+    void AddScore(int);
+    int GetScore() { return score; }
+
+    void FireShot() { if ( scorenode != 0 ) scorenode->firedShots++; }
+
+    void SavedBall() { if ( scorenode != 0 ) scorenode->ballsSaved++; }
+    void LostBall() { if ( scorenode != 0 ) scorenode->ballsLost++; }
+    void CashedBall() { if ( scorenode != 0 ) scorenode->ballsCashed++; }
+    void WonBall() { if ( scorenode != 0 ) scorenode->ballsWon++; }
+
+    const char * GetName()			const __attribute__ ((const));
+    bool IsWaiting()				const __attribute__ ((const));
+
+    bool IsUsing(int);
+    void DontUse(int);
+
+    bool Has(int);
+    void DontHave(int);
+
+    bool IsPaused()				const __attribute__ ((const));
+    void Pause();
+    void Unpause();
+
+    void UpdateForcedVisibility();
+
+    void UseTractorBeam();
+
+    void Authorize() { isAuthority = true; }
+    bool IsAuthorized() { return isAuthority; }
 };
 
+typedef player Player;
+
+inline void
+player::AddDeath()
+{
+    deaths++;
+    if ( scorenode != 0 )
+	scorenode->deaths++;
+}
+
+inline void
+player::AddKill()
+{
+    kills++;
+    if ( scorenode != 0 )
+	scorenode->kills++;
+}
+
+inline void player::AddBallKill() { AddKill(); }
+inline void player::AddTargetKill() { AddKill(); }
+inline void player::AddLaserKill() { AddKill(); }
+
+inline void
+player::AddScore(const int add)
+{
+    score += add;
+    if ( scorenode != 0 )
+	scorenode->score += add;
+}
+
+inline bool
+player::IsUsing(const int thing)
+{
+   return BIT(used, thing) != 0; 
+}
+
+inline void
+player::DontUse(const int thing)
+{
+    CLR_BIT(used, thing);
+}
+
+inline bool
+player::Has(const int thing)
+{
+    return BIT(have, thing) != 0;
+}
+
+inline void
+player::DontHave(const int thing)
+{
+    CLR_BIT(have, thing);
+}
+	
+inline void
+player::SetScore(const int newScore)
+{
+    score = newScore;
+    scorenode->score = newScore;
+}
+
+inline void
+player::UpdateForcedVisibility()
+{
+    updateVisibility = 0;
+    if ( forceVisible > 0 ) {
+	forceVisible--;
+	if ( forceVisible == 0 )
+	    updateVisibility = 1;
+    }
+}
+
+inline void
+player::DetachAllBalls()
+{
+    this->DetachBall(-1);
+}
+
+inline bool
+player::IsPaused() const
+{
+    return BIT(status, PAUSE) != 0;
+}
+
+inline void
+player::SetCharacter(const char newCharacter)
+{
+    mychar = newCharacter;
+}
+
+inline char
+player::GetCharacter() const
+{
+    return mychar;
+}
+
+inline bool
+player::IsWaiting() const
+{
+    return GetCharacter() == 'W';
+}
+
+inline const char *
+player::GetName() const
+{
+    return name;
+}
+
 #endif
+
diff -urN xpilot-3.7.1/src/play.c xpilot-3.7.1gurk/src/play.c
--- xpilot-3.7.1/src/play.c	Wed Jan 28 09:55:14 1998
+++ xpilot-3.7.1gurk/src/play.c	Sun May  3 18:35:18 1998
@@ -950,25 +950,24 @@
 		|| (BIT(Players[i]->status, PAUSE)
 		    && Players[i]->count <= 0)
 		|| (BIT(Players[i]->status, GAME_OVER)
-		    && Players[i]->mychar == 'W'
-		    && Players[i]->score == 0)) {
+		    && Players[i]->mychar == 'W')) {
 		continue;
 	    }
 	    if (Players[i]->team == td->team) {
-		lose_score += Players[i]->score;
+		lose_score += Players[i]->GetScore();
 		lose_team_members++;
 		if (BIT(Players[i]->status, GAME_OVER) == 0) {
 		    somebody_flag = 1;
 		}
 	    }
 	    else if (Players[i]->team == tt->team) {
-		win_score += Players[i]->score;
+		win_score += Players[i]->GetScore();
 		win_team_members++;
 	    }
 	}
     }
     if (!somebody_flag) {
-	SCORE(ind, Rate(pl->score, CANNON_SCORE)/2,
+	SCORE(ind, Rate(pl->GetScore(), CANNON_SCORE)/2,
 	      tt->pos.x, tt->pos.y, "Treasure:");
 	return 0;
     }
@@ -987,29 +986,34 @@
     por = (sc * lose_team_members) / (2 * win_team_members + 1);
 
     for (i = 0; i < NumPlayers; i++) {
+	player * const pl = Players[i];
 	if (IS_TANK_IND(i)
-	    || (BIT(Players[i]->status, PAUSE)
+	    || (pl->IsPaused()
 		&& Players[i]->count <= 0)
 	    || (BIT(Players[i]->status, GAME_OVER)
-		&& Players[i]->mychar == 'W'
-		&& Players[i]->score == 0)) {
+		&& Players[i]->IsWaiting()
+		&& Players[i]->GetScore() == 0)) {
 	    continue;
 	}
 	if (Players[i]->team == td->team) {
 	    SCORE(i, -sc, tt->pos.x, tt->pos.y,
 		  "Treasure: ");
+	    Players[i]->LostBall();
 	    if (treasureKillTeam)
 		SET_BIT(Players[i]->status, KILLED);
 	}
 	else if (Players[i]->team == tt->team &&
 		 (Players[i]->team != TEAM_NOT_SET || i == ind)) {
+	    if ( i == ind && lose_team_members > 0 )
+		Players[i]->CashedBall();
+	    Players[i]->WonBall();
 	    SCORE(i, (i == ind ? 3*por : 2*por), tt->pos.x, tt->pos.y,
 		  "Treasure: ");
 	}
     }
 
     if (treasureKillTeam) {
-	Players[ind]->kills++;
+	Players[ind]->AddKill();
     }
 
     updateScores = true;
@@ -1221,6 +1225,7 @@
 	    Add_fuel(&(pl->fuel), (long)(ED_SHOT));
 	    sound_play_sensors(pl->pos.x, pl->pos.y, FIRE_SHOT_SOUND);
 	    pl->shots++;
+	    pl->FireShot();
 	}
 	if (!ShotsGravity) {
 	    CLR_BIT(status, GRAVITY);
@@ -1771,12 +1776,15 @@
 
     case OBJ_BALL:
 	if (shot->id != -1)
-	    Detach_ball(GetInd[shot->id], ind);
+	    Players[GetInd[shot->id]]->DetachBall(ind);
+//	    DetachBall(GetInd[shot->id], ind);
 	else {
 	    /*
 	     * Maybe some player is still busy trying to connect to this ball.
 	     */
 	    for (i = 0; i < NumPlayers; i++) {
+
+
 		if (Players[i]->ball == shot) {
 		    Players[i]->ball = NULL;
 		}
@@ -2395,7 +2403,7 @@
 		    shot = Obj[j];
 		    if (BIT(shot->type, OBJ_BALL) && shot->owner == p->id) {
 			if (rand() % 100 < ((int)(20*range)+5))
-			    Detach_ball(i, j);
+			    p->DetachBall(j);
 		    }
 		}
 	    }
@@ -2463,8 +2471,8 @@
      * It can be enabled by adding -DORIGINAL_BALL to the compilation flags.
      */
 
-    object		*ball = Obj[ind];
-    player		*pl = Players[ GetInd[ball->id] ];
+    object * const ball = Obj[ind];
+    player * const pl   = Players[ GetInd[ball->id] ];
     vector		F;
     const DFLOAT		k = 10.0,
 			a = 0.01,
@@ -2475,7 +2483,7 @@
 
     if (l > BALL_STRING_LENGTH * 1.25
 	|| l < BALL_STRING_LENGTH * 0.75) {
-	Detach_ball(GetInd[ball->id], ind);
+	DetachBall(GetInd[ball->id], ind);
 	return;
     }
 
@@ -2563,7 +2571,8 @@
 
     /* if the tether is too long or too short, release it */
     if (ABS(ratio) > max_spring_ratio) {
-	Detach_ball(GetInd[ball->id], ind);
+	Players[GetInd[ball->id]]->DetachBall(ind);
+//	Detach_ball(GetInd[ball->id], ind);
 	return;
     }
     ball->length = length;
@@ -3047,7 +3056,7 @@
     dummy->team		= pl->team;
     dummy->pseudo_team	= pl->pseudo_team;
     dummy->mychar       = 'T';
-    dummy->score	= pl->score - 500; /* It'll hurt to be hit by this */
+    dummy->score        = (pl->GetScore() - 500);
     updateScores	= true;
     dummy->count	= -1;		/* Don't commit suicide :) */
     dummy->conn		= NOT_CONNECTED;
@@ -3119,7 +3128,7 @@
     for (i = 0; i < NumPlayers - 1; i++) {
 	if (Players[i]->conn != NOT_CONNECTED) {
 	    Send_player(Players[i]->conn, dummy->id);
-	    Send_score(Players[i]->conn, dummy->id, dummy->score, dummy->life,
+	    Send_score(Players[i]->conn, dummy->id, dummy->GetScore(), dummy->life,
 		       dummy->mychar);
 	}
     }
@@ -3184,6 +3193,8 @@
     }
 
     num_debris = min_debris + (int)(rfrac() * (max_debris - min_debris));
+    num_debris >>= 1;
+
     if (num_debris > MAX_TOTAL_SHOTS - NumObjs) {
 	num_debris = MAX_TOTAL_SHOTS - NumObjs;
     }
@@ -3224,31 +3235,4 @@
 	debris->status = status;
 	debris->mods = mods;
     }
-}
-
-/* Explode a fighter */
-void Explode_fighter(int ind)
-{
-    player *pl = Players[ind];
-    int min_debris, max_debris;
-
-    sound_play_sensors(pl->pos.x, pl->pos.y, PLAYER_EXPLOSION_SOUND);
-
-    min_debris = (int)(1 + (pl->fuel.sum / (8.0 * FUEL_SCALE_FACT)));
-    max_debris = (int)(min_debris + (pl->mass * 2.0));
-
-    Make_debris(
-	/* pos.x, pos.y   */ pl->pos.x, pl->pos.y,
-	/* vel.x, vel.y   */ pl->vel.x, pl->vel.y,
-	/* owner id       */ pl->id,
-	/* kind           */ OBJ_DEBRIS,
-	/* mass           */ 3.5,
-	/* status         */ GRAVITY,
-	/* color          */ RED,
-	/* radius         */ 8,
-	/* min,max debris */ min_debris, max_debris,
-	/* min,max dir    */ 0, RES-1,
-	/* min,max speed  */ 20.0, 20 + (((int)(pl->mass))>>1),
-	/* min,max life   */ 5, (int)(5 + (pl->mass * 1.5))
-	);
 }
diff -urN xpilot-3.7.1/src/player.c xpilot-3.7.1gurk/src/player.c
--- xpilot-3.7.1/src/player.c	Tue Jan 27 12:38:59 1998
+++ xpilot-3.7.1gurk/src/player.c	Mon Oct 26 13:35:28 1998
@@ -22,6 +22,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <assert.h>
 #ifdef	_WINDOWS
 #include "../contrib/NT/xpilots/winServer.h"
 #include <math.h>
@@ -44,6 +45,7 @@
 #include "saudio.h"
 #include "error.h"
 #include "objpos.h"
+#include "rank.h"
 
 char player_version[] = VERSION;
 
@@ -61,10 +63,10 @@
  * Functions on player array.
  */
 
-void Pick_startpos(int ind)
+void
+Pick_startpos(const int ind)
 {
-    player	*pl = Players[ind];
-    int		i, num_free, pick, seen;
+    Player * const pl = Players[ind];
     static int	prev_num_bases = 0;
     static char	*free_bases = NULL;
 
@@ -74,17 +76,19 @@
     }
     if (prev_num_bases != World.NumBases) {
 	prev_num_bases = World.NumBases;
-	if (free_bases != NULL) {
-	    free(free_bases);
-	}
-	free_bases = (char *) malloc(World.NumBases * sizeof(*free_bases));
-	if (free_bases == NULL) {
+	if (free_bases != 0)
+	    delete free_bases;
+	free_bases = new char[World.NumBases];
+	if (free_bases == 0) {
 	    error("Can't allocate memory for free_bases");
 	    End_game();
 	}
     }
-    num_free = 0;
-    for (i = 0; i < World.NumBases; i++) {
+
+    int num_free = 0;
+    for ( int i = 0;
+	  i < World.NumBases;
+	  i++ ) {
 	if (World.base[i].team == pl->team) {
 	    num_free++;
 	    free_bases[i] = 1;
@@ -92,26 +96,32 @@
 	    free_bases[i] = 0;
 	}
     }
-    for (i = 0; i < NumPlayers; i++) {
-	if (i != ind
-	    && !IS_TANK_IND(i)
-	    && free_bases[Players[i]->home_base] != 0) {
+    for ( int i = 0;
+	  i < NumPlayers;
+	  i++ ) {
+	if ( i != ind &&
+	     !IS_TANK_IND(i) &&
+	     free_bases[Players[i]->home_base] != 0) {
 	    free_bases[Players[i]->home_base] = 0;
 	    num_free--;
 	}
     }
-    pick = rand() % num_free;
-    seen = 0;
-    for (i = 0; i < World.NumBases; i++) {
+    const int pick = rand() % num_free;
+    int seen = 0;
+    for ( int i = 0;
+	  i < World.NumBases;
+	  i++ ) {
 	if (free_bases[i] != 0) {
 	    if (seen < pick) {
 		seen++;
 	    } else {
 		pl->home_base = i;
 		if (ind < NumPlayers) {
-		    for (i = 0; i < NumPlayers; i++) {
-			if (Players[i]->conn != NOT_CONNECTED) {
-			    Send_base(Players[i]->conn,
+		    for ( int j = 0;
+			  j < NumPlayers;
+			  j++ ) {
+			if (Players[j]->conn != NOT_CONNECTED) {
+			    Send_base(Players[j]->conn,
 				      pl->id,
 				      pl->home_base);
 			}
@@ -119,7 +129,7 @@
 		    if (BIT(pl->status, PLAYING) == 0) {
 			pl->count = RECOVERY_DELAY;
 		    }
-		    else if (BIT(pl->status, PAUSE|GAME_OVER)) {
+		    else if (BIT(pl->status, PAUSE|GAME_OVER) != 0) {
 			Go_home(ind);
 		    }
 		}
@@ -128,7 +138,7 @@
 	}
     }
     error("Can't pick startpos (ind=%d,num=%d,free=%d,pick=%d,seen=%d)",
-	ind, World.NumBases, num_free, pick, seen);
+	  ind, World.NumBases, num_free, pick, seen);
     End_game();
 }
 
@@ -205,12 +215,12 @@
  * amount of fuel, the number of sensor items (each one adds 25%), and the
  * minimum and maximum visibility limits in effect.
  */
-void Compute_sensor_range(player *pl)
+void Compute_sensor_range(Player * const pl)
 {
-    static int		init = 0;
-    static DFLOAT	EnergyRangeFactor;
+    static bool   init = true;
+    static DFLOAT EnergyRangeFactor;
 
-    if (!init) {
+    if (init) {
 	if (minVisibilityDistance <= 0.0)
 	    minVisibilityDistance = VISIBILITY_DISTANCE;
 	else
@@ -228,7 +238,7 @@
 	} else {
 	    EnergyRangeFactor = ENERGY_RANGE_FACTOR;
 	}
-	init = 1;
+	init = false;
     }
 
     pl->sensor_range = pl->fuel.sum * EnergyRangeFactor;
@@ -378,7 +388,7 @@
     pl->shot_mass	= ShotsMass;
     pl->shot_time	= 0;
     pl->color		= WHITE;
-    pl->score		= 0;
+    pl->score           = 0;
     pl->prev_score	= 0;
     pl->prev_check	= 0;
     pl->prev_round	= 0;
@@ -424,8 +434,13 @@
     pl->player_round	= 0;
     pl->player_count	= 0;
 
-    pl->kills		= 0;
-    pl->deaths		= 0;
+    pl->ClearKills();
+    pl->ClearDeaths();
+
+    pl->idleCount = 0;
+    pl->isAuthority = false;
+    pl->flooding = -1;
+    pl->grabbedBallFrame = -1;
 
     /*
      * If limited lives and if nobody has lost a life yet, you may enter
@@ -521,16 +536,16 @@
 
     for (j = 0; j < NumPlayers; j++) {
 	pl = Players[j];
-	if (pl->score != pl->prev_score
+	if (pl->GetScore() != pl->prev_score
 	    || pl->life != pl->prev_life
-	    || pl->mychar != pl->prev_mychar) {
-	    pl->prev_score = pl->score;
+	    || pl->GetCharacter() != pl->prev_mychar) {
+	    pl->prev_score = pl->GetScore();
 	    pl->prev_life = pl->life;
-	    pl->prev_mychar = pl->mychar;
+	    pl->prev_mychar = pl->GetCharacter();
 	    for (i = 0; i < NumPlayers; i++) {
 		if (Players[i]->conn != NOT_CONNECTED) {
 		    Send_score(Players[i]->conn, pl->id,
-			       pl->score, pl->life, pl->mychar);
+			       pl->GetScore(), pl->life, pl->mychar);
 		}
 	    }
 	}
@@ -576,12 +591,15 @@
 		    i--;
 		    continue;
 		}
+		pl->IgnoreLastDeath();
 	    }
 	}
 	CLR_BIT(pl->status, GAME_OVER);
 	CLR_BIT(pl->have, OBJ_BALL);
-	pl->kills = 0;
-	pl->deaths = 0;
+	pl->ClearKills();
+	pl->ClearDeaths();
+	if ( !BIT(pl->status, PAUSE) && pl->GetCharacter() != 'W' )
+	    pl->AddRound();
 	pl->round = 0;
 	pl->check = 0;
 	pl->time = 0;
@@ -589,16 +607,16 @@
 	pl->last_lap = 0;
 	pl->last_lap_time = 0;
 	if (!BIT(pl->status, PAUSE)) {
-	    pl->mychar = ' ';
+	    pl->SetCharacter(' ');
 	    pl->life = World.rules->lives;
 	    if (BIT(World.rules->mode, TIMING)) {
 		pl->count = RECOVERY_DELAY;
 	    }
 	}
 	if (IS_TANK_PTR(pl))
-	    pl->mychar = 'T';
+	    pl->SetCharacter('T');
 	else if (IS_ROBOT_PTR(pl))
-	    pl->mychar = 'R';
+	    pl->SetCharacter('R');
     }
     if (BIT(World.rules->mode, TEAM_PLAY)) {
 
@@ -742,8 +760,8 @@
 	       && Players[i]->count <= 0)) {
 	    continue;
 	}
-	*average_score += Players[i]->score;
-	ratio = (DFLOAT) Players[i]->kills / (Players[i]->deaths + 1);
+	*average_score += Players[i]->GetScore();
+	ratio = DFLOAT(Players[i]->GetKills() / (Players[i]->GetDeaths() + 1));
 	if (ratio > *best_ratio) {
 	    *best_ratio = ratio;
 	    best_players[0] = i;
@@ -776,8 +794,8 @@
 	sprintf(msg,
 		"%s is the Deadliest Player with a kill ratio of %d/%d.",
 		bp->name,
-		bp->kills, bp->deaths);
-	points = (int) (best_ratio * Rate(bp->score, average_score));
+		bp->GetKills(), bp->GetDeaths());
+	points = (int) (best_ratio * Rate(bp->GetScore(), average_score));
 	SCORE(best_players[0], points,
 	      OBJ_X_IN_BLOCKS(bp),
 	      OBJ_Y_IN_BLOCKS(bp),
@@ -787,7 +805,7 @@
 	msg[0] = '\0';
 	for (i = 0; i < num_best_players; i++) {
 	    player	*bp = Players[best_players[i]];
-	    int		ratio = Rate(bp->score, average_score);
+	    int		ratio = Rate(bp->GetScore(), average_score);
 	    DFLOAT	score = (DFLOAT) (ratio + num_best_players)
 				/ num_best_players;
 
@@ -814,8 +832,8 @@
 	}
 	sprintf(msg + strlen(msg),
 		" are the Deadly Players with kill ratios of %d/%d.",
-		Players[best_players[0]]->kills,
-		Players[best_players[0]]->deaths);
+		Players[best_players[0]]->GetKills(),
+		Players[best_players[0]]->GetDeaths());
     }
     Set_message(msg);
 }
@@ -825,8 +843,8 @@
     DFLOAT		ratio;
     int			points;
 
-    ratio = (DFLOAT) Players[ind]->kills / (Players[ind]->deaths + 1);
-    points = (int) (ratio * Rate(Players[ind]->score, average_score));
+    ratio = DFLOAT(Players[ind]->GetKills() / (Players[ind]->GetDeaths() + 1));
+    points = (int) (ratio * Rate(Players[ind]->GetScore(), average_score));
     SCORE(ind, points,
 	  OBJ_X_IN_BLOCKS(Players[ind]),
 	  OBJ_Y_IN_BLOCKS(Players[ind]),
@@ -881,8 +899,8 @@
 		|| (BIT(Players[i]->status, PAUSE)
 		    && Players[i]->count <= 0)
 		|| (BIT(Players[i]->status, GAME_OVER)
-		    && Players[i]->mychar == 'W'
-		    && Players[i]->score == 0)) {
+		    && Players[i]->GetCharacter() == 'W'
+		    && Players[i]->GetScore() == 0)) {
 		continue;
 	    }
 	    for (j = 0; j < num_best_players; j++) {
@@ -899,6 +917,8 @@
     Reset_all_players();
 
     free(best_players);
+
+    Rank_score(); Show_ranks();  // ranking
 }
 
 static void Individual_game_over(int winner)
@@ -995,8 +1015,8 @@
 	    if (IS_TANK_PTR(pl)) {
 		continue;
 	    }
-	    if (BIT(pl->status, PAUSE)
-		|| (BIT(pl->status, GAME_OVER) && pl->mychar == 'W')
+	    if ( pl->IsPaused() 
+		|| (BIT(pl->status, GAME_OVER) && pl->IsWaiting())
 		|| pl->best_lap <= 0) {
 		j = i;
 	    }
@@ -1005,9 +1025,9 @@
 		    if (pl->best_lap < Players[order[j]]->best_lap) {
 			break;
 		    }
-		    if (BIT(Players[order[j]]->status, PAUSE)
-			|| (BIT(Players[order[j]]->status, GAME_OVER)
-			    && Players[order[j]]->mychar == 'W')) {
+		    if ( Players[order[j]]->IsPaused()
+			 || (BIT(Players[order[j]]->status, GAME_OVER)
+			     && Players[order[j]]->IsWaiting()) ) {
 			break;
 		    }
 		}
@@ -1029,7 +1049,7 @@
 				  pl->home_base);
 		    }
 		}
-		if (BIT(pl->status, PAUSE)) {
+		if ( pl->IsPaused() ) {
 		    Go_home(order[i]);
 		}
 	    }
@@ -1040,9 +1060,9 @@
     for (i = NumPlayers - 1; i >= 0; i--)  {
 	pl = Players[i];
 	CLR_BIT(pl->status, RACE_OVER | FINISH);
-	if (BIT(pl->status, PAUSE)
-	    || (BIT(pl->status, GAME_OVER) && pl->mychar == 'W')
-	    || IS_TANK_PTR(pl)) {
+	if ( pl->IsPaused()
+	     || (BIT(pl->status, GAME_OVER) && pl->IsWaiting())
+	     || IS_TANK_PTR(pl) ) {
 	    continue;
 	}
 	num_active_players++;
@@ -1052,6 +1072,8 @@
 	    Kill_player(i);
 	else
 	    Player_death_reset(i);
+	pl->IgnoreLastDeath();
+
 	if (pl != Players[i]) {
 	    continue;
 	}
@@ -1068,15 +1090,15 @@
     if (bestlap > 0) {
 	for (i = 0; i < NumPlayers; i++)  {
 	    pl = Players[i];
-	    if (BIT(pl->status, PAUSE)
-		|| (BIT(pl->status, GAME_OVER) && pl->mychar == 'W')
-		|| IS_TANK_PTR(pl)) {
+	    if ( pl->IsPaused()
+		 || (BIT(pl->status, GAME_OVER) && pl->IsWaiting())
+		 || IS_TANK_PTR(pl)) {
 		continue;
 	    }
 	    if (pl->best_lap == bestlap) {
 		sprintf(msg,
 			"%s %s the best lap time of %.2fs",
-			pl->name,
+			pl->GetName(),
 			(num_best_players == 1) ? "had" : "shares",
 			(DFLOAT) bestlap / FPS);
 		Set_message(msg);
@@ -1100,10 +1122,6 @@
 
 void Compute_game_status(void)
 {
-    int			i;
-    player		*pl;
-    char		msg[MSG_LEN];
-
     if (BIT(World.rules->mode, TIMING)) {
 	/*
 	 * We need a completely separate scoring system for race mode.
@@ -1136,28 +1154,30 @@
 			total_pts, pts;
 
 	/* First count the players */
-	for (i = 0; i < NumPlayers; i++)  {
-	    pl = Players[i];
-	    if (BIT(pl->status, PAUSE)
-		|| IS_TANK_PTR(pl)) {
+	for ( int i = 0;
+	      i < NumPlayers;
+	      i++ ) {
+	    player * const pl = Players[i];
+	    if ( pl->IsPaused()
+		 || IS_TANK_PTR(pl) ) {
 		continue;
 	    }
-	    if (!BIT(pl->status, GAME_OVER)) {
+	    if ( !BIT(pl->status, GAME_OVER) ) {
 		num_alive_players++;
 	    }
-	    else if (pl->mychar == 'W') {
+	    else if ( pl->IsWaiting() ) {
 		num_waiting_players++;
 		continue;
 	    }
 
-	    if (BIT(pl->status, RACE_OVER)) {
+	    if ( BIT(pl->status, RACE_OVER) ) {
 		num_race_over_players++;
 		position++;
 	    }
-	    else if (BIT(pl->status, FINISH)) {
+	    else if ( BIT(pl->status, FINISH) ) {
 		num_finished_players++;
 	    }
-	    else if (!BIT(pl->status, GAME_OVER)) {
+	    else if ( !BIT(pl->status, GAME_OVER) ) {
 		alive = pl;
 	    }
 
@@ -1174,6 +1194,7 @@
 	    return;
 	}
 
+
 	/* Now if any players are unaccounted for */
 	if (num_finished_players > 0) {
 	    /*
@@ -1186,32 +1207,41 @@
 		sound_play_all(PLAYER_WIN_SOUND);
 
 	    total_pts = 0;
-	    for (i = 0; i < num_finished_players; i++) {
-		total_pts += (10 + 2 * num_active_players) >> (position - 1 + i);
+	    for ( int i = 0;
+		  i < num_finished_players;
+		  i++ ) {
+		total_pts += ((10 + 2 * num_active_players) >>
+			      (position - 1 + i));
 	    }
 	    pts = total_pts / num_finished_players;
 
-	    for (i = 0; i < NumPlayers; i++)  {
-		pl = Players[i];
-		if (BIT(pl->status, PAUSE)
-		    || (BIT(pl->status, GAME_OVER) && pl->mychar == 'W')
-		    || IS_TANK_PTR(pl)) {
+	    for ( int i = 0;
+		  i < NumPlayers;
+		  i++ ) {
+		player * const pl = Players[i];
+		if ( pl->IsPaused()
+		    || (BIT(pl->status, GAME_OVER) && pl->IsWaiting())
+		    || IS_TANK_PTR(pl) ) {
 		    continue;
 		}
 		if (BIT(pl->status, FINISH)) {
 		    CLR_BIT(pl->status, FINISH);
 		    SET_BIT(pl->status, RACE_OVER);
+
+		    char msg[MSG_LEN];
+	
 		    if (pts > 0) {
 			sprintf(msg,
 				"%s finishes %sin position %d "
 				"scoring %d point%s.",
-				pl->name,
+				pl->GetName(),
 				(num_finished_players == 1) ? "" : "jointly ",
 				position, pts,
 				(pts == 1) ? "" : "s");
 			Set_message(msg);
 			sprintf(msg, "[Position %d%s]", position,
-				(num_finished_players == 1) ? "" : " (jointly)");
+				(num_finished_players == 1) ? "" :
+				" (jointly)");
 			SCORE(i, pts,
 			      OBJ_X_IN_BLOCKS(pl),
 			      OBJ_Y_IN_BLOCKS(pl),
@@ -1220,7 +1250,7 @@
 		    else {
 			sprintf(msg,
 				"%s finishes %sin position %d.",
-				pl->name,
+				pl->GetName(),
 				(num_finished_players == 1) ? "" : "jointly ",
 				position);
 			Set_message(msg);
@@ -1265,11 +1295,11 @@
 	int	num_alive_teams = 0;
 	int	winning_team = -1;
 
-	for (i = 0; i < MAX_TEAMS; i++) {
+	for (int i = 0; i < MAX_TEAMS; i++) {
 	    team_state[i] = TeamEmpty;
 	}
 
-	for (i = 0; i < NumPlayers; i++) {
+	for (int i = 0; i < NumPlayers; i++) {
 	    if (IS_TANK_IND(i)) {
 		/* Ignore tanks. */
 		continue;
@@ -1312,6 +1342,8 @@
 	    }
 	}
 
+	char msg[MSG_LEN];
+
 	if (num_alive_teams > 1) {
 	    char	*bp;
 	    int		teams_with_treasure = 0;
@@ -1329,7 +1361,9 @@
 	     * destroys some treasures and then all quit, and the remaining
 	     * teams did not destroy any.
 	     */
-	    for (i = 0; i < MAX_TEAMS; i++) {
+	    for ( int i = 0;
+		  i < MAX_TEAMS;
+		  i++ ) {
 		team_score[i] = 0;
 		if (team_state[i] != TeamAlive) {
 		    team_win[i] = 0;
@@ -1342,20 +1376,17 @@
 		    teams_with_treasure++;
 	    }
 
-	    /*
-	     * Game is not over if more than one team has treasure.
-	     */
+	    // Game is not over if more than one team has treasure.
 	    if (teams_with_treasure > 1 || !max_destroyed)
 		return;
 
-	    /*
-	     * Find the winning team;
-	     *	Team destroying most number of treasures;
-	     *	If drawn; the one with most saved treasures,
-	     *	If drawn; the team with the most points,
-	     *	If drawn; an overall draw.
-	     */
-	    for (winners = i = 0; i < MAX_TEAMS; i++) {
+	    // Find the winning team;
+	    // Team destroying most number of treasures;
+	    // If drawn; the one with most saved treasures,
+	    // If drawn; the team with the most points,
+	    // If drawn; an overall draw.
+	    winners = 0;
+	    for (int i = 0; i < MAX_TEAMS; i++) {
 		if (!team_win[i])
 		    continue;
 		if (World.teams[i].TreasuresDestroyed == max_destroyed) {
@@ -1373,15 +1404,18 @@
 		return;
 	    }
 
-	    for (i = 0; i < NumPlayers; i++) {
-		if (BIT(Players[i]->status, PAUSE)
-		    || IS_TANK_IND(i)) {
+	    for (int i = 0;
+		 i < NumPlayers;
+		 i++) {
+		if ( Players[i]->IsPaused()
+		     || IS_TANK_IND(i)) {
 		    continue;
 		}
-		team_score[Players[i]->team] += Players[i]->score;
+		team_score[Players[i]->team] += Players[i]->GetScore();
 	    }
 
-	    for (winners = i = 0; i < MAX_TEAMS; i++) {
+	    winners = 0;
+	    for (int i = 0; i < MAX_TEAMS; i++) {
 		if (!team_win[i])
 		    continue;
 		if (World.teams[i].TreasuresLeft == max_left) {
@@ -1400,7 +1434,8 @@
 		return;
 	    }
 
-	    for (winners = i = 0; i < MAX_TEAMS; i++) {
+	    winners = 0;
+	    for (int i = 0; i < MAX_TEAMS; i++) {
 		if (!team_win[i])
 		    continue;
 		if (team_score[i] == max_score) {
@@ -1422,7 +1457,7 @@
 
 	    sprintf(msg, " between teams ");
 	    bp = msg + strlen(msg);
-	    for (i = 0; i < MAX_TEAMS; i++) {
+	    for (int i = 0; i < MAX_TEAMS; i++) {
 		if (!team_win[i])
 		    continue;
 		*bp++ = "0123456789"[i]; *bp++ = ','; *bp++ = ' ';
@@ -1465,7 +1500,7 @@
 	int num_active_humans = 0;
 	int winner = -1;
 
-	for (i=0; i<NumPlayers; i++)  {
+	for (int i=0; i<NumPlayers; i++)  {
 	    if (BIT(Players[i]->status, PAUSE)
 		|| IS_TANK_IND(i)) {
 		continue;
@@ -1554,6 +1589,14 @@
 	NumPseudoPlayers--;
     }
 
+    else if ( IS_HUMAN_PTR(pl) ) { // ranking
+	Save_score(*pl);
+	if ( NumPlayers == NumRobots + NumPseudoPlayers ) {
+	    Rank_score();
+	    Print_saved_scores();
+	}
+    }
+
     if (pl->team != TEAM_NOT_SET && !IS_TANK_PTR(pl))
 	World.teams[pl->team].NumMembers--;
 
@@ -1620,18 +1663,21 @@
     release_ID(id);
 }
 
-void Detach_ball(int ind, int obj)
+void
+Player::DetachBall(const int obj)
 {
+    Player & pl = *this;
+
     int			i, cnt;
 
-    if (obj == -1 || Obj[obj] == Players[ind]->ball) {
-	Players[ind]->ball = NULL;
-	CLR_BIT(Players[ind]->used, OBJ_CONNECTOR);
+    if (obj == -1 || Obj[obj] == pl.ball) {
+	pl.ball = NULL;
+	CLR_BIT(pl.used, OBJ_CONNECTOR);
     }
 
-    if (BIT(Players[ind]->have, OBJ_BALL)) {
+    if (BIT(pl.have, OBJ_BALL)) {
 	for (cnt = i = 0; i < NumObjs; i++) {
-	    if (Obj[i]->type == OBJ_BALL && Obj[i]->id == Players[ind]->id) {
+	    if (Obj[i]->type == OBJ_BALL && Obj[i]->id == pl.id) {
 		if (obj == -1 || obj == i) {
 		    Obj[i]->id = -1;
 		    /* Don't reset owner so you can throw balls */
@@ -1641,33 +1687,53 @@
 	    }
 	}
 	if (cnt == 0)
-	    CLR_BIT(Players[ind]->have, OBJ_BALL);
+	    CLR_BIT(pl.have, OBJ_BALL);
 	else {
-	    sound_play_sensors(Players[ind]->pos.x, Players[ind]->pos.y, DROP_BALL_SOUND);
+	    sound_play_sensors(pl.pos.x, pl.pos.y, DROP_BALL_SOUND);
 	}
     }
+
 }
 
-void Kill_player(int ind)
+void Kill_player(const int ind)
 {
-    Explode_fighter(ind);
+    // Explode fighter
+    Player & pl = *Players[ind];
+
+    sound_play_sensors(pl.pos.x, pl.pos.y, PLAYER_EXPLOSION_SOUND);
+
+    const int min_debris = int(1 + (pl.fuel.sum / (8.0 * FUEL_SCALE_FACT)));
+    const int max_debris = int(min_debris + (pl.mass * 2.0));
+
+    Make_debris(
+	/* pos.x, pos.y   */ pl.pos.x, pl.pos.y,
+	/* vel.x, vel.y   */ pl.vel.x, pl.vel.y,
+	/* owner id       */ pl.Id(),
+	/* kind           */ OBJ_DEBRIS,
+	/* mass           */ 3.5,
+	/* status         */ GRAVITY,
+	/* color          */ RED,
+	/* radius         */ 8,
+	/* min,max debris */ min_debris, max_debris,
+	/* min,max dir    */ 0, RES-1,
+	/* min,max speed  */ 20.0, 20 + ((int(pl.mass))>>1),
+	/* min,max life   */ 5, int(5 + (pl.mass * 1.5))
+	);
+
     Player_death_reset(ind);
 }
 
-void Player_death_reset(int ind)
+void Player_death_reset(const int ind)
 {
-    player		*pl = Players[ind];
-    long		minfuel;
-    int			i;
-
+    Player * const pl = Players[ind];
 
     if (IS_TANK_PTR(pl)) {
 	Delete_player(ind);
 	return;
     }
 
-    Detach_ball(ind, -1);
-    if (BIT(pl->used, OBJ_AUTOPILOT) || BIT(pl->status, HOVERPAUSE)) {
+    pl->DetachAllBalls();
+    if ( pl->IsUsing(OBJ_AUTOPILOT) || BIT(pl->status, HOVERPAUSE)) {
 	CLR_BIT(pl->status, HOVERPAUSE);
 	Autopilot (ind, 0);
     }
@@ -1678,7 +1744,9 @@
     pl->status		|= DEF_BITS;
     pl->status		&= ~(KILL_BITS);
 
-    for (i = 0; i < NUM_ITEMS; i++) {
+    for ( int i = 0;
+	  i < NUM_ITEMS;
+	  i++ ) {
 	if (!BIT(1U << i, ITEM_BIT_FUEL | ITEM_BIT_TANK)) {
 	    pl->item[i] = World.items[i].initial;
 	}
@@ -1698,9 +1766,9 @@
     pl->damaged 	= 0;
     pl->lock.distance	= 0;
 
-    pl->fuel.sum       	= (long)(pl->fuel.sum*0.90);		/* Loose 10% of fuel */
-    minfuel		= (World.items[ITEM_FUEL].initial * FUEL_SCALE_FACT);
-    minfuel		+= (rand() % (1 + minfuel) / 5);
+    pl->fuel.sum       	= (long) (pl->fuel.sum*0.90); // Loose 10% of fuel
+    const long minfuel  = ((World.items[ITEM_FUEL].initial * FUEL_SCALE_FACT) +
+			   (rand() % (1 + minfuel) / 5));
     pl->fuel.sum	= MAX(pl->fuel.sum, minfuel);
     Player_init_fuel(ind, pl->fuel.sum);
 
@@ -1727,13 +1795,13 @@
 	pl->life--;
 
 	if (pl->life == -1) {
-	    if(!IS_ROBOT_PTR(pl) ||
-	       (BIT(World.rules->mode, TIMING) &&
-		(pl->score >= robotLeaveScore || !robotsLeave))) {
+	    if( !IS_ROBOT_PTR(pl) ||
+	        (BIT(World.rules->mode, TIMING) &&
+		 (pl->GetScore() >= robotLeaveScore || !robotsLeave))) {
 		pl->life = 0;
 		SET_BIT(pl->status, GAME_OVER);
 		Player_lock_closest(ind, 0);
-		pl->mychar = 'D';
+		pl->SetCharacter('D');
 	    }
 	    else {
 		Robot_delete(ind,false);
@@ -1745,10 +1813,140 @@
 	pl->life++;
     }
 
-    pl->deaths++;
+    pl->AddDeath();
 
     pl->have	= DEF_HAVE;
     pl->used	|= DEF_USED;
     pl->used	&= ~(USED_KILL);
     pl->used	&= pl->have;
+}
+
+void
+player::UseTractorBeam()
+{
+    if ( IsUsing(OBJ_TRACTOR_BEAM) ) {
+	/*
+	 * Do tractor beam attraction between two players, where `pl' is doing
+	 * the tractor beam and `to' is the target.
+	 */
+
+	player * const pl = this;
+
+	const DFLOAT maxdist = TRACTOR_MAX_RANGE(pl);
+	
+	if (BIT(pl->lock.tagged, (LOCK_PLAYER|LOCK_VISIBLE))
+	    != (LOCK_PLAYER|LOCK_VISIBLE)
+	    || lock.distance >= maxdist) {
+	    pl->tractor = NULL;
+	    return;
+	}
+	
+	const DFLOAT maxforce = TRACTOR_MAX_FORCE(pl);
+	const DFLOAT percent  = TRACTOR_PERCENT(pl, maxdist);
+	const DFLOAT force    = TRACTOR_FORCE(pl, percent, maxforce);
+	const long   cost     = (long) TRACTOR_COST(percent);
+	
+	// Stop if we're out of fuel.
+	if (pl->fuel.sum < -cost) {
+	    pl->tractor = NULL;
+	    CLR_BIT(pl->used, OBJ_TRACTOR_BEAM);
+	    return;
+	}
+	
+	sound_play_sensors(pl->pos.x, pl->pos.y,
+			   (force < 0) ? TRACTOR_BEAM_SOUND :
+			   PRESSOR_BEAM_SOUND);
+	
+	// Find the guy at the other end.
+	player * const victim = Players[GetInd[pl->lock.pl_id]];
+	pl->tractor = victim;
+
+	Add_fuel(&(pl->fuel), cost);
+	
+	const DFLOAT xd   = WRAP_DX(pl->pos.x - victim->pos.x);
+	const DFLOAT yd   = WRAP_DY(pl->pos.y - victim->pos.y);
+	const DFLOAT mass = pl->mass + victim->mass;
+
+	const int theta = (int) findDir(xd, yd);
+
+	// Adjust my velocity.
+	DFLOAT dvx = tcos(theta) * (force / pl->mass);
+	DFLOAT dvy = tsin(theta) * (force / pl->mass);
+	pl->vel.x += dvx;
+	pl->vel.y += dvy;
+	Record_shove(pl, victim, frame_loops);
+
+	// Adjust his/her velocity.
+	dvx = -(tcos(theta) * (force / victim->mass));
+	dvy = -(tsin(theta) * (force / victim->mass));
+	victim->vel.x += dvx;
+	victim->vel.y += dvy;
+	Record_shove(victim, pl, frame_loops);
+    
+    } else
+	// Not using the tractor beam now.
+	tractor = 0;
+}
+
+void
+player::Pause()
+{
+    if ( !IsPaused() ) {
+	DetachAllBalls();
+	count = 10*FPS;
+	updateVisibility = 1;
+	CLR_BIT(status, SELF_DESTRUCT|PLAYING);
+	SET_BIT(status, PAUSE);
+	mychar = 'P';
+	updateScores = true;
+	scorenode->SetLogoutMessage("paused");
+    }
+}
+
+void
+player::Unpause()
+{
+    if ( IsPaused() && count <= 0 ) {
+	
+	const int ind = GetInd[id];
+
+	idleCount = 0;
+	CLR_BIT(status, PAUSE);
+	updateScores = true;
+
+	if (BIT(mode, LIMITED_LIVES)) {
+	    bool toolate = false;
+	    for (int i = 0;
+		 i < NumPlayers;
+		 i++) {
+		// If a non-team member has lost a life,
+		// then it's too late to join.
+		if ( i != ind &&
+		     Players[i]->life < World.rules->lives &&
+		     !TEAM(ind, i) ) {
+		    toolate = true;
+		    break;
+		}
+	    }
+	    if (toolate) {
+		life = 0;
+		mychar = 'W';
+		SET_BIT(status, GAME_OVER);
+	    } else {
+		mychar = ' ';
+		Go_home(ind);
+		SET_BIT(status, PLAYING);
+		life = World.rules->lives;
+	    }
+	}
+	if (BIT(World.rules->mode, TIMING)) {
+	    round         = 0;
+	    check         = 0;
+	    time          = 0;
+	    best_lap      = 0;
+	    last_lap      = 0;
+	    last_lap_time = 0;
+	}
+	scorenode->SetLogoutMessage("playing");
+    }
 }
diff -urN xpilot-3.7.1/src/proto.h xpilot-3.7.1gurk/src/proto.h
--- xpilot-3.7.1/src/proto.h	Tue Jan 27 12:38:59 1998
+++ xpilot-3.7.1gurk/src/proto.h	Wed May  6 02:30:53 1998
@@ -239,6 +239,8 @@
 void Frame_update(void);
 void Set_message(const char *message);
 void Set_player_message(player *pl, char *message);
+void SendMessageToAll(const char format[], ...);
+void SendMessageToPlayer(Player *, const char format[], ...);
 
 /*
  * Prototypes for update.c
diff -urN xpilot-3.7.1/src/robot.c xpilot-3.7.1gurk/src/robot.c
--- xpilot-3.7.1/src/robot.c	Tue Jan 27 12:39:00 1998
+++ xpilot-3.7.1gurk/src/robot.c	Fri Feb 13 17:06:23 1998
@@ -843,9 +843,9 @@
 	    if (!IS_ROBOT_IND(i))
 		continue;
 
-	    if (Players[i]->score < low_score) {
+	    if (Players[i]->GetScore() < low_score) {
 		low_i = i;
-		low_score = Players[i]->score;
+		low_score = Players[i]->GetScore();
 	    }
 	}
 	if (low_i >= 0) {
@@ -938,7 +938,7 @@
     }
 
     if (IS_ROBOT_PTR(pl)
-	&& rand()%100 < kp->score - pl->score
+	&& rand()%100 < kp->GetScore() - pl->GetScore()
 	&& !(BIT(World.rules->mode, TEAM_PLAY) && pl->team == kp->team)) {
 
 	Robot_talks(ROBOT_TALK_WAR, pl->name, kp->name);
@@ -1061,10 +1061,10 @@
 	    if (robotLeaveLife > 0 && pl->life >= robotLeaveLife) {
 		sprintf(msg, "%s retired.", pl->name);
 	    }
-	    else if (robotLeaveScore != 0 && pl->score < robotLeaveScore) {
+	    else if (robotLeaveScore != 0 && pl->GetScore() < robotLeaveScore) {
 		sprintf(msg, "%s left out of disappointment.", pl->name);
 	    }
-	    else if (robotLeaveRatio != 0 && pl->score / (pl->life + 1)
+	    else if (robotLeaveRatio != 0 && pl->GetScore() / (pl->life + 1)
 		    < robotLeaveRatio) {
 		sprintf(msg, "%s played too badly.", pl->name);
 	    }
diff -urN xpilot-3.7.1/src/server.c xpilot-3.7.1gurk/src/server.c
--- xpilot-3.7.1/src/server.c	Tue Jan 27 12:39:00 1998
+++ xpilot-3.7.1gurk/src/server.c	Sun Apr 19 22:06:37 1998
@@ -67,6 +67,7 @@
 #include "netserver.h"
 #include "error.h"
 #include "portability.h"
+#include "rank.h"
 
 char server_version[] = VERSION;
 
@@ -149,6 +150,8 @@
 
     Robot_init();
 
+    Init_saved_scores(); // ranking
+
     if (BIT(World.rules->mode, TEAM_PLAY)) {
 	int i;
 	for (i=0; i < World.NumTreasures; i++)
@@ -160,6 +163,10 @@
      * Get server's official name.
      */
     GetLocalHostName(Server.host, sizeof Server.host);
+    for ( int i = 0;
+	  i < strlen(Server.host);
+	  i++ )
+	Server.host[i] = tolower(Server.host[i]);
 
     Get_login_name(Server.name, sizeof Server.name);
 
@@ -303,6 +310,9 @@
 
     Contact_cleanup();
 
+    Rank_score();  // ranking
+    Print_saved_scores();
+
     Free_players();
     Free_shots();
     Free_map();
@@ -366,7 +376,7 @@
 	    playing_teams++;
 	}
 	if (IS_HUMAN_PTR(pl)) {
-	    team_score[i] += pl->score;
+	    team_score[i] += pl->GetScore();
 	}
     }
     if (playing_teams <= 1) {
@@ -464,9 +474,9 @@
     for (i=0; i<NumPlayers; i++) {
 	pl = Players[i];
 	if (BIT(pl->mode, LIMITED_LIVES)) {
-	    ratio = (DFLOAT) pl->score;
+	    ratio = (DFLOAT) pl->GetScore();
 	} else {
-	    ratio = (DFLOAT) pl->score / (pl->life + 1);
+	    ratio = (DFLOAT) pl->GetScore() / (pl->life + 1);
 	}
 	if ((best == NULL
 		|| ratio > best_ratio)
@@ -475,7 +485,7 @@
 	    best = pl;
 	}
 	for (j = 0; j < i; j++) {
-	    if (order[j]->score < pl->score) {
+	    if (order[j]->GetScore() < pl->GetScore()) {
 		for (k = i; k > j; k--) {
 		    order[k] = order[k - 1];
 		}
@@ -496,9 +506,9 @@
 	    }
 	}
 	sprintf(lblstr, "%c%c %-19s%03d%6d",
-		(pl == best) ? '*' : pl->mychar,
+		(pl == best) ? '*' : pl->GetCharacter(),
 		(pl->team == TEAM_NOT_SET) ? ' ' : pl->team+'0',
-		name, pl->life, (int)pl->score);
+		name, pl->life, (int)pl->GetScore());
 	sprintf(msg, "%2d... %-36s%s@%s\n",
 		i+1, lblstr, pl->realname,
 		IS_HUMAN_PTR(pl)
@@ -608,7 +618,7 @@
 		if (teamscore[team] == 1234567) {
 		    teamscore[team] = 0;
 		}
-		teamscore[team] += Players[i]->score;
+		teamscore[team] += Players[i]->GetScore();
 	    }
 	}
 
@@ -645,12 +655,12 @@
     for (i = 0; i < NumPlayers; i++) {
 	SET_BIT(Players[i]->status, GAME_OVER);
 	if (IS_HUMAN_IND(i)) {
-	    if (Players[i]->score > maxsc) {
-		maxsc = Players[i]->score;
+	    if (Players[i]->GetScore() > maxsc) {
+		maxsc = Players[i]->GetScore();
 		win = i;
 	    }
-	    if (Players[i]->score < minsc) {
-		minsc = Players[i]->score;
+	    if (Players[i]->GetScore() < minsc) {
+		minsc = Players[i]->GetScore();
 		loose = i;
 	    }
 	}
diff -urN xpilot-3.7.1/src/update.c xpilot-3.7.1gurk/src/update.c
--- xpilot-3.7.1/src/update.c	Tue Jan 27 12:39:00 1998
+++ xpilot-3.7.1gurk/src/update.c	Tue Jun 23 19:07:35 1998
@@ -38,10 +38,12 @@
 #include "global.h"
 #include "proto.h"
 #include "map.h"
+#include "netserver.h"
 #include "score.h"
 #include "bit.h"
 #include "saudio.h"
 #include "objpos.h"
+#include "rank.h"
 
 char update_version[] = VERSION;
 
@@ -362,66 +364,6 @@
     }
 }
 
-/*
- * Do tractor beam attraction between two players, where `pl' is doing
- * the tractor beam and `to' is the target.
- */
-static void Tractor_beam (player *pl)
-{
-    player		*victim;
-    DFLOAT		maxdist, maxforce, percent;
-    DFLOAT		xd, yd;
-    DFLOAT		dvx, dvy;
-    DFLOAT		force, mass;
-    long		cost;
-    int			theta;
-
-    maxdist = TRACTOR_MAX_RANGE(pl);
-
-    if (BIT(pl->lock.tagged, (LOCK_PLAYER|LOCK_VISIBLE))
-		!= (LOCK_PLAYER|LOCK_VISIBLE)
-	|| pl->lock.distance >= maxdist) {
-	pl->tractor = NULL;
-	return;
-    }
-
-    maxforce = TRACTOR_MAX_FORCE(pl);
-    percent = TRACTOR_PERCENT(pl, maxdist);
-    cost = (long)TRACTOR_COST(percent);
-    force = TRACTOR_FORCE(pl, percent, maxforce);
-
-    if (pl->fuel.sum < -cost) {
-	pl->tractor = NULL;
-	CLR_BIT(pl->used, OBJ_TRACTOR_BEAM);
-	return;
-    }
-
-    sound_play_sensors(pl->pos.x, pl->pos.y,
-		       (force < 0) ? TRACTOR_BEAM_SOUND : PRESSOR_BEAM_SOUND);
-
-    victim = pl->tractor = Players[GetInd[pl->lock.pl_id]];
-
-    Add_fuel(&(pl->fuel), cost);
-
-    xd = WRAP_DX(pl->pos.x - victim->pos.x);
-    yd = WRAP_DY(pl->pos.y - victim->pos.y);
-
-    theta = (int)findDir(xd, yd);
-    mass = pl->mass + victim->mass;
-
-    dvx = tcos(theta) * (force / pl->mass);
-    dvy = tsin(theta) * (force / pl->mass);
-    pl->vel.x += dvx;
-    pl->vel.y += dvy;
-    Record_shove(pl, victim, frame_loops);
-
-    dvx = -(tcos(theta) * (force / victim->mass));
-    dvy = -(tsin(theta) * (force / victim->mass));
-    victim->vel.x += dvx;
-    victim->vel.y += dvy;
-    Record_shove(victim, pl, frame_loops);
-}
-
 /********** **********
  * Updating objects and the like.
  */
@@ -605,10 +547,39 @@
 	    }
 	}
 
+	if ( pl->flooding > FPS + 1 ) {
+	    char msg[MSG_LEN];
+	    sprintf(msg, "%s was kicked out because of flooding.", pl->name);
+	    Destroy_connection(pl->conn, "flooding");
+	    i--;
+	    continue;
+	} else if ( pl->flooding >= 0 )
+	    pl->flooding--;
+
+#define IDLETHRESHOLD (FPS * 60)
+
+	if ( IS_HUMAN_PTR(pl) ) {
+	    pl->scorenode->score = pl->score;
+	    if ( pl->mychar == ' ' ) {
+		if ( pl->idleCount++ == IDLETHRESHOLD )
+		    if ( NumPlayers - 1 > NumPseudoPlayers + NumRobots ) {
+			// Kill player, he/she will be paused when returned
+			// to base, unless he/she wakes up.
+			Kill_player(i);
+			pl->IgnoreLastDeath();
+		    } else
+			pl->idleCount = 0;
+	    }
+	}
+
 	if (pl->count == 0) {
 	    pl->count = -1;
 
 	    if (!BIT(pl->status, PLAYING)) {
+		if ( pl->idleCount >= IDLETHRESHOLD ) { // idle
+		    Pause_player(i, 1);
+		    continue;
+		}
 		SET_BIT(pl->status, PLAYING);
 		Go_home(i);
 	    }
@@ -941,6 +912,7 @@
 	    SET_BIT(pl->status, WARPED);
 
 	    sound_play_sensors(pl->pos.x, pl->pos.y, WORM_HOLE_SOUND);
+
 	}
 
 	/*
@@ -997,22 +969,24 @@
 	   --World.wormHoles[i].countdown;
 
     for (i = 0; i < NumPlayers; i++) {
-	player *pl = Players[i];
+	player * const pl = Players[i];
 
-	pl->updateVisibility = 0;
+//	pl->updateVisibility = 0;
 
-	if (pl->forceVisible) {
+	pl->UpdateForcedVisibility();
+/*	if (pl->forceVisible) {
 	    pl->forceVisible--;
 
 	    if (!pl->forceVisible)
 		pl->updateVisibility = 1;
 	}
-
-	if (BIT(pl->used, OBJ_TRACTOR_BEAM))
+	*/
+	pl->UseTractorBeam();
+/*	if (BIT(pl->used, OBJ_TRACTOR_BEAM))
 	    Tractor_beam(pl);
 	else
 	    pl->tractor = NULL;
-    }
+	    */ }
 
     /*
      * Checking for collision, updating score etc. (see collision.c)
diff -urN xpilot-3.7.1/src/walls.c xpilot-3.7.1gurk/src/walls.c
--- xpilot-3.7.1/src/walls.c	Tue Jan 27 12:39:02 1998
+++ xpilot-3.7.1gurk/src/walls.c	Mon Oct 26 13:38:28 1998
@@ -977,6 +977,7 @@
 			  tt->pos.x, tt->pos.y, "Treasure: ");
 		    sprintf(msg, " < %s (team %d) has replaced the treasure >",
 			    pl->name, pl->team);
+		    pl->SavedBall();
 		    Set_message(msg);
 		    break;
 		}
@@ -984,8 +985,8 @@
 		if (mi->obj->owner == -1) {
 		    return;
 		}
-		if (World.treasures[ms->treasure].team ==
-			Players[GetInd[mi->obj->owner]]->team) {
+		player * const pl = Players[GetInd[mi->obj->owner]];
+		if ( World.treasures[ms->treasure].team == pl->team) {
 		    /*
 		     * Ball has been brought back to home treasure.
 		     * The team should be punished.
@@ -1716,7 +1717,7 @@
     Set_message(msg);
 
     if (targetKillTeam) {
-	Players[killer]->kills++;
+	Players[killer]->AddTargetKill();
     }
 
     sc  = Rate(win_score, lose_score);
