view 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 source

/*--------------------------------------------------------------------------
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);
}