Logo Search packages:      
Sourcecode: acm version File versions  Download package

missile.c

/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1998  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 "pm.h"
#ifdef HAVE_DIS
#include "dis.h"
#endif

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

extern int mdebug;
extern void lookForCannonImpacts(craft * m);
extern int absorbDamage();
int       isMissileHit(double min, craft * c);

int
inCloud(craft * c)
{
      int       state;

      if (c->w.z > ctop) {
            state = 0;
      }
      else if (c->w.z > cbase) {
            state = 1;
      }
      else {
            state = 2;
      }
      return state;
}

int
fireMissile(craft * c, int ind)
{

      register craft *m;
      register int i;
      VPoint    s, s1;
      VPoint    cY, mX, mY, mZ;
      double    v;

#ifdef HAVE_DIS
      double    disLocation[3];
      double    disVelocity[3];
      double    disZeroVec[3];
      double    disOrientation[3];

#endif

      for ((i = 0, m = &mtbl[0]); i < MAXPROJECTILES; (++i, ++m))
            if (m->type == CT_FREE) {
                  m->type = CT_MISSILE;
                  break;
            }

      if (i == MAXPROJECTILES)
            return -1;

      m->cinfo = lookupCraft("aim-9m");
      m->fuel = m->cinfo->maxFuel;
      m->curThrust = m->cinfo->maxThrust;
      m->owner = c->pIndex;

      m->gvs_instance = (GVS_OBI) NULL;

/*
 *  Build trihedral based on the launching aircraft's current velocity vector
 *  rather than simply it's current direction vector.
 *
 *      (1)  build a unit velocity vector.
 *      (2)  calculate missiles local Z axis from
 *              plane's-y-axis CROSS missile's-unit-velocity-vector
 *      (3)  calculate missile's Y axis.
 */

      if ((v = mag(c->Cg)) < 1.0) {
            m->trihedral = c->trihedral;
            m->curRoll = c->curRoll;
            m->curPitch = c->curPitch;
            m->curHeading = c->curHeading;
      }
      else {
            mX = c->Cg;
            mX.x /= v;
            mX.y /= v;
            mX.z /= v;
            cY.x = c->trihedral.m[0][1];
            cY.y = c->trihedral.m[1][1];
            cY.z = c->trihedral.m[2][1];

            VCrossProd(&mX, &cY, &mZ);
            VCrossProd(&mZ, &mX, &mY);

            m->trihedral.m[0][0] = mX.x;
            m->trihedral.m[1][0] = mX.y;
            m->trihedral.m[2][0] = mX.z;
            m->trihedral.m[0][1] = mY.x;
            m->trihedral.m[1][1] = mY.y;
            m->trihedral.m[2][1] = mY.z;
            m->trihedral.m[0][2] = mZ.x;
            m->trihedral.m[1][2] = mZ.y;
            m->trihedral.m[2][2] = mZ.z;

            euler(m);
      }

      m->Cg = c->Cg;
      VTransform(&(c->cinfo->wStation[ind]), &(c->trihedral), &s1);
      VReverseTransform_(&s1, &c->XYZtoNED, &s);
      m->Sg.x = c->prevSg.x + FEETtoMETERS(s.x);
      m->Sg.y = c->prevSg.y + FEETtoMETERS(s.y);
      m->Sg.z = c->prevSg.z + FEETtoMETERS(s.z);
      DISGeocentricToWorldCoordinates
            ((dis_world_coordinates *) & m->Sg, &m->w);
      m->prevw = m->w;
      GenerateWorldToLocalMatrix(&m->w, &m->XYZtoNED);
      m->armTimer = m->cinfo->armDelay;
      m->flags = FL_HAS_GYRO;
      m->createTime = curTime;

/*
 * kludge
 */

      m->curRadarTarget = c->curRadarTarget;

#ifdef HAVE_DIS

/*
 *  ACM missiles are DIS "tracked munitions", so we are
 *  responsible for sending entity state PDU's for them
 */

      if (disInUse) {
            VPoint    tmp;

            disLocation[0] = m->Sg.x;
            disLocation[1] = m->Sg.y;
            disLocation[2] = m->Sg.z;
            tmp.x = FEETtoMETERS(m->Cg.x);
            tmp.y = FEETtoMETERS(m->Cg.y);
            tmp.z = FEETtoMETERS(m->Cg.z);
            VReverseTransform_(&tmp, &m->XYZtoNED, (VPoint *) & disVelocity[0]);
            disZeroVec[0] = 0.0;
            disZeroVec[1] = 0.0;
            disZeroVec[2] = 0.0;
            disOrientation[0] = m->curHeading;
            disOrientation[1] = m->curPitch;
            disOrientation[2] = m->curRoll;
            dis_entityEnter(c->team, m,
                                    &m->cinfo->entityType,
                                    &m->cinfo->altEntityType,
                                    disLocation, disVelocity,
                                    disZeroVec, disOrientation,
                                    disZeroVec, &m->disId);
#ifdef DIS_DEBUG
            printf("Missile Entering m%d %d\n", i, m->disId);
#endif
      }
#endif
      return 0;
}

int
killMissile(craft * c, craft * target)
{
#ifdef HAVE_DIS
      double    worldLocation[3], entityLocation[3];
      int       target_eid, missile_eid;
      VPoint    worldVel, tmp;

      if (c->type == CT_MISSILE || c->type == CT_CANNON) {

            if (target == NULL) {
                  target_eid = DIS_ID_NONE;
            }
            else {
                  target_eid = target->disId;
            }

            if (c->type == CT_CANNON) {
                  missile_eid = DIS_ID_NONE;
            }
            else {
                  missile_eid = c->disId;
            }

            worldLocation[0] = c->Sg.x;
            worldLocation[1] = c->Sg.y;
            worldLocation[2] = c->Sg.z;

/*
 *  killMissile's calling sequence needs to be updated to allow for the
 *  entity detonation location to be passed.
 */

            entityLocation[0] = 0.0;
            entityLocation[1] = 0.0;
            entityLocation[2] = 0.0;

            tmp.x = FEETtoMETERS(c->Cg.x);
            tmp.y = FEETtoMETERS(c->Cg.y);
            tmp.z = FEETtoMETERS(c->Cg.z);
            VReverseTransform_(&tmp, &c->XYZtoNED, &worldVel);

            if (disInUse) {
                  dis_detonation(&c->cinfo->entityType,
                                       ptbl[c->owner].disId,
                                       target_eid,
                                       missile_eid,
                                       worldLocation,
                                       entityLocation,
                                       (double *) &worldVel);
            }
      }
#endif
      c->type = CT_FREE;
      return 0;
}

int
lookForImpacts(void)
{

      craft    *c, *m;
      entry     p[MAXPLAYERS], *list, *q, *r, *rprev;
      VPoint    v, s0;
      double    t, d, explosion_diameter_meters;
      int       i, j;

      for (m = mtbl, i = 0; i < MAXPROJECTILES; ++i, ++m) {

            if (m->type == CT_CANNON) {
                  lookForCannonImpacts(m);
                  continue;
            }
            else if (m->type != CT_MISSILE || m->armTimer > 0.0)
                  continue;

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

                  if (c->type == CT_FREE)
                        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 - m->Sg.x + m->prevSg.x;
                  v.y = c->Sg.y - c->prevSg.y - m->Sg.y + m->prevSg.y;
                  v.z = c->Sg.z - c->prevSg.z - m->Sg.z + m->prevSg.z;
                  s0.x = c->prevSg.x - m->prevSg.x;
                  s0.y = c->prevSg.y - m->prevSg.y;
                  s0.z = c->prevSg.z - m->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 (mdebug)
                        printf("perigee in %g seconds with player %d\n",
                                 t * deltaT, j);

/*
 *  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 (isMissileHit(d, c)) {
                              t = 0.0;
                        }
                  }

                  if (t >= 0.0 && t <= 1.0) {
                        q = &p[j];

                        q->Sg = m->prevSg;
                        q->Sg.x += (m->Sg.x - m->prevSg.x) * t;
                        q->Sg.y += (m->Sg.y - m->prevSg.y) * t;
                        q->Sg.z += (m->Sg.z - m->prevSg.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;
                              }
                              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));
#ifdef FLIGHTDEBUG
                        if (mdebug)
                              printf("perigee %g feet; craft %d.\n",
                                       q->min, j);
#endif
                  }
            }

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

            for (r = list; r != (entry *) NULL; r = r->next)
                  if (isMissileHit(r->min, r->c)) {
                        m->Sg = r->Sg;    /* Set detonation point for killMissile() */
                        killMissile(m, r->c);
#ifdef HAVE_DIS
                        /* can only damage local player */
                        if (r->c->type != CT_DIS_PLANE)
#endif
                              if (absorbDISDamage(r->c, 
                                                            &m->cinfo->entityType, 0, 0, 
                                                            r->min,
                                                            mag(r->rvel),
                                                            &explosion_diameter_meters) == 0) {
                                    killPlayerEx(r->c,
                                                       "Your aircraft was destroyed by an air-to-air missile.",
                                                       "The missile's warhead detonated %.1f meters from your plane.",
                                                       r->min);
                              }
                        newExplosion(&(r->Sg), &r->rvel, 5.0, 10.0, 3.0);
                        break;
                  }
      }

      return (0);
}

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

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

#define IRMaxRange      FEETtoMETERS(15.0 * NM)

int
isIRVisible(craft * m, craft * c, VPoint * t, double IRScanSlope)
{

      VPoint    relPos, tmp;
      int       cstate, mstate;

      if (c->type == CT_FREE)
            return 0;

/*
 *  If the seeker is in clouds, or the target is not at the same level
 *  (e.g seeker is above clouds, but target is below), then the target is
 *  not IR visible.
 */

      if ((mstate = inCloud(m)) == 1) {
            return 0;
      }

      if ((cstate = inCloud(c)) != mstate) {
            return 0;
      }

      VTransform(&c->prevSg, &m->XYZtoNED, &tmp);
      VReverseTransform_(&tmp, &m->trihedral, t);

      if (sqrt(t->x * t->x + t->y * t->y + t->z * t->z) > IRMaxRange)
            return 0;

      if (t->x <= 0.0)
            return 0;

      relPos.z = t->z / (t->x * IRScanSlope);
      relPos.y = t->y / (t->x * IRScanSlope);

      return (sqrt(relPos.z * relPos.z + relPos.y * relPos.y) > 1.0) ? 0 : 1;
}

int
getIRTarget(craft * c, VPoint * t, double scanSlope)
{

      int       i, n;
      craft    *p;
      VPoint    tNew, tMin;
      double    m1, min;

      if (c->curRadarTarget != -1 &&
            isIRVisible(c, &ptbl[c->curRadarTarget], t, scanSlope))
            return c->curRadarTarget;

/*
 *  Look for a target.  Designate the closest one as a new target.
 */

      min = 1000000.0;
      n = -1;
      for (i = 0, p = ptbl; i < MAXPLAYERS; ++i, ++p) {
            if (p == c)
                  continue;
            if (p->type != CT_FREE)
                  if (isIRVisible(c, p, &tNew, scanSlope)) {
                        m1 = mag(tNew);
                        if (m1 < min) {
                              n = i;
                              min = m1;
                              tMin = tNew;
                        }
                  }
      }

      *t = tMin;
      return n;
}

/*
 *  Track target using proportional navigation guidance (N = 4).
 */

#define AIM9SLOPE 0.57735

void
trackTarget(craft * c)
{

      VMatrix   mtx, mtx1;
      VPoint    t, t1, v, vrel, zeroVec = {0,0,0};
      double    h, maxTurn, omegap, omegay;
      double    hs;
      double    deltaRoll, deltaPitch, deltaYaw;
      craft    *target;

/*
 *  Now let's get to target tracking;  the missile won't start tracking until
 *  0.60 seconds has elapsed.  Then, if we don't already have a target
 *  designated, get one.
 */

      if (curTime - c->createTime < 0.60) {
            deltaPitch = 0.0;
            deltaYaw = 0.0;
            goto change;
      }
      else if ((c->curRadarTarget = getIRTarget(c, &t, AIM9SLOPE)) == -1) {

/*
 *  Not target; missile goes ballistic
 */

            deltaPitch = 0.0;
            deltaYaw = 0.0;
            goto change;

#ifdef FLIGHTDEBUG
            if (mdebug)
                  printf("Missile elects to self-destruct\n");
#endif
            newExplosion(&(c->Sg), &zeroVec, 5.0, 10.0, 3.0);
            killMissile(c, (craft *) NULL);
            return;
      }

/*
 *  We'll steer towards the target at a rate proportional to the
 *  rate of change of the target's position in the missile's XZ (pitch)
 *  and XY (yaw) planes.
 */

      target = &ptbl[c->curRadarTarget];

      v.x = target->Cg.x - c->Cg.x;
      v.y = target->Cg.y - c->Cg.y;
      v.z = target->Cg.z - c->Cg.z;

      t.x = METERStoFEET(t.x);
      t.y = METERStoFEET(t.y);
      t.z = METERStoFEET(t.z);

      VReverseTransform_( &v, &c->trihedral, &vrel );

      hs = t.x * t.x + t.y * t.y;

      omegay = (vrel.y * t.x - vrel.x * t.y) / hs;

      omegap = (vrel.z * hs - t.z * (vrel.x * t.x + vrel.y * t.y)) /
            (sqrt(hs) * (hs + t.z * t.z));

      deltaPitch = omegap * 4.0 * deltaT;
      deltaYaw   = omegay * 4.0 * deltaT;

      h = sqrt( deltaPitch * deltaPitch + deltaYaw * deltaYaw );

/*
 *  We'll constrain missile turns to about 20 degree/second unless it's velocity
 *  would make that greater than a 25g load factor.
 */

      if ( c->VT > 0.0 ) {
            maxTurn = (earth_g / c->VT) * sqrt( 25.0 * 25.0 - 1.0 );
      }
      else {
            maxTurn = 0.0;
      }

      if (maxTurn > DEGtoRAD(20.0)) {
            maxTurn = DEGtoRAD(20.0);
      }
      maxTurn *= deltaT;

#ifdef FLIGHTDEBUG
      if (mdebug)
            printf("\nturn rate = %g; maxTurn = %g\n", h, maxTurn);
#endif

      if (h > maxTurn) {
            deltaPitch *= maxTurn / h;
            deltaYaw *= maxTurn / h;
      }

/*
 *  Re-orient the missile and velocity vector.
 */

  change:

      deltaRoll = 0.0;

#ifdef FLIGHTDEBUG
      if (mdebug) {
            printf("Missile changes: pitch/yaw: %g %g (deg).\n",
                     RADtoDEG(deltaPitch), RADtoDEG(deltaYaw));
            printf("position [%g, %g, %g]\n", t.x, t.y, t.z);
            printf("target pitch/yaw rates: %g, %g (deg/sec)\n",
                     RADtoDEG(omegap), RADtoDEG(omegay));
      }
#endif

      buildEulerMatrix(deltaRoll, -deltaPitch, deltaYaw, &mtx);

      VReverseTransform_(&c->Cg, &c->trihedral, &t);
      VTransform(&t, &mtx, &t1);
      VTransform(&t1, &c->trihedral, &c->Cg);

      VMatrixMultByRank(&mtx, &c->trihedral, &mtx1, 3);
      c->trihedral = mtx1;
      euler(c);

}

Generated by  Doxygen 1.6.0   Back to index