/*
    Amiga port by Oliver Gantert

    27.04.2000 - fixed some compiler warnings
*/
/*

ORBIT, a freeware space combat simulator
Copyright (C) 1999  Steve Belczyk <steve1@genesis.nred.ma.us>

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; either version 2
of the License, or (at your option) any later version.

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "orbit.h"

/* Ignore beyond this distance */
#define THINK_CUTOFFA (20000.0 / KM_TO_UNITS1)
#define THINK_CUTOFFA2 (THINK_CUTOFFA * THINK_CUTOFFA)

/* Pursue beyond this distance */
#define THINK_CUTOFFB (2000.0 / KM_TO_UNITS1)
#define THINK_CUTOFFB2 (THINK_CUTOFFB * THINK_CUTOFFB)

/* Maintain distance beyond this distance */
#define THINK_CUTOFFC (500.0 / KM_TO_UNITS1)
#define THINK_CUTOFFC2 (THINK_CUTOFFC * THINK_CUTOFFC)

void ThinkTarget (int t)
/*
 *  Give this target a chance to think about what it wants to do
 */
{
  /* Targets do their own thinking in network games */
  if (am_client || am_server) return;

  switch (target[t].strategy)
  {
    case STRAT_DONOTHING:
    break;

    case STRAT_SIT1:
    ThinkSit1 (t);
    break;

    case STRAT_SIT2:
    ThinkSit2 (t);
    break;

    case STRAT_SIT3:
    ThinkSit3 (t);
    break;

    case STRAT_SIT4:
    ThinkSit4 (t);
    break;

    case STRAT_HUNT1:
    ThinkHunt1 (t);
    break;

    case STRAT_HUNT2:
    ThinkHunt2 (t);
    break;

    case STRAT_HUNT3:
    ThinkHunt3 (t);
    break;

    case STRAT_HUNT4:
    ThinkHunt4 (t);
    break;
  }
}

void ThinkSit1 (int t)
/*
 *  Don't move, just turn toward player and shoot
 */
{
  TurnToward (t, player.pos);
}

void ThinkSit2 (int t)
/*
 *  Don't move, just turn toward player and shoot
 */
{
  double v[3];

  /* Aim at player */
  if (!Aim (v, target[t].pos, target[t].vel, player.pos, player.vel,
  weapon[target[t].weapon].speed))
  {
    TurnToward (t, player.pos);
    return;
  }

  TurnToward (t, v);
}

void ThinkSit3 (int t)
/*
 *  Don't move, just turn toward nearest enemy and shoot poorly
 */
{
  double pos[3], vel[3];

  if (!FindEnemy (t, pos, vel)) return;
  TurnToward (t, pos);
}

void ThinkSit4 (int t)
/*
 *  Don't move, just turn toward nearest enemy and shoot well
 */
{
  double v[3], pos[3], vel[3];

  if (!FindEnemy (t, pos, vel)) return;
  if (!Aim (v, target[t].pos, target[t].vel, pos, vel,
  weapon[target[t].weapon].speed))
  {
    TurnToward (t, pos);
    return;
  }

  TurnToward (t, v);
}

void ThinkHunt1 (int t)
/*
 *  Move toward player and shoot
 */
{
  MoveToward (t, player.pos);
}

void ThinkHunt2 (int t)
/*
 *  Move toward aiming point and shoot
 */
{
  double v[3];

  /* Aim at player */
  if (!Aim (v, target[t].pos, target[t].vel, player.pos, player.vel,
  weapon[target[t].weapon].speed))
  {
    ThinkHunt1 (t);
    return;
  }

  MoveToward (t, v);
}

void ThinkHunt3 (int t)
/*
 *  Move toward closest enemy and shoot poorly
 */
{
  double pos[3], vel[3];

  if (!FindEnemy (t, pos, vel)) return;
  MoveToward (t, pos);
}

void ThinkHunt4 (int t)
/*
 *  Move toward closest enemy and shoot well
 */
{
  double v[3], pos[3], vel[3];

  if (!FindEnemy (t, pos, vel)) return;
  if (!Aim (v, target[t].pos, target[t].vel, pos, vel,
  weapon[target[t].weapon].speed))
  {
    ThinkHunt3 (t);
    return;
  }

  MoveToward (t, v);
}

void TurnToward (int t, double *pos)
{
  /*
 *  Turn target t toward position v
 */
  double r, v[3], alpha, beta, theta;

  Vsub (v, pos, target[t].pos);
  r = Mag2 (v);

  /* Don't bother if too far away */
  if (r > THINK_CUTOFFA2)
  {
    target[t].vel[0] =
    target[t].vel[1] =
    target[t].vel[2] = 0.0;

    return;
  }

  /* v is unit vector toward player */
  Normalize (v);

  /* Determine angles from right-hand and up vectors */
  alpha = Dotp (v, target[t].right);
  beta = Dotp (v, target[t].up);
  theta = Dotp (v, target[t].view);

  /* Turn towards player */

  /* First check left or right */
  /* Is player to our right or left? */
  if (alpha > 0.0)
  {
    /* To our right */
    target[t].move_left = target[t].turnrate;
  }
  else
  {
    /* To our left */
    target[t].move_right = target[t].turnrate;
  }

  /* Up or down? */
  if (beta > 0.0)
  {
    /* Above us */
    target[t].move_up = target[t].turnrate;
  }
  else
  {
    target[t].move_down = target[t].turnrate;
  }

  /* If we are facing target and are in range, shoot
    a missile! */
  if ( (theta > 0.9) && (r < weapon[target[t].weapon].range2) )
  {
    TargetFiresMissile (t);
  }
}

void MoveToward (int t, double *pos)
{
  /*
 *  Turn target t toward position v
 */
  double r, v[3], alpha, beta, theta;

  Vsub (v, pos, target[t].pos);
  r = Mag2 (v);

  /* Don't bother if too far away */
  if (r > THINK_CUTOFFA2)
  {
    target[t].vel[0] =
    target[t].vel[1] =
    target[t].vel[2] = 0.0;

    return;
  }

  /* v is unit vector toward player */
  Normalize (v);

  /* Determine angles from right-hand and up vectors */
  alpha = Dotp (v, target[t].right);
  beta = Dotp (v, target[t].up);
  theta = Dotp (v, target[t].view);

  /* Turn towards player */

  /* First check left or right */
  /* Is player to our right or left? */
  if (alpha > 0.0)
  {
    /* To our right */
    target[t].move_left = target[t].turnrate;
  }
  else
  {
    /* To our left */
    target[t].move_right = target[t].turnrate;
  }

  /* Up or down? */
  if (beta > 0.0)
  {
    /* Above us */
    target[t].move_up = target[t].turnrate;
  }
  else
  {
    target[t].move_down = target[t].turnrate;
  }

  /* Don't bother if too far away */
  if (r > THINK_CUTOFFA2)
  {
    target[t].vel[0] = target[t].vel[1] = target[t].vel[2] = 0.0;
  }

  /* If far enough away, move toward player */
  if (r > THINK_CUTOFFB2)
  {
    if (theta > 0.0)
    target[t].move_forward = target[t].maxvel;
    else
    target[t].move_backward = target[t].maxvel;
  }
  /* If too close, move away */
  if (r < THINK_CUTOFFC2)
  {
    target[t].vel[0] = target[t].vel[1] = target[t].vel[2] = 0.0;
  }

  /* If we are facing target and are in range, shoot
    a missile! */
  if ( (theta > 0.9) && (r < weapon[target[t].weapon].range2) )
  {
    TargetFiresMissile (t);
  }
}

int FindEnemy (int targ, double *pos, double *vel)
/*
 *  Find closest enemy to target targ, return position and speed
 */
{
  int t, tt;
  double d, v[3], r;

  /* d will be distance to closest */
  d = -1.0;

  /* Loop through targets */
  for (t=0; t<NTARGETS; t++)
  {
    if ( (target[t].age > 0.0) &&
    (!target[t].hidden) &&
    (t != targ) &&
    (target[t].friendly != target[targ].friendly) )
    {
      /* Closer? */
      Vsub (v, target[t].pos, target[targ].pos);
      r = Mag2 (v);
      if ( (d < 0.0) || (r < d) )
      {
        tt = t;
        d = r;
      }
    }
  }

  /* Set position and velocity if we found something */
  if (d > 0.0)
  {
    Vset (pos, target[tt].pos);
    Vset (vel, target[tt].vel);
  }

  /* Must check player? */
  if (!target[targ].friendly)
  {
    if ( (d < 0.0) || (target[targ].range2 < d) )
    {
      Vset (pos, player.pos);
      Vset (vel, player.vel);
      d = target[targ].range2;
    }
  }

  /* Found nothing? */
  if (d == (-1.0)) return 0;

  return 1;
}
