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

/*
 * $Log: misc.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.39  1993/10/28  22:31:20  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
 */

#define IO
#define NOTIMEB
#include "common.h"
#include "prototypes.h"
#include "misc.h"
#include "misc-g.h"
#include "exit.h"
#include "speed.h"

extern double galaxysize;
extern planet *planets;
extern player *players;
extern sorted *sorted_planets;
extern shiptype *shiptypes;
extern char err_tmp[];
extern char buf[256];
extern int nplanets;
extern fleetname *fleets;

#ifdef KCMD
extern planetdefense *defender;

#endif


void misc_rcsid (void);

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

/*
 * just for easier debugging; to stop whenever desired call breakf and put a
 * break on breakf
 */

void
breakf (void)
{
  printf ("break\n");
}

/* For debugging, use findp to print a planet */

void
findp (int n)
{
  planet *p;

  p = numtop (planets, n);

#ifdef __LINT__
  p = p;
#endif

  printf ("yo\n");
}

void
freelist (void *base)
{
  list *p, *p2;

  p2 = base;
  while (p2)
  {
    p = p2;
    p2 = p2->next;
    free (p);
  }
}

void
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;
}

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

  if (*base == p)
    *base = p->next;
  else
  {
    for (q = *base; q->next != p; q = q->next)
      assert (q != NULL);
    q->next = p->next;
  }
  free (p);
}

long int
rnd (int x)
{

#if (HPUX9==1) || (SOLARIS==1) || (USESRAND48==1)
  return (lrand48 () % (long) x);
#else
  return (random () % (long) x);
#endif
}

double
frand1 (void)
{
  return (double) rnd (10000);
}

double
dist (planet * p1, planet * p2)
{
  double dx = min (fabs (p1->x - p2->x), galaxysize - fabs (p1->x - p2->x));
  double dy = min (fabs (p1->y - p2->y), galaxysize - fabs (p1->y - p2->y));

  return sqrt (dx * dx + dy * dy);
}

double
Ftrunc (double x)
{
  long int y;
  double z;

  y = (long int) (100.0 * x);
  z = ((double) y) / 100.0;
  return (z);
}

void *
alloc (unsigned int bytes)
{
  void *s = malloc (bytes);

  if (s == NULL)
  {
    printf ("Out of memory\n");
    exit (NOMEM);
  }
  memset (s, 0, bytes);
  return s;
}

double
weaponmass (shiptype * t)
{
  return (t->nguns ? t->guns : 0.0) +
    ((t->nguns > 1) ? ((double) (t->nguns - 1) * 0.5 * t->guns) : 0.0);
}

double
typemass (shiptype * t)
{
  return (t->drive + weaponmass (t) + t->shields + t->cargo);
}

double
ttechmass (group * g)
{
  return (g->tech.drive * g->type->drive + g->tech.guns * weaponmass (g->type) +
       g->tech.shields * g->type->shields + g->tech.cargo * g->type->cargo);
}

double
shipmass (group * g)
{
  if (g->tech.cargo)
    return (typemass (g->type) + g->load / g->tech.cargo);
  else
    return (typemass (g->type));
}

double
cargospace (group * g)
{
  double size;

  size = g->type->cargo;
  return (g->tech.cargo * size * (1.0 + size / 10.0));
}

double
shield_str (double shields, double shield_tech, double mass)
{
  return (shields * shield_tech / pow (mass, 1.0 / 3.0) * BATTLEMAGIC);
}

double
group_speed (Tech t, shiptype * T, double size)
{
  return (t.drive * T->drive * DRIVEMAGIC / size);
}

double
effective_ind (planet * p)
{
  return (p->ind * 0.75 + p->pop * 0.25);
}

double
produce_cap (planet * p)
{
  double cap;

  cap = effective_ind (p) / INDPERCAP;
  if (cap > p->mat)
    cap = (p->mat) + ((cap * INDPERCAP) - (p->mat * INDPERCAP)) /
      (INDPERCAP + 1.0 / p->resources);
  return (cap);
}

double
produce_mat (planet * p)
{
  double mat;

  mat = effective_ind (p) * p->resources;
  return (mat);
}

double
produce_mass (planet * p)
{
  double mass;

  mass = effective_ind (p) / INDPERSHIP;
  if (mass > p->mat)
    mass = p->mat + ((mass * INDPERSHIP) - (p->mat * INDPERSHIP)) /
      (INDPERSHIP + 1.0 / p->resources);
  return (mass);
}

double
upgrade_cost (player * P, group * g)
{
  shiptype *T = g->type;

  return (INDPERSHIP *
          ((1.0 - g->tech.drive / P->tech.drive) * T->drive +
           (1.0 - g->tech.guns / P->tech.guns) * weaponmass (T) +
           (1.0 - g->tech.shields / P->tech.shields) * T->shields +
           (1.0 - g->tech.cargo / P->tech.cargo) * T->cargo));
}

#ifdef PARTTECH
/* Cost = fraction of max tech + normal (max tech) cost, divided by 2
   Ex: building at tech lvl 1, when you have TL 2 would be .5+1/2= .75
   1->3 --> 2/3
   1->4 --> 5/8
   1->5 --> 3/5
   1->infinity -> 1/2
 */

double
downgrade_cost (player * P, shiptype * T, planet * p)
{
  double cost, f_tech;

  if ((p->drive == 0.0) && (p->guns == 0.0) && (p->shields == 0.0) && (p->cargo == 0.0))
    return (typemass (T));
  f_tech = ((p->drive / P->tech.drive) * T->drive + (p->guns / P->tech.guns)
            * weaponmass (T) + (p->shields / P->tech.shields) * T->shields +
            (p->cargo / P->tech.cargo) * T->cargo);
  if (p->drive == 0.0)
    f_tech += T->drive;
  if (p->guns == 0.0)
    f_tech += weaponmass (T);
  if (p->shields == 0.0)
    f_tech += T->shields;
  if (p->cargo == 0.0)
    f_tech += T->cargo;
  cost = (typemass (T) + f_tech) / 2.0;
  return (cost);
}

double
upgrade_partial_tech (player * P, player * Px, group * g)
{
  /* This function stuff is here in case we want to do something else */
  /* Bugfix */
  if (Px->tech.drive == 0.0)
    Px->tech.drive = P->tech.drive;
  if (Px->tech.guns == 0.0)
    Px->tech.guns = P->tech.guns;
  if (Px->tech.shields == 0.0)
    Px->tech.shields = P->tech.shields;
  if (Px->tech.cargo == 0.0)
    Px->tech.cargo = P->tech.cargo;
  return (upgrade_cost (Px, g));
}
#endif

int
fleettonum (group * g)
{
  fleetname *fl;
  int i;

  for (i = 0, fl = fleets; fl; fl = fl->next)
  {
    i++;
    if (fl == g->fleet)
      return (i);
  }
  return (0);
}

fleetname *
numtofleet (int i)
{
  fleetname *fl;
  int j;

  if (i == 0)
    return (NULL);
  for (j = 0, fl = fleets; fl; fl = fl->next)
  {
    j++;
    if (i == j)
      return (fl);
  }
  assert (0);                   /* Unreachable! */
  return (NULL);
}

#ifndef PREPROC
double
frand (double x)
{
  double amm = frand1 () / 100000000.0;
  double am = frand1 () / 10000.0;
  double a = frand1 ();
  double ap = frand1 () * 10000.0;
  double app = frand1 () * 100000000.0;

  return fmod (amm + am + a + ap + app, x);
}

#ifdef KCMD
void
wipe_defend (planet * p)
{
  planetdefense *d, *d2;

  for (d = defender; d; d = d2)
  {
    d2 = d->next;
    if (d->planet == p)
      removelist (&defender, d);
  }
}
#endif

void
sterilize (planet * p)
{
  p->owner = NULL;
  p->pop = 0.0;
  p->ind = 0.0;
  p->cap = 0.0;
  p->mat = 0.0;
  p->col = 0.0;
  p->inprogress = 0.0;
  p->spent = 0.0;
  p->producing = 0;
  p->producingshiptype = NULL;

#ifdef KCMD
  wipe_defend (p);
#endif

  p->drive = p->guns = p->shields = p->cargo = 0.0;
  memset (p->routes, 0, sizeof (p->routes));
}
#endif

/* create a list of all inhabited planets, sorted by industry level */

void
sortplanets (void)
{
  planet *p;
  sorted *tmp, *trailer, *new;

  freelist (sorted_planets);
  sorted_planets = NULL;

  for (p = planets; p; p = p->next)
    if (p->owner)
    {
      for (trailer = NULL, tmp = sorted_planets;
           tmp && (tmp->p->ind > p->ind);
           trailer = tmp, tmp = tmp->next)
        ;                       /* just advance pointer */

      new = alloc (sizeof (sorted));
      new->p = p;

      if (trailer)              /* inserting into middle or end of list */
      {
        new->next = tmp;
        trailer->next = new;
      }
      else
        /* inserting at the beginning of the list */
      {
        new->next = tmp;
        sorted_planets = new;
      }
    }                           /* end of: if a settled planet */
}

/* Illegal names:
   -,_, ^#, ^;
 */
int
legal_name (const char *name, int flags)
{
  int x;

  assert (name[0]);
  if ((name[0] == '#') || (name[0] == ';'))
  {
    sprintf (err_tmp, "Illegal name: \"%s\"\n", name);
    return (COMMENTNAME);
  }
  if (((name[0] == '-') || (name[0] == '_')) && (strlen (name) == 1))
  {
    sprintf (err_tmp, "Illegal name: \"%s\"\n", name);
    return (DASHNAME);
  }
  if (flags & EMPIRE)
  {
    /* Only care if empire name about these chars... */
    for (x = 0; name[x]; x++)
    {
      switch (name[x])
      case '.':
      case '`':
      case '\'':
      case '"':
      case '>':
      case '<':
      case '?':
      case '!':
      case '&':
      case '*':
      case '|':
      case ':':
      case '\\':
      case '%':
        sprintf (err_tmp, "Illegal name: \"%s\" because of %c\n", name, name[x]);
      return (ILLEGNAME);
      break;
    }
  }
  return (0);
}


void
getstr (void)
{
  int i;

  fgets (buf, MAXSTRLEN, stdin);
  buf[MAXTEXT - 1] = 0;
  i = strlen (buf);
  while (i && ((buf[i - 1] == ' ') || (buf[i - 1] == '\n')))
    buf[--i] = 0;
}

int
geti (void)
{

REDO:

  getstr ();
  if (buf[0] && !isdigit (buf[0]))
  {
    fprintf (stderr, "Integer expected.\n");
    goto REDO;
  }
  return atoi (buf);
}

double
getf (void)
{

REDO:

  getstr ();
  if (buf[0] && !isdigit (buf[0]) && buf[0] != '.')
  {
    fprintf (stderr, "Number expected.\n");
    goto REDO;
  }
  return atof (buf);
}

int
hstricmp (const char *s1, const char *s2)
{
  char c1, c2;

  do
  {
    c1 = tolower (*s1++);
    c2 = tolower (*s2++);
    if (c1 != c2)
      return c1 - c2;
  }
  while (c1);
  if (c2)
    return (1);                 /* different string lengths */

  return 0;
}

player *
inputplayer (player * head, char *prompt)
{
  player *p;

  printf ("%s%c", prompt, lf);
  getstr ();

  if (buf[0])
  {
    if ((p = nametop (head, buf)) != 0)
      return p;
    fprintf (stderr, "Player \"%s\" not found.\n", buf);
  }
  return 0;
}

int
zeromem (void *_p, int bytes)
{
  char *p = _p;

  do
    if (*p++)
      return 0;
  while (--bytes);
  return 1;
}

void *
nametop (void *head, char *s)
{
  list *p;

  for (p = head; p; p = p->next)
    if (!hstricmp ((char *) (p + 1), s))
      return p;
  return 0;
}

/*
 * gellings 1/28/93: Fixed to prevent naming planet A w/ the number of planet
 * B from affecting ships sent to planet B using B's number
 * Bampton 11/5/95: Changed n_cmd to prevent this from happening too...
 */

planet *
nametoplan (planet * head, const char *s)
{
  planet *p;

  for (p = head; p; p = p->next)
    if (!hstricmp (p->num, s))
      return p;
  for (p = head; p; p = p->next)
    if (!hstricmp (p->name, s))
      return p;
  return (NULL);
}

int
ptonum (void *base, void *target)
{
  list *p;
  int i = 1;

  for (p = base; p; p = p->next)
  {
    if (p == target)
      return i;
    i++;
  }
  return 0;
}

#if SPEED&2
void *
numtop2 (void *base, int n)
{
  int i;
  list *p;
  static list *PlanIndex[MAXPLAN];
  static list *PlayIndex[MAXPLAY];
  static list *ShipIndex[MAXPLAY * MAXSHIP];

  if (!n)
    return 0;
  i = 0;

  /* Doing planet lookup */
  if (base == planets)
  {
    if (PlanIndex[0] == NULL)
    {
      assert (MAXPLAN > nplanets);
      PlanIndex[0] = (list *) 0xff;
      for (p = base; p; p = p->next)
      {
        PlanIndex[++i] = p;
      }
    }
    if (n < 0)
      return (NULL);
    return (PlanIndex[n]);
  }

  /* Doing Player lookup */
  if (base == players)
  {
    if (PlayIndex[0] == NULL)
    {
      PlayIndex[0] = (list *) 0xff;
      for (p = base; p; p = p->next)
      {
        assert (i < MAXPLAY);
        PlayIndex[++i] = p;
      }
    }
    if (n < 0)
      return (NULL);
    return (PlayIndex[n]);
  }

  /* Doing Shiptype lookup */
  if (base == shiptypes)
  {
    if (ShipIndex[0] == NULL)
    {
      ShipIndex[0] = (list *) 0xff;
      for (p = base; p; p = p->next)
      {
        if (i < (MAXPLAY * MAXSHIP))
          ShipIndex[++i] = p;
        else
          break;
      }
    }
    if (n < MAXPLAY * MAXSHIP)
      return (ShipIndex[n]);    /* Shortcut */
    else
    {
      /* BUG: Should really call numtop with ShipIndex[MAXPLAY * MAXSHIP-1]
         and decrement n */
      return (numtop (base, n));
    }
  }
  /* Should never get here, but gcc grumbles if we don't do this */
  assert (0);
  return (0);
}
#endif

void *
numtop (void *base, int n)
{
  list *p;
  int i = 0;

  if (!n)
    return 0;
  for (p = base; p; p = p->next)
  {
    i++;
    if (i == n)
      return p;
  }
  return 0;
}

#ifndef PREPROC
void
dip_status (player * P1, player * P2)
{
  alliance *a;
  defensive *d;

  if (P1 && P2)
  {
    if (P1 == P2)
    {
      printf ("Players equal\n");
      return;
    }
    else
    {
      for (a = P1->allies; a; a = a->next)
        if (a->ally == P2)
        {
          printf ("Peace\n");
          return;
        }
      for (d = P1->defend; d; d = d->next)
        if (d->defend == P2)
        {
          printf ("Guard\n");
          return;
        }
    }
    printf ("War\n");
  }
  return;
}

void
delete_player (player * P, int force)
{
  player *P2;
  shiptype *T, *T2;
  alliance *a, *a2;
  defensive *d, *d2;
  planet *p;

#ifdef KCMD
  planetdefense *df, *df2;

#endif
  techinfo *ot, *ot2;

  if (force)
  {
    for (p = planets; p; p = p->next)
      if (p->owner == P)
        sterilize (p);
    freelist (P->groups);
    P->groups = NULL;
  }
  else
    for (p = planets; p && p->owner != P; p = p->next)
      ;                         /* Does he have Planets ? */

  if (!p && !P->groups)         /* No planets or groups... */
  {
    printf ("Deleting dead player %s\n", P->name);
    for (T = shiptypes; T;)
    {
      T2 = T->next;
      if (T->owner == P)
        removelist (&shiptypes, T);
      T = T2;
    }
    for (P2 = players; P2; P2 = P2->next)
      for (a = P2->allies; a; a = a2)
      {
        a2 = a->next;
        if (a->ally == P)
        {
          removelist (&P2->allies, a);
          break;
        }
      }
    freelist (P->allies);
    for (P2 = players; P2; P2 = P2->next)
      for (d = P2->defend; d; d = d2)
      {
        d2 = d->next;
        if (d->defend == P)
        {
          removelist (&P2->defend, d);
          break;
        }
      }
    freelist (P->defend);

#ifdef KCMD
    for (df = defender; df; df = df2)
    {
      df2 = df->next;
      if (df->defender == P)
        removelist (&defender, df);
    }
#endif

    freelist (P->othertechs);
    for (P2 = players; P2; P2 = P2->next)
    {
      if (P2 != P)
        for (ot = P2->othertechs; ot; ot = ot2)
        {
          ot2 = ot->next;
          if (ot->whoose == P)
          {
            removelist (P2->othertechs, ot);
          }
        }
    }
    removelist (&players, P);
  }
}

extern FILE *pr;

void
forward_list ()
{
  player *P, *P2;
  planet *p;
  double *seen;
  group *g;
  double m;
  int see;

  seen = alloc (sizeof (double) * nplanets);

  for (P = players; P; P = P->next)
  {
    memset (seen, 0, (sizeof (double) * nplanets));

    /* Build a matrix of systems we can see */
    for (g = P->groups; g; g = g->next)
    {
      if (g->dist == 0.0)
      {
        /* Mass =  "0" if it is only drive */
        if ((g->type->guns > 0.0) || (g->type->shields > 0.0) || (g->type->cargo > 0.0))
          m = typemass (g->type);
        else
          m = 0.1;

        seen[atoi (g->where->num)] = max (seen[atoi (g->where->num)], m);
      }
    }

    /* For all other players ... */
    for (P2 = players; P2; P2 = P2->next)
    {
      if (P2 != P)
      {
        see = 0;

        /* They own a system we can see */
        for (p = planets; p; p = p->next)
          if ((p->owner == P2) && canseeplanet (P, p, 0))
            break;

        if (p)
        {
          fprintf (pr, "%-s:%-s\n", P->name, P2->name);
        }
        else
        {
          /* They do NOT own a system we can see, so check group masses */
          for (g = P2->groups; g; g = g->next)
          {
            if (g->dist == 0.0)
            {
              if ((g->type->guns > 0.0) || (g->type->shields > 0.0) ||
                  (g->type->cargo > 0.0))
                m = typemass (g->type);
              else
                m = 0.1;

              if ((seen[atoi (g->where->num)] > 1.0) || ((m > 1.0) &&
                                       (seen[atoi (g->where->num)] >= 1.0)))
              {
                see = 1;
                break;
              }
            }
          }
          if (see)
            fprintf (pr, "%-s:%-s\n", P->name, P2->name);

        }
      }
    }
    fprintf (pr, "%-s:%-s\n", P->name, P->name);
  }
  free (seen);
}
#endif

#ifdef ULTRIX
/* One of these two may work. Take your pick.
   Mr SJ Murphy <sjmur2@aurora.cc.monash.edu.au> claims that the 
   first dumps core. Strange. 

   11/20/94- Yup, args to memcpy were backwards. HJB */

char *
strdup (char *s1)
{
  char *s2;
  size_t len;

  len = strlen (s1) + 1;
  s2 = (char *) alloc (sizeof (char) * len);

  memcpy (s2, s1, len);
  return s2;
}
/* Duplicate S, returning an identical malloc'd string.  */
/* Note: you may have to hack this a bit, as I couldn't get it 
   to work on the Ultrix machine I have access to. 
   #include <ansidecl.h>
   #include <stddef.h>
   #include <stdlib.h>
   #include <string.h>

   char *
   DEFUN(strdup, (s), CONST char *s)
   {
   size_t len = strlen(s) + 1;
   PTR new = malloc(len);

   if (new == NULL)
   return NULL;

   memcpy(new, (PTR) s, len);

   return (char *) new;
   }
 */
#endif
