/* 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 #include // abs() #include // sprintf() #include // 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