//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 "solarsys.h"
#include "lander.h"
#include "../colors.h"
#include "../controls.h"
#include "../menustat.h"
		// for DrawMenuStateStrings()
#include "../starmap.h"
#include "../races.h"
#include "../gamestr.h"
#include "../gendef.h"
#include "../globdata.h"
#include "../sis.h"
#include "../init.h"
#include "../shipcont.h"
#include "../gameopt.h"
#include "../nameref.h"
#include "../resinst.h"
#include "../settings.h"
#include "../ipdisp.h"
#include "../grpinfo.h"
#include "../process.h"
#include "../setup.h"
#include "../sounds.h"
#include "../state.h"
#include "../uqmdebug.h"
#include "../save.h"
#include "options.h"
#include "libs/graphics/gfx_common.h"
#include "libs/mathlib.h"
#include "libs/log.h"
#include "libs/misc.h"





////////////////
// TEST
////////////////
#include<time.h> 
#include <math.h>
#include "../build.h"	// for StarSphereTracking
#include "../gameev.h"	// for SetRaceDest



//#define DEBUG_SOLARSYS
//#define SMOOTH_SYSTEM_ZOOM  1

#define IP_FRAME_RATE  (ONE_SECOND / 30)

static BOOLEAN DoIpFlight (SOLARSYS_STATE *pSS);
static void DrawSystem (SIZE radius, BOOLEAN IsInnerSystem);
static FRAME CreateStarBackGround (void);
static void DrawInnerSystem (void);
static void DrawOuterSystem (void);
static void ValidateOrbits (void);

// SolarSysMenu() items
enum SolarSysMenuMenuItems
{
	// XXX: Must match the enum in menustat.h
	STARMAP = 1,
	EQUIP_DEVICE,
	CARGO,
	ROSTER,
	GAME_MENU,
	NAVIGATION,
};


SOLARSYS_STATE *pSolarSysState;
FRAME SISIPFrame;
FRAME SunFrame;
FRAME OrbitalFrame;
FRAME SpaceJunkFrame;
COLORMAP OrbitalCMap;
COLORMAP SunCMap;
MUSIC_REF SpaceMusic;

SIZE EncounterRace;
BYTE EncounterGroup;
		// last encountered group info

static FRAME StarsFrame;
		// prepared star-field graphic
static FRAME SolarSysFrame;
		// saved solar system view graphic

static RECT scaleRect;
		// system zooms in when the flagship enters this rect

RandomContext *SysGenRNG;

#define DISPLAY_TO_LOC  (DISPLAY_FACTOR >> 1)

POINT
locationToDisplay (POINT pt, SIZE scaleRadius)
{
	POINT out;

	out.x = (SIS_SCREEN_WIDTH >> 1)
			+ (long)pt.x * DISPLAY_TO_LOC / scaleRadius;
	out.y = (SIS_SCREEN_HEIGHT >> 1)
			+ (long)pt.y * DISPLAY_TO_LOC / scaleRadius;

	return out;
}

POINT
displayToLocation (POINT pt, SIZE scaleRadius)
{
	POINT out;

	out.x = ((long)pt.x - (SIS_SCREEN_WIDTH >> 1))
			* scaleRadius / DISPLAY_TO_LOC;
	out.y = ((long)pt.y - (SIS_SCREEN_HEIGHT >> 1))
			* scaleRadius / DISPLAY_TO_LOC;

	return out;
}

POINT
planetOuterLocation (COUNT planetI)
{
	SIZE scaleRadius = pSolarSysState->SunDesc[0].radius;
	return displayToLocation (pSolarSysState->PlanetDesc[planetI].image.origin,
			scaleRadius);
}

bool
worldIsPlanet (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
{
	return world->pPrevDesc == solarSys->SunDesc;
}

bool
worldIsMoon (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
{
	return world->pPrevDesc != solarSys->SunDesc;
}

// Returns the planet index of the world. If the world is a moon, then
// this is the index of the planet it is orbiting.
COUNT
planetIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
{
	const PLANET_DESC *planet = worldIsPlanet (solarSys, world) ?
			world : world->pPrevDesc;
	return planet - solarSys->PlanetDesc;
}

COUNT
moonIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *moon)
{
	assert (!worldIsPlanet (solarSys, moon));
	return moon - solarSys->MoonDesc;
}

// Test whether 'world' is the planetI-th planet, and if moonI is not
// set to MATCH_PLANET, also whether 'world' is the moonI-th moon.
bool
matchWorld (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world,
		BYTE planetI, BYTE moonI)
{
	// Check whether we have the right planet.
	if (planetIndex (solarSys, world) != planetI)
		return false;

	if (moonI == MATCH_PLANET)
	{
		// Only test whether we are at the planet.
		if (!worldIsPlanet (solarSys, world))
			return false;
	}
	else
	{
		// Test whether the moon matches too
		if (!worldIsMoon (solarSys, world))
			return false;

		if (moonIndex (solarSys, world) != moonI)
			return false;
	}

	return true;
}

bool
playerInSolarSystem (void)
{
	return pSolarSysState != NULL;
}

bool
playerInPlanetOrbit (void)
{
	return playerInSolarSystem () && pSolarSysState->InOrbit;
}

bool
playerInInnerSystem (void)
{
	assert (playerInSolarSystem ());
	assert (pSolarSysState->pBaseDesc == pSolarSysState->PlanetDesc
			|| pSolarSysState->pBaseDesc == pSolarSysState->MoonDesc);
	return pSolarSysState->pBaseDesc != pSolarSysState->PlanetDesc;
}

// Sets the SysGenRNG to the required state cofirst.
static void
GenerateMoons (SOLARSYS_STATE *system, PLANET_DESC *planet)
{
	COUNT i;
	//COUNT facing;
	PLANET_DESC *pMoonDesc;

	RandomContext_SeedRandom (SysGenRNG, planet->rand_seed);

	(*system->genFuncs->generateName) (system, planet);
	(*system->genFuncs->generateMoons) (system, planet);

	//facing = NORMALIZE_FACING (ANGLE_TO_FACING (ARCTAN (planet->location.x, planet->location.y)));
	
	for (i = 0, pMoonDesc = &system->MoonDesc[0];
			i < MAX_MOONS; ++i, ++pMoonDesc)
	{
		pMoonDesc->pPrevDesc = planet;
		if (i >= planet->NumPlanets)
			continue;
		
		pMoonDesc->temp_color = planet->temp_color;
	}
}

void
FreeIPData (void)
{
	DestroyDrawable (ReleaseDrawable (SISIPFrame));
	SISIPFrame = 0;
	DestroyDrawable (ReleaseDrawable (SunFrame));
	SunFrame = 0;
	DestroyColorMap (ReleaseColorMap (SunCMap));
	SunCMap = 0;
	DestroyColorMap (ReleaseColorMap (OrbitalCMap));
	OrbitalCMap = 0;
	DestroyDrawable (ReleaseDrawable (OrbitalFrame));
	OrbitalFrame = 0;
	DestroyDrawable (ReleaseDrawable (SpaceJunkFrame));
	SpaceJunkFrame = 0;
	DestroyMusic (SpaceMusic);
	SpaceMusic = 0;

	RandomContext_Delete (SysGenRNG);
	SysGenRNG = NULL;
}

void
LoadIPData (void)
{
	if (SpaceJunkFrame == 0)
	{
		SpaceJunkFrame = CaptureDrawable (
				LoadGraphic (IPBKGND_MASK_PMAP_ANIM));
		SISIPFrame = CaptureDrawable (LoadGraphic (SISIP_MASK_PMAP_ANIM));

		OrbitalCMap = CaptureColorMap (LoadColorMap (ORBPLAN_COLOR_MAP));
		OrbitalFrame = CaptureDrawable (
				LoadGraphic (ORBPLAN_MASK_PMAP_ANIM));
		SunCMap = CaptureColorMap (LoadColorMap (IPSUN_COLOR_MAP));
		SunFrame = CaptureDrawable (LoadGraphic (SUN_MASK_PMAP_ANIM));

		SpaceMusic = LoadMusic (IP_MUSIC);
	}

	if (!SysGenRNG)
	{
		SysGenRNG = RandomContext_New ();
	}
}
	

static void
sortPlanetPositions (void)
{
	COUNT i;
	SIZE sort_array[MAX_PLANETS + 1];

	// When this part is done, sort_array will contain the indices to
	// all planets, sorted on their y position.
	// The sun itself, which has its data located at
	// pSolarSysState->PlanetDesc[-1], is included in this array.
	// Very ugly stuff, but it's correct.

	// Initialise sort_array.
	for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i)
		sort_array[i] = i - 1;

	// Sort sort_array, based on the positions of the planets/sun.
	for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i)
	{
		COUNT j;

		for (j = pSolarSysState->SunDesc[0].NumPlanets; j > i; --j)
		{
			SIZE real_i, real_j;

			real_i = sort_array[i];
			real_j = sort_array[j];
			if (pSolarSysState->PlanetDesc[real_i].image.origin.y >
					pSolarSysState->PlanetDesc[real_j].image.origin.y)
			{
				SIZE temp;

				temp = sort_array[i];
				sort_array[i] = sort_array[j];
				sort_array[j] = temp;
			}
		}
	}

	// Put the results of the sorting in the solar system structure.
	pSolarSysState->FirstPlanetIndex = sort_array[0];
	pSolarSysState->LastPlanetIndex =
			sort_array[pSolarSysState->SunDesc[0].NumPlanets];
	for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i) {
		PLANET_DESC *planet = &pSolarSysState->PlanetDesc[sort_array[i]];
		planet->NextIndex = sort_array[i + 1];
	}
}

static void
initSolarSysSISCharacteristics (void)
{
	BYTE i;
	BYTE num_thrusters;

	num_thrusters = 0;
	for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
	{
		if (GLOBAL_SIS (DriveSlots[i]) == FUSION_THRUSTER)
			++num_thrusters;
	}
	pSolarSysState->max_ship_speed = (BYTE)(
			(num_thrusters + 5) * IP_SHIP_THRUST_INCREMENT);

	pSolarSysState->turn_wait = IP_SHIP_TURN_WAIT;
	for (i = 0; i < NUM_JET_SLOTS; ++i)
	{
		if (GLOBAL_SIS (JetSlots[i]) == TURNING_JETS)
			pSolarSysState->turn_wait -= IP_SHIP_TURN_DECREMENT;
	}
}








/*

DWORD
GetRandomSeedForStar (const STAR_DESC *star)
{
	
	
	//TESTING:
	// init / load the random seed
	if(!GET_GAME_STATE(WORLDGEN_SEED))
		SET_GAME_STATE (WORLDGEN_SEED,TFB_Random());
	
	BYTE randomNum = GET_GAME_STATE (WORLDGEN_SEED);
	
	return MAKE_DWORD (star->star_pt.x*randomNum, star->star_pt.y*randomNum);
}
*/
// Returns an orbital PLANET_DESC when player is in orbit
static PLANET_DESC *
LoadSolarSys (void)
{
	COUNT i;
	PLANET_DESC *orbital = NULL;
	PLANET_DESC *pCurDesc;
#define NUM_TEMP_RANGES 5
	static const Color temp_color_array[NUM_TEMP_RANGES] =
	{
		BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x0E), 0x54),
		BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x06, 0x08), 0x62),
		BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x0B, 0x00), 0x6D),
		BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D),
		BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x08, 0x00), 0x75),
	};

	RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (CurStarDescPtr));

	SunFrame = SetAbsFrameIndex (SunFrame, STAR_TYPE (CurStarDescPtr->Type));

	pCurDesc = &pSolarSysState->SunDesc[0];
	pCurDesc->pPrevDesc = 0;
	pCurDesc->rand_seed = RandomContext_Random (SysGenRNG);

	pCurDesc->data_index = STAR_TYPE (CurStarDescPtr->Type);
	pCurDesc->location.x = 0;
	pCurDesc->location.y = 0;
	pCurDesc->image.origin = pCurDesc->location;
	pCurDesc->image.frame = SunFrame;

	(*pSolarSysState->genFuncs->generatePlanets) (pSolarSysState);
	if (GET_GAME_STATE (PLANETARY_CHANGE))
	{
		PutPlanetInfo ();
		SET_GAME_STATE (PLANETARY_CHANGE, 0);
	}

	for (i = 0, pCurDesc = pSolarSysState->PlanetDesc;
			i < MAX_PLANETS; ++i, ++pCurDesc)
	{
		pCurDesc->pPrevDesc = &pSolarSysState->SunDesc[0];
		pCurDesc->image.origin = pCurDesc->location;
		if (i >= pSolarSysState->SunDesc[0].NumPlanets)
		{
			pCurDesc->image.frame = 0;
		}
		else
		{
			COUNT index;
			SYSTEM_INFO SysInfo;

			DoPlanetaryAnalysis (&SysInfo, pCurDesc);
			index = (SysInfo.PlanetInfo.SurfaceTemperature + 250) / 100;
			if (index >= NUM_TEMP_RANGES)
				index = NUM_TEMP_RANGES - 1;
			pCurDesc->temp_color = temp_color_array[index];
		}
	}

	sortPlanetPositions ();

	if (!GLOBAL (ip_planet))
	{	// Outer system
		pSolarSysState->pBaseDesc = pSolarSysState->PlanetDesc;
		pSolarSysState->pOrbitalDesc = NULL;
	}
	else
	{	// Inner system
		pSolarSysState->SunDesc[0].location = GLOBAL (ip_location);
		GLOBAL (ip_location) = displayToLocation (
				GLOBAL (ShipStamp.origin), MAX_ZOOM_RADIUS);

		i = GLOBAL (ip_planet) - 1;
		pSolarSysState->pOrbitalDesc = &pSolarSysState->PlanetDesc[i];
		GenerateMoons (pSolarSysState, pSolarSysState->pOrbitalDesc);
		pSolarSysState->pBaseDesc = pSolarSysState->MoonDesc;

		SET_GAME_STATE (PLANETARY_LANDING, 0);
	}

	initSolarSysSISCharacteristics ();

	if (GLOBAL (in_orbit))
	{	// Only when loading a game into orbital
		i = GLOBAL (in_orbit) - 1;
		if (i == 0)
		{	// Orbiting the planet itself
			orbital = pSolarSysState->pBaseDesc->pPrevDesc;
		}
		else
		{	// Orbiting a moon
			// -1 because planet itself is 1, and moons have to be 1-based
			i -= 1;
			orbital = &pSolarSysState->MoonDesc[i];
		}
		GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
		GLOBAL (in_orbit) = 0;
	}
	else
	{
		i = GLOBAL (ShipFacing);
		// XXX: Solar system reentry test depends on ShipFacing != 0
		if (i == 0)
			++i;

		GLOBAL (ShipStamp.frame) = SetAbsFrameIndex (SISIPFrame, i - 1);
	}

	return orbital;
}


saveNonOrbitalLocation (void)
{
	// XXX: Solar system reentry test depends on ShipFacing != 0
	GLOBAL (ShipFacing) = GetFrameIndex (GLOBAL (ShipStamp.frame)) + 1;
	GLOBAL (in_orbit) = 0;
	if (!playerInInnerSystem ())
	{
		GLOBAL (ip_planet) = 0;
	}
	else
	{
		// ip_planet is 1-based because code tests for ip_planet!=0
		GLOBAL (ip_planet) = 1 + planetIndex (pSolarSysState,
				pSolarSysState->pOrbitalDesc);
		GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
	}
}

static void
FreeSolarSys (void)
{
	if (pSolarSysState->InIpFlight)
	{
		pSolarSysState->InIpFlight = FALSE;
		
		if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
			saveNonOrbitalLocation ();
	}

	DestroyDrawable (ReleaseDrawable (SolarSysFrame));
	SolarSysFrame = NULL;

	StopMusic ();

//    FreeIPData ();
}

static FRAME
getCollisionFrame (PLANET_DESC *planet, COUNT WaitPlanet)
{
	if (pSolarSysState->WaitIntersect != (COUNT)~0
			&& pSolarSysState->WaitIntersect != WaitPlanet)
	{	// New collisions are with a single point (center of planet)
		return DecFrameIndex (stars_in_space);
	}
	else
	{	// Existing collisions are cleared only once the ship does not
		// intersect anymore with a full planet image
		return planet->image.frame;
	}
}

// Returns the planet with which the flagship is colliding
static PLANET_DESC *
CheckIntersect (void)
{
	COUNT i;
	PLANET_DESC *pCurDesc;
	INTERSECT_CONTROL ShipIntersect, PlanetIntersect;
	COUNT NewWaitPlanet;
	BYTE PlanetOffset, MoonOffset;

	// Check collisions with the system center object
	// This may be the planet in inner view, or the sun
	pCurDesc = pSolarSysState->pBaseDesc->pPrevDesc;
	PlanetOffset = pCurDesc - pSolarSysState->PlanetDesc + 1;
	MoonOffset = 1; // the planet itself

	ShipIntersect.IntersectStamp.origin = GLOBAL (ShipStamp.origin);
	ShipIntersect.EndPoint = ShipIntersect.IntersectStamp.origin;
	ShipIntersect.IntersectStamp.frame = GLOBAL (ShipStamp.frame);

	PlanetIntersect.IntersectStamp.origin.x = SIS_SCREEN_WIDTH >> 1;
	PlanetIntersect.IntersectStamp.origin.y = SIS_SCREEN_HEIGHT >> 1;
	PlanetIntersect.EndPoint = PlanetIntersect.IntersectStamp.origin;

	PlanetIntersect.IntersectStamp.frame = getCollisionFrame (pCurDesc,
			MAKE_WORD (PlanetOffset, MoonOffset));

	// Start with no collisions
	NewWaitPlanet = 0;

	if (pCurDesc != pSolarSysState->SunDesc /* can't intersect with sun */
			&& DrawablesIntersect (&ShipIntersect,
			&PlanetIntersect, MAX_TIME_VALUE))
	{
#ifdef DEBUG_SOLARSYS
		log_add (log_Debug, "0: Planet %d, Moon %d", PlanetOffset,
				MoonOffset);
#endif /* DEBUG_SOLARSYS */
		NewWaitPlanet = MAKE_WORD (PlanetOffset, MoonOffset);
		if (pSolarSysState->WaitIntersect != (COUNT)~0
				&& pSolarSysState->WaitIntersect != NewWaitPlanet)
		{
			pSolarSysState->WaitIntersect = NewWaitPlanet;
#ifdef DEBUG_SOLARSYS
			log_add (log_Debug, "Star index = %d, Planet index = %d, <%d, %d>",
					CurStarDescPtr - star_array,
					pCurDesc - pSolarSysState->PlanetDesc,
					pSolarSysState->SunDesc[0].location.x,
					pSolarSysState->SunDesc[0].location.y);
#endif /* DEBUG_SOLARSYS */
			return pCurDesc;
		}
	}

	for (i = pCurDesc->NumPlanets,
			pCurDesc = pSolarSysState->pBaseDesc; i; --i, ++pCurDesc)
	{
		PlanetIntersect.IntersectStamp.origin = pCurDesc->image.origin;
		PlanetIntersect.EndPoint = PlanetIntersect.IntersectStamp.origin;
		if (playerInInnerSystem ())
		{
			PlanetOffset = pCurDesc->pPrevDesc -
					pSolarSysState->PlanetDesc;
			MoonOffset = pCurDesc - pSolarSysState->MoonDesc + 2;
		}
		else
		{
			PlanetOffset = pCurDesc - pSolarSysState->PlanetDesc;
			MoonOffset = 0;
		}
		++PlanetOffset;
		PlanetIntersect.IntersectStamp.frame = getCollisionFrame (pCurDesc,
				MAKE_WORD (PlanetOffset, MoonOffset));

		if (DrawablesIntersect (&ShipIntersect,
				&PlanetIntersect, MAX_TIME_VALUE))
		{
#ifdef DEBUG_SOLARSYS
			log_add (log_Debug, "1: Planet %d, Moon %d", PlanetOffset,
					MoonOffset);
#endif /* DEBUG_SOLARSYS */
			NewWaitPlanet = MAKE_WORD (PlanetOffset, MoonOffset);
			
			if (pSolarSysState->WaitIntersect == (COUNT)~0)
			{	// All collisions disallowed, but the ship is still colliding
				// with something. Collisions will remain disabled.
				break;
			}
			else if (pSolarSysState->WaitIntersect == NewWaitPlanet)
			{	// Existing and continued collision -- ignore
				continue;
			}
			
			// Collision with a new planet/moon. This may cause a transition
			// to an inner system or start an orbital view.
			pSolarSysState->WaitIntersect = NewWaitPlanet;
			return pCurDesc;
		}
	}

	// This records the planet/moon with which the ship just collided
	// It may be a previously existing collision also (the value won't change)
	// If all collisions were disabled, this will reenable then once the ship
	// stops colliding with any planets
	if (pSolarSysState->WaitIntersect != (COUNT)~0 || NewWaitPlanet == 0)
		pSolarSysState->WaitIntersect = NewWaitPlanet;

	return NULL;
}

static void
GetOrbitRect (RECT *pRect, COORD dx, COORD dy, SIZE radius,
		int xnumer, int ynumer, int denom)
{
	pRect->corner.x = (SIS_SCREEN_WIDTH >> 1) + (long)-dx * xnumer / denom;
	pRect->corner.y = (SIS_SCREEN_HEIGHT >> 1) + (long)-dy * ynumer / denom;
	pRect->extent.width = (long)radius * (xnumer << 1) / denom;
	pRect->extent.height = pRect->extent.width >> 1;
}

static void
GetPlanetOrbitRect (RECT *r, PLANET_DESC *planet, int sizeNumer,
		int dyNumer, int denom)
{
	COORD dx, dy;

	dx = planet->radius;
	dy = planet->radius;
	if (sizeNumer > DISPLAY_FACTOR)
	{
		dx = dx + planet->location.x;
		dy = (dy + planet->location.y) << 1;
	}
	GetOrbitRect (r, dx, dy, planet->radius, sizeNumer, dyNumer, denom);
}

static void
ValidateOrbit (PLANET_DESC *planet, int sizeNumer, int dyNumer, int denom)
{
	COUNT index;

	if (sizeNumer <= DISPLAY_FACTOR)
	{	// All planets in outer view, and moons in inner
		RECT r;

		GetPlanetOrbitRect (&r, planet, sizeNumer, dyNumer, denom);

		// Calculate the location of the planet's image
		r.corner.x += (r.extent.width >> 1);
		r.corner.y += (r.extent.height >> 1);
		r.corner.x += (long)planet->location.x * sizeNumer / denom;
		// Ellipse function always has coefficients a^2 = 2 * b^2
		r.corner.y += (long)planet->location.y * (sizeNumer / 2) / denom;

		planet->image.origin = r.corner;
	}

	// Calculate the size and lighting angle of planet's image and
	// set the image that will be drawn
	index = planet->data_index & ~WORLD_TYPE_SPECIAL;
	if (index < NUMBER_OF_PLANET_TYPES)
	{	// The world is a normal planetary body (planet or moon)
		BYTE Type;
		COUNT Size;
		COUNT angle;

		Type = PlanData[index].Type;
		Size = PLANSIZE (Type);
		if (sizeNumer > DISPLAY_FACTOR)
		{
			Size += 3;
		}
		else if (worldIsMoon (pSolarSysState, planet))
		{
			Size += 2;
		}
		else if (denom <= (MAX_ZOOM_RADIUS >> 2))
		{
			++Size;
			if (denom == MIN_ZOOM_RADIUS)
				++Size;
		}
		
		if (worldIsPlanet (pSolarSysState, planet))
		{	// Planet
			angle = ARCTAN (planet->location.x, planet->location.y);
		}
		else
		{	// Moon
			angle = ARCTAN (planet->pPrevDesc->location.x,
					planet->pPrevDesc->location.y);
		}
		planet->image.frame =	SetAbsFrameIndex (OrbitalFrame,
				(Size << FACING_SHIFT) + NORMALIZE_FACING (
				ANGLE_TO_FACING (angle)));
	}
	else if (planet->data_index == HIERARCHY_STARBASE)
	{
		planet->image.frame = SetAbsFrameIndex (SpaceJunkFrame, 16);
	}
	else if (planet->data_index == SA_MATRA)
	{
		planet->image.frame = SetAbsFrameIndex (SpaceJunkFrame, 19);
	}
}

static void
DrawOrbit (PLANET_DESC *planet, int sizeNumer, int dyNumer, int denom)
{
	RECT r;

	GetPlanetOrbitRect (&r, planet, sizeNumer, dyNumer, denom);

	SetContextForeGroundColor (planet->temp_color);
	DrawOval (&r, 1);
}

static SIZE
FindRadius (POINT shipLoc, SIZE fromRadius)
{
	SIZE nextRadius;
	POINT displayLoc;

	do
	{
		fromRadius >>= 1;
		if (fromRadius > MIN_ZOOM_RADIUS)
			nextRadius = fromRadius >> 1;
		else
			nextRadius = 0; // scaleRect will be nul

		GetOrbitRect (&scaleRect, nextRadius, nextRadius, nextRadius,
				DISPLAY_FACTOR, DISPLAY_FACTOR >> 2, fromRadius);
		displayLoc = locationToDisplay (shipLoc, fromRadius);
	
	} while (pointWithinRect (scaleRect, displayLoc));

	return fromRadius;
}

static UWORD
flagship_inertial_thrust (COUNT CurrentAngle)
{
	BYTE max_speed;
	SIZE cur_delta_x, cur_delta_y;
	COUNT TravelAngle;
	VELOCITY_DESC *VelocityPtr;

	max_speed = pSolarSysState->max_ship_speed;
	VelocityPtr = &GLOBAL (velocity);
	GetCurrentVelocityComponents (VelocityPtr, &cur_delta_x, &cur_delta_y);
	TravelAngle = GetVelocityTravelAngle (VelocityPtr);
	if (TravelAngle == CurrentAngle
			&& cur_delta_x == COSINE (CurrentAngle, max_speed)
			&& cur_delta_y == SINE (CurrentAngle, max_speed))
		return (SHIP_AT_MAX_SPEED);
	else
	{
		SIZE delta_x, delta_y;
		DWORD desired_speed;

		delta_x = cur_delta_x
				+ COSINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT);
		delta_y = cur_delta_y
				+ SINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT);
		desired_speed = (DWORD) ((long) delta_x * delta_x)
				+ (DWORD) ((long) delta_y * delta_y);
		if (desired_speed <= (DWORD) ((UWORD) max_speed * max_speed))
			SetVelocityComponents (VelocityPtr, delta_x, delta_y);
		else if (TravelAngle == CurrentAngle)
		{
			SetVelocityComponents (VelocityPtr,
					COSINE (CurrentAngle, max_speed),
					SINE (CurrentAngle, max_speed));
			return (SHIP_AT_MAX_SPEED);
		}
		else
		{
			VELOCITY_DESC v;

			v = *VelocityPtr;

			DeltaVelocityComponents (&v,
					COSINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT >> 1)
					- COSINE (TravelAngle, IP_SHIP_THRUST_INCREMENT),
					SINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT >> 1)
					- SINE (TravelAngle, IP_SHIP_THRUST_INCREMENT));
			GetCurrentVelocityComponents (&v, &cur_delta_x, &cur_delta_y);
			desired_speed =
					(DWORD) ((long) cur_delta_x * cur_delta_x)
					+ (DWORD) ((long) cur_delta_y * cur_delta_y);
			if (desired_speed > (DWORD) ((UWORD) max_speed * max_speed))
			{
				SetVelocityComponents (VelocityPtr,
						COSINE (CurrentAngle, max_speed),
						SINE (CurrentAngle, max_speed));
				return (SHIP_AT_MAX_SPEED);
			}

			*VelocityPtr = v;
		}

		return 0;
	}
}

static void
ProcessShipControls (void)
{
	COUNT index;
	SIZE delta_x, delta_y;

	if (CurrentInputState.key[PlayerControls[0]][KEY_UP])
		delta_y = -1;
	else
		delta_y = 0;

	delta_x = 0;
	if (CurrentInputState.key[PlayerControls[0]][KEY_LEFT])
		delta_x -= 1;
	if (CurrentInputState.key[PlayerControls[0]][KEY_RIGHT])
		delta_x += 1;
		
	if (delta_x || delta_y < 0)
	{
		GLOBAL (autopilot.x) = ~0;
		GLOBAL (autopilot.y) = ~0;
	}
	else if (GLOBAL (autopilot.x) != ~0 && GLOBAL (autopilot.y) != ~0)
		delta_y = -1;
	else
		delta_y = 0;

	index = GetFrameIndex (GLOBAL (ShipStamp.frame));
	if (pSolarSysState->turn_counter)
		--pSolarSysState->turn_counter;
	else if (delta_x)
	{
		if (delta_x < 0)
			index = NORMALIZE_FACING (index - 1);
		else
			index = NORMALIZE_FACING (index + 1);

		GLOBAL (ShipStamp.frame) =
				SetAbsFrameIndex (GLOBAL (ShipStamp.frame), index);

		pSolarSysState->turn_counter = pSolarSysState->turn_wait;
	}
	if (pSolarSysState->thrust_counter)
		--pSolarSysState->thrust_counter;
	else if (delta_y < 0)
	{
#define THRUST_WAIT 1
		flagship_inertial_thrust (FACING_TO_ANGLE (index));

		pSolarSysState->thrust_counter = THRUST_WAIT;
	}
}

static void
enterInnerSystem (PLANET_DESC *planet)
{
#define INNER_ENTRY_DISTANCE  (MIN_MOON_RADIUS + ((MAX_MOONS - 1) \
		* MOON_DELTA) + (MOON_DELTA / 4))
	COUNT angle;

	// Calculate the inner system entry location and facing
	angle = FACING_TO_ANGLE (GetFrameIndex (GLOBAL (ShipStamp.frame)))
			+ HALF_CIRCLE;
	GLOBAL (ShipStamp.origin.x) = (SIS_SCREEN_WIDTH >> 1)
			+ COSINE (angle, INNER_ENTRY_DISTANCE);
	GLOBAL (ShipStamp.origin.y) = (SIS_SCREEN_HEIGHT >> 1)
			+ SINE (angle, INNER_ENTRY_DISTANCE);
	if (GLOBAL (ShipStamp.origin.y) < 0)
		GLOBAL (ShipStamp.origin.y) = 1;
	else if (GLOBAL (ShipStamp.origin.y) >= SIS_SCREEN_HEIGHT)
		GLOBAL (ShipStamp.origin.y) =
				(SIS_SCREEN_HEIGHT - 1) - 1;

	GLOBAL (ip_location) = displayToLocation (
			GLOBAL (ShipStamp.origin), MAX_ZOOM_RADIUS);
	
	pSolarSysState->SunDesc[0].location =
			planetOuterLocation (planetIndex (pSolarSysState, planet));
	ZeroVelocityComponents (&GLOBAL (velocity));

	GenerateMoons (pSolarSysState, planet);
	pSolarSysState->pBaseDesc = pSolarSysState->MoonDesc;
	pSolarSysState->pOrbitalDesc = planet;
}

static void
leaveInnerSystem (PLANET_DESC *planet)
{
	COUNT outerPlanetWait;

	pSolarSysState->pBaseDesc = pSolarSysState->PlanetDesc;
	pSolarSysState->pOrbitalDesc = NULL;

	outerPlanetWait = MAKE_WORD (planet - pSolarSysState->PlanetDesc + 1, 0);
	GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
	XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
	ZeroVelocityComponents (&GLOBAL (velocity));

	// Now the ship is in outer system (as per game logic)

	pSolarSysState->WaitIntersect = outerPlanetWait;
	// See if we also intersect with another planet, and if we do,
	// disable collisions comletely until we stop intersecting
	// with any planet at all.
	CheckIntersect ();
	if (pSolarSysState->WaitIntersect != outerPlanetWait)
		pSolarSysState->WaitIntersect = (COUNT)~0;
}

static void
enterOrbital (PLANET_DESC *planet)
{
	ZeroVelocityComponents (&GLOBAL (velocity));
	pSolarSysState->pOrbitalDesc = planet;
	pSolarSysState->InOrbit = TRUE;
}

static BOOLEAN
CheckShipLocation (SIZE *newRadius)
{
	SIZE radius;

	radius = pSolarSysState->SunDesc[0].radius;
	*newRadius = pSolarSysState->SunDesc[0].radius;
	
	if (GLOBAL (ShipStamp.origin.x) < 0
			|| GLOBAL (ShipStamp.origin.x) >= SIS_SCREEN_WIDTH
			|| GLOBAL (ShipStamp.origin.y) < 0
			|| GLOBAL (ShipStamp.origin.y) >= SIS_SCREEN_HEIGHT)
	{
		// The ship leaves the screen.
		if (!playerInInnerSystem ())
		{	// Outer zoom-out transition
			if (radius == MAX_ZOOM_RADIUS)
			{
				// The ship leaves IP.
				GLOBAL (CurrentActivity) |= END_INTERPLANETARY;
				return FALSE; // no location change
			}

			*newRadius = FindRadius (GLOBAL (ip_location),
					MAX_ZOOM_RADIUS << 1);
		}
		else
		{
			leaveInnerSystem (pSolarSysState->pOrbitalDesc);
		}
		
		return TRUE;
	}

	if (!playerInInnerSystem ()
			&& pointWithinRect (scaleRect, GLOBAL (ShipStamp.origin)))
	{	// Outer zoom-in transition
		*newRadius = FindRadius (GLOBAL (ip_location), radius);
		return TRUE;
	}

	if (GLOBAL (autopilot.x) == ~0 && GLOBAL (autopilot.y) == ~0)
	{	// Not on autopilot -- may collide with a planet
		PLANET_DESC *planet = CheckIntersect ();
		if (planet)
		{	// Collision with a planet
			if (playerInInnerSystem ())
			{	// Entering planet orbit (scans, etc.)
				enterOrbital (planet);
				return FALSE; // no location change
			}
			else
			{	// Transition to inner system
				enterInnerSystem (planet);
				return TRUE;
			}
		}
	}

	return FALSE; // no location change
}

static void
DrawSystemTransition (BOOLEAN inner)
{
	SetTransitionSource (NULL);
	BatchGraphics ();
	if (inner)
		DrawInnerSystem ();
	else
		DrawOuterSystem ();
	RedrawQueue (FALSE);
	ScreenTransition (3, NULL);
	UnbatchGraphics ();
}

static void
TransitionSystemIn (void)
{
	SetContext (SpaceContext);
	DrawSystemTransition (playerInInnerSystem ());
}

static void
ScaleSystem (SIZE new_radius)
{
#ifdef SMOOTH_SYSTEM_ZOOM
	// XXX: This appears to have been an attempt to zoom the system view
	//   in a different way. This code zooms gradually instead of
	//   doing a crossfade from one zoom level to the other.
	// TODO: Do not loop here, and instead increment the zoom level
	//   in IP_frame() with a function drawing the new zoom. The ship
	//   controls are not handled in the loop, and the flagship
	//   can collide with a group while zooming, and that is not handled
	//   100% correctly.
#define NUM_STEPS 10
	COUNT i;
	SIZE old_radius;
	SIZE d, step;

	old_radius = pSolarSysState->SunDesc[0].radius;

	assert (old_radius != 0);
	assert (old_radius != new_radius);

	d = new_radius - old_radius;
	step = d / NUM_STEPS;

	for (i = 0; i < NUM_STEPS - 1; ++i)
	{
		pSolarSysState->SunDesc[0].radius += step;
		XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);

		BatchGraphics ();
		DrawOuterSystem ();
		RedrawQueue (FALSE);
		UnbatchGraphics ();

		SleepThread (ONE_SECOND / 30);
	}
	
	// Final zoom step
	pSolarSysState->SunDesc[0].radius = new_radius;
	XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
	
	BatchGraphics ();
	DrawOuterSystem ();
	RedrawQueue (FALSE);
	UnbatchGraphics ();
	
#else // !SMOOTH_SYSTEM_ZOOM
	RECT r;

	pSolarSysState->SunDesc[0].radius = new_radius;
	XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);

	GetContextClipRect (&r);
	SetTransitionSource (&r);
	BatchGraphics ();
	DrawOuterSystem ();
	RedrawQueue (FALSE);
	ScreenTransition (3, &r);
	UnbatchGraphics ();
#endif // SMOOTH_SYSTEM_ZOOM
}

static void
RestoreSystemView (void)
{
	STAMP s;

	s.origin.x = 0;
	s.origin.y = 0;
	s.frame = SolarSysFrame;
	DrawStamp (&s);
}

// Normally called by DoIpFlight() to process a frame
static void
IP_frame (void)
{
	BOOLEAN locChange;
	SIZE newRadius;

	SetContext (SpaceContext);

	GameClockTick ();
	ProcessShipControls ();
	
	locChange = CheckShipLocation (&newRadius);
	if (locChange)
	{
		if (playerInInnerSystem ())
		{	// Entering inner system
			DrawSystemTransition (TRUE);
		}
		else if (pSolarSysState->SunDesc[0].radius == newRadius)
		{	// Leaving inner system to outer
			DrawSystemTransition (FALSE);
		}
		else
		{	// Zooming outer system
			ScaleSystem (newRadius);
		}
	}
	else
	{	// Just flying around, minding own business..
		BatchGraphics ();
		RestoreSystemView ();
		RedrawQueue (FALSE);
		DrawAutoPilotMessage (FALSE);
		UnbatchGraphics ();
	}
	
}

static BOOLEAN
CheckZoomLevel (void)
{
	BOOLEAN InnerSystem;
	POINT shipLoc;

	InnerSystem = playerInInnerSystem ();
	if (InnerSystem)
		shipLoc = pSolarSysState->SunDesc[0].location;
	else
		shipLoc = GLOBAL (ip_location);

	pSolarSysState->SunDesc[0].radius = FindRadius (shipLoc,
			MAX_ZOOM_RADIUS << 1);
	if (!InnerSystem)
	{	// Update ship stamp since the radius probably changed
		XFormIPLoc (&shipLoc, &GLOBAL (ShipStamp.origin), TRUE);
	}

	return InnerSystem;
}

static void
ValidateOrbits (void)
{
	COUNT i;
	PLANET_DESC *planet;

	for (i = pSolarSysState->SunDesc[0].NumPlanets,
			planet = &pSolarSysState->PlanetDesc[0]; i; --i, ++planet)
	{
		ValidateOrbit (planet, DISPLAY_FACTOR, DISPLAY_FACTOR / 4,
				pSolarSysState->SunDesc[0].radius);
	}
}

static void
ValidateInnerOrbits (void)
{
	COUNT i;
	PLANET_DESC *planet;

	assert (playerInInnerSystem ());

	planet = pSolarSysState->pOrbitalDesc;
	ValidateOrbit (planet, DISPLAY_FACTOR * 4, DISPLAY_FACTOR,
			planet->radius);

	for (i = 0; i < planet->NumPlanets; ++i)
	{
		PLANET_DESC *moon = &pSolarSysState->MoonDesc[i];
		ValidateOrbit (moon, 2, 1, 2);
	}
}

static void
DrawInnerSystem (void)
{
	ValidateInnerOrbits ();
	DrawSystem (pSolarSysState->pOrbitalDesc->radius, TRUE);
	DrawSISTitle (GLOBAL_SIS (PlanetName));
}

static void
DrawOuterSystem (void)
{
	ValidateOrbits ();
	DrawSystem (pSolarSysState->SunDesc[0].radius, FALSE);
	DrawHyperCoords (CurStarDescPtr->star_pt);
}

static void
ResetSolarSys (void)
{
	// Originally there was a flash_task test here, however, I found no cases
	// where flash_task could be set at the time of call. The test was
	// probably needed on 3DO when IP_frame() was a task.
	assert (!pSolarSysState->InIpFlight);

	DrawMenuStateStrings (PM_STARMAP, -(PM_NAVIGATE - PM_SCAN));

	InitDisplayList ();
	// This also spawns the flagship element
	DoMissions ();

	// Figure out and note which planet/moon we just left, if any
	// This records any existing collision and prevents the ship
	// from entering planets until a new collision occurs.
	// TODO: this may need logic similar to one in leaveInnerSystem()
	//   for when the ship collides with more than one planet at
	//   the same time. While quite rare, it's still possible.
	CheckIntersect ();
	
	pSolarSysState->InIpFlight = TRUE;

	// Do not start playing the music if we entered the solarsys only
	// to load a game (load invoked from Main menu)
	// XXX: This is quite hacky
	if (!PLRPlaying ((MUSIC_REF)~0) &&
			(LastActivity != CHECK_LOAD || NextActivity))
	{
		PlayMusic (SpaceMusic, TRUE, 1);
	}
}

static void
EnterPlanetOrbit (void)
{
	if (pSolarSysState->InIpFlight)
	{	// This means we hit a planet in IP flight; not a Load into orbit
		FreeSolarSys ();

		if (worldIsMoon (pSolarSysState, pSolarSysState->pOrbitalDesc))
		{	// Moon -- use its origin
			// XXX: The conversion functions do not error-correct, so the
			//   point we set here will change once flag_ship_preprocess()
			//   in ipdisp.c starts over again.
			GLOBAL (ShipStamp.origin) =
					pSolarSysState->pOrbitalDesc->image.origin;
		}
		else
		{	// Planet -- its origin is for the outer view, so use mid-screen
			GLOBAL (ShipStamp.origin.x) = SIS_SCREEN_WIDTH >> 1;
			GLOBAL (ShipStamp.origin.y) = SIS_SCREEN_HEIGHT >> 1;
		}
	}

	GetPlanetInfo ();
	(*pSolarSysState->genFuncs->generateOrbital) (pSolarSysState,
			pSolarSysState->pOrbitalDesc);
	LastActivity &= ~(CHECK_LOAD | CHECK_RESTART);
	if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD |
			START_ENCOUNTER)) || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0
			|| GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
		return;

	// Implement a to-do in generate.h for a better test
	if (pSolarSysState->TopoFrame)
	{	// We've entered orbit; LoadPlanet() called planet surface-gen code
		PlanetOrbitMenu ();
		FreePlanet ();
	}
	// Otherwise, generateOrbital function started a homeworld conversation,
	// and we did not get to the planet no matter what.

	// START_ENCOUNTER could be set by Devices menu a number of ways:
	// Talking Pet, Sun Device or a Caster over Chmmr, or
	// a Caster for Ilwrath
	// Could also have blown self up with Utwig Bomb
	if (!(GLOBAL (CurrentActivity) & (START_ENCOUNTER |
			CHECK_ABORT | CHECK_LOAD))
			&& GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
	{	// Reload the system and return to the inner view
		PLANET_DESC *orbital = LoadSolarSys ();
		assert (!orbital);
		CheckZoomLevel ();
		ValidateOrbits ();
		ValidateInnerOrbits ();
		ResetSolarSys ();

		RepairSISBorder ();
		TransitionSystemIn ();
	}
}

static void
InitSolarSys (void)
{
	BOOLEAN InnerSystem;
	BOOLEAN Reentry;
	PLANET_DESC *orbital;


	LoadIPData ();
	LoadLanderData ();

	Reentry = (GLOBAL (ShipFacing) != 0);
	if (!Reentry)
	{
		GLOBAL (autopilot.x) = ~0;
		GLOBAL (autopilot.y) = ~0;

		GLOBAL (ShipStamp.origin.x) = SIS_SCREEN_WIDTH >> 1;
		GLOBAL (ShipStamp.origin.y) = SIS_SCREEN_HEIGHT - 2;
		
		GLOBAL (ip_location) = displayToLocation (GLOBAL (ShipStamp.origin),
				MAX_ZOOM_RADIUS);
	}


	StarsFrame = CreateStarBackGround ();
	
	SetContext (SpaceContext);
	SetContextFGFrame (Screen);
	SetContextBackGroundColor (BLACK_COLOR);
	

	orbital = LoadSolarSys ();
	InnerSystem = CheckZoomLevel ();
	ValidateOrbits ();
	if (InnerSystem)
		ValidateInnerOrbits ();

	if (Reentry)
	{
		(*pSolarSysState->genFuncs->reinitNpcs) (pSolarSysState);
	}
	else
	{
		EncounterRace = -1;
		EncounterGroup = 0;
		GLOBAL (BattleGroupRef) = 0;
		ReinitQueue (&GLOBAL (ip_group_q));
		ReinitQueue (&GLOBAL (npc_built_ship_q));
		(*pSolarSysState->genFuncs->initNpcs) (pSolarSysState);
	}

	if (orbital)
	{
		enterOrbital (orbital);
	}
	else
	{	// Draw the borders, the system (inner or outer) and fade/transition
		SetContext (SpaceContext);

		SetTransitionSource (NULL);
		BatchGraphics ();

		DrawSISFrame ();
		DrawSISMessage (NULL);

		ResetSolarSys ();

		if (LastActivity == (CHECK_LOAD | CHECK_RESTART))
		{	// Starting a new game, NOT from load!
			// We have to fade the screen in from intro or menu
			DrawOuterSystem ();
			RedrawQueue (FALSE);
			UnbatchGraphics ();
			FadeScreen (FadeAllToColor, ONE_SECOND / 2);

			LastActivity = 0;
		}
		else if (LastActivity == CHECK_LOAD && !NextActivity)
		{	// Called just to load a game; invoked from Main menu
			// No point in drawing anything
			UnbatchGraphics ();
		}
		else
		{	// Entered a new system, or loaded into inner or outer
			if (InnerSystem)
				DrawInnerSystem ();
			else
				DrawOuterSystem ();
			RedrawQueue (FALSE);
			ScreenTransition (3, NULL);
			UnbatchGraphics ();

			LastActivity &= ~CHECK_LOAD;
		}
	}
}

static void
endInterPlanetary (void)
{
	GLOBAL (CurrentActivity) &= ~END_INTERPLANETARY;
	
	if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
	{
		// These are game state changing ops and so cannot be
		// called once another game has been loaded!
		(*pSolarSysState->genFuncs->uninitNpcs) (pSolarSysState);
		SET_GAME_STATE (USED_BROADCASTER, 0);
	}
}

// Find the closest planet to a point, in interplanetary.
static PLANET_DESC *
closestPlanetInterPlanetary (const POINT *point)
{
	BYTE i;
	BYTE numPlanets;
	DWORD bestDistSquared;
	PLANET_DESC *bestPlanet = NULL;

	assert(pSolarSysState != NULL);

	numPlanets = pSolarSysState->SunDesc[0].NumPlanets;

	bestDistSquared = (DWORD) -1;  // Maximum value of DWORD.
	for (i = 0; i < numPlanets; i++)
	{
		PLANET_DESC *planet = &pSolarSysState->PlanetDesc[i];

		SIZE dx = point->x - planet->image.origin.x;
		SIZE dy = point->y - planet->image.origin.y;

		DWORD distSquared = (DWORD) ((long) dx * dx + (long) dy * dy);
		if (distSquared < bestDistSquared)
		{
			bestDistSquared = distSquared;
			bestPlanet = planet;
		}
	}

	return bestPlanet;
}

static void
UninitSolarSys (void)
{
	FreeSolarSys ();

//FreeLanderData ();
//FreeIPData ();

	DestroyDrawable (ReleaseDrawable (StarsFrame));
	StarsFrame = NULL;

	if (GLOBAL (CurrentActivity) & END_INTERPLANETARY)
	{
		endInterPlanetary ();
		return;
	}

	if ((GLOBAL (CurrentActivity) & START_ENCOUNTER) && EncounterGroup)
	{
		GetGroupInfo (GLOBAL (BattleGroupRef), EncounterGroup);
		// Generate the encounter location name based on the closest planet

		if (GLOBAL (ip_planet) == 0)
		{
			PLANET_DESC *planet =
					closestPlanetInterPlanetary (&GLOBAL (ShipStamp.origin));

			(*pSolarSysState->genFuncs->generateName) (
					pSolarSysState, planet);
		}
	}
}
































/********************************************/
// make sure this is too big...is used for search stuff
int const MAX_NUM_STARS = 500;




/*

// this is the total number of comm flags
#define NCM 36
// LIST OF ALL THE COMM FLAGS
int const NUM_COM_FLAGS = NCM;	// #'s below must match!

char *COMM_FLAGS[NCM];
BYTE *COMM_FLAG_STAR_IDS[NCM];
BYTE COMM_FLAG_SHIP_IDS[NCM];


// this is the # of comm flags we dont use symbols for
#define NHW 14
int const NUM_HOMEWORLDS = NHW;

#define NUM_ZEROS NCM - NHW

// # of 0's in ZERO_STRING must match:
//	1.) size COMM_FLAG_STAR_HIGHLIGHTS[X] (add 1 for string terminus)
//	2.) the # of "points of interest" in the "InitCommFlags" function
static char * ZERO_STRING = "0000000000000000000000";	// USE ME
//static char * ZERO_STRING = "1111111111111111111111";// for testing!

char COMM_FLAG_STAR_HIGHLIGHTS[NUM_ZEROS+1];	// add 1 for string terminus!
*/

// this is the total number of comm flags
#define NCM 36

// LIST OF ALL THE COMM FLAGS
int const NUM_COM_FLAGS = NCM;	// #'s below must match!

char *COMM_FLAGS[NCM];
BYTE *COMM_FLAG_STAR_IDS[NCM];
BYTE COMM_FLAG_SHIP_IDS[NCM];

// this is the # of comm flags we dont use symbols for
#define NHW 14
int const NUM_HOMEWORLDS = NHW;

#define NUM_ZEROS NCM

// # of 0's in ZERO_STRING must match:
//	1.) size COMM_FLAG_STAR_HIGHLIGHTS[X] (add 1 for string terminus)
//	2.) the # of "points of interest" in the "InitCommFlags" function
static char * ZERO_STRING = "000000000000000000000000000000000000";	// USE ME
//static char * ZERO_STRING = "111111111111111111111111111111111111";// for testing!

char COMM_FLAG_STAR_HIGHLIGHTS[NUM_ZEROS+1];	// add 1 for string terminus!

//----------------------------

typedef struct {
	char *flag;
	BYTE starId;
	BYTE shipId;
	char *displayName;
	
} CommFlag;

//----------------------------
// TODO: replace above arrays with this, here
static const CommFlag comm_flags[] = {

	{"SPATHI_DEFINED",SPATHI_DEFINED,SPATHI_SHIP,"Spathi Homeworld"},
	{"SAMATRA_DEFINED",SAMATRA_DEFINED,0,"Final Battle"},	// urquan / korah
	{"DRUUGE_DEFINED",DRUUGE_DEFINED,DRUUGE_SHIP,"Druuge Homeworld"},
	{"ILWRATH_DEFINED",ILWRATH_DEFINED,ILWRATH_SHIP,"Ilwrath Homeworld"},
	{"THRADD_DEFINED",THRADD_DEFINED,THRADDASH_SHIP,"Thraddash Homeworld"},
	{"MYCON_DEFINED",MYCON_DEFINED,MYCON_SHIP,"Mycon Homeworld"},
	{"YEHAT_DEFINED",YEHAT_DEFINED,YEHAT_SHIP,"Yehat Homeworld"},
	{"PKUNK_DEFINED",PKUNK_DEFINED,PKUNK_SHIP,"Pkunk Homeworld"},
	{"VUX_DEFINED",VUX_DEFINED,VUX_SHIP,"Vux Homeworld"},
	{"ORZ_DEFINED",ORZ_DEFINED,ORZ_SHIP,"Orz Homeworld"},
	{"ZOQFOT_DEFINED",ZOQFOT_DEFINED,ZOQFOTPIK_SHIP,"ZoqFotPik Homeworld"},
	{"UTWIG_DEFINED",UTWIG_DEFINED,UTWIG_SHIP,"Utwig Homeworld"},
	{"SUPOX_DEFINED",SUPOX_DEFINED,SUPOX_SHIP,"Supox Homeworld"},
	{"TALKING_PET_DEFINED",TALKING_PET_DEFINED,UMGAH_SHIP,"Umgah Homeworld"},
		
	// points of interest
	{"SYREEN_DEFINED",SYREEN_DEFINED,0,"Syreen"},
	{"SHOFIXTI_DEFINED",SHOFIXTI_DEFINED,0,"Shofixti Homeworld"},
	{"ZOQ_SCOUT_DEFINED",ZOQ_SCOUT_DEFINED,0,"Hyperwave Transmission"},
	{"CHMMR_DEFINED",CHMMR_DEFINED,0,"Chmmr"},
	{"SUN_DEVICE_DEFINED",SUN_DEVICE_DEFINED,0,"Sun Device"},
	{"ANDROSYNTH_DEFINED",ANDROSYNTH_DEFINED,0,"Androsynth Homeworld"},
	{"TAALO_PROTECTOR_DEFINED",TAALO_PROTECTOR_DEFINED,0,"Taalo Device"},
	{"SHIP_VAULT_DEFINED",SHIP_VAULT_DEFINED,0,"Syreen Ships"},
	{"BURVIXESE_DEFINED",BURVIXESE_DEFINED,0,"Burvixese Homeworld"},
	{"URQUAN_WRECK_DEFINED",URQUAN_WRECK_DEFINED,0,"Urquan Shipwreck"},
	{"AQUA_HELIX_DEFINED",AQUA_HELIX_DEFINED,0,"Aqua Helix"},
	{"BOMB_DEFINED",BOMB_DEFINED,0,"Precursor Bomb"},
	{"VUX_BEAST_DEFINED",VUX_BEAST_DEFINED,0,"Beast"},
	{"SLYLANDRO_DEFINED",SLYLANDRO_DEFINED,0,"Slylandro Homeworld"},
	{"MAIDENS_DEFINED",MAIDENS_DEFINED,0,"Admiral Zex"},
	{"RAINBOW_DEFINED",RAINBOW_DEFINED,0,"Strange Planet"}, 		// any rainbow world
	{"MYCON_TRAP_DEFINED",MYCON_TRAP_DEFINED,0,"Mycon Trap"}, 
	{"EGG_CASE0_DEFINED",EGG_CASE0_DEFINED,0,"Mycon Egg Case"},
	{"EGG_CASE1_DEFINED",EGG_CASE1_DEFINED,0,"Mycon Egg Case"},
	{"EGG_CASE2_DEFINED",EGG_CASE2_DEFINED,0,"Mycon Egg Case"},
	{"START_COLONY_DEFINED",START_COLONY_DEFINED,0,"Strange Star"}, 		// arilou / quas-space portal
	{"SOL_DEFINED",SOL_DEFINED,0,"Human Homeworld"}, 		// human homeworld

};
/*
//------------------------------
// USED FOR TESTING:
void UpdateRaceStrengths(int newNum){
	
	for(int i=0; i < NUM_COM_FLAGS; i++){
		if(comm_flags[i].shipId != 0){
			SetRaceStrength(comm_flags[i].shipId,newNum);
		}
	}
}
*/
//------------------------------
// return a visual index of the star (SPATHI_DEFINED, etc.)
extern char* GetStarINDEX(BYTE newIndex){
	
	for(int i=0; i < NUM_COM_FLAGS; i++){
		if(comm_flags[i].starId == newIndex){
			return comm_flags[i].displayName;
		}
	}
	return NULL;
}









/********************************************/
extern void Trace_CLEAR(){

	FILE *f = fopen("traceLog.txt", "w");

	if (f == NULL)
	{
		printf("Error opening file!\n");
		exit(1);
	}

	// print some text
	fprintf(f,"");

	fclose(f);
}
extern void Trace(char *traceStatement){

	FILE *f = fopen("traceLog.txt", "a");

	if (f == NULL)
	{
		printf("Error opening file!\n");
		exit(1);
	}

	// print some text
	fprintf(f,"%s\n", traceStatement);

	fclose(f);
}

extern void TraceNum(int newNum){
	char result[50];
    sprintf(result, "TraceNum: %d", newNum);
    Trace(result);
}

extern void TraceBYTE(BYTE newNum){
	char result[50];
    sprintf(result, "TraceNum: %d", newNum);
    Trace(result);
}

extern void Trace_SEED(char *traceStatement){

	// Trace Seed #
	char result[50];
    float num = GET_GAME_STATE (WORLDGEN_SEED);
    sprintf(result, "...SEED: %f", num);

	Trace(traceStatement);
    Trace(result);

}

extern void Trace_TRACEBUG(){
	Trace("--GET_GAME_STATE(STAR_HIGHLIGHTS)");
	TraceNum(GET_GAME_STATE(STAR_HIGHLIGHTS));


	Trace("--COMM_FLAG_STAR_HIGHLIGHTS");
	Trace(COMM_FLAG_STAR_HIGHLIGHTS);
}



/************************************************/
extern void RevealAlien(BYTE which_alien){
	//Trace("RevealAlien");
	StartSphereTracking(which_alien);
}
//------------------------
int GetStarDist(STAR_DESC star1,STAR_DESC star2){
	int a = star1.star_pt.x - star2.star_pt.x;
	int b = star1.star_pt.y - star2.star_pt.y;
	int c = sqrt(a*a + b*b);
	return ceil(c);

}
//------------------------
int GetDistance(int x1, int y1, int x2, int y2){
	int a = x1 - x2;
	int b = y1 - y2;
	int c = sqrt(a*a + b*b);
	return ceil(c);
}
int GetDistanceP(POINT p1, POINT p2){
	int a = p1.x - p2.x;
	int b = p1.y - p2.y;
	int c = sqrt(a*a + b*b);
	return ceil(c);
}


/********************************************/


// return the current location of the player within the universe
POINT UniversePos(){
	POINT universe;

	universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
	universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
		
	return universe;
}
//-----------------------------
int CurrStarIndex(){
	return CurStarDescPtr->Index;
}	
//-----------------------------
STAR_DESC GetClosestStar(){
	
	
	POINT player = UniversePos();
	int shortestDistance = 999999;	
	STAR_DESC closestStar;

	// for each star...
	for(int i=0; i < MAX_NUM_STARS; i++){

		// check star distance
		STAR_DESC s = star_array[i];
		//int d = GetDistanceP(player,s);
		int d = GetDistanceP(player,s.star_pt);
		
		
		
		// if closer, update closest star
		if(d < shortestDistance){
			shortestDistance = d;
			closestStar = s;
		}

	}
	
	
	return closestStar;
	
}

extern void GetClosestStarName (UNICODE buf[]){
	
	STAR_DESC s = GetClosestStar();
	return GetClusterName(&s,buf);
}
//-----------------------------
float GetSEED(){
	// init / load the random seed
	if(!GET_GAME_STATE(WORLDGEN_SEED)){
		Trace("xxxxxxxx NO SEED FOUND! MAKING ONE UP! xxxxxxxx");
		
		//TEST
		srand(time(0));
		
		
		SET_GAME_STATE (WORLDGEN_SEED,rand());
	
	}

	//Trace_SEED("GetSEED");
	return GET_GAME_STATE(WORLDGEN_SEED);
}
//-----------------------------
// does this work in hyperspace?
// return a seed for the current star system
extern int GetStarSEED(){
	
	Trace_SEED("-GetRandomSeedForStarPoint");


	int randomNum = GetSEED();
	STAR_DESC closestStar = GetClosestStar();
	int randSeed = MAKE_WORD (closestStar.star_pt.x+randomNum,closestStar.star_pt.y+randomNum);
	//int randSeed = MAKE_WORD (CurStarDescPtr->star_pt.x+randomNum,CurStarDescPtr->star_pt.y+randomNum);
	
	/*
	Trace("closestStar XY");
	TraceNum(closestStar.star_pt.x);
	TraceNum(closestStar.star_pt.y);
	*/

	/*
	Trace("GetStarSEED: rand seed is...");
	TraceNum(randSeed);
	*/
	return randSeed;
}



/************************************************/
// return a star based on its "special index" (homeworld, etc.)
// from the ORIGINAL map
extern STAR_DESC GetStarOM(BYTE newIndex){

	// make this accessible!
	extern STAR_DESC starmap_array[];

	int fullSize = MAX_NUM_STARS;

	for(int i=0; i < fullSize; i++){

		BYTE nt = starmap_array[i].Index;
		if(nt == newIndex){
			//Trace("--STAR OM FOUND");
			return starmap_array[i];
		}

	}
	Trace("--star OM not found...");

	return starmap_array[0];
}
//-----------------------------
// return a star based on its "special index" (homeworld, etc.)
extern STAR_DESC GetStar(BYTE newIndex){

	int fullSize = MAX_NUM_STARS;

	for(int i=0; i < fullSize; i++){

		BYTE nt = star_array[i].Index;
		if(nt == newIndex){
			//Trace("--STAR FOUND");
			return star_array[i];
		}

	}
	Trace("--star not found...");

	return star_array[0];
}
//-----------------------------
extern int Star_X(int newIndex){
	return GetStar(newIndex).star_pt.x;
}
extern int Star_Y(int newIndex){
	return GetStar(newIndex).star_pt.y;
}

//-----------------------------
extern STAR_DESC GetStarOG(BYTE newIndex){

	int fullSize = MAX_NUM_STARS;

	for(int i=0; i < fullSize; i++){

		BYTE nt = star_array[i].Index;
		if(nt == newIndex){
			//Trace("--STAR FOUND");
			return star_array[i];
		}

	}
	Trace("--star not found...");

	return star_array[0];
}
//-----------------------------
extern int GetPortal_X(){
	//return GET_GAME_STATE(PORTAL_X);
	return Star_X(START_COLONY_DEFINED) - 10;
}
//-----------------------------
extern int GetPortal_Y(){
	//return GET_GAME_STATE(PORTAL_Y);
	return Star_Y(START_COLONY_DEFINED);
}
//-----------------------------
void InitPortal(){

	// just treat the start colony as arilou homeworld...
	STAR_DESC arilouStar = GetStar(START_COLONY_DEFINED);
	SET_GAME_STATE (PORTAL_X,arilouStar.star_pt.x - 10);
	SET_GAME_STATE (PORTAL_Y,arilouStar.star_pt.y);
	
	
	Trace("InitPortal()");
	TraceNum(GetPortal_X());
	TraceNum(GetPortal_Y());

}


/************************************************/

void SetRaceLoc (BYTE which_race, COORD x, COORD y)
{
	//Trace("SetRaceLoc");
	
	HFLEETINFO hFleet;
	FLEET_INFO *FleetPtr;

	hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), which_race);
	FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);

	// TEMP:
	//StartSphereTracking(which_race);

	// set pos / starting pos
	FleetPtr->known_loc.x = x;
	FleetPtr->known_loc.y = y;
	FleetPtr->loc.x = x;
	FleetPtr->loc.y = y;
	
	UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
}
/************************************************/
DWORD GetRandomSeedForStar (const STAR_DESC *star)
{

	//Trace_SEED("-GetRandomSeedForStar");

	// used in the seed:
	int randomNum = GetSEED();
	int newX = star->star_pt.x;
	int newY = star->star_pt.y;

	// SPECIAL star: use original SEED so we dont break anything hard-coded!
	BYTE nt = star->Index;

	//Trace("GetRandomSeedForStar");
	//TraceNum(nt);


	if(nt != 0){
		STAR_DESC originalStar = GetStarOM(nt);
		randomNum = 1;
		newX = originalStar.star_pt.x;
		newY = originalStar.star_pt.y;
	}

	return MAKE_DWORD (newX * randomNum, newY * randomNum);
}

/************************************************/





//-----------------------------
void UpdateRaceHomeworld(BYTE which_race, BYTE newIndex){

	// get race homeworld
	STAR_DESC newStar = GetStar(newIndex);

	// move race's fleet to center on race homeworld
	SetRaceLoc(which_race,newStar.star_pt.x,newStar.star_pt.y);

}
//-----------------------------
void  UpdateAllRaceHomeworlds(){

	Trace("+++UpdateAllRaceHomeworlds");



	// set the race location of all aliens on "new game" (not load), after the galaxy is randomized
	UpdateRaceHomeworld (SPATHI_SHIP, SPATHI_DEFINED);
	UpdateRaceHomeworld (THRADDASH_SHIP, THRADD_DEFINED);
	UpdateRaceHomeworld (SHOFIXTI_SHIP, SHOFIXTI_DEFINED);
	UpdateRaceHomeworld (YEHAT_SHIP, YEHAT_DEFINED);
	UpdateRaceHomeworld (PKUNK_SHIP, PKUNK_DEFINED);
	UpdateRaceHomeworld (VUX_SHIP, VUX_DEFINED);
	UpdateRaceHomeworld (ORZ_SHIP, ORZ_DEFINED);
	UpdateRaceHomeworld (DRUUGE_SHIP, DRUUGE_DEFINED);
	UpdateRaceHomeworld (ILWRATH_SHIP, ILWRATH_DEFINED);
	UpdateRaceHomeworld (ZOQFOTPIK_SHIP, ZOQFOT_DEFINED);
	UpdateRaceHomeworld (UTWIG_SHIP, UTWIG_DEFINED);
	UpdateRaceHomeworld (SUPOX_SHIP, SUPOX_DEFINED);
	UpdateRaceHomeworld (UMGAH_SHIP, TALKING_PET_DEFINED);// UMGAH use talking pet
	UpdateRaceHomeworld (MYCON_SHIP, MYCON_DEFINED);


	UpdateRaceHomeworld (SYREEN_SHIP, SYREEN_DEFINED);


	// URUAN use SAMATRA
	STAR_DESC newStar = GetStar(SAMATRA_DEFINED);
	SetRaceLoc(BLACK_URQUAN_SHIP,newStar.star_pt.x,newStar.star_pt.y - 200);
	SetRaceLoc(URQUAN_SHIP,newStar.star_pt.x,newStar.star_pt.y + 200);

	// SEND ILWRATH TO THE PKUNK
	STAR_DESC pkunkStar = GetStar(PKUNK_DEFINED);
	//SetRaceDest (ILWRATH_SHIP, pkunkStar.star_pt.x, pkunkStar.star_pt.y, 14, (BYTE)~0);
	SetRaceDest (ILWRATH_SHIP, pkunkStar.star_pt.x, pkunkStar.star_pt.y + 200, 14, (BYTE)~0);	// slightly offset

	// HUMANITY
	UpdateRaceHomeworld (HUMAN_SHIP, SOL_DEFINED);
	//SetRaceStrength(HUMAN_SHIP,100);

	// move arilou space to the portal location
	InitPortal();
	SetRaceLoc(ARILOU_SHIP,GetPortal_X(),GetPortal_Y());

	// TEMP: give "module blueprints"
	//SET_GAME_STATE (WEAPON3_ON_SHIP, 1);
	
		
	//TEST
	//UnlockModules();
}


/************************************************/
// write the given star's coordinates to the "writeMe" string
static char* GetStarPOS(BYTE newIndex){

	char *writeMe = malloc(64);

	STAR_DESC s = GetStar(newIndex);

	float starX = s.star_pt.x / 10.0;
	float starY = s.star_pt.y / 10.0;

    sprintf(writeMe, "%.1f, %.1f", starX, starY);

	//Trace(writeMe);
	return writeMe;
}
/*
//------------------------
static void PrintCommFlags(){
	for(int i=0; i < NUM_COM_FLAGS; i++){
		 
		Trace("..............................");
		Trace(COMM_FLAGS[i]);	
		STAR_DESC s = GetStar(COMM_FLAG_STAR_IDS[i]);

		char *writeMe = malloc(64);
		float starX = s.star_pt.x / 10.0;
		float starY = s.star_pt.y / 10.0;
		sprintf(writeMe, "%.1f, %.1f", starX, starY);
		Trace(writeMe);

	}
}
*/
/*****************************************************/
// You must free the result if result is non-NULL.
extern char *str_replace(char *orig, char *rep, char *with) {
    char *result; // the return string
    char *ins;    // the next insert point
    char *tmp;    // varies
    int len_rep;  // length of rep (the string to remove)
    int len_with; // length of with (the string to replace rep with)
    int len_front; // distance between rep and end of last rep
    int count;    // number of replacements

    // sanity checks and initialization
    if (!orig || !rep)
        return NULL;
    len_rep = strlen(rep);
    if (len_rep == 0)
        return NULL; // empty rep causes infinite loop during count
    if (!with)
        with = "";
    len_with = strlen(with);

    // count the number of replacements needed
    ins = orig;
    for(count = 0; (tmp = strstr(ins, rep)); ++count) {
        ins = tmp + len_rep;
    }

    tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);

    if (!result)
        return NULL;

    // first time through the loop, all the variable are set correctly
    // from here on,
    //    tmp points to the end of the result string
    //    ins points to the next occurrence of rep in orig
    //    orig points to the remainder of orig after "end of rep"
    while (count--) {
        ins = strstr(orig, rep);
        len_front = ins - orig;
        tmp = strncpy(tmp, orig, len_front) + len_front;
        tmp = strcpy(tmp, with) + len_with;
        orig += len_front + len_rep; // move to next "end of rep"
    }
    strcpy(tmp, orig);
    return result;
}
/*****************************************************/
//extern char* GetStarPOS(BYTE newIndex);
//------------------------
void AddCommFlag(char *newFlag, BYTE newStarID,int newIndex, BYTE newReveal){

	COMM_FLAGS[newIndex] = newFlag;
	COMM_FLAG_SHIP_IDS[newIndex] = newReveal;
	COMM_FLAG_STAR_IDS[newIndex] = newStarID;
}


//------------------------
int fromBinary(const char *s) {
  return (int) strtol(s, NULL, 2);
}
//------------------------
extern
void
InitCommFlags(){




	// !!! make sure NUM_COM_FLAGS matches the # of items added !!!

	// homeworlds
	AddCommFlag("SPATHI_DEFINED",SPATHI_DEFINED,0,SPATHI_SHIP);
	AddCommFlag("SAMATRA_DEFINED",SAMATRA_DEFINED,1,0);	// urquan / korah
	AddCommFlag("DRUUGE_DEFINED",DRUUGE_DEFINED,2,DRUUGE_SHIP);
	AddCommFlag("ILWRATH_DEFINED",ILWRATH_DEFINED,3,ILWRATH_SHIP);
	AddCommFlag("THRADD_DEFINED",THRADD_DEFINED,4,THRADDASH_SHIP);
	AddCommFlag("MYCON_DEFINED",MYCON_DEFINED,5,MYCON_SHIP);
	AddCommFlag("YEHAT_DEFINED",YEHAT_DEFINED,6,YEHAT_SHIP);
	AddCommFlag("PKUNK_DEFINED",PKUNK_DEFINED,7,PKUNK_SHIP);
	AddCommFlag("VUX_DEFINED",VUX_DEFINED,8,VUX_SHIP);
	AddCommFlag("ORZ_DEFINED",ORZ_DEFINED,9,ORZ_SHIP);
	AddCommFlag("ZOQFOT_DEFINED",ZOQFOT_DEFINED,10,ZOQFOTPIK_SHIP);
	AddCommFlag("UTWIG_DEFINED",UTWIG_DEFINED,11,UTWIG_SHIP);
	AddCommFlag("SUPOX_DEFINED",SUPOX_DEFINED,12,SUPOX_SHIP);
	AddCommFlag("TALKING_PET_DEFINED",TALKING_PET_DEFINED,13,UMGAH_SHIP);
 
	// points of interest
	AddCommFlag("SYREEN_DEFINED",SYREEN_DEFINED,14,0);
	AddCommFlag("SHOFIXTI_DEFINED",SHOFIXTI_DEFINED,15,0);
	AddCommFlag("ZOQ_SCOUT_DEFINED",ZOQ_SCOUT_DEFINED,16,0);
	AddCommFlag("CHMMR_DEFINED",CHMMR_DEFINED,17,0);
	AddCommFlag("SUN_DEVICE_DEFINED",SUN_DEVICE_DEFINED,18,0);
	AddCommFlag("ANDROSYNTH_DEFINED",ANDROSYNTH_DEFINED,19,0);
	AddCommFlag("TAALO_PROTECTOR_DEFINED",TAALO_PROTECTOR_DEFINED,20,0);
	AddCommFlag("SHIP_VAULT_DEFINED",SHIP_VAULT_DEFINED,21,0);
	AddCommFlag("BURVIXESE_DEFINED",BURVIXESE_DEFINED,22,0);
	AddCommFlag("URQUAN_WRECK_DEFINED",URQUAN_WRECK_DEFINED,23,0);
	AddCommFlag("AQUA_HELIX_DEFINED",AQUA_HELIX_DEFINED,24,0);
	AddCommFlag("BOMB_DEFINED",BOMB_DEFINED,25,0);
	AddCommFlag("VUX_BEAST_DEFINED",VUX_BEAST_DEFINED,26,0);
	AddCommFlag("SLYLANDRO_DEFINED",SLYLANDRO_DEFINED,27,0);
	AddCommFlag("MAIDENS_DEFINED",MAIDENS_DEFINED,28,0);
	AddCommFlag("RAINBOW_DEFINED",RAINBOW_DEFINED,29,0); 		// any rainbow world
	AddCommFlag("MYCON_TRAP_DEFINED",MYCON_TRAP_DEFINED,30,0); 		// mycon trap
	AddCommFlag("EGG_CASE0_DEFINED",EGG_CASE0_DEFINED,31,0); 		// mycon egg case
	AddCommFlag("EGG_CASE1_DEFINED",EGG_CASE1_DEFINED,32,0); 		// mycon egg case
	AddCommFlag("EGG_CASE2_DEFINED",EGG_CASE2_DEFINED,33,0); 		// mycon egg case	
	AddCommFlag("START_COLONY_DEFINED",START_COLONY_DEFINED,34,0); 		// arilou / quas-space portal
	
	AddCommFlag("SOL_DEFINED",SOL_DEFINED,35,0); 		// human homeworld (earth)


	// init state
	if(!GET_GAME_STATE(STAR_HIGHLIGHTS)){
		Trace("zzzzzzzzz STAR_HIGHLIGHTS xxxxxxxx");

		strcpy(COMM_FLAG_STAR_HIGHLIGHTS, ZERO_STRING);
		SET_GAME_STATE (STAR_HIGHLIGHTS,fromBinary(COMM_FLAG_STAR_HIGHLIGHTS));
	
		Trace_TRACEBUG();

	}

	// convert INT in saved game data to STRING we use for display
	itoa(GET_GAME_STATE(STAR_HIGHLIGHTS),COMM_FLAG_STAR_HIGHLIGHTS,2);


	char zeroString[strlen(ZERO_STRING)+1];
	strcpy(zeroString,ZERO_STRING);
	
	
	int count=0;
	for(unsigned i=0; i < sizeof(zeroString); i++){
		
		if(i > sizeof(zeroString) - strlen(COMM_FLAG_STAR_HIGHLIGHTS)-1){
			zeroString[i-1] = COMM_FLAG_STAR_HIGHLIGHTS[count];
			count++;
		}
	}
	
	Trace("MERGED ZERO STRING");
	Trace(zeroString);
	
	strcpy(COMM_FLAG_STAR_HIGHLIGHTS,zeroString);

	
	Trace("InitCommFlags");
	Trace_TRACEBUG();
}


//------------------------
char* concat(const char *s1, const char *s2)
{
    char *result = malloc(strlen(s1) + strlen(s2) + 1); // +1 for the null-terminator
    // in real code you would check for errors in malloc here
    strcpy(result, s1);
    strcat(result, s2);
    return result;
}
// Replace FLAGS in comm dialogue (to homeworld locations, etc.)
char * ReplaceCommFlags(char *newPtr) {

	for(int i =0; i < NUM_COM_FLAGS; i++){

		// flag is in dialogue...
		if(strstr(newPtr, COMM_FLAGS[i]) != NULL) {

			//Trace("---FLAG FOUND!");
			//Trace(COMM_FLAGS[i]);
		
/*
			// show alien race on map
			if(COMM_FLAG_SHIP_IDS[i] > 0)
				RevealAlien(COMM_FLAG_SHIP_IDS[i]);
			else
				SetStarHighlight((int)COMM_FLAG_STAR_IDS[i],1);// highlight star on map (point of interest)
			*/
			
			
			// NEW: just show highlight for everything...
			//SetStarHighlight((int)COMM_FLAG_STAR_IDS[i],1);// highlight star on map (point of interest)
		}

		
		
		
		// get star name
		STAR_DESC newStar = GetStar(COMM_FLAG_STAR_IDS[i]);
		UNICODE buf[256];
		GetClusterName (&newStar, buf);
		

		// add coords
		int length = 0;
		length += sprintf(buf + length, buf);
		length += sprintf(buf + length, " (");
		length += sprintf(buf + length, GetStarPOS((int)COMM_FLAG_STAR_IDS[i]));
		length += sprintf(buf + length, ")");
		
		// print it
		newPtr = str_replace(newPtr,COMM_FLAGS[i],buf);	


		
		
		// use STAR COORDS
//		newPtr = str_replace(newPtr,COMM_FLAGS[i],GetStarPOS((int)COMM_FLAG_STAR_IDS[i]));
	}
	return newPtr;
}

/************************************************/
extern void UpdateStarArray (void){

	Trace("UpdateStarArray");



	// default star array (original game)
	int randomNum = GetSEED();


	#define VORTEX_SCALE 20 
	#define NUM_STARMAPS 300 
	#define NUM_STARS 600 
	extern STAR_DESC starmap_arrays[NUM_STARMAPS][NUM_STARS];

	//TEST
	int seed = randomNum % NUM_STARMAPS;
	star_array = starmap_arrays[seed];

	Trace("===== STAR ARRAY SEED=====");
	TraceNum(seed);

	
	UpdateAllRaceHomeworlds();

	InitCommFlags();

	//PrintCommFlags();


}
/************************************************/
extern void SetGameStartLocation(){
	// SET GAME START LOCATION
	STAR_DESC newStar = GetStar(SOL_DEFINED);
	GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (newStar.star_pt.x);
	GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (newStar.star_pt.y);
	
	

	
	// init quasi-space portal
	//InitPortal();
}
extern BOOLEAN
IsAtGameStartLocation(){
	STAR_DESC newStar = GetStar(SOL_DEFINED);
	Trace("IsAtGameStartLocation");
	TraceNum(GLOBAL_SIS(log_x));
	TraceNum(GLOBAL_SIS(log_y));
	TraceNum(UNIVERSE_TO_LOGX (newStar.star_pt.x));
	TraceNum(UNIVERSE_TO_LOGY (newStar.star_pt.y));

	return GLOBAL_SIS(log_x) == UNIVERSE_TO_LOGX (newStar.star_pt.x) && GLOBAL_SIS(log_y) == UNIVERSE_TO_LOGY (newStar.star_pt.y);
	
}

/************************************************/
/*
// set a star's highlight state to on / off
void SetStarHighlight(int starIndex,int isOn){

	for(int i=0; i < NUM_COM_FLAGS - NUM_HOMEWORLDS; i++){

		if((int)COMM_FLAG_STAR_IDS[i + NUM_HOMEWORLDS] == starIndex){

			//Trace("SetStarHighlight...star index found!");
		
			if(isOn)
				COMM_FLAG_STAR_HIGHLIGHTS[i] = '1';
			else
				COMM_FLAG_STAR_HIGHLIGHTS[i] = '0';

		}
	}

	// TESTING: SAVE DATA
	SET_GAME_STATE (STAR_HIGHLIGHTS,fromBinary(COMM_FLAG_STAR_HIGHLIGHTS));


}

//------------------------
// tell map to draw all stars that have been
// referenced (flagged) in the comm
extern void ShowQuestHighlights(){
	
	
	//Trace("ShowQuestHighlights");

	// un-highlight stars for quests we've completed!
	UnhighlightCompletedQuests();
	
	// for each comm flag (referencing a star)...
	for(int i=0; i < NUM_COM_FLAGS - NUM_HOMEWORLDS; i++){

		// check to see if this star is highlighted
		if(COMM_FLAG_STAR_HIGHLIGHTS[i] == '1'){

			
			// not in quasi-space!
			// tell map to draw a highlight at this star's point
			if(!inQuasiSpace ()){
				
				extern void DrawMapHighlight(POINT newPoint);// SILENCE!
				DrawMapHighlight(GetStar((int)COMM_FLAG_STAR_IDS[i + NUM_HOMEWORLDS]).star_pt);
			}
		}
	}

}
//------------------------
// return true if the given star (via index) is highlighted
extern int StarIsHighlighted(int newIndex){
	for(int i=0; i < NUM_COM_FLAGS - NUM_HOMEWORLDS; i++){
		if(newIndex == COMM_FLAG_STAR_IDS[i+NUM_HOMEWORLDS] && COMM_FLAG_STAR_HIGHLIGHTS[i] == '1')
			return 1;
	}
	return 0;
}
*/
// set a star's highlight state to on / off
void SetStarHighlight(int starIndex,int isOn){

	for(int i=0; i < NUM_COM_FLAGS; i++){

		if((int)COMM_FLAG_STAR_IDS[i] == starIndex){

			//Trace("SetStarHighlight...star index found!");
		
			if(isOn)
				COMM_FLAG_STAR_HIGHLIGHTS[i] = '1';
			else
				COMM_FLAG_STAR_HIGHLIGHTS[i] = '0';

		}
	}

	// TESTING: SAVE DATA
	SET_GAME_STATE (STAR_HIGHLIGHTS,fromBinary(COMM_FLAG_STAR_HIGHLIGHTS));


}

//------------------------
// tell map to draw all stars that have been
// referenced (flagged) in the comm
extern void ShowQuestHighlights(){
	
	
	//Trace("ShowQuestHighlights");

	// un-highlight stars for quests we've completed!
	UnhighlightCompletedQuests();
	
	// for each comm flag (referencing a star)...
	for(int i=0; i < NUM_COM_FLAGS; i++){

		// check to see if this star is highlighted
		if(COMM_FLAG_STAR_HIGHLIGHTS[i] == '1'){

			
			// not in quasi-space!
			// tell map to draw a highlight at this star's point
			if(!inQuasiSpace ()){
				
				extern void DrawMapHighlight(POINT newPoint);// SILENCE!
				DrawMapHighlight(GetStar((int)COMM_FLAG_STAR_IDS[i]).star_pt);
			}
		}
	}

}
//------------------------
// return true if the given star (via index) is highlighted
extern int StarIsHighlighted(int newIndex){
	for(int i=0; i < NUM_COM_FLAGS; i++){
		if(newIndex == COMM_FLAG_STAR_IDS[i] && COMM_FLAG_STAR_HIGHLIGHTS[i] == '1')
			return 1;
	}
	return 0;
}
//------------------------
// unhighlight these stars when their quests have been completed
void UnhighlightCompletedQuests(){
	
	
	// met zoq
	if(GET_GAME_STATE (MET_ZOQFOT)){
		//Trace("MET_ZOQFOT");
		
		// zoq scout
		SetStarHighlight(ZOQ_SCOUT_DEFINED,0);
	}
	
	// shfoxi
	if(GET_GAME_STATE (SHOFIXTI_RECRUITED)){
		//Trace("SHOFIXTI_RECRUITED");
		
		// shfoxi homeworld
		SetStarHighlight(SHOFIXTI_DEFINED,0);
	}
	
	// zex
	if(GET_GAME_STATE (ZEX_IS_DEAD)){
		//Trace("ZEX_IS_DEAD");
		
		// zex's base
		SetStarHighlight(MAIDENS_DEFINED,0);
		
		
		// unnecessary?!
		// vux beast location
		//SetStarHighlight(VUX_BEAST_DEFINED,0);
	}
	
	// acquired vux beast
	if(GET_GAME_STATE (VUX_BEAST)){
		//Trace("VUX_BEAST");
	
		// vux beast location
		SetStarHighlight(VUX_BEAST_DEFINED,0);
	}
	
	// acquired taalo device
	if(GET_GAME_STATE (TAALO_PROTECTOR)){
		//Trace("TAALO_PROTECTOR");
		
		// taalo device location
		SetStarHighlight(TAALO_PROTECTOR_DEFINED,0);
	}

	// acquired sun device
	if(GET_GAME_STATE (SUN_DEVICE)){
		//Trace("SUN_DEVICE");
	
		// sun device location
		SetStarHighlight(SUN_DEVICE,0);
	}
	
	
	if(GET_GAME_STATE (BURVIXESE_BROADCASTERS)){
		//Trace("BURVIXESE_BROADCASTERS");
		SetStarHighlight(BURVIXESE_DEFINED,0);
	}
	
	if(GET_GAME_STATE (UTWIG_BOMB)){
		//Trace("UTWIG_BOMB");
		SetStarHighlight(BOMB_DEFINED,0);
	}
	
	if(GET_GAME_STATE (AQUA_HELIX)){
		//Trace("AQUA_HELIX");
		SetStarHighlight(AQUA_HELIX_DEFINED,0);
	}
	
	// acquired urquan wreck
	if(GET_GAME_STATE (PORTAL_KEY)){
		//Trace("PORTAL_KEY");
		SetStarHighlight(URQUAN_WRECK_DEFINED,0);
	}
	
	// hide natural portal to quasi space once we get the spawner
	if(GET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP)){
		//Trace("PORTAL_SPAWNER");
		SetStarHighlight(START_COLONY_DEFINED,0);
	}
	
	// complete mycon ambush quest
	if(GET_GAME_STATE (MYCON_FELL_FOR_AMBUSH)){
		//Trace("MYCON_FELL_FOR_AMBUSH");
		
		// mycon trap location
		SetStarHighlight(MYCON_TRAP_DEFINED,0);
		
		// egg cases
		SetStarHighlight(EGG_CASE0_DEFINED,0);
		SetStarHighlight(EGG_CASE1_DEFINED,0);
		SetStarHighlight(EGG_CASE2_DEFINED,0);
	}
	
	// touched the ship syreen vault
	if(GET_GAME_STATE (SHIP_VAULT_UNLOCKED)){
		//Trace("SHIP_VAULT_UNLOCKED");
		SetStarHighlight(SHIP_VAULT_DEFINED,0);
	}
	

}

/************************************************/
STAR_DESC GetHomeworld(BYTE newRace){
	
	STAR_DESC s;
	for(int i=0; i < NUM_HOMEWORLDS; i++){
		if(newRace == COMM_FLAG_SHIP_IDS[i])
			return s = GetStar((int)COMM_FLAG_STAR_IDS[i]);
	}
	
	return s;
}
//TEST
int GetRaceDistance(int race1, int race2){
	STAR_DESC s1 = GetHomeworld(race1);
	STAR_DESC s2 = GetHomeworld(race2);
	return GetStarDist(s1,s2);
}
//------------------------
extern int CountRaceStars(int homeworldId,int fleetStrength){
	
	
	//Trace("CountRaceStars homeworldId, fleetStrength");
	//TraceNum(homeworldId);
	//TraceNum(fleetStrength);
	
	int numStars = 0;
	
	//long diameter = (long)(fleetStrength * SPHERE_RADIUS_INCREMENT);
	
	// use this for distance
	//int maxDistance = GetRaceStrength(raceId);
	int maxDistance = fleetStrength;	// just aprox this...
	
	
	//maxDistance = maxDistance * 20;
	
	//Trace("Max D");
	//TraceNum(maxDistance);
	
	// get the race's homeworld
	//STAR_DESC homeWorld = GetHomeworld(raceId);
	STAR_DESC homeWorld = GetStar(homeworldId);
	
	/*
	Trace("homeworld x,y");
	TraceNum(homeWorld.star_pt.x);
	TraceNum(homeWorld.star_pt.y);
	*/
	// for all stars
	int fullSize = MAX_NUM_STARS;
	for(int i=0; i < fullSize; i++){

		STAR_DESC s = star_array[i];
	
		int d = GetStarDist(homeWorld,s);
			

		// if withn distance, count++
		if(d < maxDistance){
			numStars++;
			//Trace("CountRaceStars numStars++");	
			//TraceNum(numStars);
		}

	}
	Trace("CountRaceStars numStars");
	TraceNum(numStars);

	return numStars;
}



static int justEntered;
// SKIP the game's beginning "tutorial" quests
extern void SKIP_TUTORIAL(){
	
	// THIS IS JUST FOR TESTING:
	return;
	
	//Trace("PLAYER CONTROLS ARE");
	//TraceNum(PlayerControls[0]);
	//PlayerControls[0] = 4; // force joystick controls!

	//res_PutInteger ("config.player1control", opts->player1);

	//justEntered = 1;	// reset on new game / game load
	justEntered = 0;
	
	// FOR TESTING:
	//equipShip();
	//giveDevices();
	//showSpheres ();
	//debugKeyPressedSynchronous();

	// start with money / fuel
	//GLOBAL_SIS (ResUnits) = 40000;
	//GLOBAL_SIS (ResUnits) = 990000;
	GLOBAL_SIS (FuelOnBoard) = FUEL_TANK_CAPACITY / 1;
	GLOBAL_SIS (CrewEnlisted) = CREW_POD_CAPACITY / 1;

	
	
	// Thrusters:
	int i;
	for (i = 0; i < NUM_DRIVE_SLOTS; i++)
		GLOBAL_SIS (DriveSlots[i]) = FUSION_THRUSTER;

	// Turning jets:
	for (i = 0; i < NUM_JET_SLOTS; i++)
		GLOBAL_SIS (JetSlots[i]) = TURNING_JETS;
	

	// make landers cheap!
	//GLOBAL (ModuleCost[PLANET_LANDER]) = 50 / MODULE_COST_SCALE;
	GLOBAL_SIS (NumLanders) = 5;

	
	// starbase tutorial
	SET_GAME_STATE (RADIOACTIVES_PROVIDED, 1);
	SET_GAME_STATE (STARBASE_VISITED, 1);
	SET_GAME_STATE (MOONBASE_DESTROYED,1);
	SET_GAME_STATE (STARBASE_AVAILABLE, 1);
/*

	// spathi tutorial
	//RevealAlien(SPATHI_SHIP);
	SET_GAME_STATE (KNOW_SPATHI_PASSWORD, 0);
	SET_GAME_STATE (FOUND_PLUTO_SPATHI, 2);
	SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
	//AddEscortShips (SPATHI_SHIP, 1);
*/
	
	SetStarHighlight(SOL_DEFINED,1);	// human homeworld
	
	/*
	// extended starbase dialogue
	SetStarHighlight(TAALO_PROTECTOR_DEFINED,1);// taalo device location
	SetStarHighlight(SHOFIXTI_DEFINED,1);		// shfoxi "homeworld"
	SetStarHighlight(CHMMR_DEFINED,1);			// chrmmr location
	
	
	RevealAlien(VUX_SHIP);
	RevealAlien(MYCON_SHIP);
	RevealAlien(THRADDASH_SHIP);
	RevealAlien(UMGAH_SHIP);	// umgah
	*/
	
	
	// also reveal nearby aliens (just for fun)
	//RevealNearbyAliens();	
	
	//InitCharm();

}



























/************************************************/
/************************************************/
/************************************************/
static void
CalcSunSize (PLANET_DESC *pSunDesc, SIZE radius)
{
	SIZE index = 0;

	if (radius <= (MAX_ZOOM_RADIUS >> 1))
	{
		++index;
		if (radius <= (MAX_ZOOM_RADIUS >> 2))
			++index;
	}

	pSunDesc->image.origin.x = SIS_SCREEN_WIDTH >> 1;
	pSunDesc->image.origin.y = SIS_SCREEN_HEIGHT >> 1;
	pSunDesc->image.frame = SetRelFrameIndex (SunFrame, index);
}

static void
SetPlanetColorMap (PLANET_DESC *planet)
{
	COUNT index = planet->data_index & ~WORLD_TYPE_SPECIAL;
	assert (index < NUMBER_OF_PLANET_TYPES);
	SetColorMap (GetColorMapAddress (SetAbsColorMapIndex (OrbitalCMap,
			PLANCOLOR (PlanData[index].Type))));
}

static void
DrawInnerPlanets (PLANET_DESC *planet)
{
	STAMP s;
	COUNT i;
	PLANET_DESC *moon;

	// Draw the planet image
	SetPlanetColorMap (planet);
	s.origin.x = SIS_SCREEN_WIDTH >> 1;
	s.origin.y = SIS_SCREEN_HEIGHT >> 1;
	s.frame = planet->image.frame;

	i = planet->data_index & ~WORLD_TYPE_SPECIAL;
	if (i < NUMBER_OF_PLANET_TYPES
			&& (planet->data_index & PLANET_SHIELDED))
	{	// Shielded world looks "shielded" in inner view
		s.frame = SetAbsFrameIndex (SpaceJunkFrame, 17);
	}
	DrawStamp (&s);

	// Draw the moon images
	for (i = planet->NumPlanets, moon = pSolarSysState->MoonDesc;
			i; --i, ++moon)
	{
		if (!(moon->data_index & WORLD_TYPE_SPECIAL))
			SetPlanetColorMap (moon);
		DrawStamp (&moon->image);
	}
}

static void
DrawSystem (SIZE radius, BOOLEAN IsInnerSystem)
{
	BYTE i;
	PLANET_DESC *pCurDesc;
	PLANET_DESC *pBaseDesc;
	CONTEXT oldContext;
	STAMP s;

	if (!SolarSysFrame)
	{	// Create the saved view graphic
		RECT clipRect;

		GetContextClipRect (&clipRect);
		SolarSysFrame = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
				clipRect.extent.width, clipRect.extent.height, 1));
	}

	oldContext = SetContext (OffScreenContext);
	SetContextFGFrame (SolarSysFrame);
	SetContextClipRect (NULL);

	DrawStarBackGround ();

	pBaseDesc = pSolarSysState->pBaseDesc;
	if (IsInnerSystem)
	{	// Draw the inner system view *planet's* orbit segment
		pCurDesc = pSolarSysState->pOrbitalDesc;
		DrawOrbit (pCurDesc, DISPLAY_FACTOR * 4, DISPLAY_FACTOR, radius);
	}

	// Draw the planet orbits or moon orbits
	for (i = pBaseDesc->pPrevDesc->NumPlanets, pCurDesc = pBaseDesc;
			i; --i, ++pCurDesc)
	{
		if (IsInnerSystem)
			DrawOrbit (pCurDesc, 2, 1, 2);
		else
			DrawOrbit (pCurDesc, DISPLAY_FACTOR, DISPLAY_FACTOR / 4,
					radius);
	}

	if (IsInnerSystem)
	{	// Draw the inner system view
		DrawInnerPlanets (pSolarSysState->pOrbitalDesc);
	}
	else
	{	// Draw the outer system view
		SIZE index;

		CalcSunSize (&pSolarSysState->SunDesc[0], radius);

		index = pSolarSysState->FirstPlanetIndex;
		for (;;)
		{
			pCurDesc = &pSolarSysState->PlanetDesc[index];
			if (pCurDesc == &pSolarSysState->SunDesc[0])
			{	// It's a sun
				SetColorMap (GetColorMapAddress (SetAbsColorMapIndex (
						SunCMap, STAR_COLOR (CurStarDescPtr->Type))));
			}
			else
			{	// It's a planet
				SetPlanetColorMap (pCurDesc);
			}
			DrawStamp (&pCurDesc->image);

			if (index == pSolarSysState->LastPlanetIndex)
				break;
			index = pCurDesc->NextIndex;
		}
	}

	SetContext (oldContext);

	// Draw the now-saved view graphic
	s.origin.x = 0;
	s.origin.y = 0;
	s.frame = SolarSysFrame;
	DrawStamp (&s);
}

void
DrawStarBackGround (void)
{
	STAMP s;
	
	s.origin.x = 0;
	s.origin.y = 0;
	s.frame = StarsFrame;
	DrawStamp (&s);
}

static FRAME
CreateStarBackGround (void)
{
	COUNT i, j;
	DWORD rand_val;
	STAMP s;
	CONTEXT oldContext;
	RECT clipRect;
	FRAME frame;

	// Use SpaceContext to find out the dimensions of the background
	oldContext = SetContext (SpaceContext);
	GetContextClipRect (&clipRect);

	// Prepare a pre-drawn stars frame for this system
	frame = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
			clipRect.extent.width, clipRect.extent.height, 1));
	SetContext (OffScreenContext);
	SetContextFGFrame (frame);
	SetContextClipRect (NULL);
	SetContextBackGroundColor (BLACK_COLOR);

	ClearDrawable ();

	RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (CurStarDescPtr));

#define NUM_DIM_PIECES 8
	s.frame = SpaceJunkFrame;
	for (i = 0; i < NUM_DIM_PIECES; ++i)
	{
#define NUM_DIM_DRAWN 5
		for (j = 0; j < NUM_DIM_DRAWN; ++j)
		{
			rand_val = RandomContext_Random (SysGenRNG);
			s.origin.x = LOWORD (rand_val) % SIS_SCREEN_WIDTH;
			s.origin.y = HIWORD (rand_val) % SIS_SCREEN_HEIGHT;

			DrawStamp (&s);
		}
		s.frame = IncFrameIndex (s.frame);
	}
#define NUM_BRT_PIECES 8
	for (i = 0; i < NUM_BRT_PIECES; ++i)
	{
#define NUM_BRT_DRAWN 30
		for (j = 0; j < NUM_BRT_DRAWN; ++j)
		{
			rand_val = RandomContext_Random (SysGenRNG);
			s.origin.x = LOWORD (rand_val) % SIS_SCREEN_WIDTH;
			s.origin.y = HIWORD (rand_val) % SIS_SCREEN_HEIGHT;

			DrawStamp (&s);
		}
		s.frame = IncFrameIndex (s.frame);
	}

	SetContext (oldContext);

	return frame;
}

void
XFormIPLoc (POINT *pIn, POINT *pOut, BOOLEAN ToDisplay)
{
	if (ToDisplay)
		*pOut = locationToDisplay (*pIn, pSolarSysState->SunDesc[0].radius);
	else
		*pOut = displayToLocation (*pIn, pSolarSysState->SunDesc[0].radius);
}

void
ExploreSolarSys (void)
{
	SOLARSYS_STATE SolarSysState;
	
	if (CurStarDescPtr == 0)
	{
		POINT universe;

		universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
		universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
		CurStarDescPtr = FindStar (0, &universe, 1, 1);
		if (!CurStarDescPtr)
		{
			log_add (log_Fatal, "ExploreSolarSys(): do not know where you are!");
			explode ();
		}
	}
	GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (CurStarDescPtr->star_pt.x);
	GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (CurStarDescPtr->star_pt.y);

	pSolarSysState = &SolarSysState;

	memset (pSolarSysState, 0, sizeof (*pSolarSysState));

	SolarSysState.genFuncs = getGenerateFunctions (CurStarDescPtr->Index);

	InitSolarSys ();
	SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
	SolarSysState.InputFunc = DoIpFlight;
	DoInput (&SolarSysState, FALSE);
	UninitSolarSys ();
	pSolarSysState = 0;
}

UNICODE *
GetNamedPlanetaryBody (void)
{
	if (!CurStarDescPtr || !playerInSolarSystem () || !playerInInnerSystem ())
		return NULL; // Not inside an inner system, so no name

	assert (pSolarSysState->pOrbitalDesc != NULL);

	if (CurStarDescPtr->Index == SOL_DEFINED)
	{	// Planets and moons in Sol
		int planet;
		int moon;

		planet = planetIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);

		if (worldIsPlanet (pSolarSysState, pSolarSysState->pOrbitalDesc))
		{	// A planet
			return GAME_STRING (PLANET_NUMBER_BASE + planet);
		}

		// Moons
		moon = moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
		switch (planet)
		{
			case 2: // Earth
				switch (moon)
				{
					case 0: // Starbase
						return GAME_STRING (STARBASE_STRING_BASE + 0);
					case 1: // Luna
						return GAME_STRING (PLANET_NUMBER_BASE + 9);
				}
				break;
			case 4: // Jupiter
				switch (moon)
				{
					case 0: // Io
						return GAME_STRING (PLANET_NUMBER_BASE + 10);
					case 1: // Europa
						return GAME_STRING (PLANET_NUMBER_BASE + 11);
					case 2: // Ganymede
						return GAME_STRING (PLANET_NUMBER_BASE + 12);
					case 3: // Callisto
						return GAME_STRING (PLANET_NUMBER_BASE + 13);
				}
				break;
			case 5: // Saturn
				if (moon == 0) // Titan
					return GAME_STRING (PLANET_NUMBER_BASE + 14);
				break;
			case 7: // Neptune
				if (moon == 0) // Triton
					return GAME_STRING (PLANET_NUMBER_BASE + 15);
				break;
		}
	}
	else if (CurStarDescPtr->Index == SPATHI_DEFINED)
	{
		if (matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc,
				0, MATCH_PLANET))
		{
#ifdef NOTYET
			return "Spathiwa";
#endif // NOTYET
		}
	}
	else if (CurStarDescPtr->Index == SAMATRA_DEFINED)
	{
		if (matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc, 4, 0))
		{	// Sa-Matra
			return GAME_STRING (PLANET_NUMBER_BASE + 32);
		}
	}

	return NULL;
}

void
GetPlanetOrMoonName (UNICODE *buf, COUNT bufsize)
{
	UNICODE *named;
	int moon;
	int i;

	named = GetNamedPlanetaryBody ();
	if (named)
	{
		utf8StringCopy (buf, bufsize, named);
		return;
	}
		
	// Either not named or we already have a name
	utf8StringCopy (buf, bufsize, GLOBAL_SIS (PlanetName));

	if (!playerInSolarSystem () || !playerInInnerSystem () ||
			worldIsPlanet (pSolarSysState, pSolarSysState->pOrbitalDesc))
	{	// Outer or inner system or orbiting a planet
		return;
	}

	// Orbiting an unnamed moon
	i = strlen (buf);
	buf += i;
	bufsize -= i;
	moon = moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
	if (bufsize >= 3)
	{
		snprintf (buf, bufsize, "-%c", 'A' + moon);
		buf[bufsize - 1] = '\0';
	}
}

void
SaveSolarSysLocation (void)
{
	assert (playerInSolarSystem ());

	// This is a two-stage saving procedure
	// Stage 1: called when saving from inner/outer view
	// Stage 2: called when saving from orbital

	if (!playerInPlanetOrbit ())
	{
		saveNonOrbitalLocation ();
	}
	else
	{	// In orbit around a planet.
		BYTE moon;

		// Update the starinfo.dat file if necessary.
		if (GET_GAME_STATE (PLANETARY_CHANGE))
		{
			PutPlanetInfo ();
			SET_GAME_STATE (PLANETARY_CHANGE, 0);
		}

		// GLOBAL (ip_planet) is already set
		assert (GLOBAL (ip_planet) != 0);

		// has to be at least 1 because code tests for in_orbit!=0
		moon = 1; /* the planet itself */
		if (worldIsMoon (pSolarSysState, pSolarSysState->pOrbitalDesc))
		{
			moon += moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
			// +1 because moons have to be 1-based
			moon += 1;
		}
		GLOBAL (in_orbit) = moon;
	}
}

static BOOLEAN
DoSolarSysMenu (MENU_STATE *pMS)
{
	BOOLEAN select = PulsedInputState.menu[KEY_MENU_SELECT];
	BOOLEAN handled;

	if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
			|| GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
		return FALSE;

	handled = DoMenuChooser (pMS, PM_STARMAP);
	if (handled)
		return TRUE;

	if (LastActivity == CHECK_LOAD)
		select = TRUE; // Selected LOAD from main menu

	if (!select)
		return TRUE;

	SetFlashRect (NULL);

	switch (pMS->CurState)
	{
		case EQUIP_DEVICE:
			select = DevicesMenu ();
			if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
			{	// Invoked Talking Pet or a Caster for Ilwrath
				// Going into conversation
				return FALSE;
			}
			break;
		case CARGO:
			CargoMenu ();
			break;
		case ROSTER:
			select = RosterMenu ();
			break;
		case GAME_MENU:
			if (!GameOptions ())
				return FALSE; // abort or load
			break;
		case STARMAP:
			StarMap ();
			if (GLOBAL (CurrentActivity) & CHECK_ABORT)
				return FALSE;

			TransitionSystemIn ();
			// Fall through !!!
		case NAVIGATION:
			return FALSE;
	}

	if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
	{
		if (select)
		{	// 3DO menu jumps to NAVIGATE after a successful submenu run
			if (optWhichMenu != OPT_PC)
				pMS->CurState = NAVIGATION;
			DrawMenuStateStrings (PM_STARMAP, pMS->CurState);
		}
		SetFlashRect (SFR_MENU_3DO);
	}

	return TRUE;
}

static void
SolarSysMenu (void)
{
	MENU_STATE MenuState;

	memset (&MenuState, 0, sizeof MenuState);

	if (LastActivity == CHECK_LOAD)
	{	// Selected LOAD from main menu
		MenuState.CurState = GAME_MENU;
	}
	else
	{
		DrawMenuStateStrings (PM_STARMAP, STARMAP);
		MenuState.CurState = STARMAP;
	}

	DrawStatusMessage (NULL);
	SetFlashRect (SFR_MENU_3DO);

	SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
	MenuState.InputFunc = DoSolarSysMenu;
	DoInput (&MenuState, TRUE);

	DrawMenuStateStrings (PM_STARMAP, -NAVIGATION);
}

static BOOLEAN
DoIpFlight (SOLARSYS_STATE *pSS)
{
	static TimeCount NextTime;
	BOOLEAN cancel = PulsedInputState.menu[KEY_MENU_CANCEL];

	if (pSS->InOrbit)
	{	// CheckShipLocation() or InitSolarSys() sent us to orbital
		EnterPlanetOrbit ();
		SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
		pSS->InOrbit = FALSE;
	}
	else if (cancel || LastActivity == CHECK_LOAD)
	{
		SolarSysMenu ();
		SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
	}
	else
	{
		assert (pSS->InIpFlight);
		IP_frame ();
		SleepThreadUntil (NextTime);
		NextTime = GetTimeCounter () + IP_FRAME_RATE;
	}

	return (!(GLOBAL (CurrentActivity)
			& (START_ENCOUNTER | END_INTERPLANETARY
			| CHECK_ABORT | CHECK_LOAD))
			&& GLOBAL_SIS (CrewEnlisted) != (COUNT)~0);
}
