summaryrefslogtreecommitdiff
path: root/src/uqm/ipdisp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/ipdisp.c')
-rw-r--r--src/uqm/ipdisp.c777
1 files changed, 777 insertions, 0 deletions
diff --git a/src/uqm/ipdisp.c b/src/uqm/ipdisp.c
new file mode 100644
index 0000000..09a6fb8
--- /dev/null
+++ b/src/uqm/ipdisp.c
@@ -0,0 +1,777 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * 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 "ipdisp.h"
+
+#include "collide.h"
+#include "globdata.h"
+#include "init.h"
+#include "races.h"
+#include "process.h"
+#include "grpinfo.h"
+#include "encount.h"
+ // for EncounterGroup, EncounterRace
+#include "libs/mathlib.h"
+
+
+void
+NotifyOthers (COUNT which_race, BYTE target_loc)
+{
+ HSHIPFRAG hGroup, hNextGroup;
+
+ // NOTE: "Others" includes the group causing the notification too.
+
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ hGroup; hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ if (GroupPtr->race_id != which_race)
+ {
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ continue;
+ }
+
+ if (target_loc == IPNL_INTERCEPT_PLAYER)
+ {
+ GroupPtr->task &= ~IGNORE_FLAGSHIP;
+ // XXX: orbit_pos is abused here to store the previous
+ // group destination, before the intercept task.
+ // Returned to dest_loc below.
+ GroupPtr->orbit_pos = GroupPtr->dest_loc;
+ GroupPtr->dest_loc = IPNL_INTERCEPT_PLAYER;
+ }
+ else if (target_loc == IPNL_ALL_CLEAR)
+ {
+ GroupPtr->task |= IGNORE_FLAGSHIP;
+
+ if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ { // The group was intercepting, so send it back where it came
+ // XXX: orbit_pos was abused to store the previous
+ // group destination, before the intercept task.
+ GroupPtr->dest_loc = GroupPtr->orbit_pos;
+ GroupPtr->orbit_pos = NORMALIZE_FACING (TFB_Random ());
+#ifdef OLD
+ GroupPtr->dest_loc = (BYTE)(((COUNT)TFB_Random ()
+ % pSolarSysState->SunDesc[0].NumPlanets) + 1);
+#endif /* OLD */
+ }
+ // If the group wasn't intercepting, it will just continue
+ // going about its business.
+
+ if (!(GroupPtr->task & REFORM_GROUP))
+ {
+ if ((GroupPtr->task & ~IGNORE_FLAGSHIP) != EXPLORE)
+ GroupPtr->group_counter = 0;
+ else
+ GroupPtr->group_counter = ((COUNT) TFB_Random ()
+ % MAX_REVOLUTIONS) << FACING_SHIFT;
+ }
+ }
+ else
+ { // Send the group to the location.
+ // XXX: There is currently no use of such notify that I know of.
+ GroupPtr->task |= IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = target_loc;
+ }
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+}
+
+static SIZE
+zoomRadiusForLocation (BYTE location)
+{
+ if (location == 0)
+ { // In outer system view; use current zoom radius
+ return pSolarSysState->SunDesc[0].radius;
+ }
+ else
+ { // In inner system view; always max zoom
+ return MAX_ZOOM_RADIUS;
+ }
+}
+
+static inline void
+adjustDeltaVforZoom (SIZE zoom, SIZE *dx, SIZE *dy)
+{
+ if (zoom == MIN_ZOOM_RADIUS)
+ {
+ *dx >>= 2;
+ *dy >>= 2;
+ }
+ else if (zoom < MAX_ZOOM_RADIUS)
+ {
+ *dx >>= 1;
+ *dy >>= 1;
+ }
+}
+
+static BYTE
+getFlagshipLocation (void)
+{
+ if (!playerInInnerSystem ())
+ return 0;
+ else
+ return 1 + planetIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
+}
+
+static void
+ip_group_preprocess (ELEMENT *ElementPtr)
+{
+#define TRACK_WAIT 5
+ BYTE task;
+ BYTE target_loc, group_loc, flagship_loc;
+ SIZE radius;
+ POINT dest_pt;
+ SIZE vdx, vdy;
+ ELEMENT *EPtr;
+ IP_GROUP *GroupPtr;
+
+ EPtr = ElementPtr;
+ EPtr->state_flags &= ~(DISAPPEARING | NONSOLID); // "I'm not quite dead"
+ ++EPtr->life_span; // so that it will 'die' again next time
+
+ GetElementStarShip (EPtr, &GroupPtr);
+ group_loc = GroupPtr->sys_loc; // save old location
+ DisplayArray[EPtr->PrimIndex].Object.Point = GroupPtr->loc;
+
+ radius = zoomRadiusForLocation (group_loc);
+ dest_pt = locationToDisplay (GroupPtr->loc, radius);
+ EPtr->current.location.x = DISPLAY_TO_WORLD (dest_pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ EPtr->current.location.y = DISPLAY_TO_WORLD (dest_pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ InitIntersectStartPoint (EPtr);
+
+ flagship_loc = getFlagshipLocation ();
+
+ task = GroupPtr->task;
+
+ if ((task & REFORM_GROUP) && --GroupPtr->group_counter == 0)
+ { // Finished reforming the group
+ task &= ~REFORM_GROUP;
+ GroupPtr->task = task;
+ if ((task & ~IGNORE_FLAGSHIP) != EXPLORE)
+ GroupPtr->group_counter = 0;
+ else
+ GroupPtr->group_counter = ((COUNT)TFB_Random ()
+ % MAX_REVOLUTIONS) << FACING_SHIFT;
+ }
+
+ // If fleeing *and* ignoring flagship
+ if ((task & ~(IGNORE_FLAGSHIP | REFORM_GROUP)) == FLEE
+ && (task & IGNORE_FLAGSHIP))
+ { // Make fleeing groups non-collidable
+ EPtr->state_flags |= NONSOLID;
+ }
+
+ target_loc = GroupPtr->dest_loc;
+ if (!(task & (IGNORE_FLAGSHIP | REFORM_GROUP)))
+ {
+ if (target_loc == IPNL_INTERCEPT_PLAYER && task != FLEE)
+ {
+ /* if intercepting flagship */
+ target_loc = flagship_loc;
+ if (EPtr->thrust_wait > TRACK_WAIT)
+ {
+ EPtr->thrust_wait = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+ }
+ }
+ else if (group_loc == flagship_loc)
+ {
+ long detect_dist;
+
+ detect_dist = 1200;
+ if (group_loc != 0) /* if in planetary views */
+ {
+ detect_dist *= (MAX_ZOOM_RADIUS / MIN_ZOOM_RADIUS);
+ if (GroupPtr->race_id == URQUAN_DRONE_SHIP)
+ detect_dist <<= 1;
+ }
+ vdx = GLOBAL (ip_location.x) - GroupPtr->loc.x;
+ vdy = GLOBAL (ip_location.y) - GroupPtr->loc.y;
+ if ((long)vdx * vdx
+ + (long)vdy * vdy < (long)detect_dist * detect_dist)
+ {
+ EPtr->thrust_wait = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+
+ NotifyOthers (GroupPtr->race_id, IPNL_INTERCEPT_PLAYER);
+ task = GroupPtr->task;
+ target_loc = GroupPtr->dest_loc;
+ if (target_loc == IPNL_INTERCEPT_PLAYER)
+ target_loc = flagship_loc;
+ }
+ }
+ }
+
+ GetCurrentVelocityComponents (&EPtr->velocity, &vdx, &vdy);
+
+ task &= ~IGNORE_FLAGSHIP;
+#ifdef NEVER
+ if (task <= FLEE || (task == ON_STATION && GroupPtr->dest_loc == 0))
+#else
+ if (task <= ON_STATION)
+#endif /* NEVER */
+ {
+ BOOLEAN Transition;
+ SIZE dx, dy;
+ SIZE delta_x, delta_y;
+ COUNT angle;
+
+ Transition = FALSE;
+ if (task == FLEE)
+ {
+ dest_pt.x = GroupPtr->loc.x << 1;
+ dest_pt.y = GroupPtr->loc.y << 1;
+ }
+ else if (((task != ON_STATION ||
+ GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ && group_loc == target_loc)
+ || (task == ON_STATION &&
+ GroupPtr->dest_loc != IPNL_INTERCEPT_PLAYER
+ && group_loc == 0))
+ {
+ if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ dest_pt = GLOBAL (ip_location);
+ else
+ {
+ COUNT orbit_dist;
+ POINT org;
+
+ if (task != ON_STATION)
+ {
+ orbit_dist = ORBIT_RADIUS;
+ org.x = org.y = 0;
+ }
+ else
+ {
+ orbit_dist = STATION_RADIUS;
+ org = planetOuterLocation (target_loc - 1);
+ }
+
+ angle = FACING_TO_ANGLE (GroupPtr->orbit_pos + 1);
+ dest_pt.x = org.x + COSINE (angle, orbit_dist);
+ dest_pt.y = org.y + SINE (angle, orbit_dist);
+ if (GroupPtr->loc.x == dest_pt.x
+ && GroupPtr->loc.y == dest_pt.y)
+ {
+ BYTE next_loc;
+
+ GroupPtr->orbit_pos = NORMALIZE_FACING (
+ ANGLE_TO_FACING (angle));
+ angle += FACING_TO_ANGLE (1);
+ dest_pt.x = org.x + COSINE (angle, orbit_dist);
+ dest_pt.y = org.y + SINE (angle, orbit_dist);
+
+ EPtr->thrust_wait = (BYTE)~0;
+ if (GroupPtr->group_counter)
+ --GroupPtr->group_counter;
+ else if (task == EXPLORE
+ && (next_loc = (BYTE)(((COUNT)TFB_Random ()
+ % pSolarSysState->SunDesc[0].NumPlanets)
+ + 1)) != target_loc)
+ {
+ EPtr->thrust_wait = 0;
+ target_loc = next_loc;
+ GroupPtr->dest_loc = next_loc;
+ }
+ }
+ }
+ }
+ else if (group_loc == 0)
+ {
+ if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ dest_pt = pSolarSysState->SunDesc[0].location;
+ else
+ dest_pt = planetOuterLocation (target_loc - 1);
+ }
+ else
+ {
+ if (task == ON_STATION)
+ target_loc = 0;
+
+ dest_pt.x = GroupPtr->loc.x << 1;
+ dest_pt.y = GroupPtr->loc.y << 1;
+ }
+
+ delta_x = dest_pt.x - GroupPtr->loc.x;
+ delta_y = dest_pt.y - GroupPtr->loc.y;
+ angle = ARCTAN (delta_x, delta_y);
+
+ if (EPtr->thrust_wait && EPtr->thrust_wait != (BYTE)~0)
+ --EPtr->thrust_wait;
+ else if ((vdx == 0 && vdy == 0)
+ || angle != GetVelocityTravelAngle (&EPtr->velocity))
+ {
+ SIZE speed;
+
+ if (EPtr->thrust_wait &&
+ GroupPtr->dest_loc != IPNL_INTERCEPT_PLAYER)
+ {
+#define ORBIT_SPEED 60
+ speed = ORBIT_SPEED;
+ if (task == ON_STATION)
+ speed >>= 1;
+ }
+ else
+ {
+ SIZE RaceIPSpeed[] =
+ {
+ RACE_IP_SPEED
+ };
+
+ speed = RaceIPSpeed[GroupPtr->race_id];
+ EPtr->thrust_wait = TRACK_WAIT;
+ }
+
+ vdx = COSINE (angle, speed);
+ vdy = SINE (angle, speed);
+ SetVelocityComponents (&EPtr->velocity, vdx, vdy);
+ }
+
+ dx = vdx;
+ dy = vdy;
+ if (group_loc == target_loc)
+ {
+ if (target_loc == 0)
+ {
+ if (task == FLEE)
+ goto CheckGetAway;
+ }
+ else if (target_loc == GroupPtr->dest_loc)
+ {
+PartialRevolution:
+ if ((long)((COUNT)(dx * dx) + (COUNT)(dy * dy))
+ >= (long)delta_x * delta_x + (long)delta_y * delta_y)
+ {
+ GroupPtr->loc = dest_pt;
+ vdx = 0;
+ vdy = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+ }
+ }
+ }
+ else
+ {
+ if (group_loc == 0)
+ { // In outer system
+ adjustDeltaVforZoom (radius, &dx, &dy);
+
+ if (task == ON_STATION && GroupPtr->dest_loc)
+ goto PartialRevolution;
+ else if ((long)((COUNT)(dx * dx) + (COUNT)(dy * dy))
+ >= (long)delta_x * delta_x + (long)delta_y * delta_y)
+ Transition = TRUE;
+ }
+ else
+ { // In inner system; also leaving outer CheckGetAway hack
+CheckGetAway:
+ dest_pt = locationToDisplay (GroupPtr->loc, radius);
+ if (dest_pt.x < 0
+ || dest_pt.x >= SIS_SCREEN_WIDTH
+ || dest_pt.y < 0
+ || dest_pt.y >= SIS_SCREEN_HEIGHT)
+ Transition = TRUE;
+ }
+
+ if (Transition)
+ {
+ /* no collisions during transition */
+ EPtr->state_flags |= NONSOLID;
+
+ vdx = 0;
+ vdy = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+ if (group_loc != 0)
+ {
+ GroupPtr->loc = planetOuterLocation (group_loc - 1);
+ group_loc = 0;
+ GroupPtr->sys_loc = 0;
+ }
+ else if (target_loc == 0)
+ {
+ /* Group completely left the star system */
+ EPtr->life_span = 0;
+ EPtr->state_flags |= DISAPPEARING | NONSOLID;
+ GroupPtr->in_system = 0;
+ return;
+ }
+ else
+ {
+ POINT entryPt;
+
+ if (target_loc == GroupPtr->dest_loc)
+ {
+ GroupPtr->orbit_pos = NORMALIZE_FACING (
+ ANGLE_TO_FACING (angle + HALF_CIRCLE));
+ GroupPtr->group_counter =
+ ((COUNT)TFB_Random () % MAX_REVOLUTIONS)
+ << FACING_SHIFT;
+ }
+ // The group enters inner system exactly on the edge of a
+ // circle with radius = 9/16 * window-dim, which is
+ // different from how the flagship enters, but similar
+ // in the way that the group will never show up in any
+ // of the corners.
+ entryPt.x = (SIS_SCREEN_WIDTH >> 1) - COSINE (angle,
+ SIS_SCREEN_WIDTH * 9 / 16);
+ entryPt.y = (SIS_SCREEN_HEIGHT >> 1) - SINE (angle,
+ SIS_SCREEN_HEIGHT * 9 / 16);
+ GroupPtr->loc = displayToLocation (entryPt,
+ MAX_ZOOM_RADIUS);
+ group_loc = target_loc;
+ GroupPtr->sys_loc = target_loc;
+ }
+ }
+ }
+ }
+
+ radius = zoomRadiusForLocation (group_loc);
+ adjustDeltaVforZoom (radius, &vdx, &vdy);
+ GroupPtr->loc.x += vdx;
+ GroupPtr->loc.y += vdy;
+
+ dest_pt = locationToDisplay (GroupPtr->loc, radius);
+ EPtr->next.location.x = DISPLAY_TO_WORLD (dest_pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ EPtr->next.location.y = DISPLAY_TO_WORLD (dest_pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ // Don't draw the group if it's not at flagship location,
+ // or flash the group while it's reforming
+ if (group_loc != flagship_loc
+ || ((task & REFORM_GROUP)
+ && (GroupPtr->group_counter & 1)))
+ {
+ SetPrimType (&DisplayArray[EPtr->PrimIndex], NO_PRIM);
+ EPtr->state_flags |= NONSOLID;
+ }
+ else
+ {
+ SetPrimType (&DisplayArray[EPtr->PrimIndex], STAMP_PRIM);
+ if (task & REFORM_GROUP)
+ EPtr->state_flags |= NONSOLID;
+ }
+
+ EPtr->state_flags |= CHANGING;
+}
+
+static void
+flag_ship_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ return; // ignore the rest of the collisions
+
+ if (!(ElementPtr1->state_flags & COLLISION))
+ { // The other element's collision has not been processed yet
+ // Defer starting the encounter until it is.
+ ElementPtr0->state_flags |= COLLISION | NONSOLID;
+ }
+ else
+ { // Both element's collisions have now been processed
+ ElementPtr1->state_flags &= ~COLLISION;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+ip_group_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ IP_GROUP *GroupPtr;
+ void *OtherPtr;
+
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ return; // ignore the rest of the collisions
+
+ GetElementStarShip (ElementPtr0, &GroupPtr);
+ GetElementStarShip (ElementPtr1, &OtherPtr);
+ if (OtherPtr)
+ { // Collision with another group
+ // Prevent the groups from coalescing into a single ship icon
+ if ((ElementPtr0->state_flags & COLLISION)
+ || (ElementPtr1->current.location.x == ElementPtr1->next.location.x
+ && ElementPtr1->current.location.y == ElementPtr1->next.location.y))
+ {
+ ElementPtr0->state_flags &= ~COLLISION;
+ }
+ else
+ {
+ ElementPtr1->state_flags |= COLLISION;
+
+ GroupPtr->loc = DisplayArray[ElementPtr0->PrimIndex].Object.Point;
+ ElementPtr0->next.location = ElementPtr0->current.location;
+ InitIntersectEndPoint (ElementPtr0);
+ }
+ }
+ else // if (!OtherPtr)
+ { // Collision with a flagship
+ EncounterGroup = GroupPtr->group_id;
+
+ GroupPtr->task |= REFORM_GROUP;
+ GroupPtr->group_counter = 100;
+ // Send "all clear" for the time being. After the encounter, if
+ // the player battles the group, the "intercept" notify will be
+ // resent.
+ NotifyOthers (GroupPtr->race_id, IPNL_ALL_CLEAR);
+
+ if (!(ElementPtr1->state_flags & COLLISION))
+ { // The other element's collision has not been processed yet
+ // Defer starting the encounter until it is.
+ ElementPtr0->state_flags |= COLLISION | NONSOLID;
+ }
+ else
+ { // Both element's collisions have now been processed
+ ElementPtr1->state_flags &= ~COLLISION;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ }
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+spawn_ip_group (IP_GROUP *GroupPtr)
+{
+ HELEMENT hIPSHIPElement;
+
+ hIPSHIPElement = AllocElement ();
+ if (hIPSHIPElement)
+ {
+ ELEMENT *IPSHIPElementPtr;
+
+ LockElement (hIPSHIPElement, &IPSHIPElementPtr);
+ // Must have mass_points for collisions to work
+ IPSHIPElementPtr->mass_points = 1;
+ IPSHIPElementPtr->hit_points = 1;
+ IPSHIPElementPtr->state_flags =
+ CHANGING | FINITE_LIFE | IGNORE_VELOCITY;
+
+ SetPrimType (&DisplayArray[IPSHIPElementPtr->PrimIndex], STAMP_PRIM);
+ // XXX: Hack: farray points to FRAME[3] and given FRAME
+ IPSHIPElementPtr->current.image.farray = &GroupPtr->melee_icon;
+ IPSHIPElementPtr->current.image.frame = SetAbsFrameIndex (
+ GroupPtr->melee_icon, 1);
+ /* preprocessing has a side effect
+ * we wish to avoid. So death_func
+ * is used instead, but will achieve
+ * same result without the side
+ * effect (InitIntersectFrame)
+ */
+ IPSHIPElementPtr->death_func = ip_group_preprocess;
+ IPSHIPElementPtr->collision_func = ip_group_collision;
+
+ {
+ SIZE radius;
+ POINT pt;
+
+ radius = zoomRadiusForLocation (GroupPtr->sys_loc);
+ pt = locationToDisplay (GroupPtr->loc, radius);
+
+ IPSHIPElementPtr->current.location.x =
+ DISPLAY_TO_WORLD (pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ IPSHIPElementPtr->current.location.y =
+ DISPLAY_TO_WORLD (pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+ }
+
+ SetElementStarShip (IPSHIPElementPtr, GroupPtr);
+
+ SetUpElement (IPSHIPElementPtr);
+ IPSHIPElementPtr->IntersectControl.IntersectStamp.frame =
+ DecFrameIndex (stars_in_space);
+
+ UnlockElement (hIPSHIPElement);
+
+ PutElement (hIPSHIPElement);
+ }
+}
+
+#define FLIP_WAIT 42
+
+static void
+flag_ship_preprocess (ELEMENT *ElementPtr)
+{
+ if (--ElementPtr->thrust_wait == 0)
+ /* juggle list after flagship */
+ {
+ HELEMENT hSuccElement;
+
+ if ((hSuccElement = GetSuccElement (ElementPtr))
+ && hSuccElement != GetTailElement ())
+ {
+ HELEMENT hPredElement;
+ ELEMENT *TailPtr;
+
+ LockElement (GetTailElement (), &TailPtr);
+ hPredElement = _GetPredLink (TailPtr);
+ UnlockElement (GetTailElement ());
+
+ RemoveElement (hSuccElement);
+ PutElement (hSuccElement);
+ }
+
+ ElementPtr->thrust_wait = FLIP_WAIT;
+ }
+
+ {
+ BYTE flagship_loc, ec;
+ SIZE vdx, vdy, radius;
+ POINT pt;
+
+ GetCurrentVelocityComponents (&GLOBAL (velocity), &vdx, &vdy);
+
+ flagship_loc = getFlagshipLocation ();
+ radius = zoomRadiusForLocation (flagship_loc);
+ adjustDeltaVforZoom (radius, &vdx, &vdy);
+
+ pt = locationToDisplay (GLOBAL (ip_location), radius);
+ ElementPtr->current.location.x = DISPLAY_TO_WORLD (pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ ElementPtr->current.location.y = DISPLAY_TO_WORLD (pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+ InitIntersectStartPoint (ElementPtr);
+
+ GLOBAL (ip_location.x) += vdx;
+ GLOBAL (ip_location.y) += vdy;
+
+ pt = locationToDisplay (GLOBAL (ip_location), radius);
+ ElementPtr->next.location.x = DISPLAY_TO_WORLD (pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ ElementPtr->next.location.y = DISPLAY_TO_WORLD (pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ GLOBAL (ShipStamp.origin) = pt;
+ ElementPtr->next.image.frame = GLOBAL (ShipStamp.frame);
+
+ if (ElementPtr->sys_loc == flagship_loc)
+ {
+ if (ElementPtr->state_flags & NONSOLID)
+ ElementPtr->state_flags &= ~NONSOLID;
+ }
+ else /* no collisions during transition */
+ {
+ ElementPtr->state_flags |= NONSOLID;
+ ElementPtr->sys_loc = flagship_loc;
+ }
+
+ if ((ec = GET_GAME_STATE (ESCAPE_COUNTER))
+ && !(GLOBAL (CurrentActivity) & START_ENCOUNTER))
+ {
+ ElementPtr->state_flags |= NONSOLID;
+
+ --ec;
+ SET_GAME_STATE (ESCAPE_COUNTER, ec);
+ }
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static void
+spawn_flag_ship (void)
+{
+ HELEMENT hFlagShipElement;
+
+ hFlagShipElement = AllocElement ();
+ if (hFlagShipElement)
+ {
+ ELEMENT *FlagShipElementPtr;
+
+ LockElement (hFlagShipElement, &FlagShipElementPtr);
+ FlagShipElementPtr->hit_points = 1;
+ // Must have mass_points for collisions to work
+ FlagShipElementPtr->mass_points = 1;
+ FlagShipElementPtr->sys_loc = getFlagshipLocation ();
+ FlagShipElementPtr->state_flags = APPEARING | IGNORE_VELOCITY;
+ if (GET_GAME_STATE (ESCAPE_COUNTER))
+ FlagShipElementPtr->state_flags |= NONSOLID;
+ FlagShipElementPtr->life_span = NORMAL_LIFE;
+ FlagShipElementPtr->thrust_wait = FLIP_WAIT;
+ SetPrimType (&DisplayArray[FlagShipElementPtr->PrimIndex], STAMP_PRIM);
+ FlagShipElementPtr->current.image.farray =
+ &GLOBAL (ShipStamp.frame);
+ FlagShipElementPtr->current.image.frame =
+ GLOBAL (ShipStamp.frame);
+ FlagShipElementPtr->preprocess_func = flag_ship_preprocess;
+ FlagShipElementPtr->collision_func = flag_ship_collision;
+
+ FlagShipElementPtr->current.location.x =
+ DISPLAY_TO_WORLD (GLOBAL (ShipStamp.origin.x))
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ FlagShipElementPtr->current.location.y =
+ DISPLAY_TO_WORLD (GLOBAL (ShipStamp.origin.y))
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ UnlockElement (hFlagShipElement);
+
+ PutElement (hFlagShipElement);
+ }
+}
+
+void
+DoMissions (void)
+{
+ HSHIPFRAG hGroup, hNextGroup;
+
+ spawn_flag_ship ();
+
+ if (EncounterRace >= 0)
+ { // There was a battle. Call in reinforcements.
+ NotifyOthers (EncounterRace, IPNL_INTERCEPT_PLAYER);
+ EncounterRace = -1;
+ }
+
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ hGroup; hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ if (GroupPtr->in_system)
+ spawn_ip_group (GroupPtr);
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+}
+