/* c_sector.cc 1.19 95/12/23 03:11:28 */


// xspacewarp by Greg Walker (gow@math.orst.edu)

// This is free software. Non-profit redistribution and/or modification
// is allowed and welcome.


// methods for the sector class

#include "c_sector.hh"
#include <iostream.h>
#include <stdlib.h>		// abs()
#include <stdio.h>		// sprintf()
#include <math.h>		// sqrt()
#include "common.hh"
#include "params.hh"
#include "globals.hh"
#include "c_block.hh"
#include "c_body.hh"
#include "space_objects.hh"
#include "messages.hh"

extern void flash_to(XtPointer client_data, XtIntervalId *id);
extern void clear_echo(void);
extern void echo(const char *str, int len);


// define static members

char Sector::attack_msg[ATTACK_STR_LEN1 + ATTACK_STR_LEN2 + 6];
Ucoors Sector::attack_loc;


Sector::Sector()
{
  base_pt = NULL;
  bh_pt = NULL;
  endv_pt = NULL;
  jovrec_pt = NULL;
  starrec_pt = NULL;
  njovians = 0;
  nstars = 0;
  mask = false;
}


// restore sector to empty state for program restarts

void Sector::erase()
{
  Jovian_rec *jr_pt, *nextjr_pt;
  Star_rec *sr_pt, *nextsr_pt;

  if (gamestate.input != REPLAY)
  {
    cerr << "xspacewarp: attempt to erase sector while game in progress."
         << endl;
    exit(1);
  }

  // de-allocate Jovian_recs used in this sector

  jr_pt = jovrec_pt;
  while (jr_pt)
  {
    nextjr_pt = jr_pt->next;
    delete jr_pt;
    jr_pt = nextjr_pt;
  }

  // de-allocate Star_recs used in this sector

  sr_pt = starrec_pt;
  while (sr_pt)
  {
    nextsr_pt = sr_pt->next;
    delete sr_pt;
    sr_pt = nextsr_pt;
  }

  base_pt = NULL;
  bh_pt = NULL;
  endv_pt = NULL;
  jovrec_pt = NULL;
  starrec_pt = NULL;
  njovians = 0;
  nstars = 0;
  mask = false;
}


// so the sector knows its position within the universe matrix

void Sector::setucoors(int ur, int uc)
{
  if (ur < 1  || uc < 1)
  {
    cerr << "xspacewarp: Sector::setucoors: illegal coordinates." << endl;
    exit(1);
  }

  ucoors.urow = ur;
  ucoors.ucol = uc;
}


// include a base in the sector

bool Sector::add_base(Base& base)
{
  int row, col;

  if (base_pt)			// at most one base per sector
     return (false);

  // find random location for base

  while (1)
  {
    row = rand() % SECTROWS;
    col = 1 + rand() % (SECTCOLS-2);
    
    // reject this random position if it overlaps some other
    // body in the sector or if it places the base too
    // close to a jovian

    if (!overlap(row, col, 3) && !near_jovian(row, col))
       break;
  }

  base.setrow(row);
  base.setcol(col);
  base_pt = &base;

  return (true);
}


// include a blackhole

bool Sector::add_blackhole(Blackhole& bh)
{
  int row, col;

  if (bh_pt)			// at most one blackhole per sector
     return (false);

  // find random location for blackhole

  while (1)
  {
    row = rand() % SECTROWS;
    col = rand() % SECTCOLS;
    
    // check if this random position overlaps some other body in the sector

    if (!overlap(row, col, 1))
       break;
  }

  bh.setrow(row);
  bh.setcol(col);
  bh_pt = &bh;

  return (true);
}


// put the endever in this sector

void Sector::add_endever()	// always succeeds: no return value
{
  int row, col;

  // find random location for endever

  while (1)
  {
    row = rand() % SECTROWS;
    col = 1 + rand() % (SECTCOLS-2);
    

    // reject this random position if it overlaps some other
    // body in the sector or if it places the base too
    // close to a jovian

    if (!overlap(row, col, 3) && !near_jovian(row, col))
       break;
  }

  endever.setrow(row);
  endever.setcol(col);
  endv_pt = &endever;
}


// the "next" jovian record is the one that got added before the
// current record. this version of add_jovian() is used only when
// initializing the universe. It creates a new JovianRec struct
// and tacks onto the linked list.

bool Sector::add_jovian(Jovian* j_pt)
{
  Jovian_rec* newjr_pt;
  int row, col;

  if (njovians == MAXJOVSECT)	// too many jovians
     return (false);

  if (!endv_pt && !base_pt)	// no targets around
     Jovian::addleapable(1);
  else if (!endv_pt && base_pt)	// only a base around
     Jovian::addraidable(1);

  if (!(newjr_pt = new Jovian_rec)) // make new record
  {
    cerr << "xspacewarp: Sector::add_jovian: not enough memory." << endl;
    exit(1);
  }
  else
  {
    newjr_pt->jov_pt = j_pt;
  }

  // find random location for jovian

  while (1)
  {
    row = rand() % SECTROWS;
    col = 1 + rand() % (SECTCOLS-2);
    
    // Reject this random position if it overlaps some other
    // body in the sector or if it is too close to a target
    // (base or endever). This is so the game starts fair.

    if (!overlap(row, col, 3) && !near_target(row, col))
       break;
  }

  // tack onto the linked list

  newjr_pt->jov_pt->setrow(row);
  newjr_pt->jov_pt->setcol(col);
  if (jovrec_pt)
  {
    jovrec_pt->prev = newjr_pt;
    newjr_pt->next = jovrec_pt;
    newjr_pt->prev = NULL;
    jovrec_pt = newjr_pt;
  }
  else
  {
    newjr_pt->prev = NULL;
    newjr_pt->next = NULL;
    jovrec_pt = newjr_pt;
  }
  njovians++;

  return (true);
}


// the "next" jovian record is the one that got added before the
// current record. this is the version of add_jovian() used when
// a jovian leaps to a different sector. It does not allocate a
// new JovianRec, instead the caller passes one in.

bool Sector::add_jovian(Jovian_rec* jr_pt)
{
  int row, col;

  if (njovians == MAXJOVSECT)	// too many
     return (false);

  if (!endv_pt && !base_pt)	// no targets around
     Jovian::addleapable(1);
  else if (!endv_pt && base_pt)	// only a base around
     Jovian::addraidable(1);

  // find random location for jovian

  while (1)
  {
    if (endv_pt)	// get random location on an edge if endever is around
    {
      switch (rand() % 4)
      {
      case 0:			// place jovian on left edge
	col = 1;
	row = rand() % SECTROWS;
	break;
      case 1:			// place jovian on right edge
	col = SECTCOLS-2;
	row = rand() % SECTROWS;
	break;
      case 2:			// place jovian on top edge
	row = 0;
	col = 1 + rand() % (SECTCOLS-2);
	break;
      case 3:			// place jovian on bottom edge
	row = SECTROWS-1;
	col = 1 + rand() % (SECTCOLS-2);
	break;
      }
    }
    else     // accept random location anywhere in sector if endever not around
    {
      row = rand() % SECTROWS;
      col = 1 + rand() % (SECTCOLS-2);
    }
    
    // check if this random position overlaps some other body in the sector

    if (!overlap(row, col, 3))
       break;
  }

  // tack onto the linked list

  jr_pt->jov_pt->setrow(row);
  jr_pt->jov_pt->setcol(col);
  if (jovrec_pt)		// other jovs in list
  {
    jovrec_pt->prev = jr_pt;
    jr_pt->next = jovrec_pt;
    jr_pt->prev = NULL;
    jovrec_pt = jr_pt;
  }
  else				// only jov in list
  {
    jr_pt->prev = NULL;
    jr_pt->next = NULL;
    jovrec_pt = jr_pt;
  }
  njovians++;

  return (true);
}


// tack a star onto the linked list of stars.

bool Sector::add_star(Star* s_pt)
{
  Star_rec* newsr_pt;
  int row, col;

  if (nstars == MAXSTARSECT)	// too many stars
     return (false);

  if (!(newsr_pt = new Star_rec)) // make new record
  {
    cerr << "xspacewarp: Sector::add_star: not enough memory." << endl;
    exit(1);
  }
  else
  {
    newsr_pt->star_pt = s_pt;
  }

  // find random location for star

  while (1)
  {
    row = rand() % SECTROWS;
    col = rand() % SECTCOLS;
    
    // check if this random position overlaps some other body in the sector

    if (!overlap(row, col, 1))
       break;
  }

  // tack onto the linked list

  newsr_pt->star_pt->setrow(row);
  newsr_pt->star_pt->setcol(col);
  if (starrec_pt)
  {
    newsr_pt->next = starrec_pt;
  }
  else
  {
    newsr_pt->next = NULL;
  }
  starrec_pt = newsr_pt;

  nstars++;
  return (true);
}


// call the draw methods on all the ships, etc contained in the sector.

void Sector::draw(Drawable drawable) const
{
  Jovian_rec *jrec_pt;
  Star_rec *srec_pt;

  if (base_pt)			// check if sector contains a base
    base_pt->draw(drawable, baseGC);
  if (bh_pt)
    bh_pt->draw(drawable, blackholeGC);
  if (endv_pt)
    endv_pt->draw(drawable, endeverGC);
  
  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    jrec_pt->jov_pt->draw(drawable, jovianGC);
    jrec_pt = jrec_pt->next;
  }
  
  srec_pt = starrec_pt;
  while (srec_pt)
  {
    srec_pt->star_pt->draw(drawable, starGC);
    srec_pt = srec_pt->next;
  }
}


// remove base from sector

void Sector::rm_base()
{
  if (base_pt)
  {
    base_pt = NULL;
  }
  else
  {
    cerr << "xspacewarp: class Sector: no base in sector." << endl;
    exit(1);
  }
}


// remove endever from sector

void Sector::rm_endever()
{
  if (endv_pt)
  {
    endv_pt = NULL;
  }
  else
  {
    cerr << "xspacewarp: class Sector: no endever in sector." << endl;
    exit(1);
  }
}


// Remove the jovian having the given id. Return a pointer to
// the Jovian_rec containing the jovian that was removed. This
// pointer can subsequently be used to add the jovian to a
// different sector. Return NULL and print error message if
// there is no jovian in the sector with the given id.

Jovian_rec* Sector::rm_jovian(int id)
{
  Jovian_rec *jrec_pt;

  // alter Jovian data that jovians use to find sectors to leap to.

  if (!endv_pt && !base_pt)	// no targets around
     Jovian::addleapable(-1);
  else if (!endv_pt && base_pt)	// only a base around
     Jovian::addraidable(-1);

  // check if there is a jovian in the list having the given id.

  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    if (jrec_pt->jov_pt->getid() == id) // found it
    {
      // 4 cases to consider

      if ((jrec_pt->prev == NULL) && (jrec_pt->next == NULL))
      {
	jovrec_pt = NULL;	// only member of list
      }
      else if ((jrec_pt->prev == NULL) && (jrec_pt->next != NULL))
      {
	jrec_pt->next->prev = NULL; // head of list
	jovrec_pt = jrec_pt->next;
      }
      else if ((jrec_pt->prev != NULL) && (jrec_pt->next != NULL))
      {
	jrec_pt->prev->next = jrec_pt->next; // middle of list
	jrec_pt->next->prev = jrec_pt->prev;
      }
      else
      {
	jrec_pt->prev->next = NULL; // tail of list
      }
      njovians--;
      return (jrec_pt);
    }
    jrec_pt = jrec_pt->next;
  }
  cerr << "xspacewarp: Sector::rm_jovian: no such jovian in sector." << endl;
  return (NULL);
}


// Given a row/col position in the sector, return a pointer to the 
// Body at that location or NULL if row/col is empty space.

Body* Sector::occupant(int row, int col) const
{
  Jovian_rec *jrec_pt;
  Star_rec *srec_pt;
  int c, left, right;

  // check if row/col contains a base

  if (base_pt)
  {
    left = base_pt->getcol() - Base::geticon_len()/2;
    right = base_pt->getcol() + Base::geticon_len()/2;
    if (row == (base_pt->getrow()) && (col <= right) && (col >= left))
       return ((Body *)base_pt);
  }

  // check if row/col contains a blackhole

  if (bh_pt)
  {
    left = bh_pt->getcol() - Blackhole::geticon_len()/2;
    right = bh_pt->getcol() + Blackhole::geticon_len()/2;
    if (row == (bh_pt->getrow()) && (col <= right) && (col >= left))
       return ((Body *)bh_pt);
  }

  // check if row/col contains the endever

  if (endv_pt)
  {
    left = endv_pt->getcol() - Endever::geticon_len()/2;
    right = endv_pt->getcol() + Endever::geticon_len()/2;
    if (row == (endv_pt->getrow()) && (col <= right) && (col >= left))
       return ((Body *)endv_pt);
  }

  // check if row/col contains a jovian

  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    left = jrec_pt->jov_pt->getcol() - Jovian::geticon_len()/2;
    right = jrec_pt->jov_pt->getcol() + Jovian::geticon_len()/2;
    if (row == (jrec_pt->jov_pt->getrow()) && (col <= right) && (col >= left))
       return ((Body *)(jrec_pt->jov_pt));
    jrec_pt = jrec_pt->next;
  }

  // check if row/col contains a star

  srec_pt = starrec_pt;
  while (srec_pt)
  {
    left = srec_pt->star_pt->getcol() - Star::geticon_len()/2;
    right = srec_pt->star_pt->getcol() + Star::geticon_len()/2;
    if (row == (srec_pt->star_pt->getrow()) && (col <= right) && (col >= left))
       return ((Body *)(srec_pt->star_pt));
    srec_pt = srec_pt->next;
  }

  // row/col contains nothing

  return (NULL);
}


// return true if a collection, "width" blocks wide, of
// consecutive blocks in a row centered at (row,col) overlap
// some object in the sector.

bool Sector::overlap(int row, int col, int width) const
{
  Jovian_rec *jrec_pt;
  Star_rec *srec_pt;

  // check for overlap w/ a base

  if (base_pt && (base_pt->getrow() == row) &&
      (abs(base_pt->getcol() - col) <= (width+1)/2))
     return (true);

  // check for overlap w/ a blackhole

  if (bh_pt && (bh_pt->getrow() == row) &&
      (abs(bh_pt->getcol() - col) <= ((width+1)/2 - 1)))
     return (true);

  // check for overlap w/ the endever

  if (endv_pt && (endv_pt->getrow() == row) &&
      (abs(endv_pt->getcol() - col) <= (width+1)/2))
     return (true);

  // check for overlap w/ a jovian

  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    if ((jrec_pt->jov_pt->getrow() == row) &&
	(abs(jrec_pt->jov_pt->getcol() - col) <= (width+1)/2))
       return (true);
    jrec_pt = jrec_pt->next;
  }

  // check for overlap w/ a star

  srec_pt = starrec_pt;
  while (srec_pt)
  {
    if ((srec_pt->star_pt->getrow() == row) &&
	(abs(srec_pt->star_pt->getcol() - col) <= ((width+1)/2 - 1)))
       return (true);
    srec_pt = srec_pt->next;
  }

  return (false);
}


// Given a row/col position within this sector, return true if
// that position is within MINJOVDIST of some jovian inside the
// sector.

bool Sector::near_jovian(int row, int col) const
{
  Jovian_rec *jrec_pt;
  Block pos(SECTROW+row, SECTCOL+col); // block for the given row/col position
  Point cen;			       // center of "pos"

  cen = pos.center();
 
  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    if (sqrt((double)sqdist(jrec_pt->jov_pt->center(), cen)) < MINJOVDIST)
       return (true);
    jrec_pt = jrec_pt->next;
  }

  return (false);
}


// Given a row/col position within this sector, return true if
// that position is within MINJOVDIST of a base or the endever.

bool Sector::near_target(int row, int col) const
{
  Block pos(SECTROW+row, SECTCOL+col); // block for the given row/col position
  Point cen;			       // center of "pos"

  cen = pos.center();
  
  if (base_pt &&
      sqrt((double)sqdist(base_pt->center(), cen)) < MINJOVDIST)
  {
    return (true);
  }
  else if (endv_pt &&
	   sqrt((double)sqdist(endv_pt->center(), cen)) < MINJOVDIST)
  {
    return (true);
  }
  else
  {
    return (false);
  }
}


// See if the faser/torpedo hits any of the objects in the
// sector. A hit occurs whenever the left or right edge (looking
// up the line of fire) of the shot intersects one of the 9x15
// blocks containing an object. If a hit occurs, return a
// HitReport describing the impact. HitReport contains an
// adjusted value, "newto", for "to" such that from->newto does
// not touch the object that the faser/torpedo hit. "newto" is set
// to "to" if there were no hits. detect_hit() assumes the
// length of from->to is greater than the diagonals of the
// objects in the sector, ie assumes from->to cannot fit
// completely inside the 3-block rectangle containing a jovian,
// a base or the endever.

HitReport Sector::detect_hit(const Shot& sh) const
{
  HitReport hr;
  Point nw, ne, se, sw, lfrom, rfrom, lto, rto;
  Block block;
  Jovian_rec *jrec_pt;
  Star_rec *srec_pt;

  // lfrom->lto and rfrom->rto are the right and left
  // edges of the shot when looking up the line of fire.

  if (sh.weapon == FASER)
  {
    lfrom.x = sh.from.x - (int)(sh.sin*FASERWTH/2);
    lfrom.y = sh.from.y - (int)(sh.cos*FASERWTH/2);
    rfrom.x = sh.from.x + (int)(sh.sin*FASERWTH/2);
    rfrom.y = sh.from.y + (int)(sh.cos*FASERWTH/2);
    lto.x = sh.to.x - (int)(sh.sin*FASERWTH/2);
    lto.y = sh.to.y - (int)(sh.cos*FASERWTH/2);
    rto.x = sh.to.x + (int)(sh.sin*FASERWTH/2);
    rto.y = sh.to.y + (int)(sh.cos*FASERWTH/2);
  }
  else
  {
    lfrom.x = sh.from.x - (int)(sh.sin*TORPWTH/2);
    lfrom.y = sh.from.y - (int)(sh.cos*TORPWTH/2);
    rfrom.x = sh.from.x + (int)(sh.sin*TORPWTH/2);
    rfrom.y = sh.from.y + (int)(sh.cos*TORPWTH/2);
    lto.x = sh.to.x - (int)(sh.sin*TORPWTH/2);
    lto.y = sh.to.y - (int)(sh.cos*TORPWTH/2);
    rto.x = sh.to.x + (int)(sh.sin*TORPWTH/2);
    rto.y = sh.to.y + (int)(sh.cos*TORPWTH/2);
  }

  // see if lfrom->lto or rfrom->rto passes through base

  if (base_pt)
  {
    block.setrow(SECTROW + base_pt->getrow());
    block.setcol(SECTCOL + base_pt->getcol() - 1);
    nw = block.northwest();
    sw = block.southwest();
    block.setcol(SECTCOL + base_pt->getcol() + 1);
    ne = block.northeast();
    se = block.southeast();
    if (intersect(nw, ne, se, sw, lfrom, lto) ||
	intersect(nw, ne, se, sw, rfrom, rto))
    {
      hr.damage = base_pt->hit(sh);
      hr.what = BASE;
      hr.newto = newto(nw, ne, se, sw, sh.from, sh.to, sh.sin, sh.cos);
      

      if (sh.source == JOVIAN && !endv_pt) // base being raided
      {
	if (hr.damage == FATAL)
	{
	  // announce that a base got destroyed while endever not around

	  const int DEAD_STR_LEN1 = (XtNumber(dead_base_str_1) - 1);
	  const int DEAD_STR_LEN2 = (XtNumber(dead_base_str_2) - 1);
	  char dead_base_msg[DEAD_STR_LEN1 + DEAD_STR_LEN2 + 6];
	  
	  sprintf(dead_base_msg, "%s%c%1d%c%1d%c%s", dead_base_str_1, '(',
		  ucoors.urow, ',', ucoors.ucol, ')', dead_base_str_2);
	  clear_echo();
	  echo(dead_base_msg, DEAD_STR_LEN1 + DEAD_STR_LEN2 + 5); // w/o NULL
	  gamestate.input = ACTION;
	}
	else
	{
	  // flash warning that base is under attack when endever not around

	  sprintf(attack_msg, "%s%c%1d%c%1d%c%s", attack_warning_str_1, '(',
		  ucoors.urow, ',', ucoors.ucol, ')', attack_warning_str_2);
	  attack_loc = ucoors;
	  flash_to((XtPointer)1, NULL);
	}
      }

      return (hr);
    }
  }
  
  // see if lfrom->lto or rfrom->rto passes through blackhole

  if (bh_pt)
  {
    block.setrow(SECTROW + bh_pt->getrow());
    block.setcol(SECTCOL + bh_pt->getcol());
    nw = block.northwest();
    sw = block.southwest();
    ne = block.northeast();
    se = block.southeast();
    if (intersect(nw, ne, se, sw, lfrom, lto) ||
	intersect(nw, ne, se, sw, rfrom, rto))
    {
      hr.damage = NONFATAL;
      hr.what = BLACKHOLE;
      hr.newto = newto(nw, ne, se, sw, sh.from, sh.to, sh.sin, sh.cos);
      return (hr);
    }
  }

  // see if lfrom->lto or rfrom->rto passes through endever

  if (endv_pt)
  {
    block.setrow(SECTROW + endv_pt->getrow());
    block.setcol(SECTCOL + endv_pt->getcol() - 1);
    nw = block.northwest();
    sw = block.southwest();
    block.setcol(SECTCOL + endv_pt->getcol() + 1);
    ne = block.northeast();
    se = block.southeast();
    if (intersect(nw, ne, se, sw, lfrom, lto) ||
	intersect(nw, ne, se, sw, rfrom, rto))
    {
      hr.damage = endv_pt->hit(sh);
      hr.what = ENDEVER;
      hr.newto = newto(nw, ne, se, sw, sh.from, sh.to, sh.sin, sh.cos);
      return (hr);
    }
  }

  // see if lfrom->lto or rfrom->rto passes through a jovian

  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    block.setrow(SECTROW + jrec_pt->jov_pt->getrow());
    block.setcol(SECTCOL + jrec_pt->jov_pt->getcol() - 1);
    nw = block.northwest();
    sw = block.southwest();
    block.setcol(SECTCOL + jrec_pt->jov_pt->getcol() + 1);
    ne = block.northeast();
    se = block.southeast();
    if (intersect(nw, ne, se, sw, lfrom, lto) ||
	intersect(nw, ne, se, sw, rfrom, rto))
    {
      hr.damage = jrec_pt->jov_pt->hit(sh);
      hr.what = JOVIAN;
      hr.newto = newto(nw, ne, se, sw, sh.from, sh.to, sh.sin, sh.cos);
      return (hr);
    }
    jrec_pt = jrec_pt->next;
  }

  // see if lfrom->lto or rfrom->rto passes through a star

  srec_pt = starrec_pt;
  while (srec_pt)
  {
    block.setrow(SECTROW + srec_pt->star_pt->getrow());
    block.setcol(SECTCOL + srec_pt->star_pt->getcol());
    nw = block.northwest();
    sw = block.southwest();
    ne = block.northeast();
    se = block.southeast();
    if (intersect(nw, ne, se, sw, lfrom, lto) ||
	intersect(nw, ne, se, sw, rfrom, rto))
    {
      hr.damage = NONFATAL;
      hr.what = STAR;
      hr.newto = newto(nw, ne, se, sw, sh.from, sh.to, sh.sin, sh.cos);
      return (hr);
    }
    srec_pt = srec_pt->next;
  }

  // nothing was hit
 
  hr.damage = NONFATAL;
  hr.what = NOTHING;
  hr.newto = sh.to;
  return (hr);
}


// Return true if the line segment from->to passes through the
// rectangle with corners nw, ne, se, sw (compass
// positions). Theory: we can represent the line from->to with
// the vector formula (x,y) = from + s(to - from) and represent
// the rectangle edge nw->ne with the formula
// (x,y) = nw + t(ne - nw).  If we set these formulas equal to
// eachother and solve the linear system for the scalars s and
// t, then the line segments from->to and nw->ne intersect iff
// the determinant is nonzero and 0 <= s <= 1 and
// 0 <= t <= 1. Similarly, we can test whether from->to
// intersects any of the other edges of the rectangle. from->to
// enters the rectangle iff from->to intersects one of the edges
// (assuming from->to is too long to fit completely within the
// rectangle).

bool Sector::intersect(Point nw, Point ne, Point se, Point sw,
	       Point from, Point to) const
{
  float s, t;			// scalar parameters
  int num, det;			// numerator, determinant

  // see if edge nw->ne intersects line from->to.
  
  det = (to.x - from.x)*(nw.y - ne.y) - (to.y - from.y)*(nw.x - ne.x);
  if (det)
  {
    num = (nw.x - from.x)*(nw.y - ne.y) - (nw.y - from.y)*(nw.x - ne.x);
    s = (float)num / (float)det;
    num = (to.x - from.x)*(nw.y - from.y) - (to.y - from.y)*(nw.x - from.x);
    t = (float)num / (float)det;
    if ((0 <= s) && (s <= 1) && (0 <= t) && (t <= 1))
       return (true);
  }

  // see if edge sw->se intersects line from->to.
  
  det = (to.x - from.x)*(sw.y - se.y) - (to.y - from.y)*(sw.x - se.x);
  if (det)
  {
    num = (sw.x - from.x)*(sw.y - se.y) - (sw.y - from.y)*(sw.x - se.x);
    s = (float)num / (float)det;
    num = (to.x - from.x)*(sw.y - from.y) - (to.y - from.y)*(sw.x - from.x);
    t = (float)num / (float)det;
    if ((0 <= s) && (s <= 1) && (0 <= t) && (t <= 1))
       return (true);
  }

  // see if edge nw->sw intersects line from->to.
  
  det = (to.x - from.x)*(nw.y - sw.y) - (to.y - from.y)*(nw.x - sw.x);
  if (det)
  {
    num = (nw.x - from.x)*(nw.y - sw.y) - (nw.y - from.y)*(nw.x - sw.x);
    s = (float)num / (float)det;
    num = (to.x - from.x)*(nw.y - from.y) - (to.y - from.y)*(nw.x - from.x);
    t = (float)num / (float)det;
    if ((0 <= s) && (s <= 1) && (0 <= t) && (t <= 1))
       return (true);
  }

  // see if edge ne->se intersects line from->to.
  
  det = (to.x - from.x)*(ne.y - se.y) - (to.y - from.y)*(ne.x - se.x);
  if (det)
  {
    num = (ne.x - from.x)*(ne.y - se.y) - (ne.y - from.y)*(ne.x - se.x);
    s = (float)num / (float)det;
    num = (to.x - from.x)*(ne.y - from.y) - (to.y - from.y)*(ne.x - from.x);
    t = (float)num / (float)det;
    if ((0 <= s) && (s <= 1) && (0 <= t) && (t <= 1))
       return (true);
  }

  return (false);
}


// Given a rectangle {nw, ne, se, sw} and a line segment
// from->to, determine the corner of the rectangle nearest the
// point "from", project this corner onto the point "pnt" in
// from->to, and return "pnt", a new value for "to".  A faser
// drawn from "from" to "pnt" will not touch the
// rectangle. Arguments "sine" and "cosine" refer to the sine
// and cosine of the firing angle.

Point Sector::newto(Point nw, Point ne, Point se, Point sw,
		    Point from, Point to, double sine, double cosine) const
{
  Point nearest;		// corner nearest to "from"
  Point proj;			// projection of "nearest" onto from->to
  long sdist, tmp;	// square of distance
  double f_t, f_n, n_t, f_p;	// distances:
				// f_t = "from" to "to"
				// f_n = "from" to "nearest"
				// n_t = "nearest" to "to"
				// f_p = "from" to "proj"

  // find corner of rectangle nearest to the point "from"
  
  nearest = nw;
  sdist = sqdist(nw, from);

  tmp = sqdist(ne, from);
  if (tmp < sdist)
  {
    nearest = ne;
    sdist = tmp;
  }

  tmp = sqdist(se, from);
  if (tmp < sdist)
  {
    nearest = se;
    sdist = tmp;
  }

  tmp = sqdist(sw, from);
  if (tmp < sdist)
  {
    nearest = sw;
    sdist = tmp;
  }

  // compute lengths of edges of the triangle {from, to, nearest}

  f_n = sqrt((double)sdist);
  tmp = sqdist(to, from);
  f_t = sqrt((double)tmp);
  tmp = sqdist(to, nearest);
  n_t = sqrt((double)tmp);

  // via cosine law

  f_p = ((f_n * f_n) + (f_t * f_t) - (n_t * n_t)) / ((double)2 * f_t);

  // obtain "proj"

  proj.x = from.x + (int)(f_p * cosine);
  proj.y = from.y - (int)(f_p * sine);

  return (proj);
}


// invoke energize method on each inhabitant of the sector

void Sector::energize()
{
  Jovian_rec *jrec_pt;

  if (base_pt)
     base_pt->energize();

  if (endv_pt)
     endv_pt->energize();

  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    jrec_pt->jov_pt->energize();
    jrec_pt = jrec_pt->next;
  }
}


// invoke the jovian update method for each jovian in the sector
// so the jovians will fight back.

void Sector::jovian()
{
  Jovian_rec *jrec_pt;

  jrec_pt = jovrec_pt;
  while (jrec_pt)
  {
    jrec_pt->jov_pt->update();
    jrec_pt = jrec_pt->next;
  }
}


// return the position of any base in the sector. coordinates
// are assigned -1 if no base in sector.

Point Sector::getbase()
{
  Point nobase = {-1, -1};

  if (base_pt)
     return (base_pt->center());
  else
     return (nobase);
}


// return position of endever if it is in the
// sector. coordinates are assigned -1 if no endever in sector.

Point Sector::getendever()
{
  Point noendv = {-1, -1};

  if (endv_pt)
     return (endv_pt->center());
  else
     return (noendv);
}

// end
