diff src/rmove.c @ 6:8c6d5731234d

First entry of Paradise Server 2.9 patch 10 Beta
author darius
date Sat, 06 Dec 1997 04:37:04 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rmove.c	Sat Dec 06 04:37:04 1997 +0000
@@ -0,0 +1,1539 @@
+/*--------------------------------------------------------------------------
+NETREK II -- Paradise
+
+Permission to use, copy, modify, and distribute this software and its
+documentation, or any derivative works thereof, for any NON-COMMERCIAL
+purpose and without fee is hereby granted, provided that this copyright
+notice appear in all copies.  No representations are made about the
+suitability of this software for any purpose.  This software is provided
+"as is" without express or implied warranty.
+
+    Xtrek Copyright 1986                            Chris Guthrie
+    Netrek (Xtrek II) Copyright 1989                Kevin P. Smith
+                                                    Scott Silvey
+    Paradise II (Netrek II) Copyright 1993          Larry Denys
+                                                    Kurt Olsen
+                                                    Brandon Gillespie
+--------------------------------------------------------------------------*/
+
+#include "config.h"
+#include <stdio.h>
+#include <signal.h>
+#include <math.h>
+#include "defs.h"
+#include "struct.h"
+#include "data.h"
+#include "weapons.h"
+#include "shmem.h"
+
+#define SIZEOF(s)		(sizeof (s) / sizeof (*(s)))
+#define AVOID_TIME		4
+#define AVOID_CLICKS		200
+
+#define BOREDOM_TIME		1200	/* 10 minutes since last torp fired
+					 * => become hostile to all 4/13/92
+					 * TC */
+
+#define AVOID_STAR 8000		/* Distance from star within which collision
+				 * avoidance is invoked. - MAK, 16-Jun-93 */
+#define AVOID_MELT 2000		/* Distance from star within which collision
+				 * prevention is invoked. - MAK, 16-Jun-93 */
+
+#define NORMALIZE(d) 		(((d) + 256) % 256)
+
+#define E_INTRUDER	0x01
+#define E_TSHOT		0x02
+#define E_PSHOT 	0x04
+#define E_TRACT 	0x08
+#define NOENEMY (struct Enemy *) -1
+
+struct Enemy
+{
+  int e_info;
+  int e_dist;
+  unsigned char e_course;	/* course to enemy */
+  unsigned char e_edir;		/* enemy's current heading */
+  unsigned char e_tcourse;	/* torpedo intercept course to enemy */
+  unsigned int e_flags;
+  int e_tractor;		/* who he's tractoring/pressoring */
+  int e_phrange;		/* his phaser range */
+  unsigned int e_hisflags;	/* his pflags. bug fix: 6/24/92 TC */
+};
+
+static int avoidTime;
+
+#define MESSFUSEVAL 200		/* maint: removed trailing ';' 6/22/92 TC */
+static int messfuse = MESSFUSEVAL;
+static char rmessage[110];
+static char tmessage[110];
+static char *rmessages[] = {
+  /* I got bored and actually made it say something: 'DIE SCUM' */
+  "1000100 1001001 1000101 100000 1010011 1000011 1010101 1001101 !"
+  "Crush, Kill, DESTROY!!",
+  "Run coward!",
+  "Hey!  Come on you Hoser!  I dare you!",
+  "Resistance is useless!",
+  "Dry up and blow away you weenie!",
+  "Go ahead, MAKE MY DAY!",
+  "Just hit '<shift> Q' ... and spare both of us the trouble",
+  "Is that REALLY you Kurt?",
+  "I have you now!",
+  "By the way, any last requests?",
+  "Dropping your shields is considered a peaceful act.",
+  "Drop your shields, and I MAY let you live ...",
+  "Don't you have midterms coming up or something?",
+  "Ya wanna Tango baby?",
+  "Hey! Outta my turf you filthy $t!",
+  "I seeeee you...",
+  "I claim this space for the $f.",
+  "Give up fool ... you're meat.",
+  "And another one bites the dust ...",
+  "Surrender ... NOW!",
+  "Hey!  Have you heard about the clever $t?  No?.. Me neither.",
+  "You have been registered for termination.",
+  "This'll teach you to mind your own business!",
+  "Man, you stupid $ts never learn!",
+  "ALL RIGHT, enough goofing off ... you're toast buster!",
+  "You pesky humans just NEVER give up, do you?",
+  "You're actually paying money so you can play this game?",
+  "My job is to put over-zealous newbies like you out of your misery.",
+  "How can you stand to be $T?!?  What a hideous life!",
+  "All $ts are losers!",
+  "$ts all suck.  Come join the $f!",
+  "The $f shall crush all filthy $ts like you!",
+  "TWINK",
+  "How can your friends stand to let you live?",
+  "Happy, Happy, Joy, Joy."
+};
+
+char *termie_messages[] = {
+  "$n:  Hasta la vista, Baby.",
+  "Come quietly, or there will be trouble. [thump] [thump]",
+  "Make your peace with GOD.",
+  "$n:  Say your prayers",
+  "This galaxy isn't big enough for the two of us, $n.",
+  "$n, I have a warrant for your arrest.",
+  "$n, self-destruct now.",
+  "Coming through.  Out of my way.",
+  "You feel lucky, punk?",
+  "Killing is our business.  Business is good.",
+  "$n, Die.  You have 10 seconds to comply.",
+  "You can run $n, but you can't hide!",
+  "Sorry, I am out of cool quotes.  Just die.",
+  "$n: duck."
+};
+
+extern int debug;
+extern int hostile;
+extern int sticky;
+extern int practice;
+extern int polymorphic;
+
+extern int startplanet;		/* CRD feature - MAK,  2-Jun-93 */
+
+extern int target;		/* robotII.c 7/27/91 TC */
+extern int phrange;		/* robotII.c 7/31/91 TC */
+extern int trrange;		/* robotII.c 8/2/91 TC */
+
+extern int dogslow;		/* robotII.c 8/9/91 TC */
+extern int dogfast;
+extern int runslow;
+extern int runfast;
+extern int closeslow;
+extern int closefast;
+
+unsigned char getcourse();
+char *robo_message();
+char *termie_message();		/* added 8/2/91 TC */
+
+
+extern void (*r_signal()) ();
+#ifndef	IRIX
+extern int fprintf();
+#endif
+extern unsigned int sleep();
+void exitRobot();
+extern void move_player();
+void messAll();
+int phaser_plasmas();
+void pmessage2();
+int do_repair();
+void go_home();
+extern int ntorp();
+extern int repair_off();
+extern int cloak_off();
+extern int phaser();
+extern int angdist();
+int isTractoringMe();
+extern int pressor_player();
+extern int tractor_player();
+int projectDamage();
+extern int set_course();
+extern int cloak_on();
+extern int shield_up();
+extern void detothers();
+extern int shield_down();
+extern int set_speed();
+extern int lock_planet();
+extern int orbit();
+extern int repair();
+#ifdef ROBOTSTATS
+extern void save_robot();
+#endif
+
+void
+rmove()
+{
+  register struct player *j;
+  register struct planet *l;
+  register int i;
+  register int burst;
+  register int numHits, tDir;
+  int avDir;
+  extern struct Enemy *get_nearest();
+  struct Enemy *enemy_buf;
+  struct player *enemy;
+  static int clock = 0;
+  static int avoid[2] = {-32, 32};
+  int no_cloak;
+  char towhom[80];
+  int timer;
+  static int lastTorpped = 0;	/* when we last fired a torp 4/13/92 TC */
+
+  clock++;
+  /* Check that I'm alive */
+  if (me->p_status == PEXPLODE)
+  {
+    r_signal(SIGALRM, SIG_IGN);
+    if (debug)
+      fprintf(stderr, "Robot: Augh! exploding.\n");
+
+
+    /* hack:  get rid of robot processes! */
+
+    if (1)
+    {
+      sleep(3);
+    }
+    else
+    {
+      while (me->p_status == PEXPLODE);
+    }
+    if (debug)
+      fprintf(stderr, "Robot: done exploding.\n");
+    if (1)
+    {
+      sleep(3);
+    }
+    else
+    {
+      while (me->p_ntorp > 0);
+    }
+    if (debug)
+      fprintf(stderr, "Robot: torps are gone.\n");
+    exitRobot();
+  }
+  if (me->p_status == PDEAD)
+  {
+    r_signal(SIGALRM, SIG_IGN);
+    /*
+     * me->p_status = PFREE; move_player(me->p_no, -1,-1, 1); exit(0);
+     */
+    exitRobot();
+  }
+  /* keep ghostbuster away */
+  me->p_ghostbuster = 0;
+
+  timer = 0;
+  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+  {
+    if ((j->p_status != PFREE) && !(j->p_flags & PFROBOT))
+      timer = 1;
+  }
+  if (!timer && !sticky)
+  {
+    r_signal(SIGALRM, SIG_IGN);
+    me->p_status = PFREE;
+    move_player(me->p_no, -1, -1, 1);
+    exit(0);
+  }
+  /* if I'm a Terminator, quit if he quits, and quit if he dies and */
+  /* I'm not "sticky" (-s) */
+
+  if (target >= 0)
+  {
+    if (players[target].p_status == PFREE)
+    {				/* he went away */
+      me->p_status = PEXPLODE;
+      return;
+    }
+    if ((!sticky) && (players[target].p_status != PALIVE))
+    {				/* he died */
+      me->p_status = PEXPLODE;
+      return;
+    }
+  }
+  /*
+   * If it's been BOREDOM_TIME updates since we fired a torp, become hostile
+   * to all races, if we aren't already, and if we're not a practice robot
+   * (intended for guardian bots). 4/13/92 TC
+   */
+
+  if ((clock - lastTorpped > BOREDOM_TIME) && (!practice) && (!hostile) &&
+      (me->p_team != 0))
+  {
+    messAll("Bored, Bored, Bored.");
+    hostile++;
+    declare_war(ALLTEAM);
+  }
+  /* Our first priority is to phaser plasma torps in nearby vicinity... */
+  /* If we fire, we aren't allowed to cloak... */
+  no_cloak = phaser_plasmas();
+
+  /* Find an enemy */
+
+  enemy_buf = get_nearest();
+
+  if ((enemy_buf != NULL) && (enemy_buf != NOENEMY))
+  {				/* Someone to kill */
+    enemy = &players[enemy_buf->e_info];
+    if (((lrand48() % messfuse) == 0) &&
+	(ihypot(me->p_x - enemy->p_x, me->p_y - enemy->p_y) < 20000))
+    {
+      /* change 5/10/21 TC ...neut robots don't message */
+      messfuse = MESSFUSEVAL;
+      if (me->p_team != 0)
+      {
+	sprintf(towhom, " %s->%s",
+		twoletters(&players[me->p_no]),
+		twoletters(&players[enemy->p_no]));
+	pmessage2(robo_message(enemy), enemy->p_no,
+		  MINDIV, towhom, me->p_no);
+      }
+      else if (target >= 0)
+      {
+	/*
+	 * MAK, 28-Apr-92 - send termie messages only to target
+	 * messAll(termie_message(enemy));
+	 */
+	sprintf(towhom, " %s->%s",
+		twoletters(&players[me->p_no]),
+		twoletters(&players[enemy->p_no]));
+	pmessage2(termie_message(enemy), enemy->p_no,
+		  MINDIV, towhom, me->p_no);
+      }
+    }
+    else if (--messfuse == 0)
+      messfuse = 1;
+    timer = 0;
+    if (debug)
+      fprintf(stderr, "%d) noticed %d\n", me->p_no, enemy_buf->e_info);
+  }
+  else if (enemy_buf == NOENEMY)
+  {				/* no more players. wait 1 minute. */
+    if (do_repair())
+    {
+      return;
+    }
+    go_home(0);
+    /*
+     * if (debug) fprintf(stderr, "%d) No players in game.\n", me->p_no);
+     */
+    return;
+  }
+  else if (enemy_buf == 0)
+  {				/* no one hostile */
+    /*
+     * if (debug) fprintf(stderr, "%d) No hostile players in game.\n",
+     * me->p_no);
+     */
+    if (do_repair())
+    {
+      return;
+    }
+    go_home(0);
+    timer = 0;
+    return;
+  }
+  /*
+   * Note a bug in this algorithm: * Once someone dies, he is forgotten.
+   * This makes robots particularly easy *  to kill on a suicide run, where
+   * you aim to where you think he will turn *  as you die.  Once dead, the
+   * robot will ignore you and all of your *  active torpedoes!
+   */
+
+  /*
+   * Algorithm: * We have an enemy. * First priority: shoot at target in
+   * range. * Second: Dodge stars, torps, and plasma torps. * Third: Get away
+   * if we are damaged. * Fourth: repair. * Fifth: attack.
+   */
+
+  /*
+   * * If we are a practice robot, we will do all but the second.  One * will
+   * be modified to shoot poorly and not use phasers.
+   */
+  /* Fire weapons!!! */
+  /*
+   * get_nearest() has already determined if torpedoes and phasers * will
+   * hit.  It has also determined the courses which torps and * phasers
+   * should be fired.  If so we will go ahead and shoot here. * We will lose
+   * repair and cloaking for the rest of this interrupt. * if we fire here.
+   */
+
+  if (practice)
+  {
+    no_cloak = 1;
+    if (enemy_buf->e_flags & E_TSHOT)
+    {
+      /*
+       * if (debug) fprintf(stderr, "%d) firing torps\n", me->p_no);
+       */
+      for (burst = 0; (burst < 3) && (me->p_ntorp < MAXTORP); burst++)
+      {
+	ntorp(enemy_buf->e_tcourse, TMOVE);
+      }
+    }
+  }
+  else
+  {
+    if (enemy_buf->e_flags & E_TSHOT)
+    {
+      /*
+       * if (debug) fprintf(stderr, "%d) firing torps\n", me->p_no);
+       */
+      for (burst = 0; (burst < 2) && (me->p_ntorp < MAXTORP); burst++)
+      {
+	repair_off();
+	cloak_off();
+	ntorp(enemy_buf->e_tcourse, TMOVE);	/* was TSTRAIGHT 8/9/91 TC */
+	no_cloak++;
+	lastTorpped = clock;	/* record time of firing 4/13/92 TC */
+      }
+    }
+    if (enemy_buf->e_flags & E_PSHOT)
+    {
+      /*
+       * if (debug) fprintf(stderr, "%d) phaser firing\n", me->p_no);
+       */
+      no_cloak++;
+      repair_off();
+      cloak_off();
+      phaser(enemy_buf->e_course);
+    }
+  }
+
+  /* auto pressor 7/27/91 TC */
+  /* tractor/pressor rewritten on 5/1/92... glitches galore :-| TC */
+
+  /*
+   * whoa, too close for comfort, or he's tractoring me, or headed in for me,
+   * or I'm hurt
+   */
+
+  /* a little tuning -- 0.8 on phrange and +/- 90 degrees in for pressor */
+
+  /*
+   * pressor_player(-1); this didn't do anything before, so we'll let the
+   * pressors disengage by themselves 5/1/92 TC
+   */
+  if (enemy_buf->e_flags & E_TRACT)
+  {				/* if pressorable */
+    if (((enemy_buf->e_dist < 0.8 * enemy_buf->e_phrange) &&
+	 (angdist(enemy_buf->e_edir, enemy_buf->e_course) > 64)) ||
+	(isTractoringMe(enemy_buf)) ||
+	(me->p_damage > 0))
+    {
+      if (!(enemy->p_flags & PFCLOAK))
+      {
+	if (debug)
+	  fprintf(stderr, "%d) pressoring %d\n", me->p_no,
+		  enemy_buf->e_info);
+	pressor_player(enemy->p_no);
+	no_cloak++;
+	repair_off();
+	cloak_off();
+      }
+    }
+  }
+  /* auto tractor 7/31/91 TC */
+
+  /* tractor if not pressoring and... */
+
+  /* tractor if: in range, not too close, and not headed +/- 90 degrees */
+  /* of me, and I'm not hurt */
+
+  if ((!(me->p_flags & PFPRESS)) &&
+      (enemy_buf->e_flags & E_TRACT) &&
+      (angdist(enemy_buf->e_edir, enemy_buf->e_course) < 64) &&
+      (enemy_buf->e_dist > 0.7 * enemy_buf->e_phrange))
+  {
+    if (!(me->p_flags & PFTRACT))
+    {
+      if (debug)
+	fprintf(stderr, "%d) tractoring %d\n", me->p_no,
+		enemy_buf->e_info);
+      tractor_player(enemy->p_no);
+      no_cloak++;
+    }
+  }
+  else
+    tractor_player(-1);		/* otherwise don't tractor */
+
+  /* Avoid stars - MAK, 16-Jun-93 */
+  /*
+   * This section of code allows robots to avoid crashing into stars. Within
+   * a specific range (AVOID_STAR), they will check to see if their current
+   * course will take them close to the star.  If so, course will be adjusted
+   * to avoid the star. Within a smaller range (AVOID_MELT), course is
+   * adjusted directly away from the star. Collision avoidance is all they
+   * will do for this round, other than shooting.
+   */
+  for (i = 0, l = &planets[0]; i < NUMPLANETS; i++, l++)
+  {
+    if (PL_TYPE(*l) == PLSTAR)
+    {
+      int dx, dy, stardir, newcourse = -1;
+      dx = ABS(l->pl_x - me->p_x);
+      dy = ABS(l->pl_y - me->p_y);
+      /* Avoid over box rather than circle to speed calculations */
+      if (dx < AVOID_STAR && dy < AVOID_STAR)
+      {
+	stardir = getcourse(l->pl_x, l->pl_y);
+
+	if (dx < AVOID_MELT && dy < AVOID_MELT)
+	{
+	  /* Emergency! Way too close! */
+	  newcourse = NORMALIZE(stardir - 128);
+	  if (debug)
+	  {
+	    fprintf(stderr, "Steering away from star %s, dir = %d\n",
+		    l->pl_name, newcourse);
+	  }
+	}
+	else if (angdist(me->p_dir, stardir) < 16)
+	{
+	  /* Heading towards star, so adjust course. */
+	  newcourse =
+	    NORMALIZE((me->p_dir < stardir) ? stardir - 32 : stardir + 32);
+	  if (debug)
+	  {
+	    fprintf(stderr, "Adjusting course away from star %s, dir = %d\n",
+		    l->pl_name, newcourse);
+	  }
+	}
+	if (newcourse != -1)
+	{			/* Change course and speed */
+	  me->p_desdir = newcourse;
+	  if (angdist(me->p_desdir, me->p_dir) > 64)
+	    me->p_desspeed = dogslow;
+	  else
+	    me->p_desspeed = dogfast;
+	  return;
+	}
+      }
+    }
+  }
+
+  /* Avoid torps */
+  /*
+   * This section of code allows robots to avoid torps. * Within a specific
+   * range they will check to see if * any of the 'closest' enemies torps
+   * will hit them. * If so, they will evade for four updates. * Evading is
+   * all they will do for this round, other than shooting.
+   */
+
+  if (!practice)
+  {
+    if ((enemy->p_ntorp < 5))
+    {
+      if ((enemy_buf->e_dist < 15000) || (avoidTime > 0))
+      {
+	numHits = projectDamage(enemy->p_no, &avDir);
+	if (debug)
+	{
+	  /*
+	   * fprintf(stderr, "%d hits expected from %d from dir = %d\n",
+	   * numHits, enemy->p_no, avDir);
+	   */
+	}
+	if (numHits == 0)
+	{
+	  if (--avoidTime > 0)
+	  {			/* we may still be avoiding */
+	    if (angdist(me->p_desdir, me->p_dir) > 64)
+	      me->p_desspeed = dogslow;
+	    else
+	      me->p_desspeed = dogfast;
+	    return;
+	  }
+	}
+	else
+	{
+	  /* Actually avoid Torps */
+	  avoidTime = AVOID_TIME;
+	  tDir = avDir - me->p_dir;
+	  /* put into 0->255 range */
+	  tDir = NORMALIZE(tDir);
+	  if (debug)
+	    fprintf(stderr, "mydir = %d avDir = %d tDir = %d q = %d\n",
+		    me->p_dir, avDir, tDir, tDir / 64);
+	  switch (tDir / 64)
+	  {
+	   case 0:
+	   case 1:
+	    set_course(NORMALIZE(avDir + 64));
+	    break;
+	   case 2:
+	   case 3:
+	    set_course(NORMALIZE(avDir - 64));
+	    break;
+	  }
+	  if (!no_cloak)
+	    cloak_on();
+
+	  if (angdist(me->p_desdir, me->p_dir) > 64)
+	    me->p_desspeed = dogslow;
+	  else
+	    me->p_desspeed = dogfast;
+
+	  shield_up();
+	  detothers();		/* hmm */
+	  if (debug)
+	    fprintf(stderr, "evading to dir = %d\n", me->p_desdir);
+	  return;
+	}
+      }
+    }
+    /*
+     * Trying another scheme. * Robot will keep track of the number of torps
+     * a player has * launched.  If they are greater than say four, the robot
+     * will * veer off immediately.  Seems more humanlike to me.
+     */
+
+    else if (enemy_buf->e_dist < 15000)
+    {
+      if (--avoidTime > 0)
+      {				/* we may still be avoiding */
+	if (angdist(me->p_desdir, me->p_dir) > 64)
+	  me->p_desspeed = dogslow;
+	else
+	  me->p_desspeed = dogfast;
+	return;
+      }
+      if (lrand48() % 2)
+      {
+	me->p_desdir = NORMALIZE(enemy_buf->e_course - 64);
+	avoidTime = AVOID_TIME;
+      }
+      else
+      {
+	me->p_desdir = NORMALIZE(enemy_buf->e_course + 64);
+	avoidTime = AVOID_TIME;
+      }
+      if (angdist(me->p_desdir, me->p_dir) > 64)
+	me->p_desspeed = dogslow;
+      else
+	me->p_desspeed = dogfast;
+      shield_up();
+      return;
+    }
+  }
+  /* Run away */
+  /*
+   * The robot has taken damage.  He will now attempt to run away from * the
+   * closest player.  This obviously won't do him any good if there * is
+   * another player in the direction he wants to go. * Note that the robot
+   * will not run away if he dodged torps, above. * The robot will lower his
+   * shields in hopes of repairing some damage.
+   */
+
+#define STARTDELTA 5000		/* ships appear +/- delta of home planet */
+
+  if (me->p_damage > 0 && enemy_buf->e_dist < 13000)
+  {
+    if (me->p_etemp > 900)	/* 90% of 1000 */
+      me->p_desspeed = runslow;
+    else
+      me->p_desspeed = runfast;
+    if (!no_cloak)
+      cloak_on();
+    repair_off();
+    shield_down();
+    set_course(enemy_buf->e_course - 128);
+    if (debug)
+      fprintf(stderr, "%d(%d)(%d/%d) running from %s %16s damage (%d/%d) dist %d\n",
+	      me->p_no,
+	      (int) me->p_kills,
+	      me->p_damage,
+	      me->p_shield,
+	      twoletters(enemy),
+	      enemy->p_login,
+	      enemy->p_damage,
+	      enemy->p_shield,
+	      enemy_buf->e_dist);
+    return;
+  }
+  /* Repair if necessary (we are safe) */
+  /*
+   * The robot is safely away from players.  It can now repair in peace. * It
+   * will try to do so now.
+   */
+
+  if (do_repair())
+  {
+    return;
+  }
+  /* Attack. */
+  /*
+   * The robot has nothing to do.  It will check and see if the nearest *
+   * enemy fits any of its criterion for attack.  If it does, the robot *
+   * will speed in and deliver a punishing blow.  (Well, maybe)
+   */
+
+  if ((enemy_buf->e_flags & E_INTRUDER) || (enemy_buf->e_dist < 15000)
+      || (hostile))
+  {
+    if ((!no_cloak) && (enemy_buf->e_dist < 10000))
+      cloak_on();
+    shield_up();
+    if (debug)
+      fprintf(stderr, "%d(%d)(%d/%d) attacking %s %16s damage (%d/%d) dist %d\n",
+	      me->p_no,
+	      (int) me->p_kills,
+	      me->p_damage,
+	      me->p_shield,
+	      twoletters(enemy),
+	      enemy->p_login,
+	      enemy->p_damage,
+	      enemy->p_shield,
+	      enemy_buf->e_dist);
+
+    if (enemy_buf->e_dist < 15000)
+    {
+      set_course(enemy_buf->e_course +
+		 avoid[(clock / AVOID_CLICKS) % SIZEOF(avoid)]);
+      if (angdist(me->p_desdir, me->p_dir) > 64)
+	set_speed(closeslow, 1);
+      else
+	set_speed(closefast, 1);
+    }
+    else
+    {
+      me->p_desdir = enemy_buf->e_course;
+      if (angdist(me->p_desdir, me->p_dir) > 64)
+	set_speed(closeslow, 1);
+      if (target >= 0)		/* 7/27/91 TC */
+	set_speed(12, 1);
+      else if (me->p_etemp > 900)	/* 90% of 1000 */
+	set_speed(runslow, 1);
+      else
+	set_speed(runfast, 1);
+    }
+  }
+  else
+  {
+    go_home(enemy_buf);
+  }
+}
+
+unsigned char
+getcourse(x, y)
+  int x, y;
+{
+  return ((unsigned char) (int) (atan2((double) (x - me->p_x),
+				 (double) (me->p_y - y)) / 3.14159 * 128.));
+}
+
+/* the 100000's here were once GWIDTHs... */
+struct
+{
+  int x;
+  int y;
+}   center[] =
+{
+  {
+
+    100000 / 2, 100000 / 2
+  },				/* change 5/20/91 TC was {0,0} */
+  {
+    100000 / 4, 100000 * 3 / 4
+  },				/* Fed */
+  {
+    100000 / 4, 100000 / 4
+  },				/* Rom */
+  {
+    0, 0
+  },
+  {
+    100000 *3 / 4, 100000 / 4
+  }  ,				/* Kli */
+  {
+    0, 0
+  }  ,
+  {
+    0, 0
+  }  ,
+  {
+    0, 0
+  }  ,
+  {
+    100000 *3 / 4, 100000 * 3 / 4
+  }
+};				/* Ori */
+
+/*
+ * This function means that the robot has nothing better to do. If there are
+ * hostile players in the game, it will try to get as close to them as it
+ * can, while staying in its own space. Otherwise, it will head to the center
+ * of its own space.
+ */
+/* CRD feature: robots now hover near their start planet - MAK,  2-Jun-93 */
+
+#define GUARDDIST 8000
+
+void
+go_home(ebuf)
+  struct Enemy *ebuf;
+{
+  int x, y;
+  double dx, dy;
+  struct player *j;
+
+  if (ebuf == 0)
+  {				/* No enemies */
+    /*
+     * if (debug) fprintf(stderr, "%d) No enemies\n", me->p_no);
+     */
+    if (target >= 0)
+    {
+      /* First priority, current target (if any) */
+      j = &players[target];
+      x = j->p_x;
+      y = j->p_y;
+    }
+    else if (startplanet == -1)
+    {
+      /* No start planet, so go to center of galaxy */
+      x = (GWIDTH / 2);
+      y = (GWIDTH / 2);
+    }
+    else
+    {
+      /* Return to start planet */
+      x = planets[startplanet].pl_x + (lrand48() % 2000) - 1000;
+      y = planets[startplanet].pl_y + (lrand48() % 2000) - 1000;
+    }
+  }
+  else
+  {				/* Let's get near him */
+    j = &players[ebuf->e_info];
+    x = j->p_x;
+    y = j->p_y;
+
+    if (startplanet != -1)
+    {
+      /* Get between enemy and planet */
+      int px, py;
+      double theta;
+
+      px = planets[startplanet].pl_x;
+      py = planets[startplanet].pl_y;
+      theta = atan2((double) (y - py), (double) (x - px));
+      x = px + GUARDDIST * cos(theta);
+      y = py + GUARDDIST * sin(theta);
+    }
+  }
+  /*
+   * if (debug) fprintf(stderr, "%d) moving towards (%d/%d)\n", me->p_no, x,
+   * y);
+   */
+
+  /*
+   * Note that I've decided that robots should never stop moving. * It makes
+   * them too easy to kill
+   */
+
+  me->p_desdir = getcourse(x, y);
+  if (angdist(me->p_desdir, me->p_dir) > 64)
+    set_speed(dogslow, 1);
+  else if (me->p_etemp > 900)	/* 90% of 1000 */
+    set_speed(runslow, 1);
+  else
+  {
+    dx = x - me->p_x;
+    dy = y - me->p_y;
+    set_speed((ihypot((int) dx, (int) dy) / 5000) + 3, 1);
+  }
+  cloak_off();
+}
+
+int
+phaser_plasmas()
+{
+  register struct plasmatorp *pt;
+  register int i;
+  int myphrange;
+
+  myphrange = phrange;		/* PHASEDIST * me->p_ship.s_phaserdamage /
+				 * 100; */
+  for (i = 0, pt = &plasmatorps[0]; i < MAXPLASMA * MAXPLAYER; i++, pt++)
+  {
+    if (pt->pt_status != PTMOVE)
+      continue;
+    if (i == me->p_no)
+      continue;
+    if (!(pt->pt_war & me->p_team) && !(me->p_hostile & pt->pt_team))
+      continue;
+    if (abs(pt->pt_x - me->p_x) > myphrange)
+      continue;
+    if (abs(pt->pt_y - me->p_y) > myphrange)
+      continue;
+    if (ihypot(pt->pt_x - me->p_x, pt->pt_y - me->p_y) > myphrange)
+      continue;
+    repair_off();
+    cloak_off();
+    phaser(getcourse(pt->pt_x, pt->pt_y));
+    return 1;			/* was this missing? 3/25/92 TC */
+    break;
+  }
+  return 0;			/* was this missing? 3/25/92 TC */
+}
+
+int
+projectDamage(eNum, dirP)
+  int eNum;
+  int *dirP;
+{
+  register int i, j, numHits = 0, mx, my, tx, ty, dx, dy;
+  double tdx, tdy, mdx, mdy;
+  register struct torp *t;
+
+  *dirP = 0;
+  for (i = 0, t = &torps[eNum * MAXTORP]; i < MAXTORP; i++, t++)
+  {
+    if (t->t_status == TFREE)
+      continue;
+    tx = t->t_x;
+    ty = t->t_y;
+    mx = me->p_x;
+    my = me->p_y;
+    tdx = (double) t->t_speed * Cos[t->t_dir] * WARP1;
+    tdy = (double) t->t_speed * Sin[t->t_dir] * WARP1;
+    mdx = (double) me->p_speed * Cos[me->p_dir] * WARP1;
+    mdy = (double) me->p_speed * Sin[me->p_dir] * WARP1;
+    for (j = t->t_fuse; j > 0; j--)
+    {
+      tx += tdx;
+      ty += tdy;
+      mx += mdx;
+      my += mdy;
+      dx = tx - mx;
+      dy = ty - my;
+      if (ABS(dx) < EXPDIST && ABS(dy) < EXPDIST)
+      {
+	numHits++;
+	*dirP += t->t_dir;
+	break;
+      }
+    }
+  }
+  if (numHits > 0)
+    *dirP /= numHits;
+  return (numHits);
+}
+
+int
+isTractoringMe(enemy_buf)
+  struct Enemy *enemy_buf;
+{
+  return ((enemy_buf->e_hisflags & PFTRACT) &&	/* bug fix: was using */
+	  !(enemy_buf->e_hisflags & PFPRESS) &&	/* e_flags 6/24/92 TC */
+	  (enemy_buf->e_tractor == me->p_no));
+}
+
+struct Enemy ebuf;
+
+struct Enemy *
+get_nearest()
+{
+  int pcount = 0;
+  register int i;
+  register struct player *j;
+  int intruder = 0;
+  int tdist;
+  double dx, dy;
+  double vxa, vya, l;		/* Used for trap shooting */
+  double vxt, vyt;
+  double vxs, vys;
+  double dp;
+
+  /* Find an enemy */
+  ebuf.e_info = me->p_no;
+  ebuf.e_dist = GWIDTH + 1;
+
+  pcount = 0;			/* number of human players in game */
+
+  if (target >= 0)
+  {
+    j = &players[target];
+    if (!((me->p_swar | me->p_hostile) & j->p_team))
+      declare_war(players[target].p_team);	/* make sure we're at war
+						 * 7/31/91 TC */
+
+    /* We have an enemy */
+    /* Get his range */
+    dx = j->p_x - me->p_x;
+    dy = j->p_y - me->p_y;
+    tdist = ihypot((int) dx, (int) dy);
+
+    if (j->p_status != POUTFIT)
+    {				/* ignore target if outfitting */
+      ebuf.e_info = target;
+      ebuf.e_dist = tdist;
+      ebuf.e_flags &= ~(E_INTRUDER);
+    }
+    /* need a loop to find hostile ships */
+    /* within tactical range */
+
+    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+    {
+      if ((j->p_status != PALIVE) || (j == me) ||
+	  ((j->p_flags & PFROBOT) && (!hostile)))
+	continue;
+      else
+	pcount++;		/* Other players in the game */
+      if (((j->p_swar | j->p_hostile) & me->p_team)
+	  || ((me->p_swar | me->p_hostile) & j->p_team))
+      {
+	/* We have an enemy */
+	/* Get his range */
+	dx = j->p_x - me->p_x;
+	dy = j->p_y - me->p_y;
+	tdist = ihypot((int) dx, (int) dy);
+
+	/* if target's teammate is too close, mark as nearest */
+
+	if ((tdist < ebuf.e_dist) && (tdist < 15000))
+	{
+	  ebuf.e_info = i;
+	  ebuf.e_dist = tdist;
+	  ebuf.e_flags &= ~(E_INTRUDER);
+	}
+      }				/* end if */
+    }				/* end for */
+  }
+  else
+  {				/* no target */
+    /* avoid dead slots, me, other robots (which aren't hostile) */
+    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+    {
+      if ((j->p_status != PALIVE) || (j == me) ||
+	  ((j->p_flags & PFROBOT) && (!hostile)))
+	continue;
+      else
+	pcount++;		/* Other players in the game */
+      if (((j->p_swar | j->p_hostile) & me->p_team)
+	  || ((me->p_swar | me->p_hostile) & j->p_team))
+      {
+	/* We have an enemy */
+	/* Get his range */
+	dx = j->p_x - me->p_x;
+	dy = j->p_y - me->p_y;
+	tdist = ihypot((int) dx, (int) dy);
+
+	/* Check to see if ship is near our planet. */
+	if (startplanet != -1)
+	{
+	  int px, py;
+	  px = planets[startplanet].pl_x;
+	  py = planets[startplanet].pl_y;
+
+	  intruder = (ihypot(j->p_x - px, j->p_y - py)
+		      < GUARDDIST);
+	}
+	if (tdist < ebuf.e_dist)
+	{
+	  ebuf.e_info = i;
+	  ebuf.e_dist = tdist;
+	  if (intruder)
+	    ebuf.e_flags |= E_INTRUDER;
+	  else
+	    ebuf.e_flags &= ~(E_INTRUDER);
+	}
+      }				/* end if */
+    }				/* end for */
+  }				/* end else */
+  if (pcount == 0)
+  {
+    return (NOENEMY);		/* no players in game */
+  }
+  else if (ebuf.e_info == me->p_no)
+  {
+    return (0);			/* no hostile players in the game */
+  }
+  else
+  {
+    j = &players[ebuf.e_info];
+
+    /* Get torpedo course to nearest enemy */
+    ebuf.e_flags &= ~(E_TSHOT);
+
+    vxa = (j->p_x - me->p_x);
+    vya = (j->p_y - me->p_y);
+    l = ihypot((int) vxa, (int) vya);	/* Normalize va */
+    if (l != 0)
+    {
+      vxa /= l;
+      vya /= l;
+    }
+    vxs = (Cos[j->p_dir] * j->p_speed) * WARP1;
+    vys = (Sin[j->p_dir] * j->p_speed) * WARP1;
+    dp = vxs * vxa + vys * vya;	/* Dot product of (va vs) */
+    dx = vxs - dp * vxa;
+    dy = vys - dp * vya;
+    l = hypot(dx, dy);		/* Determine how much speed is required */
+    dp = (float) (me->p_ship.s_torp.speed * WARP1);
+    l = (dp * dp - l * l);
+    if (l > 0)
+    {
+      double he_x, he_y, area;
+
+      /* Only shoot if within distance */
+      he_x = j->p_x + Cos[j->p_dir] * j->p_speed * 50 * WARP1;
+      he_y = j->p_y + Sin[j->p_dir] * j->p_speed * 50 * WARP1;
+      area = 50 * me->p_ship.s_torp.speed * WARP1;
+      if (ihypot(he_x - me->p_x, he_y - me->p_y) < area)
+      {
+	l = sqrt(l);
+	vxt = l * vxa + dx;
+	vyt = l * vya + dy;
+	ebuf.e_flags |= E_TSHOT;
+	ebuf.e_tcourse = getcourse((int) vxt + me->p_x, (int) vyt + me->p_y);
+      }
+    }
+    /* Get phaser shot status */
+    if (ebuf.e_dist < 0.8 * phrange)
+      ebuf.e_flags |= E_PSHOT;
+    else
+      ebuf.e_flags &= ~(E_PSHOT);
+
+    /* Get tractor/pressor status */
+    if (ebuf.e_dist < trrange)
+      ebuf.e_flags |= E_TRACT;
+    else
+      ebuf.e_flags &= ~(E_TRACT);
+
+
+    /* get his phaser range */
+    ebuf.e_phrange = PHASEDIST * j->p_ship.s_phaser.damage / 100;
+
+    /* get course info */
+    ebuf.e_course = getcourse(j->p_x, j->p_y);
+    ebuf.e_edir = j->p_dir;
+    ebuf.e_hisflags = j->p_flags;
+    ebuf.e_tractor = j->p_tractor;
+    /*
+     * if (debug) fprintf(stderr, "Set course to enemy is %d (%d)\n",
+     * (int)ebuf.e_course, (int) ebuf.e_course * 360 / 256);
+     */
+
+    /* check to polymorph */
+
+    if ((polymorphic) && (j->p_ship.s_type != me->p_ship.s_type) &&
+	(j->p_ship.s_type != ATT))
+    {				/* don't polymorph to ATT 4/8/92 TC */
+      extern int config();
+      extern int getship();
+      int old_shield;
+      int old_damage;
+      old_shield = me->p_ship.s_maxshield;
+      old_damage = me->p_ship.s_maxdamage;
+
+      getship(&(me->p_ship), j->p_ship.s_type);
+      config();
+      if (me->p_speed > me->p_ship.s_imp.maxspeed)
+	me->p_speed = me->p_ship.s_imp.maxspeed;
+      me->p_shield = (me->p_shield * (float) me->p_ship.s_maxshield)
+	/ (float) old_shield;
+      me->p_damage = (me->p_damage * (float) me->p_ship.s_maxdamage)
+	/ (float) old_damage;
+    }
+    return (&ebuf);
+  }
+}
+
+struct planet *
+get_nearest_planet()
+{
+  register int i;
+  register struct planet *l;
+  register struct planet *nearest;
+  int dist = GWIDTH;		/* Manhattan distance to nearest planet */
+  int ldist;
+
+  nearest = &planets[0];
+  for (i = 0, l = &planets[i]; i < NUMPLANETS; i++, l++)
+  {
+    if ((ldist = (abs(me->p_x - l->pl_x) + abs(me->p_y - l->pl_y))) <
+	dist)
+    {
+      dist = ldist;
+      nearest = l;
+    }
+  }
+  return nearest;
+}
+
+int
+do_repair()
+{
+  /* Repair if necessary (we are safe) */
+
+  register struct planet *l;
+  int dx, dy;
+  int dist;
+
+  l = get_nearest_planet();
+  dx = abs(me->p_x - l->pl_x);
+  dy = abs(me->p_y - l->pl_y);
+
+  if (me->p_damage > 0)
+  {
+    if ((me->p_swar | me->p_hostile) & l->pl_owner)
+    {
+      if (l->pl_armies > 0)
+      {
+	if ((dx < PFIREDIST) && (dy < PFIREDIST))
+	{
+	  if (debug)
+	    fprintf(stderr, "%d) on top of hostile planet (%s)\n", me->p_no, l->pl_name);
+	  return (0);		/* can't repair on top of hostile planets */
+	}
+	if (ihypot((int) dx, (int) dy) < PFIREDIST)
+	{
+	  if (debug)
+	    fprintf(stderr, "%d) on top of hostile planet (%s)\n", me->p_no, l->pl_name);
+	  return (0);
+	}
+      }
+      me->p_desspeed = 0;
+    }
+    else
+    {				/* if friendly */
+      if ((l->pl_flags & PLREPAIR) &&
+	  !(me->p_flags & PFORBIT))
+      {				/* oh, repair! */
+	dist = ihypot((int) dx, (int) dy);
+
+	if (debug)
+	  fprintf(stderr, "%d) locking on to planet %d\n",
+		  me->p_no, l->pl_no);
+	cloak_off();
+	shield_down();
+	me->p_desdir = getcourse(l->pl_x, l->pl_y);
+	lock_planet(l->pl_no);
+	me->p_desspeed = 4;
+	if (dist - (ORBDIST / 2) < (11500 * me->p_speed * me->p_speed) /
+	    me->p_ship.s_imp.dec)
+	{
+	  if (me->p_desspeed > 2)
+	  {
+	    set_speed(2, 1);
+	  }
+	}
+	if ((dist < ENTORBDIST) && (me->p_speed <= 2))
+	{
+	  me->p_flags &= ~PFPLLOCK;
+	  orbit();
+	}
+	return (1);
+      }
+      else
+      {				/* not repair, so ignore it */
+	me->p_desspeed = 0;
+      }
+    }
+    shield_down();
+    if (me->p_speed == 0)
+      repair();
+    if (debug)
+      fprintf(stderr, "%d) repairing damage at %d\n",
+	      me->p_no,
+	      me->p_damage);
+    return (1);
+  }
+  else
+  {
+    return (0);
+  }
+}
+
+
+void
+pmessage(str, recip, group, address)
+  char *str;
+  int recip;
+  int group;
+  char *address;
+{
+  pmessage2(str, recip, group, address, 255);
+}
+
+
+void
+pmessage2(str, recip, group, address, from)
+  char *str;
+  int recip;
+  int group;
+  char *address;
+  unsigned char from;
+{
+  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 = from;
+  (void) sprintf(cur->m_data, "%-9s %s", address, str);
+  cur->m_flags |= MVALID;
+}
+
+
+char *
+robo_message(enemy)
+  struct player *enemy;
+{
+  int i;
+  char *s, *t;
+
+  i = (lrand48() % (sizeof(rmessages) / sizeof(rmessages[0])));
+
+  for (t = rmessages[i], s = rmessage; *t != '\0'; s++, t++)
+  {
+    switch (*t)
+    {
+     case '$':
+      switch (*(++t))
+      {
+       case '$':
+	*s = *t;
+	break;
+       case 'T':		/* "a Fed" or "a Klingon" ... */
+	if (enemy->p_team != me->p_team &&
+	    enemy->p_team == ORI)
+	{
+	  strcpy(s, "an ");
+	}
+	else
+	{
+	  strcpy(s, "a ");
+	}
+	s = s + strlen(s);
+       case 't':		/* "Fed" or "Orion" */
+	if (enemy->p_team != me->p_team)
+	{
+	  switch (enemy->p_team)
+	  {
+	   case FED:
+	    strcpy(s, "Fed");
+	    break;
+	   case ROM:
+	    strcpy(s, "Romulan");
+	    break;
+	   case KLI:
+	    strcpy(s, "Klingon");
+	    break;
+	   case ORI:
+	    strcpy(s, "Orion");
+	    break;
+	  }
+	}
+	else
+	{
+	  strcpy(s, "TRAITOR");
+	}
+	s = s + strlen(s) - 1;
+	break;
+       case 'f':
+	switch (me->p_team)
+	{
+	 case FED:
+	  strcpy(s, "Federation");
+	  break;
+	 case ROM:
+	  strcpy(s, "Romulan empire");
+	  break;
+	 case KLI:
+	  strcpy(s, "Klingon empire");
+	  break;
+	 case ORI:
+	  strcpy(s, "Orion empire");
+	  break;
+	}
+	s = s + strlen(s) - 1;
+	break;
+       default:
+	*s = *t;
+      }
+      break;
+     default:
+      *s = *t;
+      break;
+    }
+  }
+  *s = '\0';
+  return (rmessage);
+}
+
+char *
+termie_message(enemy)
+  struct player *enemy;
+{
+  int i;
+  char *s, *t;
+
+  i = (lrand48() % (sizeof(termie_messages) / sizeof(termie_messages[0])));
+
+  for (t = termie_messages[i], s = tmessage; *t != '\0'; s++, t++)
+  {
+    switch (*t)
+    {
+     case '$':
+      switch (*(++t))
+      {
+       case '$':
+	*s = *t;
+	break;
+       case 'T':		/* "a Fed" or "a Klingon" ... */
+	if (enemy->p_team != me->p_team &&
+	    enemy->p_team == ORI)
+	{
+	  strcpy(s, "an ");
+	}
+	else
+	{
+	  strcpy(s, "a ");
+	}
+	s = s + strlen(s);
+       case 't':		/* "Fed" or "Orion" */
+	if (enemy->p_team != me->p_team)
+	{
+	  switch (enemy->p_team)
+	  {
+	   case FED:
+	    strcpy(s, "Fed");
+	    break;
+	   case ROM:
+	    strcpy(s, "Romulan");
+	    break;
+	   case KLI:
+	    strcpy(s, "Klingon");
+	    break;
+	   case ORI:
+	    strcpy(s, "Orion");
+	    break;
+	  }
+	}
+	else
+	{
+	  strcpy(s, "TRAITOR");
+	}
+	s = s + strlen(s) - 1;
+	break;
+       case 'f':
+	switch (me->p_team)
+	{
+	 case FED:
+	  strcpy(s, "Federation");
+	  break;
+	 case ROM:
+	  strcpy(s, "Romulan empire");
+	  break;
+	 case KLI:
+	  strcpy(s, "Klingon empire");
+	  break;
+	 case ORI:
+	  strcpy(s, "Orion empire");
+	  break;
+	}
+	s = s + strlen(s) - 1;
+	break;
+       case 'n':		/* name 8/2/91 TC */
+	strcpy(s, enemy->p_name);
+	s = s + strlen(s) - 1;
+	break;
+       default:
+	*s = *t;
+      }
+      break;
+     default:
+      *s = *t;
+      break;
+    }
+  }
+  *s = '\0';
+  return (tmessage);
+
+}
+
+void
+exitRobot()
+{
+  static char buf[80];
+
+  r_signal(SIGALRM, SIG_IGN);
+  if (me != NULL && me->p_team != ALLTEAM)
+  {
+    if (target >= 0)
+    {
+      strcpy(buf, "I'll be back.");
+      messAll(buf);
+    }
+    else
+    {
+      /*
+       * sprintf(buf, "%s %s (%s) leaving the game (%.16s@%.16s)",
+       * ranks[me->p_stats.st_rank].name, me->p_name, me->p_mapchars,
+       * me->p_login, me->p_monitor); messAll(buf);
+       */
+    }
+  }
+#ifdef ROBOTSTATS
+  save_robot();
+#endif
+
+  strcpy(buf, me->p_name);
+  /* something about Terminators hangs up the slot when a human */
+  /* tries to log in on that slot, so... */
+  memset(me, 0, sizeof(struct player));	/* confusion 8/5/91 TC */
+  strcpy(me->p_name, buf);
+
+  /*
+   * me->p_status = PFREE; move_player(me->p_no, -1,-1, 1);
+   */
+
+  /*
+   * all right, so zeroing out p_stats.st_tticks has undesireable side
+   * effects when the client tries to compute ratings...
+   */
+
+  me->p_stats.st_tticks = 1;	/* quick fix 3/15/92 TC */
+  exit(0);
+}
+
+void
+messAll(buf)
+  char *buf;			/* wasn't K&R before...oops 5/1/92 TC */
+{
+  static char addrbuf[20];
+
+  sprintf(addrbuf, " %s->ALL", twoletters(me));
+  pmessage2(buf, 0, MALL, addrbuf, me->p_no);
+}