static char *rcsid = "$Header: /plaid/homes/zeus/Blind-2.47/RCS/battle.c,v 2.47 1998/04/15 21:29:39 zeus Exp zeus $";

/*
 * $Log: battle.c,v $
 * Revision 2.47  1998/04/15 21:29:39  zeus
 * *** empty log message ***
 *
 * Revision 2.46  1997/04/18 12:06:12  zeus
 * *** empty log message ***
 *
 * Revision 2.45  1996/12/31  23:40:54  zeus
 * Revision 2.44  1995/12/31  19:45:18  zeus
 * Revision 2.43  1995/03/20  01:43:09  zeus
 * Revision 2.42  1994/05/17  13:43:15  bampton
 * Revision 2.41  1994/04/08  03:11:28  bampton
 * Revision 2.40  1994/03/09  20:22:55  bampton
 * Revision 2.38  1993/09/27  17:15:54  bampton
 * Revision 2.37  1993/09/07  20:50:32  bampton
 * Revision 2.33  1993/08/03  23:10:13  bampton
 * Revision 2.30  1993/07/14  14:59:26  bampton
 * Revision 0.0  1993/07/14  13:41:46  bampton RCS prototype.
 * 
 * 
 */

#define NOTIMEB
#include "common.h"
#include "prototypes.h"
#include "battle.h"
#include "battle-g.h"
#include "misc-g.h"

void battle_rcsid (void);

extern player *players;
extern planet *planets;
extern sorted *sorted_planets;
extern shiptype *shiptypes;
extern battle *battles;
extern bombing *bombings;
extern double galaxysize;
extern char buf[], buf2[];
extern planetdefense *defender;

double *groupdef;

#define INVOLVED -2.0

void
battle_rcsid (void)
{
  printf ("%s\n", rcsid);
}

void shot_addlist (void *_base, void *_p);

#if SPEED & 0x10
void
shot_addlist (void *_base, void *_p)
{
  list **base = _base;
  list *p = _p;
  static list *tail;

  p->next = NULL;
  if (*base)
  {
    tail->next = p;
    tail = p;
  }
  else
  {
    *base = p;
    tail = p;
  }
}

#else

/* This is here so we can differentiate between addlist overhead and 
   shot's addlist overhead (which is a _LOT_ more). */

void
shot_addlist (void *_base, void *_p)
{
  list **base = _base;
  list *p = _p;
  list *q;

  p->next = NULL;
  if (*base)
  {
    for (q = *base; q->next; q = q->next)
      ;
    q->next = p;
  }
  else
    *base = p;
}
#endif


int
atwar (player * P1, player * P2, planet * p)
{
  alliance *a;
  defensive *d;

  planetdefense *pd;

  if (P1 == P2)                 /* Speed mod */
    return PEACE;

  for (pd = defender; pd; pd = pd->next)
    if ((pd->planet == p) && (pd->defender == P2) && (p->owner == P1))
      return pd->dipstat;

  for (a = P1->allies; a; a = a->next)
    if (a->ally == P2)
      return PEACE;
  for (d = P1->defend; d; d = d->next)
    if (d->defend == P2 && p->owner != P1)
      return PEACE;
  return WAR;
}

int
cankill (group * attack, double defense)
{
  double ratio;

  if (!attack->type->nguns)
    return (0);
  if (defense > 0.0)
  {
    ratio = defense / (attack->tech.guns * attack->type->guns);
    return (ratio < 4.0);
  }
  return (1);
}

int
kill (group * attack, double defense)
{
  double randomn, ratio;

  if (defense > 0.0)
  {
    ratio = attack->tech.guns * attack->type->guns / defense;
    randomn = pow (4.0, (double) (frand (2.0) - 1.0));
    return (ratio > randomn);
  }
  return (1);
}

void
fightphase (void)
{
  planet *p;
  player *P, *P2;
  group *g;
  group *g2;
  int i, j, ngroups, nships, nshipstofire, agroup, tgroup, shipno, involved;
  int moretargets, foundtarget, notfirstround;
  int *shipstofire;
  group *g3;
  group **fightgroups;
  player **groupplayers;
  battle *B;
  shot *S;
  viewer *V;
  participant *PA = NULL;       /* To shut up lint */

  /* Find out who is at war */
  for (p = planets; p; p = p->next)
  {
    ngroups = 0;
    nships = 0;
    for (P = players; P; P = P->next)
      for (g = P->groups; g; g = g->next)
      {
        if (g->where == p && g->dist == 0.0)
        {
          involved = 0;
          for (P2 = players; P2 && !involved; P2 = P2->next)
          {
            for (g3 = P2->groups; g3; g3 = g3->next)
              if (g3->where == p && g3->dist == 0.0 &&
                  ((g->type->nguns && atwar (P, P2, p)) ||
                   (g3->type->nguns && atwar (P2, P, p))))
              {
                involved = 1;
                break;
              }
          }
          if (involved)
          {
            ngroups++;
            nships += g->ships;
          }
          else
            g->dist = INVOLVED;
        }
      }
    /* if one group of a player is involved, then all groups must be */
    for (P = players; P; P = P->next)
      for (g = P->groups; g; g = g->next)
        if (g->dist == INVOLVED)
          for (g3 = P->groups; g3; g3 = g3->next)
            if (g3->where == p && g3->dist == 0.0 && g3 != g)
            {
              g->dist = 0.0;
              ngroups++;
              nships += g->ships;
              break;
            }

    /* restore dist numbers */
    for (P = players; P; P = P->next)
      for (g = P->groups; g; g = g->next)
        if (g->dist == INVOLVED)
          g->dist = 0.0;

    if (ngroups)                /* if involved ships then do a battle */
    {
      fightgroups = alloc (ngroups * sizeof (group *));
      shipstofire = alloc (ngroups * sizeof (int));
      groupdef = alloc (ngroups * sizeof (double));

      groupplayers = alloc (ngroups * sizeof (player *));
      i = 0;
      for (P = players; P; P = P->next)
        for (g = P->groups; g; g = g->next)
          if (g->where == p && g->dist == 0.0)
          {
            involved = 0;
            for (P2 = players; P2 && !involved; P2 = P2->next)
              for (g3 = P2->groups; g3; g3 = g3->next)
                if (g3->where == p && g3->dist == 0.0 &&
                    ((g->type->nguns && atwar (P, P2, p)) ||
                     (g3->type->nguns && atwar (P2, P, p))))
                {
                  involved = 1;
                  break;
                }
            if (involved)
            {
              groupplayers[i] = P;
              shipstofire[i] = 0;
              groupdef[i] = shield_str (g->type->shields, g->tech.shields,
                                        shipmass (g));
              fightgroups[i++] = g;
            }
            else
              g->dist = INVOLVED;
          }
      for (P = players; P; P = P->next)
        for (g = P->groups; g; g = g->next)
          if (g->dist == INVOLVED)
            for (g3 = P->groups; g3; g3 = g3->next)
              if (g3->where == p && g3->dist == 0.0 && g3 != g)
              {
                g->dist = 0.0;
                groupplayers[i] = P;
                shipstofire[i] = 0;
                groupdef[i] = shield_str (g->type->shields, g->tech.shields,
                                          shipmass (g));
                fightgroups[i++] = g;
                break;
              }
      for (P = players; P; P = P->next)
        for (g = P->groups; g; g = g->next)
          if (g->dist == INVOLVED)
            g->dist = 0.0;
      assert (i == ngroups);
      B = alloc (sizeof (battle));
      B->where = p;
      B->shots = 0;
      B->viewers = 0;
      B->participants = 0;
      for (P = players; P; P = P->next)
        if (canseeplanet (P, p, 0))
        {
          V = alloc (sizeof (viewer));
          V->who = P;
          addlist (&B->viewers, V);
        }
      for (P = players; P; P = P->next)
      {
        i = 0;
        for (g = P->groups; g; g = g->next)
          if (g->where == p && !g->dist)
          {
            if (!i)
            {
              i = 1;
              PA = alloc (sizeof (participant));
              PA->who = P;
              PA->groups = NULL;
              addlist (&B->participants, PA);
            }
            g2 = alloc (sizeof (group));
            memcpy (g2, g, sizeof (group));
            g2->left = g->ships;
            g2->alias = g;
            addlist (&PA->groups, g2);
          }
      }
      addlist (&battles, B);
      notfirstround = 0;
      printf ("Battle at %s (%s)\n", p->name, p->num);

      do
      {
        nshipstofire = 0;
        for (i = 0; i < ngroups; i++)
          if (fightgroups[i]->type->nguns)
          {
            shipstofire[i] = fightgroups[i]->ships;
            nshipstofire += fightgroups[i]->ships;
          }
        assert (nshipstofire);
        do
        {
          shipno = rnd (nshipstofire);
          agroup = 0;
          while (shipno >= shipstofire[agroup])
          {
            shipno -= shipstofire[agroup++];
            assert (agroup < ngroups);
          }
          nshipstofire--;
          shipstofire[agroup]--;

          /* shoot once for each gun on the chosen ship */

          for (j = fightgroups[agroup]->type->nguns; j > 0; j--)
          {
            moretargets = 0;
            for (tgroup = 0; tgroup < ngroups && !moretargets; tgroup++)
            {
              if (!fightgroups[tgroup]->ships)
                continue;
              if (notfirstround &&
                  !cankill (fightgroups[agroup], groupdef[tgroup]))
                continue;
              if (atwar (groupplayers[agroup], groupplayers[tgroup], p) ||
                  (atwar (groupplayers[tgroup], groupplayers[agroup], p) &&
                   fightgroups[tgroup]->type->nguns))
                moretargets = 1;
              else
              {
                if (atwar (groupplayers[tgroup], groupplayers[agroup], p))
                  for (i = 0; i != ngroups; i++)
                    if (groupplayers[i] == groupplayers[tgroup] &&
                        fightgroups[i]->type->nguns)
                    {
                      moretargets = 1;
                      break;
                    }
              }
            }
            if (!moretargets)
              break;

            /* Select a target */

            foundtarget = 0;
            do
            {
              shipno = rnd (nships);
              tgroup = 0;
              while (shipno >= fightgroups[tgroup]->ships)
              {
                shipno -= fightgroups[tgroup++]->ships;
                assert (tgroup < ngroups);
              }
              assert (fightgroups[tgroup]->ships);
              if (atwar (groupplayers[agroup], groupplayers[tgroup], p) ||
                  (atwar (groupplayers[tgroup], groupplayers[agroup], p) &&
                   fightgroups[tgroup]->type->nguns))
                foundtarget = 1;
              else if (atwar (groupplayers[tgroup], groupplayers[agroup], p))
                for (i = 0; i != ngroups; i++)
                  if (groupplayers[i] == groupplayers[tgroup] &&
                      fightgroups[i]->type->nguns)
                  {
                    foundtarget = 1;
                    break;
                  }
            }
            while (!foundtarget);

            /* Store shot, and determine if there was a kill */
            S = alloc (sizeof (shot));
            S->attacker = fightgroups[agroup]->type;
            S->target = fightgroups[tgroup]->type;
            S->kill = 0;
            printf (">");
            if (kill (fightgroups[agroup], groupdef[tgroup]))
            {
              if (rnd (fightgroups[tgroup]->ships) <
                  shipstofire[tgroup])
              {
                shipstofire[tgroup]--;
                nshipstofire--;
              }
              fightgroups[tgroup]->ships--;
              nships--;
              S->kill = 1;
              printf ("*\n");
            }
            shot_addlist (&B->shots, S);
          }
        }
        while (nshipstofire);

        /* END OF A ROUND */

        moretargets = 0;
        for (agroup = 0; agroup < ngroups && !moretargets; agroup++)
          if (fightgroups[agroup]->ships)
            for (tgroup = 0; tgroup < ngroups && !moretargets; tgroup++)
            {
              if (!fightgroups[tgroup]->ships ||
                  !cankill (fightgroups[agroup], groupdef[tgroup]))
                continue;
              if (atwar (groupplayers[agroup], groupplayers[tgroup], p) ||
                  (atwar (groupplayers[tgroup], groupplayers[agroup], p) &&
                   fightgroups[tgroup]->type->nguns))
                moretargets = 1;
              else if (atwar (groupplayers[tgroup], groupplayers[agroup], p))
                for (i = 0; i != ngroups; i++)
                  if (groupplayers[i] == groupplayers[tgroup] &&
                      fightgroups[i]->type->nguns)
                  {
                    moretargets = 1;
                    break;
                  }
            }
        notfirstround = 1;
      }
      while (moretargets);      /* while loop: keep fighting while targets */

      free (groupplayers);
      free (shipstofire);
      free (fightgroups);
      free (groupdef);
      printf ("\n");

      /* Collect information on number of survivors */

      for (PA = B->participants; PA; PA = PA->next)
        for (g = PA->groups; g; g = g->next)

          g->left = g->alias->ships;
    }
  }
}

void
bombphase (void)
{
  player *P, *P2;
  group *g;
  planet *p;
  bombing *b;
  viewer *V;

  for (P = players; P; P = P->next)
    for (g = P->groups; g; g = g->next)
    {
      p = g->where;
      if (g->dist == 0.0 && g->ships && g->type->nguns && p->owner
          && atwar (P, p->owner, p))
      {
        b = alloc (sizeof (bombing));
        b->where = p;
        b->who = P;
        b->whoose = p->owner;
        b->viewers = 0;
        b->pop = p->pop;
        b->ind = p->ind;
        b->col = p->col;
        b->cap = p->cap;
        b->mat = p->mat;
        for (P2 = players; P2; P2 = P2->next)
          if (canseeplanet (P2, p, 0))
          {
            V = alloc (sizeof (viewer));
            V->who = P2;
            addlist (&b->viewers, V);
          }
        addlist (&bombings, b);
        sterilize (p);

#ifdef VERBOSE
        printf ("Planet %s nuked by %s was %s\n", p->num, b->who->name,
                b->whoose->name);
#endif
      }
    }
}
