From: trown@ecst.csuchico.edu (Nick Trown)
Newsgroups: rec.games.netrek
Subject: Re: Hosers roasting on an open fire...
Keywords: Gerbil toastie
Date: 20 Dec 92 21:41:24 GMT
Organization: California State University, Chico
NNTP-Posting-Host: guzzler.ecst.csuchico.edu

In article <1h04vaINN4ks@charnel.ecst.csuchico.edu> trown@ecst.csuchico.edu (Nick Trown) writes:
>In article <1992Dec17.213024@cs.bham.ac.uk> aqw@cs.bham.ac.uk (Andy Williams) writes:
>	Since I see a need for this and noticed that this has happen even on
>guzzler I think I have an alternative. The whole problem arises when a player
>causes an artifical t-mode. So when the server goes into t-mode have the server
>check to see if there are more than 2 players with the same login. If this
>happens then have the server send mail to god showing the player list. The
>god can then decide on a course of action if necessary. The server could also
>inform the offending player(s) that a message has been sent to god.
>

I went ahead and made a function so the servers can check the player list when
t-mode occurs. At present it doesn't mail the god but instead put the info
in a file that the god can later view. The info is basically the same output 
that the players tool makes. Please let me know of an problems, comments, etc.

Nick Trown
Netherworld
Server God of Guzzler




In defs.h add the following line:

#define SCUM_FILE	"/h/h/games/hp700/lib/ntserv/god.LOG"




In daemonII.c in the function move() make this change:

if (tournamentMode()) {
        if (!oldtourn) {
            check_scummers();		/* check for scummers.  NBT */
            switch(oldmessage=(random() % 8)) {




Then add the following function before move() in daemonII.c:

/* Check the player list for possible t-mode scummers.
   Nick Trown	12/19/92
*/

void check_scummers()
{
    int i, j;
    int num;
    FILE *fp;

    for (i=0; i<MAXPLAYER; i++) {
	num=0;
	if (players[i].p_status == PFREE || players[i].p_stats.st_tticks==0)
            continue;

	for (j=i+1; j<MAXPLAYER; j++) {
	    if (strcmp(players[i].p_login,players[j].p_login)==0) {
		num++;
	    }
	}
	if (num>1){
	    pmessage("Possible t-mode scummers have been found.",0,MALL,"GOD->ALL");
	    pmessage("They have been noted for god to review.",0,MALL,"GOD->ALL");
	    if ((fp = fopen (SCUM_FILE,"a"))==NULL) {
		fprintf(stderr,"Unable to open scum file.\n");
		return;
	    }
	    fprintf(fp,"POSSIBLE T-MODE SCUMMERS FOUND!!!\n");
	    for (j=0; j<MAXPLAYER; j++) {
	        fprintf(fp,"  %2s: %-16s   %6.2f %6d %7d %6d %6d\n",
                    players[j].p_mapchars, players[j].p_name,
                    players[j].p_kills,
                    players[j].p_damage,
                    players[j].p_shield,
                    players[j].p_armies,
                    players[j].p_fuel);
	    }
	    fclose(fp);
	    return;
	}
    }
		 
}

void move()
{
    static int oldtourn=0;
    static int oldmessage;

    if (++ticks == dietime) {/* no player for 1 minute. kill self */
	if (debug) {
	    fprintf(stderr,"Ho hum.  1 minute, no activity...\n");
	}
	else {
	    fprintf(stderr, "Self-destructing the daemon!\n");
	    freemem(0, 0, NULL);
	}
    }
    if (update_sys_defaults()) {
	/* This message does 2 things:
	 * First, it tells the players that the system defaults have in fact
	 *  changed.
	 * Secondly, it triggers the ntserv processes to check the new defaults.
	 */
	pmessage("Loading new server configuration.", 0, MALL, "GOD->ALL");
    }
    if (tournamentMode()) {
	if (!oldtourn) {
	    check_scummers();
	    switch(oldmessage=(random() % 8)) {
	    case 0:
		pmessage("A dark mood settles upon the galaxy", 0, MALL, "GOD->ALL");
		break;
	    case 1:
		pmessage("Political pressure to expand is rising", 0, MALL, "GOD->ALL");
		break;
	    case 2:
		pmessage("Border disputes break out as political tensions increase!", 0, MALL, "GOD->ALL");
		break;
	    case 3:
		pmessage("Galactic superpowers seek to colonize new territory!", 0, MALL, "GOD->ALL");
		break;
	    case 4:
		pmessage("'Manifest Destiny' becomes motive in galactic superpower conflict!", 0, MALL, "GOD->ALL");
		break;
	    case 5:
		pmessage("Diplomat insults foriegn emperor's mother and fighting breaks out!", 0, MALL, "GOD->ALL");
		break;
	    case 6:
		pmessage("Dan Quayle declares self as galactic emperor and chaos breaks out!", 0, MALL, "GOD->ALL");
		break;
	    default:
		pmessage("Peace parties have been demobilized, and fighting ensues.", 0, MALL, "GOD->ALL");
		break;
	    }
	}
	oldtourn=1;
	status->tourn=1;
	status->time++;
	tourntimestamp = ticks;
    } else {
	if (oldtourn) {
	    tourntimestamp = ticks; /* record end of Tmode 8/2/91 TC */
	    switch(oldmessage) {
	    case 0:
		pmessage("A new day dawns as the oppressive mood lifts", 0, MALL, "GOD->ALL");
		break;
	    case 1:
		pmessage("The expansionist pressure subsides", 0, MALL, "GOD->ALL");
		break;
	    case 2:
		pmessage("Temporary agreement is reached in local border disputes.", 0, MALL, "GOD->ALL");
		break;
	    case 3:
		pmessage("Galactic governments reduce colonization efforts.", 0, MALL, "GOD->ALL");
		break;
	    case 4:
		pmessage("'Manifest Destiny is no longer a fad.' says influential philosopher.", 0, MALL, "GOD->ALL");
		break;
	    case 5:
		pmessage("Diplomat apologizes to foreign emperor's mother and invasion is stopped!", 0, MALL, "GOD->ALL");
		break;
	    case 6:
		pmessage("Dan Quayle is locked up and order returns to the galaxy!", 0, MALL, "GOD->ALL");
		break;
	    default:
		pmessage("The peace party has reformed, and is rallying for peace", 0, MALL, "GOD->ALL");
		break;
	    }
	}
	oldtourn=0;
	status->tourn=0;
    }
    if (fuse(PLAYERFUSE)) {
	udplayers();
    }
    if (fuse(TORPFUSE)) {
	udtorps();
    }
    if (fuse(PLASMAFUSE)) {
        udplasmatorps();
    }
    if (fuse(PHASERFUSE)) {
	udphaser();
    }
    if (fuse(CLOAKFUSE)) {
	udcloak();
    }
    if (fuse(TEAMFUSE)) {
	teamtimers();
    }
    if (fuse(PLFIGHTFUSE)) {
	plfight();
    }
    if (fuse(BEAMFUSE)) {
	beam();
    }
   
    if (planet_move && fuse(PMOVEFUSE)) {/* call planet movement 10/10/91 TC */
	pmove();			 /* modified NBT 9/28/92 */
    }

    if (fuse(SYNCFUSE)) {
	save_planets();
    }
    if ((!topgun && fuse(HOSEFUSE)) ||
	(topgun && fuse(HOSEFUSE2))) {
				/* change 5/20/91 TC: add the HK bot */
	rescue(HUNTERKILLER, 0); /* no team, no target */
    }
    if (fuse(PLANETFUSE)) {
	udplanets();
    }
    if (fuse(MINUTEFUSE)) {	/* was SURRENDFUSE 4/15/92 TC */
	udsurrend();
    }
    if (fuse(CHECKLOADFUSE)) {
	/*check_load(); this causes blocked pipes when too many processes */
	/* are running */
    }
}

udplayers()
{
    register int i, k;
    register struct player *j;
    float dist;	/* used by tractor beams */
    int maxspeed;

    nplayers = 0;
    tcount[FED] = tcount[ROM] = tcount[KLI] = tcount[ORI] = 0;
    for (i = status->active = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	switch (j->p_status) {
	    case POUTFIT:
		if (++(j->p_ghostbuster) > OUTFITTIME) {
		    saveplayer(j);
		    j->p_status = PFREE;
		}
		continue;
	    case PFREE:
		nplayers++;
		j->p_ghostbuster = 0;	/* stop from hosing new players */
		continue;
	    case PDEAD:
		if (--j->p_explode <= 0) { 	/* Ghost Buster */
		    saveplayer(j);
		    j->p_status = POUTFIT; /* change 5/24/91 TC, was PFREE */
		}
		continue;
	    case PEXPLODE:
		j->p_updates++;
		j->p_flags &= ~PFCLOAK;
		if ((j->p_ship.s_type == STARBASE) && 
		   (j->p_explode == ((2*SBEXPVIEWS-3)/PLAYERFUSE)))
			blowup(j);  /* damdiffe everyone else around */
		else if (j->p_explode == ((10-3)/PLAYERFUSE))
			blowup(j);	/* damdiffe everyone else around */

		if (--j->p_explode <= 0) {
		    j->p_status = PDEAD;
		    j->p_explode = 600/PLAYERFUSE; /* set ghost buster */
		}

		/* At this point, docked ships become removed. */
		if (j->p_flags & PFDOCK) {
		    players[j->p_docked].p_docked--;
		    players[j->p_docked].p_port[j->p_port[0]] = VACANT;
		    j->p_flags &= ~PFDOCK;
		} 
		if (j->p_ship.s_type == STARBASE) {
		    if ((j->p_whydead == KSHIP) || 
			    (j->p_whydead == KTORP) ||
		            (j->p_whydead == KPHASER) || 
			    (j->p_whydead == KPLASMA) ||
			    (j->p_whydead == KPLANET) ||
			    (j->p_whydead == KGENOCIDE))
			teams[j->p_team].s_turns=BUILD_SB_TIME;	/* in defs.h */
			/* 30 minute reconstruction period for new starbase */
		    for (k=0; k<NUMPORTS; k++) 
			if(j->p_port[k] != VACANT) {
			    players[j->p_port[k]].p_flags &= ~PFDOCK;
			    j->p_port[k] = VACANT;
			}
		    j->p_docked = 0;
		}
		/* And he is ejected from orbit. */
		if (j->p_flags & PFORBIT)
		  j->p_flags &= ~PFORBIT;

		/* Fall through to alive so explosions move */
			
	    case PALIVE:
		/* Move Player in orbit */
		if ((j->p_flags & PFORBIT) && !(j->p_flags & PFDOCK)) {
		    j->p_dir += 2;
		    j->p_desdir = j->p_dir;
		    j->p_x = planets[j->p_planet].pl_x + ORBDIST
			* Cos[(unsigned char) (j->p_dir - (unsigned char) 64)];
		    j->p_y = planets[j->p_planet].pl_y + ORBDIST
			* Sin[(unsigned char) (j->p_dir - (unsigned char) 64)];
		}

		/* Move player through space */
		else if (!(j->p_flags & PFDOCK)) {

		    if ((j->p_dir != j->p_desdir) && (j->p_status != PEXPLODE))
			changedir(j);

		    /* Alter speed */
		    maxspeed = (j->p_ship.s_maxspeed+2) -
		            (j->p_ship.s_maxspeed+1)*
		           ((float)j->p_damage/ (float)(j->p_ship.s_maxdamage));
		    if (j->p_desspeed > maxspeed)
			j->p_desspeed = maxspeed;
		    if (j->p_flags & PFENG)
			j->p_desspeed = 1;

		    /* Charge for speed */
		    if (j->p_fuel < (j->p_ship.s_warpcost * j->p_speed)) {
		      /* 
		       * Out of fuel ... set speed to one that uses less fuel
		       *  than the ship's reactor generates (if necessary).
		       */
		      if (j->p_ship.s_recharge/j->p_ship.s_warpcost < 
			    	j->p_speed) {
			j->p_desspeed = (j->p_ship.s_recharge*2/j->p_ship.s_warpcost) - 1;
/*			j->p_desspeed = j->p_ship.s_recharge/j->p_ship.s_warpcost + 2;		*/
			/* Above incantation is a magic formula ... (hack). */
			if (j->p_desspeed > j->p_ship.s_maxspeed)
			  j->p_desspeed = j->p_ship.s_maxspeed - 1;
		      } else {
			j->p_desspeed = j->p_speed;
		      }
		      j->p_fuel = 0;
		    } else {
			j->p_fuel -= (j->p_ship.s_warpcost * j->p_speed);
			j->p_etemp += j->p_speed;

			/* Charge SB's for mass of docked vessels ... */
			if (j->p_ship.s_type == STARBASE) {
			    for (k=0; k<NUMPORTS; k++) 
				if(j->p_port[k] != VACANT) {
				    j->p_fuel -= players[j->p_port[k]].p_ship.s_warpcost * j->p_speed;
				    j->p_etemp += .7*(j->p_speed * j->p_docked);
				}
			}
		    }

		    if (j->p_desspeed > j->p_speed) {
			j->p_subspeed += j->p_ship.s_accint;
		    }
		    if (j->p_desspeed < j->p_speed)
			j->p_subspeed -= j->p_ship.s_decint;

		    if (j->p_subspeed / 1000) {
			j->p_speed += j->p_subspeed / 1000;
			j->p_subspeed /= 1000;
			if (j->p_speed < 0)
			    j->p_speed = 0;
			if (j->p_speed > j->p_ship.s_maxspeed)
			    j->p_speed = j->p_ship.s_maxspeed;
		    }
		    j->p_x += (double) j->p_speed * Cos[j->p_dir] * WARP1;
		    j->p_y += (double) j->p_speed * Sin[j->p_dir] * WARP1;

		    /* Bounce off the side of the galaxy */
		    if (j->p_x < 0) {
			j->p_x = -j->p_x;
			if (j->p_dir == 192) 
			    j->p_dir = j->p_desdir = 64;
			else 
			    j->p_dir = j->p_desdir = 64 - (j->p_dir - 192);
		    } else if (j->p_x > GWIDTH) {
			j->p_x = GWIDTH - (j->p_x - GWIDTH);
			if (j->p_dir == 64) 
			    j->p_dir = j->p_desdir = 192;
			else 
			    j->p_dir = j->p_desdir = 192 - (j->p_dir - 64);
		    } if (j->p_y < 0) {
			j->p_y = -j->p_y;
			if (j->p_dir == 0) 
			    j->p_dir = j->p_desdir = 128;
			else 
			    j->p_dir = j->p_desdir = 128 - j->p_dir;
		    } else if (j->p_y > GWIDTH) {
			j->p_y = GWIDTH - (j->p_y - GWIDTH);
			if (j->p_dir == 128) 
			    j->p_dir = j->p_desdir = 0;
			else 
			    j->p_dir = j->p_desdir = 0 - (j->p_dir - 128);
		    }
		}

		/* If player is actually dead, don't do anything below ... */
		if (j->p_status == PEXPLODE || j->p_status == PDEAD)
		  break;

		if (j->p_ship.s_type==STARBASE) {
		    j->p_stats.st_sbticks++;
		} else if (status->tourn) {
		    j->p_stats.st_tticks++;
		    status->timeprod++;
		} else {
		    j->p_stats.st_ticks++;
		}

		if (++(j->p_ghostbuster) > GHOSTTIME) {
		    j->p_status = PEXPLODE;
		    if (j->p_ship.s_type == STARBASE)
			j->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
		    else
			j->p_explode = 10/PLAYERFUSE;
		    ghostmess(j);
		    saveplayer(j);
		    j->p_whydead = KGHOST;
		    j->p_whodead = i;
		}
			
		status->active += (1<<i);
		tcount[j->p_team]++;
		j->p_updates++;

		/* Charge for shields */
		if (j->p_flags & PFSHIELD) {  /* This is a kludge, I know. */
		    switch (j->p_ship.s_type) {
		    case SCOUT:
			j->p_fuel -= 2;
			break;
		    case DESTROYER: 
		    case CRUISER:
		    case BATTLESHIP:
		    case ASSAULT:
			j->p_fuel -= 3;
			break;
		    case STARBASE:
			j->p_fuel -= 6;
			break;
		    default:
			break;
		    }
		}

		/* affect tractor beams */
		if (j->p_flags & PFTRACT) {
		  if ((isAlive(&players[j->p_tractor])) &&
		      ((j->p_fuel > TRACTCOST) && !(j->p_flags & PFENG)) &&
		      ((dist=hypot((double)(j->p_x-players[j->p_tractor].p_x),
				   (double)(j->p_y-players[j->p_tractor].p_y))) 
		                   < ((double) TRACTDIST)*j->p_ship.s_tractrng) &&
		      (!(j->p_flags & (PFORBIT | PFDOCK))) &&
		      (!(players[j->p_tractor].p_flags & PFDOCK))) {
		    float cosTheta, sinTheta;  /* Cos and Sin from me to him */
		    int halfforce; /* Half force of tractor */
		    int dir;       /* -1 for repress, otherwise 1 */

		    if (players[j->p_tractor].p_flags & PFORBIT)
		       players[j->p_tractor].p_flags &= ~PFORBIT;
		    
		    j->p_fuel -= TRACTCOST;
		    j->p_etemp += TRACTEHEAT;
		    
		    cosTheta = players[j->p_tractor].p_x - j->p_x;
		    sinTheta = players[j->p_tractor].p_y - j->p_y;
		    if (dist == 0) dist = 1; /* prevent divide by zero */
		    cosTheta /= dist;   /* normalize sin and cos */
		    sinTheta /= dist;
		    /* force of tractor is WARP2 * tractstr */
		    halfforce = (WARP1*j->p_ship.s_tractstr);

		    dir = 1;
		    if (j->p_flags & PFPRESS) dir = -1;
		    /* change in position is tractor strength over mass */
		    j->p_x += dir * cosTheta * halfforce/(j->p_ship.s_mass);
		    j->p_y += dir * sinTheta * halfforce/(j->p_ship.s_mass);
		    players[j->p_tractor].p_x -= dir * cosTheta *
		       halfforce/(players[j->p_tractor].p_ship.s_mass);
		    players[j->p_tractor].p_y -= dir * sinTheta *
		       halfforce/(players[j->p_tractor].p_ship.s_mass);
		  } else {
		    j->p_flags &= ~(PFTRACT | PFPRESS);
		  }	
		}
			
		/* cool weapons */
		j->p_wtemp -= j->p_ship.s_wpncoolrate;
		if (j->p_wtemp < 0)
		    j->p_wtemp = 0;
		if (j->p_flags & PFWEP) {
		    if (--j->p_wtime <= 0)
			j->p_flags &= ~PFWEP;
		}
		else if (j->p_wtemp > j->p_ship.s_maxwpntemp) {
		    if (!(random() % 40)) {
			j->p_flags |= PFWEP;
			j->p_wtime = ((random() % 150) + 100) / PLAYERFUSE;
		    }
		}
		/* cool engine */
		j->p_etemp -= j->p_ship.s_egncoolrate;
		if (j->p_etemp < 0)
		    j->p_etemp = 0;
		if (j->p_flags & PFENG) {
		    if (--j->p_etime <= 0)
			j->p_flags &= ~PFENG;
		}
		else if (j->p_etemp > j->p_ship.s_maxegntemp) {
		    if (!(random() % 40)) {
			j->p_flags |= PFENG;
			j->p_etime = ((random() % 150) + 100) / PLAYERFUSE;
			j->p_desspeed = 0;
		    }
		}

		/* Charge for cloaking */
		if (j->p_flags & PFCLOAK) {
		    if (j->p_fuel < j->p_ship.s_cloakcost) {
			j->p_flags &= ~PFCLOAK;
		    } else {
			j->p_fuel -= j->p_ship.s_cloakcost;
		    }
		}

		/* Add fuel */
		if ((j->p_flags & PFORBIT) &&
		    (planets[j->p_planet].pl_flags & PLFUEL) &&
		    (!(planets[j->p_planet].pl_owner 
			& (j->p_swar | j->p_hostile)))) {
			    j->p_fuel += 8 * j->p_ship.s_recharge;
		} else if ((j->p_flags & PFDOCK) && (j->p_fuel < j->p_ship.s_maxfuel)) {
		    if (players[j->p_docked].p_fuel > SBFUELMIN) {
			j->p_fuel += 12*j->p_ship.s_recharge;
			players[j->p_docked].p_fuel -= 12*j->p_ship.s_recharge;
		    }
		} else
		    j->p_fuel += 2 * j->p_ship.s_recharge;

		if (j->p_fuel > j->p_ship.s_maxfuel)
		    j->p_fuel = j->p_ship.s_maxfuel;
		if (j->p_fuel < 0) {
		    j->p_desspeed = 0;
		    j->p_flags &= ~PFCLOAK;
		}

		/* repair shields */
		if (j->p_shield < j->p_ship.s_maxshield) {
		    if ((j->p_flags & PFREPAIR) && (j->p_speed == 0)) {
			j->p_subshield += j->p_ship.s_repair * 4;
			if ((j->p_flags & PFORBIT) &&
			    (planets[j->p_planet].pl_flags & PLREPAIR) &&
			    (!(planets[j->p_planet].pl_owner 
				& (j->p_swar | j->p_hostile)))) {
				    j->p_subshield += j->p_ship.s_repair * 4;
			} 
			if (j->p_flags & PFDOCK) 
			    j->p_subshield += 6*j->p_ship.s_repair;
		    }
		    else
			j->p_subshield += j->p_ship.s_repair * 2;
		    if (j->p_subshield / 1000) {
			j->p_shield += j->p_subshield / 1000;
			j->p_subshield %= 1000;
		    }
		    if (j->p_shield > j->p_ship.s_maxshield) {
			j->p_shield = j->p_ship.s_maxshield;
			j->p_subshield = 0;
		    }
		}

		/* repair damage */
		if (j->p_damage && !(j->p_flags & PFSHIELD)) {
		    if ((j->p_flags & PFREPAIR) && (j->p_speed == 0)) {
			j->p_subdamage += j->p_ship.s_repair * 2;
			if ((j->p_flags & PFORBIT) &&
			    (planets[j->p_planet].pl_flags & PLREPAIR) &&
			    (!(planets[j->p_planet].pl_owner 
				& (j->p_swar | j->p_hostile)))) {
				    j->p_subdamage += j->p_ship.s_repair * 2;
			}
			if (j->p_flags & PFDOCK)
			    j->p_subdamage += j->p_ship.s_repair * 3;
		    }
		    else
			j->p_subdamage += j->p_ship.s_repair;
		    if (j->p_subdamage / 1000) {
			j->p_damage -= j->p_subdamage / 1000;
			j->p_subdamage %= 1000;
		    }
		    if (j->p_damage < 0) {
			j->p_damage = 0;
			j->p_subdamage = 0;
		    }
		}

		/* Move Player in dock */
		if (j->p_flags & PFDOCK) {
		    j->p_x = players[j->p_docked].p_x + DOCKDIST*Cos[(j->p_port[0]*90+45)*255/360];
		    j->p_y = players[j->p_docked].p_y + DOCKDIST*Sin[(j->p_port[0]*90+45)*255/360];
		}

		/* Set player's alert status */
#define YRANGE ((GWIDTH)/7)
#define RRANGE ((GWIDTH)/10)
		j->p_flags |= PFGREEN;
		j->p_flags &= ~(PFRED|PFYELLOW);
		for (k = 0; k < MAXPLAYER; k++) {
		    int dx, dy, dist;
		    if ((players[k].p_status != PALIVE) ||
			((!((j->p_swar | j->p_hostile) & players[k].p_team)) &&
			(!((players[k].p_swar | players[k].p_hostile) & 
			j->p_team)))) {
			    continue;
		    }
		    else if (j == &players[k]) {
			continue;
		    }
		    else {
			dx = j->p_x - players[k].p_x;
			dy = j->p_y - players[k].p_y;
			if (ABS(dx) > YRANGE || ABS(dy) > YRANGE)
			    continue;
			dist = dx * dx + dy * dy;
			if (dist <  RRANGE * RRANGE) {
			    j->p_flags |= PFRED;
			    j->p_flags &= ~(PFGREEN|PFYELLOW);
			}
			else if ((dist <  YRANGE * YRANGE) &&
			    (!(j->p_flags & PFRED))) {
			    j->p_flags |= PFYELLOW;
			    j->p_flags &= ~(PFGREEN|PFRED);
			}
		    }
		}
	    break;
	} /* end switch */
    }
    if (nplayers == MAXPLAYER) {
	if (dietime == -1)
	    dietime = ticks + 600 / PLAYERFUSE;
    }
    else {
	dietime = -1;
    }
}

changedir(sp)
struct player *sp;
{
    unsigned int ticks;
    unsigned int min, max;
    int speed;

/* Change 1/20/91 TC new-style turn rates*/

    speed=sp->p_speed;
    if (speed == 0) {
	sp->p_dir = sp->p_desdir;
	sp->p_subdir = 0;
    }
    else {
	if (newturn) 
	    sp->p_subdir += sp->p_ship.s_turns/(speed*speed);
	else
	    sp->p_subdir += sp->p_ship.s_turns/((sp->p_speed<30)?(1<<sp->p_speed):1000000000);

	ticks = sp->p_subdir / 1000;
	if (ticks) {
	    if (sp->p_dir > sp->p_desdir) {
		min=sp->p_desdir;
		max=sp->p_dir;
	    } else {
		min=sp->p_dir;
		max=sp->p_desdir;
	    }
	    if ((ticks > max-min) || (ticks > 256-max+min))
		sp->p_dir = sp->p_desdir;
	    else if ((unsigned char) ((int)sp->p_dir - (int)sp->p_desdir) > 127)
		sp->p_dir += ticks;
	    else 
		sp->p_dir -= ticks;
	    sp->p_subdir %= 1000;
	}
    }
}


udcloak()
{
  register int i;

  for (i=0; i<MAXPLAYER; i++) {
    if (isAlive(&players[i]))
      if ((players[i].p_flags & PFCLOAK) && 
	  (players[i].p_cloakphase < (CLOAK_PHASES-1))) {
	players[i].p_cloakphase++;
      } else if (!(players[i].p_flags & PFCLOAK) && 
		 (players[i].p_cloakphase > 0)) {
	players[i].p_cloakphase--;
      }
  }
}
    
    

udtorps()
{
    register int i, turn=0, heading=0;
    register struct torp *j;

    for (i = 0, j = &torps[i]; i < MAXPLAYER * MAXTORP; i++, j++) {
	switch (j->t_status) {
	    case TFREE:
		continue;
	    case TMOVE:
	    case TSTRAIGHT:
		if (j->t_turns > 0) {
		    turn = torp_track_target(j);
		    if (turn < 0) {
		      heading = ((int) j->t_dir) - j->t_turns;
		      j->t_dir = ((heading < 0)? ((unsigned char) (256+heading)): 
				    ((unsigned char) heading));
		    } else if (turn > 0) {
		      heading = ((int) j->t_dir) + j->t_turns;
		      j->t_dir = ((heading > 255)? ((unsigned char) (heading-256)): 
				    ((unsigned char) heading));
		    }
		}
		
		j->t_x += (double) j->t_speed * Cos[j->t_dir] * WARP1;
		if (j->t_x < 0) {
		    j->t_x = 0;
		    j->t_status = TDET;	/* no wall kills 8/31/91 TC */
		    j->t_whodet = j->t_owner; /* 8/31/91 TC */
		    explode(j);
		    break;
		}
		else if (j->t_x > GWIDTH) {
		    j->t_x = GWIDTH;
		    j->t_status = TDET;	/* no wall kills 8/31/91 TC */
		    j->t_whodet = j->t_owner; /* 8/31/91 TC */
		    explode(j);
		    break;
		}
		j->t_y += (double) j->t_speed * Sin[j->t_dir] * WARP1;
		if (j->t_y < 0) {
		    j->t_y = 0;
		    j->t_status = TDET;	/* no wall kills 8/31/91 TC */
		    j->t_whodet = j->t_owner; /* 8/31/91 TC */
		    explode(j);
		    break;
		}
		else if (j->t_y > GWIDTH) {
		    j->t_y = GWIDTH;
		    j->t_status = TDET;	/* no wall kills 8/31/91 TC */
		    j->t_whodet = j->t_owner; /* 8/31/91 TC */
		    explode(j);
		    break;
		}

		/* Make sure that player torps wobble */
		if (j->t_status == TMOVE)
		    j->t_dir += (random() % 3) - 1;

		if (j->t_fuse-- <= 0) {
		    j->t_status = TFREE;
		    players[j->t_owner].p_ntorp--;
		}
		else if (near(j)) {
		    explode(j);
		}
		break;
	    case TDET:
		explode(j);
		break;
	    case TEXPLODE:
		if (j->t_fuse-- <= 0) {
		    j->t_status = TFREE;
		    players[j->t_owner].p_ntorp--;
		}
		break;
	    case TOFF:
		j->t_status = TFREE;
		players[j->t_owner].p_ntorp--;
		break;
	    default:		/* Shouldn't happen */
		j->t_status = TFREE;
		break;
	}
    }
}


udplasmatorps()
{
    register int i, turn=0, heading;
    register struct plasmatorp *j;

    for (i = 0, j = &plasmatorps[i]; i < MAXPLAYER * MAXPLASMA; i++, j++) {
	switch (j->pt_status) {
	    case PTFREE:
		continue;
	    case PTMOVE:
		turn = plasma_track_target(j);
		if (turn < 0) {
		  heading = ((int) j->pt_dir) - j->pt_turns;
		  j->pt_dir = ((heading < 0)? ((unsigned char) (256+heading)): 
			        ((unsigned char) heading));
		} else if (turn > 0) {
		  heading = ((int) j->pt_dir) + j->pt_turns;
		  j->pt_dir = ((heading > 255)? ((unsigned char) (heading-256)): 
			        ((unsigned char) heading));
		}
		
		j->pt_x += (double) j->pt_speed * Cos[j->pt_dir] * WARP1;
		if (j->pt_x < 0) {
		    j->pt_x = 0;
		    pexplode(j);
		    break;
		}
		else if (j->pt_x > GWIDTH) {
		    j->pt_x = GWIDTH;
		    pexplode(j);
		    break;
		}
		j->pt_y += (double) j->pt_speed * Sin[j->pt_dir] * WARP1;
		if (j->pt_y < 0) {
		    j->pt_y = 0;
		    pexplode(j);
		    break;
		}
		else if (j->pt_y > GWIDTH) {
		    j->pt_y = GWIDTH;
		    pexplode(j);
		    break;
		}

		if (j->pt_fuse-- <= 0) {
		    j->pt_status = PTFREE;
		    players[j->pt_owner].p_nplasmatorp--;
		}
		else if (pnear(j)) {
		    pexplode(j);
		}
		break;
	    case PTDET:
		pexplode(j);
		break;
	    case PTEXPLODE:
		if (j->pt_fuse-- <= 0) {
		    j->pt_status = PTFREE;
		    players[j->pt_owner].p_nplasmatorp--;
		}
		break;
	    default:		/* Shouldn't happen */
		j->pt_status = PTFREE;
		break;
	}
    }
}


/* See if there is someone close enough to explode for */
near(torp)
struct torp *torp;
{
    register int i, expdist=EXPDIST;
    int dx, dy;
    register struct player *j;

    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if (!(j->p_status == PALIVE))
	    continue;
	if (torp->t_owner == j->p_no)
	    continue;
	/* This is tricky.  If you aren't at war with the team,
	   and that team is not at war with your team, continue.
	*/
	if ((!(torp->t_war & j->p_team)) &&
	    (!(torp->t_team & (j->p_swar | j->p_hostile))))
	        continue;
	dx = torp->t_x - j->p_x;
	dy = torp->t_y - j->p_y;
	if (ABS(dx) > expdist || ABS(dy) > expdist)
	    continue;
	if (dx * dx + dy * dy < expdist * expdist)
	    return 1;
    }
    return 0;
}


/* See if there is someone close enough to explode for */
pnear(plasmatorp)
struct plasmatorp *plasmatorp;
{
    register int i, expdist=EXPDIST;
    int dx, dy;
    register struct player *j;

    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if (!(j->p_status == PALIVE))
	    continue;
	if (plasmatorp->pt_owner == j->p_no)
	    continue;
	/* This is tricky.  If you aren't at war with the team,
	   and that team is not at war with your team, continue.
	*/
	if ((!(plasmatorp->pt_war & j->p_team)) &&
	    (!(plasmatorp->pt_team & (j->p_swar | j->p_hostile))))
	        continue;
	dx = plasmatorp->pt_x - j->p_x;
	dy = plasmatorp->pt_y - j->p_y;
	if (ABS(dx) > expdist || ABS(dy) > expdist)
	    continue;
	if (dx * dx + dy * dy < (expdist) * (expdist))
	    return 1;
    }
    return 0;
}

    

/* Do damage to all surrounding players */

explode(torp)
struct torp *torp;
{
    register int i;
    int dx, dy, dist;
    int damage;
    register struct player *j;

    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if (!(j->p_status == PALIVE))
	    continue;
	if (torp->t_owner == j->p_no)	/* This isn't realistic */
	    continue;
	/* Now, only the person who detonated the torpedo and people
	 * on one of the other teams takes damage.
	 * This prevents hosers from killing teammates with the 'd' key 
	 */
	if (torp->t_status==TDET && 
	    j->p_no!=torp->t_whodet && 
	    j->p_team==players[torp->t_whodet].p_team) {
	    continue;
	}
	dx = torp->t_x - j->p_x;
	dy = torp->t_y - j->p_y;
	if (ABS(dx) > DAMDIST || ABS(dy) > DAMDIST) /*XXX*/
	    continue;
	dist = dx * dx + dy * dy;
	if (dist > DAMDIST * DAMDIST)
	    continue;
	if (dist > EXPDIST * EXPDIST) {
	    damage = torp->t_damage * (DAMDIST - sqrt((double) dist)) /
		(DAMDIST - EXPDIST);
	}
	else {
	    damage = torp->t_damage;
	}
	if (damage > 0) {
	    /* First, check to see if torp owner has started a war */
	    if (players[torp->t_owner].p_hostile & j->p_team) {
		players[torp->t_owner].p_swar |= j->p_team;
	    }
	    /* Note that if a player is at peace with the victim, then
	       the torp has caused damage either accidently, or because
	       the victim was at war with, or hostile to, the player.
	       In either case, we don't consider the damage to be
	       an act of war */

	    if (j->p_flags & PFSHIELD) {
		j->p_shield -= damage;
		if (j->p_shield < 0) {
		    j->p_damage -= j->p_shield;
		    j->p_shield = 0;
		}
	    }
	    else {
		j->p_damage += damage;
	    }
	    if (j->p_damage >= j->p_ship.s_maxdamage) {
		j->p_status = PEXPLODE;
		if (j->p_ship.s_type == STARBASE) {
		    j->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
		} else {
		    j->p_explode = 10/PLAYERFUSE;
		}
		killerstats(torp->t_owner, j);
		players[torp->t_owner].p_kills += 1.0 + 
		    j->p_armies * 0.1 + j->p_kills * 0.1;
		checkmaxkills(torp->t_owner);
		killmess(j, &players[torp->t_owner]);
		loserstats(i);
		j->p_whydead = KTORP;
		j->p_whodead = torp->t_owner;
	    } 
	} 
    } 
    torp->t_status = TEXPLODE; 
    torp->t_fuse = 10/TORPFUSE; 
} 

/* Do damage to all surrounding players */

pexplode(plasmatorp)
struct plasmatorp *plasmatorp;
{
    register int i;
    int dx, dy, dist;
    int damage;
    register struct player *j;

    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if (!(j->p_status == PALIVE))
	    continue;

	/* Testing to see if this helps make them risky ... (Scott: 12/10/88)
	if (plasmatorp->pt_owner == j->p_no)	/ This isn't realistic /
	    continue;
	*/

	dx = plasmatorp->pt_x - j->p_x;
	dy = plasmatorp->pt_y - j->p_y;
	if (ABS(dx) > PLASDAMDIST || ABS(dy) > PLASDAMDIST) /*XXX*/
	    continue;
	dist = dx * dx + dy * dy;
	if (dist > PLASDAMDIST * PLASDAMDIST)
	    continue;
	if (dist > (EXPDIST) * (EXPDIST)) {
	    damage = plasmatorp->pt_damage * (PLASDAMDIST - sqrt((double) dist)) /
		(PLASDAMDIST - (EXPDIST));
	}
	else {
	    damage = plasmatorp->pt_damage;
	}
	if (damage > 0) {
	    /* First, check to see if torp owner has started a war */
	    if (players[plasmatorp->pt_owner].p_hostile & j->p_team) {
		players[plasmatorp->pt_owner].p_swar |= j->p_team;
	    }
	    /* Note that if a player is at peace with the victim, then
	       the torp has caused damage either accidently, or because
	       the victim was at war with, or hostile to, the player.
	       In either case, we don't consider the damage to be
	       an act of war */

	    if (j->p_flags & PFSHIELD) {
		j->p_shield -= damage;
		if (j->p_shield < 0) {
		    j->p_damage -= j->p_shield;
		    j->p_shield = 0;
		}
	    }
	    else {
		j->p_damage += damage;
	    }
	    if (j->p_damage >= j->p_ship.s_maxdamage) {
		j->p_status = PEXPLODE;
		if (j->p_ship.s_type == STARBASE)
		    j->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
		else
		    j->p_explode = 10/PLAYERFUSE;
		killerstats(plasmatorp->pt_owner, j);
		players[plasmatorp->pt_owner].p_kills += 1.0 + 
		    j->p_armies * 0.1 + j->p_kills * 0.1;
		checkmaxkills(plasmatorp->pt_owner);
		killmess(j, &players[plasmatorp->pt_owner]);
		loserstats(i);
		j->p_whydead = KPLASMA;
		j->p_whodead = plasmatorp->pt_owner;
	    } 
	} 
    } 
    plasmatorp->pt_status = PTEXPLODE; 
    plasmatorp->pt_fuse = 10/PLASMAFUSE; 
} 

loserstats(pl)
int pl;
{
    struct player *dude;

    dude= &players[pl];

    if (dude->p_ship.s_type == STARBASE) {
	dude->p_stats.st_sblosses++;
    } else {
	if (status->tourn) {
	    dude->p_stats.st_tlosses++;
	    status->losses++;
	} else {
	    dude->p_stats.st_losses++;
	}
    }
}

killerstats(pl, victim)
int pl;
struct player *victim;
{
    struct player *dude;

    dude= &players[pl];
    
    if (dude->p_ship.s_type == STARBASE) {
	dude->p_stats.st_sbkills++;
	dude->p_stats.st_armsbomb += 5 * victim->p_armies;
    } else {
	if (status->tourn) {
	    dude->p_stats.st_tkills++;
	    status->kills++;
	    dude->p_stats.st_tarmsbomb += 5 * victim->p_armies;
	    status->armsbomb += 5 * victim->p_armies;
	} else {
	    dude->p_stats.st_kills++;
	    dude->p_stats.st_armsbomb += 5 * victim->p_armies;
	}
    }
}

checkmaxkills(pl)
int pl;
{
    struct stats *stats;
    struct player *dude;

    dude= &(players[pl]);
    stats= &(dude->p_stats);

    if (dude->p_ship.s_type == STARBASE) {
	if (stats->st_sbmaxkills < dude->p_kills) {
	    stats->st_sbmaxkills = dude->p_kills;
	}
    } else if (stats->st_maxkills < dude->p_kills) {
	stats->st_maxkills = dude->p_kills;
    }
}

udplanets()
{
    register int i;
    register struct planet *l;

/* moved to udsurrend
    for (i = 0; i <= MAXTEAM; i++) {
	(teams[i].s_turns--);
    }
*/

    for (i = 0, l = &planets[i]; i < MAXPLANETS; i++, l++) {

#ifdef PLANETSTAGGER
	/* since we only want to run the pop code on about */
	/* MAXPLANETS/PLANETSTAGGER planets on average, there should be */
	/* a corresponding probability that we do so; otherwise, we just */
	/* skip the planet.  Note that randomly choosing a planet */
	/* MAXPLANETS/PLANETSTAGGER times to pop results in much wilder */
	/* pop distributions.  Also note that we can't just scale down */
	/* the probabilities in each (random() % X), because not each */
	/* statement below involves a probability.  4/15/92 TC */

	if ((random() % PLANETSTAGGER) > 0)
	    continue;
#endif

/* moved to udsurrend
	if (l->pl_couptime)	/ Decrement coup counter one minute /
	    l->pl_couptime--;
*/
	if (l->pl_armies == 0)
	    continue;
	if ((random() % 3000) < l->pl_armies) {
	    l->pl_armies -= (random() % l->pl_armies);
	    if (l->pl_armies < 5) l->pl_flags |= PLREDRAW;
	}
	if (l->pl_armies < 4) {
	    if ((random() % 20) == 0) {
		l->pl_armies++;
	    }
	    if (l->pl_flags & PLAGRI) { /* Always grow 1 if Agricultural. */
		if (++l->pl_armies > 4) l->pl_flags |= PLREDRAW;
	    }
	}
	if ((random() % 10) == 0) {  /* Chance for extra armies. */
	    if (l->pl_armies < 5) l->pl_flags |= PLREDRAW;
	    l->pl_armies += (random() % 3) + 1;
	}
	/* Argicultural worlds have extra chance for army growth. */
	if (((random() % 5) == 0) && (l->pl_flags & PLAGRI)) {
	    if (++l->pl_armies > 4) l->pl_flags |= PLREDRAW;
	}
    }
}

/* new code TC */
udsurrend()
{
    register int i, t;
    char buf[80];
    struct planet *l;
    struct player *j;

    /* Decrement SB reconstruction timer code moved here (from udplanets()) */

    for (i = 0; i <= MAXTEAM; i++) {
	(teams[i].s_turns--);
    }

    /* Coup timer moved here (from udplanets()) */

    for (i = 0, l = &planets[i]; i < MAXPLANETS; i++, l++) {
	if (l->pl_couptime)	/* Decrement coup counter one minute */
	    l->pl_couptime--;
    }

    /* All tmode-only code must follow this statement. */

    if (!status->tourn) return;

    /* Terminate non-warring humans 9/1/91 TC */

    for (i = 0, j = &players[0]; i < MAXPLAYER; i++, j++) {
	if ((j->p_status == PALIVE) &&
	    !(j->p_flags & PFROBOT) &&
	    (j->p_team != NOBODY) &&
	    (realNumShips(j->p_team) < tournplayers) &&
	    (random()%5 == 0))
	    rescue(TERMINATOR, j->p_no);
    }

    for (t = 0; t <= MAXTEAM; t++) { /* maint: was "<" 6/22/92 TC */
	/* "suspend" countdown if less than Tmode players */
	if ((teams[t].s_surrender == 0) || (realNumShips(t) < tournplayers))
	    continue;

	/* also if team has more than SURRSTART planets */
	if (teams[t].s_plcount > SURRSTART)
	    continue;

	teams[t].s_surrender--;
	if (teams[t].s_surrender == 0) {

	    /* terminate race t */

	    sprintf(buf, "The %s %s surrendered.", teamNames[t],
		    teamVerbage[t]);
	    pmessage("", 0, MALL, "");
	    pmessage(buf, 0, MALL, "GOD->ALL");
	    pmessage("", 0, MALL, "");
	    surrenderMessage(t);
	    
	    /* neutralize race t's planets  */

	    for (i = 0, l = &planets[i]; i < MAXPLANETS; i++, l++) {
		if (((l->pl_flags & ALLTEAM) == t) && (l->pl_flags & PLHOME))
		    l->pl_couptime = MIN_COUP_TIME +
			(random() % (MAX_COUP_TIME - MIN_COUP_TIME));

		/* removed above dependency on PLANETFUSE and hardcoded */
		/* constants (now in defs.h).  30-60 min for a coup. */
		/* 4/15/92 TC */

		if (l->pl_owner == t) {
		    l->pl_owner = 0;
		    l->pl_armies = 0;
		}
	    }

	    /* kick out all the players on that team */
	    /* makes it easy for them to come in as a different team */
	    for (i = 0, j = &players[0]; i < MAXPLAYER; i++, j++) {
		if (j->p_team == t) {
		    j->p_genoplanets=0;
		    j->p_genoarmsbomb=0;
		    j->p_armsbomb=0;
		    j->p_planets=0;
		}
		if ((j->p_status == PALIVE) && (j->p_team == t)) {
		    j->p_status = PEXPLODE;
		    if (j->p_ship.s_type == STARBASE)
			j->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
		    else
			j->p_explode = 10/PLAYERFUSE;
		    j->p_whydead = KPROVIDENCE;
		    j->p_whodead = 0;
		}
	    }			/* end for */
	    
	}			/* end if */
	else {			/* surrender countdown */
	    if ((teams[t].s_surrender % 5) == 0) {
		sprintf(buf, "The %s %s %d minutes remaining.",
			teamNames[t], teamVerbage[t],
			teams[t].s_surrender);
		pmessage("", 0, MALL, "");
		pmessage(buf, 0, MALL, "GOD->ALL");

		sprintf(buf, "%d planets will sustain the empire.  %d suspends countdown.",
			SURREND, SURRSTART+1);
		pmessage(buf, 0, MALL, "GOD->ALL");
		pmessage("", 0, MALL, "");
	    }
	}
    }				/* end for team */
}

udphaser()
{
    register int i;
    register struct phaser *j;
    register struct player *victim;

    for (i = 0, j = &phasers[i]; i < MAXPLAYER; i++, j++) {
	switch (j->ph_status) {
	    case PHFREE:
		continue;
	    case PHMISS:
	    case PHHIT2:
		if (j->ph_fuse-- == 1)
		    j->ph_status = PHFREE;
		break;
	    case PHHIT:
		if (j->ph_fuse-- == players[i].p_ship.s_phaserfuse) {
		    victim = &players[j->ph_target];

		    /* start a war, if necessary */
		    if (players[i].p_hostile & victim->p_team) {
			players[i].p_swar |= victim->p_team;
		    }

		    if (victim->p_status == PALIVE) {
			if (victim->p_flags & PFSHIELD) {
			    victim->p_shield -= j->ph_damage;
			    if (victim->p_shield < 0) {
				victim->p_damage -= victim->p_shield;
				victim->p_shield = 0;
			    }
			}
			else {
			    victim->p_damage += j->ph_damage;
			}
			if (victim->p_damage >= victim->p_ship.s_maxdamage) {
			    victim->p_status = PEXPLODE;
			    if (victim->p_ship.s_type == STARBASE)
				victim->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
			    else
				victim->p_explode = 10/PLAYERFUSE;
			    players[i].p_kills += 1.0 + 
				victim->p_armies * 0.1 +
				victim->p_kills * 0.1;
			    killerstats(i, victim);
			    checkmaxkills(i);
			    killmess(victim, &players[i]);
			    loserstats(j->ph_target);
			    victim->p_whydead = KPHASER;
			    victim->p_whodead = i;
			}
		    }
		}
		if (j->ph_fuse == 0)
		    j->ph_status = PHFREE;
		break;
	}
    }
}

int pl_warning[MAXPLANETS];	/* To keep planets shut up for awhile */
int tm_robots[MAXTEAM + 1];		/* To limit the number of robots */
int tm_coup[MAXTEAM + 1];		/* To allow a coup */

teamtimers()
{
    register int i;
    for (i = 0; i <= MAXTEAM; i++) {
	if (tm_robots[i] > 0)
	    tm_robots[i]--;
	if (tm_coup[i] > 0)
	    tm_coup[i]--;
    }
}

plfight()
{
    register int h, i;
    register struct player *j;
    register struct planet *l;
    register struct player *pl2;
    int seen;
    int dx, dy;
    int damage;
    int dist;
    int rnd;
    int oldowner = NOBODY;
    char buf[90];
    char buf1[80];
    char addrbuf[10];

    for (h = 0, l = &planets[h]; h < MAXPLANETS; h++, l++) {
	if (l->pl_flags & PLCOUP) {
	    l->pl_flags &= ~PLCOUP;
	    sprintf(addrbuf, "%-3s->%s ", l->pl_name, teamshort[l->pl_owner]);
	    pmessage("Planet lost in political struggle", 
		l->pl_owner, MTEAM | MCOUP1, addrbuf);
	    l->pl_owner = (l->pl_flags & ALLTEAM);
	    l->pl_armies = 4;
	    sprintf(addrbuf, "%-3s->%s ", l->pl_name, teamshort[l->pl_owner]);
	    pmessage("Previous dictatorship overthrown in coup d'etat", 
		l->pl_owner, MTEAM | MCOUP2, addrbuf);
	}
	l->pl_flags &= ~PLREDRAW;
	if (pl_warning[h] > 0)
	    pl_warning[h]--;
    }
    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if (j->p_status == PALIVE) {

	    seen=0;
	    for (h = 0, pl2= &players[0]; h < MAXPLAYER; h++, pl2++) {
		if (pl2->p_status==PALIVE) {
		    if (pl2->p_flags & PFROBOT) continue;
		    if (pl2->p_team != j->p_team) {
			dx=ABS(pl2->p_x - j->p_x);
			dy=ABS(pl2->p_y - j->p_y);
			if (dx>GWIDTH/3 || dy>GWIDTH/3) continue;
			dist = (int) hypot((double) dx, (double) dy);
			if ((dist < (GWIDTH/3) && ((j->p_flags & PFCLOAK)==0)) 
			    || (dist < (GWIDTH/7))
			    || ((j->p_flags & PFORBIT) && 
			      (planets[j->p_planet].pl_owner != j->p_team))) {
			    seen=1;
			    break;
			}
		    }
		}
	    }
	    if (seen) {
		j->p_flags |= PFSEEN;
	    } else {
		j->p_flags &= ~PFSEEN;
	    }
	    /* Have planets do damage to nearby players.  Note that
	       this is the most inefficient code in the game.  If
	       one needs cycles, simply change the code to have
	       planets only attack ships that are in orbit.   Thus
	       you get rid of the following for statement 
	    */
	    for (h = 0, l = &planets[h]; h < MAXPLANETS; h++, l++) {
		dx = ABS(l->pl_x - j->p_x);
		dy = ABS(l->pl_y - j->p_y);
		if (dx < 3 * PFIREDIST && dy < 3 * PFIREDIST)
		    l->pl_flags |= PLREDRAW;
		if (dx > PFIREDIST || dy > PFIREDIST)	/*XXX*/
		    continue;
		dist = (int) hypot((double) dx, (double) dy);
		if (dist > PFIREDIST)
		    continue;
		if ((j->p_swar | j->p_hostile) & l->pl_owner) {
		    if (l->pl_armies > 0)
			damage = l->pl_armies / 10 + 2;
		    else
			damage = 0;
		    if (damage > 0) {
			if (j->p_flags & PFSHIELD) {
			    j->p_shield -= damage;
			    if (j->p_shield < 0) {
				j->p_damage -= j->p_shield;
				j->p_shield = 0;
			    }
			}
			else {
			    j->p_damage += damage;
			}
			if (j->p_damage >= j->p_ship.s_maxdamage) {
			if (j->p_ship.s_type == STARBASE)
			    j->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
			else
			    j->p_explode = 10/PLAYERFUSE;
			j->p_status = PEXPLODE;
			loserstats(i);
			(void) sprintf(buf, "%s (%c%c) killed by %s (%c)",
			    j->p_name,
			    teamlet[j->p_team],
			    shipnos[j->p_no],
			    l->pl_name,
			    teamlet[l->pl_owner]);
			pmessage(buf, 0, MALL | MKILLP, "GOD->ALL");
			j->p_whydead = KPLANET;
			j->p_whodead = h;
			}
		    }
		}
	    }	/* End planet damage */

	    /* do bombing */
	    if ((!(j->p_flags & PFORBIT)) || (!(j->p_flags & PFBOMB)))
		continue;
	    l = &planets[j->p_planet];

	    if (j->p_team == l->pl_owner)
		continue;
	    if (!((j->p_swar | j->p_hostile) & l->pl_owner))
		continue;
	    if (l->pl_armies < 5)
		continue;

	    /* Warn owning team */
	    if (pl_warning[j->p_planet] <= 0) {
		pl_warning[j->p_planet] = 50/PLFIGHTFUSE; 
		(void) sprintf(buf, "We are being attacked by %s %c%c who is %d%% damaged.",
			j->p_name,
			teamlet[j->p_team],
			shipnos[j->p_no],
			(100*j->p_damage)/(j->p_ship.s_maxdamage));
		(void) sprintf(buf1, "%-3s->%-3s",
		    l->pl_name, teamshort[l->pl_owner]);
		pmessage(buf, l->pl_owner, MTEAM | MBOMB, buf1);
	    }

	    /* start the war (if necessary) */
	    j->p_swar |= l->pl_owner;

	    rnd = random() % 100;
	    if (rnd < 50) {
		continue;
	    } else if (rnd < 80) {
		l->pl_armies -= 1;
		if (l->pl_armies<5) l->pl_flags |= PLREDRAW;
		j->p_kills += 0.02;
		j->p_armsbomb += 1;
		j->p_genoarmsbomb += 1;
		checkmaxkills(i);
		/* Give him bombing stats if he is bombing a team with 3+ */
		if (status->tourn && realNumShips(l->pl_owner) >= 3) {
		    status->armsbomb++;
		    j->p_stats.st_tarmsbomb++;
		} else {
		    j->p_stats.st_armsbomb++;
		}
	    } else if (rnd < 90) {
		l->pl_armies -= 2;
		if (l->pl_armies<5) l->pl_flags |= PLREDRAW;
		j->p_kills += 0.04;
		j->p_armsbomb += 2;
		j->p_genoarmsbomb += 2;
		checkmaxkills(i);
		if (status->tourn && realNumShips(l->pl_owner) >= 3) {
		    status->armsbomb += 2;
		    j->p_stats.st_tarmsbomb += 2;
		} else {
		    j->p_stats.st_armsbomb += 2;
		}
	    } else  {
		l->pl_armies -= 3;
		if (l->pl_armies<5) l->pl_flags |= PLREDRAW;
		j->p_kills += 0.06;
		j->p_armsbomb += 3;
		j->p_genoarmsbomb += 3;
		checkmaxkills(i);
		if (status->tourn && realNumShips(l->pl_owner) >= 3) {
		    status->armsbomb += 3;
		    j->p_stats.st_tarmsbomb += 3;
		} else {
		    j->p_stats.st_armsbomb += 3;
		}
	    }

	    if ((j->p_ship.s_type == ASSAULT) && (rnd > 20)) {
		l->pl_armies -= 1;
		if (l->pl_armies<5) l->pl_flags |= PLREDRAW;
		j->p_kills += 0.02;
		j->p_armsbomb += 1;
		j->p_genoarmsbomb += 1;
		checkmaxkills(i);
		if (status->tourn && realNumShips(l->pl_owner) >= 3) {
		    status->armsbomb += 1;
		    j->p_stats.st_tarmsbomb += 1;
		} else {
		    j->p_stats.st_armsbomb += 1;
		}
	    } 

	    /* Send in a robot if there are no other defenders (or not Tmode)
		and the planet is in the team's home space */
	    
	    if (((tcount[l->pl_owner] == 0) || (NotTmode(ticks))) && 
		(l->pl_flags & l->pl_owner) &&
		    tm_robots[l->pl_owner] == 0) {
			rescue(l->pl_owner, NotTmode(ticks));
			tm_robots[l->pl_owner] = (1800 + 
			    (random() % 1800)) /
			    TEAMFUSE;
	    }
	} else {
	    j->p_flags |= PFSEEN;
	}
    }
}

beam()
{
    register int i;
    register struct player *j;
    register struct planet *l;
    char buf[80];
    char buf1[80];

    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if ((j->p_status != PALIVE) || !(j->p_flags & (PFORBIT|PFDOCK)))
	    continue;
	if (j->p_flags & PFORBIT)
	    l = &planets[j->p_planet];
	if (j->p_flags & PFBEAMUP) {
	    if (j->p_flags & PFORBIT)
		if (l->pl_armies < 5)
		    continue;
	    if (j->p_flags & PFDOCK)
		if (players[j->p_docked].p_armies == 0)
		    continue;
	    if (j->p_armies == j->p_ship.s_maxarmies)
		continue;
	    /* XXX */
	    if (j->p_ship.s_type == ASSAULT) {
		if (j->p_armies == (int)(j->p_kills * 3.0))
		    continue;
            } else if (j->p_ship.s_type != STARBASE)
		if (j->p_armies == (int)(j->p_kills * 2.0))
		    continue;
	    if (j->p_flags & PFORBIT) {
		if (j->p_team != l->pl_owner)
		    continue;
		if ((j->p_swar | j->p_hostile) & l->pl_owner)
		    continue;
	    } 

	    j->p_armies++;
	    if (j->p_flags & PFORBIT)
		l->pl_armies--;
	    else if (j->p_flags & PFDOCK)
		players[j->p_docked].p_armies--;
	    continue;
	}
	if (j->p_flags & PFBEAMDOWN) {
	    if (j->p_armies == 0)
		continue;
	    if (j->p_flags & PFORBIT) {
		if ((!((j->p_swar | j->p_hostile) & l->pl_owner))
		    && (j->p_team != l->pl_owner) && (l->pl_owner != NOBODY))
		    continue;
		if (j->p_team != l->pl_owner) {
		    j->p_armies--;
		    if (l->pl_armies) {
			int oldowner = l->pl_owner;

			/* start the war (if necessary) */
			j->p_swar |= l->pl_owner;

			l->pl_armies--;
			j->p_kills += 0.02;
			j->p_armsbomb += 1;
			j->p_genoarmsbomb += 1;
			checkmaxkills(i);
			if (status->tourn && realNumShips(oldowner) >= 3) {
			    j->p_stats.st_tarmsbomb++;
			    status->armsbomb++;
			} else {
			    j->p_stats.st_armsbomb++;
			}
			if (l->pl_armies == 0) {
			    /* Give planet to nobody */
			    (void) sprintf(buf, "%s destroyed by %s (%c%c)",
					   l->pl_name,
					   j->p_name,
					   teamlet[j->p_team],
					   shipnos[j->p_no]);
			    (void) sprintf(buf1, "%-3s->%-3s",
					   l->pl_name, teamshort[l->pl_owner]);
			    pmessage(buf, l->pl_owner, MTEAM | MDEST, buf1);
			    if (status->tourn && 
				  realNumShips(l->pl_owner) < 2 &&
				  realNumShips(l->pl_flags & 
				    (FED|ROM|ORI|KLI)) < 2) {
				l->pl_flags |= PLCHEAP;
				/* Next person to take this planet will */
				/* get limited credit */
			    }
			    l->pl_owner = NOBODY;

			    /* out of Tmode?  Terminate.  8/2/91 TC */

			    if (NotTmode(ticks)) {
				if ((l->pl_flags & (FED|ROM|KLI|ORI)) !=
				    j->p_team) /* not in home quad, give them an extra one 8/26/91 TC */
				    rescue(STERMINATOR, j->p_no);
				rescue(STERMINATOR, j->p_no);
			    }
			    checkgen(oldowner, j);
			}
		    }
		    else { 	/* planet taken over */
			l->pl_armies++;
			j->p_planets++;
			j->p_genoplanets++;
			if (status->tourn && !(l->pl_flags & PLCHEAP)) {
			    j->p_stats.st_tplanets++;
			    status->planets++;
			    if ((l->pl_flags & PLCORE) && 
			      !(j->p_team & l->pl_flags) &&
			      (realNumShips(ALLTEAM & l->pl_flags)>2)) {
				j->p_stats.st_tplanets++;
				status->planets++;
			    }
			} else {
			    j->p_stats.st_planets++;
			}
			l->pl_flags &= ~PLCHEAP;

			j->p_kills += 0.25;
			checkmaxkills(i);
			(void) sprintf(buf, "%s taken over by %s (%c%c)",
				       l->pl_name,
				       j->p_name,
				       teamlet[j->p_team],
				       shipnos[j->p_no]);
			l->pl_owner = j->p_team;
			l->pl_info = j->p_team;
			checkwin(j);
			/* now tell new team */
			(void) sprintf(buf1, "%-3s->%-3s",
				       l->pl_name, teamshort[l->pl_owner]);
			pmessage(buf, l->pl_owner, MTEAM | MTAKE, buf1);
		    }
		} else { 
		    if (status->tourn && l->pl_armies < 4 && 
			  me->p_ship.s_type!=STARBASE) {
			j->p_stats.st_tarmsbomb+=(4 - l->pl_armies);
			status->armsbomb+=(4 - l->pl_armies);
		    }
		    j->p_armies--;
		    l->pl_armies++;
		}
	    } else if (j->p_flags & PFDOCK) {
		if (players[j->p_docked].p_team != j->p_team)
		    continue;
		if (players[j->p_docked].p_armies == players[j->p_docked].p_ship.s_maxarmies) {
		    continue;
		} else {
		    j->p_armies--;
		    players[j->p_docked].p_armies ++;
		}
	    }
	}

    }
}

blowup(sh)
struct player *sh;
{
    register int i;
    int dx, dy, dist;
    int damage;
    register struct player *j;

    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if (j->p_status != PALIVE)
	    continue;
	if (sh == j)
	    continue;
	/* the following keeps people from blowing up on others */
	me = sh;
	if ((me->p_whydead == KQUIT) && (friendlyPlayer(j)))
	    continue;
	if (me->p_whydead == KGHOST) /* ghostbusted ships do no damage 7/19/92 TC */
	    continue;
	dx = sh->p_x - j->p_x;
	dy = sh->p_y - j->p_y;
	if (ABS(dx) > SHIPDAMDIST || ABS(dy) > SHIPDAMDIST)
	    continue;
	dist = dx * dx + dy * dy;
	if (dist > SHIPDAMDIST * SHIPDAMDIST)
	    continue;
	if (dist > EXPDIST * EXPDIST) {
	    if (sh->p_ship.s_type == STARBASE)
		damage = 200 * (SHIPDAMDIST - sqrt((double) dist)) /
		    (SHIPDAMDIST - EXPDIST);
	    else if (sh->p_ship.s_type == SCOUT)
		damage = 75 * (SHIPDAMDIST - sqrt((double) dist)) /
		    (SHIPDAMDIST - EXPDIST);
	    else
		damage = 100 * (SHIPDAMDIST - sqrt((double) dist)) /
		    (SHIPDAMDIST - EXPDIST);
	}
	else {
	    if (sh->p_ship.s_type == STARBASE)
		damage = 200;
	    else if (sh->p_ship.s_type == SCOUT)
		damage = 75;
	    else
		damage = 100;
	}
	if (damage > 0) {
	    if (j->p_flags & PFSHIELD) {
		j->p_shield -= damage;
		if (j->p_shield < 0) {
		    j->p_damage -= j->p_shield;
		    j->p_shield = 0;
		}
	    }
	    else {
		j->p_damage += damage;
	    }
	    if (j->p_damage >= j->p_ship.s_maxdamage) {
		j->p_status = PEXPLODE;
		if (j->p_ship.s_type == STARBASE)
		    j->p_explode = 2*SBEXPVIEWS;
		else
		    j->p_explode = 10;
		sh->p_kills += 1.0 + 
		   j->p_armies * 0.1 + j->p_kills * 0.1;
		killerstats(sh->p_no, j);
		checkmaxkills(sh->p_no);
		killmess(j, sh);
		loserstats(i);
		j->p_whydead = KSHIP;
		j->p_whodead = sh->p_no;
	    }
	}
    }
}

void freemem(sig, code, scp)
int sig, code;
struct sigcontext *scp;
{
    register int i;
    register struct player *j;
    FILE *logfile;
    long timeoday;

    printf("freemem called\n");

    if (sig == SIGFPE) return;  /* Too many errors occur ... ignore them. */


    /* Blow players out of the game */
    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	j->p_status = PEXPLODE;
	j->p_whydead = KDAEMON;
	j->p_ntorp = 0;
	j->p_nplasmatorp = 0;
	j->p_explode = 600/PLAYERFUSE; /* ghost buster was leaving players in */
    }
    /* Kill waiting players */
    status->gameup = 0; /* say goodbye to xsg et. al. 4/10/92 TC */
    status->count=0;
    save_planets();
    sleep(2);
    shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0);
    exit(0);
}

void trapper(sig, code, scp)
int sig, code;
struct sigcontext *scp;
{

    printf("Signal caught: %d\n",sig);
    fflush(stdout);
    freemem(sig,code,scp);
}

save_planets()
{
    if (plfd >= 0) {
	(void) lseek(plfd, (long) 0, 0);
	(void) write(plfd, (char *) planets, sizeof(pdata));
    }
    if (glfd >= 0) {
	(void) lseek(glfd, (long) 0, 0);
	(void) write(glfd, (char *) status, sizeof(struct status));
    }
}

check_load()
{
    FILE *fp, *popen();
    char buf[100];
    char *s;
#ifndef SYSV
    char *rindex();
#endif
    float load;

  if (loadcheck) {
    printf("checking load\n");
    fp=popen("/usr/ucb/uptime", "r");
    if (fp==NULL) {
/*	status->gameup=0;*/
	return;
    }
    fgets(buf, 99, fp);
#ifndef SYSV
    s=rindex(buf, ':');
#endif
    if (s==NULL) {
/*	status->gameup=0;*/
	pclose(fp);
	return;
    }
    if (sscanf(s+1, " %f", &load) == 1) {
	if (load>=maxload && status->gameup==1) {
	    status->gameup=0;
	    sprintf(buf, "The load is %f, this game is going down", load);
	    pmessage(buf, 0, MALL, "GOD->ALL");
	} else if (load<maxload && status->gameup==0) {
	    status->gameup=1;
	    sprintf(buf, "The load is %f, this game is coming up", load);
	    pmessage(buf, 0, MALL, "GOD->ALL");
	}
	else {
	    sprintf(buf, "Load check%s", s);
	    buf[strlen(buf)-1]='\0';
	    pmessage(buf, 0, MALL, "GOD->ALL");
	}
    } else {
/*	status->gameup=0;*/
    }
    SIGNAL(SIGCHLD, SIG_DFL);
    pclose(fp);
    SIGNAL(SIGCHLD, reaper);
  }
}

/* This function checks to see if a team has been genocided --
   their last planet has been beamed down to zero.  It will set
   a timer on their home planet that will prevent them from
   couping for a random number of minutes.
*/
checkgen(loser, winner)
int loser;
struct player *winner;
{
    register int i;
    register struct planet *l;
    struct planet *homep;
    struct player *j;

    for (i = 0; i <= MAXTEAM; i++) /* maint: was "<" 6/22/92 TC */
	teams[i].s_plcount = 0;

    for (i = 0, l = &planets[i]; i < MAXPLANETS; i++, l++) {
	teams[l->pl_owner].s_plcount++; /* for now, recompute here */
/*	if (l->pl_owner == loser)
	    return;*/
	if (((l->pl_flags & ALLTEAM) == loser) && (l->pl_flags & PLHOME))
	    homep = l;		/* Keep track of his home planet for marking */
    }

    /* check to initiate surrender */

    if ((teams[loser].s_surrender == 0) &&
	(teams[loser].s_plcount == SURRSTART) &&
	(realNumShips(loser) >= tournplayers)) {
	char buf[80];

	sprintf(buf, "The %s %s %d minutes before the empire collapses.",
		teamNames[loser], teamVerbage[loser],
		binconfirm ? SURRLENGTH : SURRLENGTH*2/3);
	pmessage("", 0, MALL, "");
	pmessage(buf, 0, MALL, "GOD->ALL");
	sprintf(buf, "%d planets are needed to sustain the empire.",
		SURREND);
	pmessage(buf, 0, MALL, "GOD->ALL");
	pmessage("", 0, MALL, "");

	teams[loser].s_surrender = binconfirm ? /* shorter for borgs? */
	    SURRLENGTH : SURRLENGTH*2/3; /* start the clock 1/27/92 TC */
    }

    if (teams[loser].s_plcount > 0) return;

    teams[loser].s_surrender = 0;	/* stop the clock */

	    /* Give extra credit for neutralizing last planet! */
    winner->p_genoplanets++;
    winner->p_planets++;
    if (status->tourn) {
	winner->p_stats.st_tplanets++;
    } else {
	winner->p_stats.st_planets++;
    }

    checkwin(winner);		/* conquer is higher priority, but
				   genocide is a necessary condition
				   3/25/92 TC */

    /* Tell winning team what wonderful people they are (pat on the back!) */
    genocideMessage(loser, winner->p_team);
    /* Give more troops to winning team? */
    addTroops(loser, winner->p_team);

    /* kick out all the players on that team */
    /* makes it easy for them to come in as a different team */
    for (i = 0, j = &players[0]; i < MAXPLAYER; i++, j++) {
	if (j->p_team == loser) {
	    j->p_genoplanets=0;
	    j->p_genoarmsbomb=0;
	    j->p_armsbomb=0;
	    j->p_planets=0;
	}
	if (j->p_status == PALIVE && j->p_team == loser) {
	    j->p_status = PEXPLODE;
	    if (j->p_ship.s_type == STARBASE)
		j->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
	    else
		j->p_explode = 10/PLAYERFUSE;
	    j->p_whydead = KGENOCIDE;
	    j->p_whodead = winner->p_no;
	}
    }

    /* well, I guess they are losers.  Hose them now */
    homep->pl_couptime = MIN_COUP_TIME +
	(random() % (MAX_COUP_TIME - MIN_COUP_TIME));

    /* removed above dependency on PLANETFUSE and hardcoded */
    /* constants (now in defs.h).  30-60 min for a coup.  4/15/92 TC */
}


/* This function is called when a planet has been taken over.
   It checks all the planets to see if the victory conditions
   are right.  If so, it blows everyone out of the game and
   resets the galaxy
*/

/* Now also called by checkgen after a planet has been neutralized; this
   is somewhat inefficient but it works. 3/25/92 TC */

checkwin(winner)
struct player *winner;
{
    register int i, h, count;
    register struct planet *l, *m;
    register struct player *j;
    int team[MAXTEAM + 1];
    int teamtemp[MAXTEAM + 1];

    teams[0].s_plcount = 0;
    for (i = 0; i < 4; i++) {
	team[1<<i] = 0;
	teams[1<<i].s_plcount = 0;
    }
    
    /* count the number of home systems owned by one team */
    for (i = 0; i < 4; i++) {
	count = 0;
	teamtemp[NOBODY] = teamtemp[FED] = teamtemp[ROM] = teamtemp[KLI] =
	    teamtemp[ORI] = 0;

	for (h = 0, l = m = &planets[i*10]; h < 10; h++, l++) {
	    teams[l->pl_owner].s_plcount++;
	    teamtemp[l->pl_owner]++;
	}

	/* count neutral planets towards conquer 3/24/92 TC */

	if ((teamtemp[FED]+teamtemp[ROM]+teamtemp[KLI]) == 0) team[ORI]++;
	else if ((teamtemp[FED]+teamtemp[ROM]+teamtemp[ORI]) == 0) team[KLI]++;
	else if ((teamtemp[FED]+teamtemp[KLI]+teamtemp[ORI]) == 0) team[ROM]++;
	else if ((teamtemp[ROM]+teamtemp[KLI]+teamtemp[ORI]) == 0) team[FED]++;
    }

    /* check to end surrender */

    if ((teams[winner->p_team].s_surrender > 0) &&
	     (teams[winner->p_team].s_plcount == SURREND)) {
	char buf[80];
	pmessage("", 0, MALL, "");
	sprintf(buf, "The %s %s prevented collapse of the empire.",
		teamNames[winner->p_team], teamVerbage[winner->p_team]);
	pmessage(buf, 0, MALL, "GOD->ALL");
	pmessage("", 0, MALL, "");
	teams[winner->p_team].s_surrender = 0; /* stop the clock */
    }

    for (i = 0; i < 4; i++) {
#ifdef NEUTRAL
	if (team[1<<j] >= 4) {
#else
	if (team[1<<i] >= VICTORY) {
#endif
	    /* We have a winning team */
	    conquerMessage(winner->p_team);
	    for (h = 0, j = &players[0]; h < MAXPLAYER; h++, j++) {
		if (j->p_status == PFREE) continue;
		j->p_status = PEXPLODE;
		j->p_whydead = KWINNER;
		j->p_whodead = winner->p_no;
		if (j->p_ship.s_type == STARBASE)
		    j->p_explode = 2*SBEXPVIEWS/PLAYERFUSE;
		else
		    j->p_explode = 10/PLAYERFUSE;
		j->p_ntorp = 0;
		j->p_nplasmatorp = 0;
	    }
	    doResources();
	    longjmp(env, 0);
	}
    }
}

pmessage(str, recip, group, address)
char *str;
int recip;
int group;
char *address;
{
    struct message *cur;
    int mesgnum;

    if ((mesgnum = ++(mctl->mc_current)) >= MAXMESSAGE) {
	mctl->mc_current = 0;
	mesgnum = 0;
    }
    cur = &messages[mesgnum];
    cur->m_no = mesgnum;
    cur->m_flags = group;
    cur->m_recpt = recip;
    cur->m_from = 255;	/* From god */
    (void) sprintf(cur->m_data, "%s %s", address, str);
    cur->m_flags |= MVALID;
}

ghostmess(victim)
struct player	*victim;
{
    char 		buf[80];
    static float	ghostkills = 0.0;
    int 		i,k;

    ghostkills += 1.0 + victim->p_armies * 0.1 + victim->p_kills * 0.1;
    (void) sprintf(buf, "%s (%c%c) was kill %0.2f for the GhostBusters",
	victim->p_name, teamlet[victim->p_team], shipnos[victim->p_no], 
	ghostkills);
    pmessage(buf, 0, MALL, "GOD->ALL");
    if (victim->p_armies>0) {
	k=10*(remap[victim->p_team]-1);
	if (k>=0 && k<=30) for (i=0; i<10; i++) {
	    if (planets[i+k].pl_owner==victim->p_team) {
		planets[i+k].pl_armies += victim->p_armies;
		(void) sprintf(buf, "%s's %d armies placed on %s",
		    victim->p_name, victim->p_armies, planets[k+i].pl_name);
		pmessage(buf, 0, MALL | MGHOST, "GOD->ALL");
		break;
	    }
	}
    }
}

saveplayer(victim)
struct player *victim;
{
    int fd;

    if (victim->p_pos < 0) return;
    if (victim->p_stats.st_lastlogin == 0) return;
    if (victim->p_flags & PFROBOT) return;

    fd = open(PLAYERFILE, O_WRONLY, 0644);
    if (fd >= 0) {
	lseek(fd, 32 + victim->p_pos * sizeof(struct statentry) ,0);
	write(fd, (char *) &victim->p_stats, sizeof(struct stats));
	close(fd);
    }
}

killmess(victim, killer)
struct player *victim, *killer;
{
    char buf[80];
    
    if (victim->p_armies == 0) {
	(void) sprintf(buf, "%s (%c%c) was kill %0.2f for %s (%c%c)",
		       victim->p_name,
		       teamlet[victim->p_team],
		       shipnos[victim->p_no],
		       killer->p_kills,
		       killer->p_name,
		       teamlet[killer->p_team],
		       shipnos[killer->p_no]);
	pmessage(buf, 0, MALL | MKILL, "GOD->ALL");
    } else {
	(void) sprintf(buf, "%s (%c%c+%d armies) was kill %0.2f for %s (%c%c)",
		       victim->p_name,
		       teamlet[victim->p_team],
		       shipnos[victim->p_no],
		       victim->p_armies,
		       killer->p_kills,
		       killer->p_name,
		       teamlet[killer->p_team],
		       shipnos[killer->p_no]); 
	pmessage(buf, 0, MALL | MKILLA, "GOD->ALL");
	if (doosher) {
	    if (victim->p_ship.s_type == STARBASE)
		pmessage("GULP!!",0,MALL | MKILLA,"");
	    else if (victim->p_armies<4) 
		pmessage("doosh",0,MALL | MKILLA, "");
	    else if (victim->p_armies<8) 
		pmessage("Doosh",0,MALL | MKILLA, "");
	    else pmessage("DOOSH!",0,MALL | MKILLA,"");
	}
    }
}

/* Send in a robot to avenge the aggrieved team */
/* -1 for HK, -2 for Terminator, -3 for sticky Terminator */

/* if team in { FED, ROM, KLI, ORI }, a nonzero target means "fleet" mode */
rescue(team, target)
int team;
int target;
{
    char *arg1;
    int pid;

    if ((pid = fork()) == 0) {
	if (!debug) {
	    (void) close(0);
	    (void) close(1);
	    (void) close(2);
	}
	printf("made child\n");
	(void) SIGNAL(SIGALRM, SIG_DFL);
	switch (team) {
	  case FED:
	    arg1 = "-Tf";
	    break;
	  case ROM:
	    arg1 = "-Tr";
	    break;
	  case KLI:
	    arg1 = "-Tk";
	    break;
	  case ORI:
	    arg1 = "-To";
	    break;
	  case HUNTERKILLER:
   	    arg1 = "-Ti";	/* -1 means independent robot */
	    if (!debug) {
		execl(ROBOT, "robot", arg1, "-P", 0);
	    }
	    else {
		execl(ROBOT, "robot", arg1, "-P", "-d", 0);
	    }
	    break;
	  case TERMINATOR:	/* Terminator */
	    arg1 = "-Tt_";
	    arg1[3] = players[target].p_mapchars[1];
	    break;
	  case STERMINATOR:	/* sticky Terminator */
	    arg1 = "-Tt_";
	    arg1[3] = players[target].p_mapchars[1];
	    if (!debug)
		execl(ROBOT, "robot", arg1, "-s", 0);
	    else
		execl(ROBOT, "robot", arg1, "-s", "-d", 0);
	    break;
	}
	if (target > 0)	{	/* be fleet 8/28/91 TC */
	    execl(ROBOT, "robot", arg1, "-f", 0);
	    perror("execl");
	}
	else if (!debug) {
	    execl(ROBOT, "robot", arg1, 0);
	}
	else {
	    execl(ROBOT, "robot", arg1, "-d", 0);
	}
	exit(0);		/* NBT exit just in case a robot couldn't
				   be started				   */
	/* If we get here, we are hosed anyway */
    }
    else {
	if (debug) {
	    fprintf(stderr, "Forking robot: pid is %d\n", pid);
	}
    }
    SIGNAL(SIGCHLD,reaper);
    SIGNAL(SIGALRM,move);
}

#include <sys/resource.h>

/* Don't fear the ... */

void reaper(sig)
{
    static union wait status;
    static int pid;

    while ((pid = wait3(&status, WNOHANG, (struct rusage *) 0)) > 0) {
	if (debug) {
	    fprintf(stderr, "Reaping: pid is %d (status: %lX)\n",
		    pid, status);
	}
    }
}

unsigned char
getcourse(x, y, xme, yme)
int x, y, xme, yme;
{
	return((unsigned char) (atan2((double) (x - xme),
	    (double) (yme - y)) / 3.14159 * 128.));
}


unsigned char
get_bearing(xme, yme, x, y, dir)
int x, y;
{
  int phi=0;

  phi = ((int) (atan2((double) (x-xme), (double) (yme-y)) / 3.14159 * 128.));
  if (phi < 0)
    phi = 256 + phi;
  if (phi >= dir)
    return((unsigned char) (phi - dir));
  else
    return((unsigned char) (256 + phi - dir));
}

/* 
 * Find nearest hostile vessel and indicate if it is on the right or the left
 *   of the current heading.  A return value of -1 means left and +1 means right. 
 */
int torp_track_target(torp)
struct torp *torp;
{
  int i, closest = -1, min_dist=GWIDTH*2, dist=0, war_mask, x, y, owner;
  unsigned char phi=0, bearing=0, dir=0;
  
  x = torp->t_x;
  y = torp->t_y;
  dir = torp->t_dir;
  owner = torp->t_owner;
  war_mask = torp->t_war;
  for (i=0; i<MAXPLAYER; i++) {
    if ((isAlive(&players[i])) && 
	((owner!=i && (war_mask & players[i].p_team)) ||
	 (players[owner].p_team & (players[i].p_hostile | players[i].p_swar)))) {
      bearing = get_bearing(x, y, players[i].p_x, players[i].p_y, dir);
      /* 
       * An approximation to prevent the torpedo from tracking unreasonable targets:
       * If target is in an arc that the torp might reach, then it is valid.
       */
      /*
       * fprintf(stderr, "Target %d bearing [%d]:  heading %d,  fuse %d\n", i, 
       *      (int) bearing, (int) dir, torp->t_fuse);
       */
      if ((torp->t_turns*torp->t_fuse > 127) || 
	  (bearing < ((unsigned char) torp->t_turns*torp->t_fuse)) || 
	   (bearing > ((unsigned char) (256 - torp->t_turns*torp->t_fuse)))) {
	dist=((int) hypot((double) (x-players[i].p_x), 
			  (double) (y-players[i].p_y)));
	if (dist < min_dist) {
	  min_dist = dist;
	  closest = i;
	}
      }
    }
  }

  if (closest>=0) {
    if (get_bearing(x, y, players[closest].p_x, players[closest].p_y, dir) > 128)
      return (-1);  /* Target is on the left. */
    else
      return (1);  /* Target is on the right. */
  }

  return(0);  /* No target ... go straight. */
} /* torp_track_target() ------------------------------------------------- */

/* 
 * Find nearest hostile vessel and indicate if it is on the right or the left
 *   of the current heading.  A return value of -1 means left and +1 means right. 
 */
int plasma_track_target(torp)
struct plasmatorp *torp;
{
  int i, closest = -1, min_dist=GWIDTH*2, dist=0, war_mask, x, y, owner;
  unsigned char phi=0, bearing=0, dir=0;
  
  x = torp->pt_x;
  y = torp->pt_y;
  dir = torp->pt_dir;
  owner = torp->pt_owner;
  war_mask = torp->pt_war;
  for (i=0; i<MAXPLAYER; i++) {
    if ((isAlive(&players[i])) && 
	((owner!=i && (war_mask & players[i].p_team)) ||
	 (players[owner].p_team & (players[i].p_hostile | players[i].p_swar)))) {
      bearing = get_bearing(x, y, players[i].p_x, players[i].p_y, dir);
      /* 
       * An approximation to prevent the torpedo from tracking unreasonable targets:
       * If target is in an arc that the torp might reach, then it is valid.
       */
      /*
       * fprintf(stderr, "Target %d bearing [%d]:  heading %d,  fuse %d\n", i, 
       *      (int) bearing, (int) dir, torp->pt_fuse);
       */
      if ((torp->pt_turns*torp->pt_fuse > 127) || 
	  (bearing < ((unsigned char) torp->pt_turns*torp->pt_fuse)) || 
	   (bearing > ((unsigned char) (256 - torp->pt_turns*torp->pt_fuse)))) {
	dist=((int) hypot((double) (x-players[i].p_x), 
			  (double) (y-players[i].p_y)));
	if (dist < min_dist) {
	  min_dist = dist;
	  closest = i;
	}
      }
    }
  }

  if (closest>=0) {
    if (get_bearing(x, y, players[closest].p_x, players[closest].p_y, dir) > 128)
      return (-1);  /* Target is on the left. */
    else
      return (1);  /* Target is on the right. */
  }

  return(0);  /* No target ... go straight. */
} /* plasma_track_target() ------------------------------------------------- */

/* Give more troops to winning team in case they need them. */
addTroops(loser, winner) 
int loser, winner;
{
    int count=0;
    int i;
    char addrbuf[10];

    for (i=0; i<MAXPLAYER; i++) {
	if (players[i].p_status != PFREE && players[i].p_team == loser) count++;
    }
    /* Never give armies if 0-2 defenders */
    if (count<3) return;
    /* Sometimes give armies if 3-5 defenders */
    if (count<6 && (random() % count)==0) return;
    /* Always give armies if 6+ defenders */
    sprintf(addrbuf, "GOD->%s ", teamshort[winner]);
    pmessage("The recent victory has boosted enrollment!", winner, MTEAM, addrbuf);
    for (i=0; i<MAXPLANETS; i++) { 
	if (planets[i].pl_owner==winner) {
	    planets[i].pl_armies+=random() % (2*count);
	}
    }
}

surrenderMessage(loser)
int loser;
{
    char buf[80];
    FILE *conqfile;
    long curtime;

    conqfile=fopen(CONQFILE, "a");

/* TC 10/90 */
    time(&curtime);
    strcpy(buf,"\nSurrender! ");
    strcat(buf, ctime(&curtime));
    fprintf(conqfile,"  %s\n",buf);

    sprintf(buf, "The %s %s surrendered.", teamNames[loser],
	teamVerbage[loser]);
	
    fprintf(conqfile, "  %s\n", buf);
/*    sprintf(buf, "The %s:", teamNames[winner]);
    pmessage(buf, 0, MALL | MGENO, " ");
    fprintf(conqfile, "  %s\n", buf);
    displayBest(conqfile, winner, KGENOCIDE);*/
/*    sprintf(buf, "The %s:", teamNames[loser]);
    pmessage(buf, 0, MALL | MGENO, " ");
    fprintf(conqfile, "  %s\n", buf);
    displayBest(conqfile, loser, KGENOCIDE);
    pmessage("***********************************************************", 
	0, MALL | MGENO, " ");*/
    fprintf(conqfile, "\n");
    fclose(conqfile);
}

genocideMessage(loser, winner)
int loser, winner;
{
    char buf[80];
    FILE *conqfile;
    long curtime;

    conqfile=fopen(CONQFILE, "a");

/* TC 10/90 */
    time(&curtime);
    strcpy(buf,"\nGenocide! ");
    strcat(buf, ctime(&curtime));
    fprintf(conqfile,"  %s\n",buf);

    pmessage("***********************************************************", 
	0, MALL | MGENO, " ");
    sprintf(buf, "The %s %s been genocided by the %s.", teamNames[loser],
	teamVerbage[loser], teamNames[winner]);
    pmessage(buf, 0, MALL | MGENO, " ");
	
    fprintf(conqfile, "  %s\n", buf);
    sprintf(buf, "The %s:", teamNames[winner]);
    pmessage(buf, 0, MALL | MGENO, " ");
    fprintf(conqfile, "  %s\n", buf);
    displayBest(conqfile, winner, KGENOCIDE);
    sprintf(buf, "The %s:", teamNames[loser]);
    pmessage(buf, 0, MALL | MGENO, " ");
    fprintf(conqfile, "  %s\n", buf);
    displayBest(conqfile, loser, KGENOCIDE);
    pmessage("***********************************************************", 
	0, MALL | MGENO, " ");
    fprintf(conqfile, "\n");
    fclose(conqfile);
}

conquerMessage(winner)
int winner;
{
    char buf[80];
    FILE *conqfile;
	long curtime;

    conqfile=fopen(CONQFILE, "a");

/* TC 10/90 */
    time(&curtime);
    strcpy(buf,"\nConquer! ");
    strcat(buf, ctime(&curtime));
    fprintf(conqfile,"  %s\n",buf);

    pmessage("***********************************************************", 
	0, MALL | MCONQ, " ");
    sprintf(buf, "The galaxy has been conquered by the %s:", teamNames[winner]);
    pmessage(buf, 0, MALL | MCONQ, " ");
    fprintf(conqfile, "  %s\n", buf);
    sprintf(buf, "The %s:", teamNames[winner]);
    pmessage(buf, 0, MALL | MCONQ, " ");
    fprintf(conqfile, "  %s\n", buf);
    displayBest(conqfile, winner, KWINNER);
    pmessage("***********************************************************", 
	0, MALL | MCONQ, " ");
    fprintf(conqfile, "\n");
    fclose(conqfile);
}

typedef struct playerlist {
    char name[16];
    char mapchars[2];
    int planets, armies;
} Players;

displayBest(conqfile, team, type)
FILE *conqfile;
int team, type;
{
    register int i,k,l;
    register struct player *j;
    int planets, armies;
    Players winners[MAXPLAYER+1];
    char buf[80];
    int number;

    number=0;
    for (i = 0, j = &players[0]; i < MAXPLAYER; i++, j++) {
	if (j->p_team != team || j->p_status == PFREE) continue;
	if (type == KGENOCIDE) {
	    planets=j->p_genoplanets;
	    armies=j->p_genoarmsbomb;
	    j->p_genoplanets=0;
	    j->p_genoarmsbomb=0;
	} else {
	    planets=j->p_planets;
	    armies=j->p_armsbomb;
	}
	for (k = 0; k < number; k++) {
	    if (30 * winners[k].planets + winners[k].armies < 
		30 * planets + armies) {
		/* insert person at location k */
		break;
	    }
	}
	for (l = number; l >= k; l--) {
	    winners[l+1] = winners[l];
	}
	number++;
	winners[k].planets=planets;
	winners[k].armies=armies;
	strncpy(winners[k].mapchars, j->p_mapchars, 2);
	strncpy(winners[k].name, j->p_name, 16);
	winners[k].name[15]=0;	/* `Just in case' paranoia */
    }
    for (k=0; k < number; k++) {
	if (winners[k].planets != 0 || winners[k].armies != 0) {
	    sprintf(buf, "  %16s (%2.2s) with %d planets and %d armies.", 
		winners[k].name, winners[k].mapchars, winners[k].planets, winners[k].armies);
	    pmessage(buf, 0, MALL, " ");
	    fprintf(conqfile, "  %s\n", buf);
	}
    }
    return;
}

/* maximum number of agris that can exist in one quadrant */

#define AGRI_LIMIT 3

void doResources()
{
    int i;
    int agricount[4];



    do {
	bcopy(pdata, planets, sizeof(pdata)); /* bug, was before do 2/12/92 TMC */
        agricount[0] = 0;
        agricount[1] = 0;
        agricount[2] = 0;
        agricount[3] = 0;

	for (i = 0; i < 40; i++) {
	    /*	if (planets[i].pl_flags & PLHOME)
		planets[i].pl_flags |= (PLREPAIR|PLFUEL|PLAGRI);*/
	    if (random() % 4 == 0)
		planets[i].pl_flags |= PLREPAIR;
	    if (random() % 2 == 0)
		planets[i].pl_flags |= PLFUEL;
	    if (random() % 8 == 0) {
		planets[i].pl_flags |= PLAGRI; agricount[i/10]++;
	    }
	}
    } while ((agricount[0] > AGRI_LIMIT) || /* bug: used && 1/23/92 TC */
	     (agricount[1] > AGRI_LIMIT) ||
	     (agricount[2] > AGRI_LIMIT) ||
	     (agricount[3] > AGRI_LIMIT));
}
Newsgroups: rec.games.netrek