diff src/weapons.c @ 9:331055a97a9d

Initial revision
author darius
date Sat, 06 Dec 1997 04:37:05 +0000
parents
children ed82a42ba89d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/weapons.c	Sat Dec 06 04:37:05 1997 +0000
@@ -0,0 +1,1163 @@
+/*--------------------------------------------------------------------------
+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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <setjmp.h>
+
+#include "defs.h"
+#include "struct.h"
+#include "data.h"
+#include "daemonII.h"
+#include "weapons.h"
+#include "shmem.h"
+
+
+
+/*------------------------------VISIBLE FUNCTIONS-------------------------*/
+
+/*---------------------------------HOSTILE_TO-----------------------------*/
+/*
+ * This returns whether an object is hostile to another object.  It returns 1
+ * if we are hostile or at war with the object
+ */
+
+
+extern int inflict_damage();
+int torp_track_opportunity();
+extern void move_torp();
+extern void move_missile();
+
+int
+hostile_to(warmask, team, pl)
+  int warmask, team;
+  struct player *pl;
+{
+  return (warmask & pl->p_team) || (team & (pl->p_swar | pl->p_hostile));
+}
+
+
+
+
+/*----------------------------------EXPLODE-------------------------------*/
+/*
+ * This function decides which players to inflict damage on when a torp
+ * explodes.
+ */
+
+void
+explode_damage(torp, radius, why)
+  struct basetorp *torp;
+  int radius;
+  int why;
+{
+  register int i;		/* looping var */
+  int dx, dy, dist;		/* to calc distance from torp to players */
+  int damage;			/* to hold damage inflicted */
+  register struct player *j;	/* to point to players */
+
+  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+  {
+    if (j->p_status != PALIVE)	/* no need to check players not alive */
+      continue;
+
+    if (j->p_no == torp->bt_owner)	/* General Vote, September, 1995. */
+      continue;			/* Torps no longer damage owner. */
+
+    if ((torp->bt_status == TDET)	/* if torp was detted then */
+	&& ((j->p_no == torp->bt_owner) ||	/* cannot damage firing
+						 * player */
+	    ((j->p_no != torp->bt_whodet)	/* cannot damage players on
+						 * same team */
+	     && (j->p_team == players[torp->bt_whodet].p_team)))
+      )				/* except the detter */
+      continue;
+
+    dx = torp->bt_x - j->p_x;	/* calc delta x and y */
+    dy = torp->bt_y - j->p_y;
+    if (ABS(dx) > radius || ABS(dy) > radius)
+      continue;			/* continue if obviously too far */
+
+    dist = dx * dx + dy * dy;	/* calc distnace squared */
+    if (dist > radius * radius)	/* if not within damage distance */
+      continue;			/* then continue */
+    if (dist > EXPDIST * EXPDIST)	/* if not direct hit */
+      damage = torp->bt_damage * (radius - sqrt((double) dist)) /
+	(radius - EXPDIST);
+    else			/* else if direct hit */
+      damage = torp->bt_damage;	/* do torp damage */
+
+    if (damage > 0)
+    {				/* if damage was done then */
+      if (players[torp->bt_owner].p_hostile & j->p_team)	/* start war if */
+	players[torp->bt_owner].p_swar |= j->p_team;	/* necessary */
+      inflict_damage(&players[torp->bt_owner], &players[torp->bt_whodet],
+		     j, damage, why);
+    }
+  }
+}
+
+void
+explode(torp)
+  struct basetorp *torp;	/* pointer to exploding torp's struct */
+{
+  explode_damage(torp, DAMDIST, KTORP);
+  torp->bt_status = TEXPLODE;	/* set the torp to explode */
+  torp->bt_fuse = 10 / TORPFUSE;
+}
+
+
+
+
+/*---------------------------------PEXPLODE-------------------------------*/
+/*
+ * This function does the explosion of a plasma torp.  It goes through all
+ * players and damages them if they are close enough to get damaged
+ */
+
+void
+pexplode(plasmatorp)
+  struct plasmatorp *plasmatorp;/* ptr to plasma to explode */
+{
+  explode_damage(&plasmatorp->pt_base, PLASDAMDIST, KPLASMA);
+  plasmatorp->pt_status = PTEXPLODE;	/* set the plasma to explode */
+  plasmatorp->pt_fuse = 10 / PLASMAFUSE;
+}
+
+
+
+
+/*-------------------------------UDPHASER----------------------------------*/
+/*
+ * This function goes through all players and calcs the damage from a phaser
+ * hit if the phaser hit a target.
+ */
+
+void
+udphaser()
+{
+  register int i;		/* looping var */
+  register struct phaser *j;	/* to point to phaser being fired */
+  register struct player *victim;	/* to point to the poor victim */
+
+  for (i = 0, j = &phasers[i]; i < MAXPLAYER; i++, j++)
+  {				/* all players */
+    switch (j->ph_status)
+    {				/* check player's phaser status */
+     case PHFREE:		/* if not beging fired */
+      continue;			/* then continue */
+     case PHMISS:		/* if it missed */
+     case PHHIT2:		/* or ????? */
+      if (j->ph_fuse-- == 1)	/* dec count of phaser */
+	j->ph_status = PHFREE;	/* free it up if count done */
+      break;
+     case PHHIT:		/* if it hit someone then */
+      if (j->ph_fuse-- == players[i].p_ship.s_phaser.fuse)
+      {
+	victim = &players[j->ph_target];	/* get the victim */
+	if (players[i].p_hostile & victim->p_team)	/* start war if */
+	  players[i].p_swar |= victim->p_team;	/* necessary */
+	if (victim->p_status == PALIVE)	/* only damage if alive */
+	  inflict_damage(&players[i], 0, victim, j->ph_damage, KPHASER);
+      }				/* end of if phaser hit someone */
+      if (j->ph_fuse == 0)
+	j->ph_status = PHFREE;
+      break;
+    }				/* end of switch */
+  }				/* end of for loop through players */
+}
+
+
+
+/*----------------------------WEAP_NEAR_OBJECT-----------------------------*/
+/* This function checks for objects within *dist* of a weapon.           */
+/* this function will use the space grid */
+
+int
+weap_near_object(torp, type, dist)
+  struct basetorp *torp;
+  int type;
+  int dist;
+{
+  register struct planet *pl;	/* to point to the planets */
+  int dx, dy, i;
+
+  for (i = 0, pl = &planets[i]; i < NUMPLANETS; i++, pl = &planets[i])
+  {
+    if (PL_TYPE(*pl) != type)
+      /* if not of the type specified, don't bother */
+      continue;
+    dx = torp->bt_x - pl->pl_x;
+    dy = torp->bt_y - pl->pl_y;
+    if (ABS(dx) > dist || ABS(dy) > dist)
+      continue;			/* the obvious thing */
+    if (dx * dx + dy * dy < dist * dist)
+      return 1;			/* yep, it hit.  return a 1. */
+  }
+  return 0;			/* return that it should continue */
+}
+
+
+#if 0				/* I think this is now unused */
+/*----------------------------------NEAR----------------------------------*/
+/*
+ * This function checks to see if a player is close enough to a torp for the
+ * torp to explode on.  This function returns a 1 if the torp should explode
+ * and a zero if not.
+ */
+
+int
+near(torp)
+  struct torp *torp;		/* the torp to check for */
+{
+#if 1
+  return near_player(&torp->t_base, EXPDIST);
+#else
+  register int i;		/* looping var */
+  int dx, dy;			/* to calc torp-player distance */
+  register struct player *j;	/* to point to players */
+
+  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+  {
+    if (j->p_status != PALIVE)
+      continue;			/* don't check players not alive */
+    if (j->p_no == torp->t_owner)
+      continue;			/* no exploding on self */
+    if (!hostile_to(torp->t_war, torp->t_team, j))
+      continue;			/* disregard if both teams not at war */
+    dx = torp->t_x - j->p_x;	/* calc delta coords */
+    dy = torp->t_y - j->p_y;
+    if (ABS(dx) > EXPDIST || ABS(dy) > EXPDIST)
+      continue;			/* disregard if obviously too far */
+    if (dx * dx + dy * dy < EXPDIST * EXPDIST)
+      return 1;			/* if close enough to explode then return 1 */
+  }
+  return 0;			/* return that torp should continue */
+#endif
+}
+#endif
+
+int
+near_player(torp, dist)
+  struct basetorp *torp;	/* the torp to check for */
+  int dist;
+{
+  register int i;		/* looping var */
+  int dx, dy;			/* to calc torp-player distance */
+  register struct player *j;	/* to point to players */
+
+  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+  {
+    if (j->p_status != PALIVE)
+      continue;			/* don't check players not alive */
+    if (j->p_no == torp->bt_owner)
+      continue;			/* no exploding on self */
+    if (!hostile_to(torp->bt_war, torp->bt_team, j))
+      continue;			/* disregard if both teams not at war */
+    dx = torp->bt_x - j->p_x;	/* calc delta coords */
+    dy = torp->bt_y - j->p_y;
+    if (ABS(dx) > dist || ABS(dy) > dist)
+      continue;			/* disregard if obviously too far */
+    if (dx * dx + dy * dy < dist * dist)
+    {
+      torp->bt_whodet = i;	/* this guy is the detonator */
+      return 1;			/* if close enough to explode then return 1 */
+    }
+  }
+  return 0;			/* return that torp should continue */
+}
+
+
+int
+f_land(mis)			/* the fighter landing function */
+  struct missile *mis;		/* the fighter to check for */
+{
+  int dx, dy;			/* to calc fighter-player distance */
+  register struct player *j;	/* to point to players */
+
+  j = &players[mis->ms_owner];
+
+  if (!(j->p_armies < j->p_ship.s_maxarmies))
+    return 0;			/* no room! */
+  dx = mis->ms_x - j->p_x;	/* calc delta coords */
+  dy = mis->ms_y - j->p_y;
+  if (ABS(dx) > EXPDIST || ABS(dy) > EXPDIST)
+    return 0;			/* obviously too far */
+  if (dx * dx + dy * dy < EXPDIST * EXPDIST)
+    return 1;			/* if close enough to land then return 1 */
+
+  return 0;			/* return that fighter should continue */
+}
+
+
+/*----------------------------------PNEAR----------------------------------*/
+/*
+ * This function goes through the players and sees if there is a player close
+ * enough for a plasma torp to explode on.  This function returns a 1 if the
+ * plasma should explode and a 0 if it should continue to travel through
+ * spac.
+ */
+
+int
+pnear(plasmatorp)
+  struct plasmatorp *plasmatorp;/* the plasma torp to check for */
+{
+#if 1
+  return near_player(&plasmatorp->pt_base, EXPDIST);
+#else
+  register int i;		/* looping var */
+  int dx, dy;			/* to calc distances with */
+  register struct player *j;	/* to point to players */
+  /* fprintf(stderr, "ENTERING PNEAR\n"); */
+  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+  {
+    if (!(j->p_status == PALIVE))
+      continue;			/* don't check players not alive */
+    if (plasmatorp->pt_owner == j->p_no)
+      continue;			/* no exploding on self */
+    if (!hostile_to(plasmatorp->pt_war, plasmatorp->pt_team, j))
+      continue;			/* disregard if both teams not at war */
+    dx = plasmatorp->pt_x - j->p_x;	/* calc delta coords */
+    dy = plasmatorp->pt_y - j->p_y;
+    /* fprintf(stderr, "ENTERING UDORPS #1\n"); */
+    if (ABS(dx) > EXPDIST || ABS(dy) > EXPDIST)
+      continue;			/* disregard if obviously too far */
+    /* fprintf(stderr, "ENTERING UDORPS #2\n"); */
+    if (dx * dx + dy * dy < EXPDIST * EXPDIST)
+      return 1;			/* if close enough to explode then return 1 */
+    /* fprintf(stderr, "ENTERING UDORPS #3\n"); */
+  }
+  return 0;			/* return that plasma torp should continue */
+#endif
+}
+
+
+int
+outofbounds(x, y)
+  int x, y;
+{
+  return x < 0 || x > GWIDTH || y < 0 || y > GWIDTH;
+}
+
+/*--------------------------------UDTORPS----------------------------------*/
+/*
+ * This function updates the torps.  It goes through all torps and checks to
+ * see if they need to be updated.  It they are mving they track toward the
+ * nearest target.  this function also handles the explosion and makes sure
+ * that torps do not go off the edge of the galaxy.
+ */
+
+void
+udtorps()
+{
+  register int i;		/* looping var--to loop through torps */
+  int turn;			/* to get whether to go right or left */
+  int heading;			/* to hold torps heading */
+  register struct torp *j;	/* to point to torps */
+
+  for (i = 0, j = &torps[i]; i < MAXPLAYER * MAXTORP; i++, j++)
+  {
+    switch (j->t_status)
+    {				/* check status of torp */
+     case TFREE:		/* if torp not active then */
+      continue;			/* go on to next torp */
+     case TMOVE:
+     case TSTRAIGHT:		/* if torp moving then */
+      if (j->t_turns > 0)
+      {				/* if torp can turn then */
+	turn = torp_track_opportunity
+	  (&j->t_base, j->t_turns,
+	   configvals->improved_tracking[SS_PHOTON]);
+	/* should we go right or left */
+	if (turn < 0)
+	{			/* we will go left */
+	  heading = ((int) j->t_dir) - j->t_turns;	/* turn left */
+	  if (heading < 0)
+	    j->t_dir = heading + 256;
+	  /*
+	   * j->t_dir = ((heading < 0) ? ((unsigned char) (256 + heading)) :
+	   * ((unsigned char) heading));	* no underflow
+	   */
+	}
+	else if (turn > 0)
+	{			/* we will go right */
+	  heading = ((int) j->t_dir) + j->t_turns;	/* turn right */
+	  if (heading > 255)
+	    j->t_dir = heading - 256;
+	  /*
+	   * j->t_dir = ((heading > 255) ? ((unsigned char) (heading - 256))
+	   * : ((unsigned char) heading));	* no overflow
+	   */
+	}
+      }
+      j->t_x += (double) j->t_speed * Cos[j->t_dir] * WARP1;
+      j->t_y += (double) j->t_speed * Sin[j->t_dir] * WARP1;
+
+#if 0
+      if (outofbounds(j->t_x, j->t_y))
+      {				/* hit top wall? */
+	j->t_status = TEXPLODE;	/* so you cannot wall kill */
+	j->t_whodet = j->t_owner;
+	/* explode(&j->t_base);	eliminate self wallkills */
+	break;			/* done with this torp */
+      }
+#endif
+
+      move_torp(i, j->t_x, j->t_y, 1);
+
+      if (j->t_status == TMOVE)	/* if a TMOVE torp then */
+	j->t_dir += (lrand48() % 3) - 1;	/* make the torp wobble */
+      if (j->t_fuse-- <= 0)
+      {				/* dec torp's life and see if dead */
+	j->t_status = TFREE;	/* dead, free the torp */
+	move_torp(i, -1, -1, 1);
+	players[j->t_owner].p_ntorp--;	/* let player fire another */
+	break;			/* no more torp processing */
+      }
+      if ((sun_effect[SS_PHOTON] && weap_near_object(&j->t_base, PLSTAR, ORBDIST))
+	  ||
+	  (wh_effect[SS_PHOTON] && weap_near_object(&j->t_base, PLWHOLE, ORBDIST)))
+      {
+	/* did it hit a star or wormhole? */
+	j->t_whodet = j->t_owner;
+	explode(&j->t_base);
+	break;
+      }
+
+      if ((terrain_grid[(int) (j->t_x) / TGRID_GRANULARITY * TGRID_SIZE +
+			(int) (j->t_y) / TGRID_GRANULARITY].types
+	   & T_ASTEROIDS) &&
+	  ast_effect[SS_PHOTON])
+	if (TORP_HIT_AST > (lrand48() % 100))
+	{
+	  explode(&j->t_base);
+	  break;
+	}
+
+      if (near_player(&j->t_base, EXPDIST))
+      {
+	/* if torp near enough to hit */
+	explode(&j->t_base);	/* let torp explode on player */
+      }
+
+      break;
+     case TDET:		/* if torp was detted */
+      explode(&j->t_base);	/* make it explode */
+      break;			/* on to next torp */
+     case TEXPLODE:		/* if torp exploding */
+      if (j->t_fuse-- <= 0)
+      {				/* dec explosion timer */
+	j->t_status = TFREE;	/* if torp done, free it up */
+	move_torp(i, -1, -1, 1);
+	players[j->t_owner].p_ntorp--;	/* let player fire another */
+      }
+      break;			/* on to next torp */
+     case TOFF:
+      j->t_status = TFREE;
+      move_torp(i, -1, -1, 1);
+      players[j->t_owner].p_ntorp--;
+      break;
+     default:			/* Shouldn't happen */
+      j->t_status = TFREE;
+      break;
+    }				/* end of switch */
+  }				/* end of for */
+}
+
+void
+udmissiles()
+{
+  int i;
+  int x, y, turn;
+  struct missile *mis;
+  struct player *j;
+
+  for (i = 0; i < MAXPLAYER * NPTHINGIES; i++)
+  {
+    mis = &missiles[i];
+    switch (mis->ms_status)
+    {
+     case TFREE:
+      break;
+     case TLAND:
+      j = &players[mis->ms_owner];
+      j->p_ship.s_missilestored++;
+      j->p_armies = (int) (j->p_ship.s_missilestored / FAE_RATE);
+      mis->ms_status = TFREE;
+      j->p_nthingys--;
+      break;
+#if 0
+     case TRETURN:
+      j = &players[mis->ms_owner];
+      if (!(j->p_ship.s_nflags & SFNHASFIGHTERS))
+      {
+	mis->ms_type = MISSILETHINGY;	/* If the player no longer has em, */
+	mis->ms_status = TMOVE;	/* make his fighters kamikazes */
+	mis->fi_hasfired = 0;
+	break;
+      }
+
+
+      if (mis->ms_fuse-- <= 0)
+      {
+	mis->ms_status = TFREE;
+	move_missile(i, -1, -1, 1);
+	break;
+      }
+
+      if (((sun_effect[SS_MISSILE] && mis->ms_type == MISSILETHINGY
+	    || sun_effect[SS_FIGHTER] && mis->ms_type == FIGHTERTHINGY)
+	   && weap_near_object(&mis->ms_base, PLSTAR, ORBDIST)) ||
+	  ((wh_effect[SS_MISSILE] && mis->ms_type == MISSILETHINGY
+	    || wh_effect[SS_FIGHTER] && mis->ms_type == FIGHTERTHINGY)
+	   && weap_near_object(&mis->ms_base, PLWHOLE, ORBDIST)))
+      {
+	/* did it hit a star or wormhole? */
+	explode(&mis->ms_base);
+	break;
+      }
+      if (mis->ms_turns > 0)
+      {
+	turn = fighter_track_target(&mis->ms_base, mis->ms_turns);
+	mis->ms_dir = (unsigned char) (mis->ms_dir + turn * mis->ms_turns);
+      }
+      x = mis->ms_x + mis->ms_speed * Cos[mis->ms_dir] * WARP1;
+      y = mis->ms_y + mis->ms_speed * Sin[mis->ms_dir] * WARP1;
+      move_missile(i, x, y, 1);
+
+      if (mis->ms_fuse-- <= 0)
+      {
+	mis->ms_status = TFREE;
+	move_missile(i, -1, -1, 1);
+      }
+      else if (f_land(mis))
+      {
+	mis->ms_status = TLAND;
+	move_missile(i, -1, -1, 1);
+      }
+      break;
+#endif
+     case TRETURN:
+     case TMOVE:
+     case TSTRAIGHT:
+
+      if (mis->ms_fuse-- <= 0)
+      {
+	mis->ms_status = TFREE;
+	move_missile(i, -1, -1, 1);
+	break;
+      }
+
+      if (terrain_grid[(int) (mis->ms_x) / TGRID_GRANULARITY * TGRID_SIZE +
+		       (int) (mis->ms_y) / TGRID_GRANULARITY].types
+	  & T_ASTEROIDS)
+	if ((mis->ms_type == FIGHTERTHINGY) &&
+	    ast_effect[SS_FIGHTER] &&
+	    (FIGHTER_HIT_AST > (lrand48() % 100)))
+	{
+	  mis->ms_whodet = mis->ms_owner;
+	  explode(&mis->ms_base);
+	  break;
+	}
+	else if ((MISSILE_HIT_AST > (lrand48() % 100)) &&
+		 ast_effect[SS_MISSILE])
+	{
+	  mis->ms_whodet = mis->ms_owner;
+	  explode(&mis->ms_base);
+	  break;
+	}
+
+      if ((((sun_effect[SS_MISSILE] && mis->ms_type == MISSILETHINGY)
+	    || (sun_effect[SS_FIGHTER] && mis->ms_type == FIGHTERTHINGY))
+	   && weap_near_object(&mis->ms_base, PLSTAR, ORBDIST))
+	  ||
+	  (((wh_effect[SS_MISSILE] && mis->ms_type == MISSILETHINGY)
+	    || (wh_effect[SS_FIGHTER] && mis->ms_type == FIGHTERTHINGY))
+	   && weap_near_object(&mis->ms_base, PLWHOLE, ORBDIST)))
+      {
+	/* did it hit a star? */
+	explode(&mis->ms_base);
+	break;
+      }
+
+      j = &players[mis->ms_owner];
+
+      if (mis->ms_type == FIGHTERTHINGY &&
+	  !(j->p_ship.s_nflags & SFNHASFIGHTERS))
+      {
+	mis->ms_type = MISSILETHINGY;	/* If the player no longer has em, */
+	mis->ms_status = TMOVE;	/* make his fighters kamikazes */
+	mis->fi_hasfired = 0;
+	break;
+      }
+
+      if ((mis->ms_type == FIGHTERTHINGY)
+	  && ((mis->ms_fuse < .6 * j->p_ship.s_missile.fuse)
+	      || (mis->fi_hasfired))
+	  && mis->ms_status != TRETURN)
+	mis->ms_status = TRETURN;
+
+      if (mis->ms_turns > 0)
+      {
+	if (mis->ms_type == FIGHTERTHINGY)
+	{
+	  turn = fighter_track_target(&mis->ms_base, mis->ms_turns);
+	}
+	else
+	{
+	  turn = torp_track_opportunity
+	    (&mis->ms_base, mis->ms_turns,
+	     configvals->improved_tracking[SS_MISSILE]);
+	}
+	mis->ms_dir = (unsigned char) (mis->ms_dir + turn * mis->ms_turns);
+      }
+      x = mis->ms_x + mis->ms_speed * Cos[mis->ms_dir] * WARP1;
+      y = mis->ms_y + mis->ms_speed * Sin[mis->ms_dir] * WARP1;
+
+#if 0
+      if (outofbounds(x, y))
+      {
+	explode(&mis->ms_base);
+	break;
+      }
+#endif
+
+      move_missile(i, x, y, 1);
+
+      if (mis->ms_status != TSTRAIGHT)
+	mis->ms_dir += (lrand48() % 3) - 1;
+
+      if (mis->ms_type == MISSILETHINGY
+	  && near_player(&mis->ms_base, EXPDIST))
+      {
+	explode(&mis->ms_base);
+      }
+      else if (mis->ms_type == FIGHTERTHINGY
+	       && near_player(&mis->ms_base, FSTRIKEDIST)
+	       && !mis->fi_hasfired)
+      {
+	if (f_torp(mis))
+	  mis->fi_hasfired = 1;	/* if within strike range, fire a torp */
+      }
+
+      if (mis->ms_status == TRETURN &&
+	  f_land(mis))
+      {
+	mis->ms_status = TLAND;
+	move_missile(i, -1, -1, 1);
+      }
+
+      break;
+
+     case TDET:
+      explode(&mis->ms_base);
+      break;
+
+     case TEXPLODE:
+      if (mis->ms_fuse-- <= 0)
+      {
+	mis->ms_status = TFREE;
+	players[mis->ms_owner].p_nthingys--;
+	move_missile(i, -1, -1, 1);
+      }
+      break;
+     default:
+      mis->ms_status = TFREE;
+      break;
+    }
+  }
+}
+
+int 
+anticipate_impact(w, dx, dy, s, dir)
+  int w;			/* speed of torp */
+  int dx, dy;			/* distance to target */
+  int s, dir;			/* speed of target */
+{
+  float sdx, sdy;
+  float a, b, c, d;
+  float t;
+  float tdx, tdy;
+  float theta;
+
+#if 1				/* mathematically, these affect t, but not
+				 * the return value */
+  s *= WARP1 * TICKSPERSEC;
+  w *= WARP1 * TICKSPERSEC;
+#endif
+
+  sdx = s * Cos[dir];
+  sdy = s * Sin[dir];
+
+  a = s * (float) s - w * (float) w;
+  b = 2 * (sdx * (float) dx + sdy * (float) dy);
+  c = dx * (float) dx + dy * (float) dy;
+
+  if (a == 0)
+  {
+    t = -c / b;
+  }
+  else
+  {
+
+    d = b * b - 4 * a * c;
+    if (d < 0)
+      return -1;
+    d = sqrt(d);
+
+    if (a < 0)
+    {
+      a = -a;
+      b = -b;
+    }
+
+    t = (-b - d) / (2 * a);
+
+    if (t < 0)
+      t = (-b + d) / (2 * a);
+  }
+
+  if (t < 0)
+    return -1;
+
+  if (t > 10)
+    return -1;
+
+  tdx = dx / t + sdx;
+  tdy = dy / t + sdy;
+
+  theta = atan2(tdx, -tdy);
+  return (unsigned char) (int) (theta * 128 / 3.14159);
+}
+
+/*----------------------------TORP_TRACK_OPPORTUNITY----------------------*/
+/*
+ * This function finds the closest ship to a torp and returns -1 if the ship
+ * is to the left of the torp on its current heading and a 1 if the ship is
+ * to the right.  If no target is found then a 0 is return indicating the
+ * torp should go straight.
+ */
+
+int
+torp_track_opportunity(torp, turnspeed, smart)
+  struct basetorp *torp;	/* the torp to check for */
+  int turnspeed;
+  int smart;			/* which tracking algorithm? */
+{
+  int i;			/* looping var */
+  int closest;			/* to hold closest player */
+  int clbearing;		/* bearing to closest player */
+  int min_distsq;		/* to hold closest distance */
+  int war_mask;			/* who torp own is at war with */
+  int x, y;			/* to hold torps x, y coords */
+  int bearing;			/* to get bearing to hit player */
+  int dir;			/* to hold torps direction */
+  int range;
+
+  closest = -1;			/* initialize closest player--no plyr */
+  x = torp->bt_x;		/* get the coords of torp */
+  y = torp->bt_y;
+  dir = torp->bt_dir;		/* get torp's directions */
+  war_mask = torp->bt_war;	/* and who he as war with */
+
+  range = torp->bt_fuse * torp->bt_speed * WARP1;
+
+  min_distsq = range * range * 4;	/* intialize closest player distance */
+
+  for (i = 0; i < MAXPLAYER; i++)
+  {				/* check all other players */
+    int dx, dy;
+    if (!(isAlive(&players[i]) &&
+	  hostile_to(war_mask, torp->bt_team, &players[i])))
+      continue;			/* only do if player alive and at war */
+
+    dx = players[i].p_x - x;
+    dy = players[i].p_y - y;
+
+    if (ABS(dx) > range || ABS(dy) > range)
+      continue;			/* clearly out of range */
+
+    if (smart)
+    {
+      bearing = anticipate_impact(torp->bt_speed, dx, dy,
+				  players[i].p_speed, players[i].p_dir);
+      if (bearing < 0)
+	bearing = get_bearing(dx, dy, dir);
+      else
+	bearing = (unsigned char) (bearing - dir);
+    }
+    else
+    {
+      bearing = get_bearing(dx, dy, dir);
+    }
+    /* torps will only track to targets they have a reasonable chance */
+    /* of hitting */
+    if ((turnspeed * torp->bt_fuse > 127) ||
+	(bearing < ((unsigned char) turnspeed * torp->bt_fuse)) ||
+	(bearing > ((unsigned char) (256 - turnspeed * torp->bt_fuse))))
+    {
+      int distsq;
+      distsq = dx * dx + dy * dy;
+      if (distsq < min_distsq)
+      {				/* record it if it is */
+	min_distsq = distsq;	/* less than current closest */
+	closest = i;		/* player */
+	clbearing = bearing;
+      }
+    }
+  }
+  if (closest >= 0)
+  {				/* if a target found then */
+    if (clbearing > 128)
+      return (-1);		/* Target is on the left */
+    else
+      return (1);		/* Target is on the right */
+  }
+  return (0);			/* No target ... go straight. */
+}
+
+
+
+
+/*------------------------------UDPLASMATORPS-----------------------------*/
+/*
+ * This function updates the plasma torps.  It goes through all the plasma
+ * torps and if they are alive it adjusts their heading to track players.  It
+ * then moves the plasma torp and checks to see if the plasma should explode.
+ * It will ensure that a plasma torp explodes when it hits the edge of the
+ * galaxy.
+ */
+
+void
+udplasmatorps()
+{
+  register int i;		/* looping var--loop through plasmas */
+  int turn;			/* to get whether to go left or right */
+  int heading;			/* to hold plasma heading */
+  struct plasmatorp *j;		/* to point to a plasma */
+
+  for (i = 0, j = &plasmatorps[i]; i < MAXPLAYER * MAXPLASMA; i++, j++)
+  {
+    switch (j->pt_status)
+    {				/* check torp's status */
+     case PTFREE:		/* if plasma not being fired */
+      continue;			/* go to next plasma */
+     case PTMOVE:		/* if plasma moving */
+      turn = torp_track_opportunity
+	(&j->pt_base, j->pt_turns,
+	 configvals->improved_tracking[SS_PLASMA]);
+      /* should we go right or left */
+      if (turn < 0)
+      {				/* if left then */
+	heading = ((int) j->pt_dir) - j->pt_turns;
+	j->pt_dir = ((heading < 0) ? ((unsigned char) (256 + heading)) :
+		     ((unsigned char) heading));	/* no rollunder */
+      }
+      else if (turn > 0)
+      {				/* else if right */
+	heading = ((int) j->pt_dir) + j->pt_turns;
+	j->pt_dir = ((heading > 255) ? ((unsigned char) (heading - 256)) :
+		     ((unsigned char) heading));	/* no rollover */
+      }
+      j->pt_x += (double) j->pt_speed * Cos[j->pt_dir] * WARP1;
+
+#if 0
+      if (j->pt_x < 0)
+      {				/* if torp at left edge */
+	j->pt_x = 0;		/* set x to left edge */
+	pexplode(j);		/* make torp explode */
+	break;			/* go to next torp */
+      }
+      else if (j->pt_x > GWIDTH)
+      {				/* if torp is at right edge */
+	j->pt_x = GWIDTH;	/* set x to right edge */
+	pexplode(j);		/* make torp explode */
+	break;			/* on to next torp */
+      }
+#endif
+      j->pt_y += (double) j->pt_speed * Sin[j->pt_dir] * WARP1;
+#if 0
+      if (j->pt_y < 0)
+      {				/* if torp at top */
+	j->pt_y = 0;		/* set torp to top edge */
+	pexplode(j);		/* make torp explode */
+	break;			/* on to next torp */
+      }
+      else if (j->pt_y > GWIDTH)
+      {				/* if torp is at bottom */
+	j->pt_y = GWIDTH;	/* set y to bottom */
+	pexplode(j);		/* make torp explode */
+	break;			/* on to next torp */
+      }
+#endif
+
+      if (j->pt_fuse-- <= 0)
+      {				/* dec the torp fuse. if torp done */
+	j->pt_status = PTFREE;	/* free it up */
+	players[j->pt_owner].p_nplasmatorp--;	/* dec p-torps fired */
+	break;
+      }
+
+      if ((terrain_grid[(int) (j->pt_x) / TGRID_GRANULARITY * TGRID_SIZE +
+			(int) (j->pt_y) / TGRID_GRANULARITY].types
+	   & T_ASTEROIDS) &&
+	  ast_effect[SS_PLASMA])
+	if (PLASMA_HIT_AST > (lrand48() % 100))
+	{
+	  pexplode(j);
+	  break;
+	}
+
+      if ((sun_effect[SS_PLASMA] && weap_near_object(&j->pt_base, PLSTAR, ORBDIST))
+	  ||
+	  (sun_effect[SS_PLASMA] && weap_near_object(&j->pt_base, PLWHOLE, ORBDIST))
+      /* did it hit a star? */
+	  || pnear(j) /* or a player */ )
+      {
+	pexplode(j);
+      }
+      break;			/* on to next torp */
+     case PTDET:		/* if torp was detted */
+      pexplode(j);		/* make it explode */
+      break;			/* on to next torp */
+     case PTEXPLODE:		/* if torp is exploding */
+      if (j->pt_fuse-- <= 0)
+      {				/* dec the timer until torp dead */
+	j->pt_status = PTFREE;	/* set the torp free is timer zero */
+	players[j->pt_owner].p_nplasmatorp--;	/* dec ptorps fired by player */
+      }
+      break;
+     default:			/* Shouldn't happen */
+      j->pt_status = PTFREE;	/* free torp if it got screwed */
+      break;			/* on to next torp */
+    }
+  }
+}
+
+
+
+/*--------------------------------GET_BEARING------------------------------*/
+/*
+ * This function takes two set of coordinates and a direction.  One set of
+ * coordinates is the current coords of a trop.  The other set is some other
+ * coords you want to get a change in direction for.  The direction is the
+ * current direction of the torp.  The function returns the angle that the
+ * dir need to be changed by to travel to the new points.
+ */
+
+unsigned char
+get_bearing(dx, dy, dir)
+  int dx, dy;			/* delta x, y coords */
+  int dir;			/* current direction travelling */
+{
+  int phi;			/* to hold angle */
+
+  phi = (int) (atan2((double) dx, (double) -dy) / 3.14159 * 128.0);
+  if (phi < 0)			/* make phi positive */
+    phi = 256 + phi;
+  if (phi >= dir)
+    return ((unsigned char) (phi - dir));
+  else
+    return ((unsigned char) (256 + phi - dir));
+}
+
+/*------------------------------------------------------------------------*/
+
+
+
+
+/*------------------------------------------------------------------------*/
+/*--------------------------FIGHTER_TRACK_TARGET--------------------------*/
+/*
+ * This function finds the closest ship to a fighter and returns -1 if the
+ * ship is to the left of the fighter on its current heading and a 1 if the
+ * ship is to the right.  If no target is found then a 0 is return indicating
+ * the fighter should go straight.  Also returns fighters to the CV. If the
+ * player is locked onto an enemy ship, that's the only ship that gets
+ * checked.
+ */
+
+int
+fighter_track_target(mis, turnspeed)
+  struct missile *mis;		/* the torp to check for */
+  int turnspeed;
+{
+  int i;			/* looping var */
+  int closest;			/* to hold closest player */
+  int min_dist;			/* to hold closest distance */
+  int dist;			/* temp var to hold distance */
+  int war_mask;			/* who fighter own is at war with */
+  int x, y;			/* to hold fighters x, y coords */
+  int owner;			/* to hold fighters owner */
+  int bearing;			/* to get bearing to hit player */
+  int dir;			/* to hold fighters direction */
+  int range;
+  int dx, dy;
+
+  min_dist = GWIDTH * 2;	/* intialize closest player distance */
+  closest = -1;			/* initialize closest player--no plyr */
+  x = mis->ms_x;		/* get the coords of torp */
+  y = mis->ms_y;
+  dir = mis->ms_dir;		/* get fighter's directions */
+  owner = mis->ms_owner;	/* get the fighter's owner */
+  war_mask = mis->ms_war;	/* and who he as war with */
+
+  range = mis->ms_fuse * mis->ms_speed * WARP1;
+
+  for (i = 0; i < MAXPLAYER; i++)
+  {				/* check all other players */
+    if (mis->ms_status == TRETURN)
+    {
+      if (!(isAlive(&players[i])) || (owner != i))
+	continue;
+    }				/* if returning, only check owning player */
+    else if ((players[owner].p_flags & PFPLOCK) &&
+	     (players[owner].p_playerl != i))
+      continue;			/* if player is locked onto a player, only
+				 * check that player */
+    else if (!(isAlive(&players[i]) &&
+	       hostile_to(war_mask, mis->ms_team, &players[i])))
+      continue;			/* only do if player alive and at war */
+
+    dx = players[i].p_x - x;
+    dy = players[i].p_y - y;
+
+    if (ABS(dx) > range || ABS(dy) > range)
+      continue;			/* clearly out of range */
+
+    bearing = get_bearing(dx, dy, dir);
+    if ((turnspeed * mis->ms_fuse > 127) ||
+	(bearing < ((unsigned char) turnspeed * mis->ms_fuse)) ||
+	(bearing > ((unsigned char) (256 - turnspeed * mis->ms_fuse))))
+    {
+      dist = ihypot(dx, dy);
+      if (dist < min_dist)
+      {				/* record it if it is */
+	min_dist = dist;	/* less than current closest */
+	closest = i;		/* player */
+      }
+    }
+  }
+  if (closest >= 0)
+  {				/* if a target found then */
+    if (get_bearing(players[closest].p_x - x,
+		    players[closest].p_y - y, dir) > 128)
+    {
+      return (-1);		/* Target is on the left */
+    }
+    else
+      return (1);		/* Target is on the right */
+  }
+  return (0);			/* No target ... go straight. */
+}
+
+
+/*--------------------------------------------------------------------------*/
+/*------------------------------------F_TORP--------------------------------*/
+/* Checks to see if a valid target is within a certain forward firing angle */
+/* then fires a torpedo at that target.  A return value of 1 indicates firing */
+
+int
+f_torp(mis)
+  struct missile *mis;
+{
+  register int i;
+  int torp2fire = -1, targetdist = FSTRIKEDIST + 1, tdist, target;
+  unsigned char bearing;
+  register struct torp *k;
+  int dx, dy;
+  register struct player *j;	/* to point to players */
+
+  for (i = mis->ms_owner * MAXTORP, k = &torps[i];	/* Find a free torp */
+       i < mis->ms_owner * MAXTORP + MAXTORP; i++, k++)
+    if (k->t_status == TFREE)
+    {
+      torp2fire = i;
+      break;
+    }
+  if (torp2fire == -1)
+    return 0;
+
+
+  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
+  {
+    if (j->p_status != PALIVE)
+      continue;			/* don't check players not alive */
+    if (j->p_no == mis->ms_owner)
+      continue;			/* no firing on self */
+    if (!hostile_to(mis->ms_war, mis->ms_team, j))
+      continue;			/* disregard if both teams not at war */
+    if ((players[mis->ms_owner].p_flags & PFPLOCK) &&
+	(players[mis->ms_owner].p_playerl != i))
+      continue;			/* ignore if this isn't the target */
+
+    dx = mis->ms_x - j->p_x;	/* calc delta coords */
+    dy = mis->ms_y - j->p_y;
+    if (ABS(dx) > FSTRIKEDIST || ABS(dy) > FSTRIKEDIST)
+      continue;			/* disregard if obviously too far */
+
+    tdist = ihypot(dx, dy);
+    if (tdist < FSTRIKEDIST)
+    {
+      bearing = (int) get_bearing(dx, dy, mis->ms_dir);
+      targetdist = tdist;	/* record the target ship */
+      target = i;
+    }
+  }
+
+  if (targetdist < FSTRIKEDIST)
+  {
+    j = &players[mis->ms_owner];
+    k = &torps[torp2fire];
+    k->t_no = torp2fire;
+    k->t_status = TMOVE;
+    k->t_owner = mis->ms_owner;
+    k->t_team = mis->ms_team;
+
+    move_torp(torp2fire, mis->ms_x, mis->ms_y, 0);
+
+    k->t_damage = FTORP_DAMAGE;
+    k->t_speed = FTORP_SPEED;
+    k->t_war = j->p_hostile |
+      j->p_swar;
+    k->t_fuse = FTORP_FUSE + (lrand48() % 20);
+    k->t_turns = FTORP_TRACK;
+
+    /*
+     * here's the biggie -- what angle do I fire this torp at, so I have a
+     * reasonable chance of hitting?  Especially since I only get one shot.
+     * But, then, I have a bunch of buddies, too...
+     */
+
+    if ((mis->ms_no % MAXPLAYER % 3) == 0)
+      k->t_dir = mis->ms_dir;
+    else if ((mis->ms_no % MAXPLAYER % 3) == 1)
+      k->t_dir = mis->ms_dir - 8;
+    else if ((mis->ms_no % MAXPLAYER % 3) == 2)
+      k->t_dir = mis->ms_dir + 8;
+    return 1;
+  }
+  return 0;
+}
+
+
+/*----------END OF FILE--------*/