/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1997  Riley Rainey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 dated June, 1991.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program;  if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
 */

#include <math.h>
#include <stdio.h>
#include <string.h>
#include "sounds.h"
#include "damage.h"
#include "dis_if.h"
#include "effects.h"
#include "init.h"
#include "inventory.h"
#include "pm.h"
#include "weapon.h"
#include "../util/prng.h"
#include "../util/memory.h"
#include "../util/units.h"

#define m61a1_IMPORT
#include "m61a1.h"

#define BORE_CROSS_SIZE	7
#define MUZZLE_VELOCITY (3.2808 * 1036.0)
#define TRACER_MOD	10
#define OFFSET_ANGLE	(units_DEGtoRAD(3.0))		/* the gun points up at this angle */
#define RATE_OF_FIRE	(3000.0 / 60.0)		/* rounds per second */
#define FUZZ		(units_DEGtoRAD(1.0))		/* distribution due to vibration */

static int  select_m61a1(craft *);
static int  display_m61a1(craft *, craftType *, viewer *, int, int);
static int  update_m61a1(craft *);
static int  fire_m61a1(craft *);
static int  release_m61a1(craft *);
static void lcos(craft * c, viewer * u);
static int  isCannonHit(double min, craft * c);

static weapon_Type m61a1Desc =
{
    select_m61a1,      /* select */
    update_m61a1,      /* update */
    display_m61a1,     /* display procedure */
    fire_m61a1,        /* fire */
    release_m61a1,     /* fire button release */
};

/*
 *  We'll take a running average of our pitch and yaw rates to keep the
 *  aiming reticle from jumping all over the screen.
 */

#define HIST	8

typedef struct {
	double    pitch_hist[HIST];
	double    yaw_hist[HIST];
	int       cur;				/* current entry in the histories */
	int       count;			/* number of valid entries */
	double    pitch_total;
	double    yaw_total;
} hist_t;

static hist_t history[manifest_MAXPLAYERS];

static void m61a1_lookForCannonImpacts(craft * burst);

/*
 *  M61A1 selection function
 *
 *  A selection function normally determines whether there are any weapons
 *  of this type on-board.  If so, and the weapon system is functional
 *  (in other words, undamaged) then return 1; otherwise return 0.
 */

static int
select_m61a1(craft * c)
{

	hist_t   *p;

	p = &history[c->pIndex];
	p->count = p->cur = 0;
	p->pitch_total = p->yaw_total = 0.0;

/*
 *  FIXME. The cannon must be located at station zero, for now.  We should
 *  change that.
 */

	if (c->station[0].id == weapon_M61A1 && c->station[0].info > 0) {
		c->station[0].info4 = 0.0;
		return 1;
	}
	else
		return 0;

}

/*
 *  M61A1 display function
 *
 *  Update the HUD display strings associated with this weapon system.
 */

/*ARGSUSED */
static int
display_m61a1(craft * c, craftType * w, viewer * u, int fpm_x, int fpm_y)
{

	char      s[16];
	int tx, ty, m;
	Alib_Segment  seg[2];
	hist_t   *p;

	p = &history[c->pIndex];
	if ((m = p->cur = p->cur + 1) >= HIST)
		m = p->cur = 0;
	if (p->count != HIST)
		(p->count)++;
	else {
		p->pitch_total -= p->pitch_hist[m];
		p->yaw_total -= p->yaw_hist[m];
	}
	p->pitch_hist[m] = c->q;
	p->yaw_hist[m] = c->r;
	p->pitch_total += c->q;
	p->yaw_total += c->r;

	strcpy(c->leftHUD[2], "LCOS");
	sprintf(s, "%.3d %s", c->station[0].info, weapon_idToName(weapon_M61A1));
	strcpy(c->leftHUD[3], s);
	if (c->station[0].info3)
		strcpy(c->leftHUD[4], "FIRING");
	else
		strcpy(c->leftHUD[4], "");

/*
 *  Draw the boresight cross
 */

	m = (int) ((double) BORE_CROSS_SIZE * u->xscaleFactor);

	tx = u->v->focus.x;
	ty = u->v->focus.y - (int)(u->v->yres * u->zoom/100.0 * eye_to_screen_cm * 0.01 * OFFSET_ANGLE);
	seg[0].x1 = tx - m;
	seg[0].x2 = tx + m;
	seg[0].y1 = seg[0].y2 = ty;
	seg[1].x1 = seg[1].x2 = tx;
	seg[1].y1 = ty - m;
	seg[1].y2 = ty + m;
	VDrawSegments(u->v, seg, 2, HUDColor);

/*
 *  Plot the reticle.
 */

	lcos(c, u);

	return 0;

}

static int
fire_m61a1(craft * c)
{
	c->station[0].info3 |= 1;
	if (c->station[0].info > 0) {
		sounds_playSound(c, sounds_CannonFiring, TRUE);
	}
	return 0;
}

static int
release_m61a1(craft * c)
{
	c->station[0].info3 &= ~1;
	c->station[0].info4 = 0.0;
	sounds_stopSound(c, sounds_CannonFiring);
	return 0;
}

static char *
m61a1_update(craft * burst)
{
	double    dNorth, dEast, dmag;

/*
 *  Kill projectile streams after 10.0 seconds of flight or when
 *  they strike the ground.
 */

	if (curTime - burst->createTime > 10.0)
		return "lifetime expired";

	/* FIXME: actually, this is the impact with the sea level... */
	if (burst->w.z < 0.0) {
		return "impact with the ground";
	}

	burst->prevSg = burst->Sg;

	dNorth  = units_FEETtoMETERS(burst->Cg.x * deltaT);
	dEast   = units_FEETtoMETERS(burst->Cg.y * deltaT);
	burst->w.z -= units_FEETtoMETERS(burst->Cg.z * deltaT + units_earth_g * halfDeltaTSquared);

	dmag = sqrt(dNorth * dNorth + dEast * dEast);

	earth_updateLatLon(&burst->w, dNorth / dmag, dEast / dmag, dmag);
	earth_LatLonAltToXYZ(&burst->w, &burst->Sg);

	burst->Cg.z += units_earth_g * deltaT;
	
	// FIXME: not sure why we should look for cannon impacts only for local shells.
	// Should we include CT_DIS_CANNON too in the calculation?
	
	if( burst->type == CT_CANNON )
		m61a1_lookForCannonImpacts(burst);

	return NULL;

}


static void
m61a1_kill(craft * burst, char *reason)
{
	burst->type = CT_FREE;
}


static int
update_m61a1(craft * c)
{

	craft    *burst;
	int       i;
	VPoint    tmp, tmp1, mvel;
	double    fuzz, fuzzAngle, phiY, phiZ, tm, mv = MUZZLE_VELOCITY;

	if (c->station[0].info3 == 0)
		return 0;

/*
 *  Got any ammunition ?
 */

	if (c->station[0].info <= 0)
		return -1;

/*
 *  It is possible (especially on today's faster machines) that no rounds
 *  actually left the barrel during this time interval.
 */

	if (c->station[0].info4 > deltaT) {
		c->station[0].info4 -= deltaT;
	}
	else {

/*
 *  Allocate a projectile record
 */

		for ((i = 0, burst = &mtbl[0]); i < manifest_MAXPROJECTILES; (++i, ++burst))
			if (burst->type == CT_FREE) {
				break;
			}
		if (i == manifest_MAXPROJECTILES)
			return -1;
		
		memory_zero(burst);
		burst->type = CT_CANNON;
		burst->pIndex = i;
		burst->disId = dis_if_ID_NONE;

/*
 *  Determine how far we're fuzzed off the ideal boresight.
 */

		fuzz = 2.0 * prng_getDouble() - 1.0;  /* -1.0 <= fuzz <= 1.0 */
		fuzz = fuzz * FUZZ;
		fuzzAngle = 2.0 * M_PI * prng_getDouble();

/*
 *  Determine the initial velocity of the projectile stream.
 */

		phiZ = fuzz * sin(fuzzAngle);
		phiY = fuzz * cos(fuzzAngle);
		tm = mv * cos(OFFSET_ANGLE + phiZ);

		tmp.x = tm * cos(phiY);
		tmp.y = tm * sin(phiY);
		tmp.z = mv * sin(-OFFSET_ANGLE + phiZ);
		VTransform(&tmp, &(c->trihedral), &mvel);

		burst->owner = c->pIndex;
		burst->createTime = curTime;
		burst->curRoll = c->curRoll;
		burst->curPitch = c->curPitch;
		burst->curHeading = c->curHeading;

		burst->Cg = c->Cg;
		burst->Cg.x += mvel.x;
		burst->Cg.y += mvel.y;
		burst->Cg.z += mvel.z;

		/* Determine the initial position. */
		VTransform_(&c->cinfo->wStation[0], &c->trihedral, &tmp1);
		VReverseTransform_(&tmp1, &c->XYZtoNED, &tmp);
		burst->Sg.x = c->Sg.x + units_FEETtoMETERS(tmp.x);
		burst->Sg.y = c->Sg.y + units_FEETtoMETERS(tmp.y);
		burst->Sg.z = c->Sg.z + units_FEETtoMETERS(tmp.z);
		earth_XYZToLatLonAlt(&burst->Sg, &burst->w);
		burst->prevSg = burst->Sg;

		earth_XYZToLatLonAlt(&burst->Sg, &burst->w);
		earth_generateWorldToLocalMatrix(&burst->w, &burst->XYZtoNED);

		/* Subtract the number of rounds fired. */
		burst->offset = c->station[0].info4;
		burst->interval = deltaT;
		burst->rounds = 1 + (int) (RATE_OF_FIRE * (deltaT - burst->offset));
		burst->rounds = (burst->rounds > c->station[0].info) ?
			c->station[0].info : burst->rounds;

		/* Compute time interval until the next round leaves the barrel (in a
		 * subsequent time interval).
		 */
		c->station[0].info4 = 1.0 / RATE_OF_FIRE -
			fmod(deltaT - burst->offset, 1.0 / RATE_OF_FIRE);

		/* Include tracer information */
		burst->tracerMod = TRACER_MOD;
		burst->tracerVal = c->station[0].info2 % burst->tracerMod;
		if ( ! arcadeMode ) {
			c->station[0].info -= burst->rounds;
		}
		c->station[0].info2 += burst->rounds;

		burst->cinfo = inventory_craftTypeSearchByZoneAndName(NULL, weapon_idToName(weapon_M61A1));
		
		burst->update = m61a1_update;
		burst->kill   = m61a1_kill;

		dis_if_fireCannon(c, &burst->Sg, &burst->Cg, burst->rounds, (int) RATE_OF_FIRE);

	}

	if (c->station[0].info <= 0) {
		sounds_stopSound(c, sounds_CannonFiring);
	}
	return 0;

}


void m61a1_DISFire(int owner, VPoint *pos, VPoint *vel, int rounds)
{
	int i;
	craft *burst;

	for ((i = 0, burst = mtbl); i < manifest_MAXPROJECTILES; (++i, ++burst)) {
		if (burst->type == CT_FREE) {
			break;
		}
	}
	if (i == manifest_MAXPROJECTILES) {
		return;
	}
	memory_zero(burst);
	burst->pIndex = i;
	burst->disId = dis_if_ID_NONE; // not a tracked entity
	burst->type = CT_DIS_CANNON;
	strcpy(burst->name, "M61A1");
	burst->owner = owner;
	burst->createTime = curTime;
	burst->curRoll = 0.0;
	burst->curPitch = 0.0;
	burst->curHeading = 0.0;
	burst->Sg = *pos;
	burst->Cg = *vel;
	earth_XYZToLatLonAlt(pos, &burst->w); // (needed, check renderer)
	burst->prevSg = burst->Sg;
	burst->rounds = rounds;
	burst->tracerMod = TRACER_MOD;
	burst->tracerVal = 0;
	burst->offset = 0.0;
	burst->interval = deltaT;
	burst->cinfo = inventory_craftTypeSearchByZoneAndName(NULL, weapon_idToName(weapon_M61A1));
	burst->update = m61a1_update;
	burst->kill   = m61a1_kill;
}


static int
placeCannon(Viewport * vp, craft * burst, VMatrix * m, VPolySet *ps)
{

	double    t, startT;
	VPoint    v, s;
	int       i, k, n;
	VPoint   *q, tmp;
	VPolygon **p, *poly;

/*
 *  Reduce the shell path to a set of parametric equations.
 */

	v.x = burst->Sg.x - burst->prevSg.x;
	v.y = burst->Sg.y - burst->prevSg.y;
	v.z = burst->Sg.z - burst->prevSg.z;

/*
 *  Now add each tracer shell to the view.
 */

	startT = (burst->offset + (burst->tracerVal / RATE_OF_FIRE)) / burst->interval;

	for (t = startT; t <= 1.0; t += TRACER_MOD / (RATE_OF_FIRE * burst->interval)) {

		s.x = -v.x * t;
		s.y = -v.y * t;
		s.z = -v.z * t;

/* TODO: check should be based on local altitude */
/* underground? don't plot it */

		if (burst->w.z + s.z < 0.0) {
			continue;
		}

		n = burst->cinfo->object->numPolys;
		p = burst->cinfo->object->polygon;
		for (i = 0; i < n; ++i) {
			poly = VCopyPolygon(p[i]);
			for ((k = 0, q = poly->vertex); k < poly->numVtces; (++k, ++q)) {
				VTransform(q, m, &tmp);
				tmp.x += s.x;
				tmp.y += s.y;
				tmp.z += s.z;
				*q = tmp;
			}
			VTransformPolygon(poly, &vp->eyeSpace);
			VPolySet_Add(ps, poly);
		}
	}

	return 0;
}

weapon_Type *
m61a1_new(void)
{

	craftType *c;
	FILE     *f;
	dis_entity_type em1 =
	{2, 9, 225, 2, 1, 0, 0};
	dis_entity_type em2 =
	{2, 1, 222, 2, 3, 0, 0};

	c = inventory_craftTypeNew(NULL);
	c->name = memory_strdup( weapon_idToName(weapon_M61A1) );

	c->entityType = em1;
	c->altEntityType = em2;

	c->placeProc = placeCannon;

	f = init_fopen("tracer.obv", "r");
	c->object = VReadObject(f);
	fclose(f);

	m61a1Desc.w = c;

	return &m61a1Desc;
}

/*
 *  plotReticle :  draws the aiming reticle onto the HUD.
 *
 *  An aiming reticle is a circle with an inner arc that clues the
 *  pilot as to the range of the target.  Twelve tic marks are used
 *  to demarcate the range -- each tic represents 1000 feet of
 *  distance.
 */

static struct {
	double    x, y;
} ticTable[12] = {

	{
		0.0, -1.0
	},
	{
		0.5, -0.866
	},
	{
		0.866, -0.5
	},
	{
		1.0, 0.0
	},
	{
		0.866, 0.5
	},
	{
		0.5, 0.866
	},
	{
		0.0, 1.0
	},
	{
		-0.5, 0.866
	},
	{
		-0.866, 0.5
	},
	{
		-1.0, 0.0
	},
	{
		-0.866, -0.5
	},
	{
		-0.5, -0.866
	}
};

/*ARGSUSED */
static void
plotReticle(craft * c, viewer * u, int x, int y, int range)
{

#define RETICLE_SIZE	65
#define RANGE_SIZE	59
#define TICK_SIZE	(RETICLE_SIZE + 12)

	int i, size, xt, yt, size1, xt1, yt1, nseg, arc;
	Alib_Segment  seg[16];
	register double rs, rt;

	size = (int) (RETICLE_SIZE * u->xscaleFactor);
	if ((size & 1) == 0)
		++size;					/* insure it is an odd value */
	xt = x - size / 2;
	yt = y - size / 2;
	rs = size / 2;
	rt = ((TICK_SIZE + 1) / 2) * u->xscaleFactor;

	for (i = 0; i < 12; i++) {
		seg[i].x1 = x +
			(int) (rs * ticTable[i].x + 0.5);
		seg[i].y1 = y +
			(int) (rs * ticTable[i].y + 0.5);
		seg[i].x2 = x +
			(int) (rt * ticTable[i].x + 0.5);
		seg[i].y2 = y +
			(int) (rt * ticTable[i].y + 0.5);
	}

	nseg = 12;
	Alib_drawArc(u->v->w, xt, yt, size, size, 0,
			 360 * 64, HUDColor);

	if (range != -1) {
		if (range > 12000)
			range = 12000;
		arc = -range * 23040 / 12000;
		size1 = (int) (RANGE_SIZE * u->xscaleFactor);
		if ((size1 & 1) == 0)
			++size1;
		if (size == size1)
			--size1;
		xt1 = x - size1 / 2;
		yt1 = y - size1 / 2;
		Alib_drawArc(u->v->w, xt1, yt1, size1, size1,
				 90 * 64, arc, HUDColor);
	}
	VDrawSegments(u->v, seg, nseg, HUDColor);

}

void
lcos(craft * c, viewer * u)
{

/*
 *  How does this LCOS thing work, anyway?
 *
 *  First, let me say that this may or, more probably, may not look
 *  anything like the technique used in a real fighter's gun sighting
 *  system.  Having said that, here's the basic assumptions and steps
 *  used to generate the reticle on the HUD:
 *
 *  1)  Using radar, we know the distance to the target.  Assuming that the
 *      radar is in working order and is locked onto something.
 *
 *  2)  We know the muzzle velocity of the shells we're firing and can
 *      use that value to closely estimate the flight time of the shells
 *      to a target that's a certain distance away (the target).  Call
 *      this value "fTime".
 *
 *  3)  Use the muzzle velocity vector to determine the
 *      relative position (w.r.t. our craft) of a shell "fTime" seconds into
 *      its flight.  Call this vector "pos".
 *
 *  4)  Gravity will accelerate the shells. Use d = 0.5 * g * t ^ 2
 *      to add a distance (along the Z axis that will approximate the
 *      effect of gravity.  Add that to "pos".
 *
 *  5)  We know our pitch and yaw rates.  These rotations will have the
 *      effect of visually "bending" the cannon stream, from the pilot's
 *      perspective.  Use the values to generate a matrix to transform
 *      "pos" by an amount proportional to our "fTime" value.
 *
 *  6)  Convert our "pos" vector to screen x,y coordinates and call
 *      plotReticle.
 */

	double    fTime, range, gm, pitch_rate, yaw_rate;
	VPoint    pos, tmp;
	VPoint    zg, z;
	VMatrix   rotation;
	int       clue, x, y;
	hist_t   *hist;

	hist = &history[c->pIndex];

/*  Step 1  */

	if (c->curRadarTarget >= 0)
		range = c->targetDistance;
	else
		range = 2500.0;

/*  Step 2  */

	fTime = range / MUZZLE_VELOCITY;

/*  Step 3  */

	pos.x = fTime * MUZZLE_VELOCITY * cos(OFFSET_ANGLE);
	pos.y = 0.0;
	pos.z = -fTime * MUZZLE_VELOCITY * sin(OFFSET_ANGLE);

/*  Step 4  */

	zg.x = zg.y = 0.0;
	zg.z = 1.0;

	VReverseTransform_(&zg, &c->trihedral, &z);

	gm = 0.5 * units_earth_g * fTime * fTime;
	pos.x += z.x * gm;
	pos.y += z.y * gm;
	pos.z += z.z * gm;

/*  Step 5  */

	VIdentMatrix(&rotation);

	pitch_rate = hist->pitch_total / (double) hist->count;
	yaw_rate = hist->yaw_total / (double) hist->count;

	if (pitch_rate != 0.0)
		VRotate(&rotation, YRotation, -pitch_rate * fTime);
	if (yaw_rate != 0.0)
		VRotate(&rotation, ZRotation, -yaw_rate * fTime);

	VTransform(&pos, &rotation, &tmp);

/*  Step 6  */

	x = (u->v->Middl.x + (int) (tmp.y * u->v->Scale.x / tmp.x)) >> 2;
	y = (u->v->Middl.y + (int) (tmp.z * u->v->Scale.y / tmp.x)) >> 2;

	if (c->curRadarTarget >= 0)
		clue = (int) c->targetDistance;
	else
		clue = -1;

	plotReticle(c, u, x, y, clue);

	m61a1_lcos_last_pos.x = x;
	m61a1_lcos_last_pos.y = y;
}

typedef struct _entry {
	double    time;
	double    min;
	VPoint    Sg;
	VPoint    rvel;
	craft    *c;
	struct _entry *next;
} entry;


/**
 * Track cannon shells and look for impacts with aircraft.  This algorithm
 * tracks each shell's flight.
 */
static void
m61a1_lookForCannonImpacts(craft * burst)
{

	craft    *c;
	entry     p[manifest_MAXPLAYERS], *list, *q, *r, *rprev;
	VPoint    v, s0, prevSg, Vm, zeroVec = {0, 0, 0};
	double    t, d, roundT, startT, explosion_diameter_meters;
	int       j;
	double    worldLocation[3], entityLocation[3];

	startT = burst->offset / burst->interval;

	Vm.x = burst->Sg.x - burst->prevSg.x;
	Vm.y = burst->Sg.y - burst->prevSg.y;
	Vm.z = burst->Sg.z - burst->prevSg.z;

	for (roundT = startT; roundT < 1.0; roundT += 1.0 / (RATE_OF_FIRE * burst->interval)) {

		prevSg.x = burst->prevSg.x - Vm.x * (1.0 - roundT);
		prevSg.y = burst->prevSg.y - Vm.y * (1.0 - roundT);
		prevSg.z = burst->prevSg.z - Vm.z * (1.0 - roundT);

		list = (entry *) NULL;
		for (c = ptbl, j = 0; j < manifest_MAXPLAYERS; ++j, ++c) {

			if (c->type == CT_FREE || burst->owner == c->pIndex)
				continue;

/*
 * Reduce the relative motion of this object to a the parametric system
 * of equations:
 *              x(t) = vx * t + s0x
 *              y(t) = vy * t + s0y
 *              z(t) = vz * t + s0z
 *
 * We can then compute the time of perigee (closest pass) along with
 * the associated minimum distance.
 */

			v.x = c->Sg.x - c->prevSg.x - Vm.x;
			v.y = c->Sg.y - c->prevSg.y - Vm.y;
			v.z = c->Sg.z - c->prevSg.z - Vm.z;
			s0.x = c->prevSg.x - prevSg.x;
			s0.y = c->prevSg.y - prevSg.y;
			s0.z = c->prevSg.z - prevSg.z;

/*
 * Compute time of minimum distance between the two objects (note that units
 * here are UPDATE_INTERVAL seconds).
 */

			t = -(v.x * s0.x + v.y * s0.y + v.z * s0.z) /
				(v.x * v.x + v.y * v.y + v.z * v.z);

/*
 *  If the closest pass occurs during this update interval, check for a hit.
 *  We'll build a linked list of all craft that this projectile may strike
 *  during this period, arranged in ascending order by time of "perigee"
 *  (closest pass).  We'll then test for strikes.  If a projectile misses
 *  the first object, then it may have struck subsequent objects in the
 *  list ...
 */

/*
 *  One special case occurs when a target or missile's turn suddenly
 *  changes the perigee time from positive to negative.  If the missile
 *  is within hitting range at t=0 and the time of perigee is negative,
 *  then zap 'em.
 */

			if (t < 0.0) {
				d = sqrt(s0.x * s0.x + s0.y * s0.y +
						 s0.z * s0.z);
				if (isCannonHit(d, c)) {
					t = 0.0;
				}
			}
			if (t >= 0.0 && t <= 1.0) {
				q = &p[j];

				q->Sg = prevSg;
				q->Sg.x += Vm.x * t;
				q->Sg.y += Vm.y * t;
				q->Sg.z += Vm.z * t;

				q->rvel = v;

				if (list == (entry *) NULL) {
					q->next = list;
					list = q;
				}
				else if (list->time > t) {
					q->next = list;
					list = q;
				}
				else {
					for (rprev = list, r = list->next; r != (entry *) NULL;) {
						if (r->time > t)
							break;
						rprev = r;
						r = r->next;
					}
					if (rprev != list)
						rprev->next = q;
					q->next = r;
				}
				q->time = t;
				q->c = c;
				q->min = sqrt(pow(v.x * t + s0.x, 2.0) +
							  pow(v.y * t + s0.y, 2.0) +
							  pow(v.z * t + s0.z, 2.0));
			}
		}

/*
 *  Now look for cannon hits in the list of perigees.
 */

		for (r = list; r != (entry *) NULL; r = r->next)
			if (isCannonHit(r->min, r->c)) {
				effects_new_explosion(&(r->Sg), &zeroVec, 1.0, 2.0, 0.5);
				/* can only damage local player */
				if (r->c->type != CT_DIS_PLANE)
					if (damage_absorbDISDamage(r->c, &burst->cinfo->entityType, 0, 0,
										0.0,
										VMagnitude(&r->rvel),
										&explosion_diameter_meters) == 0) {

						r->c->kill(r->c, "cannon fire");
					}
				worldLocation[0] = r->Sg.x;
				worldLocation[1] = r->Sg.y;
				worldLocation[2] = r->Sg.z;
				entityLocation[0] = 0.0;
				entityLocation[1] = 0.0;
				entityLocation[2] = 0.0;
				dis_if_detonation(
					&burst->cinfo->entityType, /* munition type */
					ptbl[burst->owner].disId, /* munition owner */
					r->c->disId, /* target */
					burst->disId, /* always zero -- not a tracked entity */
					worldLocation, entityLocation,
					(double *) &r->rvel );
				break;
			}
	}
}

/*ARGSUSED */
int
isCannonHit(double min, craft * c)
{

	return (min < 3.0) ? 1 : 0;
}
