summaryrefslogtreecommitdiff
path: root/src/uqm/planets
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/planets')
-rw-r--r--src/uqm/planets/Makeinfo7
-rw-r--r--src/uqm/planets/calc.c530
-rw-r--r--src/uqm/planets/cargo.c356
-rw-r--r--src/uqm/planets/devices.c690
-rw-r--r--src/uqm/planets/elemdata.h215
-rw-r--r--src/uqm/planets/generate.h110
-rw-r--r--src/uqm/planets/generate/Makeinfo6
-rw-r--r--src/uqm/planets/generate/genall.h27
-rw-r--r--src/uqm/planets/generate/genand.c164
-rw-r--r--src/uqm/planets/generate/genburv.c192
-rw-r--r--src/uqm/planets/generate/genchmmr.c154
-rw-r--r--src/uqm/planets/generate/gencol.c126
-rw-r--r--src/uqm/planets/generate/gendefault.c373
-rw-r--r--src/uqm/planets/generate/gendefault.h66
-rw-r--r--src/uqm/planets/generate/gendru.c169
-rw-r--r--src/uqm/planets/generate/genilw.c150
-rw-r--r--src/uqm/planets/generate/genmel.c114
-rw-r--r--src/uqm/planets/generate/genmyc.c286
-rw-r--r--src/uqm/planets/generate/genorz.c222
-rw-r--r--src/uqm/planets/generate/genpet.c257
-rw-r--r--src/uqm/planets/generate/genpku.c159
-rw-r--r--src/uqm/planets/generate/genrain.c102
-rw-r--r--src/uqm/planets/generate/gensam.c324
-rw-r--r--src/uqm/planets/generate/genshof.c178
-rw-r--r--src/uqm/planets/generate/gensly.c70
-rw-r--r--src/uqm/planets/generate/gensol.c671
-rw-r--r--src/uqm/planets/generate/genspa.c283
-rw-r--r--src/uqm/planets/generate/gensup.c159
-rw-r--r--src/uqm/planets/generate/gensyr.c102
-rw-r--r--src/uqm/planets/generate/genthrad.c217
-rw-r--r--src/uqm/planets/generate/gentrap.c80
-rw-r--r--src/uqm/planets/generate/genutw.c269
-rw-r--r--src/uqm/planets/generate/genvault.c130
-rw-r--r--src/uqm/planets/generate/genvux.c329
-rw-r--r--src/uqm/planets/generate/genwreck.c111
-rw-r--r--src/uqm/planets/generate/genyeh.c140
-rw-r--r--src/uqm/planets/generate/genzfpscout.c96
-rw-r--r--src/uqm/planets/generate/genzoq.c170
-rw-r--r--src/uqm/planets/gentopo.c206
-rw-r--r--src/uqm/planets/lander.c2101
-rw-r--r--src/uqm/planets/lander.h88
-rw-r--r--src/uqm/planets/lifeform.h75
-rw-r--r--src/uqm/planets/orbits.c629
-rw-r--r--src/uqm/planets/oval.c329
-rw-r--r--src/uqm/planets/pl_stuff.c318
-rw-r--r--src/uqm/planets/plandata.h318
-rw-r--r--src/uqm/planets/planets.c483
-rw-r--r--src/uqm/planets/planets.h322
-rw-r--r--src/uqm/planets/plangen.c1954
-rw-r--r--src/uqm/planets/pstarmap.c1631
-rw-r--r--src/uqm/planets/report.c271
-rw-r--r--src/uqm/planets/roster.c428
-rw-r--r--src/uqm/planets/scan.c1385
-rw-r--r--src/uqm/planets/scan.h72
-rw-r--r--src/uqm/planets/solarsys.c2021
-rw-r--r--src/uqm/planets/solarsys.h34
-rw-r--r--src/uqm/planets/sundata.h73
-rw-r--r--src/uqm/planets/surface.c251
58 files changed, 20793 insertions, 0 deletions
diff --git a/src/uqm/planets/Makeinfo b/src/uqm/planets/Makeinfo
new file mode 100644
index 0000000..da4026f
--- /dev/null
+++ b/src/uqm/planets/Makeinfo
@@ -0,0 +1,7 @@
+uqm_SUBDIRS="generate"
+uqm_CFILES="calc.c cargo.c devices.c gentopo.c lander.c orbits.c
+ oval.c pl_stuff.c planets.c plangen.c pstarmap.c report.c
+ roster.c scan.c solarsys.c surface.c"
+uqm_HFILES="elemdata.h generate.h lander.h lifeform.h plandata.h planets.h
+ scan.h solarsys.h sundata.h"
+
diff --git a/src/uqm/planets/calc.c b/src/uqm/planets/calc.c
new file mode 100644
index 0000000..09bd461
--- /dev/null
+++ b/src/uqm/planets/calc.c
@@ -0,0 +1,530 @@
+//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.
+ */
+
+/* ----------------------------- INCLUDES ---------------------------- */
+#include "planets.h"
+#include "uqm/starmap.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+/* -------------------------------- DATA -------------------------------- */
+
+/* -------------------------------- CODE -------------------------------- */
+
+//#define DEBUG_PLANET_CALC
+
+#define LOW_TEMP 0
+#define MED_TEMP 500
+#define HIGH_TEMP 1500
+#define LOW_TEMP_BONUS 10
+#define MED_TEMP_BONUS 25
+#define HIGH_TEMP_BONUS 50
+#define MAX_TECTONICS 255
+
+enum
+{
+ RED_SUN_INTENSITY = 0,
+ ORANGE_SUN_INTENSITY,
+ YELLOW_SUN_INTENSITY,
+ GREEN_SUN_INTENSITY,
+ BLUE_SUN_INTENSITY,
+ WHITE_SUN_INTENSITY
+};
+
+static UWORD
+CalcFromBase (UWORD base, UWORD variance)
+{
+ return base + LOWORD (RandomContext_Random (SysGenRNG)) % variance;
+}
+
+static inline UWORD
+CalcHalfBaseVariance (UWORD base)
+{
+ return CalcFromBase (base, (base >> 1) + 1);
+}
+
+static void
+CalcSysInfo (SYSTEM_INFO *SysInfoPtr)
+{
+ SysInfoPtr->StarSize = pSolarSysState->SunDesc[0].data_index;
+ switch (STAR_COLOR (CurStarDescPtr->Type))
+ {
+ case BLUE_BODY:
+ SysInfoPtr->StarIntensity = BLUE_SUN_INTENSITY;
+ break;
+ case GREEN_BODY:
+ SysInfoPtr->StarIntensity = GREEN_SUN_INTENSITY;
+ break;
+ case ORANGE_BODY:
+ SysInfoPtr->StarIntensity = ORANGE_SUN_INTENSITY;
+ break;
+ case RED_BODY:
+ SysInfoPtr->StarIntensity = RED_SUN_INTENSITY;
+ break;
+ case WHITE_BODY:
+ SysInfoPtr->StarIntensity = WHITE_SUN_INTENSITY;
+ break;
+ case YELLOW_BODY:
+ SysInfoPtr->StarIntensity = YELLOW_SUN_INTENSITY;
+ break;
+ }
+
+ switch (STAR_TYPE (CurStarDescPtr->Type))
+ {
+ case DWARF_STAR:
+ SysInfoPtr->StarEnergy =
+ (SysInfoPtr->StarIntensity + 1) * DWARF_ENERGY;
+ break;
+ case GIANT_STAR:
+ SysInfoPtr->StarEnergy =
+ (SysInfoPtr->StarIntensity + 1) * GIANT_ENERGY;
+ break;
+ case SUPER_GIANT_STAR:
+ SysInfoPtr->StarEnergy =
+ (SysInfoPtr->StarIntensity + 1) * SUPERGIANT_ENERGY;
+ break;
+ }
+}
+
+static UWORD
+GeneratePlanetComposition (PLANET_INFO *PlanetInfoPtr, SIZE SurfaceTemp,
+ SIZE radius)
+{
+ if (PLANSIZE (PlanetInfoPtr->PlanDataPtr->Type) == GAS_GIANT)
+ {
+ PlanetInfoPtr->Weather = 7 << 5;
+ return (GAS_GIANT_ATMOSPHERE);
+ }
+ else
+ {
+ BYTE range;
+ UWORD atmo;
+
+ PlanetInfoPtr->Weather = 0;
+ atmo = 0;
+ if ((range = HINIBBLE (PlanetInfoPtr->PlanDataPtr->AtmoAndDensity)) <= HEAVY)
+ {
+ if (SurfaceTemp < COLD_THRESHOLD)
+ --range;
+ else if (SurfaceTemp > HOT_THRESHOLD)
+ ++range;
+
+ if (range <= HEAVY + 1)
+ {
+ switch (range)
+ {
+ case LIGHT:
+ atmo = THIN_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 1 << 5;
+ break;
+ case MEDIUM:
+ atmo = NORMAL_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 2 << 5;
+ break;
+ case HEAVY:
+ atmo = THICK_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 4 << 5;
+ break;
+ default:
+ atmo = SUPER_THICK_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 6 << 5;
+ break;
+ }
+
+ radius /= EARTH_RADIUS;
+ if (radius < 2)
+ PlanetInfoPtr->Weather += 1 << 5;
+ else if (radius > 10)
+ PlanetInfoPtr->Weather -= 1 << 5;
+ atmo = CalcHalfBaseVariance (atmo);
+ }
+ }
+
+ return (atmo);
+ }
+}
+
+// This function is called both when the solar system is generated,
+// and when planetary orbit is entered.
+// In the former case, the if() block will not be executed,
+// which means that the temperature calculated in that case will be
+// slightly lower. The result is that the orbits may not always
+// have the colour you'd expect based on the true temperature.
+// (eg. Beta Corvi I). I don't know what the idea behind this is,
+// but the if statement must be there for a reason. -- SvdB
+// Update 2013-03-28: The contents of the if() block is probably there to
+// model a greenhouse effect. It seems that it is taken into account when
+// calculating the actual temperature (when landing or scanning), but not
+// when determining the colors of the drawn orbits. (Thanks to James Scott
+// for this insight.)
+static SIZE
+CalcTemp (SYSTEM_INFO *SysInfoPtr, SIZE radius)
+{
+#define GENERIC_ALBEDO 33 /* In %, 0=black, 100 is reflective */
+#define ADJUST_FOR_KELVIN 273
+#define PLANET_TEMP_CONSTANT 277L
+ DWORD alb;
+ SIZE centigrade, bonus;
+
+ alb = 100 - GENERIC_ALBEDO;
+ alb = square_root (square_root (alb * 100 * 10000))
+ * PLANET_TEMP_CONSTANT * SysInfoPtr->StarEnergy
+ / ((YELLOW_SUN_INTENSITY + 1) * DWARF_ENERGY);
+
+ centigrade = (SIZE)(alb / square_root (radius * 10000L / EARTH_RADIUS))
+ - ADJUST_FOR_KELVIN;
+
+ bonus = 0;
+ if (SysInfoPtr == &pSolarSysState->SysInfo
+ && HINIBBLE (SysInfoPtr->PlanetInfo.PlanDataPtr->AtmoAndDensity) <= HEAVY)
+ {
+#define COLD_BONUS 20
+#define HOT_BONUS 200
+ if (centigrade >= HOT_THRESHOLD)
+ bonus = HOT_BONUS;
+ else if (centigrade >= COLD_THRESHOLD)
+ bonus = COLD_BONUS;
+
+ bonus <<= HINIBBLE (SysInfoPtr->PlanetInfo.PlanDataPtr->AtmoAndDensity);
+ bonus = CalcHalfBaseVariance (bonus);
+ }
+
+ return (centigrade + bonus);
+}
+
+static COUNT
+CalcRotation (PLANET_INFO *PlanetInfoPtr)
+{
+ if (PLANSIZE (PlanetInfoPtr->PlanDataPtr->Type) == GAS_GIANT)
+ return CalcFromBase (80, 80);
+ else if (LOBYTE (RandomContext_Random (SysGenRNG)) % 10 == 0)
+ return CalcFromBase (50 * 240, 200 * 240);
+ else
+ return CalcFromBase (150, 150);
+}
+
+static SIZE
+CalcTilt (void)
+{ /* Calculate Axial Tilt */
+ SIZE tilt;
+ BYTE i;
+
+#define NUM_TOSSES 10
+#define TILT_RANGE 180
+ tilt = -(TILT_RANGE / 2);
+ i = NUM_TOSSES;
+ do /* Using added Randomom values to give bell curve */
+ {
+ tilt += LOWORD (RandomContext_Random (SysGenRNG))
+ % ((TILT_RANGE / NUM_TOSSES) + 1);
+ } while (--i);
+
+ return (tilt);
+}
+
+UWORD
+CalcGravity (const PLANET_INFO *PlanetInfoPtr)
+{
+ return (DWORD)PlanetInfoPtr->PlanetDensity * PlanetInfoPtr->PlanetRadius
+ / 100;
+}
+
+static UWORD
+CalcTectonics (UWORD base, UWORD temp)
+{
+ UWORD tect = CalcFromBase (base, 3 << 5);
+#ifdef OLD
+ if (temp >= HIGH_TEMP)
+ tect += HIGH_TEMP_BONUS;
+ else if (temp >= MED_TEMP)
+ tect += MED_TEMP_BONUS;
+ else if (temp >= LOW_TEMP)
+ tect += LOW_TEMP_BONUS;
+#else /* !OLD */
+ (void) temp; /* silence compiler whining */
+#endif /* OLD */
+ return tect;
+}
+
+// This code moved from planets/surface.c:CalcLifeForms()
+static int
+CalcLifeChance (const PLANET_INFO *PlanetInfoPtr)
+{
+ SIZE life_var = 0;
+
+ if (PLANSIZE (PlanetInfoPtr->PlanDataPtr->Type) == GAS_GIANT)
+ return -1;
+
+ if (PlanetInfoPtr->SurfaceTemperature < -151)
+ life_var -= 300;
+ else if (PlanetInfoPtr->SurfaceTemperature < -51)
+ life_var -= 100;
+ else if (PlanetInfoPtr->SurfaceTemperature < 0)
+ life_var += 100;
+ else if (PlanetInfoPtr->SurfaceTemperature < 50)
+ life_var += 300;
+ else if (PlanetInfoPtr->SurfaceTemperature < 150)
+ life_var += 50;
+ else if (PlanetInfoPtr->SurfaceTemperature < 250)
+ life_var -= 100;
+ else if (PlanetInfoPtr->SurfaceTemperature < 500)
+ life_var -= 400;
+ else
+ life_var -= 800;
+
+ if (PlanetInfoPtr->AtmoDensity == 0)
+ life_var -= 1000;
+ else if (PlanetInfoPtr->AtmoDensity < 15)
+ life_var += 100;
+ else if (PlanetInfoPtr->AtmoDensity < 30)
+ life_var += 200;
+ else if (PlanetInfoPtr->AtmoDensity < 100)
+ life_var += 300;
+ else if (PlanetInfoPtr->AtmoDensity < 1000)
+ life_var += 150;
+ else if (PlanetInfoPtr->AtmoDensity < 2500)
+ ;
+ else
+ life_var -= 100;
+
+#ifndef NOTYET
+ life_var += 200 + 80 + 80;
+#else /* NOTYET */
+ if (PlanetInfoPtr->SurfaceGravity < 10)
+ ;
+ else if (PlanetInfoPtr->SurfaceGravity < 35)
+ life_var += 50;
+ else if (PlanetInfoPtr->SurfaceGravity < 75)
+ life_var += 100;
+ else if (PlanetInfoPtr->SurfaceGravity < 150)
+ life_var += 200;
+ else if (PlanetInfoPtr->SurfaceGravity < 400)
+ life_var += 50;
+ else if (PlanetInfoPtr->SurfaceGravity < 800)
+ ;
+ else
+ life_var -= 100;
+
+ if (PlanetInfoPtr->Tectonics < 1)
+ life_var += 80;
+ else if (PlanetInfoPtr->Tectonics < 2)
+ life_var += 70;
+ else if (PlanetInfoPtr->Tectonics < 3)
+ life_var += 60;
+ else if (PlanetInfoPtr->Tectonics < 4)
+ life_var += 50;
+ else if (PlanetInfoPtr->Tectonics < 5)
+ life_var += 25;
+ else if (PlanetInfoPtr->Tectonics < 6)
+ ;
+ else
+ life_var -= 100;
+
+ if (PlanetInfoPtr->Weather < 1)
+ life_var += 80;
+ else if (PlanetInfoPtr->Weather < 2)
+ life_var += 70;
+ else if (PlanetInfoPtr->Weather < 3)
+ life_var += 60;
+ else if (PlanetInfoPtr->Weather < 4)
+ life_var += 50;
+ else if (PlanetInfoPtr->Weather < 5)
+ life_var += 25;
+ else if (PlanetInfoPtr->Weather < 6)
+ ;
+ else
+ life_var -= 100;
+#endif /* NOTYET */
+
+ return life_var;
+}
+
+// Sets the SysGenRNG to the required state first.
+void
+DoPlanetaryAnalysis (SYSTEM_INFO *SysInfoPtr, PLANET_DESC *pPlanetDesc)
+{
+ assert ((pPlanetDesc->data_index & ~WORLD_TYPE_SPECIAL)
+ < NUMBER_OF_PLANET_TYPES);
+
+ RandomContext_SeedRandom (SysGenRNG, pPlanetDesc->rand_seed);
+
+ CalcSysInfo (SysInfoPtr);
+
+#ifdef DEBUG_PLANET_CALC
+ {
+ BYTE ColorClass[6][8] = {
+ "Red",
+ "Orange",
+ "Yellow",
+ "Green",
+ "Blue",
+ "White",
+ };
+ BYTE SizeName[3][12] = {
+ "Dwarf",
+ "Giant",
+ "Supergiant",
+ };
+
+ log_add (log_Debug, "%s %s",
+ ColorClass[SysInfoPtr->StarIntensity],
+ SizeName[SysInfoPtr->StarSize]);
+ log_add (log_Debug, "Stellar Energy: %d (sol = 3)",
+ SysInfoPtr->StarEnergy);
+ }
+#endif /* DEBUG_PLANET_CALC */
+
+ {
+ SIZE radius;
+
+ SysInfoPtr->PlanetInfo.PlanDataPtr =
+ &PlanData[pPlanetDesc->data_index & ~PLANET_SHIELDED];
+
+ if (pPlanetDesc->pPrevDesc == pSolarSysState->SunDesc)
+ radius = pPlanetDesc->radius;
+ else
+ radius = pPlanetDesc->pPrevDesc->radius;
+ SysInfoPtr->PlanetInfo.PlanetToSunDist = radius;
+
+ SysInfoPtr->PlanetInfo.SurfaceTemperature =
+ CalcTemp (SysInfoPtr, radius);
+ switch (LONIBBLE (SysInfoPtr->PlanetInfo.PlanDataPtr->AtmoAndDensity))
+ {
+ case GAS_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 20;
+ break;
+ case LIGHT_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 33;
+ break;
+ case LOW_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 60;
+ break;
+ case NORMAL_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 100;
+ break;
+ case HIGH_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 150;
+ break;
+ case SUPER_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 200;
+ break;
+ }
+ SysInfoPtr->PlanetInfo.PlanetDensity +=
+ (SysInfoPtr->PlanetInfo.PlanetDensity / 20)
+ - (LOWORD (RandomContext_Random (SysGenRNG))
+ % (SysInfoPtr->PlanetInfo.PlanetDensity / 10));
+
+ switch (PLANSIZE (SysInfoPtr->PlanetInfo.PlanDataPtr->Type))
+ {
+ case SMALL_ROCKY_WORLD:
+#define SMALL_RADIUS 25
+ SysInfoPtr->PlanetInfo.PlanetRadius = CalcHalfBaseVariance (SMALL_RADIUS);
+ break;
+ case LARGE_ROCKY_WORLD:
+#define LARGE_RADIUS 75
+ SysInfoPtr->PlanetInfo.PlanetRadius = CalcHalfBaseVariance (LARGE_RADIUS);
+ break;
+ case GAS_GIANT:
+#define MIN_GAS_RADIUS 300
+#define MAX_GAS_RADIUS 1500
+ SysInfoPtr->PlanetInfo.PlanetRadius =
+ CalcFromBase (MIN_GAS_RADIUS, MAX_GAS_RADIUS - MIN_GAS_RADIUS);
+ break;
+ }
+
+ SysInfoPtr->PlanetInfo.RotationPeriod = CalcRotation (&SysInfoPtr->PlanetInfo);
+ SysInfoPtr->PlanetInfo.SurfaceGravity = CalcGravity (&SysInfoPtr->PlanetInfo);
+ SysInfoPtr->PlanetInfo.AxialTilt = CalcTilt ();
+ if ((SysInfoPtr->PlanetInfo.Tectonics =
+ CalcTectonics (SysInfoPtr->PlanetInfo.PlanDataPtr->BaseTectonics,
+ SysInfoPtr->PlanetInfo.SurfaceTemperature)) > MAX_TECTONICS)
+ SysInfoPtr->PlanetInfo.Tectonics = MAX_TECTONICS;
+
+ SysInfoPtr->PlanetInfo.AtmoDensity =
+ GeneratePlanetComposition (&SysInfoPtr->PlanetInfo,
+ SysInfoPtr->PlanetInfo.SurfaceTemperature, radius);
+
+ SysInfoPtr->PlanetInfo.Tectonics >>= 5;
+ SysInfoPtr->PlanetInfo.Weather >>= 5;
+
+ SysInfoPtr->PlanetInfo.LifeChance = CalcLifeChance (&SysInfoPtr->PlanetInfo);
+
+#ifdef DEBUG_PLANET_CALC
+ radius = (SIZE)((DWORD)UNSCALE_RADIUS (radius) * 100 / UNSCALE_RADIUS (EARTH_RADIUS));
+ log_add (log_Debug, "\tOrbital Distance : %d.%02d AU", radius / 100, radius % 100);
+ //log_add (log_Debug, "\tPlanetary Mass : %d.%02d Earth masses",
+ // SysInfoPtr->PlanetInfo.PlanetMass / 100,
+ // SysInfoPtr->PlanetInfo.PlanetMass % 100);
+ log_add (log_Debug, "\tPlanetary Radius : %d.%02d Earth radii",
+ SysInfoPtr->PlanetInfo.PlanetRadius / 100,
+ SysInfoPtr->PlanetInfo.PlanetRadius % 100);
+ log_add (log_Debug, "\tSurface Gravity: %d.%02d gravities",
+ SysInfoPtr->PlanetInfo.SurfaceGravity / 100,
+ SysInfoPtr->PlanetInfo.SurfaceGravity % 100);
+ log_add (log_Debug, "\tSurface Temperature: %d degrees C",
+ SysInfoPtr->PlanetInfo.SurfaceTemperature );
+ log_add (log_Debug, "\tAxial Tilt : %d degrees",
+ abs (SysInfoPtr->PlanetInfo.AxialTilt));
+ log_add (log_Debug, "\tTectonics : Class %u",
+ SysInfoPtr->PlanetInfo.Tectonics + 1);
+ log_add (log_Debug, "\tAtmospheric Density: %u.%02u",
+ SysInfoPtr->PlanetInfo.AtmoDensity / EARTH_ATMOSPHERE,
+ (SysInfoPtr->PlanetInfo.AtmoDensity * 100 / EARTH_ATMOSPHERE) % 100);
+ if (SysInfoPtr->PlanetInfo.AtmoDensity == 0)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Vacuum)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < THIN_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Thin)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < NORMAL_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Normal)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < THICK_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Thick)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < SUPER_THICK_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Super thick)");
+ }
+ else
+ {
+ log_add (log_Debug, "\tAtmosphere: (Gas Giant)");
+ }
+
+ log_add (log_Debug, "\tWeather : Class %u",
+ SysInfoPtr->PlanetInfo.Weather + 1);
+
+ if (SysInfoPtr->PlanetInfo.RotationPeriod >= 480)
+ {
+ log_add (log_Debug, "\tLength of day : %d.%d Earth days",
+ SysInfoPtr->PlanetInfo.RotationPeriod / 240,
+ SysInfoPtr->PlanetInfo.RotationPeriod % 240);
+ }
+ else
+ {
+ log_add (log_Debug, "\tLength of day : %d.%d Earth hours",
+ SysInfoPtr->PlanetInfo.RotationPeriod / 10,
+ SysInfoPtr->PlanetInfo.RotationPeriod % 10);
+ }
+#endif /* DEBUG_PLANET_CALC */
+ }
+}
+
diff --git a/src/uqm/planets/cargo.c b/src/uqm/planets/cargo.c
new file mode 100644
index 0000000..147d95a
--- /dev/null
+++ b/src/uqm/planets/cargo.c
@@ -0,0 +1,356 @@
+//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 "../colors.h"
+#include "../controls.h"
+#include "../gamestr.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../util.h"
+#include "../sis.h"
+ // for ClearSISRect(), DrawStatusMessage()
+#include "planets.h"
+#include "libs/graphics/drawable.h"
+ // for GetFrameBounds()
+
+
+#define ELEMENT_ORG_Y 35
+#define FREE_ORG_Y (ELEMENT_ORG_Y + (NUM_ELEMENT_CATEGORIES \
+ * ELEMENT_SPACING_Y))
+#define BIO_ORG_Y 119
+#define ELEMENT_SPACING_Y 9
+
+#define ELEMENT_COL_0 7
+#define ELEMENT_COL_1 32
+#define ELEMENT_COL_2 58
+
+#define ELEMENT_SEL_ORG_X (ELEMENT_COL_0 + 7 + 5)
+#define ELEMENT_SEL_WIDTH (ELEMENT_COL_2 - ELEMENT_SEL_ORG_X + 1)
+
+#define TEXT_BASELINE 6
+
+
+void
+ShowRemainingCapacity (void)
+{
+ RECT r;
+ TEXT t;
+ CONTEXT OldContext;
+ UNICODE buf[40];
+
+ OldContext = SetContext (StatusContext);
+ SetContextFont (TinyFont);
+
+ r.corner.x = 40;
+ r.corner.y = FREE_ORG_Y;
+
+ snprintf (buf, sizeof buf, "%u",
+ GetStorageBayCapacity () - GLOBAL_SIS (TotalElementMass));
+ t.baseline.x = ELEMENT_COL_2 + 1;
+ t.baseline.y = r.corner.y + TEXT_BASELINE;
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ r.extent.width = t.baseline.x - r.corner.x + 1;
+ r.extent.height = ELEMENT_SPACING_Y - 2;
+
+ BatchGraphics ();
+ // erase previous free amount
+ SetContextForeGroundColor (CARGO_BACK_COLOR);
+ DrawFilledRectangle (&r);
+ // print the new free amount
+ SetContextForeGroundColor (CARGO_WORTH_COLOR);
+ font_DrawText (&t);
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static void
+DrawElementAmount (COUNT element, bool selected)
+{
+ RECT r;
+ TEXT t;
+ UNICODE buf[40];
+
+ r.corner.x = ELEMENT_SEL_ORG_X;
+ r.extent.width = ELEMENT_SEL_WIDTH;
+ r.extent.height = ELEMENT_SPACING_Y - 2;
+
+ if (element == NUM_ELEMENT_CATEGORIES)
+ r.corner.y = BIO_ORG_Y;
+ else
+ r.corner.y = ELEMENT_ORG_Y + (element * ELEMENT_SPACING_Y);
+
+ // draw line background
+ SetContextForeGroundColor (selected ?
+ CARGO_SELECTED_BACK_COLOR : CARGO_BACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+ t.baseline.y = r.corner.y + TEXT_BASELINE;
+
+ if (element == NUM_ELEMENT_CATEGORIES)
+ { // Bio
+ snprintf (buf, sizeof buf, "%u", GLOBAL_SIS (TotalBioMass));
+ }
+ else
+ { // Element
+ // print element's worth
+ SetContextForeGroundColor (selected ?
+ CARGO_SELECTED_WORTH_COLOR : CARGO_WORTH_COLOR);
+ t.baseline.x = ELEMENT_COL_1;
+ snprintf (buf, sizeof buf, "%u", GLOBAL (ElementWorth[element]));
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ snprintf (buf, sizeof buf, "%u", GLOBAL_SIS (ElementAmounts[element]));
+ }
+
+ // print the element/bio amount
+ SetContextForeGroundColor (selected ?
+ CARGO_SELECTED_AMOUNT_COLOR : CARGO_AMOUNT_COLOR);
+ t.baseline.x = ELEMENT_COL_2;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+DrawCargoDisplay (void)
+{
+ STAMP s;
+ TEXT t;
+ RECT r;
+ COORD cy;
+ COUNT i;
+
+ r.corner.x = 2;
+ r.extent.width = FIELD_WIDTH + 1;
+ r.corner.y = 20;
+ // XXX: Shouldn't the height be 1 less? This draws the bottom border
+ // 1 pixel too low. Or if not, why do we need another box anyway?
+ r.extent.height = 129 - r.corner.y;
+ DrawStarConBox (&r, 1,
+ SHADOWBOX_MEDIUM_COLOR, SHADOWBOX_DARK_COLOR,
+ TRUE, CARGO_BACK_COLOR);
+
+ // draw the "CARGO" title
+ SetContextFont (StarConFont);
+ t.baseline.x = (STATUS_WIDTH >> 1) - 1;
+ t.baseline.y = 27;
+ t.align = ALIGN_CENTER;
+ t.pStr = GAME_STRING (CARGO_STRING_BASE);
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (CARGO_SELECTED_AMOUNT_COLOR);
+ font_DrawText (&t);
+
+ SetContextFont (TinyFont);
+
+ s.frame = SetAbsFrameIndex (MiscDataFrame,
+ (NUM_SCANDOT_TRANSITIONS * 2) + 3);
+ r.corner.x = ELEMENT_COL_0;
+ r.extent = GetFrameBounds (s.frame);
+ s.origin.x = r.corner.x + (r.extent.width >> 1);
+
+ cy = ELEMENT_ORG_Y;
+
+ // print element column headings
+ t.align = ALIGN_RIGHT;
+ t.baseline.y = cy - 1;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (CARGO_WORTH_COLOR);
+ t.baseline.x = ELEMENT_COL_1;
+ t.pStr = "$";
+ font_DrawText (&t);
+
+ t.baseline.x = ELEMENT_COL_2;
+ t.pStr = "#";
+ font_DrawText (&t);
+
+ // draw element icons and print amounts
+ for (i = 0; i < NUM_ELEMENT_CATEGORIES; ++i, cy += ELEMENT_SPACING_Y)
+ {
+ // erase background under an element icon
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.y = cy;
+ DrawFilledRectangle (&r);
+
+ // draw an element icon
+ s.origin.y = r.corner.y + (r.extent.height >> 1);
+ DrawStamp (&s);
+ s.frame = SetRelFrameIndex (s.frame, 5);
+
+ DrawElementAmount (i, false);
+ }
+
+ // erase background under the Bio icon
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.y = BIO_ORG_Y;
+ DrawFilledRectangle (&r);
+
+ // draw the Bio icon
+ s.origin.y = r.corner.y + (r.extent.height >> 1);
+ s.frame = SetAbsFrameIndex (s.frame, 68);
+ DrawStamp (&s);
+
+ // print the Bio amount
+ DrawElementAmount (NUM_ELEMENT_CATEGORIES, false);
+
+ // draw the line over the Bio amount
+ r.corner.x = 4;
+ r.corner.y = BIO_ORG_Y - 2;
+ r.extent.width = FIELD_WIDTH - 3;
+ r.extent.height = 1;
+ SetContextForeGroundColor (CARGO_SELECTED_BACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ // print "Free"
+ t.baseline.x = 5;
+ t.baseline.y = FREE_ORG_Y + TEXT_BASELINE;
+ t.align = ALIGN_LEFT;
+ t.pStr = GAME_STRING (CARGO_STRING_BASE + 1);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ ShowRemainingCapacity ();
+}
+
+void
+DrawCargoStrings (BYTE OldElement, BYTE NewElement)
+{
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ SetContextFont (TinyFont);
+
+ BatchGraphics ();
+
+ if (OldElement > NUM_ELEMENT_CATEGORIES)
+ { // Asked for the initial display
+ DrawCargoDisplay ();
+
+ // do not draw unselected again this time
+ OldElement = NewElement;
+ }
+
+ if (OldElement != NewElement)
+ { // unselect the previous element
+ DrawElementAmount (OldElement, false);
+ }
+
+ if (NewElement != (BYTE)~0)
+ { // select the new element
+ DrawElementAmount (NewElement, true);
+ }
+
+ UnbatchGraphics ();
+ SetContext (OldContext);
+}
+
+static void
+DrawElementDescription (COUNT element)
+{
+ DrawStatusMessage (GAME_STRING (element + (CARGO_STRING_BASE + 2)));
+}
+
+static BOOLEAN
+DoDiscardCargo (MENU_STATE *pMS)
+{
+ BYTE NewState;
+ BOOLEAN select, cancel, back, forward;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+ back = PulsedInputState.menu[KEY_MENU_UP] || PulsedInputState.menu[KEY_MENU_LEFT];
+ forward = PulsedInputState.menu[KEY_MENU_DOWN] || PulsedInputState.menu[KEY_MENU_RIGHT];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (cancel)
+ {
+ return FALSE;
+ }
+ else if (select)
+ {
+ if (GLOBAL_SIS (ElementAmounts[pMS->CurState]))
+ {
+ --GLOBAL_SIS (ElementAmounts[pMS->CurState]);
+ DrawCargoStrings (pMS->CurState, pMS->CurState);
+
+ --GLOBAL_SIS (TotalElementMass);
+ ShowRemainingCapacity ();
+ }
+ else
+ { // no element left in cargo hold
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ else
+ {
+ NewState = pMS->CurState;
+ if (back)
+ {
+ if (NewState == 0)
+ NewState += NUM_ELEMENT_CATEGORIES;
+ --NewState;
+ }
+ else if (forward)
+ {
+ ++NewState;
+ if (NewState == NUM_ELEMENT_CATEGORIES)
+ NewState = 0;
+ }
+
+ if (NewState != pMS->CurState)
+ {
+ DrawCargoStrings (pMS->CurState, NewState);
+ DrawElementDescription (NewState);
+ pMS->CurState = NewState;
+ }
+ }
+
+ SleepThread (ONE_SECOND / 30);
+
+ return (TRUE);
+}
+
+void
+CargoMenu (void)
+{
+ MENU_STATE MenuState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ // draw the initial cargo display
+ DrawCargoStrings ((BYTE)~0, MenuState.CurState);
+ DrawElementDescription (MenuState.CurState);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoDiscardCargo;
+ DoInput (&MenuState, TRUE);
+
+ // erase the cargo display
+ ClearSISRect (DRAW_SIS_DISPLAY);
+}
+
diff --git a/src/uqm/planets/devices.c b/src/uqm/planets/devices.c
new file mode 100644
index 0000000..e781d5b
--- /dev/null
+++ b/src/uqm/planets/devices.c
@@ -0,0 +1,690 @@
+//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 "../build.h"
+#include "../colors.h"
+#include "../gendef.h"
+#include "../starmap.h"
+#include "../encount.h"
+#include "../gamestr.h"
+#include "../controls.h"
+#include "../save.h"
+#include "../settings.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../state.h"
+#include "../sis.h"
+ // for ClearSISRect()
+#include "../grpinfo.h"
+#include "../sounds.h"
+#include "../util.h"
+#include "../hyper.h"
+ // for SaveSisHyperState()
+#include "planets.h"
+ // for SaveSolarSysLocation() and tests
+#include "libs/strlib.h"
+
+
+// If DEBUG_DEVICES is defined, the device list shown in the game will
+// include the pictures of all devices defined, regardless of which
+// devices the player actually possesses.
+//#define DEBUG_DEVICES
+
+#define DEVICE_ICON_WIDTH 16
+#define DEVICE_ICON_HEIGHT 16
+
+#define DEVICE_ORG_Y 33
+#define DEVICE_SPACING_Y (DEVICE_ICON_HEIGHT + 2)
+
+#define DEVICE_COL_0 4
+#define DEVICE_COL_1 40
+
+#define DEVICE_SEL_ORG_X (DEVICE_COL_0 + DEVICE_ICON_WIDTH)
+#define DEVICE_SEL_WIDTH (FIELD_WIDTH + 1 - DEVICE_SEL_ORG_X + 1)
+
+#define ICON_OFS_Y 1
+#define NAME_OFS_Y 2
+#define TEXT_BASELINE 6
+#define TEXT_SPACING_Y 7
+
+#define MAX_VIS_DEVICES ((129 - DEVICE_ORG_Y) / DEVICE_SPACING_Y)
+
+
+typedef enum
+{
+ DEVICE_FAILURE = 0,
+ DEVICE_SUCCESS,
+ DEVICE_SUCCESS_NO_SOUND,
+} DeviceStatus;
+
+typedef struct
+{
+ BYTE list[NUM_DEVICES];
+ // List of all devices player has
+ COUNT count;
+ // Number of devices in the list
+ COUNT topIndex;
+ // Index of the top device displayed
+} DEVICES_STATE;
+
+
+#if 0
+static void
+EraseDevicesBackground (void)
+{
+ RECT r;
+
+ r.corner.x = 2 + 1;
+ r.extent.width = FIELD_WIDTH + 1 - 2;
+ r.corner.y = DEVICE_ORG_Y;
+ r.extent.height = MAX_VIS_DEVICES * DEVICE_SPACING_Y;
+ SetContextForeGroundColor (DEVICES_BACK_COLOR);
+ DrawFilledRectangle (&r);
+}
+#endif
+
+static void
+DrawDevice (COUNT device, COUNT pos, bool selected)
+{
+ RECT r;
+ TEXT t;
+
+ t.align = ALIGN_CENTER;
+ t.baseline.x = DEVICE_COL_1;
+
+ r.extent.width = DEVICE_SEL_WIDTH;
+ r.extent.height = TEXT_SPACING_Y * 2;
+ r.corner.x = DEVICE_SEL_ORG_X;
+
+ // draw line background
+ r.corner.y = DEVICE_ORG_Y + pos * DEVICE_SPACING_Y + NAME_OFS_Y;
+ SetContextForeGroundColor (selected ?
+ DEVICES_SELECTED_BACK_COLOR : DEVICES_BACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ SetContextFont (TinyFont);
+
+ // print device name
+ SetContextForeGroundColor (selected ?
+ DEVICES_SELECTED_NAME_COLOR : DEVICES_NAME_COLOR);
+ t.baseline.y = r.corner.y + TEXT_BASELINE;
+ t.pStr = GAME_STRING (device + DEVICE_STRING_BASE + 1);
+ t.CharCount = utf8StringPos (t.pStr, ' ');
+ font_DrawText (&t);
+ t.baseline.y += TEXT_SPACING_Y;
+ t.pStr = skipUTF8Chars (t.pStr, t.CharCount + 1);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+DrawDevicesDisplay (DEVICES_STATE *devState)
+{
+ TEXT t;
+ RECT r;
+ STAMP s;
+ COORD cy;
+ COUNT i;
+
+ r.corner.x = 2;
+ r.corner.y = 20;
+ r.extent.width = FIELD_WIDTH + 1;
+ // XXX: Shouldn't the height be 1 less? This draws the bottom border
+ // 1 pixel too low. Or if not, why do we need another box anyway?
+ r.extent.height = 129 - r.corner.y;
+ DrawStarConBox (&r, 1,
+ SHADOWBOX_MEDIUM_COLOR, SHADOWBOX_DARK_COLOR,
+ TRUE, DEVICES_BACK_COLOR);
+
+ // print the "DEVICES" title
+ SetContextFont (StarConFont);
+ t.baseline.x = (STATUS_WIDTH >> 1) - 1;
+ t.baseline.y = r.corner.y + 7;
+ t.align = ALIGN_CENTER;
+ t.pStr = GAME_STRING (DEVICE_STRING_BASE);
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (DEVICES_SELECTED_NAME_COLOR);
+ font_DrawText (&t);
+
+ s.origin.x = DEVICE_COL_0;
+ cy = DEVICE_ORG_Y;
+
+ // draw device icons and print names
+ for (i = 0; i < MAX_VIS_DEVICES; ++i, cy += DEVICE_SPACING_Y)
+ {
+ COUNT devIndex = devState->topIndex + i;
+
+ if (devIndex >= devState->count)
+ break;
+
+ // draw device icon
+ s.origin.y = cy + ICON_OFS_Y;
+ s.frame = SetAbsFrameIndex (MiscDataFrame,
+ 77 + devState->list[devIndex]);
+ DrawStamp (&s);
+
+ DrawDevice (devState->list[devIndex], i, false);
+ }
+}
+
+static void
+DrawDevices (DEVICES_STATE *devState, COUNT OldDevice, COUNT NewDevice)
+{
+ BatchGraphics ();
+
+ SetContext (StatusContext);
+
+ if (OldDevice > NUM_DEVICES)
+ { // Asked for the initial display or refresh
+ DrawDevicesDisplay (devState);
+
+ // do not draw unselected again this time
+ OldDevice = NewDevice;
+ }
+
+ if (OldDevice != NewDevice)
+ { // unselect the previous element
+ DrawDevice (devState->list[OldDevice], OldDevice - devState->topIndex,
+ false);
+ }
+
+ if (NewDevice < NUM_DEVICES)
+ { // select the new element
+ DrawDevice (devState->list[NewDevice], NewDevice - devState->topIndex,
+ true);
+ }
+
+ UnbatchGraphics ();
+}
+
+// Returns TRUE if the broadcaster has been successfully activated,
+// and FALSE otherwise.
+static BOOLEAN
+UseCaster (void)
+{
+ if (inHQSpace ())
+ {
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ SET_GAME_STATE (USED_BROADCASTER, 1);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != IN_INTERPLANETARY
+ || !playerInSolarSystem ())
+ return FALSE;
+
+ if (playerInPlanetOrbit ()
+ && matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc,
+ 1, MATCH_PLANET)
+ && CurStarDescPtr->Index == CHMMR_DEFINED
+ && !GET_GAME_STATE (CHMMR_UNLEASHED))
+ {
+ // In orbit around the Chenjesu/Mmrnmhrm home planet.
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+
+ EncounterGroup = 0;
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ SaveSolarSysLocation ();
+ return TRUE;
+ }
+
+ {
+ BOOLEAN FoundIlwrath;
+ HIPGROUP hGroup;
+
+ FoundIlwrath = (CurStarDescPtr->Index == ILWRATH_DEFINED)
+ && StartSphereTracking (ILWRATH_SHIP);
+ // In the Ilwrath home system and they are alive?
+
+ if (!FoundIlwrath &&
+ (hGroup = GetHeadLink (&GLOBAL (ip_group_q))))
+ {
+ // Is an Ilwrath ship in the system?
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ FoundIlwrath = (GroupPtr->race_id == ILWRATH_SHIP);
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+
+ if (FoundIlwrath)
+ {
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+
+ EncounterGroup = 0;
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (CurStarDescPtr->Index == ILWRATH_DEFINED)
+ {
+ // Ilwrath home system.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 4);
+ }
+ else
+ {
+ // Ilwrath ship.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 5);
+ }
+
+ if (playerInPlanetOrbit ())
+ SaveSolarSysLocation ();
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static DeviceStatus
+InvokeDevice (BYTE which_device)
+{
+ BYTE val;
+
+ switch (which_device)
+ {
+ case ROSY_SPHERE_DEVICE:
+ val = GET_GAME_STATE (ULTRON_CONDITION);
+ if (val)
+ {
+ SET_GAME_STATE (ULTRON_CONDITION, val + 1);
+ SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 0);
+ SET_GAME_STATE (DISCUSSED_ULTRON, 0);
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case ARTIFACT_2_DEVICE:
+ break;
+ case ARTIFACT_3_DEVICE:
+ break;
+ case SUN_EFFICIENCY_DEVICE:
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && playerInPlanetOrbit ())
+ {
+ PlayMenuSound (MENU_SOUND_INVOKED);
+ SleepThreadUntil (FadeScreen (FadeAllToWhite, ONE_SECOND * 1)
+ + (ONE_SECOND * 2));
+ if (CurStarDescPtr->Index != CHMMR_DEFINED
+ || !matchWorld (pSolarSysState,
+ pSolarSysState->pOrbitalDesc,
+ 1, MATCH_PLANET))
+ {
+ FadeScreen (FadeAllToColor, ONE_SECOND * 2);
+ }
+ else
+ {
+ SET_GAME_STATE (CHMMR_EMERGING, 1);
+
+ EncounterGroup = 0;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (CHMMR_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ return DEVICE_SUCCESS_NO_SOUND;
+ }
+ break;
+ case UTWIG_BOMB_DEVICE:
+ SET_GAME_STATE (UTWIG_BOMB, 0);
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
+ return DEVICE_SUCCESS;
+ case ULTRON_0_DEVICE:
+ break;
+ case ULTRON_1_DEVICE:
+ break;
+ case ULTRON_2_DEVICE:
+ break;
+ case ULTRON_3_DEVICE:
+ break;
+ case MAIDENS_DEVICE:
+ break;
+ case TALKING_PET_DEVICE:
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 0);
+ if (inHQSpace ())
+ {
+ if (GetHeadEncounter ())
+ {
+ SET_GAME_STATE (SHIP_TO_COMPEL, 1);
+ }
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+
+ SaveSisHyperState ();
+ }
+ else
+ {
+ EncounterGroup = 0;
+ if (GetHeadLink (&GLOBAL (ip_group_q)))
+ {
+ SET_GAME_STATE (SHIP_TO_COMPEL, 1);
+
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+ }
+
+ if (CurStarDescPtr->Index == SAMATRA_DEFINED)
+ {
+ SET_GAME_STATE (READY_TO_CONFUSE_URQUAN, 1);
+ }
+ if (playerInPlanetOrbit ())
+ SaveSolarSysLocation ();
+ }
+ return DEVICE_SUCCESS;
+ case AQUA_HELIX_DEVICE:
+ val = GET_GAME_STATE (ULTRON_CONDITION);
+ if (val)
+ {
+ SET_GAME_STATE (ULTRON_CONDITION, val + 1);
+ SET_GAME_STATE (AQUA_HELIX_ON_SHIP, 0);
+ SET_GAME_STATE (DISCUSSED_ULTRON, 0);
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case CLEAR_SPINDLE_DEVICE:
+ val = GET_GAME_STATE (ULTRON_CONDITION);
+ if (val)
+ {
+ SET_GAME_STATE (ULTRON_CONDITION, val + 1);
+ SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 0);
+ SET_GAME_STATE (DISCUSSED_ULTRON, 0);
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case UMGAH_HYPERWAVE_DEVICE:
+ case BURVIX_HYPERWAVE_DEVICE:
+ if (UseCaster ())
+ return DEVICE_SUCCESS;
+ break;
+ case TAALO_PROTECTOR_DEVICE:
+ break;
+ case EGG_CASING0_DEVICE:
+ case EGG_CASING1_DEVICE:
+ case EGG_CASING2_DEVICE:
+ break;
+ case SYREEN_SHUTTLE_DEVICE:
+ break;
+ case VUX_BEAST_DEVICE:
+ break;
+ case DESTRUCT_CODE_DEVICE:
+ break;
+ case PORTAL_SPAWNER_DEVICE:
+#define PORTAL_FUEL_COST (10 * FUEL_TANK_SCALE)
+ if (inHyperSpace ()
+ && GLOBAL_SIS (FuelOnBoard) >= PORTAL_FUEL_COST)
+ {
+ /* No DeltaSISGauges because the flagship picture
+ * is currently obscured.
+ */
+ GLOBAL_SIS (FuelOnBoard) -= PORTAL_FUEL_COST;
+ SET_GAME_STATE (PORTAL_COUNTER, 1);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case URQUAN_WARP_DEVICE:
+ break;
+ case LUNAR_BASE_DEVICE:
+ break;
+ }
+
+ return DEVICE_FAILURE;
+}
+
+static BOOLEAN
+DoManipulateDevices (MENU_STATE *pMS)
+{
+ DEVICES_STATE *devState = pMS->privData;
+ BOOLEAN select, cancel, back, forward;
+ BOOLEAN pagefwd, pageback;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+ back = PulsedInputState.menu[KEY_MENU_UP] ||
+ PulsedInputState.menu[KEY_MENU_LEFT];
+ forward = PulsedInputState.menu[KEY_MENU_DOWN]
+ || PulsedInputState.menu[KEY_MENU_RIGHT];
+ pagefwd = PulsedInputState.menu[KEY_MENU_PAGE_DOWN];
+ pageback = PulsedInputState.menu[KEY_MENU_PAGE_UP];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (cancel)
+ {
+ return FALSE;
+ }
+ else if (select)
+ {
+ DeviceStatus status;
+
+ status = InvokeDevice (devState->list[pMS->CurState]);
+ if (status == DEVICE_FAILURE)
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ else if (status == DEVICE_SUCCESS)
+ PlayMenuSound (MENU_SOUND_INVOKED);
+
+ return (status == DEVICE_FAILURE);
+ }
+ else
+ {
+ SIZE NewTop;
+ SIZE NewState;
+
+ NewTop = devState->topIndex;
+ NewState = pMS->CurState;
+
+ if (back)
+ --NewState;
+ else if (forward)
+ ++NewState;
+ else if (pagefwd)
+ NewState += MAX_VIS_DEVICES;
+ else if (pageback)
+ NewState -= MAX_VIS_DEVICES;
+
+ if (NewState < 0)
+ NewState = 0;
+ else if (NewState >= devState->count)
+ NewState = devState->count - 1;
+
+ if (NewState < NewTop || NewState >= NewTop + MAX_VIS_DEVICES)
+ NewTop = NewState - NewState % MAX_VIS_DEVICES;
+
+ if (NewState != pMS->CurState)
+ {
+ if (NewTop != devState->topIndex)
+ { // redraw the display
+ devState->topIndex = NewTop;
+ DrawDevices (devState, (COUNT)~0, NewState);
+ }
+ else
+ { // move selection to new device
+ DrawDevices (devState, pMS->CurState, NewState);
+ }
+ pMS->CurState = NewState;
+ }
+
+ SleepThread (ONE_SECOND / 30);
+ }
+
+ return TRUE;
+}
+
+SIZE
+InventoryDevices (BYTE *pDeviceMap, COUNT Size)
+{
+ BYTE i;
+ SIZE DevicesOnBoard;
+
+ DevicesOnBoard = 0;
+ for (i = 0; i < NUM_DEVICES && Size > 0; ++i)
+ {
+ BYTE DeviceState;
+
+ DeviceState = 0;
+ switch (i)
+ {
+ case ROSY_SPHERE_DEVICE:
+ DeviceState = GET_GAME_STATE (ROSY_SPHERE_ON_SHIP);
+ break;
+ case ARTIFACT_2_DEVICE:
+ DeviceState = GET_GAME_STATE (ARTIFACT_2_ON_SHIP);
+ break;
+ case ARTIFACT_3_DEVICE:
+ DeviceState = GET_GAME_STATE (ARTIFACT_3_ON_SHIP);
+ break;
+ case SUN_EFFICIENCY_DEVICE:
+ DeviceState = GET_GAME_STATE (SUN_DEVICE_ON_SHIP);
+ break;
+ case UTWIG_BOMB_DEVICE:
+ DeviceState = GET_GAME_STATE (UTWIG_BOMB_ON_SHIP);
+ break;
+ case ULTRON_0_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 1);
+ break;
+ case ULTRON_1_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 2);
+ break;
+ case ULTRON_2_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 3);
+ break;
+ case ULTRON_3_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 4);
+ break;
+ case MAIDENS_DEVICE:
+ DeviceState = GET_GAME_STATE (MAIDENS_ON_SHIP);
+ break;
+ case TALKING_PET_DEVICE:
+ DeviceState = GET_GAME_STATE (TALKING_PET_ON_SHIP);
+ break;
+ case AQUA_HELIX_DEVICE:
+ DeviceState = GET_GAME_STATE (AQUA_HELIX_ON_SHIP);
+ break;
+ case CLEAR_SPINDLE_DEVICE:
+ DeviceState = GET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP);
+ break;
+ case UMGAH_HYPERWAVE_DEVICE:
+ DeviceState = GET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP);
+ break;
+ case TAALO_PROTECTOR_DEVICE:
+ DeviceState = GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP);
+ break;
+ case EGG_CASING0_DEVICE:
+ DeviceState = GET_GAME_STATE (EGG_CASE0_ON_SHIP);
+ break;
+ case EGG_CASING1_DEVICE:
+ DeviceState = GET_GAME_STATE (EGG_CASE1_ON_SHIP);
+ break;
+ case EGG_CASING2_DEVICE:
+ DeviceState = GET_GAME_STATE (EGG_CASE2_ON_SHIP);
+ break;
+ case SYREEN_SHUTTLE_DEVICE:
+ DeviceState = GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP);
+ break;
+ case VUX_BEAST_DEVICE:
+ DeviceState = GET_GAME_STATE (VUX_BEAST_ON_SHIP);
+ break;
+ case DESTRUCT_CODE_DEVICE:
+#ifdef NEVER
+ DeviceState = GET_GAME_STATE (DESTRUCT_CODE_ON_SHIP);
+#endif /* NEVER */
+ break;
+ case PORTAL_SPAWNER_DEVICE:
+ DeviceState = GET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP);
+ break;
+ case URQUAN_WARP_DEVICE:
+ DeviceState = GET_GAME_STATE (PORTAL_KEY_ON_SHIP);
+ break;
+ case BURVIX_HYPERWAVE_DEVICE:
+ DeviceState = GET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP);
+ break;
+ case LUNAR_BASE_DEVICE:
+ DeviceState = GET_GAME_STATE (MOONBASE_ON_SHIP);
+ break;
+ }
+
+#ifndef DEBUG_DEVICES
+ if (DeviceState)
+#endif /* DEBUG_DEVICES */
+ {
+ *pDeviceMap++ = i;
+ ++DevicesOnBoard;
+ --Size;
+ }
+ }
+
+ return DevicesOnBoard;
+}
+
+BOOLEAN
+DevicesMenu (void)
+{
+ MENU_STATE MenuState;
+ DEVICES_STATE DevicesState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.privData = &DevicesState;
+
+ memset (&DevicesState, 0, sizeof DevicesState);
+
+ DevicesState.count = InventoryDevices (DevicesState.list, NUM_DEVICES);
+ if (!DevicesState.count)
+ return FALSE;
+
+ DrawDevices (&DevicesState, (COUNT)~0, MenuState.CurState);
+
+ SetMenuSounds (MENU_SOUND_ARROWS | MENU_SOUND_PAGEUP | MENU_SOUND_PAGEDOWN,
+ MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoManipulateDevices;
+ DoInput (&MenuState, TRUE);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ if (GLOBAL_SIS (CrewEnlisted) != (COUNT)~0
+ && !(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ ClearSISRect (DRAW_SIS_DISPLAY);
+
+ if (!GET_GAME_STATE (PORTAL_COUNTER)
+ && !(GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
diff --git a/src/uqm/planets/elemdata.h b/src/uqm/planets/elemdata.h
new file mode 100644
index 0000000..53d228c
--- /dev/null
+++ b/src/uqm/planets/elemdata.h
@@ -0,0 +1,215 @@
+//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.
+ */
+
+#ifndef UQM_PLANETS_ELEMDATA_H_
+#define UQM_PLANETS_ELEMDATA_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*------------------------------ Type Defines ----------------------------- */
+enum
+{
+ COMMON = 0,
+ CORROSIVE,
+ BASE_METAL,
+ NOBLE,
+ RARE_EARTH,
+ PRECIOUS,
+ RADIOACTIVE,
+ EXOTIC,
+
+ NUM_ELEMENT_CATEGORIES
+};
+
+#define ElementCategory(et) (Elements[et] & 0x7)
+
+/*------------------------------ Global Data ------------------------------ */
+
+enum
+{
+ HYDROGEN,
+ HELIUM,
+ LITHIUM,
+ BERYLLIUM,
+ BORON,
+ CARBON,
+ NITROGEN,
+ OXYGEN,
+ FLUORINE,
+ NEON,
+ SODIUM,
+ MAGNESIUM,
+ ALUMINUM,
+ SILICON,
+ PHOSPHORUS,
+ SULFUR,
+ CHLORINE,
+ ARGON,
+ POTASSIUM,
+ CALCIUM,
+ SCANDIUM,
+ TITANIUM,
+ VANADIUM,
+ CHROMIUM,
+ MANGANESE,
+ IRON,
+ COBALT,
+ NICKEL,
+ COPPER,
+ ZINC,
+ GALLIUM,
+ GERMANIUM,
+ ARSENIC,
+ SELENIUM,
+ BROMINE,
+ KRYPTON,
+ RUBIDIUM,
+ STRONTIUM,
+ YTTRIUM,
+ ZIRCONIUM,
+ NIOBIUM,
+ MOLYBDENUM,
+ TECHNETIUM,
+ RUTHENIUM,
+ RHODIUM,
+ PALLADIUM,
+ SILVER,
+ CADMIUM,
+ INDIUM,
+ TIN,
+ ANTIMONY,
+ TELLURIUM,
+ IODINE,
+ XENON,
+ CESIUM,
+ BARIUM,
+ LANTHANUM,
+ CERIUM,
+ PRASEODYMIUM,
+ NEODYMIUM,
+ PROMETHIUM,
+ SAMARIUM,
+ EUROPIUM,
+ GADOLINIUM,
+ TERBIUM,
+ DYPROSIUM,
+ HOLMIUM,
+ ERBIUM,
+ THULIUM,
+ YTTERBIUM,
+ LUTETIUM,
+ HAFNIUM,
+ TANTALUM,
+ TUNGSTEN,
+ RHENIUM,
+ OSMIUM,
+ IRIDIUM,
+ PLATINUM,
+ GOLD,
+ MERCURY,
+ THALLIUM,
+ LEAD,
+ BISMUTH,
+ POLONIUM,
+ ASTATINE,
+ RADON,
+ FRANCIUM,
+ RADIUM,
+ ACTINIUM,
+ THORIUM,
+ PROTACTINIUM,
+ URANIUM,
+ NEPTUNIUM,
+ PLUTONIUM,
+ NUMBER_OF_NORMAL,
+
+ OZONE = NUMBER_OF_NORMAL,
+ FREE_RADICALS,
+ CARBON_DIOXIDE,
+ CARBON_MONOXIDE,
+ AMMONIA,
+ METHANE,
+ SULFURIC_ACID,
+ HYDROCHLORIC_ACID,
+ HYDROCYANIC_ACID,
+ FORMIC_ACID,
+ PHOSPHORIC_ACID,
+ FORMALDEHYDE,
+ CYANOACETYLENE,
+ METHANOL,
+ ETHANOL,
+ SILICON_MONOXIDE,
+ TITANIUM_OXIDE,
+ ZIRCONIUM_OXIDE,
+ WATER,
+ SILICON_COMPOUNDS,
+ METAL_OXIDES,
+ QUANTUM_BH,
+ NEUTRONIUM,
+ MAGNETIC_MONOPOLES,
+ DEGENERATE_MATTER,
+ RT_SUPER_FLUID,
+ AGUUTI_NODULES,
+ IRON_COMPOUNDS,
+ ALUMINUM_COMPOUNDS,
+ NITROUS_OXIDE,
+ RADIOACTIVE_COMPOUNDS,
+ HYDROCARBONS,
+ CARBON_COMPOUNDS,
+ ANTIMATTER,
+ CHARON_DUST,
+ REISBURG_HELICES,
+ TZO_CRYSTALS,
+ CALCIUM_COMPOUNDS,
+ NITRIC_ACID,
+
+ NUMBER_OF_ELEMENTS
+};
+#define NUMBER_OF_SPECIAL (NUMBER_OF_ELEMENTS - NUMBER_OF_NORMAL)
+
+#define CHANCE_MASK ((1 << 2) - 1)
+
+#define FEW 1
+#define MODERATE 4
+#define NUMEROUS 8
+
+enum
+{
+ LIGHT = 0,
+ MEDIUM,
+ HEAVY
+};
+#define NOTHING (BYTE)(~0)
+
+#define MINERAL_DEPOSIT(qn,ql) MAKE_BYTE (qn, ql)
+#define DEPOSIT_QUANTITY(md) LONIBBLE (md)
+#define DEPOSIT_QUALITY(md) HINIBBLE (md)
+
+#define MAX_ELEMENT_UNITS 0xF
+
+extern const BYTE *Elements;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_ELEMDATA_H_ */
diff --git a/src/uqm/planets/generate.h b/src/uqm/planets/generate.h
new file mode 100644
index 0000000..9942e82
--- /dev/null
+++ b/src/uqm/planets/generate.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#ifndef GENERATE_H
+#define GENERATE_H
+
+typedef struct GenerateFunctions GenerateFunctions;
+
+#include "types.h"
+#include "planets.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * To do (for further cleanups):
+ * - split off generateOrbital in a calculation and an activation
+ * (graphics and music) part.
+ * - make generateOrbital return a meaningful value, specifically, whether
+ * or not the player is going into orbit
+ * - for GenerateNameFunction, set the name in an argument, instead
+ * of in GLOBAL_SYS(PlanetName)
+ * - make generateName work for moons
+ * - add parameters to initNcs, reinitNpcs, and uninitNpcs, so that
+ * globals don't have to be used.
+ * - Add a reference from each world to the solar system, so that most
+ * of these functions can do with one less argument.
+ * - (maybe) don't directly call the generate functions via
+ * solarSys->genFuncs->..., but use a function for this, which first
+ * checks for solar system dependent handlers, and if this does not exist,
+ * or returns false, calls the default function.
+ */
+
+// Any of these functions returning true means that the action has been
+// handled, and that the default function should not be called.
+typedef bool (*InitNpcsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*ReinitNpcsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*UninitNpcsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*GeneratePlanetsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*GenerateMoonsFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+typedef bool (*GenerateOrbitalFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+typedef bool (*GenerateNameFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world);
+// The following functions return the number of objects being generated
+// (or the index of the current object in some cases)
+typedef COUNT (*GenerateMineralsFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+typedef COUNT (*GenerateEnergyFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+typedef COUNT (*GenerateLifeFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+// The following functions return true if the node should be removed
+// from the surface, i.e. picked up.
+typedef bool (*PickupMineralsFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+typedef bool (*PickupEnergyFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+typedef bool (*PickupLifeFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+struct GenerateFunctions {
+ InitNpcsFunction initNpcs;
+ // Ships in the solar system, the first time it is accessed.
+ ReinitNpcsFunction reinitNpcs;
+ // Ships in the solar system, every next time it is accessed.
+ UninitNpcsFunction uninitNpcs;
+ // When leaving the solar system.
+ GeneratePlanetsFunction generatePlanets;
+ // Layout of planets within a solar system.
+ GenerateMoonsFunction generateMoons;
+ // Layout of moons around a planet.
+ GenerateNameFunction generateName;
+ // Name of a planet.
+ GenerateOrbitalFunction generateOrbital;
+ // Characteristics of words (planets and moons).
+ GenerateMineralsFunction generateMinerals;
+ // Minerals on the planet surface.
+ GenerateEnergyFunction generateEnergy;
+ // Energy sources on the planet surface.
+ GenerateLifeFunction generateLife;
+ // Bio on the planet surface.
+ PickupMineralsFunction pickupMinerals;
+ PickupEnergyFunction pickupEnergy;
+ PickupLifeFunction pickupLife;
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* GENERATE_H */
+
diff --git a/src/uqm/planets/generate/Makeinfo b/src/uqm/planets/generate/Makeinfo
new file mode 100644
index 0000000..520af9d
--- /dev/null
+++ b/src/uqm/planets/generate/Makeinfo
@@ -0,0 +1,6 @@
+uqm_CFILES="gendefault.c genand.c genburv.c genchmmr.c gencol.c gendru.c
+ genilw.c genmel.c genmyc.c genorz.c genpet.c genpku.c genrain.c
+ gensam.c genshof.c gensly.c gensol.c genspa.c gensup.c gensyr.c
+ genthrad.c gentrap.c genutw.c genvault.c genvux.c genwreck.c
+ genyeh.c genzfpscout.c genzoq.c"
+uqm_HFILES="genall.h gendefault.h"
diff --git a/src/uqm/planets/generate/genall.h b/src/uqm/planets/generate/genall.h
new file mode 100644
index 0000000..3776cff
--- /dev/null
+++ b/src/uqm/planets/generate/genall.h
@@ -0,0 +1,27 @@
+/*
+ * 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
+ */
+
+#ifndef UQM_PLANETS_GENERATE_GENALL_H_
+#define UQM_PLANETS_GENERATE_GENALL_H_
+
+#include "gendefault.h"
+#include "types.h"
+#include "../generate.h"
+#include "libs/compiler.h"
+
+
+#endif /* UQM_PLANETS_GENERATE_GENALL_H_ */
+
diff --git a/src/uqm/planets/generate/genand.c b/src/uqm/planets/generate/genand.c
new file mode 100644
index 0000000..457b5ff
--- /dev/null
+++ b/src/uqm/planets/generate/genand.c
@@ -0,0 +1,164 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../scan.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "../../sounds.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateAndrosynth_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateAndrosynth_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateAndrosynth_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateAndrosynth_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateAndrosynthFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateAndrosynth_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateAndrosynth_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateAndrosynth_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateAndrosynth_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateAndrosynth_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[1].data_index = TELLURIC_WORLD;
+ solarSys->PlanetDesc[1].radius = EARTH_RADIUS * 204L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[1].location.x,
+ solarSys->PlanetDesc[1].location.y);
+ solarSys->PlanetDesc[1].location.x =
+ COSINE (angle, solarSys->PlanetDesc[1].radius);
+ solarSys->PlanetDesc[1].location.y =
+ SINE (angle, solarSys->PlanetDesc[1].radius);
+
+ return true;
+}
+
+static bool
+GenerateAndrosynth_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ COUNT i;
+ COUNT visits = 0;
+
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (ANDROSYNTH_RUINS_STRTAB));
+ // Androsynth ruins are a special case. The DiscoveryString contains
+ // several lander reports which form a story. Each report is given
+ // when the player collides with a new city ruin. Ruins previously
+ // visited are marked in the upper 16 bits of ScanRetrieveMask, and
+ // the lower bits are cleared to keep the ruin nodes on the map.
+ for (i = 16; i < 32; ++i)
+ {
+ if (isNodeRetrieved (&solarSys->SysInfo.PlanetInfo, ENERGY_SCAN, i))
+ ++visits;
+ }
+ if (visits >= GetStringTableCount (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString))
+ { // All the reports were already given
+ DestroyStringTable (ReleaseStringTable (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString = 0;
+ }
+ else
+ { // Advance the report sequence to the first unread
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetRelStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, visits);
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ EARTH_ATMOSPHERE * 144 / 100;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 28;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ }
+
+ return true;
+}
+
+static bool
+GenerateAndrosynth_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
+
+ // Ruins previously visited are marked in the upper 16 bits
+ if (isNodeRetrieved (planetInfo, ENERGY_SCAN, whichNode + 16))
+ return false; // already visited this ruin, do not remove
+
+ setNodeRetrieved (planetInfo, ENERGY_SCAN, whichNode + 16);
+ // We set the retrieved bit manually here and need to indicate
+ // the change to the solar system state functions
+ SET_GAME_STATE (PLANETARY_CHANGE, 1);
+
+ // Androsynth ruins have several lander reports which form a story
+ GenerateDefault_landerReportCycle (solarSys);
+
+ return false; // do not remove the node from the surface
+ }
+
+ return false;
+}
+
+static COUNT
+GenerateAndrosynth_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
diff --git a/src/uqm/planets/generate/genburv.c b/src/uqm/planets/generate/genburv.c
new file mode 100644
index 0000000..aa5b6bd
--- /dev/null
+++ b/src/uqm/planets/generate/genburv.c
@@ -0,0 +1,192 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateBurvixese_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateBurvixese_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateBurvixese_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateBurvixese_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateBurvixese_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateBurvixeseFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateBurvixese_generatePlanets,
+ /* .generateMoons = */ GenerateBurvixese_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateBurvixese_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateBurvixese_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateBurvixese_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateBurvixese_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 39L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ return true;
+}
+
+static bool
+GenerateBurvixese_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 0, MATCH_PLANET))
+ {
+ COUNT angle;
+ DWORD rand_val;
+
+ solarSys->MoonDesc[0].data_index = SELENIC_WORLD;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS
+ + (MAX_MOONS - 1) * MOON_DELTA;
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+ return true;
+}
+
+static bool
+GenerateBurvixese_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+ GenerateLifeForms (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[ENERGY_SCAN] = rand_val;
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (BURV_RUINS_STRTAB));
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ }
+ else if (matchWorld (solarSys, world, 0, 0)
+ && !GET_GAME_STATE (BURVIXESE_BROADCASTERS))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] = CaptureDrawable (
+ LoadGraphic (BURV_BCS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (BURV_BCS_STRTAB));
+ }
+
+ LoadPlanet (NULL);
+
+ return true;
+}
+
+static COUNT
+GenerateBurvixese_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (BURVIXESE_BROADCASTERS))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateBurvixese_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ assert (!GET_GAME_STATE (BURVIXESE_BROADCASTERS) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (BURVIXESE_BROADCASTERS, 1);
+ SET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genchmmr.c b/src/uqm/planets/generate/genchmmr.c
new file mode 100644
index 0000000..672d977
--- /dev/null
+++ b/src/uqm/planets/generate/genchmmr.c
@@ -0,0 +1,154 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../sounds.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+static bool GenerateChmmr_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateChmmr_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateChmmr_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateChmmrFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateChmmr_generatePlanets,
+ /* .generateMoons = */ GenerateChmmr_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateChmmr_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateChmmr_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[1].data_index = SAPPHIRE_WORLD;
+ if (!GET_GAME_STATE (CHMMR_UNLEASHED))
+ solarSys->PlanetDesc[1].data_index |= PLANET_SHIELDED;
+ solarSys->PlanetDesc[1].NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateChmmr_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 1, MATCH_PLANET))
+ {
+ COUNT angle;
+ DWORD rand_val;
+
+ solarSys->MoonDesc[0].data_index = HIERARCHY_STARBASE;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS;
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateChmmr_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ if (GET_GAME_STATE (CHMMR_UNLEASHED))
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (CHMMR_CONVERSATION);
+
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ {
+ GLOBAL (CurrentActivity) |= END_INTERPLANETARY;
+ }
+
+ return true;
+ }
+ else if (GET_GAME_STATE (SUN_DEVICE_ON_SHIP)
+ && !GET_GAME_STATE (ILWRATH_DECEIVED)
+ && StartSphereTracking (ILWRATH_SHIP))
+ {
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (ILWRATH_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (ILWRATH_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+ }
+ else if (matchWorld (solarSys, world, 1, 0))
+ {
+ /* Starbase */
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (CHMMR_BASE_STRTAB));
+
+ DoDiscoveryReport (MenuSounds);
+
+ DestroyStringTable (ReleaseStringTable (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString = 0;
+ FreeLanderFont (&solarSys->SysInfo.PlanetInfo);
+
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gencol.c b/src/uqm/planets/generate/gencol.c
new file mode 100644
index 0000000..27e4d5b
--- /dev/null
+++ b/src/uqm/planets/generate/gencol.c
@@ -0,0 +1,126 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateColony_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateColony_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateColony_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateColonyFunctions = {
+ /* .initNpcs = */ GenerateColony_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateColony_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateColony_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateColony_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ HIPGROUP hGroup;
+
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (COLONY_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (COLONY_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+
+ GenerateDefault_initNpcs (solarSys);
+
+ if (GLOBAL (BattleGroupRef)
+ && (hGroup = GetHeadLink (&GLOBAL (ip_group_q))))
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ GroupPtr->task = IN_ORBIT;
+ GroupPtr->sys_loc = 0 + 1; /* orbitting colony */
+ GroupPtr->dest_loc = 0 + 1; /* orbitting colony */
+ GroupPtr->loc.x = 0;
+ GroupPtr->loc.y = 0;
+ GroupPtr->group_counter = 0;
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+
+ return true;
+}
+
+static bool
+GenerateColony_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+ PLANET_DESC *pMinPlanet;
+
+ pMinPlanet = &solarSys->PlanetDesc[0];
+ FillOrbits (solarSys, (BYTE)~0, pMinPlanet, FALSE);
+
+ pMinPlanet->radius = EARTH_RADIUS * 115L / 100;
+ angle = ARCTAN (pMinPlanet->location.x, pMinPlanet->location.y);
+ pMinPlanet->location.x = COSINE (angle, pMinPlanet->radius);
+ pMinPlanet->location.y = SINE (angle, pMinPlanet->radius);
+ pMinPlanet->data_index = WATER_WORLD | PLANET_SHIELDED;
+
+ return true;
+}
+
+static bool
+GenerateColony_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ EARTH_ATMOSPHERE * 98 / 100;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 28;
+
+ LoadPlanet (NULL);
+
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gendefault.c b/src/uqm/planets/generate/gendefault.c
new file mode 100644
index 0000000..a88b89c
--- /dev/null
+++ b/src/uqm/planets/generate/gendefault.c
@@ -0,0 +1,373 @@
+/*
+ * 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 "genall.h"
+#include "../planets.h"
+#include "../lander.h"
+#include "../../encount.h"
+#include "../../gamestr.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../races.h"
+#include "../../state.h"
+#include "../../sounds.h"
+#include "libs/mathlib.h"
+
+
+static void GeneratePlanets (SOLARSYS_STATE *system);
+static void check_yehat_rebellion (void);
+
+
+const GenerateFunctions generateDefaultFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+bool
+GenerateDefault_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP))
+ {
+ GLOBAL (BattleGroupRef) = 0;
+ BuildGroups ();
+ }
+
+ (void) solarSys;
+ return true;
+}
+
+bool
+GenerateDefault_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ // This is not a great place to do the Yehat rebellion check, but
+ // since you can start the rebellion in any star system (not just
+ // the Homeworld), I could not find a better place for it.
+ // At least it is better than where it was originally.
+ check_yehat_rebellion ();
+
+ (void) solarSys;
+ return true;
+}
+
+bool
+GenerateDefault_uninitNpcs (SOLARSYS_STATE *solarSys)
+{
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ ReinitQueue (&GLOBAL (ip_group_q));
+
+ (void) solarSys;
+ return true;
+}
+
+bool
+GenerateDefault_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ FillOrbits (solarSys, (BYTE)~0, solarSys->PlanetDesc, FALSE);
+ GeneratePlanets (solarSys);
+ return true;
+}
+
+bool
+GenerateDefault_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ FillOrbits (solarSys, planet->NumPlanets, solarSys->MoonDesc, FALSE);
+ return true;
+}
+
+bool
+GenerateDefault_generateName (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world)
+{
+ COUNT i = planetIndex (solarSys, world);
+ utf8StringCopy (GLOBAL_SIS (PlanetName), sizeof (GLOBAL_SIS (PlanetName)),
+ GAME_STRING (PLANET_NUMBER_BASE + (9 + 7) + i));
+ SET_GAME_STATE (BATTLE_PLANET, world->data_index);
+
+ return true;
+}
+
+bool
+GenerateDefault_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+ SYSTEM_INFO *sysInfo;
+
+#ifdef DEBUG_SOLARSYS
+ if (worldIsPlanet (solarSys, world))
+ {
+ log_add (log_Debug, "Planet index = %d",
+ planetIndex (solarSys, world));
+ }
+ else
+ {
+ log_add (log_Debug, "Planet index = %d, Moon index = %d",
+ planetIndex (solarSys, world),
+ moonIndex (solarSys, world));
+ }
+#endif /* DEBUG_SOLARSYS */
+
+ sysInfo = &solarSys->SysInfo;
+
+ DoPlanetaryAnalysis (sysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ sysInfo->PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+ GenerateLifeForms (sysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ sysInfo->PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (sysInfo, GENERATE_ALL, NULL);
+
+ sysInfo->PlanetInfo.ScanSeed[ENERGY_SCAN] = rand_val;
+ LoadPlanet (NULL);
+
+ return true;
+}
+
+COUNT
+GenerateDefault_generateMinerals (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ return GenerateMineralDeposits (&solarSys->SysInfo, whichNode, info);
+ (void) world;
+}
+
+bool
+GenerateDefault_pickupMinerals (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ // Minerals do not need any extra handling as of now
+ (void) solarSys;
+ (void) world;
+ (void) whichNode;
+ return true;
+}
+
+COUNT
+GenerateDefault_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ (void) whichNode;
+ (void) solarSys;
+ (void) world;
+ (void) info;
+ return 0;
+}
+
+bool
+GenerateDefault_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ // This should never be called since every energy node needs
+ // special handling and the function should be overridden
+ assert (false);
+ (void) solarSys;
+ (void) world;
+ (void) whichNode;
+ return false;
+}
+
+COUNT
+GenerateDefault_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ return GenerateLifeForms (&solarSys->SysInfo, whichNode, info);
+ (void) world;
+}
+
+bool
+GenerateDefault_pickupLife (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ // Bio does not need any extra handling as of now
+ (void) solarSys;
+ (void) world;
+ (void) whichNode;
+ return true;
+}
+
+COUNT
+GenerateDefault_generateArtifact (const SOLARSYS_STATE *solarSys,
+ COUNT whichNode, NODE_INFO *info)
+{
+ // Generate an energy node at a random location
+ return GenerateRandomNodes (&solarSys->SysInfo, ENERGY_SCAN, 1, 0,
+ whichNode, info);
+}
+
+COUNT
+GenerateDefault_generateRuins (const SOLARSYS_STATE *solarSys,
+ COUNT whichNode, NODE_INFO *info)
+{
+ // Generate a standard spread of city ruins of a destroyed civilization
+ return GenerateRandomNodes (&solarSys->SysInfo, ENERGY_SCAN, NUM_RACE_RUINS,
+ 0, whichNode, info);
+}
+
+static inline void
+runLanderReport (void)
+{
+ UnbatchGraphics ();
+ DoDiscoveryReport (MenuSounds);
+ BatchGraphics ();
+}
+
+bool
+GenerateDefault_landerReport (SOLARSYS_STATE *solarSys)
+{
+ PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
+
+ if (!planetInfo->DiscoveryString)
+ return false;
+
+ runLanderReport ();
+
+ // XXX: A non-cycling report is given only once and has to be deleted
+ // in some circumstances (like the Syreen Vault). It does not
+ // hurt to simply delete it in all cases. Nothing should rely on
+ // the presence of DiscoveryString, but the Syreen Vault and the
+ // Mycon Egg Cases rely on its absence.
+ DestroyStringTable (ReleaseStringTable (planetInfo->DiscoveryString));
+ planetInfo->DiscoveryString = 0;
+
+ return true;
+}
+
+bool
+GenerateDefault_landerReportCycle (SOLARSYS_STATE *solarSys)
+{
+ PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
+
+ if (!planetInfo->DiscoveryString)
+ return false;
+
+ runLanderReport ();
+ // Advance to the next report
+ planetInfo->DiscoveryString = SetRelStringTableIndex (
+ planetInfo->DiscoveryString, 1);
+
+ // If our discovery strings have cycled, we're done
+ if (GetStringTableIndex (planetInfo->DiscoveryString) == 0)
+ {
+ DestroyStringTable (ReleaseStringTable (planetInfo->DiscoveryString));
+ planetInfo->DiscoveryString = 0;
+ }
+
+ return true;
+}
+
+// NB. This function modifies the RNG state.
+static void
+GeneratePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT i;
+ PLANET_DESC *planet;
+
+ for (i = solarSys->SunDesc[0].NumPlanets,
+ planet = &solarSys->PlanetDesc[0]; i; --i, ++planet)
+ {
+ DWORD rand_val;
+ BYTE byte_val;
+ BYTE num_moons;
+ BYTE type;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ byte_val = LOBYTE (rand_val);
+
+ num_moons = 0;
+ type = PlanData[planet->data_index & ~PLANET_SHIELDED].Type;
+ switch (PLANSIZE (type))
+ {
+ case LARGE_ROCKY_WORLD:
+ if (byte_val < 0x00FF * 25 / 100)
+ {
+ if (byte_val < 0x00FF * 5 / 100)
+ ++num_moons;
+ ++num_moons;
+ }
+ break;
+ case GAS_GIANT:
+ if (byte_val < 0x00FF * 90 / 100)
+ {
+ if (byte_val < 0x00FF * 75 / 100)
+ {
+ if (byte_val < 0x00FF * 50 / 100)
+ {
+ if (byte_val < 0x00FF * 25 / 100)
+ ++num_moons;
+ ++num_moons;
+ }
+ ++num_moons;
+ }
+ ++num_moons;
+ }
+ break;
+ }
+ planet->NumPlanets = num_moons;
+ }
+}
+
+static void
+check_yehat_rebellion (void)
+{
+ HIPGROUP hGroup, hNextGroup;
+
+ // XXX: Is there a better way to do this? I could not find one.
+ // When you talk to a Yehat ship (YEHAT_SHIP) and start the rebellion,
+ // there is no battle following the comm. There is *never* a battle in
+ // an encounter with Rebels, but the group race_id (YEHAT_REBEL_SHIP)
+ // is different from Royalists (YEHAT_SHIP). There is *always* a battle
+ // in an encounter with Royalists.
+ // TRANSLATION: "If the civil war has not started yet, or the player
+ // battled a ship -- bail."
+ if (!GET_GAME_STATE (YEHAT_CIVIL_WAR) || EncounterRace >= 0)
+ return; // not this time
+
+ // Send Yehat groups to flee the system, but only if the player
+ // has actually talked to a ship.
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q)); hGroup;
+ hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+ // IGNORE_FLAGSHIP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == YEHAT_SHIP
+ && (GroupPtr->task & IGNORE_FLAGSHIP))
+ {
+ GroupPtr->task &= REFORM_GROUP;
+ GroupPtr->task |= FLEE | IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+}
+
+
diff --git a/src/uqm/planets/generate/gendefault.h b/src/uqm/planets/generate/gendefault.h
new file mode 100644
index 0000000..a6d0e71
--- /dev/null
+++ b/src/uqm/planets/generate/gendefault.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef GENDEFAULT_H
+#define GENDEFAULT_H
+
+#include "types.h"
+#include "../planets.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool GenerateDefault_initNpcs (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_reinitNpcs (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_uninitNpcs (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_generatePlanets (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+bool GenerateDefault_generateName (const SOLARSYS_STATE *,
+ const PLANET_DESC *world);
+bool GenerateDefault_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+COUNT GenerateDefault_generateMinerals (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+COUNT GenerateDefault_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+COUNT GenerateDefault_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+bool GenerateDefault_pickupMinerals (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+bool GenerateDefault_pickupEnergy (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+bool GenerateDefault_pickupLife (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+
+COUNT GenerateDefault_generateArtifact (const SOLARSYS_STATE *,
+ COUNT whichNode, NODE_INFO *info);
+COUNT GenerateDefault_generateRuins (const SOLARSYS_STATE *,
+ COUNT whichNode, NODE_INFO *info);
+bool GenerateDefault_landerReport (SOLARSYS_STATE *);
+bool GenerateDefault_landerReportCycle (SOLARSYS_STATE *);
+
+
+extern const GenerateFunctions generateDefaultFunctions;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* GENDEFAULT_H */
+
diff --git a/src/uqm/planets/generate/gendru.c b/src/uqm/planets/generate/gendru.c
new file mode 100644
index 0000000..7202010
--- /dev/null
+++ b/src/uqm/planets/generate/gendru.c
@@ -0,0 +1,169 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+#include <string.h>
+
+
+static bool GenerateDruuge_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateDruuge_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateDruuge_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateDruuge_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateDruugeFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDruuge_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDruuge_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDruuge_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDruuge_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateDruuge_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ memmove (&solarSys->PlanetDesc[1], &solarSys->PlanetDesc[0],
+ sizeof (solarSys->PlanetDesc[0])
+ * solarSys->SunDesc[0].NumPlanets);
+ ++solarSys->SunDesc[0].NumPlanets;
+
+ solarSys->PlanetDesc[0].data_index = DUST_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 50L / 100;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ angle = HALF_CIRCLE - OCTANT;
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].rand_seed = MAKE_DWORD (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+
+ return true;
+}
+
+static bool
+GenerateDruuge_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (DRUUGE_SHIP))
+ {
+ NotifyOthers (DRUUGE_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (DRUUGE_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (DRUUGE_CONVERSATION);
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (DRUUGE_RUINS_STRTAB));
+ if (GET_GAME_STATE (ROSY_SPHERE))
+ { // Already picked up Rosy Sphere, skip the report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static bool
+GenerateDruuge_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+
+ // The artifact can be picked up from any ruin
+ if (!GET_GAME_STATE (ROSY_SPHERE))
+ { // Just picked up the Rosy Sphere from a ruin
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (ROSY_SPHERE, 1);
+ SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 1);
+ }
+
+ return false; // do not remove the node
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateDruuge_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/genilw.c b/src/uqm/planets/generate/genilw.c
new file mode 100644
index 0000000..31a8fc4
--- /dev/null
+++ b/src/uqm/planets/generate/genilw.c
@@ -0,0 +1,150 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateIlwrath_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateIlwrath_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateIlwrath_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateIlwrath_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateIlwrathFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateIlwrath_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateIlwrath_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateIlwrath_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateIlwrath_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateIlwrath_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = PRIMORDIAL_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 204L / 100;
+ angle = ARCTAN (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateIlwrath_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (ILWRATH_SHIP))
+ {
+ NotifyOthers (ILWRATH_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (ILWRATH_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (ILWRATH_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.Weather = 2;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 3;
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateIlwrath_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateIlwrath_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genmel.c b/src/uqm/planets/generate/genmel.c
new file mode 100644
index 0000000..27f31b6
--- /dev/null
+++ b/src/uqm/planets/generate/genmel.c
@@ -0,0 +1,114 @@
+//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 "genall.h"
+#include "../../build.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../state.h"
+#include "libs/log.h"
+
+
+static bool GenerateMelnorme_initNpcs (SOLARSYS_STATE *solarSys);
+
+static int SelectMelnormeRefVar (void);
+static DWORD GetMelnormeRef (void);
+static void SetMelnormeRef (DWORD Ref);
+
+
+const GenerateFunctions generateMelnormeFunctions = {
+ /* .initNpcs = */ GenerateMelnorme_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateMelnorme_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ GLOBAL (BattleGroupRef) = GetMelnormeRef ();
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (MELNORME_SHIP, &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SetMelnormeRef (GLOBAL (BattleGroupRef));
+ }
+
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+
+static int
+SelectMelnormeRefVar (void)
+{
+ switch (CurStarDescPtr->Index)
+ {
+ case MELNORME0_DEFINED: return MELNORME0_GRPOFFS0;
+ case MELNORME1_DEFINED: return MELNORME1_GRPOFFS0;
+ case MELNORME2_DEFINED: return MELNORME2_GRPOFFS0;
+ case MELNORME3_DEFINED: return MELNORME3_GRPOFFS0;
+ case MELNORME4_DEFINED: return MELNORME4_GRPOFFS0;
+ case MELNORME5_DEFINED: return MELNORME5_GRPOFFS0;
+ case MELNORME6_DEFINED: return MELNORME6_GRPOFFS0;
+ case MELNORME7_DEFINED: return MELNORME7_GRPOFFS0;
+ case MELNORME8_DEFINED: return MELNORME8_GRPOFFS0;
+ default:
+ return -1;
+ }
+}
+
+static DWORD
+GetMelnormeRef (void)
+{
+ int RefVar = SelectMelnormeRefVar ();
+ if (RefVar < 0)
+ {
+ log_add (log_Warning, "GetMelnormeRef(): reference unknown");
+ return 0;
+ }
+
+ return GET_GAME_STATE_32 (RefVar);
+}
+
+static void
+SetMelnormeRef (DWORD Ref)
+{
+ int RefVar = SelectMelnormeRefVar ();
+ if (RefVar < 0)
+ {
+ log_add (log_Warning, "SetMelnormeRef(): reference unknown");
+ return;
+ }
+
+ SET_GAME_STATE_32 (RefVar, Ref);
+}
+
diff --git a/src/uqm/planets/generate/genmyc.c b/src/uqm/planets/generate/genmyc.c
new file mode 100644
index 0000000..ead32c7
--- /dev/null
+++ b/src/uqm/planets/generate/genmyc.c
@@ -0,0 +1,286 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../scan.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateMycon_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateMycon_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateMycon_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateMycon_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateMycon_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateMyconFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateMycon_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateMycon_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateMycon_generateEnergy,
+ /* .generateLife = */ GenerateMycon_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateMycon_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateMycon_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = SHATTERED_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 80L / 100;
+ if (solarSys->PlanetDesc[0].NumPlanets > 2)
+ solarSys->PlanetDesc[0].NumPlanets = 2;
+ angle = ARCTAN (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateMycon_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if ((CurStarDescPtr->Index == MYCON_DEFINED
+ || CurStarDescPtr->Index == SUN_DEVICE_DEFINED)
+ && StartSphereTracking (MYCON_SHIP))
+ {
+ if (CurStarDescPtr->Index == MYCON_DEFINED
+ || !GET_GAME_STATE (SUN_DEVICE_UNGUARDED))
+ {
+ NotifyOthers (MYCON_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (CurStarDescPtr->Index == MYCON_DEFINED
+ || !GET_GAME_STATE (MYCON_FELL_FOR_AMBUSH))
+ {
+ CloneShipFragment (MYCON_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ }
+ else
+ {
+ COUNT i;
+
+ for (i = 0; i < 5; ++i)
+ CloneShipFragment (MYCON_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ if (CurStarDescPtr->Index == MYCON_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ InitCommunication (MYCON_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ BOOLEAN MyconSurvivors;
+
+ MyconSurvivors =
+ GetHeadLink (&GLOBAL (npc_built_ship_q)) != 0;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (MyconSurvivors)
+ return true;
+
+ SET_GAME_STATE (SUN_DEVICE_UNGUARDED, 1);
+ RepairSISBorder ();
+ }
+ }
+ }
+
+ switch (CurStarDescPtr->Index)
+ {
+ case SUN_DEVICE_DEFINED:
+ if (!GET_GAME_STATE (SUN_DEVICE))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (SUN_DEVICE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (SUN_DEVICE_STRTAB));
+ }
+ break;
+ case EGG_CASE0_DEFINED:
+ case EGG_CASE1_DEFINED:
+ case EGG_CASE2_DEFINED:
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) == 0)
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 1);
+
+ if (!isNodeRetrieved (&solarSys->SysInfo.PlanetInfo,
+ ENERGY_SCAN, 0))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (EGG_CASE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (EGG_CASE_STRTAB));
+ }
+ break;
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateMycon_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == SUN_DEVICE_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (SUN_DEVICE))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ if ((CurStarDescPtr->Index == EGG_CASE0_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE1_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE2_DEFINED)
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ // XXX: DiscoveryString is set by generateOrbital() only when the
+ // node has not been picked up yet
+ if (!solarSys->SysInfo.PlanetInfo.DiscoveryString)
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateMycon_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == SUN_DEVICE_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (SUN_DEVICE) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (SUN_DEVICE, 1);
+ SET_GAME_STATE (SUN_DEVICE_ON_SHIP, 1);
+ SET_GAME_STATE (MYCON_VISITS, 0);
+
+ return true; // picked up
+ }
+
+ if ((CurStarDescPtr->Index == EGG_CASE0_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE1_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE2_DEFINED)
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ switch (CurStarDescPtr->Index)
+ {
+ case EGG_CASE0_DEFINED:
+ SET_GAME_STATE (EGG_CASE0_ON_SHIP, 1);
+ break;
+ case EGG_CASE1_DEFINED:
+ SET_GAME_STATE (EGG_CASE1_ON_SHIP, 1);
+ break;
+ case EGG_CASE2_DEFINED:
+ SET_GAME_STATE (EGG_CASE2_ON_SHIP, 1);
+ break;
+ }
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateMycon_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ // Gee, I wonder why there isn't any life in Mycon systems...
+ (void) whichNode;
+ (void) solarSys;
+ (void) world;
+ (void) info;
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/genorz.c b/src/uqm/planets/generate/genorz.c
new file mode 100644
index 0000000..a50f318
--- /dev/null
+++ b/src/uqm/planets/generate/genorz.c
@@ -0,0 +1,222 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateOrz_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateOrz_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateOrz_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateOrz_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateOrzFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateOrz_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateOrz_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateOrz_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateOrz_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateOrz_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 156L / 100;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateOrz_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if ((CurStarDescPtr->Index == ORZ_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ || (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED
+ && matchWorld (solarSys, world, 1, 2)
+ && !GET_GAME_STATE (TAALO_PROTECTOR)))
+ {
+ COUNT i;
+
+ if ((CurStarDescPtr->Index == ORZ_DEFINED
+ || !GET_GAME_STATE (TAALO_UNPROTECTED))
+ && StartSphereTracking (ORZ_SHIP))
+ {
+ NotifyOthers (ORZ_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED)
+ {
+ CloneShipFragment (ORZ_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ for (i = 0; i < 14; ++i)
+ {
+ CloneShipFragment (ORZ_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (ORZ_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ BOOLEAN OrzSurvivors;
+
+ OrzSurvivors = GetHeadLink (&GLOBAL (npc_built_ship_q))
+ && (CurStarDescPtr->Index == ORZ_DEFINED
+ || !GET_GAME_STATE (TAALO_UNPROTECTED));
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (OrzSurvivors)
+ return true;
+
+ RepairSISBorder ();
+ }
+ }
+
+ SET_GAME_STATE (TAALO_UNPROTECTED, 1);
+ if (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED)
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (TAALO_DEVICE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (TAALO_DEVICE_STRTAB));
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static COUNT
+GenerateOrz_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED
+ && matchWorld (solarSys, world, 1, 2))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (TAALO_PROTECTOR))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateOrz_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED
+ && matchWorld (solarSys, world, 1, 2))
+ {
+ assert (!GET_GAME_STATE (TAALO_PROTECTOR) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (TAALO_PROTECTOR, 1);
+ SET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genpet.c b/src/uqm/planets/generate/genpet.c
new file mode 100644
index 0000000..4c5515c
--- /dev/null
+++ b/src/uqm/planets/generate/genpet.c
@@ -0,0 +1,257 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../encount.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateTalkingPet_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateTalkingPet_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateTalkingPet_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateTalkingPet_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+static void ZapToUrquanEncounter (void);
+
+
+const GenerateFunctions generateTalkingPetFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateTalkingPet_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateTalkingPet_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateTalkingPet_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateTalkingPet_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateTalkingPet_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = TELLURIC_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 204L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateTalkingPet_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET)
+ && (GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES)
+ || !GET_GAME_STATE (TALKING_PET)
+ || StartSphereTracking (UMGAH_SHIP)))
+ {
+ NotifyOthers (UMGAH_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (StartSphereTracking (UMGAH_SHIP))
+ {
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ if (!GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES))
+ {
+ CloneShipFragment (UMGAH_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ InitCommunication (UMGAH_CONVERSATION);
+ }
+ else
+ {
+ COUNT i;
+
+ for (i = 0; i < 10; ++i)
+ {
+ CloneShipFragment (UMGAH_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ InitCommunication (TALKING_PET_CONVERSATION);
+ }
+ }
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ BOOLEAN UmgahSurvivors;
+
+ UmgahSurvivors = GetHeadLink (
+ &GLOBAL (npc_built_ship_q)) != 0;
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+
+ if (GET_GAME_STATE (PLAYER_HYPNOTIZED))
+ ZapToUrquanEncounter ();
+ else if (GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES)
+ && !UmgahSurvivors)
+ {
+ // Defeated the zombie fleet.
+ InitCommunication (TALKING_PET_CONVERSATION);
+ }
+ else if (!(StartSphereTracking (UMGAH_SHIP)))
+ {
+ // The Kohr-Ah have destroyed the Umgah, but the
+ // talking pet survived.
+ InitCommunication (TALKING_PET_CONVERSATION);
+ }
+
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+
+ return true;
+}
+
+static COUNT
+GenerateTalkingPet_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateTalkingPet_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static void
+ZapToUrquanEncounter (void)
+{
+ HENCOUNTER hEncounter;
+
+ if ((hEncounter = AllocEncounter ()) || (hEncounter = GetHeadEncounter ()))
+ {
+ SIZE dx, dy;
+ ENCOUNTER *EncounterPtr;
+ HFLEETINFO hStarShip;
+ FLEET_INFO *TemplatePtr;
+ BRIEF_SHIP_INFO *BSIPtr;
+
+ LockEncounter (hEncounter, &EncounterPtr);
+
+ if (hEncounter == GetHeadEncounter ())
+ RemoveEncounter (hEncounter);
+ memset (EncounterPtr, 0, sizeof (*EncounterPtr));
+
+ InsertEncounter (hEncounter, GetHeadEncounter ());
+
+ hStarShip = GetStarShipFromIndex (&GLOBAL (avail_race_q), URQUAN_SHIP);
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ EncounterPtr->origin = TemplatePtr->loc;
+ EncounterPtr->radius = TemplatePtr->actual_strength;
+ EncounterPtr->race_id = URQUAN_SHIP;
+ EncounterPtr->num_ships = 1;
+ EncounterPtr->flags = ONE_SHOT_ENCOUNTER;
+ BSIPtr = &EncounterPtr->ShipList[0];
+ BSIPtr->race_id = URQUAN_SHIP;
+ BSIPtr->crew_level = TemplatePtr->crew_level;
+ BSIPtr->max_crew = TemplatePtr->max_crew;
+ BSIPtr->max_energy = TemplatePtr->max_energy;
+ EncounterPtr->loc_pt.x = 5288;
+ EncounterPtr->loc_pt.y = 4892;
+ EncounterPtr->log_x = UNIVERSE_TO_LOGX (EncounterPtr->loc_pt.x);
+ EncounterPtr->log_y = UNIVERSE_TO_LOGY (EncounterPtr->loc_pt.y);
+ GLOBAL_SIS (log_x) = EncounterPtr->log_x;
+ GLOBAL_SIS (log_y) = EncounterPtr->log_y;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+
+ {
+#define LOST_DAYS 15
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND * 2));
+ MoveGameClockDays (LOST_DAYS);
+ }
+
+ GLOBAL (CurrentActivity) = MAKE_WORD (IN_HYPERSPACE, 0) | START_ENCOUNTER;
+
+ dx = CurStarDescPtr->star_pt.x - EncounterPtr->loc_pt.x;
+ dy = CurStarDescPtr->star_pt.y - EncounterPtr->loc_pt.y;
+ dx = (SIZE)square_root ((long)dx * dx + (long)dy * dy)
+ + (FUEL_TANK_SCALE >> 1);
+
+ DeltaSISGauges (0, -dx, 0);
+ if (GLOBAL_SIS (FuelOnBoard) < 5 * FUEL_TANK_SCALE)
+ {
+ dx = ((5 + ((COUNT)TFB_Random () % 5)) * FUEL_TANK_SCALE)
+ - (SIZE)GLOBAL_SIS (FuelOnBoard);
+ DeltaSISGauges (0, dx, 0);
+ }
+ DrawSISMessage (NULL);
+ DrawHyperCoords (EncounterPtr->loc_pt);
+
+ UnlockEncounter (hEncounter);
+ }
+}
+
diff --git a/src/uqm/planets/generate/genpku.c b/src/uqm/planets/generate/genpku.c
new file mode 100644
index 0000000..64b9965
--- /dev/null
+++ b/src/uqm/planets/generate/genpku.c
@@ -0,0 +1,159 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GeneratePkunk_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GeneratePkunk_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GeneratePkunk_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GeneratePkunk_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generatePkunkFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GeneratePkunk_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GeneratePkunk_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GeneratePkunk_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GeneratePkunk_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GeneratePkunk_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 104L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GeneratePkunk_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (PKUNK_SHIP))
+ {
+ NotifyOthers (PKUNK_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (PKUNK_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (PKUNK_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (PKUNK_RUINS_STRTAB));
+ if (GET_GAME_STATE (CLEAR_SPINDLE))
+ { // Already picked up the Clear Spindle, skip the report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static bool
+GeneratePkunk_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+
+ // The artifact can be picked up from any ruin
+ if (!GET_GAME_STATE (CLEAR_SPINDLE))
+ { // Just picked up the Clear Spindle from a ruin
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (CLEAR_SPINDLE, 1);
+ SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 1);
+ }
+
+ return false; // do not remove the node
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GeneratePkunk_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/genrain.c b/src/uqm/planets/generate/genrain.c
new file mode 100644
index 0000000..c149b29
--- /dev/null
+++ b/src/uqm/planets/generate/genrain.c
@@ -0,0 +1,102 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateRainbowWorld_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateRainbowWorld_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateRainbowWorldFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateRainbowWorld_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateRainbowWorld_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateRainbowWorld_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = RAINBOW_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 50L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ if (angle <= QUADRANT)
+ angle += QUADRANT;
+ else if (angle >= FULL_CIRCLE - QUADRANT)
+ angle -= QUADRANT;
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateRainbowWorld_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ BYTE which_rainbow;
+ UWORD rainbow_mask;
+ STAR_DESC *SDPtr;
+
+ rainbow_mask = MAKE_WORD (
+ GET_GAME_STATE (RAINBOW_WORLD0),
+ GET_GAME_STATE (RAINBOW_WORLD1));
+
+ which_rainbow = 0;
+ SDPtr = &star_array[0];
+ while (SDPtr != CurStarDescPtr)
+ {
+ if (SDPtr->Index == RAINBOW_DEFINED)
+ ++which_rainbow;
+ ++SDPtr;
+ }
+ rainbow_mask |= 1 << which_rainbow;
+ SET_GAME_STATE (RAINBOW_WORLD0, LOBYTE (rainbow_mask));
+ SET_GAME_STATE (RAINBOW_WORLD1, HIBYTE (rainbow_mask));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gensam.c b/src/uqm/planets/generate/gensam.c
new file mode 100644
index 0000000..b2398b3
--- /dev/null
+++ b/src/uqm/planets/generate/gensam.c
@@ -0,0 +1,324 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../encount.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSaMatra_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSaMatra_reinitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSaMatra_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSaMatra_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSaMatra_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+static void BuildUrquanGuard (SOLARSYS_STATE *solarSys);
+
+
+const GenerateFunctions generateSaMatraFunctions = {
+ /* .initNpcs = */ GenerateSaMatra_initNpcs,
+ /* .reinitNpcs = */ GenerateSaMatra_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSaMatra_generatePlanets,
+ /* .generateMoons = */ GenerateSaMatra_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSaMatra_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSaMatra_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GET_GAME_STATE (URQUAN_MESSED_UP))
+ {
+ BuildUrquanGuard (solarSys);
+ }
+ else
+ { // Exorcise Ur-Quan ghosts upon system reentry
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ // wipe out the group
+ }
+
+ (void) solarSys;
+ return true;
+}
+
+static bool
+GenerateSaMatra_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ BOOLEAN GuardEngaged;
+ HIPGROUP hGroup;
+ HIPGROUP hNextGroup;
+
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ EncounterGroup = 0;
+ EncounterRace = -1;
+ // Do not want guards to chase the player
+
+ GuardEngaged = FALSE;
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ hGroup; hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ if (GET_GAME_STATE (URQUAN_MESSED_UP))
+ {
+ GroupPtr->task &= REFORM_GROUP;
+ GroupPtr->task |= FLEE | IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = 0;
+ }
+ else if (GroupPtr->task & REFORM_GROUP)
+ {
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision
+ // during a collision with the flagship.
+ GroupPtr->task &= ~REFORM_GROUP;
+ GroupPtr->group_counter = 0;
+
+ GuardEngaged = TRUE;
+ }
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+
+ if (GuardEngaged)
+ {
+ COUNT angle;
+ POINT org;
+
+ org = planetOuterLocation (4);
+ angle = ARCTAN (GLOBAL (ip_location.x) - org.x,
+ GLOBAL (ip_location.y) - org.y);
+ GLOBAL (ip_location.x) = org.x + COSINE (angle, 3000);
+ GLOBAL (ip_location.y) = org.y + SINE (angle, 3000);
+ XFormIPLoc (&GLOBAL (ip_location),
+ &GLOBAL (ShipStamp.origin), TRUE);
+ }
+
+ (void) solarSys;
+ return true;
+}
+
+static bool
+GenerateSaMatra_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+ solarSys->PlanetDesc[4].NumPlanets = 1;
+ return true;
+}
+
+static bool
+GenerateSaMatra_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 4, MATCH_PLANET))
+ {
+ COUNT angle;
+ DWORD rand_val;
+
+ solarSys->MoonDesc[0].data_index = SA_MATRA;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS + (2 * MOON_DELTA);
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSaMatra_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ /* Samatra */
+ if (matchWorld (solarSys, world, 4, 0))
+ {
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (!GET_GAME_STATE (URQUAN_MESSED_UP))
+ {
+ CloneShipFragment (!GET_GAME_STATE (KOHR_AH_FRENZY) ?
+ URQUAN_SHIP : BLACK_URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ }
+ else
+ {
+#define URQUAN_REMNANTS 3
+ BYTE i;
+
+ for (i = 0; i < URQUAN_REMNANTS; ++i)
+ {
+ CloneShipFragment (URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ CloneShipFragment (BLACK_URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ }
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ SET_GAME_STATE (URQUAN_PROTECTING_SAMATRA, 1);
+ InitCommunication (URQUAN_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ BOOLEAN UrquanSurvivors;
+
+ UrquanSurvivors = GetHeadLink (&GLOBAL (npc_built_ship_q)) != 0;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ if (UrquanSurvivors)
+ {
+ SET_GAME_STATE (URQUAN_PROTECTING_SAMATRA, 0);
+ }
+ else
+ {
+ EncounterGroup = 0;
+ EncounterRace = -1;
+ GLOBAL (CurrentActivity) = IN_LAST_BATTLE | START_ENCOUNTER;
+ if (GET_GAME_STATE (YEHAT_CIVIL_WAR)
+ && StartSphereTracking (YEHAT_SHIP)
+ && EscortFeasibilityStudy (YEHAT_REBEL_SHIP))
+ InitCommunication (YEHAT_REBEL_CONVERSATION);
+ }
+ }
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static void
+BuildUrquanGuard (SOLARSYS_STATE *solarSys)
+{
+ BYTE ship1, ship2;
+ BYTE b0, b1;
+ POINT org;
+ HIPGROUP hGroup, hNextGroup;
+
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (SAMATRA_GRPOFFS0);
+
+ if (!GET_GAME_STATE (KOHR_AH_FRENZY))
+ {
+ ship1 = URQUAN_SHIP;
+ ship2 = BLACK_URQUAN_SHIP;
+ }
+ else
+ {
+ ship1 = BLACK_URQUAN_SHIP;
+ ship2 = URQUAN_SHIP;
+ }
+
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ for (b0 = 0; b0 < MAX_SHIPS_PER_SIDE; ++b0)
+ CloneShipFragment (ship1, &GLOBAL (npc_built_ship_q), 0);
+
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ SET_GAME_STATE_32 (SAMATRA_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+
+#define NUM_URQUAN_GUARDS0 12
+ for (b0 = 1; b0 <= NUM_URQUAN_GUARDS0; ++b0)
+ PutGroupInfo (GLOBAL (BattleGroupRef), b0);
+
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ for (b0 = 0; b0 < MAX_SHIPS_PER_SIDE; ++b0)
+ CloneShipFragment (ship2, &GLOBAL (npc_built_ship_q), 0);
+
+#define NUM_URQUAN_GUARDS1 4
+ for (b0 = 1; b0 <= NUM_URQUAN_GUARDS1; ++b0)
+ PutGroupInfo (GLOBAL (BattleGroupRef),
+ (BYTE)(NUM_URQUAN_GUARDS0 + b0));
+
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+
+ GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP);
+
+ org = planetOuterLocation (4);
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ for (b0 = 0, b1 = 0;
+ b0 < NUM_URQUAN_GUARDS0;
+ ++b0, b1 += FULL_CIRCLE / (NUM_URQUAN_GUARDS0 + NUM_URQUAN_GUARDS1))
+ {
+ IP_GROUP *GroupPtr;
+
+ if (b1 % (FULL_CIRCLE / NUM_URQUAN_GUARDS1) == 0)
+ b1 += FULL_CIRCLE / (NUM_URQUAN_GUARDS0 + NUM_URQUAN_GUARDS1);
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+ GroupPtr->task = ON_STATION | IGNORE_FLAGSHIP;
+ GroupPtr->sys_loc = 0;
+ GroupPtr->dest_loc = 4 + 1;
+ GroupPtr->orbit_pos = NORMALIZE_FACING (ANGLE_TO_FACING (b1));
+ GroupPtr->group_counter = 0;
+ GroupPtr->loc.x = org.x + COSINE (b1, STATION_RADIUS);
+ GroupPtr->loc.y = org.y + SINE (b1, STATION_RADIUS);
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hGroup = hNextGroup;
+ }
+
+ for (b0 = 0, b1 = 0;
+ b0 < NUM_URQUAN_GUARDS1;
+ ++b0, b1 += FULL_CIRCLE / NUM_URQUAN_GUARDS1)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+ GroupPtr->task = ON_STATION | IGNORE_FLAGSHIP;
+ GroupPtr->sys_loc = 0;
+ GroupPtr->dest_loc = 4 + 1;
+ GroupPtr->orbit_pos = NORMALIZE_FACING (ANGLE_TO_FACING (b1));
+ GroupPtr->group_counter = 0;
+ GroupPtr->loc.x = org.x + COSINE (b1, STATION_RADIUS);
+ GroupPtr->loc.y = org.y + SINE (b1, STATION_RADIUS);
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hGroup = hNextGroup;
+ }
+
+ (void) solarSys;
+}
+
diff --git a/src/uqm/planets/generate/genshof.c b/src/uqm/planets/generate/genshof.c
new file mode 100644
index 0000000..7025f6a
--- /dev/null
+++ b/src/uqm/planets/generate/genshof.c
@@ -0,0 +1,178 @@
+//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 "genall.h"
+#include "../../build.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+#include "../planets.h"
+
+
+static bool GenerateShofixti_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateShofixti_reinitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateShofixti_uninitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateShofixti_generatePlanets (SOLARSYS_STATE *solarSys);
+
+static void check_old_shofixti (void);
+
+
+const GenerateFunctions generateShofixtiFunctions = {
+ /* .initNpcs = */ GenerateShofixti_initNpcs,
+ /* .reinitNpcs = */ GenerateShofixti_reinitNpcs,
+ /* .uninitNpcs = */ GenerateShofixti_uninitNpcs,
+ /* .generatePlanets = */ GenerateShofixti_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateShofixti_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GET_GAME_STATE (SHOFIXTI_RECRUITED)
+ && (!GET_GAME_STATE (SHOFIXTI_KIA)
+ || (!GET_GAME_STATE (SHOFIXTI_BRO_KIA)
+ && GET_GAME_STATE (MAIDENS_ON_SHIP))))
+ {
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (SHOFIXTI_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0
+ || !GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP))
+ {
+ HSHIPFRAG hStarShip;
+
+ if (GLOBAL (BattleGroupRef) == 0)
+ GLOBAL (BattleGroupRef) = ~0L;
+
+ hStarShip = CloneShipFragment (SHOFIXTI_SHIP,
+ &GLOBAL (npc_built_ship_q), 1);
+ if (hStarShip)
+ { /* Set old Shofixti name; his brother if Tanaka died */
+ SHIP_FRAGMENT *FragPtr = LockShipFrag (
+ &GLOBAL (npc_built_ship_q), hStarShip);
+ /* Name Tanaka or Katana (+1) */
+ FragPtr->captains_name_index =
+ NAME_OFFSET + NUM_CAPTAINS_NAMES +
+ (GET_GAME_STATE (SHOFIXTI_KIA) & 1);
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+
+ GLOBAL (BattleGroupRef) = PutGroupInfo (
+ GLOBAL (BattleGroupRef), 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (SHOFIXTI_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+ }
+
+ // This was originally a fallthrough to REINIT_NPCS.
+ // XXX: is the call to check_old_shofixti() needed?
+ GenerateDefault_initNpcs (solarSys);
+ check_old_shofixti ();
+
+ return true;
+}
+
+static bool
+GenerateShofixti_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_reinitNpcs (solarSys);
+ check_old_shofixti ();
+
+ (void) solarSys;
+ return true;
+}
+
+static bool
+GenerateShofixti_uninitNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (GLOBAL (BattleGroupRef)
+ && !GET_GAME_STATE (SHOFIXTI_RECRUITED)
+ && GetHeadLink (&GLOBAL (ip_group_q)) == 0)
+ {
+ if (!GET_GAME_STATE (SHOFIXTI_KIA))
+ {
+ SET_GAME_STATE (SHOFIXTI_KIA, 1);
+ SET_GAME_STATE (SHOFIXTI_VISITS, 0);
+ }
+ else if (GET_GAME_STATE (MAIDENS_ON_SHIP))
+ {
+ SET_GAME_STATE (SHOFIXTI_BRO_KIA, 1);
+ }
+ }
+
+ GenerateDefault_uninitNpcs (solarSys);
+ return true;
+}
+
+static bool
+GenerateShofixti_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT i;
+
+#define NUM_PLANETS 6
+ solarSys->SunDesc[0].NumPlanets = NUM_PLANETS;
+ for (i = 0; i < NUM_PLANETS; ++i)
+ {
+ PLANET_DESC *pCurDesc = &solarSys->PlanetDesc[i];
+
+ pCurDesc->NumPlanets = 0;
+ if (i < (NUM_PLANETS >> 1))
+ pCurDesc->data_index = SELENIC_WORLD;
+ else
+ pCurDesc->data_index = METAL_WORLD;
+ }
+
+ FillOrbits (solarSys, NUM_PLANETS, solarSys->PlanetDesc, TRUE);
+
+ return true;
+}
+
+
+static void
+check_old_shofixti (void)
+{
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ if (!GLOBAL (BattleGroupRef))
+ return; // nothing to check
+
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ if (!hGroup)
+ return; // still nothing to check
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == SHOFIXTI_SHIP
+ && (GroupPtr->task & REFORM_GROUP)
+ && GET_GAME_STATE (SHOFIXTI_RECRUITED))
+ {
+ GroupPtr->task = FLEE | IGNORE_FLAGSHIP | REFORM_GROUP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+}
+
diff --git a/src/uqm/planets/generate/gensly.c b/src/uqm/planets/generate/gensly.c
new file mode 100644
index 0000000..48ed100
--- /dev/null
+++ b/src/uqm/planets/generate/gensly.c
@@ -0,0 +1,70 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../comm.h"
+
+
+static bool GenerateSlylandro_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSlylandro_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateSlylandroFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSlylandro_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSlylandro_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSlylandro_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[3].data_index = RED_GAS_GIANT;
+ solarSys->PlanetDesc[3].NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateSlylandro_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 3, MATCH_PLANET))
+ {
+ InitCommunication (SLYLANDRO_HOME_CONVERSATION);
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gensol.c b/src/uqm/planets/generate/gensol.c
new file mode 100644
index 0000000..d6041f3
--- /dev/null
+++ b/src/uqm/planets/generate/gensol.c
@@ -0,0 +1,671 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../lifeform.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../encount.h"
+#include "../../globdata.h"
+#include "../../gamestr.h"
+#include "../../grpinfo.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSol_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSol_reinitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSol_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSol_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSol_generateName (const SOLARSYS_STATE *,
+ const PLANET_DESC *world);
+static bool GenerateSol_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateSol_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateSol_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateSol_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+static int init_probe (void);
+static void check_probe (void);
+
+
+const GenerateFunctions generateSolFunctions = {
+ /* .initNpcs = */ GenerateSol_initNpcs,
+ /* .reinitNpcs = */ GenerateSol_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSol_generatePlanets,
+ /* .generateMoons = */ GenerateSol_generateMoons,
+ /* .generateName = */ GenerateSol_generateName,
+ /* .generateOrbital = */ GenerateSol_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateSol_generateEnergy,
+ /* .generateLife = */ GenerateSol_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateSol_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSol_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (URQUAN_PROBE_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (URQUAN_DRONE_SHIP, &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (URQUAN_PROBE_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+
+ if (!init_probe ())
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+static bool
+GenerateSol_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) != 3)
+ {
+ GenerateDefault_reinitNpcs (solarSys);
+ check_probe ();
+ }
+ else
+ {
+ GLOBAL (BattleGroupRef) = 0;
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+ }
+ return true;
+}
+
+static bool
+GenerateSol_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT planetI;
+
+#define SOL_SEED 334241042L
+ RandomContext_SeedRandom (SysGenRNG, SOL_SEED);
+
+ solarSys->SunDesc[0].NumPlanets = 9;
+ for (planetI = 0; planetI < 9; ++planetI)
+ {
+ COUNT angle;
+ DWORD rand_val;
+ UWORD word_val;
+ PLANET_DESC *pCurDesc = &solarSys->PlanetDesc[planetI];
+
+ pCurDesc->rand_seed = RandomContext_Random (SysGenRNG);
+ rand_val = pCurDesc->rand_seed;
+ word_val = LOWORD (rand_val);
+ angle = NORMALIZE_ANGLE ((COUNT)HIBYTE (word_val));
+
+ switch (planetI)
+ {
+ case 0: /* MERCURY */
+ pCurDesc->data_index = METAL_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 39L / 100;
+ pCurDesc->NumPlanets = 0;
+ break;
+ case 1: /* VENUS */
+ pCurDesc->data_index = PRIMORDIAL_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 72L / 100;
+ pCurDesc->NumPlanets = 0;
+ angle = NORMALIZE_ANGLE (FULL_CIRCLE - angle);
+ break;
+ case 2: /* EARTH */
+ pCurDesc->data_index = WATER_WORLD | PLANET_SHIELDED;
+ pCurDesc->radius = EARTH_RADIUS;
+ pCurDesc->NumPlanets = 2;
+ break;
+ case 3: /* MARS */
+ pCurDesc->data_index = DUST_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 152L / 100;
+ pCurDesc->NumPlanets = 0;
+ break;
+ case 4: /* JUPITER */
+ pCurDesc->data_index = RED_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 500L /* 520L */ / 100;
+ pCurDesc->NumPlanets = 4;
+ break;
+ case 5: /* SATURN */
+ pCurDesc->data_index = ORA_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 750L /* 952L */ / 100;
+ pCurDesc->NumPlanets = 1;
+ break;
+ case 6: /* URANUS */
+ pCurDesc->data_index = GRN_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 1000L /* 1916L */ / 100;
+ pCurDesc->NumPlanets = 0;
+ break;
+ case 7: /* NEPTUNE */
+ pCurDesc->data_index = BLU_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 1250L /* 2999L */ / 100;
+ pCurDesc->NumPlanets = 1;
+ break;
+ case 8: /* PLUTO */
+ pCurDesc->data_index = PELLUCID_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 1550L /* 3937L */ / 100;
+ pCurDesc->NumPlanets = 0;
+ angle = FULL_CIRCLE - OCTANT;
+ break;
+ }
+
+ pCurDesc->location.x = COSINE (angle, pCurDesc->radius);
+ pCurDesc->location.y = SINE (angle, pCurDesc->radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSol_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ COUNT planetNr;
+ DWORD rand_val;
+
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ planetNr = planetIndex (solarSys, planet);
+ switch (planetNr)
+ {
+ case 2: /* moons of EARTH */
+ {
+ COUNT angle;
+
+ /* Starbase: */
+ solarSys->MoonDesc[0].data_index = HIERARCHY_STARBASE;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS;
+ angle = HALF_CIRCLE + QUADRANT;
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+
+ /* Luna: */
+ solarSys->MoonDesc[1].data_index = SELENIC_WORLD;
+ solarSys->MoonDesc[1].radius = MIN_MOON_RADIUS
+ + (MAX_MOONS - 1) * MOON_DELTA;
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[1].location.x =
+ COSINE (angle, solarSys->MoonDesc[1].radius);
+ solarSys->MoonDesc[1].location.y =
+ SINE (angle, solarSys->MoonDesc[1].radius);
+ break;
+ }
+ case 4: /* moons of JUPITER */
+ solarSys->MoonDesc[0].data_index = RADIOACTIVE_WORLD;
+ /* Io */
+ solarSys->MoonDesc[1].data_index = HALIDE_WORLD;
+ /* Europa */
+ solarSys->MoonDesc[2].data_index = CYANIC_WORLD;
+ /* Ganymede */
+ solarSys->MoonDesc[3].data_index = PELLUCID_WORLD;
+ /* Callisto */
+ break;
+ case 5: /* moons of SATURN */
+ solarSys->MoonDesc[0].data_index = ALKALI_WORLD;
+ /* Titan */
+ break;
+ case 7: /* moons of NEPTUNE */
+ solarSys->MoonDesc[0].data_index = VINYLOGOUS_WORLD;
+ /* Triton */
+ break;
+ }
+
+ return true;
+}
+
+static bool
+GenerateSol_generateName (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world)
+{
+ COUNT planetNr = planetIndex (solarSys, world);
+ utf8StringCopy (GLOBAL_SIS (PlanetName), sizeof (GLOBAL_SIS (PlanetName)),
+ GAME_STRING (PLANET_NUMBER_BASE + planetNr));
+ SET_GAME_STATE (BATTLE_PLANET, solarSys->PlanetDesc[planetNr].data_index);
+
+ return true;
+}
+
+static bool
+GenerateSol_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+ COUNT planetNr;
+
+ if (matchWorld (solarSys, world, 2, 0))
+ {
+ /* Starbase */
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ EncounterGroup = 0;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ return true;
+ }
+
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ planetNr = planetIndex (solarSys, world);
+ if (worldIsPlanet (solarSys, world))
+ {
+ switch (planetNr)
+ {
+ case 0: /* MERCURY */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 0;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 98;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 38;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 3;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 2;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 59 * 240;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 165;
+ break;
+ case 1: /* VENUS */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 90 *
+ EARTH_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 95;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 95;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 177;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 243 * 240;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 457;
+ break;
+ case 2: /* EARTH */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ EARTH_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 100;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 100;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 23;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 240;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 22;
+ break;
+ case 3: /* MARS */
+ // XXX: Mars atmo should actually be 1/2 in current units
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 1;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 72;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 53;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 24;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 246;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -53;
+ break;
+ case 4: /* JUPITER */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 24;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 1120;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 3;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 98;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -143;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 520L / 100;
+ break;
+ case 5: /* SATURN */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 13;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 945;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 27;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 102;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -197;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 952L / 100;
+ break;
+ case 6: /* URANUS */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 21;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 411;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 98;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 172;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -217;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 1916L / 100;
+ break;
+ case 7: /* NEPTUNE */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 28;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 396;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 30;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 182;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -229;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 2999L / 100;
+ break;
+ case 8: /* PLUTO */
+ if (!GET_GAME_STATE (FOUND_PLUTO_SPATHI))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (SPAPLUTO_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (SPAPLUTO_STRTAB));
+ }
+
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 0;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 33;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 18;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 119;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 1533;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -235;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 3937L / 100;
+ break;
+ }
+
+ solarSys->SysInfo.PlanetInfo.SurfaceGravity =
+ CalcGravity (&solarSys->SysInfo.PlanetInfo);
+ LoadPlanet (planetNr == 2 ?
+ CaptureDrawable (LoadGraphic (EARTH_MASK_ANIM)) : NULL);
+ }
+ else
+ {
+ // World is a moon.
+ COUNT moonNr = moonIndex (solarSys, world);
+
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 0;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 0;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ switch (planetNr)
+ {
+ case 2: /* moons of EARTH */
+ // NOTE: Even though we save the seed here, it is irrelevant.
+ // The seed will be used to randomly place the tractors, but
+ // since they are mobile, they will be moved to different
+ // locations not governed by this seed.
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] =
+ rand_val;
+
+ if (!GET_GAME_STATE (MOONBASE_DESTROYED))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (MOONBASE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (MOONBASE_STRTAB));
+ }
+
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 60;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 25;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 240 * 29;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -18;
+ break;
+
+ case 4: /* moons of JUPITER */
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 520L / 100;
+ switch (moonNr)
+ {
+ case 0: /* Io */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 69;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 25;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 3;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 390;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -163;
+ break;
+ case 1: /* Europa */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 54;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 25;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 840;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -161;
+ break;
+ case 2: /* Ganymede */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 35;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 41;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 1728;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -164;
+ break;
+ case 3: /* Callisto */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 35;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 38;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 4008;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -167;
+ break;
+ }
+ break;
+
+ case 5: /* moon of SATURN: Titan */
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 952L / 100;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 160;
+ solarSys->SysInfo.PlanetInfo.Weather = 2;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 34;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 40;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 3816;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -178;
+ break;
+
+ case 7: /* moon of NEPTUNE: Triton */
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 2999L / 100;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 10;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 95;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 27;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 4300;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -216;
+ break;
+ }
+
+ solarSys->SysInfo.PlanetInfo.SurfaceGravity =
+ CalcGravity (&solarSys->SysInfo.PlanetInfo);
+ LoadPlanet (NULL);
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateSol_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 8, MATCH_PLANET))
+ {
+ /* Pluto */
+ // This check is needed because the retrieval bit is not set for
+ // this node to keep it on the surface while the lander is taking off
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI))
+ { // already picked up
+ return 0;
+ }
+
+ if (info)
+ {
+ info->loc_pt.x = 20;
+ info->loc_pt.y = MAP_HEIGHT - 8;
+ }
+
+ return 1; // only matters when count is requested
+ }
+
+ if (matchWorld (solarSys, world, 2, 1))
+ {
+ /* Earth Moon */
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (MOONBASE_DESTROYED))
+ { // already picked up
+ return 0;
+ }
+
+ if (info)
+ {
+ info->loc_pt.x = MAP_WIDTH * 3 / 4;
+ info->loc_pt.y = MAP_HEIGHT * 1 / 4;
+ }
+
+ return 1; // only matters when count is requested
+ }
+
+ (void) whichNode;
+ return 0;
+}
+
+static bool
+GenerateSol_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 8, MATCH_PLANET))
+ { // Pluto
+ assert (!GET_GAME_STATE (FOUND_PLUTO_SPATHI) && whichNode == 0);
+
+ // Ran into Fwiffo on Pluto
+ #define FWIFFO_FRAGS 8
+ if (!KillLanderCrewSeq (FWIFFO_FRAGS, ONE_SECOND / 20))
+ return false; // lander probably died
+
+ SET_GAME_STATE (FOUND_PLUTO_SPATHI, 1);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ // Do not remove the node from the surface while the lander is
+ // taking off. FOUND_PLUTO_SPATHI bit will keep the node from
+ // showing up on subsequent visits.
+ return false;
+ }
+
+ if (matchWorld (solarSys, world, 2, 1))
+ { // Earth Moon
+ assert (!GET_GAME_STATE (MOONBASE_DESTROYED) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (MOONBASE_DESTROYED, 1);
+ SET_GAME_STATE (MOONBASE_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateSol_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 2, 1))
+ {
+ /* Earth Moon */
+ return GenerateRandomNodes (&solarSys->SysInfo, BIOLOGICAL_SCAN, 10,
+ NUM_CREATURE_TYPES + 1, whichNode, info);
+ }
+
+ return 0;
+}
+
+
+static int
+init_probe (void)
+{
+ HIPGROUP hGroup;
+
+ if (!GET_GAME_STATE (PROBE_MESSAGE_DELIVERED)
+ && GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP)
+ && (hGroup = GetHeadLink (&GLOBAL (ip_group_q))))
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ GroupPtr->task = IN_ORBIT;
+ GroupPtr->sys_loc = 2 + 1; /* orbitting earth */
+ GroupPtr->dest_loc = 2 + 1; /* orbitting earth */
+ GroupPtr->loc.x = 0;
+ GroupPtr->loc.y = 0;
+ GroupPtr->group_counter = 0;
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void
+check_probe (void)
+{
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ if (!GLOBAL (BattleGroupRef))
+ return; // nothing to check
+
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ if (!hGroup)
+ return; // still nothing to check
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == URQUAN_DRONE_SHIP
+ && (GroupPtr->task & REFORM_GROUP))
+ {
+ // We just want the probe to take off as fast as possible,
+ // so clear out REFORM_GROUP
+ GroupPtr->task = FLEE | IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+}
+
diff --git a/src/uqm/planets/generate/genspa.c b/src/uqm/planets/generate/genspa.c
new file mode 100644
index 0000000..26bc412
--- /dev/null
+++ b/src/uqm/planets/generate/genspa.c
@@ -0,0 +1,283 @@
+//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 "genall.h"
+#include "../lifeform.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../scan.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSpathi_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSpathi_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSpathi_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateSpathi_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateSpathi_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateSpathi_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+static bool GenerateSpathi_pickupLife (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateSpathiFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSpathi_generatePlanets,
+ /* .generateMoons = */ GenerateSpathi_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSpathi_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateSpathi_generateEnergy,
+ /* .generateLife = */ GenerateSpathi_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateSpathi_pickupEnergy,
+ /* .pickupLife = */ GenerateSpathi_pickupLife,
+};
+
+
+static bool
+GenerateSpathi_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ PLANET_DESC *pMinPlanet;
+ COUNT angle;
+
+ pMinPlanet = &solarSys->PlanetDesc[0];
+ solarSys->SunDesc[0].NumPlanets = 1;
+ FillOrbits (solarSys,
+ solarSys->SunDesc[0].NumPlanets, pMinPlanet, FALSE);
+
+ pMinPlanet->radius = EARTH_RADIUS * 1150L / 100;
+ angle = ARCTAN (pMinPlanet->location.x, pMinPlanet->location.y);
+ pMinPlanet->location.x = COSINE (angle, pMinPlanet->radius);
+ pMinPlanet->location.y = SINE (angle, pMinPlanet->radius);
+ pMinPlanet->data_index = WATER_WORLD;
+ if (GET_GAME_STATE (SPATHI_SHIELDED_SELVES))
+ pMinPlanet->data_index |= PLANET_SHIELDED;
+ pMinPlanet->NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateSpathi_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ COUNT angle;
+
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 0, MATCH_PLANET))
+ {
+#ifdef NOTYET
+ utf8StringCopy (GLOBAL_SIS (PlanetName),
+ sizeof (GLOBAL_SIS (PlanetName)),
+ "Spathiwa");
+#endif /* NOTYET */
+
+ solarSys->MoonDesc[0].data_index = PELLUCID_WORLD;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS + MOON_DELTA;
+ angle = NORMALIZE_ANGLE (LOWORD (RandomContext_Random (SysGenRNG)));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSpathi_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ /* Spathiwa's moon */
+ if (!GET_GAME_STATE (SPATHI_SHIELDED_SELVES)
+ && StartSphereTracking (SPATHI_SHIP))
+ {
+ NotifyOthers (SPATHI_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (SPATHI_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (SPATHI_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+ GenerateLifeForms (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[ENERGY_SCAN] = rand_val;
+
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 28;
+ if (!GET_GAME_STATE (UMGAH_BROADCASTERS))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (UMGAH_BCS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (UMGAH_BCS_STRTAB));
+ if (!GET_GAME_STATE (SPATHI_SHIELDED_SELVES))
+ { // The first report talks extensively about Spathi
+ // slave-shielding selves. If they never did so, the report
+ // makes no sense, so use an alternate.
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ LoadPlanet (NULL);
+ return true;
+ }
+ else if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ /* visiting Spathiwa */
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 120;
+ solarSys->SysInfo.PlanetInfo.SurfaceGravity =
+ CalcGravity (&solarSys->SysInfo.PlanetInfo);
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 31;
+
+ LoadPlanet (NULL);
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static COUNT
+GenerateSpathi_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (UMGAH_BROADCASTERS))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateSpathi_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ assert (!GET_GAME_STATE (UMGAH_BROADCASTERS) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (UMGAH_BROADCASTERS, 1);
+ SET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateSpathi_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ #define NUM_EVIL_ONES 32
+ return GenerateRandomNodes (&solarSys->SysInfo, BIOLOGICAL_SCAN, NUM_EVIL_ONES,
+ NUM_CREATURE_TYPES, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateSpathi_pickupLife (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (SPATHI_CREATURES_ELIMINATED) &&
+ !GET_GAME_STATE (SPATHI_SHIELDED_SELVES));
+
+ SET_GAME_STATE (SPATHI_CREATURES_EXAMINED, 1);
+ if (countNodesRetrieved (&solarSys->SysInfo.PlanetInfo, BIOLOGICAL_SCAN)
+ + 1 == NUM_EVIL_ONES)
+ { // last creature picked up
+ SET_GAME_STATE (SPATHI_CREATURES_ELIMINATED, 1);
+ }
+
+ return true; // picked up
+ }
+
+ return GenerateDefault_pickupLife (solarSys, world, whichNode);
+}
diff --git a/src/uqm/planets/generate/gensup.c b/src/uqm/planets/generate/gensup.c
new file mode 100644
index 0000000..a618c89
--- /dev/null
+++ b/src/uqm/planets/generate/gensup.c
@@ -0,0 +1,159 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSupox_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSupox_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateSupox_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateSupox_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateSupoxFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSupox_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSupox_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateSupox_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateSupox_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSupox_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 2;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 152L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateSupox_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (SUPOX_SHIP))
+ {
+ NotifyOthers (SUPOX_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (SUPOX_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (SUPOX_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (SUPOX_RUINS_STRTAB));
+ if (GET_GAME_STATE (ULTRON_CONDITION))
+ { // Already picked up the Ultron, skip the report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static bool
+GenerateSupox_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+
+ // The artifact can be picked up from any ruin
+ if (!GET_GAME_STATE (ULTRON_CONDITION))
+ { // Just picked up the Ultron from a ruin
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (ULTRON_CONDITION, 1);
+ }
+
+ return false; // do not remove the node
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateSupox_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/gensyr.c b/src/uqm/planets/generate/gensyr.c
new file mode 100644
index 0000000..ebf3be4
--- /dev/null
+++ b/src/uqm/planets/generate/gensyr.c
@@ -0,0 +1,102 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../comm.h"
+
+static bool GenerateSyreen_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSyreen_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSyreen_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateSyreenFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSyreen_generatePlanets,
+ /* .generateMoons = */ GenerateSyreen_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSyreen_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSyreen_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD | PLANET_SHIELDED;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateSyreen_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 0, MATCH_PLANET))
+ {
+ solarSys->MoonDesc[0].data_index = HIERARCHY_STARBASE;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS;
+ solarSys->MoonDesc[0].location.x =
+ COSINE (QUADRANT, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (QUADRANT, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSyreen_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ /* Syreen home planet */
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 19;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = EARTH_ATMOSPHERE * 9 / 10;
+ return true;
+ }
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ /* Starbase */
+ InitCommunication (SYREEN_CONVERSATION);
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/genthrad.c b/src/uqm/planets/generate/genthrad.c
new file mode 100644
index 0000000..875e582
--- /dev/null
+++ b/src/uqm/planets/generate/genthrad.c
@@ -0,0 +1,217 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateThraddash_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateThraddash_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateThraddash_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateThraddash_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateThraddashFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateThraddash_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateThraddash_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateThraddash_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateThraddash_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateThraddash_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = PRIMORDIAL_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 65L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+ else /* CurStarDescPtr->Index == THRADD_DEFINED */
+ {
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 98L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+ return true;
+}
+
+static bool
+GenerateThraddash_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (THRADDASH_SHIP)
+ && (CurStarDescPtr->Index == THRADD_DEFINED
+ || (!GET_GAME_STATE (HELIX_UNPROTECTED)
+ && (BYTE)(GET_GAME_STATE (THRADD_MISSION) - 1) >= 3)))
+ {
+ NotifyOthers (THRADDASH_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (THRADDASH_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ if (CurStarDescPtr->Index == THRADD_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ InitCommunication (THRADD_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (CurStarDescPtr->Index == THRADD_DEFINED
+ || (!GET_GAME_STATE (HELIX_UNPROTECTED)
+ && (BYTE)(GET_GAME_STATE (THRADD_MISSION) - 1) >= 3))
+ return true;
+
+ RepairSISBorder ();
+ }
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED
+ && !GET_GAME_STATE (AQUA_HELIX))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (AQUA_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (AQUA_STRTAB));
+ }
+ else if (CurStarDescPtr->Index == THRADD_DEFINED)
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateThraddash_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == THRADD_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (AQUA_HELIX))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateThraddash_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == THRADD_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (AQUA_HELIX) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (HELIX_VISITS, 0);
+ SET_GAME_STATE (AQUA_HELIX, 1);
+ SET_GAME_STATE (AQUA_HELIX_ON_SHIP, 1);
+ SET_GAME_STATE (HELIX_UNPROTECTED, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/gentrap.c b/src/uqm/planets/generate/gentrap.c
new file mode 100644
index 0000000..e8451cd
--- /dev/null
+++ b/src/uqm/planets/generate/gentrap.c
@@ -0,0 +1,80 @@
+//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 "genall.h"
+#include "../planets.h"
+
+
+static bool GenerateTrap_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateTrap_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateTrapFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateTrap_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateTrap_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateTrap_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = TELLURIC_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 203L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateTrap_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = EARTH_ATMOSPHERE * 2;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 35;
+ solarSys->SysInfo.PlanetInfo.Weather = 3;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ }
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/genutw.c b/src/uqm/planets/generate/genutw.c
new file mode 100644
index 0000000..71ac2aa
--- /dev/null
+++ b/src/uqm/planets/generate/genutw.c
@@ -0,0 +1,269 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateUtwig_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateUtwig_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateUtwig_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateUtwig_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateUtwig_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateUtwigFunctions = {
+ /* .initNpcs = */ GenerateUtwig_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateUtwig_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateUtwig_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateUtwig_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateUtwig_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateUtwig_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && !GET_GAME_STATE (UTWIG_BOMB))
+ {
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+ }
+ else
+ {
+ GenerateDefault_initNpcs (solarSys);
+ }
+
+ return true;
+}
+
+static bool
+GenerateUtwig_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == UTWIG_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 174L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateUtwig_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if ((CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ || (CurStarDescPtr->Index == BOMB_DEFINED
+ && matchWorld (solarSys, world, 5, 1)
+ && !GET_GAME_STATE (UTWIG_BOMB)))
+ {
+ if ((CurStarDescPtr->Index == UTWIG_DEFINED
+ || !GET_GAME_STATE (UTWIG_HAVE_ULTRON))
+ && StartSphereTracking (UTWIG_SHIP))
+ {
+ NotifyOthers (UTWIG_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (UTWIG_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ if (CurStarDescPtr->Index == UTWIG_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ InitCommunication (UTWIG_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && !GET_GAME_STATE (BOMB_UNPROTECTED)
+ && StartSphereTracking (DRUUGE_SHIP))
+ {
+ COUNT i;
+
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ for (i = 0; i < 5; ++i)
+ {
+ CloneShipFragment (DRUUGE_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ InitCommunication (DRUUGE_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ BOOLEAN DruugeSurvivors;
+
+ DruugeSurvivors =
+ GetHeadLink (&GLOBAL (npc_built_ship_q)) != 0;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (DruugeSurvivors)
+ return true;
+
+ RepairSISBorder ();
+ SET_GAME_STATE (BOMB_UNPROTECTED, 1);
+ }
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED)
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (BOMB_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (BOMB_STRTAB));
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateUtwig_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && matchWorld (solarSys, world, 5, 1))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (UTWIG_BOMB))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateUtwig_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && matchWorld (solarSys, world, 5, 1))
+ {
+ assert (!GET_GAME_STATE (UTWIG_BOMB) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (UTWIG_BOMB, 1);
+ SET_GAME_STATE (UTWIG_BOMB_ON_SHIP, 1);
+ SET_GAME_STATE (DRUUGE_MANNER, 1);
+ SET_GAME_STATE (DRUUGE_VISITS, 0);
+ SET_GAME_STATE (DRUUGE_HOME_VISITS, 0);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genvault.c b/src/uqm/planets/generate/genvault.c
new file mode 100644
index 0000000..e189897
--- /dev/null
+++ b/src/uqm/planets/generate/genvault.c
@@ -0,0 +1,130 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateVault_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateVault_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateVault_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateVaultFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateVault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateVault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateVault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateVault_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (VAULT_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (VAULT_STRTAB));
+ if (GET_GAME_STATE (SHIP_VAULT_UNLOCKED))
+ {
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 2);
+ }
+ else if (GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP))
+ {
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateVault_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateVault_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ assert (whichNode == 0);
+
+ if (GET_GAME_STATE (SHIP_VAULT_UNLOCKED))
+ { // Give the final report, "omg empty" and whatnot
+ GenerateDefault_landerReportCycle (solarSys);
+ }
+ else if (GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (SHIP_VAULT_UNLOCKED, 1);
+ SET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP, 0);
+ SET_GAME_STATE (SYREEN_HOME_VISITS, 0);
+ }
+ else
+ {
+ GenerateDefault_landerReport (solarSys);
+
+ if (!GET_GAME_STATE (KNOW_SYREEN_VAULT))
+ {
+ SET_GAME_STATE (KNOW_SYREEN_VAULT, 1);
+ }
+ }
+
+ // The Vault cannot be "picked up". It is always on the surface.
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genvux.c b/src/uqm/planets/generate/genvux.c
new file mode 100644
index 0000000..d4a0642
--- /dev/null
+++ b/src/uqm/planets/generate/genvux.c
@@ -0,0 +1,329 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../lifeform.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../sounds.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateVux_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateVux_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateVux_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateVux_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateVux_pickupEnergy (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+static bool GenerateVux_pickupLife (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+
+
+const GenerateFunctions generateVuxFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateVux_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateVux_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateVux_generateEnergy,
+ /* .generateLife = */ GenerateVux_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateVux_pickupEnergy,
+ /* .pickupLife = */ GenerateVux_pickupLife,
+};
+
+
+static bool
+GenerateVux_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED)
+ {
+ GenerateDefault_generatePlanets (solarSys);
+ // XXX: this is the second time that this function is
+ // called. Is it safe to remove one, or does this change
+ // the RNG so that the outcome is different?
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 212L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+ else
+ {
+ if (CurStarDescPtr->Index == VUX_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 42L / 100;
+ angle = HALF_CIRCLE + OCTANT;
+ }
+ else /* if (CurStarDescPtr->Index == VUX_BEAST_DEFINED) */
+ {
+ memmove (&solarSys->PlanetDesc[1], &solarSys->PlanetDesc[0],
+ sizeof (solarSys->PlanetDesc[0])
+ * solarSys->SunDesc[0].NumPlanets);
+ ++solarSys->SunDesc[0].NumPlanets;
+
+ angle = HALF_CIRCLE - OCTANT;
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 110L / 100;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ }
+
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].rand_seed = MAKE_DWORD (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ }
+ return true;
+}
+
+static bool
+GenerateVux_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if ((matchWorld (solarSys, world, 0, MATCH_PLANET)
+ && (CurStarDescPtr->Index == VUX_DEFINED
+ || (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && !GET_GAME_STATE (ZEX_IS_DEAD))))
+ && StartSphereTracking (VUX_SHIP))
+ {
+ NotifyOthers (VUX_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (VUX_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ if (CurStarDescPtr->Index == VUX_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (VUX_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (CurStarDescPtr->Index == VUX_DEFINED
+ || !GET_GAME_STATE (ZEX_IS_DEAD))
+ return true;
+
+ RepairSISBorder ();
+ }
+ }
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED)
+ {
+ if (!GET_GAME_STATE (SHOFIXTI_MAIDENS))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] = CaptureDrawable (
+ LoadGraphic (MAIDENS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (MAIDENS_STRTAB));
+ }
+ }
+ else if (CurStarDescPtr->Index == VUX_BEAST_DEFINED)
+ {
+ if (!GET_GAME_STATE (VUX_BEAST))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] = 0;
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (BEAST_STRTAB));
+ }
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.Weather = 2;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateVux_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (SHOFIXTI_MAIDENS))
+ { // already picked up
+ return 0;
+ }
+
+ if (info)
+ {
+ info->loc_pt.x = MAP_WIDTH / 3;
+ info->loc_pt.y = MAP_HEIGHT * 5 / 8;
+ }
+
+ return 1; // only matters when count is requested
+ }
+
+ if (CurStarDescPtr->Index == VUX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateVux_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (SHOFIXTI_MAIDENS) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (SHOFIXTI_MAIDENS, 1);
+ SET_GAME_STATE (MAIDENS_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ if (CurStarDescPtr->Index == VUX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateVux_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ static const SBYTE life[] =
+ {
+ 9, 9, 9, 9, /* Carousel Beast */
+ 14, 14, 14, 14, /* Amorphous Trandicula */
+ 18, 18, 18, 18, /* Penguin Cyclops */
+ -1 /* term */
+ };
+ return GeneratePresetLife (&solarSys->SysInfo, life, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == VUX_BEAST_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ static const SBYTE life[] =
+ {
+ NUM_CREATURE_TYPES + 2, /* VUX Beast */
+ // Must be the first node, see pickupLife() below
+ 3, 3, 3, 3, 3, /* Whackin' Bush */
+ 8, 8, 8, 8, 8, /* Glowing Medusa */
+ -1 /* term */
+ };
+ return GeneratePresetLife (&solarSys->SysInfo, life, whichNode, info);
+ }
+
+ return GenerateDefault_generateLife (solarSys, world, whichNode, info);
+}
+
+static bool
+GenerateVux_pickupLife (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == VUX_BEAST_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (whichNode == 0)
+ { // Picked up Zex' Beauty
+ assert (!GET_GAME_STATE (VUX_BEAST));
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (VUX_BEAST, 1);
+ SET_GAME_STATE (VUX_BEAST_ON_SHIP, 1);
+ }
+
+ return true; // picked up
+ }
+
+ return GenerateDefault_pickupLife (solarSys, world, whichNode);
+}
diff --git a/src/uqm/planets/generate/genwreck.c b/src/uqm/planets/generate/genwreck.c
new file mode 100644
index 0000000..05e956e
--- /dev/null
+++ b/src/uqm/planets/generate/genwreck.c
@@ -0,0 +1,111 @@
+//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 "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateWreck_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateWreck_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateWreck_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateWreckFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateWreck_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateWreck_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateWreck_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateWreck_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 6, MATCH_PLANET))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (WRECK_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (WRECK_STRTAB));
+ if (GET_GAME_STATE (PORTAL_KEY))
+ { // Already picked it up, skip the first report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateWreck_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 6, MATCH_PLANET))
+ {
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateWreck_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 6, MATCH_PLANET))
+ {
+ assert (whichNode == 0);
+
+ GenerateDefault_landerReportCycle (solarSys);
+
+ if (!GET_GAME_STATE (PORTAL_KEY))
+ {
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (PORTAL_KEY, 1);
+ SET_GAME_STATE (PORTAL_KEY_ON_SHIP, 1);
+ }
+
+ // The Wreck cannot be "picked up". It is always on the surface.
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genyeh.c b/src/uqm/planets/generate/genyeh.c
new file mode 100644
index 0000000..caae543
--- /dev/null
+++ b/src/uqm/planets/generate/genyeh.c
@@ -0,0 +1,140 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateYehat_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateYehat_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateYehat_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateYehat_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateYehatFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateYehat_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateYehat_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateYehat_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateYehat_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateYehat_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 106L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateYehat_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (YEHAT_SHIP))
+ {
+ NotifyOthers (YEHAT_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (YEHAT_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (YEHAT_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateYehat_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateYehat_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genzfpscout.c b/src/uqm/planets/generate/genzfpscout.c
new file mode 100644
index 0000000..93a6d5d
--- /dev/null
+++ b/src/uqm/planets/generate/genzfpscout.c
@@ -0,0 +1,96 @@
+//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 "genall.h"
+#include "../../build.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+
+
+static bool GenerateZoqFotPikScout_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateZoqFotPikScout_reinitNpcs (SOLARSYS_STATE *solarSys);
+
+
+const GenerateFunctions generateZoqFotPikScoutFunctions = {
+ /* .initNpcs = */ GenerateZoqFotPikScout_initNpcs,
+ /* .reinitNpcs = */ GenerateZoqFotPikScout_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateZoqFotPikScout_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GET_GAME_STATE (MET_ZOQFOT))
+ {
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (ZOQFOT_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (ZOQFOTPIK_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (ZOQFOT_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+ }
+
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+static bool
+GenerateZoqFotPikScout_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ GenerateDefault_reinitNpcs (solarSys);
+
+ if (!GLOBAL (BattleGroupRef))
+ return true; // nothing to check
+
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ if (!hGroup)
+ return true; // still nothing to check
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == ZOQFOTPIK_SHIP
+ && (GroupPtr->task & REFORM_GROUP))
+ {
+ GroupPtr->task = FLEE | IGNORE_FLAGSHIP | REFORM_GROUP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/genzoq.c b/src/uqm/planets/generate/genzoq.c
new file mode 100644
index 0000000..9b30f89
--- /dev/null
+++ b/src/uqm/planets/generate/genzoq.c
@@ -0,0 +1,170 @@
+//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 "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateZoqFotPik_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateZoqFotPik_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateZoqFotPik_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateZoqFotPik_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateZoqFotPik_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateZoqFotPikFunctions = {
+ /* .initNpcs = */ GenerateZoqFotPik_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateZoqFotPik_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateZoqFotPik_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateZoqFotPik_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateZoqFotPik_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateZoqFotPik_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (GET_GAME_STATE (ZOQFOT_DISTRESS) != 1)
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+static bool
+GenerateZoqFotPik_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 138L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateZoqFotPik_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (ZOQFOTPIK_SHIP))
+ {
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (GET_GAME_STATE (ZOQFOT_DISTRESS))
+ {
+ CloneShipFragment (BLACK_URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (BLACKURQ_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ if (GetHeadLink (&GLOBAL (npc_built_ship_q)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ return true;
+ }
+ }
+
+ CloneShipFragment (ZOQFOTPIK_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (ZOQFOTPIK_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateZoqFotPik_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateZoqFotPik_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/gentopo.c b/src/uqm/planets/gentopo.c
new file mode 100644
index 0000000..5212143
--- /dev/null
+++ b/src/uqm/planets/gentopo.c
@@ -0,0 +1,206 @@
+//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.
+ */
+
+// See doc/devel/planettopo for details.
+
+#include "libs/gfxlib.h"
+#include "libs/mathlib.h"
+#include "planets.h"
+
+void
+DeltaTopography (COUNT num_iterations, SBYTE *DepthArray, RECT *pRect,
+ SIZE depth_delta)
+{
+ SIZE width, height, delta_y;
+ struct
+ {
+ COORD x_top, x_bot;
+ SIZE x_incr, delta_x, error_term;
+ } LineDDA0, LineDDA1;
+
+ width = pRect->extent.width;
+ height = pRect->extent.height;
+ delta_y = (height - 1) << 1;
+ do
+ {
+ SIZE d;
+ COUNT h, w1, w2;
+ DWORD rand_val;
+ SBYTE *lpDst;
+
+ if ((RandomContext_Random (SysGenRNG) & 1) == 0)
+ depth_delta = -depth_delta;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ w1 = LOWORD (rand_val);
+ w2 = HIWORD (rand_val);
+
+ LineDDA0.x_top = LOBYTE (w1) % width;
+ LineDDA0.x_bot = HIBYTE (w1) % width;
+ LineDDA0.delta_x = (LineDDA0.x_bot - LineDDA0.x_top) << 1;
+ if (LineDDA0.delta_x >= 0)
+ LineDDA0.x_incr = 1;
+ else
+ {
+ LineDDA0.x_incr = -1;
+ LineDDA0.delta_x = -LineDDA0.delta_x;
+ }
+ if (LineDDA0.delta_x > delta_y)
+ LineDDA0.error_term = -(LineDDA0.delta_x >> 1);
+ else
+ LineDDA0.error_term = -(delta_y >> 1);
+
+ LineDDA1.x_top = (LOBYTE (w2) % (width - 1)) + LineDDA0.x_top + 1;
+ LineDDA1.x_bot = (HIBYTE (w2) % (width - 1)) + LineDDA0.x_bot + 1;
+ LineDDA1.delta_x = (LineDDA1.x_bot - LineDDA1.x_top) << 1;
+ if (LineDDA1.delta_x >= 0)
+ LineDDA1.x_incr = 1;
+ else
+ {
+ LineDDA1.x_incr = -1;
+ LineDDA1.delta_x = -LineDDA1.delta_x;
+ }
+ if (LineDDA1.delta_x > delta_y)
+ LineDDA1.error_term = -(LineDDA1.delta_x >> 1);
+ else
+ LineDDA1.error_term = -(delta_y >> 1);
+
+ lpDst = &DepthArray[LineDDA0.x_top];
+ h = height;
+ do
+ {
+ COUNT w;
+
+ w1 = LineDDA1.x_top - LineDDA0.x_top;
+ w2 = width - w1;
+
+ if ((int)(LineDDA0.x_top + w1) > (int)width)
+ w = width - LineDDA0.x_top;
+ else
+ {
+ w = w1;
+ LineDDA0.x_top += w1;
+ }
+ w1 -= w;
+ while (w--)
+ {
+ d = *lpDst + depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ }
+ if (w1 == 0)
+ {
+ if (LineDDA0.x_top == width)
+ {
+ LineDDA0.x_top = 0;
+ lpDst -= width;
+ }
+ }
+ else
+ {
+ LineDDA0.x_top = w1;
+ lpDst -= width;
+ do
+ {
+ d = *lpDst + depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ } while (--w1);
+ }
+
+ if ((int)(LineDDA0.x_top + w2) > (int)width)
+ w = width - LineDDA0.x_top;
+ else
+ {
+ w = w2;
+ LineDDA0.x_top += w2;
+ }
+ w2 -= w;
+ while (w--)
+ {
+ d = *lpDst - depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ }
+ if (w2 == 0)
+ {
+ if (LineDDA0.x_top == width)
+ {
+ LineDDA0.x_top = 0;
+ lpDst -= width;
+ }
+ }
+ else
+ {
+ LineDDA0.x_top = w2;
+ lpDst -= width;
+ do
+ {
+ d = *lpDst - depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ } while (--w2);
+ }
+
+ lpDst += pRect->extent.width;
+
+ if (delta_y >= LineDDA0.delta_x)
+ {
+ if ((LineDDA0.error_term += LineDDA0.delta_x) >= 0)
+ {
+ lpDst += LineDDA0.x_incr;
+ LineDDA0.x_top += LineDDA0.x_incr;
+ LineDDA0.error_term -= delta_y;
+ }
+ }
+ else
+ {
+ do
+ {
+ lpDst += LineDDA0.x_incr;
+ LineDDA0.x_top += LineDDA0.x_incr;
+ } while ((LineDDA0.error_term += delta_y) < 0);
+ LineDDA0.error_term -= LineDDA0.delta_x;
+ }
+
+ if (delta_y >= LineDDA1.delta_x)
+ {
+ if ((LineDDA1.error_term += LineDDA1.delta_x) >= 0)
+ {
+ LineDDA1.x_top += LineDDA1.x_incr;
+ LineDDA1.error_term -= delta_y;
+ }
+ }
+ else
+ {
+ do
+ {
+ LineDDA1.x_top += LineDDA1.x_incr;
+ } while ((LineDDA1.error_term += delta_y) < 0);
+ LineDDA1.error_term -= LineDDA1.delta_x;
+ }
+ } while (--h);
+ } while (--num_iterations);
+}
+
+
+
diff --git a/src/uqm/planets/lander.c b/src/uqm/planets/lander.c
new file mode 100644
index 0000000..17aad8f
--- /dev/null
+++ b/src/uqm/planets/lander.c
@@ -0,0 +1,2101 @@
+//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 "lander.h"
+
+#include "lifeform.h"
+#include "scan.h"
+#include "../cons_res.h"
+#include "../controls.h"
+#include "../colors.h"
+#include "../process.h"
+#include "../units.h"
+#include "../gamestr.h"
+#include "../nameref.h"
+#include "../resinst.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../element.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+//define SPIN_ON_LAUNCH to let the planet spin while
+// the lander animation is playing
+#define SPIN_ON_LAUNCH
+
+// PLANET_SIDE_RATE governs how fast the lander,
+// bio and planet effects will be
+// We're using the 3DO speed, which is 35 FPS
+// The PC speed was 30 FPS.
+// Remember that all values need to evenly divide
+// ONE_SECOND.
+#define PLANET_SIDE_RATE (ONE_SECOND / 35)
+
+
+// This is a derived type from INPUT_STATE_DESC.
+typedef struct LanderInputState LanderInputState;
+struct LanderInputState {
+ // Fields required by DoInput()
+ BOOLEAN (*InputFunc) (LanderInputState *pMS);
+
+ BOOLEAN Initialized;
+ TimeCount NextTime;
+ // Frame rate control
+};
+
+FRAME LanderFrame[8];
+static SOUND LanderSounds;
+MUSIC_REF LanderMusic;
+#define NUM_ORBIT_THEMES 5
+static MUSIC_REF OrbitMusic[NUM_ORBIT_THEMES];
+
+const LIFEFORM_DESC CreatureData[] =
+{
+ {SPEED_MOTIONLESS | DANGER_HARMLESS, MAKE_BYTE (1, 1)},
+ // Roto-Dendron
+ {SPEED_MOTIONLESS | DANGER_HARMLESS, MAKE_BYTE (6, 1)},
+ // Macrocillia
+ {SPEED_MOTIONLESS | DANGER_WEAK, MAKE_BYTE (3, 1)},
+ // Splort Wort
+ {SPEED_MOTIONLESS | DANGER_NORMAL, MAKE_BYTE (5, 3)},
+ // Whackin' Bush
+ {SPEED_MOTIONLESS | DANGER_HARMLESS, MAKE_BYTE (2, 10)},
+ // Slot Machine Tree
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_HARMLESS, MAKE_BYTE (1, 2)},
+ // Neon Worm
+ {BEHAVIOR_FLEE | AWARENESS_MEDIUM | SPEED_SLOW | DANGER_HARMLESS, MAKE_BYTE (8, 5)},
+ // Stiletto Urchin
+ {BEHAVIOR_HUNT | AWARENESS_LOW | SPEED_SLOW | DANGER_WEAK, MAKE_BYTE (2, 2)},
+ // Deluxe Blob
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_NORMAL, MAKE_BYTE (3, 8)},
+ // Glowing Medusa
+ {BEHAVIOR_HUNT | AWARENESS_MEDIUM | SPEED_SLOW | DANGER_MONSTROUS, MAKE_BYTE (10, 15)},
+ // Carousel Beast
+ {BEHAVIOR_HUNT | AWARENESS_MEDIUM | SPEED_MEDIUM | DANGER_WEAK, MAKE_BYTE (3, 3)},
+ // Mysterious Bees
+ {BEHAVIOR_FLEE | AWARENESS_MEDIUM | SPEED_MEDIUM | DANGER_HARMLESS, MAKE_BYTE (2, 1)},
+ // Hopping Blobby
+ {BEHAVIOR_UNPREDICTABLE | SPEED_MEDIUM | DANGER_WEAK, MAKE_BYTE (2, 2)},
+ // Blood Monkey
+ {BEHAVIOR_HUNT | AWARENESS_HIGH | SPEED_MEDIUM | DANGER_NORMAL, MAKE_BYTE (4, 6)},
+ // Yompin Yiminy
+ {BEHAVIOR_UNPREDICTABLE | SPEED_MEDIUM | DANGER_MONSTROUS, MAKE_BYTE (9, 12)},
+ // Amorphous Trandicula
+ {BEHAVIOR_HUNT | AWARENESS_HIGH | SPEED_FAST | DANGER_WEAK, MAKE_BYTE (3, 1)},
+ // Crazy Weasel
+ {BEHAVIOR_FLEE | AWARENESS_HIGH | SPEED_FAST | DANGER_HARMLESS, MAKE_BYTE (1, 1)},
+ // Merry Whumpet
+ {BEHAVIOR_HUNT | AWARENESS_LOW | SPEED_FAST | DANGER_NORMAL, MAKE_BYTE (7, 8)},
+ // Fungal Squid
+ {BEHAVIOR_FLEE | AWARENESS_HIGH | SPEED_FAST | DANGER_WEAK, MAKE_BYTE (15, 2)},
+ // Penguin Cyclops
+ {BEHAVIOR_FLEE | AWARENESS_LOW | SPEED_FAST | DANGER_WEAK, MAKE_BYTE (1, 1)},
+ // Chicken
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_WEAK, MAKE_BYTE (6, 2)},
+ // Bubble Vine
+ {BEHAVIOR_FLEE | AWARENESS_HIGH | SPEED_SLOW | DANGER_WEAK, MAKE_BYTE (4, 2)},
+ // Bug-Eyed Bait
+ {SPEED_MOTIONLESS | DANGER_WEAK, MAKE_BYTE (8, 5)},
+ // Goo Burger
+
+ {SPEED_MOTIONLESS | DANGER_MONSTROUS, MAKE_BYTE (1, 1)},
+ // Evil One
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_HARMLESS, MAKE_BYTE (0, 1)},
+ // Brainbox Bulldozers
+ {BEHAVIOR_HUNT | AWARENESS_HIGH | SPEED_FAST | DANGER_MONSTROUS, MAKE_BYTE (15, 15)},
+ // Zex's Beauty
+};
+
+
+extern PRIM_LINKS DisplayLinks;
+
+#define DAMAGE_CYCLE 6
+// XXX: There are actually only 9 explosion images.
+// The last frame is drawn twice.
+#define EXPLOSION_LIFE 10
+// How long to wait after the lander explodes, so that the full
+// gravity of the player's situation sinks in
+#define EXPLOSION_WAIT (ONE_SECOND * 2)
+#define EXPLOSION_WAIT_FRAMES (EXPLOSION_WAIT / PLANET_SIDE_RATE)
+// The actual number of frame that the explosion and wait takes is:
+// EXPLOSION_LIFE * 3 + EXPLOSION_WAIT_FRAMES
+
+#define DEATH_EXPLOSION 0
+
+// TODO: redefine these in terms of CONTEXT width/height
+#define SURFACE_WIDTH SIS_SCREEN_WIDTH
+#define SURFACE_HEIGHT (SIS_SCREEN_HEIGHT - MAP_HEIGHT - MAP_BORDER_HEIGHT)
+
+#define REPAIR_LANDER (1 << 7)
+#define REPAIR_TRANSITION (1 << 6)
+#define KILL_CREW (1 << 5)
+#define ADD_AT_END (1 << 4)
+#define REPAIR_COUNT (0xf)
+
+#define LANDER_SPEED_DENOM 10
+
+static BYTE lander_flags;
+static POINT curLanderLoc;
+static int crew_left;
+static int shieldHit;
+ // which shield was hit, assuming it helped
+static int damage_index;
+ // number of lander damage frames left
+static int explosion_index;
+ // lander explosion progression. Semantics are similar to an
+ // inverse of ELEMENT.life_span
+static int turn_wait;
+ // thus named for similar semantics to ELEMENT.turn_wait
+static int weapon_wait;
+ // semantics similar to STARSHIP.weapon_counter
+
+// TODO: We may want to make the PLANETSIDE_DESC fields into static vars
+static PLANETSIDE_DESC *planetSideDesc;
+
+#define ON_THE_GROUND 0
+
+
+static Color
+DamageColorCycle (Color c, COUNT i)
+{
+ static const Color damage_tab[DAMAGE_CYCLE + 1] =
+ {
+ WHITE_COLOR_INIT,
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1C, 0x00), 0x78),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x0A), 0x0E),
+ };
+
+ if (i)
+ c = damage_tab[i];
+ else if (sameColor(c, WHITE_COLOR))
+ c = damage_tab[6];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E)))
+ c = damage_tab[5];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1C, 0x00), 0x78)))
+ c = damage_tab[4];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)))
+ c = damage_tab[3];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0E, 0x00), 0x7C)))
+ c = damage_tab[2];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x07, 0x00), 0x7E)))
+ c = damage_tab[1];
+ else
+ c = damage_tab[0];
+
+ return c;
+}
+
+static HELEMENT AddGroundDisaster (COUNT which_disaster);
+
+void
+object_animation (ELEMENT *ElementPtr)
+{
+ COUNT frame_index, angle;
+ PRIMITIVE *pPrim;
+
+ pPrim = &DisplayArray[ElementPtr->PrimIndex];
+ if (GetPrimType (pPrim) == STAMPFILL_PRIM
+ && !((ElementPtr->state_flags & FINITE_LIFE)
+ && ElementPtr->mass_points == EARTHQUAKE_DISASTER))
+ {
+ Color c;
+
+ c = DamageColorCycle (GetPrimColor (pPrim), 0);
+ if (sameColor(c, WHITE_COLOR))
+ {
+ SetPrimType (pPrim, STAMP_PRIM);
+ if (ElementPtr->hit_points == 0)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ pPrim->Object.Stamp.frame =
+ SetAbsFrameIndex (pPrim->Object.Stamp.frame, 0);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LIFEFORM_CANNED),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+ }
+
+ SetPrimColor (pPrim, c);
+ }
+
+ frame_index = GetFrameIndex (pPrim->Object.Stamp.frame) + 1;
+ if (LONIBBLE (ElementPtr->turn_wait))
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->turn_wait += HINIBBLE (ElementPtr->turn_wait);
+
+ pPrim->Object.Stamp.frame = IncFrameIndex (pPrim->Object.Stamp.frame);
+
+ if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ /* A natural disaster */
+ if (ElementPtr->mass_points == DEATH_EXPLOSION)
+ { // Lander explosion
+ ++explosion_index;
+ if (explosion_index >= EXPLOSION_LIFE)
+ { // XXX: The last frame is drawn twice
+ pPrim->Object.Stamp.frame =
+ DecFrameIndex (pPrim->Object.Stamp.frame);
+ }
+ }
+ else if (ElementPtr->mass_points == EARTHQUAKE_DISASTER)
+ {
+ SIZE s;
+
+ if (frame_index >= 13)
+ s = 0;
+ else
+ s = (14 - frame_index) >> 1;
+ // XXX: Was 0x8000 the background flag on 3DO?
+ //SetPrimColor (pPrim, BUILD_COLOR (0x8000 | MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+ SetPrimColor (pPrim, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+ if (frame_index == 13)
+ PlaySound (SetAbsSoundIndex (LanderSounds, EARTHQUAKE_DISASTER),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+
+ if (ElementPtr->mass_points == LAVASPOT_DISASTER
+ && frame_index == 5
+ && TFB_Random () % 100 < 90)
+ {
+ HELEMENT hLavaElement;
+
+ /* Change lava-spot direction of travel */
+ hLavaElement = AddGroundDisaster (LAVASPOT_DISASTER);
+ if (hLavaElement)
+ {
+ ELEMENT *LavaElementPtr;
+
+ angle = FACING_TO_ANGLE (ElementPtr->facing);
+ LockElement (hLavaElement, &LavaElementPtr);
+ LavaElementPtr->next.location = ElementPtr->next.location;
+ LavaElementPtr->next.location.x += COSINE (angle, 4);
+ LavaElementPtr->next.location.y += SINE (angle, 4);
+ if (LavaElementPtr->next.location.y < 0)
+ LavaElementPtr->next.location.y = 0;
+ else if (LavaElementPtr->next.location.y >= (MAP_HEIGHT << MAG_SHIFT))
+ LavaElementPtr->next.location.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+ if (LavaElementPtr->next.location.x < 0)
+ LavaElementPtr->next.location.x += MAP_WIDTH << MAG_SHIFT;
+ else
+ LavaElementPtr->next.location.x %= MAP_WIDTH << MAG_SHIFT;
+ LavaElementPtr->facing = NORMALIZE_FACING (
+ ElementPtr->facing + (TFB_Random () % 3 - 1));
+ UnlockElement (hLavaElement);
+ }
+ }
+ }
+ else if (!(frame_index & 3) && ElementPtr->hit_points)
+ {
+ BYTE index;
+ COUNT speed;
+
+ index = ElementPtr->mass_points & ~CREATURE_AWARE;
+ speed = CreatureData[index].Attributes & SPEED_MASK;
+ if (speed)
+ {
+ SIZE dx, dy;
+ COUNT old_angle;
+
+ dx = curLanderLoc.x - ElementPtr->next.location.x;
+ if (dx < 0 && dx < -(MAP_WIDTH << (MAG_SHIFT - 1)))
+ dx += MAP_WIDTH << MAG_SHIFT;
+ else if (dx > (MAP_WIDTH << (MAG_SHIFT - 1)))
+ dx -= MAP_WIDTH << MAG_SHIFT;
+ dy = curLanderLoc.y - ElementPtr->next.location.y;
+ angle = ARCTAN (dx, dy);
+ if (dx < 0)
+ dx = -dx;
+ if (dy < 0)
+ dy = -dy;
+
+ if (dx >= SURFACE_WIDTH || dy >= SURFACE_WIDTH
+ || dx * dx + dy * dy >= SURFACE_WIDTH * SURFACE_WIDTH)
+ ElementPtr->mass_points &= ~CREATURE_AWARE;
+ else if (!(ElementPtr->mass_points & CREATURE_AWARE))
+ {
+ BYTE DetectPercent;
+
+ DetectPercent = (((BYTE)(CreatureData[index].Attributes
+ & AWARENESS_MASK) >> AWARENESS_SHIFT) + 1)
+ * (30 / 6);
+ // XXX: Shouldn't this be dependent on
+ // PLANET_SIDE_RATE somehow? And why is it
+ // written as '30 / 6' instead of 5? Does the 30
+ // specify the (PC) framerate? That doesn't make
+ // sense; I would expect it to be in the
+ // denominator. And even then, it wouldn't give
+ // the same results with different frame rates,
+ // as repeating 'random(x / 30)' 30 times doesn't
+ // generally have the same result as repeating
+ // 'random(x / 35)' 25 times. - SvdB
+ if (TFB_Random () % 100 < DetectPercent)
+ {
+ ElementPtr->thrust_wait = 0;
+ ElementPtr->mass_points |= CREATURE_AWARE;
+ }
+ }
+
+ if (ElementPtr->next.location.y == 0
+ || ElementPtr->next.location.y ==
+ (MAP_HEIGHT << MAG_SHIFT) - 1)
+ ElementPtr->thrust_wait = 0;
+
+ old_angle = GetVelocityTravelAngle (&ElementPtr->velocity);
+ if (ElementPtr->thrust_wait)
+ {
+ --ElementPtr->thrust_wait;
+ angle = old_angle;
+ }
+ else if (!(ElementPtr->mass_points & CREATURE_AWARE)
+ || (CreatureData[index].Attributes
+ & BEHAVIOR_MASK) == BEHAVIOR_UNPREDICTABLE)
+ {
+ COUNT rand_val;
+
+ rand_val = TFB_Random ();
+ angle = NORMALIZE_ANGLE (LOBYTE (rand_val));
+ ElementPtr->thrust_wait =
+ (HIBYTE (rand_val) >> 2) + 10;
+ }
+ else if ((CreatureData[index].Attributes
+ & BEHAVIOR_MASK) == BEHAVIOR_FLEE)
+ {
+ if (ElementPtr->next.location.y == 0
+ || ElementPtr->next.location.y ==
+ (MAP_HEIGHT << MAG_SHIFT) - 1)
+ {
+ if (angle & (HALF_CIRCLE - 1))
+ angle = HALF_CIRCLE - angle;
+ else if (old_angle == QUADRANT
+ || old_angle == (FULL_CIRCLE - QUADRANT))
+ angle = old_angle;
+ else
+ angle = ((TFB_Random () & 1)
+ * HALF_CIRCLE) - QUADRANT;
+ ElementPtr->thrust_wait = 5;
+ }
+ angle = NORMALIZE_ANGLE (angle + HALF_CIRCLE);
+ }
+
+ switch (speed)
+ {
+ case SPEED_SLOW:
+ speed = WORLD_TO_VELOCITY (2 * 1) >> 2;
+ break;
+ case SPEED_MEDIUM:
+ speed = WORLD_TO_VELOCITY (2 * 1) >> 1;
+ break;
+ case SPEED_FAST:
+ speed = WORLD_TO_VELOCITY (2 * 1) * 9 / 10;
+ break;
+ }
+
+ SetVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, speed), SINE (angle, speed));
+ }
+ }
+ }
+
+ if ((ElementPtr->state_flags & FINITE_LIFE)
+ && ElementPtr->mass_points == DEATH_EXPLOSION
+ && GetSuccLink (DisplayLinks) != ElementPtr->PrimIndex)
+ lander_flags |= ADD_AT_END;
+}
+
+#define NUM_CREW_COLS 6
+#define NUM_CREW_ROWS 2
+
+static void
+DeltaLanderCrew (SIZE crew_delta, COUNT which_disaster)
+{
+ STAMP s;
+ CONTEXT OldContext;
+
+ if (crew_delta > 0)
+ {
+ // Filling up the crew bar when landing.
+ crew_delta = crew_left;
+ crew_left += 1;
+
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 55);
+ }
+ else /* if (crew_delta < 0) */
+ {
+ if (crew_left < 1)
+ return; // irrelevant -- all dead
+
+ shieldHit = GET_GAME_STATE (LANDER_SHIELDS);
+ shieldHit &= 1 << which_disaster;
+ if (!shieldHit || TFB_Random () % 100 >= 95)
+ { // No shield, or it did not help
+ shieldHit = 0;
+ --crew_left;
+ }
+
+ damage_index = DAMAGE_CYCLE;
+ if (shieldHit)
+ return;
+
+ crew_delta = crew_left;
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 56);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_INJURED),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+
+ s.origin.x = 11 + (6 * (crew_delta % NUM_CREW_COLS));
+ s.origin.y = 35 - (6 * (crew_delta / NUM_CREW_COLS));
+
+ OldContext = SetContext (RadarContext);
+ DrawStamp (&s);
+ SetContext (OldContext);
+}
+
+static void
+FillLanderHold (PLANETSIDE_DESC *pPSD, COUNT scan, COUNT NumRetrieved)
+{
+ COUNT start_count;
+ STAMP s;
+ CONTEXT OldContext;
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_PICKUP),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+
+ if (scan == BIOLOGICAL_SCAN)
+ {
+ start_count = pPSD->BiologicalLevel;
+
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 41);
+
+ pPSD->BiologicalLevel += NumRetrieved;
+ }
+ else
+ {
+ start_count = pPSD->ElementLevel;
+ pPSD->ElementLevel += NumRetrieved;
+ if (GET_GAME_STATE (IMPROVED_LANDER_CARGO))
+ {
+ start_count >>= 1;
+ NumRetrieved = (pPSD->ElementLevel >> 1) - start_count;
+ }
+
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 43);
+ }
+
+ s.origin.x = 0;
+ s.origin.y = -(int)start_count;
+ if (!(start_count & 1))
+ s.frame = IncFrameIndex (s.frame);
+
+ OldContext = SetContext (RadarContext);
+ while (NumRetrieved--)
+ {
+ if (start_count++ & 1)
+ s.frame = IncFrameIndex (s.frame);
+ else
+ s.frame = DecFrameIndex (s.frame);
+ DrawStamp (&s);
+ --s.origin.y;
+ }
+ SetContext (OldContext);
+}
+
+// returns true iff the node was picked up.
+static bool
+pickupMineralNode (PLANETSIDE_DESC *pPSD, COUNT NumRetrieved,
+ ELEMENT *ElementPtr, const INTERSECT_CONTROL *LanderControl,
+ const INTERSECT_CONTROL *ElementControl)
+{
+ BYTE EType;
+ UNICODE ch;
+ UNICODE *pStr;
+
+ if (pPSD->ElementLevel >= pPSD->MaxElementLevel)
+ {
+ // Lander full
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_FULL),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ return false;
+ }
+
+ if (pPSD->ElementLevel + NumRetrieved > pPSD->MaxElementLevel)
+ {
+ // Deposit could only be picked up partially.
+ NumRetrieved = (COUNT)(pPSD->MaxElementLevel - pPSD->ElementLevel);
+ }
+
+ FillLanderHold (pPSD, MINERAL_SCAN, NumRetrieved);
+
+ EType = ElementPtr->turn_wait;
+ pPSD->ElementAmounts[ElementCategory (EType)] += NumRetrieved;
+
+ pPSD->NumFrames = NUM_TEXT_FRAMES;
+ sprintf (pPSD->AmountBuf, "%u", NumRetrieved);
+ pStr = GAME_STRING (EType + ELEMENTS_STRING_BASE);
+
+ pPSD->MineralText[0].baseline.x = (SURFACE_WIDTH >> 1)
+ + (ElementControl->EndPoint.x - LanderControl->EndPoint.x);
+ pPSD->MineralText[0].baseline.y = (SURFACE_HEIGHT >> 1)
+ + (ElementControl->EndPoint.y - LanderControl->EndPoint.y);
+ pPSD->MineralText[0].CharCount = (COUNT)~0;
+ pPSD->MineralText[1].pStr = pStr;
+
+ while ((ch = *pStr++) && ch != ' ')
+ ;
+ if (ch == '\0')
+ {
+ pPSD->MineralText[1].CharCount = (COUNT)~0;
+ pPSD->MineralText[2].CharCount = 0;
+ }
+ else /* ch == ' ' */
+ {
+ // Name contains a space. Print over
+ // two lines.
+ pPSD->MineralText[1].CharCount = utf8StringCountN(
+ pPSD->MineralText[1].pStr, pStr - 1);
+ pPSD->MineralText[2].pStr = pStr;
+ pPSD->MineralText[2].CharCount = (COUNT)~0;
+ }
+
+ return true;
+}
+
+static bool
+pickupBioNode (PLANETSIDE_DESC *pPSD, COUNT NumRetrieved)
+{
+ if (pPSD->BiologicalLevel >= MAX_SCROUNGED)
+ {
+ // Lander is full.
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_FULL),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ return false;
+ }
+
+ if (pPSD->BiologicalLevel + NumRetrieved > MAX_SCROUNGED)
+ {
+ // Node could only be picked up partially.
+ NumRetrieved = (COUNT)(MAX_SCROUNGED - pPSD->BiologicalLevel);
+ }
+
+ FillLanderHold (pPSD, BIOLOGICAL_SCAN, NumRetrieved);
+
+ return true;
+}
+
+static void
+shotCreature (ELEMENT *ElementPtr, BYTE value,
+ INTERSECT_CONTROL *LanderControl, PRIMITIVE *pPrim)
+{
+ if (ElementPtr->hit_points == 0)
+ {
+ // Creature is already canned.
+ return;
+ }
+
+ --ElementPtr->hit_points;
+ if (ElementPtr->hit_points == 0)
+ {
+ // Can creature.
+ ElementPtr->mass_points = value;
+ DisplayArray[ElementPtr->PrimIndex].Object.Stamp.frame =
+ pSolarSysState->PlanetSideFrame[0];
+ }
+ else if (CreatureData[ElementPtr->mass_points & ~CREATURE_AWARE]
+ .Attributes & SPEED_MASK)
+ {
+ COUNT angle;
+
+ angle = FACING_TO_ANGLE (GetFrameIndex (
+ LanderControl->IntersectStamp.frame) -
+ ANGLE_TO_FACING (FULL_CIRCLE));
+ DeltaVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (1)),
+ SINE (angle, WORLD_TO_VELOCITY (1)));
+ ElementPtr->thrust_wait = 0;
+ ElementPtr->mass_points |= CREATURE_AWARE;
+ }
+
+ SetPrimType (pPrim, STAMPFILL_PRIM);
+ SetPrimColor (pPrim, WHITE_COLOR);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_HITS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+}
+
+static void
+CheckObjectCollision (COUNT index)
+{
+ INTERSECT_CONTROL LanderControl;
+ DRAWABLE LanderHandle;
+ PRIMITIVE *pPrim;
+ PRIMITIVE *pLanderPrim;
+ PLANETSIDE_DESC *pPSD = planetSideDesc;
+
+ if (index != END_OF_LIST)
+ {
+ pLanderPrim = &DisplayArray[index];
+ LanderControl.IntersectStamp = pLanderPrim->Object.Stamp;
+ index = GetPredLink (GetPrimLinks (pLanderPrim));
+ }
+ else
+ {
+ pLanderPrim = 0;
+ LanderControl.IntersectStamp.origin.x = SURFACE_WIDTH >> 1;
+ LanderControl.IntersectStamp.origin.y = SURFACE_HEIGHT >> 1;
+ LanderControl.IntersectStamp.frame = LanderFrame[0];
+ index = GetSuccLink (DisplayLinks);
+ }
+
+ LanderControl.EndPoint = LanderControl.IntersectStamp.origin;
+ LanderHandle = GetFrameParentDrawable (LanderControl.IntersectStamp.frame);
+
+ for (; index != END_OF_LIST; index = GetPredLink (GetPrimLinks (pPrim)))
+ {
+ INTERSECT_CONTROL ElementControl;
+ HELEMENT hElement, hNextElement;
+
+ pPrim = &DisplayArray[index];
+ ElementControl.IntersectStamp = pPrim->Object.Stamp;
+ ElementControl.EndPoint = ElementControl.IntersectStamp.origin;
+
+ if (GetFrameParentDrawable (ElementControl.IntersectStamp.frame)
+ == LanderHandle)
+ {
+ CheckObjectCollision (index);
+ continue;
+ }
+
+ if (!DrawablesIntersect (&LanderControl,
+ &ElementControl, MAX_TIME_VALUE))
+ continue;
+
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+
+ if (&DisplayArray[ElementPtr->PrimIndex] == pLanderPrim)
+ {
+ ElementPtr->state_flags |= DISAPPEARING;
+ UnlockElement (hElement);
+ continue;
+ }
+
+ if (&DisplayArray[ElementPtr->PrimIndex] != pPrim
+ || ElementPtr->playerNr != PS_NON_PLAYER)
+ {
+ UnlockElement (hElement);
+ continue;
+ }
+
+ {
+ COUNT scan, NumRetrieved;
+ SIZE which_node;
+
+ scan = LOBYTE (ElementPtr->scan_node);
+ if (pLanderPrim == 0)
+ {
+ /* Collision of lander with another object */
+ if (crew_left == 0 || pPSD->InTransit)
+ break;
+
+ if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ /* A natural disaster */
+ scan = ElementPtr->mass_points;
+ switch (scan)
+ {
+ case EARTHQUAKE_DISASTER:
+ case LAVASPOT_DISASTER:
+ if (TFB_Random () % 100 < 25)
+ DeltaLanderCrew (-1, scan);
+ break;
+ }
+
+ UnlockElement (hElement);
+ continue;
+ }
+ else if (scan == ENERGY_SCAN)
+ {
+ // noop; handled by generation funcs, see below
+ }
+ else if (scan == BIOLOGICAL_SCAN && ElementPtr->hit_points)
+ {
+ BYTE danger_vals[] =
+ {
+ 0, 6, 13, 26
+ };
+ int creatureIndex = ElementPtr->mass_points
+ & ~CREATURE_AWARE;
+ int dangerLevel =
+ (CreatureData[creatureIndex].Attributes &
+ DANGER_MASK) >> DANGER_SHIFT;
+
+ if (TFB_Random () % 128 < danger_vals[dangerLevel])
+ {
+ PlaySound (SetAbsSoundIndex (
+ LanderSounds, BIOLOGICAL_DISASTER),
+ NotPositional (), NULL,
+ GAME_SOUND_PRIORITY);
+ DeltaLanderCrew (-1, BIOLOGICAL_DISASTER);
+ }
+ UnlockElement (hElement);
+ continue;
+ }
+
+ NumRetrieved = ElementPtr->mass_points;
+ }
+ else if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ /* Collision of a stun bolt with a natural disaster */
+ UnlockElement (hElement);
+ continue;
+ }
+ else
+ {
+ BYTE value;
+
+ if (scan == ENERGY_SCAN)
+ {
+ /* Collision of a stun bolt with an energy node */
+ UnlockElement (hElement);
+ break;
+ }
+
+ if (scan == BIOLOGICAL_SCAN
+ && (value = LONIBBLE (CreatureData[
+ ElementPtr->mass_points
+ & ~CREATURE_AWARE
+ ].ValueAndHitPoints)))
+ {
+ /* Collision of a stun bolt with a viable creature */
+ shotCreature (ElementPtr, value, &LanderControl,
+ pPrim);
+ UnlockElement (hElement);
+ break;
+ }
+
+ NumRetrieved = 0;
+ }
+
+ if (NumRetrieved)
+ {
+ switch (scan)
+ {
+ case ENERGY_SCAN:
+ break;
+ case MINERAL_SCAN:
+ if (!pickupMineralNode (pPSD, NumRetrieved,
+ ElementPtr, &LanderControl,
+ &ElementControl))
+ continue;
+ break;
+ case BIOLOGICAL_SCAN:
+ if (!pickupBioNode (pPSD, NumRetrieved))
+ continue;
+ break;
+ }
+ }
+
+ which_node = HIBYTE (ElementPtr->scan_node) - 1;
+ if (callPickupForScanType (pSolarSysState,
+ pSolarSysState->pOrbitalDesc, which_node, scan))
+ { // Node retrieved, remove from the surface
+ setNodeRetrieved (&pSolarSysState->SysInfo.PlanetInfo,
+ scan, which_node);
+ SET_GAME_STATE (PLANETARY_CHANGE, 1);
+ ElementPtr->state_flags |= DISAPPEARING;
+ }
+ UnlockElement (hElement);
+ }
+ }
+ }
+}
+
+static void
+lightning_process (ELEMENT *ElementPtr)
+{
+ PRIMITIVE *pPrim;
+
+ pPrim = &DisplayArray[ElementPtr->PrimIndex];
+ if (LONIBBLE (ElementPtr->turn_wait))
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT num_frames;
+
+ num_frames = GetFrameCount (pPrim->Object.Stamp.frame) - 7;
+ if (GetFrameIndex (pPrim->Object.Stamp.frame) >= num_frames)
+ {
+ /* Advance to the next surface strike effect frame */
+ // XXX: This is unused, we never get here
+ pPrim->Object.Stamp.frame =
+ IncFrameIndex (pPrim->Object.Stamp.frame);
+ }
+ else
+ {
+ SIZE s;
+
+ // XXX: Color cycling is largely unused, because the color
+ // never actually changes RGB values (see MAKE_RGB15 below).
+ // This did, however, work in DOS SC2 version (fade effect).
+ s = 7 - ((SIZE)ElementPtr->cycle - (SIZE)ElementPtr->life_span);
+ if (s < 0)
+ s = 0;
+ // XXX: Was 0x8000 the background flag on 3DO?
+ //SetPrimColor (pPrim, BUILD_COLOR (0x8000 | MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+ SetPrimColor (pPrim, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+
+ if (ElementPtr->mass_points == LIGHTNING_DISASTER)
+ {
+ /* This one always strikes the lander and can hurt */
+ if (crew_left && TFB_Random () % 100 < 10
+ && !planetSideDesc->InTransit)
+ lander_flags |= KILL_CREW;
+
+ ElementPtr->next.location = curLanderLoc;
+ }
+
+ pPrim->Object.Stamp.frame =
+ SetAbsFrameIndex (pPrim->Object.Stamp.frame,
+ TFB_Random () % num_frames);
+ }
+
+ ElementPtr->turn_wait += HINIBBLE (ElementPtr->turn_wait);
+ }
+
+ if (GetSuccLink (DisplayLinks) != ElementPtr->PrimIndex)
+ lander_flags |= ADD_AT_END;
+}
+
+static void
+AddLightning (void)
+{
+ HELEMENT hLightningElement;
+
+ hLightningElement = AllocElement ();
+ if (hLightningElement)
+ {
+ DWORD rand_val;
+ ELEMENT *LightningElementPtr;
+
+ LockElement (hLightningElement, &LightningElementPtr);
+
+ LightningElementPtr->playerNr = PS_NON_PLAYER;
+ LightningElementPtr->state_flags = FINITE_LIFE;
+ LightningElementPtr->preprocess_func = lightning_process;
+ if (TFB_Random () % 100 >= 25)
+ LightningElementPtr->mass_points = 0; /* harmless */
+ else
+ LightningElementPtr->mass_points = LIGHTNING_DISASTER;
+
+ rand_val = TFB_Random ();
+ LightningElementPtr->life_span = 10 + (HIWORD (rand_val) % 10) + 1;
+ LightningElementPtr->next.location.x = (curLanderLoc.x
+ + ((MAP_WIDTH << MAG_SHIFT) - ((SURFACE_WIDTH >> 1) - 6))
+ + (LOBYTE (rand_val) % (SURFACE_WIDTH - 12))
+ ) % (MAP_WIDTH << MAG_SHIFT);
+ LightningElementPtr->next.location.y = (curLanderLoc.y
+ + ((MAP_HEIGHT << MAG_SHIFT) - ((SURFACE_HEIGHT >> 1) - 6))
+ + (HIBYTE (rand_val) % (SURFACE_HEIGHT - 12))
+ ) % (MAP_HEIGHT << MAG_SHIFT);
+
+ LightningElementPtr->cycle = LightningElementPtr->life_span;
+
+ SetPrimType (&DisplayArray[LightningElementPtr->PrimIndex], STAMPFILL_PRIM);
+ SetPrimColor (&DisplayArray[LightningElementPtr->PrimIndex], WHITE_COLOR);
+ DisplayArray[LightningElementPtr->PrimIndex].Object.Stamp.frame =
+ LanderFrame[2];
+
+ UnlockElement (hLightningElement);
+
+ PutElement (hLightningElement);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LIGHTNING_DISASTER),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+}
+
+static HELEMENT
+AddGroundDisaster (COUNT which_disaster)
+{
+ HELEMENT hGroundDisasterElement;
+
+ hGroundDisasterElement = AllocElement ();
+ if (hGroundDisasterElement)
+ {
+ DWORD rand_val;
+ ELEMENT *GroundDisasterElementPtr;
+ PRIMITIVE *pPrim;
+
+ LockElement (hGroundDisasterElement, &GroundDisasterElementPtr);
+
+ pPrim = &DisplayArray[GroundDisasterElementPtr->PrimIndex];
+ GroundDisasterElementPtr->mass_points = which_disaster;
+ GroundDisasterElementPtr->playerNr = PS_NON_PLAYER;
+ GroundDisasterElementPtr->state_flags = FINITE_LIFE;
+ GroundDisasterElementPtr->preprocess_func = object_animation;
+
+ rand_val = TFB_Random ();
+ GroundDisasterElementPtr->next.location.x = (curLanderLoc.x
+ + ((MAP_WIDTH << MAG_SHIFT) - (SURFACE_WIDTH * 3 / 8))
+ + (LOWORD (rand_val) % (SURFACE_WIDTH * 3 / 4))
+ ) % (MAP_WIDTH << MAG_SHIFT);
+ GroundDisasterElementPtr->next.location.y = (curLanderLoc.y
+ + ((MAP_HEIGHT << MAG_SHIFT) - (SURFACE_HEIGHT * 3 / 8))
+ + (HIWORD (rand_val) % (SURFACE_HEIGHT * 3 / 4))
+ ) % (MAP_HEIGHT << MAG_SHIFT);
+
+
+ if (which_disaster == EARTHQUAKE_DISASTER)
+ {
+ SetPrimType (pPrim, STAMPFILL_PRIM);
+ pPrim->Object.Stamp.frame = LanderFrame[1];
+ GroundDisasterElementPtr->turn_wait = MAKE_BYTE (2, 2);
+ }
+ else /* if (which_disaster == LAVASPOT_DISASTER) */
+ {
+ SetPrimType (pPrim, STAMP_PRIM);
+ GroundDisasterElementPtr->facing =
+ NORMALIZE_FACING (TFB_Random ());
+ pPrim->Object.Stamp.frame = LanderFrame[3];
+ GroundDisasterElementPtr->turn_wait = MAKE_BYTE (0, 0);
+ }
+ GroundDisasterElementPtr->life_span =
+ GetFrameCount (pPrim->Object.Stamp.frame)
+ * (LONIBBLE (GroundDisasterElementPtr->turn_wait) + 1) - 1;
+
+ UnlockElement (hGroundDisasterElement);
+
+ PutElement (hGroundDisasterElement);
+ }
+
+ return (hGroundDisasterElement);
+}
+
+// This function replaces the ELEMENT manipulations typically done by
+// PreProcess() and PostProcess() in process.c. Lander code does not
+// call RedrawQueue() & Co and thus does not reap the benefits (or curses,
+// depending how you look at it) of automatic flags processing.
+static void
+BuildObjectList (void)
+{
+ DWORD rand_val;
+ POINT org;
+ HELEMENT hElement, hNextElement;
+ PLANETSIDE_DESC *pPSD = planetSideDesc;
+
+ DisplayLinks = MakeLinks (END_OF_LIST, END_OF_LIST);
+
+ lander_flags &= ~KILL_CREW;
+
+ rand_val = TFB_Random ();
+ if (LOBYTE (HIWORD (rand_val)) < pPSD->FireChance)
+ {
+ AddGroundDisaster (LAVASPOT_DISASTER);
+ PlaySound (SetAbsSoundIndex (LanderSounds, LAVASPOT_DISASTER),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+
+ if (HIBYTE (LOWORD (rand_val)) < pPSD->TectonicsChance)
+ AddGroundDisaster (EARTHQUAKE_DISASTER);
+
+ if (LOBYTE (LOWORD (rand_val)) < pPSD->WeatherChance)
+ AddLightning ();
+
+ org = curLanderLoc;
+ for (hElement = GetHeadElement ();
+ hElement; hElement = hNextElement)
+ {
+ SIZE dx, dy;
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+
+ if (ElementPtr->life_span == 0
+ || (ElementPtr->state_flags & DISAPPEARING))
+ {
+ hNextElement = GetSuccElement (ElementPtr);
+ UnlockElement (hElement);
+ RemoveElement (hElement);
+ FreeElement (hElement);
+ continue;
+ }
+ else if (ElementPtr->state_flags & FINITE_LIFE)
+ --ElementPtr->life_span;
+
+ lander_flags &= ~ADD_AT_END;
+
+ if (ElementPtr->preprocess_func)
+ (*ElementPtr->preprocess_func) (ElementPtr);
+
+ GetNextVelocityComponents (&ElementPtr->velocity, &dx, &dy, 1);
+ if (dx || dy)
+ {
+ ElementPtr->next.location.x += dx;
+ ElementPtr->next.location.y += dy;
+ /* if not lander's shot */
+ if (ElementPtr->playerNr != PS_HUMAN_PLAYER)
+ {
+ if (ElementPtr->next.location.y < 0)
+ ElementPtr->next.location.y = 0;
+ else if (ElementPtr->next.location.y >= (MAP_HEIGHT << MAG_SHIFT))
+ ElementPtr->next.location.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+ }
+ if (ElementPtr->next.location.x < 0)
+ ElementPtr->next.location.x += MAP_WIDTH << MAG_SHIFT;
+ else
+ ElementPtr->next.location.x %= MAP_WIDTH << MAG_SHIFT;
+
+ // XXX: APPEARING flag is set by scan.c for scanned blips
+ if (ElementPtr->state_flags & APPEARING)
+ { // Update the location of a moving object on the scan map
+ ElementPtr->current.location.x =
+ ElementPtr->next.location.x >> MAG_SHIFT;
+ ElementPtr->current.location.y =
+ ElementPtr->next.location.y >> MAG_SHIFT;
+ }
+ }
+
+ {
+ PRIMITIVE *pPrim;
+
+ pPrim = &DisplayArray[ElementPtr->PrimIndex];
+ pPrim->Object.Stamp.origin.x =
+ ElementPtr->next.location.x
+ - org.x + (SURFACE_WIDTH >> 1);
+ if (pPrim->Object.Stamp.origin.x >=
+ (MAP_WIDTH << MAG_SHIFT) - (SURFACE_WIDTH * 3 / 2))
+ pPrim->Object.Stamp.origin.x -= MAP_WIDTH << MAG_SHIFT;
+ else if (pPrim->Object.Stamp.origin.x <=
+ -((MAP_WIDTH << MAG_SHIFT) - (SURFACE_WIDTH * 3 / 2)))
+ pPrim->Object.Stamp.origin.x += MAP_WIDTH << MAG_SHIFT;
+
+ pPrim->Object.Stamp.origin.y =
+ ElementPtr->next.location.y
+ - org.y + (SURFACE_HEIGHT >> 1);
+
+ if (lander_flags & ADD_AT_END)
+ InsertPrim (&DisplayLinks, ElementPtr->PrimIndex, END_OF_LIST);
+ else
+ InsertPrim (&DisplayLinks, ElementPtr->PrimIndex, GetPredLink (DisplayLinks));
+ }
+
+ hNextElement = GetSuccElement (ElementPtr);
+ UnlockElement (hElement);
+ }
+}
+
+static void
+ScrollPlanetSide (SIZE dx, SIZE dy, int landingOffset)
+{
+ POINT new_pt;
+ STAMP lander_s, shadow_s, shield_s;
+ CONTEXT OldContext;
+
+ new_pt.y = curLanderLoc.y + dy;
+ if (new_pt.y < 0)
+ {
+ new_pt.y = 0;
+ dy = new_pt.y - curLanderLoc.y;
+ dx = 0;
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ }
+ else if (new_pt.y > (MAP_HEIGHT << MAG_SHIFT) - 1)
+ {
+ new_pt.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+ dy = new_pt.y - curLanderLoc.y;
+ dx = 0;
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ }
+
+ new_pt.x = curLanderLoc.x + dx;
+ if (new_pt.x < 0)
+ new_pt.x += MAP_WIDTH << MAG_SHIFT;
+ else if (new_pt.x >= MAP_WIDTH << MAG_SHIFT)
+ new_pt.x -= MAP_WIDTH << MAG_SHIFT;
+
+ curLanderLoc = new_pt;
+
+ OldContext = SetContext (PlanetContext);
+
+ BatchGraphics ();
+
+ // Display planet area, accounting for horizontal wrapping if
+ // near the edges.
+ {
+ STAMP s;
+
+ ClearDrawable ();
+ s.origin.x = -new_pt.x + (SURFACE_WIDTH >> 1);
+ s.origin.y = -new_pt.y + (SURFACE_HEIGHT >> 1);
+ s.frame = pSolarSysState->Orbit.TopoZoomFrame;
+ DrawStamp (&s);
+ s.origin.x += MAP_WIDTH << MAG_SHIFT;
+ DrawStamp (&s);
+ s.origin.x -= MAP_WIDTH << (MAG_SHIFT + 1);
+ DrawStamp (&s);
+ }
+
+ BuildObjectList ();
+
+ DrawBatch (DisplayArray, DisplayLinks, 0);
+
+ // Draw the lander while is still alive and keep drawing for a few
+ // frames while it is exploding
+ if (crew_left || damage_index || explosion_index < 3)
+ {
+ lander_s.origin.x = SURFACE_WIDTH >> 1;
+ lander_s.origin.y = (SURFACE_HEIGHT >> 1) + landingOffset;
+ lander_s.frame = LanderFrame[0];
+
+ if (landingOffset != ON_THE_GROUND)
+ { // Landing, draw a shadow
+ shadow_s.origin.x = lander_s.origin.y + (SURFACE_WIDTH >> 1) - (SURFACE_HEIGHT >> 1);//2;
+ shadow_s.origin.y = lander_s.origin.y;
+ shadow_s.frame = lander_s.frame;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledStamp (&shadow_s);
+ }
+
+ if (damage_index == 0)
+ { // No damage -- normal lander
+ DrawStamp (&lander_s);
+ }
+ else if (shieldHit)
+ { // Was protected by a shield
+ --damage_index;
+ if (damage_index > 0)
+ {
+ shield_s.origin = lander_s.origin;
+ shield_s.frame = SetEquFrameIndex (
+ LanderFrame[4], lander_s.frame);
+
+ // XXX: Shouldn't this color-cycle with damage_index?
+ // damage_index is used, but only as a VGA index!
+ /*SetContextForeGroundColor (BUILD_COLOR (
+ MAKE_RGB15 (0x1F, 0x1F, 0x1F) | 0x8000,
+ damage_index));*/
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), damage_index));
+ DrawFilledStamp (&shield_s);
+ }
+ DrawStamp (&lander_s);
+ }
+ else
+ { // Direct hit, no shield
+ --damage_index;
+ SetContextForeGroundColor (
+ DamageColorCycle (BLACK_COLOR, damage_index));
+ DrawFilledStamp (&lander_s);
+ }
+ }
+
+ if (landingOffset == ON_THE_GROUND && crew_left
+ && GetPredLink (DisplayLinks) != END_OF_LIST)
+ CheckObjectCollision (END_OF_LIST);
+
+ {
+ PLANETSIDE_DESC *pPSD = planetSideDesc;
+ if (pPSD->NumFrames)
+ {
+ --pPSD->NumFrames;
+ SetContextForeGroundColor (pPSD->ColorCycle[pPSD->NumFrames >> 1]);
+
+ pPSD->MineralText[0].baseline.x -= dx;
+ pPSD->MineralText[0].baseline.y -= dy;
+ font_DrawText (&pPSD->MineralText[0]);
+ pPSD->MineralText[1].baseline.x =
+ pPSD->MineralText[0].baseline.x;
+ pPSD->MineralText[1].baseline.y =
+ pPSD->MineralText[0].baseline.y + 7;
+ font_DrawText (&pPSD->MineralText[1]);
+ pPSD->MineralText[2].baseline.x =
+ pPSD->MineralText[1].baseline.x;
+ pPSD->MineralText[2].baseline.y =
+ pPSD->MineralText[1].baseline.y + 7;
+ font_DrawText (&pPSD->MineralText[2]);
+ }
+ }
+
+ RedrawSurfaceScan (&new_pt);
+
+ if (lander_flags & KILL_CREW)
+ DeltaLanderCrew (-1, LIGHTNING_DISASTER);
+
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static void
+animationInterframe (TimeCount *TimeIn, COUNT periods)
+{
+#define ANIM_FRAME_RATE (ONE_SECOND / 30)
+
+ for ( ; periods; --periods)
+ {
+ RotatePlanetSphere (TRUE);
+
+ SleepThreadUntil (*TimeIn + ANIM_FRAME_RATE);
+ *TimeIn = GetTimeCounter ();
+ }
+}
+
+static void
+AnimateLaunch (FRAME farray)
+{
+ RECT r;
+ STAMP s;
+ COUNT num_frames;
+ TimeCount NextTime;
+
+ SetContext (PlanetContext);
+
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = 0;
+ r.extent.height = 0;
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = farray;
+
+ for (num_frames = GetFrameCount (s.frame); num_frames; --num_frames)
+ {
+ NextTime = GetTimeCounter () + (ONE_SECOND / 22);
+
+ BatchGraphics ();
+ RepairBackRect (&r);
+#ifdef SPIN_ON_LAUNCH
+ RotatePlanetSphere (FALSE);
+#else
+ DrawDefaultPlanetSphere ();
+#endif
+ DrawStamp (&s);
+ UnbatchGraphics ();
+
+ GetFrameRect (s.frame, &r);
+ s.frame = IncFrameIndex (s.frame);
+
+ SleepThreadUntil (NextTime);
+ }
+
+ RepairBackRect (&r);
+}
+
+static void
+AnimateLanderWarmup (void)
+{
+ SIZE num_crew;
+ STAMP s;
+ CONTEXT OldContext;
+ TimeCount TimeIn = GetTimeCounter ();
+
+ OldContext = SetContext (RadarContext);
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (LanderFrame[0],
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 1);
+
+ DrawStamp (&s);
+
+ animationInterframe (&TimeIn, 2);
+
+ for (num_crew = 0; num_crew < (NUM_CREW_COLS * NUM_CREW_ROWS)
+ && GLOBAL_SIS (CrewEnlisted); ++num_crew)
+ {
+ animationInterframe (&TimeIn, 1);
+
+ DeltaSISGauges (-1, 0, 0);
+ DeltaLanderCrew (1, 0);
+ }
+
+ animationInterframe (&TimeIn, 2);
+
+ if (GET_GAME_STATE (IMPROVED_LANDER_SHOT))
+ s.frame = SetAbsFrameIndex (s.frame, 58);
+ else
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 2);
+ DrawStamp (&s);
+
+ animationInterframe (&TimeIn, 2);
+
+ if (GET_GAME_STATE (IMPROVED_LANDER_SPEED))
+ s.frame = SetAbsFrameIndex (s.frame, 57);
+ else
+ {
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 3);
+ DrawStamp (&s);
+
+ animationInterframe (&TimeIn, 2);
+
+ s.frame = IncFrameIndex (s.frame);
+ }
+ DrawStamp (&s);
+
+ if (GET_GAME_STATE (IMPROVED_LANDER_CARGO))
+ {
+ animationInterframe (&TimeIn, 2);
+
+ s.frame = SetAbsFrameIndex (s.frame, 59);
+ DrawStamp (&s);
+ }
+
+ animationInterframe (&TimeIn, 2);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_DEPARTS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY + 1);
+}
+
+static void
+InitPlanetSide (POINT pt)
+{
+ // Adjust landing location by a random jitter.
+#define RANDOM_MISS 64
+ // Jitter the X landing point.
+ pt.x -= RANDOM_MISS - TFB_Random () % (RANDOM_MISS << 1);
+ if (pt.x < 0)
+ pt.x += (MAP_WIDTH << MAG_SHIFT);
+ else if (pt.x >= (MAP_WIDTH << MAG_SHIFT))
+ pt.x -= (MAP_WIDTH << MAG_SHIFT);
+
+ // Jitter the Y landing point.
+ pt.y -= RANDOM_MISS - TFB_Random () % (RANDOM_MISS << 1);
+ if (pt.y < 0)
+ pt.y = 0;
+ else if (pt.y >= (MAP_HEIGHT << MAG_SHIFT))
+ pt.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+
+ curLanderLoc = pt;
+
+ SetContext (PlanetContext);
+ SetContextFont (TinyFont);
+
+ {
+ RECT r;
+
+ GetContextClipRect (&r);
+
+ SetTransitionSource (&r);
+ BatchGraphics ();
+
+ {
+ STAMP s;
+
+ // Note - This code is the same as in ScrollPlanetSize,
+ // Display planet area, accounting for horizontal wrapping if
+ // near the edges.
+ ClearDrawable ();
+ s.origin.x = -pt.x + (SURFACE_WIDTH >> 1);
+ s.origin.y = -pt.y + (SURFACE_HEIGHT >> 1);
+ s.frame = pSolarSysState->Orbit.TopoZoomFrame;
+ DrawStamp (&s);
+ s.origin.x += MAP_WIDTH << MAG_SHIFT;
+ DrawStamp (&s);
+ s.origin.x -= MAP_WIDTH << (MAG_SHIFT + 1);
+ DrawStamp (&s);
+ }
+
+ ScreenTransition (3, &r);
+ UnbatchGraphics ();
+ }
+
+
+ SET_GAME_STATE (PLANETARY_LANDING, 1);
+}
+
+static void
+LanderFire (SIZE facing)
+{
+#define SHUTTLE_FIRE_WAIT 15
+ HELEMENT hWeaponElement;
+ SIZE wdx, wdy;
+ ELEMENT *WeaponElementPtr;
+ COUNT angle;
+
+ hWeaponElement = AllocElement ();
+ if (hWeaponElement == NULL)
+ return;
+
+ LockElement (hWeaponElement, &WeaponElementPtr);
+
+ WeaponElementPtr->playerNr = PS_HUMAN_PLAYER;
+ WeaponElementPtr->mass_points = 1;
+ WeaponElementPtr->life_span = 12;
+ WeaponElementPtr->state_flags = FINITE_LIFE;
+ WeaponElementPtr->next.location = curLanderLoc;
+
+ SetPrimType (&DisplayArray[WeaponElementPtr->PrimIndex], STAMP_PRIM);
+ DisplayArray[WeaponElementPtr->PrimIndex].Object.Stamp.frame =
+ SetAbsFrameIndex (LanderFrame[0],
+ /* shot images immediately follow the lander images */
+ facing + ANGLE_TO_FACING (FULL_CIRCLE));
+
+ if (!CurrentInputState.key[PlayerControls[0]][KEY_UP])
+ {
+ wdx = 0;
+ wdy = 0;
+ }
+ else
+ {
+ GetCurrentVelocityComponents (&GLOBAL (velocity), &wdx, &wdy);
+ }
+
+ angle = FACING_TO_ANGLE (facing);
+ SetVelocityComponents (
+ &WeaponElementPtr->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (2 * 3)) + wdx,
+ SINE (angle, WORLD_TO_VELOCITY (2 * 3)) + wdy);
+
+ UnlockElement (hWeaponElement);
+
+ InsertElement (hWeaponElement, GetHeadElement ());
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_SHOOTS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+}
+
+static BOOLEAN
+LanderExplosion (void)
+{
+ HELEMENT hExplosionElement;
+ ELEMENT *ExplosionElementPtr;
+
+ hExplosionElement = AllocElement ();
+ if (!hExplosionElement)
+ return FALSE;
+
+ LockElement (hExplosionElement, &ExplosionElementPtr);
+
+ ExplosionElementPtr->playerNr = PS_HUMAN_PLAYER;
+ ExplosionElementPtr->mass_points = DEATH_EXPLOSION;
+ ExplosionElementPtr->state_flags = FINITE_LIFE;
+ ExplosionElementPtr->next.location = curLanderLoc;
+ ExplosionElementPtr->preprocess_func = object_animation;
+ // Animation advances every 3rd frame
+ ExplosionElementPtr->turn_wait = MAKE_BYTE (2, 2);
+ ExplosionElementPtr->life_span = EXPLOSION_LIFE
+ * (LONIBBLE (ExplosionElementPtr->turn_wait) + 1);
+
+ SetPrimType (&DisplayArray[ExplosionElementPtr->PrimIndex],
+ STAMP_PRIM);
+ DisplayArray[ExplosionElementPtr->PrimIndex].Object.Stamp.frame =
+ SetAbsFrameIndex (LanderFrame[0], 46);
+
+ UnlockElement (hExplosionElement);
+
+ InsertElement (hExplosionElement, GetHeadElement ());
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_DESTROYED),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY + 1);
+
+ return TRUE;
+}
+
+static BOOLEAN
+DoPlanetSide (LanderInputState *pMS)
+{
+ SIZE dx = 0;
+ SIZE dy = 0;
+
+#define SHUTTLE_TURN_WAIT 2
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return (FALSE);
+
+ if (!pMS->Initialized)
+ {
+ COUNT landerSpeedNumer;
+ COUNT angle;
+
+ pMS->Initialized = TRUE;
+
+ turn_wait = 0;
+ weapon_wait = 0;
+
+ angle = FACING_TO_ANGLE (GetFrameIndex (LanderFrame[0]));
+ landerSpeedNumer = GET_GAME_STATE (IMPROVED_LANDER_SPEED) ?
+ WORLD_TO_VELOCITY (2 * 14) :
+ WORLD_TO_VELOCITY (2 * 8);
+
+#ifdef FAST_FAST
+landerSpeedNumer = WORLD_TO_VELOCITY (48);
+#endif
+
+ SetVelocityComponents (&GLOBAL (velocity),
+ COSINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM,
+ SINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM);
+
+ return TRUE;
+ }
+ else if (crew_left /* alive and taking off */
+ && ((CurrentInputState.key[PlayerControls[0]][KEY_ESCAPE] ||
+ CurrentInputState.key[PlayerControls[0]][KEY_SPECIAL])
+ || planetSideDesc->InTransit))
+ {
+ return FALSE;
+ }
+ else if (!crew_left && !damage_index)
+ { // Dead, damage dealt, and exploding
+ if (explosion_index > EXPLOSION_LIFE + EXPLOSION_WAIT_FRAMES)
+ return FALSE;
+
+ if (explosion_index > EXPLOSION_LIFE)
+ { // Keep going until the wait expires
+ ++explosion_index;
+ }
+ else if (explosion_index == 0)
+ { // Start the explosion animation
+ if (LanderExplosion ())
+ {
+ // Advance the state only once we've got the element
+ ++explosion_index;
+ }
+ else
+ { // We could not allocate because the queue was full, but
+ // we will get another chance on the next iteration
+ log_add (log_Warning, "DoPlanetSide(): could not"
+ " allocate explosion element!");
+ }
+ }
+ }
+ else
+ {
+ if (crew_left)
+ {
+ SIZE index = GetFrameIndex (LanderFrame[0]);
+ if (turn_wait)
+ --turn_wait;
+ else if (CurrentInputState.key[PlayerControls[0]][KEY_LEFT] ||
+ CurrentInputState.key[PlayerControls[0]][KEY_RIGHT])
+ {
+ COUNT landerSpeedNumer;
+ COUNT angle;
+
+ if (CurrentInputState.key[PlayerControls[0]][KEY_LEFT])
+ --index;
+ else
+ ++index;
+
+ index = NORMALIZE_FACING (index);
+ LanderFrame[0] = SetAbsFrameIndex (LanderFrame[0], index);
+
+ angle = FACING_TO_ANGLE (index);
+ landerSpeedNumer = GET_GAME_STATE (IMPROVED_LANDER_SPEED) ?
+ WORLD_TO_VELOCITY (2 * 14) :
+ WORLD_TO_VELOCITY (2 * 8);
+
+#ifdef FAST_FAST
+landerSpeedNumer = WORLD_TO_VELOCITY (48);
+#endif
+
+ SetVelocityComponents (&GLOBAL (velocity),
+ COSINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM,
+ SINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM);
+
+ turn_wait = SHUTTLE_TURN_WAIT;
+ }
+
+ if (!CurrentInputState.key[PlayerControls[0]][KEY_UP])
+ {
+ dx = 0;
+ dy = 0;
+ }
+ else
+ GetNextVelocityComponents (&GLOBAL (velocity), &dx, &dy, 1);
+
+ if (weapon_wait)
+ --weapon_wait;
+ else if (CurrentInputState.key[PlayerControls[0]][KEY_WEAPON])
+ {
+ LanderFire (index);
+
+ weapon_wait = SHUTTLE_FIRE_WAIT;
+ if (GET_GAME_STATE (IMPROVED_LANDER_SHOT))
+ weapon_wait >>= 1;
+ }
+ }
+ }
+
+ ScrollPlanetSide (dx, dy, ON_THE_GROUND);
+
+ SleepThreadUntil (pMS->NextTime);
+ // NOTE: The rate is not stabilized
+ pMS->NextTime = GetTimeCounter () + PLANET_SIDE_RATE;
+
+ return TRUE;
+}
+
+void
+FreeLanderData (void)
+{
+ COUNT i;
+ COUNT landerFrameCount;
+
+ if (LanderFrame[0] == NULL)
+ return;
+
+ for (i = 0; i < NUM_ORBIT_THEMES; ++i)
+ {
+ DestroyMusic (OrbitMusic[i]);
+ OrbitMusic[i] = 0;
+ }
+
+ DestroySound (ReleaseSound (LanderSounds));
+ LanderSounds = 0;
+
+ landerFrameCount = sizeof (LanderFrame) / sizeof (LanderFrame[0]);
+ for (i = 0; i < landerFrameCount; ++i)
+ {
+ DestroyDrawable (ReleaseDrawable (LanderFrame[i]));
+ LanderFrame[i] = 0;
+ }
+}
+
+void
+LoadLanderData (void)
+{
+ if (LanderFrame[0] != 0)
+ return;
+
+ LanderFrame[0] =
+ CaptureDrawable (LoadGraphic (LANDER_MASK_PMAP_ANIM));
+ LanderFrame[1] =
+ CaptureDrawable (LoadGraphic (QUAKE_MASK_PMAP_ANIM));
+ LanderFrame[2] =
+ CaptureDrawable (LoadGraphic (LIGHTNING_MASK_ANIM));
+ LanderFrame[3] =
+ CaptureDrawable (LoadGraphic (LAVA_MASK_PMAP_ANIM));
+ LanderFrame[4] =
+ CaptureDrawable (LoadGraphic (LANDER_SHIELD_MASK_ANIM));
+ LanderFrame[5] =
+ CaptureDrawable (LoadGraphic (LANDER_LAUNCH_MASK_PMAP_ANIM));
+ LanderFrame[6] =
+ CaptureDrawable (LoadGraphic (LANDER_RETURN_MASK_PMAP_ANIM));
+ LanderFrame[7] =
+ CaptureDrawable (LoadGraphic (ORBIT_VIEW_ANIM));
+
+ LanderSounds = CaptureSound (LoadSound (LANDER_SOUNDS));
+
+ {
+ COUNT i;
+
+ for (i = 0; i < NUM_ORBIT_THEMES; ++i)
+ OrbitMusic[i] = load_orbit_theme (i);
+ }
+}
+
+void
+SetPlanetMusic (BYTE planet_type)
+{
+ LanderMusic = OrbitMusic[planet_type % NUM_ORBIT_THEMES];
+}
+
+static void
+ReturnToOrbit (void)
+{
+ CONTEXT OldContext;
+ RECT r;
+
+ OldContext = SetContext (PlanetContext);
+ GetContextClipRect (&r);
+
+ SetTransitionSource (&r);
+ BatchGraphics ();
+ DrawStarBackGround ();
+ DrawPlanetSurfaceBorder ();
+ RedrawSurfaceScan (NULL);
+ ScreenTransition (3, &r);
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static void
+IdlePlanetSide (LanderInputState *inputState, TimeCount howLong)
+{
+#define IDLE_OFFSET
+ TimeCount TimeOut = GetTimeCounter () + howLong;
+
+ while (GetTimeCounter () < TimeOut)
+ {
+ // 10 to clear the lander off of the screen
+ ScrollPlanetSide (0, 0, -(SURFACE_HEIGHT / 2 + 10));
+ SleepThreadUntil (inputState->NextTime);
+ inputState->NextTime += PLANET_SIDE_RATE;
+ }
+}
+
+static void
+LandingTakeoffSequence (LanderInputState *inputState, BOOLEAN landing)
+{
+// We cannot solve a quadratic equation in a macro, so use a sensible max
+#define MAX_OFFSETS 20
+// 10 to clear the lander off of the screen
+#define DISTANCE_COVERED (SURFACE_HEIGHT / 2 + 10)
+ int landingOfs[MAX_OFFSETS];
+ int start;
+ int end;
+ int delta;
+ int index;
+
+ // Produce smooth acceleration deltas from a simple 1..x progression
+ delta = 0;
+ for (index = 0; index < MAX_OFFSETS && delta < DISTANCE_COVERED; ++index)
+ {
+ delta += index + 1;
+ landingOfs[index] = -delta;
+ }
+ assert (delta >= DISTANCE_COVERED && "Increase MAX_OFFSETS!");
+
+ if (landing)
+ {
+ start = index - 1;
+ end = -1;
+ delta = -1;
+ }
+ else
+ { // takeoff
+ start = 0;
+ end = index;
+ delta = +1;
+ }
+
+ if (landing)
+ IdlePlanetSide (inputState, ONE_SECOND);
+
+ // Draw the landing/takeoff lander positions
+ for (index = start; index != end; index += delta)
+ {
+ ScrollPlanetSide (0, 0, landingOfs[index]);
+ SleepThreadUntil (inputState->NextTime);
+ inputState->NextTime += PLANET_SIDE_RATE;
+ }
+
+ if (!landing)
+ IdlePlanetSide (inputState, ONE_SECOND / 2);
+}
+
+void
+SetLanderTakeoff (void)
+{
+ assert (planetSideDesc != NULL);
+ if (planetSideDesc)
+ planetSideDesc->InTransit = TRUE;
+}
+
+// Returns whether the lander is still alive at the end of the sequence
+bool
+KillLanderCrewSeq (COUNT numKilled, DWORD period)
+{
+ TimeCount TimeOut;
+ COUNT i;
+
+ TimeOut = GetTimeCounter ();
+ for (i = 0; i < numKilled && crew_left; ++i)
+ {
+ TimeOut += period;
+ DeltaLanderCrew (-1, LANDER_INJURED);
+ SleepThreadUntil (TimeOut);
+ }
+
+ return crew_left > 0;
+}
+
+// Maps a temperature to a (0-7) hazard rating.
+// Thermal hazards aren't exposed to the user as a hazard number,
+// but the code still works with them that way.
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof (*array))
+unsigned
+GetThermalHazardRating (int temp)
+{
+ static const int tempBreakpoints[] = { 50, 100, 150, 250, 350, 550, 800 };
+ const size_t numBreakpoints = ARRAY_SIZE (tempBreakpoints);
+ unsigned i;
+
+ for (i = 0; i < numBreakpoints; ++i)
+ {
+ if (temp < tempBreakpoints[i])
+ return i;
+ }
+
+ return numBreakpoints;
+}
+
+// Given a hazard type and rating, return the chance (out of 256) of the hazard
+// being generated.
+static BYTE
+GetHazardChance (int hazardType, unsigned HazardRating)
+{
+ static const BYTE TectonicsChanceTab[] = {0*3, 0*3, 1*3, 2*3, 4*3, 8*3, 16*3, 32*3};
+ static const BYTE WeatherChanceTab [] = {0*3, 0*3, 1*3, 2*3, 3*3, 6*3, 12*3, 24*3};
+ static const BYTE FireChanceTab [] = {0*3, 0*3, 1*3, 2*3, 4*3, 12*3, 24*3, 48*3};
+
+ switch (hazardType)
+ {
+ case EARTHQUAKE_DISASTER:
+ return TectonicsChanceTab[HazardRating];
+ case LIGHTNING_DISASTER:
+ return WeatherChanceTab[HazardRating];
+ case LAVASPOT_DISASTER:
+ return FireChanceTab[HazardRating];
+ }
+
+ return 0;
+}
+
+void
+PlanetSide (POINT planetLoc)
+{
+ SIZE index;
+ LanderInputState landerInputState;
+ PLANETSIDE_DESC PSD;
+
+ memset (&PSD, 0, sizeof (PSD));
+ PSD.InTransit = TRUE;
+
+ // Set our chances of hazards occurring.
+ PSD.TectonicsChance = GetHazardChance (EARTHQUAKE_DISASTER,
+ pSolarSysState->SysInfo.PlanetInfo.Tectonics);
+ PSD.WeatherChance = GetHazardChance (LIGHTNING_DISASTER,
+ pSolarSysState->SysInfo.PlanetInfo.Weather);
+ PSD.FireChance = GetHazardChance (LAVASPOT_DISASTER, GetThermalHazardRating (
+ pSolarSysState->SysInfo.PlanetInfo.SurfaceTemperature));
+
+ PSD.ElementLevel = GetStorageBayCapacity () - GLOBAL_SIS (TotalElementMass);
+ PSD.MaxElementLevel = MAX_SCROUNGED;
+ if (GET_GAME_STATE (IMPROVED_LANDER_CARGO))
+ PSD.MaxElementLevel <<= 1;
+ if (PSD.ElementLevel < PSD.MaxElementLevel)
+ PSD.MaxElementLevel = PSD.ElementLevel;
+ PSD.ElementLevel = 0;
+
+ PSD.MineralText[0].align = ALIGN_CENTER;
+ PSD.MineralText[0].pStr = PSD.AmountBuf;
+ PSD.MineralText[1] = PSD.MineralText[0];
+ PSD.MineralText[2] = PSD.MineralText[1];
+
+ PSD.ColorCycle[0] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x03, 0x00), 0x7F);
+ PSD.ColorCycle[1] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7D);
+ PSD.ColorCycle[2] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7B);
+ PSD.ColorCycle[3] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x71);
+ for (index = 4; index < (NUM_TEXT_FRAMES >> 1) - 4; ++index)
+ {
+ PSD.ColorCycle[index] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F);
+ }
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 4] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x71);
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 3] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7B);
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 2] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7D);
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 1] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x03, 0x00), 0x7F);
+ planetSideDesc = &PSD;
+
+ index = NORMALIZE_FACING (TFB_Random ());
+ LanderFrame[0] = SetAbsFrameIndex (LanderFrame[0], index);
+ crew_left = 0;
+ damage_index = 0;
+ explosion_index = 0;
+
+ AnimateLanderWarmup ();
+ AnimateLaunch (LanderFrame[5]);
+ InitPlanetSide (planetLoc);
+
+ landerInputState.NextTime = GetTimeCounter () + PLANET_SIDE_RATE;
+ LandingTakeoffSequence (&landerInputState, TRUE);
+ PSD.InTransit = FALSE;
+
+ landerInputState.Initialized = FALSE;
+ landerInputState.InputFunc = DoPlanetSide;
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ DoInput (&landerInputState, FALSE);
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (crew_left == 0)
+ {
+ --GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+
+ ReturnToOrbit ();
+ }
+ else
+ {
+ PSD.InTransit = TRUE;
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_RETURNS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY + 1);
+
+ LandingTakeoffSequence (&landerInputState, FALSE);
+ ReturnToOrbit ();
+ AnimateLaunch (LanderFrame[6]);
+
+ DeltaSISGauges (crew_left, 0, 0);
+
+ if (PSD.ElementLevel)
+ {
+ for (index = 0; index < NUM_ELEMENT_CATEGORIES; ++index)
+ {
+ GLOBAL_SIS (ElementAmounts[index]) +=
+ PSD.ElementAmounts[index];
+ GLOBAL_SIS (TotalElementMass) +=
+ PSD.ElementAmounts[index];
+ }
+ DrawStorageBays (FALSE);
+ }
+
+ GLOBAL_SIS (TotalBioMass) += PSD.BiologicalLevel;
+ }
+ }
+
+ planetSideDesc = NULL;
+
+ {
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = GetHeadElement ();
+ hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = _GetSuccLink (ElementPtr);
+ if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ UnlockElement (hElement);
+
+ RemoveElement (hElement);
+ FreeElement (hElement);
+
+ continue;
+ }
+ UnlockElement (hElement);
+ }
+ }
+
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+}
+
+void
+InitLander (BYTE LanderFlags)
+{
+ RECT r;
+
+ SetContext (RadarContext);
+ BatchGraphics ();
+
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = RADAR_WIDTH;
+ r.extent.height = RADAR_HEIGHT;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ if (GLOBAL_SIS (NumLanders) || LanderFlags)
+ {
+ BYTE ShieldFlags, capacity_shift;
+ COUNT free_space;
+ STAMP s;
+
+ s.origin.x = 0; /* set up powered-down lander */
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (LanderFrame[0],
+ ANGLE_TO_FACING (FULL_CIRCLE) << 1);
+ DrawStamp (&s);
+ if (LanderFlags == 0)
+ {
+ ShieldFlags = GET_GAME_STATE (LANDER_SHIELDS);
+ capacity_shift = GET_GAME_STATE (IMPROVED_LANDER_CARGO);
+ }
+ else
+ {
+ ShieldFlags = (unsigned char)(LanderFlags &
+ ((1 << EARTHQUAKE_DISASTER)
+ | (1 << BIOLOGICAL_DISASTER)
+ | (1 << LIGHTNING_DISASTER)
+ | (1 << LAVASPOT_DISASTER)));
+ s.frame = IncFrameIndex (s.frame);
+ DrawStamp (&s);
+ if (LanderFlags & (1 << (4 + 0)))
+ s.frame = SetAbsFrameIndex (s.frame, 57);
+ else
+ {
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 3);
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ }
+ DrawStamp (&s);
+ if (!(LanderFlags & (1 << (4 + 1))))
+ capacity_shift = 0;
+ else
+ {
+ capacity_shift = 1;
+ s.frame = SetAbsFrameIndex (s.frame, 59);
+ DrawStamp (&s);
+ }
+ if (LanderFlags & (1 << (4 + 2)))
+ s.frame = SetAbsFrameIndex (s.frame, 58);
+ else
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 2);
+ DrawStamp (&s);
+ }
+
+ free_space = GetStorageBayCapacity () - GLOBAL_SIS (TotalElementMass);
+ if ((int)free_space < (int)(MAX_SCROUNGED << capacity_shift))
+ {
+ r.corner.x = 1;
+ r.extent.width = 4;
+ r.extent.height = MAX_SCROUNGED
+ - (free_space >> capacity_shift) + 1;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ }
+
+ s.frame = SetAbsFrameIndex (s.frame, 37);
+ if (ShieldFlags & (1 << EARTHQUAKE_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << BIOLOGICAL_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << LIGHTNING_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << LAVASPOT_DISASTER))
+ DrawStamp (&s);
+ }
+
+ UnbatchGraphics ();
+}
diff --git a/src/uqm/planets/lander.h b/src/uqm/planets/lander.h
new file mode 100644
index 0000000..d41129b
--- /dev/null
+++ b/src/uqm/planets/lander.h
@@ -0,0 +1,88 @@
+//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.
+ */
+
+#ifndef UQM_PLANETS_LANDER_H_
+#define UQM_PLANETS_LANDER_H_
+
+#include "elemdata.h"
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "libs/sndlib.h"
+#include "libs/timelib.h"
+#include "../element.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// Surface magnification shift (x4)
+#define MAG_SHIFT 2
+
+#define NUM_TEXT_FRAMES 32
+
+// XXX: This is a private type now. Move it to lander.c?
+// We may also want to merge it with LanderInputState.
+typedef struct
+{
+ BOOLEAN InTransit;
+ // Landing on or taking of from a planet.
+ // Setting it while landed will initiate takeoff.
+
+ COUNT ElementLevel;
+ COUNT MaxElementLevel;
+ COUNT BiologicalLevel;
+ COUNT ElementAmounts[NUM_ELEMENT_CATEGORIES];
+
+ COUNT NumFrames;
+ UNICODE AmountBuf[40];
+ TEXT MineralText[3];
+
+ Color ColorCycle[NUM_TEXT_FRAMES >> 1];
+
+ BYTE TectonicsChance;
+ BYTE WeatherChance;
+ BYTE FireChance;
+} PLANETSIDE_DESC;
+
+extern MUSIC_REF LanderMusic;
+
+extern void PlanetSide (POINT planetLoc);
+extern void DoDiscoveryReport (SOUND ReadOutSounds);
+extern void SetPlanetMusic (BYTE planet_type);
+extern void LoadLanderData (void);
+extern void FreeLanderData (void);
+
+extern void object_animation (ELEMENT *ElementPtr);
+
+extern void SetLanderTakeoff (void);
+extern bool KillLanderCrewSeq (COUNT numKilled, DWORD period);
+
+extern unsigned GetThermalHazardRating (int temp);
+
+// ELEMENT.playerNr constants
+enum
+{
+ PS_HUMAN_PLAYER,
+ PS_NON_PLAYER,
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_LANDER_H_ */
diff --git a/src/uqm/planets/lifeform.h b/src/uqm/planets/lifeform.h
new file mode 100644
index 0000000..fbe2d9d
--- /dev/null
+++ b/src/uqm/planets/lifeform.h
@@ -0,0 +1,75 @@
+//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.
+ */
+
+#ifndef UQM_PLANETS_LIFEFORM_H_
+#define UQM_PLANETS_LIFEFORM_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define BEHAVIOR_HUNT (0 << 0)
+#define BEHAVIOR_FLEE (1 << 0)
+#define BEHAVIOR_UNPREDICTABLE (2 << 0)
+
+#define BEHAVIOR_MASK 0x03
+#define BEHAVIOR_SHIFT 0
+
+#define AWARENESS_LOW (0 << 2)
+#define AWARENESS_MEDIUM (1 << 2)
+#define AWARENESS_HIGH (2 << 2)
+
+#define AWARENESS_MASK 0x0C
+#define AWARENESS_SHIFT (BEHAVIOR_SHIFT + 2)
+
+#define SPEED_MOTIONLESS (0 << 4)
+#define SPEED_SLOW (1 << 4)
+#define SPEED_MEDIUM (2 << 4)
+#define SPEED_FAST (3 << 4)
+
+#define SPEED_MASK 0x30
+#define SPEED_SHIFT (AWARENESS_SHIFT + 2)
+
+#define DANGER_HARMLESS (0 << 6)
+#define DANGER_WEAK (1 << 6)
+#define DANGER_NORMAL (2 << 6)
+#define DANGER_MONSTROUS (3 << 6)
+
+#define DANGER_MASK 0xC0
+#define DANGER_SHIFT (SPEED_SHIFT + 2)
+
+#define NUM_CREATURE_TYPES 23
+#define NUM_SPECIAL_CREATURE_TYPES 3
+#define MAX_LIFE_VARIATION 3
+
+#define CREATURE_AWARE (BYTE)(1 << 7)
+
+typedef struct
+{
+ BYTE Attributes, ValueAndHitPoints;
+} LIFEFORM_DESC;
+
+extern const LIFEFORM_DESC CreatureData[];
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_LIFEFORM_H */
diff --git a/src/uqm/planets/orbits.c b/src/uqm/planets/orbits.c
new file mode 100644
index 0000000..f1ad0f4
--- /dev/null
+++ b/src/uqm/planets/orbits.c
@@ -0,0 +1,629 @@
+//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 "planets.h"
+#include "../starmap.h"
+#include "libs/compiler.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+//#define DEBUG_ORBITS
+
+enum
+{
+ PLANET_NEVER = 0,
+ PLANET_RARE = 15,
+ PLANET_FEW = 63,
+ PLANET_COMMON = 127,
+ PLANET_ALWAYS = 255
+};
+
+static BYTE
+BlueDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_ALWAYS, /* OOLITE_WORLD */
+ PLANET_ALWAYS, /* YTTRIC_WORLD */
+ PLANET_ALWAYS, /* QUASI_DEGENERATE_WORLD */
+ PLANET_ALWAYS, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_NEVER, /* CHONDRITE_WORLD */
+ PLANET_NEVER, /* PURPLE_WORLD */
+ PLANET_NEVER, /* SUPER_DENSE_WORLD */
+ PLANET_NEVER, /* PELLUCID_WORLD */
+ PLANET_NEVER, /* DUST_WORLD */
+ PLANET_NEVER, /* CRIMSON_WORLD */
+ PLANET_NEVER, /* CIMMERIAN_WORLD */
+ PLANET_NEVER, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_ALWAYS, /* FLUORESCENT_WORLD */
+ PLANET_ALWAYS, /* ULTRAVIOLET_WORLD */
+ PLANET_ALWAYS, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_ALWAYS, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_NEVER, /* HYDROCARBON_WORLD */
+ PLANET_NEVER, /* IODINE_WORLD */
+ PLANET_NEVER, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_NEVER, /* MAGMA_WORLD */
+ PLANET_NEVER, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+GreenDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_ALWAYS, /* QUASI_DEGENERATE_WORLD */
+ PLANET_ALWAYS, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_NEVER, /* DUST_WORLD */
+ PLANET_NEVER, /* CRIMSON_WORLD */
+ PLANET_NEVER, /* CIMMERIAN_WORLD */
+ PLANET_NEVER, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_ALWAYS, /* FLUORESCENT_WORLD */
+ PLANET_ALWAYS, /* ULTRAVIOLET_WORLD */
+ PLANET_ALWAYS, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_ALWAYS, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_NEVER, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_NEVER, /* MAGMA_WORLD */
+ PLANET_NEVER, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+OrangeDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_NEVER, /* QUASI_DEGENERATE_WORLD */
+ PLANET_NEVER, /* LANTHANIDE_WORLD */
+ PLANET_NEVER, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_NEVER, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_NEVER, /* OPALESCENT_WORLD */
+ PLANET_NEVER, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_ALWAYS, /* DUST_WORLD */
+ PLANET_ALWAYS, /* CRIMSON_WORLD */
+ PLANET_ALWAYS, /* CIMMERIAN_WORLD */
+ PLANET_ALWAYS, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_NEVER, /* AURIC_WORLD */
+
+ PLANET_NEVER, /* FLUORESCENT_WORLD */
+ PLANET_NEVER, /* ULTRAVIOLET_WORLD */
+ PLANET_NEVER, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_NEVER, /* ORGANIC_WORLD */
+ PLANET_NEVER, /* XENOLITHIC_WORLD */
+ PLANET_NEVER, /* REDUX_WORLD */
+ PLANET_NEVER, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_ALWAYS, /* MAGMA_WORLD */
+ PLANET_ALWAYS, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+RedDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_NEVER, /* QUASI_DEGENERATE_WORLD */
+ PLANET_NEVER, /* LANTHANIDE_WORLD */
+ PLANET_NEVER, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_NEVER, /* RADIOACTIVE_WORLD */
+ PLANET_NEVER, /* OPALESCENT_WORLD */
+ PLANET_NEVER, /* CYANIC_WORLD */
+ PLANET_NEVER, /* ACID_WORLD */
+ PLANET_NEVER, /* ALKALI_WORLD */
+ PLANET_NEVER, /* HALIDE_WORLD */
+ PLANET_NEVER, /* GREEN_WORLD */
+ PLANET_NEVER, /* COPPER_WORLD */
+ PLANET_NEVER, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_ALWAYS, /* DUST_WORLD */
+ PLANET_ALWAYS, /* CRIMSON_WORLD */
+ PLANET_ALWAYS, /* CIMMERIAN_WORLD */
+ PLANET_ALWAYS, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_NEVER, /* AURIC_WORLD */
+
+ PLANET_NEVER, /* FLUORESCENT_WORLD */
+ PLANET_NEVER, /* ULTRAVIOLET_WORLD */
+ PLANET_NEVER, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_NEVER, /* ORGANIC_WORLD */
+ PLANET_NEVER, /* XENOLITHIC_WORLD */
+ PLANET_NEVER, /* REDUX_WORLD */
+ PLANET_NEVER, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_NEVER, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_ALWAYS, /* RUBY_WORLD */
+ PLANET_ALWAYS, /* MAGMA_WORLD */
+ PLANET_ALWAYS, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+WhiteDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_ALWAYS, /* OOLITE_WORLD */
+ PLANET_ALWAYS, /* YTTRIC_WORLD */
+ PLANET_ALWAYS, /* QUASI_DEGENERATE_WORLD */
+ PLANET_ALWAYS, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_NEVER, /* GREEN_WORLD */
+ PLANET_NEVER, /* COPPER_WORLD */
+ PLANET_NEVER, /* CARBIDE_WORLD */
+ PLANET_NEVER, /* ULTRAMARINE_WORLD */
+ PLANET_NEVER, /* NOBLE_WORLD */
+ PLANET_NEVER, /* AZURE_WORLD */
+ PLANET_NEVER, /* CHONDRITE_WORLD */
+ PLANET_NEVER, /* PURPLE_WORLD */
+ PLANET_NEVER, /* SUPER_DENSE_WORLD */
+ PLANET_NEVER, /* PELLUCID_WORLD */
+ PLANET_NEVER, /* DUST_WORLD */
+ PLANET_NEVER, /* CRIMSON_WORLD */
+ PLANET_NEVER, /* CIMMERIAN_WORLD */
+ PLANET_NEVER, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_ALWAYS, /* FLUORESCENT_WORLD */
+ PLANET_ALWAYS, /* ULTRAVIOLET_WORLD */
+ PLANET_ALWAYS, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_ALWAYS, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_ALWAYS, /* EMERALD_WORLD */
+ PLANET_NEVER, /* CHLORINE_WORLD */
+ PLANET_NEVER, /* MAGNETIC_WORLD */
+ PLANET_NEVER, /* WATER_WORLD */
+ PLANET_NEVER, /* TELLURIC_WORLD */
+ PLANET_NEVER, /* HYDROCARBON_WORLD */
+ PLANET_NEVER, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_ALWAYS, /* RUBY_WORLD */
+ PLANET_NEVER, /* MAGMA_WORLD */
+ PLANET_NEVER, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+YellowDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_NEVER, /* QUASI_DEGENERATE_WORLD */
+ PLANET_NEVER, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_ALWAYS, /* DUST_WORLD */
+ PLANET_ALWAYS, /* CRIMSON_WORLD */
+ PLANET_ALWAYS, /* CIMMERIAN_WORLD */
+ PLANET_ALWAYS, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_NEVER, /* FLUORESCENT_WORLD */
+ PLANET_NEVER, /* ULTRAVIOLET_WORLD */
+ PLANET_NEVER, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_ALWAYS, /* MAGMA_WORLD */
+ PLANET_ALWAYS, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+#define DWARF_ROCK_DIST MIN_PLANET_RADIUS
+#define DWARF_GASG_DIST SCALE_RADIUS (12)
+
+#define GIANT_ROCK_DIST SCALE_RADIUS (8)
+#define GIANT_GASG_DIST SCALE_RADIUS (13)
+
+#define SUPERGIANT_ROCK_DIST SCALE_RADIUS (16)
+#define SUPERGIANT_GASG_DIST SCALE_RADIUS (33)
+
+void
+FillOrbits (SOLARSYS_STATE *system, BYTE NumPlanets,
+ PLANET_DESC *pBaseDesc, BOOLEAN TypesDefined)
+{ /* Generate Planets in orbit around star */
+ BYTE StarColor, PlanetCount, MaxPlanet;
+ BOOLEAN GeneratingMoons;
+ COUNT StarSize;
+ PLANET_DESC *pPD;
+ struct
+ {
+ COUNT MinRockyDist, MinGasGDist;
+ } Suns[] =
+ {
+ {DWARF_ROCK_DIST, DWARF_GASG_DIST},
+ {GIANT_ROCK_DIST, GIANT_GASG_DIST},
+ {SUPERGIANT_ROCK_DIST, SUPERGIANT_GASG_DIST},
+ };
+#ifdef DEBUG_ORBITS
+UNICODE buf[256];
+char stype[] = {'D', 'G', 'S'};
+char scolor[] = {'B', 'G', 'O', 'R', 'W', 'Y'};
+#endif /* DEBUG_ORBITS */
+
+ pPD = pBaseDesc;
+ StarSize = system->SunDesc[0].data_index;
+ StarColor = STAR_COLOR (CurStarDescPtr->Type);
+
+ if (NumPlanets == (BYTE)~0)
+ {
+#define MAX_GENERATED_PLANETS 9
+ // XXX: This is pretty funny. Instead of calling RNG once, like so:
+ // 1 + Random % MAX_GENERATED_PLANETS
+ // we spin in a loop until the result > 0.
+ // Note that this behavior must be kept to preserve the universe.
+ do
+ NumPlanets = LOWORD (RandomContext_Random (SysGenRNG))
+ % (MAX_GENERATED_PLANETS + 1);
+ while (NumPlanets == 0);
+ system->SunDesc[0].NumPlanets = NumPlanets;
+ }
+
+#ifdef DEBUG_ORBITS
+ GetClusterName (CurStarDescPtr, buf);
+ log_add (log_Debug, "cluster name = %s color = %c type = %c", buf,
+ scolor[STAR_COLOR (CurStarDescPtr->Type)],
+ stype[STAR_TYPE (CurStarDescPtr->Type)]);
+#endif /* DEBUG_ORBITS */
+ GeneratingMoons = (BOOLEAN) (pBaseDesc == system->MoonDesc);
+ if (GeneratingMoons)
+ MaxPlanet = FIRST_LARGE_ROCKY_WORLD;
+ else
+ MaxPlanet = NUMBER_OF_PLANET_TYPES;
+ PlanetCount = NumPlanets;
+ while (NumPlanets--)
+ {
+ BYTE chance;
+ DWORD rand_val;
+ COUNT min_radius, angle;
+ SIZE delta_r;
+ PLANET_DESC *pLocPD;
+
+ do
+ {
+ rand_val = RandomContext_Random (SysGenRNG);
+ if (TypesDefined)
+ rand_val = 0;
+ else
+ pPD->data_index =
+ (BYTE)(HIBYTE (LOWORD (rand_val)) % MaxPlanet);
+
+ chance = PLANET_NEVER;
+ switch (StarColor)
+ {
+ case BLUE_BODY:
+ chance = BlueDistribution (pPD->data_index);
+ break;
+ case GREEN_BODY:
+ chance = GreenDistribution (pPD->data_index);
+ break;
+ case ORANGE_BODY:
+ chance = OrangeDistribution (pPD->data_index);
+ break;
+ case RED_BODY:
+ chance = RedDistribution (pPD->data_index);
+ break;
+ case WHITE_BODY:
+ chance = WhiteDistribution (pPD->data_index);
+ break;
+ case YELLOW_BODY:
+ chance = YellowDistribution (pPD->data_index);
+ break;
+ }
+ } while (LOBYTE (LOWORD (rand_val)) >= chance);
+
+ if (pPD->data_index < FIRST_GAS_GIANT)
+ min_radius = Suns[StarSize].MinRockyDist;
+ else
+ min_radius = Suns[StarSize].MinGasGDist;
+RelocatePlanet:
+ rand_val = RandomContext_Random (SysGenRNG);
+ if (GeneratingMoons)
+ {
+ pPD->radius = MIN_MOON_RADIUS
+ + ((LOWORD (rand_val) % MAX_MOONS) * MOON_DELTA);
+ for (pLocPD = pPD - 1; pLocPD >= pBaseDesc; --pLocPD)
+ {
+ if (pPD->radius == pLocPD->radius)
+ goto RelocatePlanet;
+ }
+ pPD->NumPlanets = 0;
+ }
+ else
+ {
+ pPD->radius =
+ (LOWORD (rand_val) % (MAX_PLANET_RADIUS - min_radius))
+ + min_radius;
+ for (pLocPD = pPD - 1; pLocPD >= pBaseDesc; --pLocPD)
+ {
+ delta_r = UNSCALE_RADIUS (pLocPD->radius) / 5
+ - UNSCALE_RADIUS (pPD->radius) / 5;
+ if (delta_r < 0)
+ delta_r = -delta_r;
+ if (delta_r <= 1)
+ goto RelocatePlanet;
+ }
+ }
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ pPD->location.x = COSINE (angle, pPD->radius);
+ pPD->location.y = SINE (angle, pPD->radius);
+ pPD->rand_seed = MAKE_DWORD (pPD->location.x, pPD->location.y);
+
+ ++pPD;
+ }
+
+ {
+ BYTE i;
+
+ for (i = 0; i < PlanetCount; ++i)
+ {
+ BYTE j;
+
+ for (j = (BYTE)(PlanetCount - 1); j > i; --j)
+ {
+ if (pBaseDesc[i].radius > pBaseDesc[j].radius)
+ {
+ PLANET_DESC temp;
+
+ temp = pBaseDesc[i];
+ pBaseDesc[i] = pBaseDesc[j];
+ pBaseDesc[j] = temp;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/uqm/planets/oval.c b/src/uqm/planets/oval.c
new file mode 100644
index 0000000..4112997
--- /dev/null
+++ b/src/uqm/planets/oval.c
@@ -0,0 +1,329 @@
+//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 "../units.h"
+#include "libs/gfxlib.h"
+#include "libs/graphics/context.h"
+#include "libs/graphics/drawable.h"
+
+#include "planets.h"
+
+#define NUM_QUADS 4
+
+void
+DrawOval (RECT *pRect, BYTE num_off_pixels)
+{
+#define FIRST_QUAD (1 << 0)
+#define SECOND_QUAD (1 << 1)
+#define THIRD_QUAD (1 << 2)
+#define FOURTH_QUAD (1 << 3)
+ COUNT off;
+ COORD x, y;
+ SIZE A, B;
+ long Asquared, TwoAsquared,
+ Bsquared, TwoBsquared;
+ long d, dx, dy;
+ BYTE quad_visible;
+ LINE corners;
+ POINT mp;
+ PRIMITIVE prim[NUM_QUADS];
+ COUNT StartPrim;
+ RECT ClipRect;
+ BRESENHAM_LINE ClipLine;
+
+ ClipRect.corner.x = ClipRect.corner.y = 0;
+
+ corners.first.x = pRect->corner.x - ClipRect.corner.x;
+ corners.first.y = pRect->corner.y - ClipRect.corner.y;
+ corners.second.x = corners.first.x + pRect->extent.width - 1;
+ corners.second.y = corners.first.y + pRect->extent.height - 1;
+ if (corners.second.x <= corners.first.x
+ || corners.second.y <= corners.first.y)
+ {
+ if (corners.second.x < corners.first.x)
+ corners.second.x = corners.first.x;
+ if (corners.second.y < corners.first.y)
+ corners.second.y = corners.first.y;
+
+ DrawLine (&corners);
+ return;
+ }
+
+ ClipRect.extent.width = SIS_SCREEN_WIDTH;
+ ClipRect.extent.height = SIS_SCREEN_HEIGHT;
+
+ quad_visible = 0;
+ mp.x = (corners.first.x + corners.second.x) >> 1;
+ mp.y = (corners.first.y + corners.second.y) >> 1;
+ ClipRect.corner.x = ClipRect.corner.y = 0;
+
+ if (corners.first.y >= 0 && corners.first.y < ClipRect.extent.height
+ && corners.second.x >= 0 && corners.second.x < ClipRect.extent.width)
+ quad_visible |= FIRST_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.first.y;
+ ClipLine.second.x = corners.second.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= FIRST_QUAD;
+ }
+
+ if (corners.first.y >= 0 && corners.first.y < ClipRect.extent.height
+ && corners.first.x >= 0 && corners.first.x < ClipRect.extent.width)
+ quad_visible |= SECOND_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.first.y;
+ ClipLine.second.x = corners.first.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= SECOND_QUAD;
+ }
+
+ if (corners.second.y >= 0 && corners.second.y < ClipRect.extent.height
+ && corners.first.x >= 0 && corners.first.x < ClipRect.extent.width)
+ quad_visible |= THIRD_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.second.y;
+ ClipLine.second.x = corners.first.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= THIRD_QUAD;
+ }
+
+ if (corners.second.y >= 0 && corners.second.y < ClipRect.extent.height
+ && corners.second.x >= 0 && corners.second.x < ClipRect.extent.width)
+ quad_visible |= FOURTH_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.second.y;
+ ClipLine.second.x = corners.second.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= FOURTH_QUAD;
+ }
+
+ if (!quad_visible)
+ return;
+
+ StartPrim = END_OF_LIST;
+ for (x = 0; x < NUM_QUADS; ++x)
+ {
+ if (quad_visible & (1 << x))
+ {
+ SetPrimNextLink (&prim[x], StartPrim);
+ SetPrimType (&prim[x], POINT_PRIM);
+ SetPrimColor (&prim[x], _get_context_fg_color ());
+
+ StartPrim = x;
+ }
+ }
+
+ A = pRect->extent.width >> 1;
+ B = pRect->extent.height >> 1;
+
+ x = 0;
+ y = B;
+
+ Asquared = ((long)A * A) << 1;
+ Bsquared = ((long)B * B) << 1;
+ do
+ {
+ Asquared >>= 1;
+ Bsquared >>= 1;
+ TwoAsquared = Asquared << 1;
+ dy = TwoAsquared * B;
+ } while (dy / B != TwoAsquared);
+ TwoBsquared = Bsquared << 1;
+
+ dx = 0;
+ d = Bsquared - (dy >> 1) + (Asquared >> 2);
+
+ off = 0;
+ A += pRect->corner.x;
+ B += pRect->corner.y;
+ while (dx < dy)
+ {
+ if (off-- == 0)
+ {
+ prim[0].Object.Point.x = prim[3].Object.Point.x = A + x;
+ prim[0].Object.Point.y = prim[1].Object.Point.y = B - y;
+ prim[1].Object.Point.x = prim[2].Object.Point.x = A - x;
+ prim[2].Object.Point.y = prim[3].Object.Point.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+ off = num_off_pixels;
+ }
+
+ if (d > 0)
+ {
+ --y;
+ dy -= TwoAsquared;
+ d -= dy;
+ }
+
+ ++x;
+ dx += TwoBsquared;
+ d += Bsquared + dx;
+ }
+
+ d += ((((Asquared - Bsquared) * 3) >> 1) - (dx + dy)) >> 1;
+
+ while (y >= 0)
+ {
+ if (off-- == 0)
+ {
+ prim[0].Object.Point.x = prim[3].Object.Point.x = A + x;
+ prim[0].Object.Point.y = prim[1].Object.Point.y = B - y;
+ prim[1].Object.Point.x = prim[2].Object.Point.x = A - x;
+ prim[2].Object.Point.y = prim[3].Object.Point.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+ off = num_off_pixels;
+ }
+
+ if (d < 0)
+ {
+ ++x;
+ dx += TwoBsquared;
+ d += dx;
+ }
+
+ --y;
+ dy -= TwoAsquared;
+ d += Asquared - dy;
+ }
+}
+
+void
+DrawFilledOval (RECT *pRect)
+{
+ COORD x, y;
+ SIZE A, B;
+ long Asquared, TwoAsquared,
+ Bsquared, TwoBsquared;
+ long d, dx, dy;
+ LINE corners;
+ PRIMITIVE prim[NUM_QUADS >> 1];
+ COUNT StartPrim;
+
+ corners.first.x = pRect->corner.x;
+ corners.first.y = pRect->corner.y;
+ corners.second.x = corners.first.x + pRect->extent.width - 1;
+ corners.second.y = corners.first.y + pRect->extent.height - 1;
+ if (corners.second.x <= corners.first.x
+ || corners.second.y <= corners.first.y)
+ {
+ if (corners.second.x < corners.first.x)
+ corners.second.x = corners.first.x;
+ if (corners.second.y < corners.first.y)
+ corners.second.y = corners.first.y;
+
+ DrawLine (&corners);
+ return;
+ }
+
+ StartPrim = END_OF_LIST;
+ for (x = 0; x < (NUM_QUADS >> 1); ++x)
+ {
+ SetPrimNextLink (&prim[x], StartPrim);
+ SetPrimType (&prim[x], RECTFILL_PRIM);
+ SetPrimColor (&prim[x], _get_context_fg_color ());
+ prim[x].Object.Rect.extent.height = 1;
+
+ StartPrim = x;
+ }
+
+ A = pRect->extent.width >> 1;
+ B = pRect->extent.height >> 1;
+
+ x = 0;
+ y = B;
+
+ Asquared = ((long)A * A) << 1;
+ Bsquared = ((long)B * B) << 1;
+ do
+ {
+ Asquared >>= 1;
+ Bsquared >>= 1;
+ TwoAsquared = Asquared << 1;
+ dy = TwoAsquared * B;
+ } while (dy / B != TwoAsquared);
+ TwoBsquared = Bsquared << 1;
+
+ dx = 0;
+ d = Bsquared - (dy >> 1) + (Asquared >> 2);
+
+ A += pRect->corner.x;
+ B += pRect->corner.y;
+ while (dx < dy)
+ {
+ if (d > 0)
+ {
+ prim[0].Object.Rect.corner.x =
+ prim[1].Object.Rect.corner.x = A - x;
+ prim[0].Object.Rect.extent.width =
+ prim[1].Object.Rect.extent.width = (x << 1) + 1;
+ prim[0].Object.Rect.corner.y = B - y;
+ prim[1].Object.Rect.corner.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+
+ --y;
+ dy -= TwoAsquared;
+ d -= dy;
+ }
+
+ ++x;
+ dx += TwoBsquared;
+ d += Bsquared + dx;
+ }
+
+ d += ((((Asquared - Bsquared) * 3) >> 1) - (dx + dy)) >> 1;
+
+ while (y >= 0)
+ {
+ prim[0].Object.Rect.corner.x =
+ prim[1].Object.Rect.corner.x = A - x;
+ prim[0].Object.Rect.extent.width =
+ prim[1].Object.Rect.extent.width = (x << 1) + 1;
+ prim[0].Object.Rect.corner.y = B - y;
+ prim[1].Object.Rect.corner.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+
+ if (d < 0)
+ {
+ ++x;
+ dx += TwoBsquared;
+ d += dx;
+ }
+
+ --y;
+ dy -= TwoAsquared;
+ d += Asquared - dy;
+ }
+}
+
+
diff --git a/src/uqm/planets/pl_stuff.c b/src/uqm/planets/pl_stuff.c
new file mode 100644
index 0000000..073e215
--- /dev/null
+++ b/src/uqm/planets/pl_stuff.c
@@ -0,0 +1,318 @@
+//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 "planets.h"
+#include "../colors.h"
+#include "../setup.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/mathlib.h"
+#include "scan.h"
+#include "options.h"
+
+#include <math.h>
+
+
+// define USE_ADDITIVE_SCAN_BLIT to use additive blittting
+// instead of transparency for the planet scans.
+// It still doesn't look right though (it is too bright)
+#define USE_ADDITIVE_SCAN_BLIT
+
+static int rotFrameIndex;
+static int rotDirection;
+static bool throbShield;
+static int rotPointIndex;
+
+// Draw the planet sphere and any extra graphic (like a shield) if present
+void
+DrawPlanetSphere (int x, int y)
+{
+ STAMP s;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ s.origin.x = x;
+ s.origin.y = y;
+
+ BatchGraphics ();
+ s.frame = Orbit->SphereFrame;
+ DrawStamp (&s);
+ if (Orbit->ObjectFrame)
+ {
+ s.frame = Orbit->ObjectFrame;
+ DrawStamp (&s);
+ }
+ UnbatchGraphics ();
+}
+
+void
+DrawDefaultPlanetSphere (void)
+{
+ CONTEXT oldContext;
+
+ oldContext = SetContext (PlanetContext);
+ DrawPlanetSphere (SIS_SCREEN_WIDTH / 2, PLANET_ORG_Y);
+ SetContext (oldContext);
+}
+
+void
+InitSphereRotation (int direction, BOOLEAN shielded)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ rotDirection = direction;
+ rotPointIndex = 0;
+ throbShield = shielded && optWhichShield == OPT_3DO;
+
+ if (throbShield)
+ {
+ // ObjectFrame must contain the shield graphic
+ Orbit->WorkFrame = Orbit->ObjectFrame;
+ // We need a scratch frame so that we can apply throbbing
+ // to the shield, so create one
+ Orbit->ObjectFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP | WANT_ALPHA,
+ GetFrameWidth (Orbit->ObjectFrame),
+ GetFrameHeight (Orbit->ObjectFrame), 2));
+ }
+
+ // Render the first sphere/shield frame
+ // Prepare will set the next one
+ rotFrameIndex = 1;
+ PrepareNextRotationFrame ();
+}
+
+void
+UninitSphereRotation (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ if (Orbit->WorkFrame)
+ {
+ DestroyDrawable (ReleaseDrawable (Orbit->ObjectFrame));
+ Orbit->ObjectFrame = Orbit->WorkFrame;
+ Orbit->WorkFrame = NULL;
+ }
+}
+
+void
+PrepareNextRotationFrame (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ // Generate the next rotation frame
+ // We alternate between the frames because we do not call FlushGraphics()
+ // The frame we just drew may not have made it to the screen yet
+ rotFrameIndex ^= 1;
+
+ // Go to next point, taking care of wraparounds
+ rotPointIndex += rotDirection;
+ if (rotPointIndex < 0)
+ rotPointIndex = MAP_WIDTH - 1;
+ else if (rotPointIndex >= MAP_WIDTH)
+ rotPointIndex = 0;
+
+ // prepare the next sphere frame
+ Orbit->SphereFrame = SetAbsFrameIndex (Orbit->SphereFrame, rotFrameIndex);
+ RenderPlanetSphere (Orbit->SphereFrame, rotPointIndex, throbShield);
+
+ if (throbShield)
+ { // prepare the next shield throb frame
+ Orbit->ObjectFrame = SetAbsFrameIndex (Orbit->ObjectFrame,
+ rotFrameIndex);
+ SetShieldThrobEffect (Orbit->WorkFrame, rotPointIndex,
+ Orbit->ObjectFrame);
+ }
+}
+
+#define ZOOM_RATE 24
+#define ZOOM_TIME (ONE_SECOND * 6 / 5)
+
+// This takes care of zooming the planet sphere into place
+// when entering orbit
+void
+ZoomInPlanetSphere (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ const int base = GSCALE_IDENTITY;
+ int dx, dy;
+ int oldScale;
+ int oldMode;
+ int i;
+ int frameCount;
+ int zoomCorner;
+ RECT frameRect;
+ RECT repairRect;
+ TimeCount NextTime;
+
+ frameCount = ZOOM_TIME / (ONE_SECOND / ZOOM_RATE);
+
+ // Planet zoom in from a randomly chosen corner
+ zoomCorner = TFB_Random ();
+ dx = 1 - (zoomCorner & 1) * 2;
+ dy = 1 - (zoomCorner & 2);
+
+ if (Orbit->ObjectFrame)
+ GetFrameRect (Orbit->ObjectFrame, &frameRect);
+ else
+ GetFrameRect (Orbit->SphereFrame, &frameRect);
+ repairRect = frameRect;
+
+ for (i = 0; i <= frameCount; ++i)
+ {
+ double scale;
+ POINT pt;
+
+ NextTime = GetTimeCounter () + (ONE_SECOND / ZOOM_RATE);
+
+ // Use 1 + e^-2 - e^(-2x / frameCount)) function to get a decelerating
+ // zoom like the one 3DO does (supposedly)
+ if (i < frameCount)
+ scale = 1.134 - exp (-2.0 * i / frameCount);
+ else
+ scale = 1.0; // final frame
+
+ // start from beyond the screen
+ pt.x = SIS_SCREEN_WIDTH / 2 + (int) (dx * (1.0 - scale)
+ * (SIS_SCREEN_WIDTH * 6 / 10) + 0.5);
+ pt.y = PLANET_ORG_Y + (int) (dy * (1.0 - scale)
+ * (SCAN_SCREEN_HEIGHT * 6 / 10) + 0.5);
+
+ SetContext (PlanetContext);
+
+ BatchGraphics ();
+ if (i > 0)
+ RepairBackRect (&repairRect);
+
+ oldMode = SetGraphicScaleMode (TFB_SCALE_BILINEAR);
+ oldScale = SetGraphicScale ((int)(base * scale + 0.5));
+ DrawPlanetSphere (pt.x, pt.y);
+ SetGraphicScale (oldScale);
+ SetGraphicScaleMode (oldMode);
+
+ UnbatchGraphics ();
+
+ repairRect.corner.x = pt.x + frameRect.corner.x;
+ repairRect.corner.y = pt.y + frameRect.corner.y;
+
+ PrepareNextRotationFrame ();
+
+ SleepThreadUntil (NextTime);
+ }
+}
+
+void
+RotatePlanetSphere (BOOLEAN keepRate)
+{
+ static TimeCount NextTime;
+ TimeCount Now = GetTimeCounter ();
+
+ if (keepRate && Now < NextTime)
+ return; // not time yet
+
+ NextTime = Now + PLANET_ROTATION_RATE;
+ DrawDefaultPlanetSphere ();
+
+ PrepareNextRotationFrame ();
+}
+
+static void
+renderTintFrame (Color tintColor)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ CONTEXT oldContext;
+ DrawMode mode, oldMode;
+ STAMP s;
+ RECT r;
+
+ oldContext = SetContext (OffScreenContext);
+ SetContextFGFrame (Orbit->TintFrame);
+ SetContextClipRect (NULL);
+ // get the rect of the whole context (or our frame really)
+ GetContextClipRect (&r);
+
+ // copy the topo frame to the tint frame
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = pSolarSysState->TopoFrame;
+ DrawStamp (&s);
+
+ // apply the tint
+#ifdef USE_ADDITIVE_SCAN_BLIT
+ mode = MAKE_DRAW_MODE (DRAW_ADDITIVE, DRAW_FACTOR_1 / 2);
+#else
+ mode = MAKE_DRAW_MODE (DRAW_ALPHA, DRAW_FACTOR_1 / 2);
+#endif
+ oldMode = SetContextDrawMode (mode);
+ SetContextForeGroundColor (tintColor);
+ DrawFilledRectangle (&r);
+ SetContextDrawMode (oldMode);
+
+ SetContext (oldContext);
+}
+
+// tintColor.a is ignored
+void
+DrawPlanet (int tintY, Color tintColor)
+{
+ STAMP s;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+
+ BatchGraphics ();
+ if (sameColor (tintColor, BLACK_COLOR))
+ { // no tint -- just draw the surface
+ s.frame = pSolarSysState->TopoFrame;
+ DrawStamp (&s);
+ }
+ else
+ { // apply different scan type tints
+ FRAME tintFrame = Orbit->TintFrame;
+ int height = GetFrameHeight (tintFrame);
+
+ if (!sameColor (tintColor, Orbit->TintColor))
+ {
+ renderTintFrame (tintColor);
+ Orbit->TintColor = tintColor;
+ }
+
+ if (tintY < height - 1)
+ { // untinted piece showing, draw regular topo
+ s.frame = pSolarSysState->TopoFrame;
+ DrawStamp (&s);
+ }
+
+ if (tintY >= 0)
+ { // tinted piece showing, draw tinted piece
+ RECT oldClipRect;
+ RECT clipRect;
+
+ // adjust cliprect to confine the tint
+ GetContextClipRect (&oldClipRect);
+ clipRect = oldClipRect;
+ clipRect.extent.height = tintY + 1;
+ SetContextClipRect (&clipRect);
+ s.frame = tintFrame;
+ DrawStamp (&s);
+ SetContextClipRect (&oldClipRect);
+ }
+ }
+ UnbatchGraphics ();
+}
+
diff --git a/src/uqm/planets/plandata.h b/src/uqm/planets/plandata.h
new file mode 100644
index 0000000..cd2a1c7
--- /dev/null
+++ b/src/uqm/planets/plandata.h
@@ -0,0 +1,318 @@
+//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.
+ */
+
+#ifndef UQM_PLANETS_PLANDATA_H_
+#define UQM_PLANETS_PLANDATA_H_
+
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*------------------------------ Type Defines ----------------------------- */
+#define NUMBER_OF_ORBITS 16
+#define VACANT 0xFF
+
+enum
+{
+ SMALL_ROCKY_WORLD = 0,
+ LARGE_ROCKY_WORLD,
+ GAS_GIANT,
+
+ NUM_PLANET_TYPES
+};
+
+enum
+{
+ DWARF_STAR = 0,
+ GIANT_STAR,
+ SUPER_GIANT_STAR,
+
+ NUM_STAR_TYPES
+};
+
+enum
+{
+ BLUE_BODY = 0,
+ GREEN_BODY,
+ ORANGE_BODY,
+ RED_BODY,
+ WHITE_BODY,
+ GRAY_BODY = WHITE_BODY,
+ YELLOW_BODY,
+
+ NUM_STAR_COLORS,
+
+ CYAN_BODY = NUM_STAR_COLORS,
+ PURPLE_BODY,
+ VIOLET_BODY
+};
+
+enum
+{
+ OWNER_NOBODY = 0,
+ OWNER_NEUTRAL,
+ OWNER_HIERARCHY,
+
+ OWNER_PLAYER = (1 << 2)
+};
+
+#define STAR_OWNER_SHIFT 0
+#define STAR_TYPE_SHIFT 3 /* STAR_OWNER_SHIFT + 3 */
+#define STAR_COLOR_SHIFT 5 /* STAR_TYPE_SHIFT + 2 */
+#define STAR_COLOR_MASK (BYTE)(0xFF << STAR_COLOR_SHIFT)
+#define STAR_TYPE_MASK (BYTE)((0xFF << STAR_TYPE_SHIFT) \
+ & ~STAR_COLOR_MASK)
+#define STAR_OWNER_MASK (BYTE)((0xFF << STAR_OWNER_SHIFT) \
+ & ~(STAR_COLOR_MASK \
+ | STAR_TYPE_MASK))
+#define STAR_UNKNOWN_MASK (STAR_OWNER_MASK & ~OWNER_PLAYER)
+
+#define MAKE_STAR(t,c,o) \
+ (BYTE)((((BYTE)(t) << STAR_TYPE_SHIFT) & STAR_TYPE_MASK) \
+ | (((BYTE)(c) << STAR_COLOR_SHIFT) & STAR_COLOR_MASK) \
+ | (((BYTE)(o) << STAR_OWNER_SHIFT) & STAR_OWNER_MASK))
+#define STAR_TYPE(f) (BYTE)(((f) & STAR_TYPE_MASK) >> STAR_TYPE_SHIFT)
+#define STAR_COLOR(f) (BYTE)(((f) & STAR_COLOR_MASK) >> STAR_COLOR_SHIFT)
+#define STAR_OWNER(f) (BYTE)(((f) & STAR_OWNER_MASK) >> STAR_OWNER_SHIFT)
+#define STAR_UNKNOWN(f) (BOOLEAN)((STAR_OWNER(f) \
+ & STAR_UNKNOWN_MASK) == STAR_UNKNOWN_MASK)
+
+#define PLAN_SIZE_MASK 0x03
+
+#define TOPO_ALGO (0 << 2)
+#define CRATERED_ALGO (1 << 2)
+#define GAS_GIANT_ALGO (2 << 2)
+#define PLAN_ALGO_MASK 0x0C
+
+#define PLANSIZE(type) ((BYTE)((type) & PLAN_SIZE_MASK))
+#define PLANALGO(type) ((BYTE)((type) & PLAN_ALGO_MASK))
+#define PLANCOLOR(type) HINIBBLE (type)
+
+#define THIN_ATMOSPHERE 10
+#define NORMAL_ATMOSPHERE 75
+#define THICK_ATMOSPHERE 200
+#define SUPER_THICK_ATMOSPHERE 2500
+#define GAS_GIANT_ATMOSPHERE 0xFFFF
+
+enum
+{
+ FIRST_ROCKY_WORLD = 0,
+ FIRST_SMALL_ROCKY_WORLD = FIRST_ROCKY_WORLD,
+
+ OOLITE_WORLD = FIRST_SMALL_ROCKY_WORLD,
+ YTTRIC_WORLD,
+ QUASI_DEGENERATE_WORLD,
+ LANTHANIDE_WORLD,
+ TREASURE_WORLD,
+ UREA_WORLD,
+ METAL_WORLD,
+ RADIOACTIVE_WORLD,
+ OPALESCENT_WORLD,
+ CYANIC_WORLD,
+ ACID_WORLD,
+ ALKALI_WORLD,
+ HALIDE_WORLD,
+ GREEN_WORLD,
+ COPPER_WORLD,
+ CARBIDE_WORLD,
+ ULTRAMARINE_WORLD,
+ NOBLE_WORLD,
+ AZURE_WORLD,
+ CHONDRITE_WORLD,
+ PURPLE_WORLD,
+ SUPER_DENSE_WORLD,
+ PELLUCID_WORLD,
+ DUST_WORLD,
+ CRIMSON_WORLD,
+ CIMMERIAN_WORLD,
+ INFRARED_WORLD,
+ SELENIC_WORLD,
+ AURIC_WORLD,
+ LAST_SMALL_ROCKY_WORLD = AURIC_WORLD,
+
+ FIRST_LARGE_ROCKY_WORLD,
+ FLUORESCENT_WORLD = FIRST_LARGE_ROCKY_WORLD,
+ ULTRAVIOLET_WORLD,
+ PLUTONIC_WORLD,
+ RAINBOW_WORLD,
+ SHATTERED_WORLD,
+ SAPPHIRE_WORLD,
+ ORGANIC_WORLD,
+ XENOLITHIC_WORLD,
+ REDUX_WORLD,
+ PRIMORDIAL_WORLD,
+ EMERALD_WORLD,
+ CHLORINE_WORLD,
+ MAGNETIC_WORLD,
+ WATER_WORLD,
+ TELLURIC_WORLD,
+ HYDROCARBON_WORLD,
+ IODINE_WORLD,
+ VINYLOGOUS_WORLD,
+ RUBY_WORLD,
+ MAGMA_WORLD,
+ MAROON_WORLD,
+ LAST_LARGE_ROCKY_WORLD = MAROON_WORLD,
+
+ FIRST_GAS_GIANT,
+ BLU_GAS_GIANT = FIRST_GAS_GIANT, /* Gas Giants */
+ CYA_GAS_GIANT,
+ GRN_GAS_GIANT,
+ GRY_GAS_GIANT,
+ ORA_GAS_GIANT,
+ PUR_GAS_GIANT,
+ RED_GAS_GIANT,
+ VIO_GAS_GIANT,
+ YEL_GAS_GIANT,
+ LAST_GAS_GIANT = YEL_GAS_GIANT,
+
+ NUMBER_OF_PLANET_TYPES,
+
+ WORLD_TYPE_SPECIAL = 0x80,
+ PLANET_SHIELDED = WORLD_TYPE_SPECIAL,
+
+ HIERARCHY_STARBASE = 127 | WORLD_TYPE_SPECIAL,
+ SA_MATRA = 126 | WORLD_TYPE_SPECIAL,
+};
+
+#define NUMBER_OF_SMALL_ROCKY_WORLDS (LAST_SMALL_ROCKY_WORLD - FIRST_SMALL_ROCKY_WORLD + 1)
+#define NUMBER_OF_LARGE_ROCKY_WORLDS (LAST_LARGE_ROCKY_WORLD - FIRST_LARGE_ROCKY_WORLD + 1)
+#define NUMBER_OF_ROCKY_WORLDS (NUMBER_OF_SMALL_ROCKY_WORLDS + NUMBER_OF_LARGE_ROCKY_WORLDS)
+#define NUMBER_OF_GAS_GIANTS (LAST_GAS_GIANT - FIRST_GAS_GIANT + 1)
+
+// TODO: This struct is highly alignment and padding dependent and
+// should not be used! The data is loaded as binary from files and
+// cast to this struct.
+typedef struct
+{
+ const SIZE level_tab[3];
+ const BYTE xlat_tab[256];
+} XLAT_DESC;
+
+typedef struct
+{
+ BYTE ElementType;
+ /* Index of this element in element_array */
+ BYTE Density;
+ /* bits 0-3: quantity of the deposits (maximum number of
+ * deposits), one of FEW, MODERATE, or NUMEROUS
+ * bits 4-7: quality of the deposit, one of LOW, MEDIUM, or HEAVY
+ */
+} ELEMENT_ENTRY;
+
+// PlanetFrame describes a type of planet. It is not used to describe
+// individual planets.
+typedef struct
+{
+ BYTE Type;
+ /* bits 0-1: size, one of SMALL_ROCKY_WORLD, LARGE_ROCKY_WORLD, or
+ * GAS_GIANT
+ * bits 2-3: map creation algoritm, one of TOPO_ALGO,
+ * CRATERED_ALGO, or GAS_GIANT_ALGO
+ * bits 4-7: interplanetary color, one of BLUE_BODY, GREEN_BODY,
+ * ORANGE_BODY, RED_BODY, WHITE_BODY (same as
+ * GRAY_BODY), YELLOW_BODY, CYAN_BODY, PURPLE_BODY,
+ * VIOLET_BODY)
+ */
+ BYTE BaseTectonics;
+ /* Base constant for calculation of tectonic activity,
+ * relative to Earth at 100.
+ * One of: NO_TECTONICS, LOW_TECTONICS, MED_TECTONICS,
+ * HIGH_TECTONICS, or SUPER_TECTONICS
+ */
+ BYTE AtmoAndDensity;
+ /* bits 0-3: planet density, one of GAS_DENSITY, LIGHT_DENSITY,
+ * LOW_DENSITY, NORMAL_DENSITY, HIGH_DENSITY,
+ * SUPER_DENSITY
+ * bits 4-7: atmosphere, one of LIGHT, MEDIUM, HEAVY, or
+ * (no define for this) super thick.
+ */
+#define NUM_USEFUL_ELEMENTS 8
+ ELEMENT_ENTRY UsefulElements[NUM_USEFUL_ELEMENTS];
+ /* Minerals on the planet */
+
+ RESOURCE CMapInstance;
+ /* Color map */
+ RESOURCE XlatTabInstance;
+ /* Color translation map */
+
+ // Parameters for map-generation algoritms:
+ COUNT num_faults;
+ SIZE fault_depth;
+ COUNT num_blemishes;
+ SIZE base_elevation;
+} PlanetFrame;
+
+typedef struct
+{
+ SIZE AxialTilt;
+ UWORD Tectonics;
+ UWORD Weather;
+ UWORD PlanetDensity;
+ UWORD PlanetRadius;
+ UWORD SurfaceGravity;
+ SIZE SurfaceTemperature;
+ UWORD RotationPeriod;
+ UWORD AtmoDensity;
+ SIZE LifeChance;
+ UWORD PlanetToSunDist;
+
+ const PlanetFrame *PlanDataPtr;
+
+ DWORD ScanSeed[NUM_SCAN_TYPES];
+ DWORD ScanRetrieveMask[NUM_SCAN_TYPES];
+
+ STRING DiscoveryString;
+ FONT LanderFont;
+ FRAME LanderFontEff;
+} PLANET_INFO;
+
+enum
+{
+ GAS_DENSITY,
+ LIGHT_DENSITY,
+ LOW_DENSITY,
+ NORMAL_DENSITY,
+ HIGH_DENSITY,
+ SUPER_DENSITY
+};
+
+extern UWORD CalcGravity (const PLANET_INFO*);
+
+#define EARTH_ATMOSPHERE 50
+
+#define COLD_THRESHOLD -40
+#define HOT_THRESHOLD 100
+
+/*------------------------------ Global Data ------------------------------ */
+
+#define NO_TECTONICS 0
+#define LOW_TECTONICS 40
+#define MED_TECTONICS 80
+#define HIGH_TECTONICS 140
+#define SUPER_TECTONICS 200
+
+extern const PlanetFrame *PlanData;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_PLANDATA_H_ */
diff --git a/src/uqm/planets/planets.c b/src/uqm/planets/planets.c
new file mode 100644
index 0000000..c76c2bb
--- /dev/null
+++ b/src/uqm/planets/planets.c
@@ -0,0 +1,483 @@
+//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 "planets.h"
+
+#include "scan.h"
+#include "lander.h"
+#include "../colors.h"
+#include "../element.h"
+#include "../settings.h"
+#include "../controls.h"
+#include "../sounds.h"
+#include "../gameopt.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../uqmdebug.h"
+#include "../resinst.h"
+#include "../nameref.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+
+
+// PlanetOrbitMenu() items
+enum PlanetMenuItems
+{
+ // XXX: Must match the enum in menustat.h
+ SCAN = 0,
+ STARMAP,
+ EQUIP_DEVICE,
+ CARGO,
+ ROSTER,
+ GAME_MENU,
+ NAVIGATION,
+};
+
+CONTEXT PlanetContext;
+ // Context for rotating planet view and lander surface view
+
+static void
+CreatePlanetContext (void)
+{
+ CONTEXT oldContext;
+ RECT r;
+
+ assert (PlanetContext == NULL);
+
+ // PlanetContext rect is relative to SpaceContext
+ oldContext = SetContext (SpaceContext);
+ GetContextClipRect (&r);
+
+ PlanetContext = CreateContext ("PlanetContext");
+ SetContext (PlanetContext);
+ SetContextFGFrame (Screen);
+ r.extent.height -= MAP_HEIGHT + MAP_BORDER_HEIGHT;
+ SetContextClipRect (&r);
+
+ SetContext (oldContext);
+}
+
+static void
+DestroyPlanetContext (void)
+{
+ if (PlanetContext)
+ {
+ DestroyContext (PlanetContext);
+ PlanetContext = NULL;
+ }
+}
+
+void
+DrawScannedObjects (BOOLEAN Reversed)
+{
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = Reversed ? GetTailElement () : GetHeadElement ();
+ hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = Reversed ?
+ GetPredElement (ElementPtr) :
+ GetSuccElement (ElementPtr);
+
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ STAMP s;
+
+ s.origin = ElementPtr->current.location;
+ s.frame = ElementPtr->next.image.frame;
+ DrawStamp (&s);
+ }
+
+ UnlockElement (hElement);
+ }
+}
+
+void
+DrawPlanetSurfaceBorder (void)
+{
+ CONTEXT oldContext;
+ RECT oldClipRect;
+ RECT clipRect;
+ RECT r;
+
+ oldContext = SetContext (SpaceContext);
+ GetContextClipRect (&oldClipRect);
+
+ // Expand the context clip-rect so that we can tweak the existing border
+ clipRect = oldClipRect;
+ clipRect.corner.x -= 1;
+ clipRect.extent.width += 2;
+ clipRect.extent.height += 1;
+ SetContextClipRect (&clipRect);
+
+ BatchGraphics ();
+
+ // Border bulk
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ r.corner.x = 0;
+ r.corner.y = clipRect.extent.height - MAP_HEIGHT - MAP_BORDER_HEIGHT;
+ r.extent.width = clipRect.extent.width;
+ r.extent.height = MAP_BORDER_HEIGHT - 2;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (SIS_BOTTOM_RIGHT_BORDER_COLOR);
+
+ // Border top shadow line
+ r.extent.width -= 1;
+ r.extent.height = 1;
+ r.corner.x = 1;
+ r.corner.y -= 1;
+ DrawFilledRectangle (&r);
+
+ // XXX: We will need bulk left and right rects here if MAP_WIDTH changes
+
+ // Right shadow line
+ r.extent.width = 1;
+ r.extent.height = MAP_HEIGHT + 2;
+ r.corner.y += MAP_BORDER_HEIGHT - 1;
+ r.corner.x = clipRect.extent.width - 1;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (SIS_LEFT_BORDER_COLOR);
+
+ // Left shadow line
+ r.corner.x -= MAP_WIDTH + 1;
+ DrawFilledRectangle (&r);
+
+ // Border bottom shadow line
+ r.extent.width = MAP_WIDTH + 2;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+
+ UnbatchGraphics ();
+
+ SetContextClipRect (&oldClipRect);
+ SetContext (oldContext);
+}
+
+typedef enum
+{
+ DRAW_ORBITAL_FULL,
+ DRAW_ORBITAL_WAIT,
+ DRAW_ORBITAL_UPDATE,
+
+} DRAW_ORBITAL_MODE;
+
+static void
+DrawOrbitalDisplay (DRAW_ORBITAL_MODE Mode)
+{
+ RECT r;
+
+ SetContext (SpaceContext);
+ GetContextClipRect (&r);
+
+ BatchGraphics ();
+
+ if (Mode != DRAW_ORBITAL_UPDATE)
+ {
+ SetTransitionSource (NULL);
+
+ DrawSISFrame ();
+ DrawSISMessage (NULL);
+ DrawSISTitle (GLOBAL_SIS (PlanetName));
+ DrawStarBackGround ();
+ DrawPlanetSurfaceBorder ();
+ }
+
+ if (Mode == DRAW_ORBITAL_WAIT)
+ {
+ STAMP s;
+
+ SetContext (GetScanContext (NULL));
+ s.frame = CaptureDrawable (LoadGraphic (ORBENTER_PMAP_ANIM));
+ s.origin.x = -SAFE_X;
+ s.origin.y = 0;
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+ }
+ else if (Mode == DRAW_ORBITAL_FULL)
+ {
+ DrawDefaultPlanetSphere ();
+ }
+
+ if (Mode != DRAW_ORBITAL_WAIT)
+ {
+ SetContext (GetScanContext (NULL));
+ DrawPlanet (0, BLACK_COLOR);
+ }
+
+ if (Mode != DRAW_ORBITAL_UPDATE)
+ {
+ ScreenTransition (3, &r);
+ }
+
+ UnbatchGraphics ();
+
+ // for later RepairBackRect()
+ LoadIntoExtraScreen (&r);
+}
+
+// Initialise the surface graphics, and start the planet music.
+// Called from the GenerateFunctions.generateOribital() function
+// (when orbit is entered; either from IP, or from loading a saved game)
+// and when "starmap" is selected from orbit and then cancelled;
+// also after in-orbit comm and after defeating planet guards in combat.
+// SurfDefFrame contains surface definition images when a planet comes
+// with its own bitmap (currently only for Earth)
+void
+LoadPlanet (FRAME SurfDefFrame)
+{
+ bool WaitMode = !(LastActivity & CHECK_LOAD);
+ PLANET_DESC *pPlanetDesc;
+
+#ifdef DEBUG
+ if (disableInteractivity)
+ return;
+#endif
+
+ assert (pSolarSysState->InOrbit && !pSolarSysState->TopoFrame);
+
+ CreatePlanetContext ();
+
+ if (WaitMode)
+ {
+ DrawOrbitalDisplay (DRAW_ORBITAL_WAIT);
+ }
+
+ StopMusic ();
+
+ pPlanetDesc = pSolarSysState->pOrbitalDesc;
+ GeneratePlanetSurface (pPlanetDesc, SurfDefFrame);
+ SetPlanetMusic (pPlanetDesc->data_index & ~PLANET_SHIELDED);
+ GeneratePlanetSide ();
+
+ if (!PLRPlaying ((MUSIC_REF)~0))
+ PlayMusic (LanderMusic, TRUE, 1);
+
+ if (WaitMode)
+ {
+ ZoomInPlanetSphere ();
+ DrawOrbitalDisplay (DRAW_ORBITAL_UPDATE);
+ }
+ else
+ {
+ DrawOrbitalDisplay (DRAW_ORBITAL_FULL);
+ }
+}
+
+void
+FreePlanet (void)
+{
+ COUNT i;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ UninitSphereRotation ();
+
+ StopMusic ();
+
+ for (i = 0; i < sizeof (pSolarSysState->PlanetSideFrame)
+ / sizeof (pSolarSysState->PlanetSideFrame[0]); ++i)
+ {
+ DestroyDrawable (ReleaseDrawable (pSolarSysState->PlanetSideFrame[i]));
+ pSolarSysState->PlanetSideFrame[i] = 0;
+ }
+
+// FreeLanderData ();
+
+ DestroyStringTable (ReleaseStringTable (pSolarSysState->XlatRef));
+ pSolarSysState->XlatRef = 0;
+ DestroyDrawable (ReleaseDrawable (pSolarSysState->TopoFrame));
+ pSolarSysState->TopoFrame = 0;
+ DestroyColorMap (ReleaseColorMap (pSolarSysState->OrbitalCMap));
+ pSolarSysState->OrbitalCMap = 0;
+
+ HFree (Orbit->lpTopoData);
+ Orbit->lpTopoData = 0;
+ DestroyDrawable (ReleaseDrawable (Orbit->TopoZoomFrame));
+ Orbit->TopoZoomFrame = 0;
+ DestroyDrawable (ReleaseDrawable (Orbit->SphereFrame));
+ Orbit->SphereFrame = NULL;
+
+ DestroyDrawable (ReleaseDrawable (Orbit->TintFrame));
+ Orbit->TintFrame = 0;
+ Orbit->TintColor = BLACK_COLOR;
+
+ DestroyDrawable (ReleaseDrawable (Orbit->ObjectFrame));
+ Orbit->ObjectFrame = 0;
+ DestroyDrawable (ReleaseDrawable (Orbit->WorkFrame));
+ Orbit->WorkFrame = 0;
+
+ HFree (Orbit->TopoColors);
+ Orbit->TopoColors = NULL;
+ HFree (Orbit->ScratchArray);
+ Orbit->ScratchArray = NULL;
+
+ DestroyStringTable (ReleaseStringTable (
+ pSolarSysState->SysInfo.PlanetInfo.DiscoveryString
+ ));
+ pSolarSysState->SysInfo.PlanetInfo.DiscoveryString = 0;
+ FreeLanderFont (&pSolarSysState->SysInfo.PlanetInfo);
+
+ // Need to make sure our own CONTEXTs are not active because
+ // we will destroy them now
+ SetContext (SpaceContext);
+ DestroyPlanetContext ();
+ DestroyScanContext ();
+
+}
+
+void
+LoadStdLanderFont (PLANET_INFO *info)
+{
+ info->LanderFont = LoadFont (LANDER_FONT);
+ info->LanderFontEff = CaptureDrawable (
+ LoadGraphic (LANDER_FONTEFF_PMAP_ANIM));
+}
+
+void
+FreeLanderFont (PLANET_INFO *info)
+{
+ DestroyFont (info->LanderFont);
+ info->LanderFont = NULL;
+ DestroyDrawable (ReleaseDrawable (info->LanderFontEff));
+ info->LanderFontEff = NULL;
+}
+
+static BOOLEAN
+DoPlanetOrbit (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;
+
+ // XXX: pMS actually refers to pSolarSysState->MenuState
+ handled = DoMenuChooser (pMS, PM_SCAN);
+ if (handled)
+ return TRUE;
+
+ if (!select)
+ return TRUE;
+
+ SetFlashRect (NULL);
+
+ switch (pMS->CurState)
+ {
+ case SCAN:
+ ScanSystem ();
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ { // Found Fwiffo on Pluto
+ return FALSE;
+ }
+ break;
+ case EQUIP_DEVICE:
+ select = DevicesMenu ();
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ { // Invoked Talking Pet, a Caster or Sun Device over Chmmr,
+ // 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:
+ {
+ BOOLEAN AutoPilotSet;
+ InputFrameCallback *oldCallback;
+
+ // Deactivate planet rotation
+ oldCallback = SetInputCallback (NULL);
+
+ RepairSISBorder ();
+
+ AutoPilotSet = StarMap ();
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ // Reactivate planet rotation
+ SetInputCallback (oldCallback);
+
+ if (!AutoPilotSet)
+ { // Redraw the orbital display
+ DrawOrbitalDisplay (DRAW_ORBITAL_FULL);
+ break;
+ }
+ // 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_SCAN, pMS->CurState);
+ }
+ SetFlashRect (SFR_MENU_3DO);
+ }
+
+ return TRUE;
+}
+
+static void
+on_input_frame (void)
+{
+ RotatePlanetSphere (TRUE);
+}
+
+void
+PlanetOrbitMenu (void)
+{
+ MENU_STATE MenuState;
+ InputFrameCallback *oldCallback;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ DrawMenuStateStrings (PM_SCAN, SCAN);
+ SetFlashRect (SFR_MENU_3DO);
+
+ MenuState.CurState = SCAN;
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ oldCallback = SetInputCallback (on_input_frame);
+
+ MenuState.InputFunc = DoPlanetOrbit;
+ DoInput (&MenuState, TRUE);
+
+ SetInputCallback (oldCallback);
+
+ SetFlashRect (NULL);
+ DrawMenuStateStrings (PM_STARMAP, -NAVIGATION);
+}
diff --git a/src/uqm/planets/planets.h b/src/uqm/planets/planets.h
new file mode 100644
index 0000000..c6f440d
--- /dev/null
+++ b/src/uqm/planets/planets.h
@@ -0,0 +1,322 @@
+//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.
+ */
+
+#ifndef UQM_PLANETS_PLANETS_H_
+#define UQM_PLANETS_PLANETS_H_
+
+#include "libs/mathlib.h"
+
+#define END_INTERPLANETARY START_INTERPLANETARY
+
+enum PlanetScanTypes
+{
+ MINERAL_SCAN = 0,
+ ENERGY_SCAN,
+ BIOLOGICAL_SCAN,
+
+ NUM_SCAN_TYPES,
+};
+
+#define MAP_WIDTH SIS_SCREEN_WIDTH
+#define MAP_HEIGHT (75 - SAFE_Y)
+
+enum
+{
+ BIOLOGICAL_DISASTER = 0,
+ EARTHQUAKE_DISASTER,
+ LIGHTNING_DISASTER,
+ LAVASPOT_DISASTER,
+
+ /* additional lander sounds */
+ LANDER_INJURED,
+ LANDER_SHOOTS,
+ LANDER_HITS,
+ LIFEFORM_CANNED,
+ LANDER_PICKUP,
+ LANDER_FULL,
+ LANDER_DEPARTS,
+ LANDER_RETURNS,
+ LANDER_DESTROYED
+};
+
+#define MAX_SCROUNGED 50 /* max lander can hold */
+
+#define SCALE_RADIUS(r) ((r) << 6)
+#define UNSCALE_RADIUS(r) ((r) >> 6)
+#define MAX_ZOOM_RADIUS SCALE_RADIUS(128)
+#define MIN_ZOOM_RADIUS (MAX_ZOOM_RADIUS>>3)
+#define EARTH_RADIUS SCALE_RADIUS(8)
+
+#define MIN_PLANET_RADIUS SCALE_RADIUS (4)
+#define MAX_PLANET_RADIUS SCALE_RADIUS (124)
+
+#define DISPLAY_FACTOR ((SIS_SCREEN_WIDTH >> 1) - 8)
+
+#define NUM_SCANDOT_TRANSITIONS 4
+
+#define MIN_MOON_RADIUS 35
+#define MOON_DELTA 20
+
+#define MAX_SUNS 1
+#define MAX_PLANETS 16
+#define MAX_MOONS 4
+
+#define MAP_BORDER_HEIGHT 5
+#define SCAN_SCREEN_HEIGHT (SIS_SCREEN_HEIGHT - MAP_HEIGHT - MAP_BORDER_HEIGHT)
+
+#define PLANET_ROTATION_TIME (ONE_SECOND * 12)
+#define PLANET_ROTATION_RATE (PLANET_ROTATION_TIME / MAP_WIDTH)
+// XXX: -9 to match the original, but why? I have no idea
+#define PLANET_ORG_Y ((SCAN_SCREEN_HEIGHT - 9) / 2)
+
+#define NUM_RACE_RUINS 16
+
+typedef struct planet_desc PLANET_DESC;
+typedef struct star_desc STAR_DESC;
+typedef struct node_info NODE_INFO;
+typedef struct planet_orbit PLANET_ORBIT;
+typedef struct solarsys_state SOLARSYS_STATE;
+
+
+#include "generate.h"
+#include "../menustat.h"
+#include "../units.h"
+
+#include "elemdata.h"
+#include "lifeform.h"
+#include "plandata.h"
+#include "sundata.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct planet_desc
+{
+ DWORD rand_seed;
+
+ BYTE data_index;
+ BYTE NumPlanets;
+ SIZE radius;
+ POINT location;
+
+ Color temp_color;
+ COUNT NextIndex;
+ STAMP image;
+
+ PLANET_DESC *pPrevDesc;
+ // The Sun or planet that this world is orbiting around.
+};
+
+struct star_desc
+{
+ POINT star_pt;
+ BYTE Type;
+ BYTE Index;
+ BYTE Prefix;
+ BYTE Postfix;
+};
+
+struct node_info
+{
+ // This structire is filled in when a generateMinerals, generateEnergy,
+ // or generateLife call is made.
+ POINT loc_pt;
+ // Position of the mineral/bio/energy node on the planet.
+ COUNT density;
+ // For bio and energy: undefined
+ // For minerals the low byte is the gross size of the
+ // deposit (this determines the image), and the high
+ // byte is the fine size (the actual quantity).
+ COUNT type;
+ // For minerals: the type of element
+ // For bio: the type of the creature.
+ // 0 through NUM_CREATURE_TYPES - 1 are normal creatures,
+ // NUM_CREATURE_TYPES is an Evil One
+ // NUM_CREATURE_TYPES + 1 is a Brainbox Bulldozer
+ // NUM_CREATURE_TYPES + 2 is Zex' Beauty
+ // For energy: undefined
+};
+
+struct planet_orbit
+{
+ FRAME TopoZoomFrame;
+ // 4x scaled topo image for planet-side
+ SBYTE *lpTopoData;
+ // normal topo data; expressed in elevation levels
+ // data is signed for planets other than gas giants
+ // transformed to light variance map for 3d planet
+ FRAME SphereFrame;
+ // rotating 3d planet frames (current and next)
+ FRAME ObjectFrame;
+ // any extra planetary object (shield, atmo, rings)
+ // automatically drawn if present
+ FRAME TintFrame;
+ // tinted topo images for current scan type (dynamic)
+ Color TintColor;
+ // the color of the last used tint
+ Color *TopoColors;
+ // RGBA version of topo image; for 3d planet
+ Color *ScratchArray;
+ // temp RGBA data for whatever transforms (nuked often)
+ FRAME WorkFrame;
+ // any extra frame workspace (for dynamic objects)
+};
+
+// See doc/devel/generate for information on how this structure is
+// filled.
+struct solarsys_state
+{
+ // Standard field required by DoInput()
+ BOOLEAN (*InputFunc) (struct solarsys_state *);
+
+ BOOLEAN InIpFlight;
+ // Set to TRUE when player is flying around in interplanetary
+ // Reset to FALSE when going into orbit or encounter
+
+ COUNT WaitIntersect;
+ // Planet/moon number with which the flagship should not collide
+ // For example, if the player just left the planet or inner system
+ // If set to (COUNT)~0, all planet collisions are disabled until
+ // the flagship stops intersecting with all planets.
+ PLANET_DESC SunDesc[MAX_SUNS];
+ PLANET_DESC PlanetDesc[MAX_PLANETS];
+ // Description of the planets in the system.
+ // Only defined after a call to (*genFuncs)->generatePlanets()
+ // and overwritten by subsequent calls.
+ PLANET_DESC MoonDesc[MAX_MOONS];
+ // Description of the moons orbiting the planet pointed to
+ // by pBaseDesc.
+ // Only defined after a call to (*genFuncs)->generateMoons()
+ // as its argument, and overwritten by subsequent calls.
+ PLANET_DESC *pBaseDesc;
+ // In outer system: points to PlanetDesc[]
+ // In inner system: points to MoonDesc[]
+ PLANET_DESC *pOrbitalDesc;
+ // In orbit: points into PlanetDesc or MoonDesc to the planet
+ // currently orbiting.
+ // In inner system: points into PlanetDesc to the planet whose
+ // inner system the ship is inside
+ SIZE FirstPlanetIndex, LastPlanetIndex;
+ // The planets get sorted on their image.origin.y value.
+ // PlanetDesc[FirstPlanetIndex] is the planet with the lowest
+ // image.origin.y, and PlanetDesc[LastPlanetIndex] has the
+ // highest image.origin.y.
+ // PlanetDesc[PlanetDesc[i].NextIndex] is the next planet
+ // after PlanetDesc[i] in the ordering.
+
+ BYTE turn_counter;
+ BYTE turn_wait;
+ BYTE thrust_counter;
+ BYTE max_ship_speed;
+
+ STRING XlatRef;
+ const void *XlatPtr;
+ COLORMAP OrbitalCMap;
+
+ SYSTEM_INFO SysInfo;
+
+ const GenerateFunctions *genFuncs;
+ // Functions to call to fill in various parts of this structure.
+ // See generate.h, doc/devel/generate
+
+ FRAME PlanetSideFrame[3 + MAX_LIFE_VARIATION];
+ /* Frames for planet-side elements.
+ * [0] = bio cannister
+ * [1] = energy node (world-specific)
+ * [2] = unused (formerly static slave shield, presumed)
+ * [3] = bio 1 (world-specific)
+ * [4] = bio 2 (world-specific)
+ * [5] = bio 3 (world-specific)
+ */
+ FRAME TopoFrame;
+ PLANET_ORBIT Orbit;
+ BOOLEAN InOrbit;
+ // Set to TRUE when player hits a world in an inner system
+ // Homeworld encounters count as 'in orbit'
+};
+
+extern SOLARSYS_STATE *pSolarSysState;
+extern MUSIC_REF SpaceMusic;
+extern CONTEXT PlanetContext;
+
+// Random context used for all solar system, planets and surfaces generation
+extern RandomContext *SysGenRNG;
+
+bool playerInSolarSystem (void);
+bool playerInPlanetOrbit (void);
+bool playerInInnerSystem (void);
+bool worldIsPlanet (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world);
+bool worldIsMoon (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world);
+COUNT planetIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world);
+COUNT moonIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *moon);
+#define MATCH_PLANET ((BYTE) -1)
+bool matchWorld (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world,
+ BYTE planetI, BYTE moonI);
+
+DWORD GetRandomSeedForStar (const STAR_DESC *star);
+
+POINT locationToDisplay (POINT pt, SIZE scaleRadius);
+POINT displayToLocation (POINT pt, SIZE scaleRadius);
+POINT planetOuterLocation (COUNT planetI);
+
+extern void LoadPlanet (FRAME SurfDefFrame);
+extern void DrawPlanet (int dy, Color tintColor);
+extern void FreePlanet (void);
+extern void LoadStdLanderFont (PLANET_INFO *info);
+extern void FreeLanderFont (PLANET_INFO *info);
+
+extern void ExploreSolarSys (void);
+extern void DrawStarBackGround (void);
+extern void XFormIPLoc (POINT *pIn, POINT *pOut, BOOLEAN ToDisplay);
+extern void DrawOval (RECT *pRect, BYTE num_off_pixels);
+extern void DrawFilledOval (RECT *pRect);
+extern void FillOrbits (SOLARSYS_STATE *system, BYTE NumPlanets,
+ PLANET_DESC *pBaseDesc, BOOLEAN TypesDefined);
+extern void InitLander (BYTE LanderFlags);
+
+extern void InitSphereRotation (int direction, BOOLEAN shielded);
+extern void UninitSphereRotation (void);
+extern void PrepareNextRotationFrame (void);
+extern void DrawPlanetSphere (int x, int y);
+extern void DrawDefaultPlanetSphere (void);
+extern void RenderPlanetSphere (FRAME Frame, int offset, BOOLEAN doThrob);
+extern void SetShieldThrobEffect (FRAME FromFrame, int offset, FRAME ToFrame);
+
+extern void ZoomInPlanetSphere (void);
+extern void RotatePlanetSphere (BOOLEAN keepRate);
+
+extern void DrawScannedObjects (BOOLEAN Reversed);
+extern void GeneratePlanetSurface (PLANET_DESC *pPlanetDesc,
+ FRAME SurfDefFrame);
+extern void DeltaTopography (COUNT num_iterations, SBYTE *DepthArray,
+ RECT *pRect, SIZE depth_delta);
+
+extern void DrawPlanetSurfaceBorder (void);
+
+extern UNICODE* GetNamedPlanetaryBody (void);
+extern void GetPlanetOrMoonName (UNICODE *buf, COUNT bufsize);
+
+extern void PlanetOrbitMenu (void);
+extern void SaveSolarSysLocation (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_PLANETS_H_ */
diff --git a/src/uqm/planets/plangen.c b/src/uqm/planets/plangen.c
new file mode 100644
index 0000000..005a968
--- /dev/null
+++ b/src/uqm/planets/plangen.c
@@ -0,0 +1,1954 @@
+//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 "planets.h"
+#include "scan.h"
+#include "../nameref.h"
+#include "../resinst.h"
+#include "../setup.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include <math.h>
+#include <time.h>
+
+
+#undef PROFILE_ROTATION
+
+// define USE_ALPHA_SHIELD to use an aloha overlay instead of
+// an additive overlay for the shield effect
+#undef USE_ALPHA_SHIELD
+
+#define SHIELD_GLOW_COMP 120
+#define SHIELD_REFLECT_COMP 100
+
+#define NUM_BATCH_POINTS 64
+#define RADIUS 37
+//2*RADIUS
+#define TWORADIUS (RADIUS << 1)
+//RADIUS^2
+#define RADIUS_2 (RADIUS * RADIUS)
+// distance beyond which all pixels are transparent (for aa)
+#define RADIUS_THRES ((RADIUS + 1) * (RADIUS + 1))
+#define DIAMETER (TWORADIUS + 1)
+#if 0
+# define SPHERE_SPAN_X (MAP_WIDTH >> 1)
+#else
+# define SPHERE_SPAN_X (MAP_HEIGHT)
+#endif
+ // XXX: technically, the sphere's span over X should be MAP_WIDTH/2
+ // but this causes visible surface compression over X, because
+ // the surface dims ratio is H x H*PI, instead of H x 2*H
+ // see bug #885
+
+#define DIFFUSE_BITS 16
+#define AA_WEIGHT_BITS 16
+
+#ifndef M_TWOPI
+ #ifndef M_PI
+ #define M_PI 3.14159265358979323846
+ #endif
+ #define M_TWOPI (M_PI * 2.0)
+#endif
+#ifndef M_DEG2RAD
+#define M_DEG2RAD (M_TWOPI / 360.0)
+#endif
+
+DWORD light_diff[DIAMETER][DIAMETER];
+
+typedef struct
+{
+ POINT p[4];
+ DWORD m[4];
+} MAP3D_POINT;
+
+MAP3D_POINT map_rotate[DIAMETER][DIAMETER];
+
+typedef struct
+{
+ double x, y, z;
+} POINT3;
+
+static void
+RenderTopography (FRAME DstFrame, SBYTE *pTopoData, int w, int h)
+{
+ FRAME OldFrame;
+
+ OldFrame = SetContextFGFrame (DstFrame);
+
+ if (pSolarSysState->XlatRef == 0)
+ {
+ // There is currently nothing we can do w/o an xlat table
+ // This is still called for Earth for 4x scaled topo, but we
+ // do not need it because we cannot land on Earth.
+ }
+ else
+ {
+ COUNT i;
+ BYTE AlgoType;
+ SIZE base, d;
+ const XLAT_DESC *xlatDesc;
+ POINT pt;
+ const PlanetFrame *PlanDataPtr;
+ PRIMITIVE BatchArray[NUM_BATCH_POINTS];
+ PRIMITIVE *pBatch;
+ SBYTE *pSrc;
+ const BYTE *xlat_tab;
+ BYTE *cbase;
+ POINT oldOrigin;
+ RECT ClipRect;
+
+ oldOrigin = SetContextOrigin (MAKE_POINT (0, 0));
+ GetContextClipRect (&ClipRect);
+ SetContextClipRect (NULL);
+
+ pBatch = &BatchArray[0];
+ for (i = 0; i < NUM_BATCH_POINTS; ++i, ++pBatch)
+ {
+ SetPrimNextLink (pBatch, i + 1);
+ SetPrimType (pBatch, POINT_PRIM);
+ }
+ SetPrimNextLink (&pBatch[-1], END_OF_LIST);
+
+ PlanDataPtr = &PlanData[
+ pSolarSysState->pOrbitalDesc->data_index & ~PLANET_SHIELDED
+ ];
+ AlgoType = PLANALGO (PlanDataPtr->Type);
+ base = PlanDataPtr->base_elevation;
+ xlatDesc = (const XLAT_DESC *) pSolarSysState->XlatPtr;
+ xlat_tab = (const BYTE *) xlatDesc->xlat_tab;
+ cbase = GetColorMapAddress (pSolarSysState->OrbitalCMap);
+
+ i = NUM_BATCH_POINTS;
+ pBatch = &BatchArray[i];
+ pSrc = pTopoData;
+ for (pt.y = 0; pt.y < h; ++pt.y)
+ {
+ for (pt.x = 0; pt.x < w; ++pt.x, ++pSrc)
+ {
+ BYTE *ctab;
+
+ d = *pSrc;
+ if (AlgoType == GAS_GIANT_ALGO)
+ { // make elevation value non-negative
+ d &= 255;
+ }
+ else
+ {
+ d += base;
+ if (d < 0)
+ d = 0;
+ else if (d > 255)
+ d = 255;
+ }
+
+ --pBatch;
+ pBatch->Object.Point.x = pt.x;
+ pBatch->Object.Point.y = pt.y;
+
+ d = xlat_tab[d] - cbase[0];
+ ctab = (cbase + 2) + d * 3;
+
+ // fixed planet surfaces being too dark
+ // ctab shifts were previously >> 3 .. -Mika
+ SetPrimColor (pBatch, BUILD_COLOR (MAKE_RGB15 (ctab[0] >> 1,
+ ctab[1] >> 1, ctab[2] >> 1), d));
+
+ if (--i == 0)
+ { // flush the batch and start the next one
+ DrawBatch (BatchArray, 0, 0);
+ i = NUM_BATCH_POINTS;
+ pBatch = &BatchArray[i];
+ }
+ }
+ }
+
+ if (i < NUM_BATCH_POINTS)
+ {
+ DrawBatch (BatchArray, i, 0);
+ }
+
+ SetContextClipRect (&ClipRect);
+ SetContextOrigin (oldOrigin);
+ }
+
+ SetContextFGFrame (OldFrame);
+}
+
+static inline void
+P3mult (POINT3 *res, POINT3 *vec, double cnst)
+{
+ res->x = vec->x * cnst;
+ res->y = vec->y * cnst;
+ res->z = vec->z * cnst;
+}
+
+static inline void
+P3sub (POINT3 *res, POINT3 *v1, POINT3 *v2)
+{
+ res->x = v1->x - v2->x;
+ res->y = v1->y - v2->y;
+ res->z = v1->z - v2->z;
+}
+
+static inline double
+P3dot (POINT3 *v1, POINT3 *v2)
+{
+ return (v1->x * v2->x + v1->y * v2->y + v1->z * v2->z);
+}
+
+static inline void
+P3norm (POINT3 *res, POINT3 *vec)
+{
+ double mag = sqrt (P3dot (vec, vec));
+ P3mult (res, vec, 1/mag);
+}
+
+// GenerateSphereMask builds a shadow map for the rotating planet
+// loc indicates the planet's position relative to the sun
+static void
+GenerateSphereMask (POINT loc)
+{
+ POINT pt;
+ POINT3 light;
+ double lrad;
+ const DWORD step = 1 << DIFFUSE_BITS;
+ int y, x;
+
+#define AMBIENT_LIGHT 0.1
+#define LIGHT_Z 1.2
+ // lrad is the distance from the sun to the planet
+ lrad = sqrt (loc.x * loc.x + loc.y * loc.y);
+ // light is the sun's position. the z-coordinate is whatever
+ // looks good
+ light.x = -((double)loc.x);
+ light.y = -((double)loc.y);
+ light.z = LIGHT_Z * lrad;
+ P3norm (&light, &light);
+
+ for (pt.y = 0, y = -RADIUS; pt.y <= TWORADIUS; ++pt.y, y++)
+ {
+ DWORD y_2 = y * y;
+
+ for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, x++)
+ {
+ DWORD x_2 = x * x;
+ DWORD rad_2 = x_2 + y_2;
+ DWORD diff_int = 0;
+ POINT3 norm;
+ double diff;
+
+ if (rad_2 < RADIUS_THRES)
+ {
+ // norm is the sphere's surface normal.
+ norm.x = (double)x;
+ norm.y = (double)y;
+ norm.z = (sqrt (RADIUS_2 - x_2) * sqrt (RADIUS_2 - y_2)) /
+ RADIUS;
+ P3norm (&norm, &norm);
+ // diffuse component is norm dot light
+ diff = P3dot (&norm, &light);
+ // negative diffuse is bad
+ if (diff < 0)
+ diff = 0.0;
+#if 0
+ // Specular is not used in practice and is left here
+ // if someone decides to use it later for some reason.
+ // Specular highlight is only good for perfectly smooth
+ // surfaces, like balls (of which planets are not)
+ // This is the Phong equation
+#define LIGHT_INTENS 0.3
+#define MSHI 2
+ double fb, spec;
+ POINT3 rvec;
+ POINT3 view;
+
+ // always view along the z-axis
+ // ideally use a view point, and have the view change
+ // per pixel, but that is too much effort for now.
+ // the view MUST be normalized!
+ view.x = 0;
+ view.y = 0;
+ view.z = 1.0;
+
+ // specular highlight is the phong equation:
+ // (rvec dot view)^MSHI
+ // where rvec = (2*diff)*norm - light (reflection of light
+ // around norm)
+ P3mult (&rvec, &norm, 2 * diff);
+ P3sub (&rvec, &rvec, &light);
+ fb = P3dot (&rvec, &view);
+ if (fb > 0.0)
+ spec = LIGHT_INTENS * pow (fb, MSHI);
+ else
+ spec = 0;
+#endif
+ // adjust for the ambient light
+ if (diff < AMBIENT_LIGHT)
+ diff = AMBIENT_LIGHT;
+ // Now we antialias the edge of the spere to look nice
+ if (rad_2 > RADIUS_2)
+ {
+ diff *= 1 - (sqrt(rad_2) - RADIUS);
+ if (diff < 0)
+ diff = 0;
+ }
+ // diff_int allows us multiply by a ratio without using
+ // floating-point.
+ diff_int = (DWORD)(diff * step);
+ }
+
+ light_diff[pt.y][pt.x] = diff_int;
+ }
+ }
+}
+
+//create_aa_points creates weighted averages for
+// 4 points around the 'ideal' point at x,y
+// the concept is to compute the weight based on the
+// distance from the integer location points to the ideal point
+static void
+create_aa_points (MAP3D_POINT *ppt, double x, double y)
+{
+ double deltax, deltay, inv_deltax, inv_deltay;
+ COORD nextx, nexty;
+ COUNT i;
+ double d1, d2, d3, d4, m[4];
+
+ if (x < 0)
+ x = 0;
+ else if (x >= SPHERE_SPAN_X)
+ x = SPHERE_SPAN_X - 1;
+ if (y < 0)
+ y = 0;
+ else if (y >= MAP_HEIGHT)
+ y = MAP_HEIGHT - 1;
+
+ // get the integer value of this point
+ ppt->p[0].x = (COORD)x;
+ ppt->p[0].y = (COORD)y;
+ deltax = x - ppt->p[0].x;
+ deltay = y - ppt->p[0].y;
+
+ // if this point doesn't need modificaton, set m[0]=0
+ if (deltax == 0 && deltay == 0)
+ {
+ ppt->m[0] = 0;
+ return;
+ }
+
+ // get the neighboring points surrounding the 'ideal' point
+ if (deltax != 0)
+ nextx = ppt->p[0].x + 1;
+ else
+ nextx = ppt->p[0].x;
+ if (deltay != 0)
+ nexty = ppt->p[0].y + 1;
+ else
+ nexty = ppt->p[0].y;
+ //(x1,y)
+ ppt->p[1].x = nextx;
+ ppt->p[1].y = ppt->p[0].y;
+ //(x,y1)
+ ppt->p[2].x = ppt->p[0].x;
+ ppt->p[2].y = nexty;
+ //(x1y1)
+ ppt->p[3].x = nextx;
+ ppt->p[3].y = nexty;
+ //the square 1x1, so opposite poinnts are at 1-delta
+ inv_deltax = 1.0 - fabs (deltax);
+ inv_deltax *= inv_deltax;
+ inv_deltay = 1.0 - fabs (deltay);
+ inv_deltay *= inv_deltay;
+ deltax *= deltax;
+ deltay *= deltay;
+ //d1-d4 contain the distances from the poinnts to the ideal point
+ d1 = sqrt (deltax + deltay);
+ d2 = sqrt (inv_deltax + deltay);
+ d3 = sqrt (deltax + inv_deltay);
+ d4 = sqrt (inv_deltax + inv_deltay);
+ //compute the weights. the sum(ppt->m[])=65536
+ m[0] = 1 / (1 + d1 * (1 / d2 + 1 / d3 + 1 / d4));
+ m[1] = m[0] * d1 / d2;
+ m[2] = m[0] * d1 / d3;
+ m[3] = m[0] * d1 / d4;
+
+ for (i = 0; i < 4; i++)
+ ppt->m[i] = (DWORD)(m[i] * (1 << AA_WEIGHT_BITS) + 0.5);
+}
+
+static inline BYTE
+get_color_channel (Color c, int channel)
+{
+ switch (channel)
+ {
+ case 0:
+ return c.r;
+ case 1:
+ return c.g;
+ case 2:
+ return c.b;
+ default:
+ return 0;
+ }
+}
+
+// Creates either a red, green, or blue value by
+// computing the weighted averages of the 4 points in p
+static BYTE
+get_avg_channel (Color p[4], DWORD mult[4], int channel)
+{
+ COUNT j;
+ DWORD ci = 0;
+
+ //sum(mult[])==65536
+ //c is the red/green/blue value of this pixel
+ for (j = 0; j < 4; j++)
+ {
+ BYTE c = get_color_channel (p[j], channel);
+ ci += c * mult[j];
+ }
+ ci >>= AA_WEIGHT_BITS;
+ //check for overflow
+ if (ci > 255)
+ ci = 255;
+
+ return ((UBYTE)ci);
+}
+
+// CreateSphereTiltMap creates 'map_rotate' to map the topo data
+// for a tilted planet. It also does the sphere->plane mapping
+static void
+CreateSphereTiltMap (int angle)
+{
+ int x, y;
+ const double multx = ((double)SPHERE_SPAN_X / M_PI);
+ const double multy = ((double)MAP_HEIGHT / M_PI);
+ const double xadj = ((double)SPHERE_SPAN_X / 2.0);
+
+ for (y = -RADIUS; y <= RADIUS; y++)
+ {
+ int y_2 = y * y;
+
+ for (x = -RADIUS; x <= RADIUS; x++)
+ {
+ double dx, dy, newx, newy;
+ double da, rad, rad_2;
+ double xa, ya;
+ MAP3D_POINT *ppt = &map_rotate[y + RADIUS][x + RADIUS];
+
+ rad_2 = x * x + y_2;
+
+ if (rad_2 >= RADIUS_THRES)
+ { // pixel won't be present
+ ppt->p[0].x = x + RADIUS;
+ ppt->p[0].y = y + RADIUS;
+ ppt->m[0] = 0;
+
+ continue;
+ }
+
+ rad = sqrt (rad_2);
+ // antialiasing goes beyond the actual radius
+ if (rad >= RADIUS)
+ rad = (double)RADIUS - 0.1;
+
+ da = atan2 ((double)y, (double)x);
+ // compute the planet-tilt
+ da += M_DEG2RAD * angle;
+ dx = rad * cos (da);
+ dy = rad * sin (da);
+
+ // Map the sphere onto a plane
+ xa = acos (-dx / RADIUS);
+ ya = acos (-dy / RADIUS);
+ newx = multx * xa;
+ newy = multy * ya;
+ // Adjust for vertical curvature
+ if (ya <= 0.05 || ya >= 3.1 /* almost PI */)
+ newx = xadj; // exact centerline
+ else
+ newx = xadj + ((newx - xadj) / sin (ya));
+
+ create_aa_points (ppt, newx, newy);
+ }
+ }
+}
+
+//CreateShieldMask
+// The shield is created in two parts. This routine creates the Halo.
+// The red tint of the planet is currently applied in RenderPlanetSphere
+// This was done because the shield glows and needs to modify how the planet
+// gets lit. Currently, the planet area is transparent in the mask made by
+// this routine, but a filter can be applied if desired too.
+
+// HALO rim size
+#define SHIELD_HALO 7
+#define SHIELD_RADIUS (RADIUS + SHIELD_HALO)
+#define SHIELD_DIAM ((SHIELD_RADIUS << 1) + 1)
+#define SHIELD_RADIUS_2 (SHIELD_RADIUS * SHIELD_RADIUS)
+#define SHIELD_RADIUS_THRES ((SHIELD_RADIUS + 1) * (SHIELD_RADIUS + 1))
+#define SHIELD_HALO_GLOW (SHIELD_GLOW_COMP + SHIELD_REFLECT_COMP)
+#define SHIELD_HALO_GLOW_MIN (SHIELD_HALO_GLOW >> 2)
+
+static FRAME
+CreateShieldMask (void)
+{
+ Color clear;
+ Color *pix;
+ int x, y;
+ FRAME ShieldFrame;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ ShieldFrame = CaptureDrawable (
+ CreateDrawable (WANT_PIXMAP | WANT_ALPHA,
+ SHIELD_DIAM, SHIELD_DIAM, 1));
+
+ pix = Orbit->ScratchArray;
+ // This is 100% transparent.
+ clear = BUILD_COLOR_RGBA (0, 0, 0, 0);
+
+ for (y = -SHIELD_RADIUS; y <= SHIELD_RADIUS; y++)
+ {
+ for (x = -SHIELD_RADIUS; x <= SHIELD_RADIUS; ++x, ++pix)
+ {
+ int rad_2 = x * x + y * y;
+ // This is a non-transparent red for the halo
+ int red = SHIELD_HALO_GLOW;
+ int alpha = 255;
+ double rad;
+
+ if (rad_2 >= SHIELD_RADIUS_THRES)
+ { // outside all bounds
+ *pix = clear;
+ continue;
+ }
+ // Inside the halo
+ if (rad_2 <= RADIUS_2)
+ { // planet's pixels, ours transparent
+ *pix = clear;
+ continue;
+ }
+
+ // The halo itself
+ rad = sqrt (rad_2);
+
+ if (rad <= RADIUS + 0.8)
+ { // pixels common between the shield and planet
+ // do antialiasing using alpha
+ alpha = (int) (red * (rad - RADIUS));
+ red = 255;
+ }
+ else
+ { // shield pixels
+ red -= (int) ((red - SHIELD_HALO_GLOW_MIN) * (rad - RADIUS)
+ / SHIELD_HALO);
+ if (red < 0)
+ red = 0;
+ }
+
+ *pix = BUILD_COLOR_RGBA (red, 0, 0, alpha);
+ }
+ }
+
+ WriteFramePixelColors (ShieldFrame, Orbit->ScratchArray,
+ SHIELD_DIAM, SHIELD_DIAM);
+ SetFrameHot (ShieldFrame, MAKE_HOT_SPOT (SHIELD_RADIUS + 1,
+ SHIELD_RADIUS + 1));
+
+ return ShieldFrame;
+}
+
+// SetShieldThrobEffect adjusts the red levels in the shield glow graphic
+// the throbbing cycle is tied to the planet rotation cycle
+#define SHIELD_THROBS 12
+ // throb cycles per revolution
+#define THROB_CYCLE ((MAP_WIDTH << 8) / SHIELD_THROBS)
+#define THROB_HALF_CYCLE (THROB_CYCLE >> 1)
+
+#define THROB_MAX_LEVEL 256
+#define THROB_MIN_LEVEL 100
+#define THROB_D_LEVEL (THROB_MAX_LEVEL - THROB_MIN_LEVEL)
+
+static inline int
+shield_level (int offset)
+{
+ int level;
+
+ offset = (offset << 8) % THROB_CYCLE;
+ level = abs (offset - THROB_HALF_CYCLE);
+ level = THROB_MIN_LEVEL + level * THROB_D_LEVEL / THROB_HALF_CYCLE;
+
+ return level;
+}
+
+// See description above
+// offset is effectively the angle of rotation around the planet's axis
+void
+SetShieldThrobEffect (FRAME ShieldFrame, int offset, FRAME ThrobFrame)
+{
+ int i;
+ int width, height;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ Color *pix;
+ int level;
+
+ level = shield_level (offset);
+
+ width = GetFrameWidth (ShieldFrame);
+ height = GetFrameHeight (ShieldFrame);
+ ReadFramePixelColors (ShieldFrame, Orbit->ScratchArray, width, height);
+
+ for (i = 0, pix = Orbit->ScratchArray; i < width * height; ++i, ++pix)
+ {
+ Color p = *pix;
+
+ if (p.a == 255)
+ { // adjust color data for full-alpha pixels
+ p.r = p.r * level / THROB_MAX_LEVEL;
+ p.g = p.g * level / THROB_MAX_LEVEL;
+ p.b = p.b * level / THROB_MAX_LEVEL;
+ }
+ else if (p.a > 0)
+ { // adjust alpha for translucent pixels
+ p.a = p.a * level / THROB_MAX_LEVEL;
+ }
+
+ *pix = p;
+ }
+
+ WriteFramePixelColors (ThrobFrame, Orbit->ScratchArray, width, height);
+ SetFrameHot (ThrobFrame, GetFrameHot (ShieldFrame));
+}
+
+// Apply the shield to the topo image
+static void
+ApplyShieldTint (void)
+{
+ DrawMode mode, oldMode;
+ FRAME oldFrame;
+ Color tint;
+ RECT r;
+
+ // TopoFrame will be permanently changed
+ oldFrame = SetContextFGFrame (pSolarSysState->TopoFrame);
+ SetContextClipRect (NULL);
+ GetContextClipRect (&r);
+
+ tint = BUILD_COLOR_RGBA (0xff, 0x00, 0x00, 0xff);
+#ifdef USE_ALPHA_SHIELD
+ mode = MAKE_DRAW_MODE (DRAW_ALPHA, 150);
+#else
+ mode = MAKE_DRAW_MODE (DRAW_ADDITIVE, DRAW_FACTOR_1);
+#endif
+ oldMode = SetContextDrawMode (mode);
+ SetContextForeGroundColor (tint);
+ DrawFilledRectangle (&r);
+ SetContextDrawMode (oldMode);
+ SetContextFGFrame (oldFrame);
+}
+
+static inline UBYTE
+calc_map_light (UBYTE val, DWORD dif, int lvf)
+{
+ int i;
+
+ // apply diffusion
+ i = (dif * val) >> DIFFUSE_BITS;
+ // apply light variance for 3d lighting effect
+ i += (lvf * val) >> 7;
+
+ if (i < 0)
+ i = 0;
+ else if (i > 255)
+ i = 255;
+
+ return ((UBYTE)i);
+}
+
+static inline Color
+get_map_pixel (Color *pixels, int x, int y)
+{
+ return pixels[y * (MAP_WIDTH + SPHERE_SPAN_X) + x];
+}
+
+static inline int
+get_map_elev (SBYTE *elevs, int x, int y, int offset)
+{
+ return elevs[y * MAP_WIDTH + (offset + x) % MAP_WIDTH];
+}
+
+// RenderPlanetSphere builds a frame for the rotating planet view
+// offset is effectively the angle of rotation around the planet's axis
+// We use the SDL routines to directly write to the SDL_Surface to improve performance
+void
+RenderPlanetSphere (FRAME MaskFrame, int offset, BOOLEAN doThrob)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ POINT pt;
+ Color *pix;
+ Color clear;
+ int x, y;
+ Color *pixels;
+ SBYTE *elevs;
+ int shLevel;
+
+#if PROFILE_ROTATION
+ static clock_t t = 0;
+ static int frames_done = 1;
+ clock_t t1;
+ t1 = clock ();
+#endif
+
+
+ shLevel = shield_level (offset);
+
+ pix = Orbit->ScratchArray;
+ clear = BUILD_COLOR_RGBA (0, 0, 0, 0);
+ pixels = Orbit->TopoColors + offset;
+ elevs = Orbit->lpTopoData;
+
+ for (pt.y = 0, y = -RADIUS; pt.y <= TWORADIUS; ++pt.y, ++y)
+ {
+ for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, ++x, ++pix)
+ {
+ Color c;
+ DWORD diffus = light_diff[pt.y][pt.x];
+ int i;
+ MAP3D_POINT *ppt = &map_rotate[pt.y][pt.x];
+ int lvf; // light variance factor
+
+ if (diffus == 0)
+ { // full diffusion
+ *pix = clear;
+ continue;
+ }
+
+ // get pixel from topo map and factor from light variance map
+ if (ppt->m[0] == 0)
+ { // exact pixel from the topo map
+ c = get_map_pixel (pixels, ppt->p[0].x, ppt->p[0].y);
+ lvf = get_map_elev (elevs, ppt->p[0].x, ppt->p[0].y, offset);
+ }
+ else
+ { // fractional pixel -- blend from 4
+ Color p[4];
+ int lvsum;
+
+ // compute 'ideal' pixel
+ for (i = 0; i < 4; i++)
+ p[i] = get_map_pixel (pixels, ppt->p[i].x, ppt->p[i].y);
+
+ c.r = get_avg_channel (p, ppt->m, 0);
+ c.g = get_avg_channel (p, ppt->m, 1);
+ c.b = get_avg_channel (p, ppt->m, 2);
+
+ // compute 'ideal' light variance
+ for (i = 0, lvsum = 0; i < 4; i++)
+ lvsum += get_map_elev (elevs, ppt->p[0].x, ppt->p[0].y,
+ offset) * ppt->m[i];
+ lvf = lvsum >> AA_WEIGHT_BITS;
+ }
+
+ // Apply the lighting model. This also bounds the sphere
+ // to make it circular.
+ if (pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ {
+ int r;
+
+ // add lite red filter (3/4) component
+ c.g = (c.g >> 1) + (c.g >> 2);
+ c.b = (c.b >> 1) + (c.b >> 2);
+
+ c.r = calc_map_light (c.r, diffus, lvf);
+ c.g = calc_map_light (c.g, diffus, lvf);
+ c.b = calc_map_light (c.b, diffus, lvf);
+
+ // The shield is glow + reflect (+ filter for others)
+ r = calc_map_light (SHIELD_REFLECT_COMP, diffus, 0);
+ r += SHIELD_GLOW_COMP;
+
+ if (doThrob)
+ { // adjust red level for throbbing shield
+ r = r * shLevel / THROB_MAX_LEVEL;
+ }
+
+ r += c.r;
+ if (r > 255)
+ r = 255;
+ c.r = r;
+ }
+ else
+ {
+ c.r = calc_map_light (c.r, diffus, lvf);
+ c.g = calc_map_light (c.g, diffus, lvf);
+ c.b = calc_map_light (c.b, diffus, lvf);
+ }
+
+ c.a = 0xff;
+ *pix = c;
+ }
+ }
+
+ WriteFramePixelColors (MaskFrame, Orbit->ScratchArray, DIAMETER, DIAMETER);
+ SetFrameHot (MaskFrame, MAKE_HOT_SPOT (RADIUS + 1, RADIUS + 1));
+
+#if PROFILE_ROTATION
+ t += clock() - t1;
+ if (frames_done == MAP_WIDTH)
+ {
+ log_add (log_Debug, "Rotation frames/sec: %d/%ld(msec)=%f",
+ frames_done,
+ (long int) (((double)t / CLOCKS_PER_SEC) * 1000.0 + 0.5),
+ frames_done / ((double)t / CLOCKS_PER_SEC + 0.5));
+ frames_done = 1;
+ t = clock () - t1;
+ }
+ else
+ frames_done++;
+#endif
+}
+
+
+#define RANGE_SHIFT 6
+
+static void
+DitherMap (SBYTE *DepthArray)
+{
+#define DITHER_VARIANCE (1 << (RANGE_SHIFT - 3))
+ COUNT i;
+ SBYTE *elev;
+ DWORD rand_val = 0;
+
+ for (i = 0, elev = DepthArray; i < MAP_WIDTH * MAP_HEIGHT; ++i, ++elev)
+ {
+ // Use up the random value byte by byte
+ if ((i & 3) == 0)
+ rand_val = RandomContext_Random (SysGenRNG);
+ else
+ rand_val >>= 8;
+
+ // Bring the elevation point up or down
+ *elev += DITHER_VARIANCE / 2 - (rand_val & (DITHER_VARIANCE - 1));
+ }
+}
+
+static void
+MakeCrater (RECT *pRect, SBYTE *DepthArray, SIZE rim_delta, SIZE
+ crater_delta, BOOLEAN SetDepth)
+{
+ COORD x, y, lf_x, rt_x;
+ SIZE A, B;
+ long Asquared, TwoAsquared,
+ Bsquared, TwoBsquared;
+ long d, dx, dy;
+ COUNT TopIndex, BotIndex, rim_pixels;
+
+ A = pRect->extent.width >> 1;
+ B = pRect->extent.height >> 1;
+
+ x = 0;
+ y = B;
+
+ Asquared = (DWORD)A * A;
+ TwoAsquared = Asquared << 1;
+ Bsquared = (DWORD)B * B;
+ TwoBsquared = Bsquared << 1;
+
+ dx = 0;
+ dy = TwoAsquared * B;
+ d = Bsquared - (dy >> 1) + (Asquared >> 2);
+
+ A += pRect->corner.x;
+ B += pRect->corner.y;
+ TopIndex = (B - y) * MAP_WIDTH;
+ BotIndex = (B + y) * MAP_WIDTH;
+ rim_pixels = 1;
+ while (dx < dy)
+ {
+ if (d > 0)
+ {
+ lf_x = A - x;
+ rt_x = A + x;
+ if (SetDepth)
+ {
+ memset (&DepthArray[TopIndex + lf_x], 0, rt_x - lf_x + 1);
+ memset (&DepthArray[BotIndex + lf_x], 0, rt_x - lf_x + 1);
+ }
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ rim_pixels = 0;
+ }
+ else
+ {
+ do
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ if (lf_x != rt_x)
+ {
+ DepthArray[TopIndex + rt_x] += rim_delta;
+ DepthArray[BotIndex + rt_x] += rim_delta;
+ }
+ ++lf_x;
+ --rt_x;
+ } while (--rim_pixels);
+
+ while (lf_x < rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ DepthArray[TopIndex + rt_x] += crater_delta;
+ DepthArray[BotIndex + rt_x] += crater_delta;
+ ++lf_x;
+ --rt_x;
+ }
+
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ }
+ }
+
+ --y;
+ TopIndex += MAP_WIDTH;
+ BotIndex -= MAP_WIDTH;
+ dy -= TwoAsquared;
+ d -= dy;
+ }
+
+ ++rim_pixels;
+ ++x;
+ dx += TwoBsquared;
+ d += Bsquared + dx;
+ }
+
+ d += ((((Asquared - Bsquared) * 3) >> 1) - (dx + dy)) >> 1;
+
+ while (y > 0)
+ {
+ lf_x = A - x;
+ rt_x = A + x;
+ if (SetDepth)
+ {
+ memset (&DepthArray[TopIndex + lf_x], 0, rt_x - lf_x + 1);
+ memset (&DepthArray[BotIndex + lf_x], 0, rt_x - lf_x + 1);
+ }
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ }
+ else
+ {
+ do
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ if (lf_x != rt_x)
+ {
+ DepthArray[TopIndex + rt_x] += rim_delta;
+ DepthArray[BotIndex + rt_x] += rim_delta;
+ }
+ ++lf_x;
+ --rt_x;
+ } while (--rim_pixels);
+
+ while (lf_x < rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ DepthArray[TopIndex + rt_x] += crater_delta;
+ DepthArray[BotIndex + rt_x] += crater_delta;
+ ++lf_x;
+ --rt_x;
+ }
+
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ }
+ }
+
+ if (d < 0)
+ {
+ ++x;
+ dx += TwoBsquared;
+ d += dx;
+ }
+
+ rim_pixels = 1;
+ --y;
+ TopIndex += MAP_WIDTH;
+ BotIndex -= MAP_WIDTH;
+ dy -= TwoAsquared;
+ d += Asquared - dy;
+ }
+
+ lf_x = A - x;
+ rt_x = A + x;
+ if (SetDepth)
+ memset (&DepthArray[TopIndex + lf_x], 0, rt_x - lf_x + 1);
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ }
+ else
+ {
+ do
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ if (lf_x != rt_x)
+ DepthArray[TopIndex + rt_x] += rim_delta;
+ ++lf_x;
+ --rt_x;
+ } while (--rim_pixels);
+
+ while (lf_x < rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[TopIndex + rt_x] += crater_delta;
+ ++lf_x;
+ --rt_x;
+ }
+
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ }
+ }
+}
+
+#define NUM_BAND_COLORS 4
+
+static void
+MakeStorms (COUNT storm_count, SBYTE *DepthArray)
+{
+#define MAX_STORMS 8
+ COUNT i;
+ RECT storm_r[MAX_STORMS];
+ RECT *pstorm_r;
+
+ pstorm_r = &storm_r[i = storm_count];
+ while (i--)
+ {
+ BOOLEAN intersect;
+ DWORD rand_val;
+ UWORD loword, hiword;
+ SIZE band_delta;
+
+ --pstorm_r;
+ do
+ {
+ COUNT j;
+
+ intersect = FALSE;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loword = LOWORD (rand_val);
+ hiword = HIWORD (rand_val);
+ switch (HIBYTE (hiword) & 31)
+ {
+ case 0:
+ pstorm_r->extent.height =
+ (LOBYTE (hiword) % (MAP_HEIGHT >> 2))
+ + (MAP_HEIGHT >> 2);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ pstorm_r->extent.height =
+ (LOBYTE (hiword) % (MAP_HEIGHT >> 3))
+ + (MAP_HEIGHT >> 3);
+ break;
+ default:
+ pstorm_r->extent.height =
+ (LOBYTE (hiword) % (MAP_HEIGHT >> 4))
+ + 4;
+ break;
+ }
+
+ if (pstorm_r->extent.height <= 4)
+ pstorm_r->extent.height += 4;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loword = LOWORD (rand_val);
+ hiword = HIWORD (rand_val);
+
+ pstorm_r->extent.width = pstorm_r->extent.height
+ + (LOBYTE (loword) % pstorm_r->extent.height);
+
+ pstorm_r->corner.x = HIBYTE (loword)
+ % (MAP_WIDTH - pstorm_r->extent.width);
+ pstorm_r->corner.y = LOBYTE (loword)
+ % (MAP_HEIGHT - pstorm_r->extent.height);
+
+ for (j = i + 1; j < storm_count; ++j)
+ {
+ COORD x, y;
+ SIZE w, h;
+
+ x = storm_r[j].corner.x - pstorm_r->corner.x;
+ y = storm_r[j].corner.y - pstorm_r->corner.y;
+ w = x + storm_r[j].extent.width + 4;
+ h = y + storm_r[j].extent.height + 4;
+ intersect = (BOOLEAN) (w > 0 && h > 0
+ && x < pstorm_r->extent.width + 4
+ && y < pstorm_r->extent.height + 4);
+ if (intersect)
+ break;
+ }
+
+ } while (intersect);
+
+ MakeCrater (pstorm_r, DepthArray, 6, 6, FALSE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+
+ band_delta = HIBYTE (loword) & ((3 << RANGE_SHIFT) + 20);
+
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+
+ band_delta += 2;
+ if (pstorm_r->extent.width > 2 && pstorm_r->extent.height > 2)
+ {
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+ }
+
+ band_delta += 2;
+ if (pstorm_r->extent.width > 2 && pstorm_r->extent.height > 2)
+ {
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+ }
+
+ band_delta += 4;
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ }
+}
+
+static void
+MakeGasGiant (COUNT num_bands, SBYTE *DepthArray, RECT *pRect, SIZE
+ depth_delta)
+{
+ COORD last_y, next_y;
+ SIZE band_error, band_bump, band_delta;
+ COUNT i, j, band_height;
+ SBYTE *lpDst;
+ UWORD loword, hiword;
+ DWORD rand_val;
+
+ band_height = pRect->extent.height / num_bands;
+ band_bump = pRect->extent.height % num_bands;
+ band_error = num_bands >> 1;
+ lpDst = DepthArray;
+
+ band_delta = ((LOWORD (RandomContext_Random (SysGenRNG))
+ & (NUM_BAND_COLORS - 1)) << RANGE_SHIFT)
+ + (1 << (RANGE_SHIFT - 1));
+ last_y = next_y = 0;
+ for (i = num_bands; i > 0; --i)
+ {
+ COORD cur_y;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loword = LOWORD (rand_val);
+ hiword = HIWORD (rand_val);
+
+ next_y += band_height;
+ if ((band_error -= band_bump) < 0)
+ {
+ ++next_y;
+ band_error += num_bands;
+ }
+ if (i == 1)
+ cur_y = pRect->extent.height;
+ else
+ {
+ RECT r;
+
+ cur_y = next_y
+ + ((band_height - 2) >> 1)
+ - ((LOBYTE (hiword) % (band_height - 2)) + 1);
+ r.corner.x = r.corner.y = 0;
+ r.extent.width = pRect->extent.width;
+ r.extent.height = 5;
+ DeltaTopography (50,
+ &DepthArray[(cur_y - 2) * r.extent.width],
+ &r, depth_delta);
+ }
+
+ for (j = cur_y - last_y; j > 0; --j)
+ {
+ COUNT k;
+
+ for (k = pRect->extent.width; k > 0; --k)
+ *lpDst++ += band_delta;
+ }
+
+ last_y = cur_y;
+ band_delta = (band_delta
+ + ((((LOBYTE (loword) & 1) << 1) - 1) << RANGE_SHIFT))
+ & (((1 << RANGE_SHIFT) * NUM_BAND_COLORS) - 1);
+ }
+
+ MakeStorms (4 + (RandomContext_Random (SysGenRNG) & 3) + 1, DepthArray);
+
+ DitherMap (DepthArray);
+}
+
+static void
+ValidateMap (SBYTE *DepthArray)
+{
+ BYTE state;
+ BYTE pixel_count[2], lb[2];
+ SBYTE last_byte;
+ COUNT i;
+ SBYTE *lpDst;
+
+ i = MAP_WIDTH - 1;
+ lpDst = DepthArray;
+ last_byte = *lpDst++;
+ state = pixel_count[0] = pixel_count[1] = 0;
+ do
+ {
+ if (pixel_count[state]++ == 0)
+ lb[state] = last_byte;
+
+ if (last_byte > *lpDst)
+ {
+ if (last_byte - *lpDst > 128)
+ state ^= 1;
+ }
+ else
+ {
+ if (*lpDst - last_byte > 128)
+ state ^= 1;
+ }
+ last_byte = *lpDst++;
+ } while (--i);
+
+ i = MAP_WIDTH * MAP_HEIGHT;
+ lpDst = DepthArray;
+ if (pixel_count[0] > pixel_count[1])
+ last_byte = lb[0];
+ else
+ last_byte = lb[1];
+ do
+ {
+ if (last_byte > *lpDst)
+ {
+ if (last_byte - *lpDst > 128)
+ *lpDst = last_byte;
+ }
+ else
+ {
+ if (*lpDst - last_byte > 128)
+ *lpDst = last_byte;
+ }
+ last_byte = *lpDst++;
+ } while (--i);
+}
+
+static void
+planet_orbit_init (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ Orbit->SphereFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP | WANT_ALPHA, DIAMETER, DIAMETER, 2));
+ Orbit->TintFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, MAP_WIDTH, MAP_HEIGHT, 1));
+ Orbit->ObjectFrame = 0;
+ Orbit->WorkFrame = 0;
+ Orbit->lpTopoData = HCalloc (MAP_WIDTH * MAP_HEIGHT);
+ Orbit->TopoZoomFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, MAP_WIDTH << 2, MAP_HEIGHT << 2, 1));
+ Orbit->TopoColors = HMalloc (sizeof (Orbit->TopoColors[0])
+ * (MAP_HEIGHT * (MAP_WIDTH + SPHERE_SPAN_X)));
+ // always allocate the scratch array to largest needed size
+ Orbit->ScratchArray = HMalloc (sizeof (Orbit->ScratchArray[0])
+ * (SHIELD_DIAM) * (SHIELD_DIAM));
+}
+
+static unsigned
+frandom (void)
+{
+ static unsigned seed = 0x12345678;
+
+ if (seed == 0)
+ seed = 15807;
+ seed = (seed >> 4) * 227;
+
+ return seed;
+}
+
+static inline int
+TopoVarianceFactor (int step, int allowed, int min)
+{
+#define SCALE_SHIFT 8
+ return ((abs(step) * allowed) >> SCALE_SHIFT) + min;
+}
+
+static inline int
+TopoVarianceCalc (int factor)
+{
+ if (factor == 0)
+ return 0;
+ else
+ return (frandom () % factor) - (factor >> 1);
+}
+
+static void
+TopoScale4x (SBYTE *pDstTopo, SBYTE *pSrcTopo, int num_faults, int fault_var)
+{
+ // Interpolate the topographical data by connecting the elevations
+ // to their nearest neighboors using straight lines (in random
+ // direction) with random variance factors defined by
+ // num_faults and fault_var args
+#define AVG_VARIANCE 250
+ int x, y;
+ const int w = MAP_WIDTH, h = MAP_HEIGHT;
+ const int spitch = MAP_WIDTH, dpitch = MAP_WIDTH * 4;
+ SBYTE *pSrc;
+ SBYTE *pDst;
+ int* prevrow;
+ int* prow;
+ int elev[5][5];
+ int var_allow, var_min;
+ static const struct line_def_t
+ {
+ int x0, y0, x1, y1;
+ int dx, dy;
+ }
+ fill_lines[4][6] =
+ {
+ { // diag set 0
+ { 0, 2, 2, 0, 1, -1},
+ { 0, 3, 3, 0, 1, -1},
+ { 0, 4, 4, 0, 1, -1},
+ { 1, 4, 4, 1, 1, -1},
+ { 2, 4, 4, 2, 1, -1},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ { // diag set 1
+ { 0, 2, 2, 4, 1, 1},
+ { 0, 1, 3, 4, 1, 1},
+ { 0, 0, 4, 4, 1, 1},
+ { 1, 0, 4, 3, 1, 1},
+ { 2, 0, 4, 2, 1, 1},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ { // horizontal
+ { 0, 1, 4, 1, 1, 0},
+ { 0, 2, 4, 2, 1, 0},
+ { 0, 3, 4, 3, 1, 0},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ { // vertical
+ { 1, 0, 1, 4, 0, 1},
+ { 2, 0, 2, 4, 0, 1},
+ { 3, 0, 3, 4, 0, 1},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ };
+
+ prevrow = (int *) HMalloc ((MAP_WIDTH * 4 + 1) * sizeof(prevrow[0]));
+
+ var_allow = (num_faults << SCALE_SHIFT) / AVG_VARIANCE;
+ var_min = fault_var << SCALE_SHIFT;
+
+ //memset (pDstTopo, 0, MAP_WIDTH * MAP_HEIGHT * 16);
+
+ // init the first row in advance
+ pSrc = pSrcTopo;
+ prow = prevrow;
+#define STEP_RANGE (4 - 1)
+ prow[0] = ((int)pSrc[0]) << SCALE_SHIFT;;
+ for (x = 0; x < w; ++x, ++pSrc, prow += 4)
+ {
+ int x2;
+ int val, step, rndfact;
+
+ // next point in row
+ if (x < w - 1)
+ // one right
+ prow[4] = ((int)pSrc[1]) << SCALE_SHIFT;
+ else
+ // wrap around
+ prow[4] = ((int)pSrc[1 - spitch]) << SCALE_SHIFT;
+
+ // compute elevations between 2 points
+ val = prow[0];
+ step = (prow[4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (x2 = 1, val += step; x2 < 4; ++x2, val += step)
+ prow[x2] = val + TopoVarianceCalc (rndfact);
+ }
+
+ pSrc = pSrcTopo;
+ pDst = pDstTopo;
+ for (y = 0; y < h; ++y, pDst += dpitch * 3)
+ {
+ int x2, y2;
+ SBYTE *p;
+ int val, step, rndfact;
+ const struct line_def_t* pld;
+
+ prow = prevrow;
+ // prime the first interpolated column
+ elev[4][0] = prow[0];
+ if (y < h - 1)
+ elev[4][4] = ((int)pSrc[spitch]) << SCALE_SHIFT;
+ else
+ elev[4][4] = elev[4][0];
+ // compute elevations for interpolated column
+ val = elev[4][0];
+ step = (elev[4][4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (y2 = 1, val += step; y2 < 4; ++y2, val += step)
+ elev[4][y2] = val + TopoVarianceCalc (rndfact);
+
+ for (x = 0; x < w; ++x, ++pSrc, pDst += 4, prow += 4)
+ {
+ // recall the first interpolated row from prevrow
+ for (x2 = 0; x2 <= 4; ++x2)
+ elev[x2][0] = prow[x2];
+ // recall the first interpolated column
+ for (y2 = 1; y2 <= 4; ++y2)
+ elev[0][y2] = elev[4][y2];
+
+ if (y < h - 1)
+ {
+ if (x < w - 1)
+ // one right, one down
+ elev[4][4] = ((int)pSrc[1 + spitch]) << SCALE_SHIFT;
+ else
+ // wrap around, one down
+ elev[4][4] = ((int)pSrc[1]) << SCALE_SHIFT;
+ }
+ else
+ {
+ elev[4][4] = elev[4][0];
+ }
+
+ // compute elevations for the rest of square borders first
+ val = elev[0][4];
+ step = (elev[4][4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (x2 = 1, val += step; x2 < 4; ++x2, val += step)
+ elev[x2][4] = val + TopoVarianceCalc (rndfact);
+
+ val = elev[4][0];
+ step = (elev[4][4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (y2 = 1, val += step; y2 < 4; ++y2, val += step)
+ elev[4][y2] = val + TopoVarianceCalc (rndfact);
+
+ // fill in the rest by connecting opposing elevations
+ // some randomness to determine which elevations to connect
+ for (pld = fill_lines[frandom () & 3]; pld->x0 >= 0; ++pld)
+ {
+ int num_steps;
+
+ x2 = pld->x0;
+ y2 = pld->y0;
+ val = elev[x2][y2];
+ num_steps = pld->x1 - pld->x0;
+ if (num_steps == 0)
+ num_steps = pld->y1 - pld->y0;
+ step = (elev[pld->x1][pld->y1] - val) / num_steps;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+
+ for (x2 += pld->dx, y2 += pld->dy, val += step;
+ x2 != pld->x1 || y2 != pld->y1;
+ x2 += pld->dx, y2 += pld->dy, val += step)
+ {
+ elev[x2][y2] = val + TopoVarianceCalc (rndfact);
+ }
+ }
+
+ // output the interpolated topography
+ for (y2 = 0; y2 < 4; ++y2)
+ {
+ p = pDst + y2 * dpitch;
+ for (x2 = 0; x2 < 4; ++x2, ++p)
+ {
+ int e = elev[x2][y2] >> SCALE_SHIFT;
+ if (e > 127)
+ e = 127;
+ else if (e < -128)
+ e = -128;
+ *p = (SBYTE)e;
+ }
+ }
+
+ // save last interpolated row to prevrow for later
+ for (x2 = 0; x2 < 4; ++x2)
+ prow[x2] = elev[x2][4];
+ }
+ // save last row point
+ prow[0] = elev[4][4];
+ }
+
+ HFree (prevrow);
+}
+
+
+// GenerateLightMap produces a surface light variance map for the
+// rotating planet by, first, transforming absolute elevation data
+// into normalized relative and then applying a weighted
+// average-median of surrounding points
+// Lots of pure Voodoo here ;)
+// the goal is a 3D illusion, not mathematically correct lighting
+
+#define LMAP_AVG_BLOCK ((MAP_HEIGHT + 4) / 5)
+#define LMAP_MAX_DIST ((LMAP_AVG_BLOCK + 1) >> 1)
+#define LMAP_WEIGHT_THRES (LMAP_MAX_DIST * 2 / 3)
+
+typedef struct
+{
+ int min;
+ int max;
+ int avg;
+
+} elev_block_t;
+
+static inline void
+get_vblock_avg (elev_block_t *pblk, SBYTE *pTopo, int x, int y)
+{
+ SBYTE *elev = pTopo;
+ int y0, y1, i;
+ int min = 127, max = -127;
+ int avg = 0, total_weight = 0;
+
+ // surface wraps around along x
+ x = (x + MAP_WIDTH) % MAP_WIDTH;
+
+ y0 = y - LMAP_MAX_DIST;
+ y1 = y + LMAP_MAX_DIST;
+ if (y0 < 0)
+ y0 = 0;
+ if (y1 > MAP_HEIGHT)
+ y1 = MAP_HEIGHT;
+
+ elev = pTopo + y0 * MAP_WIDTH + x;
+ for (i = y0; i < y1; ++i, elev += MAP_WIDTH)
+ {
+ int delta = abs (i - y);
+ int weight = 255; // full weight
+ int v = *elev;
+
+ if (delta >= LMAP_WEIGHT_THRES)
+ { // too far -- progressively reduced weight
+ weight = weight * (LMAP_MAX_DIST - delta + 1)
+ / (LMAP_MAX_DIST - LMAP_WEIGHT_THRES + 2);
+ }
+
+ if (v > max)
+ max = v;
+ if (v < min)
+ min = v;
+ avg += v * weight;
+ total_weight += weight;
+ }
+ avg /= total_weight;
+
+ pblk->min = min;
+ pblk->max = max;
+ pblk->avg = avg / (y1 - y0);
+}
+
+// See description above
+static void
+GenerateLightMap (SBYTE *pTopo, int w, int h)
+{
+#define LMAP_BLOCKS (2 * LMAP_MAX_DIST + 1)
+ int x, y;
+ elev_block_t vblocks[LMAP_BLOCKS];
+ // we use a running block average to reduce the amount of work
+ // where a block is a vertical line of map points
+ SBYTE *elev;
+ int min, max, med;
+ int sfact, spread;
+
+ // normalize the topo data
+ min = 127;
+ max = -128;
+ for (x = 0, elev = pTopo; x < w * h; ++x, ++elev)
+ {
+ int v = *elev;
+ if (v > max)
+ max = v;
+ if (v < min)
+ min = v;
+ }
+ med = (min + max) / 2;
+ spread = max - med;
+
+ if (spread == 0)
+ { // perfectly smooth surface -- nothing to do but
+ // level it out completely
+ if (max != 0)
+ memset (pTopo, 0, w * h);
+ return;
+ }
+
+ // these are whatever looks right
+ if (spread < 10)
+ sfact = 30; // minimal spread
+ else if (spread < 30)
+ sfact = 60;
+ else
+ sfact = 100; // full spread
+
+ // apply spread
+ for (x = 0, elev = pTopo; x < w * h; ++x, ++elev)
+ {
+ int v = *elev;
+ v = (v - med) * sfact / spread;
+ *elev = v;
+ }
+
+ // compute and apply weighted averages of surrounding points
+ for (y = 0, elev = pTopo; y < h; ++y)
+ {
+ elev_block_t *pblk;
+ int i;
+
+ // prime the running block average
+ // get the minimum, maximum and avg elevation for each block
+ for (i = -LMAP_MAX_DIST; i < LMAP_MAX_DIST; ++i)
+ {
+ // blocks wrap around on both sides
+ pblk = vblocks + ((i + LMAP_BLOCKS) % LMAP_BLOCKS);
+
+ get_vblock_avg (pblk, pTopo, i, y);
+ }
+
+ for (x = 0; x < w; ++x, ++elev)
+ {
+ int avg = 0, total_weight = 0;
+
+ min = 127;
+ max = -127;
+
+ // prepare next block as we move along x
+ pblk = vblocks + ((x + LMAP_MAX_DIST) % LMAP_BLOCKS);
+ get_vblock_avg (pblk, pTopo, x + LMAP_MAX_DIST, y);
+
+ // compute the min, max and weighted avg of blocks
+ for (i = x - LMAP_MAX_DIST; i <= x + LMAP_MAX_DIST; ++i)
+ {
+ int delta = abs (i - x);
+ int weight = 255; // full weight
+
+ pblk = vblocks + ((i + LMAP_BLOCKS) % LMAP_BLOCKS);
+
+ if (delta >= LMAP_WEIGHT_THRES)
+ { // too far -- progressively reduced weight
+ weight = weight * (LMAP_MAX_DIST - delta + 1)
+ / (LMAP_MAX_DIST - LMAP_WEIGHT_THRES + 2);
+ }
+
+ if (pblk->max > max)
+ max = pblk->max;
+ if (pblk->min < min)
+ min = pblk->min;
+
+ avg += pblk->avg * weight;
+ total_weight += weight;
+ }
+ avg /= total_weight;
+
+ // This is mostly Voodoo
+ // figure out what kind of relative lighting factor
+ // to assign to this point
+#if 0
+ // relative to median
+ med = (min + max) / 2; // median
+ *elev = (int)*elev - med;
+#else
+ // relative to median of (average, median)
+ med = (min + max) / 2; // median
+ med = (med + avg) / 2;
+ *elev = (int)*elev - med;
+#endif
+ }
+ }
+}
+
+// Sets the SysGenRNG to the required state first.
+void
+GeneratePlanetSurface (PLANET_DESC *pPlanetDesc, FRAME SurfDefFrame)
+{
+ RECT r;
+ const PlanetFrame *PlanDataPtr;
+ PLANET_INFO *PlanetInfo = &pSolarSysState->SysInfo.PlanetInfo;
+ COUNT i, y;
+ POINT loc;
+ CONTEXT OldContext;
+ CONTEXT TopoContext;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ BOOLEAN shielded = (pPlanetDesc->data_index & PLANET_SHIELDED) != 0;
+
+ RandomContext_SeedRandom (SysGenRNG, pPlanetDesc->rand_seed);
+
+ TopoContext = CreateContext ("Plangen.TopoContext");
+ OldContext = SetContext (TopoContext);
+ planet_orbit_init ();
+
+ PlanDataPtr = &PlanData[pPlanetDesc->data_index & ~PLANET_SHIELDED];
+
+ if (SurfDefFrame)
+ { // This is a defined planet; pixmap for the topography and
+ // elevation data is supplied in Surface Definition frame
+ BOOLEAN DeleteDef = FALSE;
+ FRAME ElevFrame;
+
+ // surface pixmap
+ SurfDefFrame = SetAbsFrameIndex (SurfDefFrame, 0);
+ if (GetFrameWidth (SurfDefFrame) != MAP_WIDTH
+ || GetFrameHeight (SurfDefFrame) != MAP_HEIGHT)
+ {
+ pSolarSysState->TopoFrame = CaptureDrawable (RescaleFrame (
+ SurfDefFrame, MAP_WIDTH, MAP_HEIGHT));
+ // will not need the passed FRAME anymore
+ DeleteDef = TRUE;
+ }
+ else
+ pSolarSysState->TopoFrame = SurfDefFrame;
+
+ if (GetFrameCount (SurfDefFrame) > 1)
+ { // 2nd frame is elevation data
+ int i;
+ SBYTE* elev;
+
+ ElevFrame = SetAbsFrameIndex (SurfDefFrame, 1);
+ if (GetFrameWidth (ElevFrame) != MAP_WIDTH
+ || GetFrameHeight (ElevFrame) != MAP_HEIGHT)
+ {
+ ElevFrame = CaptureDrawable (RescaleFrame (ElevFrame,
+ MAP_WIDTH, MAP_HEIGHT));
+ }
+
+ // grab the elevation data in 1 byte per pixel format
+ ReadFramePixelIndexes (ElevFrame, (BYTE *)Orbit->lpTopoData,
+ MAP_WIDTH, MAP_HEIGHT);
+ // the supplied data is in unsigned format, must convert
+ for (i = 0, elev = Orbit->lpTopoData;
+ i < MAP_WIDTH * MAP_HEIGHT;
+ ++i, ++elev)
+ {
+ *elev = *(BYTE *)elev - 128;
+ }
+ }
+ else
+ { // no elevation data -- planet flat as a pancake
+ memset (Orbit->lpTopoData, 0, MAP_WIDTH * MAP_HEIGHT);
+ }
+
+ if (DeleteDef)
+ DestroyDrawable (ReleaseDrawable (SurfDefFrame));
+ }
+ else
+ { // Generate planet surface elevation data and look
+
+ r.corner.x = r.corner.y = 0;
+ r.extent.width = MAP_WIDTH;
+ r.extent.height = MAP_HEIGHT;
+ {
+ memset (Orbit->lpTopoData, 0, MAP_WIDTH * MAP_HEIGHT);
+ switch (PLANALGO (PlanDataPtr->Type))
+ {
+ case GAS_GIANT_ALGO:
+ MakeGasGiant (PlanDataPtr->num_faults,
+ Orbit->lpTopoData, &r, PlanDataPtr->fault_depth);
+ break;
+ case TOPO_ALGO:
+ case CRATERED_ALGO:
+ if (PlanDataPtr->num_faults)
+ DeltaTopography (PlanDataPtr->num_faults,
+ Orbit->lpTopoData, &r,
+ PlanDataPtr->fault_depth);
+
+ for (i = 0; i < PlanDataPtr->num_blemishes; ++i)
+ {
+ RECT crater_r;
+ UWORD loword;
+
+ loword = LOWORD (RandomContext_Random (SysGenRNG));
+ switch (HIBYTE (loword) & 31)
+ {
+ case 0:
+ crater_r.extent.width =
+ (LOBYTE (loword) % (MAP_HEIGHT >> 2))
+ + (MAP_HEIGHT >> 2);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ crater_r.extent.width =
+ (LOBYTE (loword) % (MAP_HEIGHT >> 3))
+ + (MAP_HEIGHT >> 3);
+ break;
+ default:
+ crater_r.extent.width =
+ (LOBYTE (loword) % (MAP_HEIGHT >> 4))
+ + 4;
+ break;
+ }
+
+ loword = LOWORD (RandomContext_Random (SysGenRNG));
+ crater_r.extent.height = crater_r.extent.width;
+ crater_r.corner.x = HIBYTE (loword)
+ % (MAP_WIDTH - crater_r.extent.width);
+ crater_r.corner.y = LOBYTE (loword)
+ % (MAP_HEIGHT - crater_r.extent.height);
+ MakeCrater (&crater_r, Orbit->lpTopoData,
+ PlanDataPtr->fault_depth << 2,
+ -(PlanDataPtr->fault_depth << 2),
+ FALSE);
+ }
+
+ if (PLANALGO (PlanDataPtr->Type) == CRATERED_ALGO)
+ DitherMap (Orbit->lpTopoData);
+ ValidateMap (Orbit->lpTopoData);
+ break;
+ }
+ }
+ pSolarSysState->TopoFrame = CaptureDrawable (
+ CreateDrawable (WANT_PIXMAP, (SIZE)MAP_WIDTH,
+ (SIZE)MAP_HEIGHT, 1));
+ pSolarSysState->OrbitalCMap = CaptureColorMap (
+ LoadColorMap (PlanDataPtr->CMapInstance));
+ pSolarSysState->XlatRef = CaptureStringTable (
+ LoadStringTable (PlanDataPtr->XlatTabInstance));
+
+ if (PlanetInfo->SurfaceTemperature > HOT_THRESHOLD)
+ {
+ pSolarSysState->OrbitalCMap = SetAbsColorMapIndex (
+ pSolarSysState->OrbitalCMap, 2);
+ pSolarSysState->XlatRef = SetAbsStringTableIndex (
+ pSolarSysState->XlatRef, 2);
+ }
+ else if (PlanetInfo->SurfaceTemperature > COLD_THRESHOLD)
+ {
+ pSolarSysState->OrbitalCMap = SetAbsColorMapIndex (
+ pSolarSysState->OrbitalCMap, 1);
+ pSolarSysState->XlatRef = SetAbsStringTableIndex (
+ pSolarSysState->XlatRef, 1);
+ }
+ pSolarSysState->XlatPtr = GetStringAddress (pSolarSysState->XlatRef);
+ RenderTopography (pSolarSysState->TopoFrame,
+ Orbit->lpTopoData, MAP_WIDTH, MAP_HEIGHT);
+
+ }
+
+ if (!shielded && PlanetInfo->AtmoDensity != GAS_GIANT_ATMOSPHERE)
+ { // produce 4x scaled topo image for Planetside
+ // for the planets that we can land on
+ SBYTE *pScaledTopo = HMalloc (MAP_WIDTH * 4 * MAP_HEIGHT * 4);
+ if (pScaledTopo)
+ {
+ TopoScale4x (pScaledTopo, Orbit->lpTopoData,
+ PlanDataPtr->num_faults, PlanDataPtr->fault_depth
+ * (PLANALGO (PlanDataPtr->Type) == CRATERED_ALGO ? 2 : 1 ));
+ RenderTopography (Orbit->TopoZoomFrame, pScaledTopo,
+ MAP_WIDTH * 4, MAP_HEIGHT * 4);
+
+ HFree (pScaledTopo);
+ }
+ }
+
+ // Generate a pixel array from the Topography map.
+ // We use this instead of lpTopoData because it needs to be
+ // WAP_WIDTH+SPHERE_SPAN_X wide and we need this method for Earth anyway.
+ // It may be more efficient to build it from lpTopoData instead of the
+ // FRAMPTR though.
+ ReadFramePixelColors (pSolarSysState->TopoFrame, Orbit->TopoColors,
+ MAP_WIDTH + SPHERE_SPAN_X, MAP_HEIGHT);
+ // Extend the width from MAP_WIDTH to MAP_WIDTH+SPHERE_SPAN_X
+ for (y = 0; y < MAP_HEIGHT * (MAP_WIDTH + SPHERE_SPAN_X);
+ y += MAP_WIDTH + SPHERE_SPAN_X)
+ memcpy (Orbit->TopoColors + y + MAP_WIDTH, Orbit->TopoColors + y,
+ SPHERE_SPAN_X * sizeof (Orbit->TopoColors[0]));
+
+ if (PLANALGO (PlanDataPtr->Type) != GAS_GIANT_ALGO)
+ { // convert topo data to a light map, based on relative
+ // map point elevations
+ GenerateLightMap (Orbit->lpTopoData, MAP_WIDTH, MAP_HEIGHT);
+ }
+ else
+ { // gas giants are pretty much flat
+ memset (Orbit->lpTopoData, 0, MAP_WIDTH * MAP_HEIGHT);
+ }
+
+ if (pSolarSysState->pOrbitalDesc->pPrevDesc ==
+ &pSolarSysState->SunDesc[0])
+ { // this is a planet -- get its location
+ loc = pSolarSysState->pOrbitalDesc->location;
+ }
+ else
+ { // this is a moon -- get its planet's location
+ loc = pSolarSysState->pOrbitalDesc->pPrevDesc->location;
+ }
+
+ // Rotating planet sphere initialization
+ GenerateSphereMask (loc);
+ CreateSphereTiltMap (PlanetInfo->AxialTilt);
+ if (shielded)
+ Orbit->ObjectFrame = CreateShieldMask ();
+ InitSphereRotation (1 - 2 * (PlanetInfo->AxialTilt & 1), shielded);
+
+ if (shielded)
+ { // This overwrites pSolarSysState->TopoFrame, so everything that
+ // needs it has to come before
+ ApplyShieldTint ();
+ }
+
+ SetContext (OldContext);
+ DestroyContext (TopoContext);
+}
+
diff --git a/src/uqm/planets/pstarmap.c b/src/uqm/planets/pstarmap.c
new file mode 100644
index 0000000..cd33858
--- /dev/null
+++ b/src/uqm/planets/pstarmap.c
@@ -0,0 +1,1631 @@
+//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 "scan.h"
+#include "../colors.h"
+#include "../controls.h"
+#include "../menustat.h"
+#include "../starmap.h"
+#include "../races.h"
+#include "../gameopt.h"
+#include "../gamestr.h"
+#include "../globdata.h"
+#include "../shipcont.h"
+#include "../units.h"
+#include "../hyper.h"
+#include "../sis.h"
+ // for DrawHyperCoords(), DrawStatusMessage()
+#include "../settings.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../state.h"
+#include "../uqmdebug.h"
+#include "options.h"
+#include "libs/inplib.h"
+#include "libs/strlib.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+#include "libs/memlib.h"
+
+#include <stdlib.h>
+
+
+static POINT cursorLoc;
+static POINT mapOrigin;
+static int zoomLevel;
+static FRAME StarMapFrame;
+
+
+static inline long
+signedDivWithError (long val, long divisor)
+{
+ int invert = 0;
+ if (val < 0)
+ {
+ invert = 1;
+ val = -val;
+ }
+ val = (val + ROUNDING_ERROR (divisor)) / divisor;
+ return invert ? -val : val;
+}
+
+#define MAP_FIT_X ((MAX_X_UNIVERSE + 1) / SIS_SCREEN_WIDTH + 1)
+
+static inline COORD
+universeToDispx (long ux)
+{
+ return signedDivWithError (((ux - mapOrigin.x) << zoomLevel)
+ * SIS_SCREEN_WIDTH, MAX_X_UNIVERSE + MAP_FIT_X)
+ + ((SIS_SCREEN_WIDTH - 1) >> 1);
+}
+#define UNIVERSE_TO_DISPX(ux) universeToDispx(ux)
+
+static inline COORD
+universeToDispy (long uy)
+{
+ return signedDivWithError (((mapOrigin.y - uy) << zoomLevel)
+ * SIS_SCREEN_HEIGHT, MAX_Y_UNIVERSE + 2)
+ + ((SIS_SCREEN_HEIGHT - 1) >> 1);
+}
+#define UNIVERSE_TO_DISPY(uy) universeToDispy(uy)
+
+static inline COORD
+dispxToUniverse (COORD dx)
+{
+ return (((long)(dx - ((SIS_SCREEN_WIDTH - 1) >> 1))
+ * (MAX_X_UNIVERSE + MAP_FIT_X)) >> zoomLevel)
+ / SIS_SCREEN_WIDTH + mapOrigin.x;
+}
+#define DISP_TO_UNIVERSEX(dx) dispxToUniverse(dx)
+
+static inline COORD
+dispyToUniverse (COORD dy)
+{
+ return (((long)(((SIS_SCREEN_HEIGHT - 1) >> 1) - dy)
+ * (MAX_Y_UNIVERSE + 2)) >> zoomLevel)
+ / SIS_SCREEN_HEIGHT + mapOrigin.y;
+}
+#define DISP_TO_UNIVERSEY(dy) dispyToUniverse(dy)
+
+static BOOLEAN transition_pending;
+
+static void
+flashCurrentLocation (POINT *where)
+{
+ static BYTE c = 0;
+ static int val = -2;
+ static POINT universe;
+ static TimeCount NextTime = 0;
+
+ if (where)
+ universe = *where;
+
+ if (GetTimeCounter () >= NextTime)
+ {
+ Color OldColor;
+ CONTEXT OldContext;
+ STAMP s;
+
+ NextTime = GetTimeCounter () + (ONE_SECOND / 16);
+
+ OldContext = SetContext (SpaceContext);
+
+ if (c == 0x00 || c == 0x1A)
+ val = -val;
+ c += val;
+ OldColor = SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (c, c, c), c));
+ s.origin.x = UNIVERSE_TO_DISPX (universe.x);
+ s.origin.y = UNIVERSE_TO_DISPY (universe.y);
+ s.frame = IncFrameIndex (StarMapFrame);
+ DrawFilledStamp (&s);
+ SetContextForeGroundColor (OldColor);
+
+ SetContext (OldContext);
+ }
+}
+
+static void
+DrawCursor (COORD curs_x, COORD curs_y)
+{
+ STAMP s;
+
+ s.origin.x = curs_x;
+ s.origin.y = curs_y;
+ s.frame = StarMapFrame;
+
+ DrawStamp (&s);
+}
+
+static void
+DrawAutoPilot (POINT *pDstPt)
+{
+ SIZE dx, dy,
+ xincr, yincr,
+ xerror, yerror,
+ cycle, delta;
+ POINT pt;
+
+ if (!inHQSpace ())
+ pt = CurStarDescPtr->star_pt;
+ else
+ {
+ pt.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ pt.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+ pt.x = UNIVERSE_TO_DISPX (pt.x);
+ pt.y = UNIVERSE_TO_DISPY (pt.y);
+
+ dx = UNIVERSE_TO_DISPX (pDstPt->x) - pt.x;
+ if (dx >= 0)
+ xincr = 1;
+ else
+ {
+ xincr = -1;
+ dx = -dx;
+ }
+ dx <<= 1;
+
+ dy = UNIVERSE_TO_DISPY (pDstPt->y) - pt.y;
+ if (dy >= 0)
+ yincr = 1;
+ else
+ {
+ yincr = -1;
+ dy = -dy;
+ }
+ dy <<= 1;
+
+ if (dx >= dy)
+ cycle = dx;
+ else
+ cycle = dy;
+ delta = xerror = yerror = cycle >> 1;
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x04, 0x04, 0x1F), 0x01));
+
+ delta &= ~1;
+ while (delta--)
+ {
+ if (!(delta & 1))
+ DrawPoint (&pt);
+
+ if ((xerror -= dx) <= 0)
+ {
+ pt.x += xincr;
+ xerror += cycle;
+ }
+ if ((yerror -= dy) <= 0)
+ {
+ pt.y += yincr;
+ yerror += cycle;
+ }
+ }
+}
+
+static void
+GetSphereRect (FLEET_INFO *FleetPtr, RECT *pRect, RECT *pRepairRect)
+{
+ long diameter;
+
+ diameter = (long)(FleetPtr->known_strength * SPHERE_RADIUS_INCREMENT);
+ pRect->extent.width = UNIVERSE_TO_DISPX (diameter)
+ - UNIVERSE_TO_DISPX (0);
+ if (pRect->extent.width < 0)
+ pRect->extent.width = -pRect->extent.width;
+ else if (pRect->extent.width == 0)
+ pRect->extent.width = 1;
+ pRect->extent.height = UNIVERSE_TO_DISPY (diameter)
+ - UNIVERSE_TO_DISPY (0);
+ if (pRect->extent.height < 0)
+ pRect->extent.height = -pRect->extent.height;
+ else if (pRect->extent.height == 0)
+ pRect->extent.height = 1;
+
+ pRect->corner.x = UNIVERSE_TO_DISPX (FleetPtr->known_loc.x);
+ pRect->corner.y = UNIVERSE_TO_DISPY (FleetPtr->known_loc.y);
+ pRect->corner.x -= pRect->extent.width >> 1;
+ pRect->corner.y -= pRect->extent.height >> 1;
+
+ {
+ TEXT t;
+ STRING locString;
+
+ SetContextFont (TinyFont);
+
+ t.baseline.x = pRect->corner.x + (pRect->extent.width >> 1);
+ t.baseline.y = pRect->corner.y + (pRect->extent.height >> 1) - 1;
+ t.align = ALIGN_CENTER;
+ locString = SetAbsStringTableIndex (FleetPtr->race_strings, 1);
+ t.CharCount = GetStringLength (locString);
+ t.pStr = (UNICODE *)GetStringAddress (locString);
+ TextRect (&t, pRepairRect, NULL);
+
+ if (pRepairRect->corner.x <= 0)
+ pRepairRect->corner.x = 1;
+ else if (pRepairRect->corner.x + pRepairRect->extent.width >=
+ SIS_SCREEN_WIDTH)
+ pRepairRect->corner.x =
+ SIS_SCREEN_WIDTH - pRepairRect->extent.width - 1;
+ if (pRepairRect->corner.y <= 0)
+ pRepairRect->corner.y = 1;
+ else if (pRepairRect->corner.y + pRepairRect->extent.height >=
+ SIS_SCREEN_HEIGHT)
+ pRepairRect->corner.y =
+ SIS_SCREEN_HEIGHT - pRepairRect->extent.height - 1;
+
+ BoxUnion (pRepairRect, pRect, pRepairRect);
+ pRepairRect->extent.width++;
+ pRepairRect->extent.height++;
+ }
+}
+
+static void
+DrawStarMap (COUNT race_update, RECT *pClipRect)
+{
+#define GRID_DELTA 500
+ SIZE i;
+ COUNT which_space;
+ long diameter;
+ RECT r, old_r;
+ POINT oldOrigin = {0, 0};
+ STAMP s;
+ FRAME star_frame;
+ STAR_DESC *SDPtr;
+ BOOLEAN draw_cursor;
+
+ if (pClipRect == (RECT*)-1)
+ {
+ pClipRect = 0;
+ draw_cursor = FALSE;
+ }
+ else
+ {
+ draw_cursor = TRUE;
+ }
+
+ SetContext (SpaceContext);
+ if (pClipRect)
+ {
+ GetContextClipRect (&old_r);
+ pClipRect->corner.x += old_r.corner.x;
+ pClipRect->corner.y += old_r.corner.y;
+ SetContextClipRect (pClipRect);
+ pClipRect->corner.x -= old_r.corner.x;
+ pClipRect->corner.y -= old_r.corner.y;
+ // Offset the origin so that we draw the correct gfx in the cliprect
+ oldOrigin = SetContextOrigin (MAKE_POINT (-pClipRect->corner.x,
+ -pClipRect->corner.y));
+ }
+
+ if (transition_pending)
+ {
+ SetTransitionSource (NULL);
+ }
+ BatchGraphics ();
+
+ which_space = GET_GAME_STATE (ARILOU_SPACE_SIDE);
+
+ if (which_space <= 1)
+ {
+ SDPtr = &star_array[0];
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x07), 0x57));
+ SetContextBackGroundColor (BLACK_COLOR);
+ }
+ else
+ {
+ SDPtr = &star_array[NUM_SOLAR_SYSTEMS + 1];
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x0B, 0x00), 0x6D));
+ SetContextBackGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x08, 0x00), 0x6E));
+ }
+ ClearDrawable ();
+
+ // Draw the fuel range circle
+ if (race_update == 0
+ && which_space < 2
+ && (diameter = (long)GLOBAL_SIS (FuelOnBoard) << 1))
+ {
+ Color OldColor;
+
+ if (!inHQSpace ())
+ r.corner = CurStarDescPtr->star_pt;
+ else
+ {
+ r.corner.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ r.corner.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+
+ // Cap the diameter to a sane range
+ if (diameter > MAX_X_UNIVERSE * 4)
+ diameter = MAX_X_UNIVERSE * 4;
+
+ r.extent.width = UNIVERSE_TO_DISPX (diameter)
+ - UNIVERSE_TO_DISPX (0);
+ if (r.extent.width < 0)
+ r.extent.width = -r.extent.width;
+ r.extent.height = UNIVERSE_TO_DISPY (diameter)
+ - UNIVERSE_TO_DISPY (0);
+ if (r.extent.height < 0)
+ r.extent.height = -r.extent.height;
+
+ r.corner.x = UNIVERSE_TO_DISPX (r.corner.x)
+ - (r.extent.width >> 1);
+ r.corner.y = UNIVERSE_TO_DISPY (r.corner.y)
+ - (r.extent.height >> 1);
+
+ OldColor = SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x03, 0x03, 0x03), 0x22));
+ DrawFilledOval (&r);
+ SetContextForeGroundColor (OldColor);
+ }
+
+ for (i = MAX_Y_UNIVERSE + 1; i >= 0; i -= GRID_DELTA)
+ {
+ SIZE j;
+
+ r.corner.x = UNIVERSE_TO_DISPX (0);
+ r.corner.y = UNIVERSE_TO_DISPY (i);
+ r.extent.width = SIS_SCREEN_WIDTH << zoomLevel;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+
+ r.corner.y = UNIVERSE_TO_DISPY (MAX_Y_UNIVERSE + 1);
+ r.extent.width = 1;
+ r.extent.height = SIS_SCREEN_HEIGHT << zoomLevel;
+ for (j = MAX_X_UNIVERSE + 1; j >= 0; j -= GRID_DELTA)
+ {
+ r.corner.x = UNIVERSE_TO_DISPX (j);
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ star_frame = SetRelFrameIndex (StarMapFrame, 2);
+ if (which_space <= 1)
+ {
+ COUNT index;
+ HFLEETINFO hStarShip, hNextShip;
+ static const Color race_colors[] =
+ {
+ RACE_COLORS
+ };
+
+ for (index = 0,
+ hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip != 0; ++index, hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if (FleetPtr->known_strength)
+ {
+ RECT repair_r;
+
+ GetSphereRect (FleetPtr, &r, &repair_r);
+ if (r.corner.x < SIS_SCREEN_WIDTH
+ && r.corner.y < SIS_SCREEN_HEIGHT
+ && r.corner.x + r.extent.width > 0
+ && r.corner.y + r.extent.height > 0
+ && (pClipRect == 0
+ || (repair_r.corner.x < pClipRect->corner.x + pClipRect->extent.width
+ && repair_r.corner.y < pClipRect->corner.y + pClipRect->extent.height
+ && repair_r.corner.x + repair_r.extent.width > pClipRect->corner.x
+ && repair_r.corner.y + repair_r.extent.height > pClipRect->corner.y)))
+ {
+ Color c;
+ TEXT t;
+ STRING locString;
+
+ c = race_colors[index];
+ if (index + 1 == race_update)
+ SetContextForeGroundColor (WHITE_COLOR);
+ else
+ SetContextForeGroundColor (c);
+ DrawOval (&r, 0);
+
+ SetContextFont (TinyFont);
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + (r.extent.height >> 1) - 1;
+ t.align = ALIGN_CENTER;
+ locString = SetAbsStringTableIndex (
+ FleetPtr->race_strings, 1);
+ t.CharCount = GetStringLength (locString);
+ t.pStr = (UNICODE *)GetStringAddress (locString);
+ TextRect (&t, &r, NULL);
+
+ if (r.corner.x <= 0)
+ t.baseline.x -= r.corner.x - 1;
+ else if (r.corner.x + r.extent.width >= SIS_SCREEN_WIDTH)
+ t.baseline.x -= (r.corner.x + r.extent.width)
+ - SIS_SCREEN_WIDTH + 1;
+ if (r.corner.y <= 0)
+ t.baseline.y -= r.corner.y - 1;
+ else if (r.corner.y + r.extent.height >= SIS_SCREEN_HEIGHT)
+ t.baseline.y -= (r.corner.y + r.extent.height)
+ - SIS_SCREEN_HEIGHT + 1;
+
+ // The text color is slightly lighter than the color of
+ // the SoI.
+ c.r = (c.r >= 0xff - CC5TO8 (0x03)) ?
+ 0xff : c.r + CC5TO8 (0x03);
+ c.g = (c.g >= 0xff - CC5TO8 (0x03)) ?
+ 0xff : c.g + CC5TO8 (0x03);
+ c.b = (c.b >= 0xff - CC5TO8 (0x03)) ?
+ 0xff : c.b + CC5TO8 (0x03);
+
+ SetContextForeGroundColor (c);
+ font_DrawText (&t);
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+ }
+
+ do
+ {
+ BYTE star_type;
+
+ star_type = SDPtr->Type;
+
+ s.origin.x = UNIVERSE_TO_DISPX (SDPtr->star_pt.x);
+ s.origin.y = UNIVERSE_TO_DISPY (SDPtr->star_pt.y);
+ if (which_space <= 1)
+ s.frame = SetRelFrameIndex (star_frame,
+ STAR_TYPE (star_type)
+ * NUM_STAR_COLORS
+ + STAR_COLOR (star_type));
+ else if (SDPtr->star_pt.x == ARILOU_HOME_X
+ && SDPtr->star_pt.y == ARILOU_HOME_Y)
+ s.frame = SetRelFrameIndex (star_frame,
+ SUPER_GIANT_STAR * NUM_STAR_COLORS + GREEN_BODY);
+ else
+ s.frame = SetRelFrameIndex (star_frame,
+ GIANT_STAR * NUM_STAR_COLORS + GREEN_BODY);
+ DrawStamp (&s);
+
+ ++SDPtr;
+ } while (SDPtr->star_pt.x <= MAX_X_UNIVERSE
+ && SDPtr->star_pt.y <= MAX_Y_UNIVERSE);
+
+ if (GET_GAME_STATE (ARILOU_SPACE))
+ {
+ if (which_space <= 1)
+ {
+ s.origin.x = UNIVERSE_TO_DISPX (ARILOU_SPACE_X);
+ s.origin.y = UNIVERSE_TO_DISPY (ARILOU_SPACE_Y);
+ }
+ else
+ {
+ s.origin.x = UNIVERSE_TO_DISPX (QUASI_SPACE_X);
+ s.origin.y = UNIVERSE_TO_DISPY (QUASI_SPACE_Y);
+ }
+ s.frame = SetRelFrameIndex (star_frame,
+ GIANT_STAR * NUM_STAR_COLORS + GREEN_BODY);
+ DrawStamp (&s);
+ }
+
+ if (race_update == 0
+ && GLOBAL (autopilot.x) != ~0
+ && GLOBAL (autopilot.y) != ~0)
+ DrawAutoPilot (&GLOBAL (autopilot));
+
+ if (transition_pending)
+ {
+ GetContextClipRect (&r);
+ ScreenTransition (3, &r);
+ transition_pending = FALSE;
+ }
+
+ UnbatchGraphics ();
+
+ if (pClipRect)
+ {
+ SetContextClipRect (&old_r);
+ SetContextOrigin (oldOrigin);
+ }
+
+ if (race_update == 0)
+ {
+ if (draw_cursor)
+ {
+ GetContextClipRect (&r);
+ LoadIntoExtraScreen (&r);
+ DrawCursor (UNIVERSE_TO_DISPX (cursorLoc.x),
+ UNIVERSE_TO_DISPY (cursorLoc.y));
+ }
+ }
+}
+
+static void
+EraseCursor (COORD curs_x, COORD curs_y)
+{
+ RECT r;
+
+ GetFrameRect (StarMapFrame, &r);
+
+ if ((r.corner.x += curs_x) < 0)
+ {
+ r.extent.width += r.corner.x;
+ r.corner.x = 0;
+ }
+ else if (r.corner.x + r.extent.width >= SIS_SCREEN_WIDTH)
+ r.extent.width = SIS_SCREEN_WIDTH - r.corner.x;
+ if ((r.corner.y += curs_y) < 0)
+ {
+ r.extent.height += r.corner.y;
+ r.corner.y = 0;
+ }
+ else if (r.corner.y + r.extent.height >= SIS_SCREEN_HEIGHT)
+ r.extent.height = SIS_SCREEN_HEIGHT - r.corner.y;
+
+#ifndef OLD
+ RepairBackRect (&r);
+#else /* NEW */
+ r.extent.height += r.corner.y & 1;
+ r.corner.y &= ~1;
+ DrawStarMap (0, &r);
+#endif /* OLD */
+}
+
+static void
+ZoomStarMap (SIZE dir)
+{
+#define MAX_ZOOM_SHIFT 4
+ if (dir > 0)
+ {
+ if (zoomLevel < MAX_ZOOM_SHIFT)
+ {
+ ++zoomLevel;
+ mapOrigin = cursorLoc;
+
+ DrawStarMap (0, NULL);
+ SleepThread (ONE_SECOND / 8);
+ }
+ }
+ else if (dir < 0)
+ {
+ if (zoomLevel > 0)
+ {
+ if (zoomLevel > 1)
+ mapOrigin = cursorLoc;
+ else
+ {
+ mapOrigin.x = MAX_X_UNIVERSE >> 1;
+ mapOrigin.y = MAX_Y_UNIVERSE >> 1;
+ }
+ --zoomLevel;
+
+ DrawStarMap (0, NULL);
+ SleepThread (ONE_SECOND / 8);
+ }
+ }
+}
+
+static void
+UpdateCursorLocation (int sx, int sy, const POINT *newpt)
+{
+ STAMP s;
+ POINT pt;
+
+ pt.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ pt.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+
+ if (newpt)
+ { // absolute move
+ sx = sy = 0;
+ s.origin.x = UNIVERSE_TO_DISPX (newpt->x);
+ s.origin.y = UNIVERSE_TO_DISPY (newpt->y);
+ cursorLoc = *newpt;
+ }
+ else
+ { // incremental move
+ s.origin.x = pt.x + sx;
+ s.origin.y = pt.y + sy;
+ }
+
+ if (sx)
+ {
+ cursorLoc.x = DISP_TO_UNIVERSEX (s.origin.x) - sx;
+ while (UNIVERSE_TO_DISPX (cursorLoc.x) == pt.x)
+ cursorLoc.x += sx;
+
+ if (cursorLoc.x < 0)
+ cursorLoc.x = 0;
+ else if (cursorLoc.x > MAX_X_UNIVERSE)
+ cursorLoc.x = MAX_X_UNIVERSE;
+
+ s.origin.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ }
+
+ if (sy)
+ {
+ cursorLoc.y = DISP_TO_UNIVERSEY (s.origin.y) + sy;
+ while (UNIVERSE_TO_DISPY (cursorLoc.y) == pt.y)
+ cursorLoc.y -= sy;
+
+ if (cursorLoc.y < 0)
+ cursorLoc.y = 0;
+ else if (cursorLoc.y > MAX_Y_UNIVERSE)
+ cursorLoc.y = MAX_Y_UNIVERSE;
+
+ s.origin.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+ }
+
+ if (s.origin.x < 0 || s.origin.y < 0
+ || s.origin.x >= SIS_SCREEN_WIDTH
+ || s.origin.y >= SIS_SCREEN_HEIGHT)
+ {
+ mapOrigin = cursorLoc;
+ DrawStarMap (0, NULL);
+
+ s.origin.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ s.origin.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+ }
+ else
+ {
+ EraseCursor (pt.x, pt.y);
+ // ClearDrawable ();
+ DrawCursor (s.origin.x, s.origin.y);
+ }
+}
+
+#define CURSOR_INFO_BUFSIZE 256
+
+static void
+UpdateCursorInfo (UNICODE *prevbuf)
+{
+ UNICODE buf[CURSOR_INFO_BUFSIZE] = "";
+ POINT pt;
+ STAR_DESC *SDPtr;
+ STAR_DESC *BestSDPtr;
+
+ pt.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ pt.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+
+ SDPtr = BestSDPtr = 0;
+ while ((SDPtr = FindStar (SDPtr, &cursorLoc, 75, 75)))
+ {
+ if (UNIVERSE_TO_DISPX (SDPtr->star_pt.x) == pt.x
+ && UNIVERSE_TO_DISPY (SDPtr->star_pt.y) == pt.y
+ && (BestSDPtr == 0
+ || STAR_TYPE (SDPtr->Type) >= STAR_TYPE (BestSDPtr->Type)))
+ BestSDPtr = SDPtr;
+ }
+
+ if (BestSDPtr)
+ {
+ cursorLoc = BestSDPtr->star_pt;
+ GetClusterName (BestSDPtr, buf);
+ }
+ else
+ { // No star found. Reset the coordinates to the cursor's location
+ cursorLoc.x = DISP_TO_UNIVERSEX (pt.x);
+ if (cursorLoc.x < 0)
+ cursorLoc.x = 0;
+ else if (cursorLoc.x > MAX_X_UNIVERSE)
+ cursorLoc.x = MAX_X_UNIVERSE;
+ cursorLoc.y = DISP_TO_UNIVERSEY (pt.y);
+ if (cursorLoc.y < 0)
+ cursorLoc.y = 0;
+ else if (cursorLoc.y > MAX_Y_UNIVERSE)
+ cursorLoc.y = MAX_Y_UNIVERSE;
+ }
+
+ if (GET_GAME_STATE (ARILOU_SPACE))
+ {
+ POINT ari_pt;
+
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ ari_pt.x = ARILOU_SPACE_X;
+ ari_pt.y = ARILOU_SPACE_Y;
+ }
+ else
+ {
+ ari_pt.x = QUASI_SPACE_X;
+ ari_pt.y = QUASI_SPACE_Y;
+ }
+
+ if (UNIVERSE_TO_DISPX (ari_pt.x) == pt.x
+ && UNIVERSE_TO_DISPY (ari_pt.y) == pt.y)
+ {
+ cursorLoc = ari_pt;
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (STAR_STRING_BASE + 132));
+ }
+ }
+
+ DrawHyperCoords (cursorLoc);
+ if (strcmp (buf, prevbuf) != 0)
+ {
+ strcpy (prevbuf, buf);
+ DrawSISMessage (buf);
+ }
+}
+
+static void
+UpdateFuelRequirement (void)
+{
+ UNICODE buf[80];
+ COUNT fuel_required;
+ DWORD f;
+ POINT pt;
+
+ if (!inHQSpace ())
+ pt = CurStarDescPtr->star_pt;
+ else
+ {
+ pt.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ pt.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+ pt.x -= cursorLoc.x;
+ pt.y -= cursorLoc.y;
+
+ f = (DWORD)((long)pt.x * pt.x + (long)pt.y * pt.y);
+ if (f == 0 || GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1)
+ fuel_required = 0;
+ else
+ fuel_required = square_root (f) + (FUEL_TANK_SCALE / 20);
+
+ sprintf (buf, "%s %u.%u",
+ GAME_STRING (NAVIGATION_STRING_BASE + 4),
+ fuel_required / FUEL_TANK_SCALE,
+ (fuel_required % FUEL_TANK_SCALE) / 10);
+
+ DrawStatusMessage (buf);
+}
+
+#define STAR_SEARCH_BUFSIZE 256
+
+typedef struct starsearch_state
+{
+ // TODO: pMS field is probably not needed anymore
+ MENU_STATE *pMS;
+ UNICODE Text[STAR_SEARCH_BUFSIZE];
+ UNICODE LastText[STAR_SEARCH_BUFSIZE];
+ DWORD LastChangeTime;
+ int FirstIndex;
+ int CurIndex;
+ int LastIndex;
+ BOOLEAN SingleClust;
+ BOOLEAN SingleMatch;
+ UNICODE Buffer[STAR_SEARCH_BUFSIZE];
+ const UNICODE *Prefix;
+ const UNICODE *Cluster;
+ int PrefixLen;
+ int ClusterLen;
+ int ClusterPos;
+ int SortedStars[NUM_SOLAR_SYSTEMS];
+} STAR_SEARCH_STATE;
+
+static int
+compStarName (const void *ptr1, const void *ptr2)
+{
+ int index1;
+ int index2;
+
+ index1 = *(const int *) ptr1;
+ index2 = *(const int *) ptr2;
+ if (star_array[index1].Postfix != star_array[index2].Postfix)
+ {
+ return utf8StringCompare (GAME_STRING (star_array[index1].Postfix),
+ GAME_STRING (star_array[index2].Postfix));
+ }
+
+ if (star_array[index1].Prefix < star_array[index2].Prefix)
+ return -1;
+
+ if (star_array[index1].Prefix > star_array[index2].Prefix)
+ return 1;
+
+ return 0;
+}
+
+static void
+SortStarsOnName (STAR_SEARCH_STATE *pSS)
+{
+ int i;
+ int *sorted = pSS->SortedStars;
+
+ for (i = 0; i < NUM_SOLAR_SYSTEMS; i++)
+ sorted[i] = i;
+
+ qsort (sorted, NUM_SOLAR_SYSTEMS, sizeof (int), compStarName);
+}
+
+static void
+SplitStarName (STAR_SEARCH_STATE *pSS)
+{
+ UNICODE *buf = pSS->Buffer;
+ UNICODE *next;
+ UNICODE *sep = NULL;
+
+ pSS->Prefix = 0;
+ pSS->PrefixLen = 0;
+ pSS->Cluster = 0;
+ pSS->ClusterLen = 0;
+ pSS->ClusterPos = 0;
+
+ // skip leading space
+ for (next = buf; *next != '\0' &&
+ getCharFromString ((const UNICODE **)&next) == ' ';
+ buf = next)
+ ;
+ if (*buf == '\0')
+ { // no text
+ return;
+ }
+
+ pSS->Prefix = buf;
+
+ // See if player gave a prefix
+ for (buf = next; *next != '\0' &&
+ getCharFromString ((const UNICODE **)&next) != ' ';
+ buf = next)
+ ;
+ if (*buf != '\0')
+ { // found possibly separating ' '
+ sep = buf;
+ // skip separating space
+ for (buf = next; *next != '\0' &&
+ getCharFromString ((const UNICODE **)&next) == ' ';
+ buf = next)
+ ;
+ }
+
+ if (*buf == '\0')
+ { // reached the end -- cluster only
+ pSS->Cluster = pSS->Prefix;
+ pSS->ClusterLen = utf8StringCount (pSS->Cluster);
+ pSS->ClusterPos = utf8StringCountN (pSS->Buffer, pSS->Cluster);
+ pSS->Prefix = 0;
+ return;
+ }
+
+ // consider the rest cluster name (whatever there is)
+ pSS->Cluster = buf;
+ pSS->ClusterLen = utf8StringCount (pSS->Cluster);
+ pSS->ClusterPos = utf8StringCountN (pSS->Buffer, pSS->Cluster);
+ *sep = '\0'; // split
+ pSS->PrefixLen = utf8StringCount (pSS->Prefix);
+}
+
+static inline int
+SkipStarCluster (int *sortedStars, int istar)
+{
+ int Postfix = star_array[sortedStars[istar]].Postfix;
+
+ for (++istar; istar < NUM_SOLAR_SYSTEMS &&
+ star_array[sortedStars[istar]].Postfix == Postfix;
+ ++istar)
+ ;
+ return istar;
+}
+
+static int
+FindNextStarIndex (STAR_SEARCH_STATE *pSS, int from, BOOLEAN WithinClust)
+{
+ int i;
+
+ if (!pSS->Cluster)
+ return -1; // nothing to search for
+
+ for (i = from; i < NUM_SOLAR_SYSTEMS; ++i)
+ {
+ STAR_DESC *SDPtr = &star_array[pSS->SortedStars[i]];
+ UNICODE FullName[STAR_SEARCH_BUFSIZE];
+ UNICODE *ClusterName = GAME_STRING (SDPtr->Postfix);
+ const UNICODE *sptr;
+ const UNICODE *dptr;
+ int dlen;
+ int c;
+
+ dlen = utf8StringCount (ClusterName);
+ if (pSS->ClusterLen > dlen)
+ { // no match, skip the rest of cluster
+ i = SkipStarCluster (pSS->SortedStars, i) - 1;
+ continue;
+ }
+
+ for (c = 0, sptr = pSS->Cluster, dptr = ClusterName;
+ c < pSS->ClusterLen; ++c)
+ {
+ UniChar sc = getCharFromString (&sptr);
+ UniChar dc = getCharFromString (&dptr);
+
+ if (UniChar_toUpper (sc) != UniChar_toUpper (dc))
+ break;
+ }
+
+ if (c < pSS->ClusterLen)
+ { // no match here, skip the rest of cluster
+ i = SkipStarCluster (pSS->SortedStars, i) - 1;
+ continue;
+ }
+
+ if (pSS->Prefix && !SDPtr->Prefix)
+ // we were given a prefix but found a singular star;
+ // that is a no match
+ continue;
+
+ if (WithinClust)
+ // searching within clusters; any prefix is a match
+ break;
+
+ if (!pSS->Prefix)
+ { // searching for cluster name only
+ // return only the first stars in a cluster
+ if (i == 0 || SDPtr->Postfix !=
+ star_array[pSS->SortedStars[i - 1]].Postfix)
+ { // found one
+ break;
+ }
+ else
+ { // another star in the same cluster, skip cluster
+ i = SkipStarCluster (pSS->SortedStars, i) - 1;
+ continue;
+ }
+ }
+
+ // check prefix
+ GetClusterName (SDPtr, FullName);
+ dlen = utf8StringCount (FullName);
+ if (pSS->PrefixLen > dlen)
+ continue;
+
+ for (c = 0, sptr = pSS->Prefix, dptr = FullName;
+ c < pSS->PrefixLen; ++c)
+ {
+ UniChar sc = getCharFromString (&sptr);
+ UniChar dc = getCharFromString (&dptr);
+
+ if (UniChar_toUpper (sc) != UniChar_toUpper (dc))
+ break;
+ }
+
+ if (c >= pSS->PrefixLen)
+ break; // found one
+ }
+
+ return (i < NUM_SOLAR_SYSTEMS) ? i : -1;
+}
+
+static void
+DrawMatchedStarName (TEXTENTRY_STATE *pTES)
+{
+ STAR_SEARCH_STATE *pSS = (STAR_SEARCH_STATE *) pTES->CbParam;
+ UNICODE buf[STAR_SEARCH_BUFSIZE] = "";
+ SIZE ExPos = 0;
+ SIZE CurPos = -1;
+ STAR_DESC *SDPtr = &star_array[pSS->SortedStars[pSS->CurIndex]];
+ COUNT flags;
+
+ if (pSS->SingleClust || pSS->SingleMatch)
+ { // draw full star name
+ GetClusterName (SDPtr, buf);
+ ExPos = -1;
+ flags = DSME_SETFR;
+ }
+ else
+ { // draw substring match
+ UNICODE *pstr = buf;
+
+ strcpy (pstr, pSS->Text);
+ ExPos = pSS->ClusterPos;
+ pstr = skipUTF8Chars (pstr, pSS->ClusterPos);
+
+ strcpy (pstr, GAME_STRING (SDPtr->Postfix));
+ ExPos += pSS->ClusterLen;
+ CurPos = pTES->CursorPos;
+
+ flags = DSME_CLEARFR;
+ if (pTES->JoystickMode)
+ flags |= DSME_BLOCKCUR;
+ }
+
+ DrawSISMessageEx (buf, CurPos, ExPos, flags);
+ DrawHyperCoords (cursorLoc);
+}
+
+static void
+MatchNextStar (STAR_SEARCH_STATE *pSS, BOOLEAN Reset)
+{
+ if (Reset)
+ pSS->FirstIndex = -1; // reset cache
+
+ if (pSS->FirstIndex < 0)
+ { // first time after changes
+ pSS->CurIndex = -1;
+ pSS->LastIndex = -1;
+ pSS->SingleClust = FALSE;
+ pSS->SingleMatch = FALSE;
+ strcpy (pSS->Buffer, pSS->Text);
+ SplitStarName (pSS);
+ }
+
+ pSS->CurIndex = FindNextStarIndex (pSS, pSS->CurIndex + 1,
+ pSS->SingleClust);
+ if (pSS->FirstIndex < 0) // first search
+ pSS->FirstIndex = pSS->CurIndex;
+
+ if (pSS->CurIndex >= 0)
+ { // remember as last (searching forward-only)
+ pSS->LastIndex = pSS->CurIndex;
+ }
+ else
+ { // wrap around
+ pSS->CurIndex = pSS->FirstIndex;
+
+ if (pSS->FirstIndex == pSS->LastIndex && pSS->FirstIndex != -1)
+ {
+ if (!pSS->Prefix)
+ { // only one cluster matching
+ pSS->SingleClust = TRUE;
+ }
+ else
+ { // exact match
+ pSS->SingleMatch = TRUE;
+ }
+ }
+ }
+}
+
+static BOOLEAN
+OnStarNameChange (TEXTENTRY_STATE *pTES)
+{
+ STAR_SEARCH_STATE *pSS = (STAR_SEARCH_STATE *) pTES->CbParam;
+ COUNT flags;
+ BOOLEAN ret = TRUE;
+
+ if (strcmp (pSS->Text, pSS->LastText) != 0)
+ { // string changed
+ pSS->LastChangeTime = GetTimeCounter ();
+ strcpy (pSS->LastText, pSS->Text);
+
+ // reset the search
+ MatchNextStar (pSS, TRUE);
+ }
+
+ if (pSS->CurIndex < 0)
+ { // nothing found
+ if (pSS->Text[0] == '\0')
+ flags = DSME_SETFR;
+ else
+ flags = DSME_CLEARFR;
+ if (pTES->JoystickMode)
+ flags |= DSME_BLOCKCUR;
+
+ ret = DrawSISMessageEx (pSS->Text, pTES->CursorPos, -1, flags);
+ }
+ else
+ {
+ STAR_DESC *SDPtr;
+
+ // move the cursor to the found star
+ SDPtr = &star_array[pSS->SortedStars[pSS->CurIndex]];
+ UpdateCursorLocation (0, 0, &SDPtr->star_pt);
+
+ DrawMatchedStarName (pTES);
+ UpdateFuelRequirement ();
+ }
+
+ return ret;
+}
+
+static BOOLEAN
+OnStarNameFrame (TEXTENTRY_STATE *pTES)
+{
+ STAR_SEARCH_STATE *pSS = (STAR_SEARCH_STATE *) pTES->CbParam;
+
+ if (PulsedInputState.menu[KEY_MENU_NEXT])
+ { // search for next match
+ STAR_DESC *SDPtr;
+
+ MatchNextStar (pSS, FALSE);
+
+ if (pSS->CurIndex < 0)
+ { // nothing found
+ if (PulsedInputState.menu[KEY_MENU_NEXT])
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ // move the cursor to the found star
+ SDPtr = &star_array[pSS->SortedStars[pSS->CurIndex]];
+ UpdateCursorLocation (0, 0, &SDPtr->star_pt);
+
+ DrawMatchedStarName (pTES);
+ UpdateFuelRequirement ();
+ }
+
+ flashCurrentLocation (NULL);
+
+ SleepThread (ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static BOOLEAN
+DoStarSearch (MENU_STATE *pMS)
+{
+ TEXTENTRY_STATE tes;
+ STAR_SEARCH_STATE *pss;
+ BOOLEAN success;
+
+ pss = HMalloc (sizeof (*pss));
+ if (!pss)
+ return FALSE;
+
+ DrawSISMessageEx ("", 0, 0, DSME_SETFR);
+
+ pss->pMS = pMS;
+ pss->LastChangeTime = 0;
+ pss->Text[0] = '\0';
+ pss->LastText[0] = '\0';
+ pss->FirstIndex = -1;
+ SortStarsOnName (pss);
+
+ // text entry setup
+ tes.Initialized = FALSE;
+ tes.BaseStr = pss->Text;
+ tes.MaxSize = sizeof (pss->Text);
+ tes.CursorPos = 0;
+ tes.CbParam = pss;
+ tes.ChangeCallback = OnStarNameChange;
+ tes.FrameCallback = OnStarNameFrame;
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetDefaultMenuRepeatDelay ();
+ success = DoTextEntry (&tes);
+
+ DrawSISMessageEx (pss->Text, -1, -1, DSME_CLEARFR);
+
+ HFree (pss);
+
+ return success;
+}
+
+static BOOLEAN
+DoMoveCursor (MENU_STATE *pMS)
+{
+#define MIN_ACCEL_DELAY (ONE_SECOND / 60)
+#define MAX_ACCEL_DELAY (ONE_SECOND / 8)
+#define STEP_ACCEL_DELAY (ONE_SECOND / 120)
+ static UNICODE last_buf[CURSOR_INFO_BUFSIZE];
+ DWORD TimeIn = GetTimeCounter ();
+
+ if (!pMS->Initialized)
+ {
+ POINT universe;
+
+ pMS->Initialized = TRUE;
+ pMS->InputFunc = DoMoveCursor;
+
+ if (!inHQSpace ())
+ universe = CurStarDescPtr->star_pt;
+ else
+ {
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+ flashCurrentLocation (&universe);
+
+ last_buf[0] = '\0';
+ UpdateCursorInfo (last_buf);
+ UpdateFuelRequirement ();
+
+ return TRUE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ GLOBAL (autopilot) = cursorLoc;
+#ifdef DEBUG
+ if (instantMove)
+ {
+ PlayMenuSound (MENU_SOUND_INVOKED);
+
+ if (inHQSpace ())
+ {
+ // Move to the new location immediately.
+ doInstantMove ();
+ }
+ else if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
+ {
+ // We're in a solar system; exit it.
+ GLOBAL (CurrentActivity) |= END_INTERPLANETARY;
+
+ // Set a hook to move to the new location:
+ debugHook = doInstantMove;
+ }
+
+ return FALSE;
+ }
+#endif
+ DrawStarMap (0, NULL);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SEARCH])
+ {
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ { // HyperSpace search
+ POINT oldpt = cursorLoc;
+
+ if (!DoStarSearch (pMS))
+ { // search failed or canceled - return cursor
+ UpdateCursorLocation (0, 0, &oldpt);
+ }
+ // make sure cmp fails
+ strcpy (last_buf, " <random garbage> ");
+ UpdateCursorInfo (last_buf);
+ UpdateFuelRequirement ();
+
+ SetMenuRepeatDelay (MIN_ACCEL_DELAY, MAX_ACCEL_DELAY,
+ STEP_ACCEL_DELAY, TRUE);
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ }
+ else
+ { // no search in QuasiSpace
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ else
+ {
+ SBYTE sx, sy;
+ SIZE ZoomIn, ZoomOut;
+
+ ZoomIn = ZoomOut = 0;
+ if (PulsedInputState.menu[KEY_MENU_ZOOM_IN])
+ ZoomIn = 1;
+ else if (PulsedInputState.menu[KEY_MENU_ZOOM_OUT])
+ ZoomOut = 1;
+
+ ZoomStarMap (ZoomIn - ZoomOut);
+
+ sx = sy = 0;
+ if (PulsedInputState.menu[KEY_MENU_LEFT]) sx = -1;
+ if (PulsedInputState.menu[KEY_MENU_RIGHT]) sx = 1;
+ if (PulsedInputState.menu[KEY_MENU_UP]) sy = -1;
+ if (PulsedInputState.menu[KEY_MENU_DOWN]) sy = 1;
+
+ if (sx != 0 || sy != 0)
+ {
+ UpdateCursorLocation (sx, sy, NULL);
+ UpdateCursorInfo (last_buf);
+ UpdateFuelRequirement ();
+ }
+
+ SleepThreadUntil (TimeIn + MIN_ACCEL_DELAY);
+ }
+
+ flashCurrentLocation (NULL);
+
+ return !(GLOBAL (CurrentActivity) & CHECK_ABORT);
+}
+
+static void
+RepairMap (COUNT update_race, RECT *pLastRect, RECT *pNextRect)
+{
+ RECT r;
+
+ /* make a rect big enough for text */
+ r.extent.width = 50;
+ r.corner.x = (pNextRect->corner.x + (pNextRect->extent.width >> 1))
+ - (r.extent.width >> 1);
+ if (r.corner.x < 0)
+ r.corner.x = 0;
+ else if (r.corner.x + r.extent.width >= SIS_SCREEN_WIDTH)
+ r.corner.x = SIS_SCREEN_WIDTH - r.extent.width;
+ r.extent.height = 9;
+ r.corner.y = (pNextRect->corner.y + (pNextRect->extent.height >> 1))
+ - r.extent.height;
+ if (r.corner.y < 0)
+ r.corner.y = 0;
+ else if (r.corner.y + r.extent.height >= SIS_SCREEN_HEIGHT)
+ r.corner.y = SIS_SCREEN_HEIGHT - r.extent.height;
+ BoxUnion (pLastRect, &r, &r);
+ BoxUnion (pNextRect, &r, &r);
+ *pLastRect = *pNextRect;
+
+ if (r.corner.x < 0)
+ {
+ r.extent.width += r.corner.x;
+ r.corner.x = 0;
+ }
+ if (r.corner.x + r.extent.width > SIS_SCREEN_WIDTH)
+ r.extent.width = SIS_SCREEN_WIDTH - r.corner.x;
+ if (r.corner.y < 0)
+ {
+ r.extent.height += r.corner.y;
+ r.corner.y = 0;
+ }
+ if (r.corner.y + r.extent.height > SIS_SCREEN_HEIGHT)
+ r.extent.height = SIS_SCREEN_HEIGHT - r.corner.y;
+
+ r.extent.height += r.corner.y & 1;
+ r.corner.y &= ~1;
+
+ DrawStarMap (update_race, &r);
+}
+
+static void
+UpdateMap (void)
+{
+ BYTE ButtonState, VisibleChange;
+ BOOLEAN MapDrawn, Interrupted;
+ COUNT index;
+ HFLEETINFO hStarShip, hNextShip;
+
+ FlushInput ();
+ ButtonState = 1; /* assume a button down */
+
+ MapDrawn = Interrupted = FALSE;
+ for (index = 1,
+ hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; ++index, hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if (ButtonState)
+ {
+ if (!AnyButtonPress (TRUE))
+ ButtonState = 0;
+ }
+ else if ((Interrupted = (BOOLEAN)(
+ Interrupted || AnyButtonPress (TRUE)
+ )))
+ MapDrawn = TRUE;
+
+ if (FleetPtr->known_strength)
+ {
+ SIZE dx, dy, delta;
+ RECT r, last_r, temp_r0, temp_r1;
+
+ dx = FleetPtr->loc.x - FleetPtr->known_loc.x;
+ dy = FleetPtr->loc.y - FleetPtr->known_loc.y;
+ if (dx || dy)
+ {
+ SIZE xincr, yincr,
+ xerror, yerror,
+ cycle;
+
+ if (dx >= 0)
+ xincr = 1;
+ else
+ {
+ xincr = -1;
+ dx = -dx;
+ }
+ dx <<= 1;
+
+ if (dy >= 0)
+ yincr = 1;
+ else
+ {
+ yincr = -1;
+ dy = -dy;
+ }
+ dy <<= 1;
+
+ if (dx >= dy)
+ cycle = dx;
+ else
+ cycle = dy;
+ delta = xerror = yerror = cycle >> 1;
+
+ if (!MapDrawn)
+ {
+ DrawStarMap ((COUNT)~0, NULL);
+ MapDrawn = TRUE;
+ }
+
+ GetSphereRect (FleetPtr, &temp_r0, &last_r);
+ ++last_r.extent.width;
+ ++last_r.extent.height;
+ VisibleChange = FALSE;
+ do
+ {
+ do
+ {
+ if ((xerror -= dx) <= 0)
+ {
+ FleetPtr->known_loc.x += xincr;
+ xerror += cycle;
+ }
+ if ((yerror -= dy) <= 0)
+ {
+ FleetPtr->known_loc.y += yincr;
+ yerror += cycle;
+ }
+ GetSphereRect (FleetPtr, &temp_r1, &r);
+ } while (delta--
+ && ((delta & 0x1F)
+ || (temp_r0.corner.x == temp_r1.corner.x
+ && temp_r0.corner.y == temp_r1.corner.y)));
+
+ if (ButtonState)
+ {
+ if (!AnyButtonPress (TRUE))
+ ButtonState = 0;
+ }
+ else if ((Interrupted = (BOOLEAN)(
+ Interrupted || AnyButtonPress (TRUE)
+ )))
+ {
+ MapDrawn = TRUE;
+ goto DoneSphereMove;
+ }
+
+ ++r.extent.width;
+ ++r.extent.height;
+ if (temp_r0.corner.x != temp_r1.corner.x
+ || temp_r0.corner.y != temp_r1.corner.y)
+ {
+ VisibleChange = TRUE;
+ RepairMap (index, &last_r, &r);
+ }
+ } while (delta >= 0);
+ if (VisibleChange)
+ RepairMap ((COUNT)~0, &last_r, &r);
+
+DoneSphereMove:
+ FleetPtr->known_loc = FleetPtr->loc;
+ }
+
+ delta = FleetPtr->actual_strength - FleetPtr->known_strength;
+ if (delta)
+ {
+ if (!MapDrawn)
+ {
+ DrawStarMap ((COUNT)~0, NULL);
+ MapDrawn = TRUE;
+ }
+
+ if (delta > 0)
+ dx = 1;
+ else
+ {
+ delta = -delta;
+ dx = -1;
+ }
+ --delta;
+
+ GetSphereRect (FleetPtr, &temp_r0, &last_r);
+ ++last_r.extent.width;
+ ++last_r.extent.height;
+ VisibleChange = FALSE;
+ do
+ {
+ do
+ {
+ FleetPtr->known_strength += dx;
+ GetSphereRect (FleetPtr, &temp_r1, &r);
+ } while (delta--
+ && ((delta & 0xF)
+ || temp_r0.extent.height == temp_r1.extent.height));
+
+ if (ButtonState)
+ {
+ if (!AnyButtonPress (TRUE))
+ ButtonState = 0;
+ }
+ else if ((Interrupted = (BOOLEAN)(
+ Interrupted || AnyButtonPress (TRUE)
+ )))
+ {
+ MapDrawn = TRUE;
+ goto DoneSphereGrowth;
+ }
+ ++r.extent.width;
+ ++r.extent.height;
+ if (temp_r0.extent.height != temp_r1.extent.height)
+ {
+ VisibleChange = TRUE;
+ RepairMap (index, &last_r, &r);
+ }
+ } while (delta >= 0);
+ if (VisibleChange || temp_r0.extent.width != temp_r1.extent.width)
+ RepairMap ((COUNT)~0, &last_r, &r);
+
+DoneSphereGrowth:
+ FleetPtr->known_strength = FleetPtr->actual_strength;
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+}
+
+BOOLEAN
+StarMap (void)
+{
+ MENU_STATE MenuState;
+ POINT universe;
+ //FRAME OldFrame;
+ RECT clip_r;
+ CONTEXT OldContext;
+
+ memset (&MenuState, 0, sizeof (MenuState));
+
+ zoomLevel = 0;
+ mapOrigin.x = MAX_X_UNIVERSE >> 1;
+ mapOrigin.y = MAX_Y_UNIVERSE >> 1;
+ StarMapFrame = SetAbsFrameIndex (MiscDataFrame, 48);
+
+ if (!inHQSpace ())
+ universe = CurStarDescPtr->star_pt;
+ else
+ {
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+
+ cursorLoc = GLOBAL (autopilot);
+ if (cursorLoc.x == ~0 && cursorLoc.y == ~0)
+ cursorLoc = universe;
+
+ MenuState.InputFunc = DoMoveCursor;
+ MenuState.Initialized = FALSE;
+
+ transition_pending = TRUE;
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ UpdateMap ();
+
+ DrawStarMap (0, (RECT*)-1);
+ transition_pending = FALSE;
+
+ BatchGraphics ();
+ OldContext = SetContext (SpaceContext);
+ GetContextClipRect (&clip_r);
+ SetContext (OldContext);
+ LoadIntoExtraScreen (&clip_r);
+ DrawCursor (UNIVERSE_TO_DISPX (cursorLoc.x),
+ UNIVERSE_TO_DISPY (cursorLoc.y));
+ UnbatchGraphics ();
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ SetMenuRepeatDelay (MIN_ACCEL_DELAY, MAX_ACCEL_DELAY, STEP_ACCEL_DELAY,
+ TRUE);
+ DoInput (&MenuState, FALSE);
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetDefaultMenuRepeatDelay ();
+
+ DrawHyperCoords (universe);
+ DrawSISMessage (NULL);
+ DrawStatusMessage (NULL);
+
+ if (GLOBAL (autopilot.x) == universe.x
+ && GLOBAL (autopilot.y) == universe.y)
+ GLOBAL (autopilot.x) = GLOBAL (autopilot.y) = ~0;
+
+ return (GLOBAL (autopilot.x) != ~0
+ && GLOBAL (autopilot.y) != ~0);
+}
+
diff --git a/src/uqm/planets/report.c b/src/uqm/planets/report.c
new file mode 100644
index 0000000..5defbe7
--- /dev/null
+++ b/src/uqm/planets/report.c
@@ -0,0 +1,271 @@
+//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 "lander.h"
+#include "scan.h"
+#include "planets.h"
+#include "../colors.h"
+#include "../controls.h"
+#include "../gamestr.h"
+#include "../setup.h"
+#include "../util.h"
+#include "../sounds.h"
+#include "../uqmdebug.h"
+#include "options.h"
+#include "libs/inplib.h"
+
+#include <ctype.h>
+#include <string.h>
+
+
+#define NUM_CELL_COLS MAP_WIDTH / 6
+#define NUM_CELL_ROWS MAP_HEIGHT / 6
+#define MAX_CELL_COLS 40
+
+extern FRAME SpaceJunkFrame;
+
+static void
+ClearReportArea (void)
+{
+ COUNT x, y;
+ RECT r;
+ STAMP s;
+ COORD startx;
+
+ if (optWhichFonts == OPT_PC)
+ s.frame = SetAbsFrameIndex (SpaceJunkFrame, 21);
+ else
+ s.frame = SetAbsFrameIndex (SpaceJunkFrame, 18);
+ GetFrameRect (s.frame, &r);
+
+ BatchGraphics ();
+
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x07, 0x00), 0x57));
+
+ startx = 1 + (r.extent.width >> 1) - 1;
+ s.origin.y = 1;
+ for (y = 0; y < NUM_CELL_ROWS; ++y)
+ {
+ s.origin.x = startx;
+ for (x = 0; x < NUM_CELL_COLS; ++x)
+ {
+ if (optWhichFonts == OPT_PC)
+ DrawStamp (&s);
+ else
+ DrawFilledStamp (&s);
+
+ s.origin.x += r.extent.width + 1;
+ }
+ s.origin.y += r.extent.height + 1;
+ }
+
+ UnbatchGraphics ();
+}
+
+static void
+MakeReport (SOUND ReadOutSounds, UNICODE *pStr, COUNT StrLen)
+{
+ BYTE ButtonState;
+ int end_page_len;
+ UNICODE end_page_buf[200];
+ UniChar last_c = 0;
+ COUNT row_cells;
+ BOOLEAN Sleepy;
+ RECT r;
+ TEXT t;
+
+ sprintf (end_page_buf, "%s\n", GAME_STRING (SCAN_STRING_BASE + NUM_SCAN_TYPES));
+ end_page_len = utf8StringCount (end_page_buf);
+
+ GetFrameRect (SetAbsFrameIndex (SpaceJunkFrame, 18), &r);
+
+ t.align = ALIGN_LEFT;
+ t.CharCount = 1;
+ t.pStr = pStr;
+
+ Sleepy = TRUE;
+
+ FlushInput ();
+ // XXX: this is a pretty ugly goto
+ goto InitPageCell;
+
+ while (StrLen)
+ {
+ COUNT col_cells;
+ const UNICODE *pLastStr;
+ const UNICODE *pNextStr;
+ COUNT lf_pos;
+
+ pLastStr = t.pStr;
+
+ // scan for LFs in the remaining string
+ // trailing LF will be ignored
+ for (lf_pos = StrLen, pNextStr = t.pStr;
+ lf_pos && getCharFromString (&pNextStr) != '\n';
+ --lf_pos)
+ ;
+
+ col_cells = 0;
+ // check if the remaining text fits on current screen
+ if (row_cells == NUM_CELL_ROWS - 1
+ && (StrLen > NUM_CELL_COLS || lf_pos > 1))
+ {
+ col_cells = (NUM_CELL_COLS >> 1) - (end_page_len >> 1);
+ t.pStr = end_page_buf;
+ StrLen += end_page_len;
+ }
+ t.baseline.x = 1 + (r.extent.width >> 1)
+ + (col_cells * (r.extent.width + 1)) - 1;
+ do
+ {
+ COUNT word_chars;
+ const UNICODE *pStr;
+ UniChar c;
+
+ pStr = t.pStr;
+ pNextStr = t.pStr;
+ while (UniChar_isGraph (getCharFromString (&pNextStr)))
+ pStr = pNextStr;
+
+ word_chars = utf8StringCountN (t.pStr, pStr);
+ if ((col_cells += word_chars) <= NUM_CELL_COLS)
+ {
+ TimeCount TimeOut;
+
+ if (StrLen -= word_chars)
+ --StrLen;
+ TimeOut = GetTimeCounter ();
+ while (word_chars--)
+ {
+ pNextStr = t.pStr;
+ c = getCharFromString (&pNextStr);
+
+ if (!Sleepy || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ font_DrawText (&t);
+ else
+ {
+ font_DrawText (&t);
+
+ PlaySound (ReadOutSounds, NotPositional (), NULL,
+ GAME_SOUND_PRIORITY);
+
+ if (c == ',')
+ TimeOut += ONE_SECOND / 4;
+ if (c == '.' || c == '!' || c == '?')
+ TimeOut += ONE_SECOND / 2;
+ else
+ TimeOut += ONE_SECOND / 20;
+ if (word_chars == 0)
+ TimeOut += ONE_SECOND / 20;
+
+ if (WaitForAnyButtonUntil (TRUE, TimeOut, FALSE))
+ {
+ Sleepy = FALSE;
+ // We draw the whole thing at once after this
+ BatchGraphics ();
+ }
+ }
+ t.pStr = pNextStr;
+ t.baseline.x += r.extent.width + 1;
+ }
+
+ ++col_cells;
+ last_c = getCharFromString (&t.pStr);
+ t.baseline.x += r.extent.width + 1;
+ }
+ } while (col_cells <= NUM_CELL_COLS && last_c != '\n' && StrLen);
+
+ t.baseline.y += r.extent.height + 1;
+ if (++row_cells == NUM_CELL_ROWS || StrLen == 0)
+ {
+ t.pStr = pLastStr;
+ if (!Sleepy)
+ {
+ UnbatchGraphics ();
+ }
+
+ if (!WaitForAnyButton (TRUE, WAIT_INFINITE, FALSE))
+ break;
+
+InitPageCell:
+ ButtonState = 1;
+ t.baseline.y = r.extent.height + 1;
+ row_cells = 0;
+ if (StrLen)
+ {
+ if (!Sleepy)
+ BatchGraphics ();
+ ClearReportArea();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1F, 0x00), 0xFF));
+ }
+ }
+ }
+}
+
+void
+DoDiscoveryReport (SOUND ReadOutSounds)
+{
+ CONTEXT OldContext;
+ CONTEXT context;
+ BOOLEAN ownContext;
+ STAMP saveStamp;
+
+#ifdef DEBUG
+ if (disableInteractivity)
+ return;
+#endif
+
+ context = GetScanContext (&ownContext);
+ OldContext = SetContext (context);
+ saveStamp = SaveContextFrame (NULL);
+ {
+ FONT OldFont;
+ FRAME OldFontEffect;
+
+ OldFont = SetContextFont (
+ pSolarSysState->SysInfo.PlanetInfo.LanderFont);
+ if (optWhichFonts == OPT_PC)
+ OldFontEffect = SetContextFontEffect (
+ pSolarSysState->SysInfo.PlanetInfo.LanderFontEff);
+ else
+ OldFontEffect = SetContextFontEffect (NULL);
+
+ MakeReport (ReadOutSounds,
+ (UNICODE *)GetStringAddress (pSolarSysState->SysInfo.PlanetInfo.DiscoveryString),
+ GetStringLength (pSolarSysState->SysInfo.PlanetInfo.DiscoveryString));
+
+ SetContextFontEffect (OldFontEffect);
+ SetContextFont (OldFont);
+ }
+ // Restore previous screen
+ DrawStamp (&saveStamp);
+ SetContext (OldContext);
+ // TODO: Make CONTEXT ref-counted
+ if (ownContext)
+ DestroyScanContext ();
+
+ DestroyDrawable (ReleaseDrawable (saveStamp.frame));
+
+ WaitForNoInput (WAIT_INFINITE, TRUE);
+}
+
+
diff --git a/src/uqm/planets/roster.c b/src/uqm/planets/roster.c
new file mode 100644
index 0000000..663ac28
--- /dev/null
+++ b/src/uqm/planets/roster.c
@@ -0,0 +1,428 @@
+//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 "../build.h"
+#include "../colors.h"
+#include "../controls.h"
+#include "../races.h"
+#include "../units.h"
+#include "../sis.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "port.h"
+#include "libs/gfxlib.h"
+#include "libs/tasklib.h"
+
+#include <stdlib.h>
+
+// Ship icon positions in status display around the flagship
+static const POINT ship_pos[MAX_BUILT_SHIPS] =
+{
+ SUPPORT_SHIP_PTS
+};
+
+typedef struct
+{
+ // Ship icon positions split into (lower half) left and right (upper)
+ // and sorted in the Y coord. These are used for navigation around the
+ // escort positions.
+ POINT shipPos[MAX_BUILT_SHIPS];
+ COUNT count;
+ // Number of ships
+
+ POINT curShipPt;
+ // Location of the currently selected escort
+ FRAME curShipFrame;
+ // Icon of the currently selected escort
+ bool modifyingCrew;
+ // true when in crew modification "sub-menu". This is simple
+ // enough that it does not require a real sub-menu.
+} ROSTER_STATE;
+
+static SHIP_FRAGMENT* LockSupportShip (ROSTER_STATE *, HSHIPFRAG *phFrag);
+
+static void
+drawSupportShip (ROSTER_STATE *rosterState, bool filled)
+{
+ STAMP s;
+
+ if (!rosterState->curShipFrame)
+ return;
+
+ s.origin = rosterState->curShipPt;
+ s.frame = rosterState->curShipFrame;
+
+ if (filled)
+ DrawFilledStamp (&s);
+ else
+ DrawStamp (&s);
+}
+
+static void
+getSupportShipIcon (ROSTER_STATE *rosterState)
+{
+ HSHIPFRAG hShipFrag;
+ SHIP_FRAGMENT *ShipFragPtr;
+
+ rosterState->curShipFrame = NULL;
+ ShipFragPtr = LockSupportShip (rosterState, &hShipFrag);
+ if (!ShipFragPtr)
+ return;
+
+ rosterState->curShipFrame = ShipFragPtr->icons;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hShipFrag);
+}
+
+static void
+flashSupportShip (ROSTER_STATE *rosterState)
+{
+ static Color c = BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x24);
+ static TimeCount NextTime = 0;
+
+ if (GetTimeCounter () >= NextTime)
+ {
+ NextTime = GetTimeCounter () + (ONE_SECOND / 15);
+
+ /* The commented code out code is the old code before the switch
+ * to 24-bits colors. The current code produces very slightly
+ * different colors due to rounding errors, but the old code wasn't
+ * original anyhow, and you can't tell the difference visually.
+ * - SvdB
+ if (c >= BUILD_COLOR (MAKE_RGB15 (0x1F, 0x19, 0x19), 0x24))
+ c = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x00, 0x00), 0x24);
+ else
+ c += BUILD_COLOR (MAKE_RGB15 (0x00, 0x02, 0x02), 0x00);
+ */
+
+ if (c.g >= CC5TO8 (0x19))
+ {
+ c = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x00, 0x00), 0x24);
+ }
+ else
+ {
+ c.g += CC5TO8 (0x02);
+ c.b += CC5TO8 (0x02);
+ }
+ SetContextForeGroundColor (c);
+
+ drawSupportShip (rosterState, TRUE);
+ }
+}
+
+static SHIP_FRAGMENT *
+LockSupportShip (ROSTER_STATE *rosterState, HSHIPFRAG *phFrag)
+{
+ const POINT *pship_pos;
+ HSHIPFRAG hStarShip, hNextShip;
+
+ // Lookup the current escort's location in the unsorted points list
+ // to find the original escort index
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)),
+ pship_pos = ship_pos;
+ hStarShip; hStarShip = hNextShip, ++pship_pos)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (pointsEqual (*pship_pos, rosterState->curShipPt))
+ {
+ *phFrag = hStarShip;
+ return StarShipPtr;
+ }
+
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ return NULL;
+}
+
+static void
+flashSupportShipCrew (void)
+{
+ RECT r;
+
+ SetContext (StatusContext);
+ GetStatusMessageRect (&r);
+ SetFlashRect (&r);
+}
+
+static BOOLEAN
+DeltaSupportCrew (ROSTER_STATE *rosterState, SIZE crew_delta)
+{
+ BOOLEAN ret = FALSE;
+ UNICODE buf[40];
+ HFLEETINFO hTemplate;
+ HSHIPFRAG hShipFrag;
+ SHIP_FRAGMENT *StarShipPtr;
+ FLEET_INFO *TemplatePtr;
+
+ StarShipPtr = LockSupportShip (rosterState, &hShipFrag);
+ if (!StarShipPtr)
+ return FALSE;
+
+ hTemplate = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ StarShipPtr->race_id);
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+
+ StarShipPtr->crew_level += crew_delta;
+
+ if (StarShipPtr->crew_level == 0)
+ StarShipPtr->crew_level = 1;
+ else if (StarShipPtr->crew_level > TemplatePtr->crew_level &&
+ crew_delta > 0)
+ StarShipPtr->crew_level -= crew_delta;
+ else
+ {
+ if (StarShipPtr->crew_level >= TemplatePtr->crew_level)
+ sprintf (buf, "%u", StarShipPtr->crew_level);
+ else
+ sprintf (buf, "%u/%u",
+ StarShipPtr->crew_level,
+ TemplatePtr->crew_level);
+
+ PreUpdateFlashRect ();
+ DrawStatusMessage (buf);
+ PostUpdateFlashRect ();
+ DeltaSISGauges (-crew_delta, 0, 0);
+ if (crew_delta)
+ {
+ flashSupportShipCrew ();
+ }
+ ret = TRUE;
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hShipFrag);
+
+ return ret;
+}
+
+static void
+drawModifiedSupportShip (ROSTER_STATE *rosterState)
+{
+ SetContext (StatusContext);
+ SetContextForeGroundColor (ROSTER_MODIFY_SHIP_COLOR);
+ drawSupportShip (rosterState, TRUE);
+}
+
+static void
+selectSupportShip (ROSTER_STATE *rosterState, COUNT shipIndex)
+{
+ rosterState->curShipPt = rosterState->shipPos[shipIndex];
+ getSupportShipIcon (rosterState);
+ DeltaSupportCrew (rosterState, 0);
+}
+
+static BOOLEAN
+DoModifyRoster (MENU_STATE *pMS)
+{
+ ROSTER_STATE *rosterState = pMS->privData;
+ BOOLEAN select, cancel, up, down, horiz;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+ up = PulsedInputState.menu[KEY_MENU_UP];
+ down = PulsedInputState.menu[KEY_MENU_DOWN];
+ // Left or right produces the same effect because there are 2 columns
+ horiz = PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_RIGHT];
+
+ if (cancel && !rosterState->modifyingCrew)
+ {
+ return FALSE;
+ }
+ else if (select || cancel)
+ {
+ rosterState->modifyingCrew ^= true;
+ if (!rosterState->modifyingCrew)
+ {
+ SetFlashRect (NULL);
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ }
+ else
+ {
+ drawModifiedSupportShip (rosterState);
+ flashSupportShipCrew ();
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN,
+ MENU_SOUND_SELECT | MENU_SOUND_CANCEL);
+ }
+ }
+ else if (rosterState->modifyingCrew)
+ {
+ SIZE delta = 0;
+ BOOLEAN failed = FALSE;
+
+ if (up)
+ {
+ if (GLOBAL_SIS (CrewEnlisted))
+ delta = 1;
+ else
+ failed = TRUE;
+ }
+ else if (down)
+ {
+ if (GLOBAL_SIS (CrewEnlisted) < GetCrewPodCapacity ())
+ delta = -1;
+ else
+ failed = TRUE;
+ }
+
+ if (delta != 0)
+ {
+ failed = !DeltaSupportCrew (rosterState, delta);
+ }
+
+ if (failed)
+ { // not enough room or crew
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ else
+ {
+ COUNT NewState;
+ POINT *pship_pos = rosterState->shipPos;
+ COUNT top_right = (rosterState->count + 1) >> 1;
+
+ NewState = pMS->CurState;
+
+ if (rosterState->count < 2)
+ {
+ // no navigation allowed
+ }
+ else if (horiz)
+ {
+ if (NewState == top_right - 1)
+ NewState = rosterState->count - 1;
+ else if (NewState >= top_right)
+ {
+ NewState -= top_right;
+ if (pship_pos[NewState].y < pship_pos[pMS->CurState].y)
+ ++NewState;
+ }
+ else
+ {
+ NewState += top_right;
+ if (NewState != top_right
+ && pship_pos[NewState].y > pship_pos[pMS->CurState].y)
+ --NewState;
+ }
+ }
+ else if (down)
+ {
+ ++NewState;
+ if (NewState == rosterState->count)
+ NewState = top_right;
+ else if (NewState == top_right)
+ NewState = 0;
+ }
+ else if (up)
+ {
+ if (NewState == 0)
+ NewState = top_right - 1;
+ else if (NewState == top_right)
+ NewState = rosterState->count - 1;
+ else
+ --NewState;
+ }
+
+ BatchGraphics ();
+ SetContext (StatusContext);
+
+ if (NewState != pMS->CurState)
+ {
+ // Draw the previous escort in unselected state
+ drawSupportShip (rosterState, FALSE);
+ // Select the new one
+ selectSupportShip (rosterState, NewState);
+ pMS->CurState = NewState;
+ }
+
+ flashSupportShip (rosterState);
+
+ UnbatchGraphics ();
+ }
+
+ SleepThread (ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static int
+compShipPos (const void *ptr1, const void *ptr2)
+{
+ const POINT *pt1 = (const POINT *) ptr1;
+ const POINT *pt2 = (const POINT *) ptr2;
+
+ // Ships on the left in the lower half
+ if (pt1->x < pt2->x)
+ return -1;
+ else if (pt1->x > pt2->x)
+ return 1;
+
+ // and ordered on Y
+ if (pt1->y < pt2->y)
+ return -1;
+ else if (pt1->y > pt2->y)
+ return 1;
+ else
+ return 0;
+}
+
+BOOLEAN
+RosterMenu (void)
+{
+ MENU_STATE MenuState;
+ ROSTER_STATE RosterState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.privData = &RosterState;
+
+ memset (&RosterState, 0, sizeof RosterState);
+
+ RosterState.count = CountLinks (&GLOBAL (built_ship_q));
+ if (!RosterState.count)
+ return FALSE;
+
+ // Get the escort positions we will use and sort on X then Y
+ assert (sizeof (RosterState.shipPos) == sizeof (ship_pos));
+ memcpy (RosterState.shipPos, ship_pos, sizeof (ship_pos));
+ qsort (RosterState.shipPos, RosterState.count,
+ sizeof (RosterState.shipPos[0]), compShipPos);
+
+ SetContext (StatusContext);
+ selectSupportShip (&RosterState, MenuState.CurState);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoModifyRoster;
+ DoInput (&MenuState, TRUE);
+
+ SetContext (StatusContext);
+ // unselect the last ship
+ drawSupportShip (&RosterState, FALSE);
+ DrawStatusMessage (NULL);
+
+ return TRUE;
+}
+
diff --git a/src/uqm/planets/scan.c b/src/uqm/planets/scan.c
new file mode 100644
index 0000000..3d5d9fd
--- /dev/null
+++ b/src/uqm/planets/scan.c
@@ -0,0 +1,1385 @@
+//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 "lander.h"
+#include "lifeform.h"
+#include "scan.h"
+#include "../build.h"
+#include "../colors.h"
+#include "../cons_res.h"
+#include "../controls.h"
+#include "../menustat.h"
+#include "../encount.h"
+ // for EncounterGroup
+#include "../gamestr.h"
+#include "../nameref.h"
+#include "../resinst.h"
+#include "../settings.h"
+#include "../util.h"
+#include "../process.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../state.h"
+#include "../sis.h"
+#include "../save.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/inplib.h"
+#include "libs/mathlib.h"
+
+extern FRAME SpaceJunkFrame;
+
+// define SPIN_ON_SCAN to allow the planet to spin
+// while scaning is going on
+#undef SPIN_ON_SCAN
+
+#define FLASH_INDEX 105
+
+static CONTEXT ScanContext;
+
+static POINT planetLoc;
+static RECT cursorRect;
+static FRAME eraseFrame;
+
+// ScanSystem() menu items
+// The first three are from enum PlanetScanTypes in planets.h
+enum ScanMenuItems
+{
+ EXIT_SCAN = NUM_SCAN_TYPES,
+ AUTO_SCAN,
+ DISPATCH_SHUTTLE,
+};
+
+
+void
+RepairBackRect (RECT *pRect)
+{
+ RECT new_r, old_r;
+
+ GetContextClipRect (&old_r);
+ new_r.corner.x = pRect->corner.x + old_r.corner.x;
+ new_r.corner.y = pRect->corner.y + old_r.corner.y;
+ new_r.extent = pRect->extent;
+
+ new_r.extent.height += new_r.corner.y & 1;
+ new_r.corner.y &= ~1;
+ DrawFromExtraScreen (&new_r);
+}
+
+static void
+EraseCoarseScan (void)
+{
+ SetContext (PlanetContext);
+
+ BatchGraphics ();
+ DrawStarBackGround ();
+ DrawDefaultPlanetSphere ();
+ UnbatchGraphics ();
+}
+
+static void
+PrintScanTitlePC (TEXT *t, RECT *r, const char *txt, int xpos)
+{
+ t->baseline.x = xpos;
+ SetContextForeGroundColor (SCAN_PC_TITLE_COLOR);
+ t->pStr = txt;
+ t->CharCount = (COUNT)~0;
+ font_DrawText (t);
+ TextRect (t, r, NULL);
+ t->baseline.x += r->extent.width;
+ SetContextForeGroundColor (SCAN_INFO_COLOR);
+}
+
+static void
+MakeScanValue (UNICODE *buf, long val, const UNICODE *extra)
+{
+ if (val >= 10 * 100)
+ { // 1 decimal place
+ sprintf (buf, "%ld.%ld%s", val / 100, (val / 10) % 10, extra);
+ }
+ else
+ { // 2 decimal places
+ sprintf (buf, "%ld.%02ld%s", val / 100, val % 100, extra);
+ }
+}
+
+static void
+GetPlanetTitle (UNICODE *buf, COUNT bufsize)
+{
+ int val;
+ UNICODE *named = GetNamedPlanetaryBody ();
+ if (named)
+ {
+ utf8StringCopy (buf, bufsize, named);
+ return;
+ }
+
+ // Unnamed body, use world type
+ val = pSolarSysState->pOrbitalDesc->data_index & ~PLANET_SHIELDED;
+ if (val >= FIRST_GAS_GIANT)
+ {
+ sprintf (buf, "%s", GAME_STRING (SCAN_STRING_BASE + 4 + 51));
+ // Gas Giant
+ }
+ else
+ {
+ sprintf (buf, "%s %s",
+ GAME_STRING (SCAN_STRING_BASE + 4 + val),
+ GAME_STRING (SCAN_STRING_BASE + 4 + 50));
+ // World
+ }
+}
+
+static void
+PrintCoarseScanPC (void)
+{
+#define SCAN_LEADING_PC 14
+ SDWORD val;
+ TEXT t;
+ RECT r;
+ UNICODE buf[200];
+
+ GetPlanetTitle (buf, sizeof (buf));
+
+ SetContext (PlanetContext);
+
+ t.align = ALIGN_CENTER;
+ t.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ t.baseline.y = 13;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (SCAN_PC_TITLE_COLOR);
+ SetContextFont (MicroFont);
+ font_DrawText (&t);
+
+ SetContextFont (TinyFont);
+
+#define LEFT_SIDE_BASELINE_X_PC 5
+#define RIGHT_SIDE_BASELINE_X_PC (SIS_SCREEN_WIDTH - 75)
+#define SCAN_BASELINE_Y_PC 40
+
+ t.baseline.y = SCAN_BASELINE_Y_PC;
+ t.align = ALIGN_LEFT;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE),
+ LEFT_SIDE_BASELINE_X_PC); // "Orbit: "
+ val = ((pSolarSysState->SysInfo.PlanetInfo.PlanetToSunDist * 100L
+ + (EARTH_RADIUS >> 1)) / EARTH_RADIUS);
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 1)); // " a.u."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 2),
+ LEFT_SIDE_BASELINE_X_PC); // "Atmo: "
+ if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == GAS_GIANT_ATMOSPHERE)
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (ORBITSCAN_STRING_BASE + 3)); // "Super Thick"
+ else if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == 0)
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (ORBITSCAN_STRING_BASE + 4)); // "Vacuum"
+ else
+ {
+ val = (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity * 100
+ + (EARTH_ATMOSPHERE >> 1)) / EARTH_ATMOSPHERE;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 5)); // " atm"
+ }
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 6),
+ LEFT_SIDE_BASELINE_X_PC); // "Temp: "
+ sprintf (buf, "%d" STR_DEGREE_SIGN " c",
+ pSolarSysState->SysInfo.PlanetInfo.SurfaceTemperature);
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 7),
+ LEFT_SIDE_BASELINE_X_PC); // "Weather: "
+ if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == 0)
+ t.pStr = GAME_STRING (ORBITSCAN_STRING_BASE + 8); // "None"
+ else
+ {
+ sprintf (buf, "%s %u",
+ GAME_STRING (ORBITSCAN_STRING_BASE + 9), // "Class"
+ pSolarSysState->SysInfo.PlanetInfo.Weather + 1);
+ t.pStr = buf;
+ }
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 10),
+ LEFT_SIDE_BASELINE_X_PC); // "Tectonics: "
+ if (PLANSIZE (pSolarSysState->SysInfo.PlanetInfo.PlanDataPtr->Type) ==
+ GAS_GIANT)
+ t.pStr = GAME_STRING (ORBITSCAN_STRING_BASE + 8); // "None"
+ else
+ {
+ sprintf (buf, "%s %u",
+ GAME_STRING (ORBITSCAN_STRING_BASE + 9), // "Class"
+ pSolarSysState->SysInfo.PlanetInfo.Tectonics + 1);
+ t.pStr = buf;
+ }
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ t.baseline.y = SCAN_BASELINE_Y_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 11),
+ RIGHT_SIDE_BASELINE_X_PC); // "Mass: "
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ val = ((DWORD) val * (DWORD) val * (DWORD) val / 100L
+ * pSolarSysState->SysInfo.PlanetInfo.PlanetDensity
+ + ((100L * 100L) >> 1)) / (100L * 100L);
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 12)); // " e.s."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 13),
+ RIGHT_SIDE_BASELINE_X_PC); // "Radius: "
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 12)); // " e.s."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 14),
+ RIGHT_SIDE_BASELINE_X_PC); // "Gravity: "
+ val = pSolarSysState->SysInfo.PlanetInfo.SurfaceGravity;
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 15)); // " g."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 16),
+ RIGHT_SIDE_BASELINE_X_PC); // "Day: "
+ val = (SDWORD)pSolarSysState->SysInfo.PlanetInfo.RotationPeriod
+ * 10 / 24;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 17)); // " days"
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 18),
+ RIGHT_SIDE_BASELINE_X_PC); // "Tilt: "
+ val = pSolarSysState->SysInfo.PlanetInfo.AxialTilt;
+ if (val < 0)
+ val = -val;
+ t.pStr = buf;
+ sprintf (buf, "%d" STR_DEGREE_SIGN, val);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+PrintCoarseScan3DO (void)
+{
+#define SCAN_LEADING 19
+ SDWORD val;
+ TEXT t;
+ STAMP s;
+ UNICODE buf[200];
+
+ GetPlanetTitle (buf, sizeof (buf));
+
+ SetContext (PlanetContext);
+
+ t.align = ALIGN_CENTER;
+ t.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ t.baseline.y = 13;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (SCAN_INFO_COLOR);
+ SetContextFont (MicroFont);
+ font_DrawText (&t);
+
+ s.origin.x = s.origin.y = 0;
+ s.origin.x = 16 - SAFE_X;
+ s.frame = SetAbsFrameIndex (SpaceJunkFrame, 20);
+ DrawStamp (&s);
+
+#define LEFT_SIDE_BASELINE_X (27 + (16 - SAFE_X))
+#define RIGHT_SIDE_BASELINE_X (SIS_SCREEN_WIDTH - LEFT_SIDE_BASELINE_X)
+#define SCAN_BASELINE_Y 25
+
+ t.baseline.x = LEFT_SIDE_BASELINE_X;
+ t.baseline.y = SCAN_BASELINE_Y;
+ t.align = ALIGN_LEFT;
+
+ t.pStr = buf;
+ val = ((pSolarSysState->SysInfo.PlanetInfo.PlanetToSunDist * 100L
+ + (EARTH_RADIUS >> 1)) / EARTH_RADIUS);
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == GAS_GIANT_ATMOSPHERE)
+ strcpy (buf, STR_INFINITY_SIGN);
+ else
+ {
+ val = (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity * 100
+ + (EARTH_ATMOSPHERE >> 1)) / EARTH_ATMOSPHERE;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ }
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ sprintf (buf, "%d" STR_DEGREE_SIGN,
+ pSolarSysState->SysInfo.PlanetInfo.SurfaceTemperature);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ sprintf (buf, "<%u>", pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == 0
+ ? 0 : (pSolarSysState->SysInfo.PlanetInfo.Weather + 1));
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ sprintf (buf, "<%u>",
+ PLANSIZE (
+ pSolarSysState->SysInfo.PlanetInfo.PlanDataPtr->Type
+ ) == GAS_GIANT
+ ? 0 : (pSolarSysState->SysInfo.PlanetInfo.Tectonics + 1));
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ t.baseline.x = RIGHT_SIDE_BASELINE_X;
+ t.baseline.y = SCAN_BASELINE_Y;
+ t.align = ALIGN_RIGHT;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ val = ((DWORD) val * (DWORD) val * (DWORD) val / 100L
+ * pSolarSysState->SysInfo.PlanetInfo.PlanetDensity
+ + ((100L * 100L) >> 1)) / (100L * 100L);
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.SurfaceGravity;
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.AxialTilt;
+ if (val < 0)
+ val = -val;
+ sprintf (buf, "%d" STR_DEGREE_SIGN, val);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = (SDWORD)pSolarSysState->SysInfo.PlanetInfo.RotationPeriod
+ * 10 / 24;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+initPlanetLocationImage (void)
+{
+ FRAME cursorFrame;
+
+ // Get the cursor image
+ cursorFrame = SetAbsFrameIndex (MiscDataFrame, FLASH_INDEX);
+ cursorRect.extent = GetFrameBounds (cursorFrame);
+}
+
+static void
+savePlanetLocationImage (void)
+{
+ RECT r;
+ FRAME cursorFrame = SetAbsFrameIndex (MiscDataFrame, FLASH_INDEX);
+ HOT_SPOT hs = GetFrameHot (cursorFrame);
+
+ DestroyDrawable (ReleaseDrawable (eraseFrame));
+
+ r = cursorRect;
+ r.corner.x -= hs.x;
+ r.corner.y -= hs.y;
+ eraseFrame = CaptureDrawable (CopyContextRect (&r));
+ SetFrameHot (eraseFrame, hs);
+}
+
+static void
+restorePlanetLocationImage (void)
+{
+ STAMP s;
+
+ s.origin = cursorRect.corner;
+ s.frame = eraseFrame; // saved image
+ DrawStamp (&s);
+}
+
+static void
+drawPlanetCursor (BOOLEAN filled)
+{
+ STAMP s;
+
+ s.origin = cursorRect.corner;
+ s.frame = SetAbsFrameIndex (MiscDataFrame, FLASH_INDEX);
+ if (filled)
+ DrawFilledStamp (&s);
+ else
+ DrawStamp (&s);
+}
+
+static void
+setPlanetCursorLoc (POINT new_pt)
+{
+ new_pt.x >>= MAG_SHIFT;
+ new_pt.y >>= MAG_SHIFT;
+ cursorRect.corner = new_pt;
+}
+
+static void
+setPlanetLoc (POINT new_pt, BOOLEAN restoreOld)
+{
+ planetLoc = new_pt;
+
+ SetContext (ScanContext);
+ if (restoreOld)
+ restorePlanetLocationImage ();
+ setPlanetCursorLoc (new_pt);
+ savePlanetLocationImage ();
+}
+
+static void
+flashPlanetLocation (void)
+{
+#define FLASH_FRAME_DELAY (ONE_SECOND / 16)
+ static BYTE c = 0x00;
+ static int val = -2;
+ static POINT prevPt;
+ static TimeCount NextTime = 0;
+ BOOLEAN locChanged;
+ TimeCount Now = GetTimeCounter ();
+
+ locChanged = prevPt.x != cursorRect.corner.x
+ || prevPt.y != cursorRect.corner.y;
+
+ if (!locChanged && Now < NextTime)
+ return; // nothing to do
+
+ if (locChanged)
+ { // Reset the flashing cycle
+ c = 0x00;
+ val = -2;
+ prevPt = cursorRect.corner;
+
+ NextTime = Now + FLASH_FRAME_DELAY;
+ }
+ else
+ { // Continue the flashing cycle
+ if (c == 0x00 || c == 0x1A)
+ val = -val;
+ c += val;
+
+ if (Now - NextTime > FLASH_FRAME_DELAY)
+ NextTime = Now + FLASH_FRAME_DELAY; // missed timing by too much
+ else
+ NextTime += FLASH_FRAME_DELAY; // stable frame rate
+ }
+
+ SetContext (ScanContext);
+ SetContextForeGroundColor (BUILD_COLOR (MAKE_RGB15 (c, c, c), c));
+ drawPlanetCursor (TRUE);
+}
+
+void
+RedrawSurfaceScan (const POINT *newLoc)
+{
+ CONTEXT OldContext;
+
+ OldContext = SetContext (ScanContext);
+
+ BatchGraphics ();
+ DrawPlanet (0, BLACK_COLOR);
+ DrawScannedObjects (TRUE);
+ if (newLoc)
+ {
+ setPlanetLoc (*newLoc, FALSE);
+ drawPlanetCursor (FALSE);
+ }
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static COUNT
+getLandingFuelNeeded (void)
+{
+ COUNT fuel;
+
+ fuel = pSolarSysState->SysInfo.PlanetInfo.SurfaceGravity << 1;
+ if (fuel > 3 * FUEL_TANK_SCALE)
+ fuel = 3 * FUEL_TANK_SCALE;
+
+ return fuel;
+}
+
+static void
+spawnFwiffo (void)
+{
+ HSHIPFRAG hStarShip;
+
+ EncounterGroup = 0;
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ hStarShip = CloneShipFragment (SPATHI_SHIP,
+ &GLOBAL (npc_built_ship_q), 1);
+ if (hStarShip)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (npc_built_ship_q),
+ hStarShip);
+ // Name Fwiffo
+ StarShipPtr->captains_name_index = NAME_OFFSET +
+ NUM_CAPTAINS_NAMES;
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+}
+
+// Returns TRUE if the parent menu should remain
+static BOOLEAN
+DispatchLander (void)
+{
+ InputFrameCallback *oldCallback;
+ SIZE landingFuel = getLandingFuelNeeded ();
+
+ EraseCoarseScan ();
+
+ // Deactivate planet rotation callback
+ oldCallback = SetInputCallback (NULL);
+
+ DeltaSISGauges (0, -landingFuel, 0);
+ SetContext (ScanContext);
+ drawPlanetCursor (FALSE);
+
+ PlanetSide (planetLoc);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI) == 1)
+ {
+ /* Create Fwiffo group and go into comm with it */
+ spawnFwiffo ();
+
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ SaveSolarSysLocation ();
+
+ return FALSE;
+ }
+
+ if (optWhichCoarseScan == OPT_PC)
+ PrintCoarseScanPC ();
+ else
+ PrintCoarseScan3DO ();
+
+ // Reactivate planet rotation callback
+ SetInputCallback (oldCallback);
+
+ return TRUE;
+}
+
+typedef struct
+{
+ bool success;
+ // true when player selected a location
+} PICK_PLANET_STATE;
+
+static BOOLEAN
+DoPickPlanetSide (MENU_STATE *pMS)
+{
+ PICK_PLANET_STATE *pickState = pMS->privData;
+ DWORD TimeIn = GetTimeCounter ();
+ BOOLEAN select, cancel;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pickState->success = false;
+ return FALSE;
+ }
+
+ if (cancel)
+ {
+ pickState->success = false;
+ return FALSE;
+ }
+ else if (select)
+ {
+ pickState->success = true;
+ return FALSE;
+ }
+ else
+ {
+ SIZE dx = 0;
+ SIZE dy = 0;
+ POINT new_pt;
+
+ new_pt = planetLoc;
+
+ if (CurrentInputState.menu[KEY_MENU_LEFT])
+ dx = -1;
+ if (CurrentInputState.menu[KEY_MENU_RIGHT])
+ dx = 1;
+ if (CurrentInputState.menu[KEY_MENU_UP])
+ dy = -1;
+ if (CurrentInputState.menu[KEY_MENU_DOWN])
+ dy = 1;
+
+ BatchGraphics ();
+
+ dx = dx << MAG_SHIFT;
+ if (dx)
+ {
+ new_pt.x += dx;
+ if (new_pt.x < 0)
+ new_pt.x += (MAP_WIDTH << MAG_SHIFT);
+ else if (new_pt.x >= (MAP_WIDTH << MAG_SHIFT))
+ new_pt.x -= (MAP_WIDTH << MAG_SHIFT);
+ }
+ dy = dy << MAG_SHIFT;
+ if (dy)
+ {
+ new_pt.y += dy;
+ if (new_pt.y < 0 || new_pt.y >= (MAP_HEIGHT << MAG_SHIFT))
+ new_pt.y = planetLoc.y;
+ }
+
+ if (!pointsEqual (new_pt, planetLoc))
+ {
+ setPlanetLoc (new_pt, TRUE);
+ }
+
+ flashPlanetLocation ();
+
+ UnbatchGraphics ();
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 40);
+ }
+
+ return TRUE;
+}
+
+static void
+drawLandingFuelUsage (COUNT fuel)
+{
+ UNICODE buf[100];
+
+ sprintf (buf, "%s%1.1f",
+ GAME_STRING (NAVIGATION_STRING_BASE + 5),
+ (float) fuel / FUEL_TANK_SCALE);
+ DrawStatusMessage (buf);
+}
+
+static void
+eraseLandingFuelUsage (void)
+{
+ DrawStatusMessage (NULL);
+}
+
+static BOOLEAN
+PickPlanetSide (void)
+{
+ MENU_STATE MenuState;
+ PICK_PLANET_STATE PickState;
+ COUNT fuel = getLandingFuelNeeded ();
+ BOOLEAN retval = TRUE;
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.privData = &PickState;
+
+ ClearSISRect (CLEAR_SIS_RADAR);
+ SetContext (ScanContext);
+ BatchGraphics ();
+ DrawPlanet (0, BLACK_COLOR);
+ DrawScannedObjects (FALSE);
+ UnbatchGraphics ();
+
+ drawLandingFuelUsage (fuel);
+ // Set the current flash location
+ setPlanetCursorLoc (planetLoc);
+ savePlanetLocationImage ();
+
+ InitLander (0);
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_SELECT);
+
+ PickState.success = false;
+ MenuState.InputFunc = DoPickPlanetSide;
+ DoInput (&MenuState, TRUE);
+
+ eraseLandingFuelUsage ();
+ if (PickState.success)
+ { // player chose a location
+ retval = DispatchLander ();
+ }
+ else
+ { // player bailed out
+ restorePlanetLocationImage ();
+ }
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ return retval;
+}
+
+#define NUM_FLASH_COLORS 8
+
+static void
+DrawScannedStuff (COUNT y, COUNT scan)
+{
+ HELEMENT hElement, hNextElement;
+ Color OldColor;
+
+ OldColor = SetContextForeGroundColor (BLACK_COLOR);
+
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+ SIZE dy;
+ STAMP s;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+
+ dy = y - ElementPtr->current.location.y;
+ if (LOBYTE (ElementPtr->scan_node) != scan || dy < 0)
+ { // node of wrong type, or not time for it yet
+ UnlockElement (hElement);
+ continue;
+ }
+
+ // XXX: flag this as 'found' scanned object
+ ElementPtr->state_flags |= APPEARING;
+
+ s.origin = ElementPtr->current.location;
+
+ if (dy >= NUM_FLASH_COLORS)
+ { // flashing done for this node, draw normal
+ s.frame = ElementPtr->next.image.frame;
+ DrawStamp (&s);
+ }
+ else
+ {
+ BYTE grad;
+ Color c = WHITE_COLOR;
+ COUNT nodeSize;
+
+ // mineral -- white --> turquoise?? (contrasts with red)
+ // energy -- white --> red (contrasts with white)
+ // bio -- white --> violet (contrasts with green)
+ grad = 0xff - 0xff * dy / (NUM_FLASH_COLORS - 1);
+ switch (scan)
+ {
+ case MINERAL_SCAN:
+ c.r = grad;
+ break;
+ case ENERGY_SCAN:
+ c.g = grad;
+ c.b = grad;
+ break;
+ case BIOLOGICAL_SCAN:
+ c.g = grad;
+ break;
+ }
+
+ SetContextForeGroundColor (c);
+
+ // flash the node from the smallest size to node size
+ // Get the node size for mineral, or number of transitions
+ // for other scan types (was set by GeneratePlanetSide())
+ nodeSize = GetFrameIndex (ElementPtr->next.image.frame)
+ - GetFrameIndex (ElementPtr->current.image.frame);
+ if (dy > nodeSize)
+ dy = nodeSize;
+
+ s.frame = SetRelFrameIndex (ElementPtr->current.image.frame, dy);
+ DrawFilledStamp (&s);
+ }
+
+ UnlockElement (hElement);
+ }
+
+ SetContextForeGroundColor (OldColor);
+}
+
+COUNT
+callGenerateForScanType (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT node, BYTE scanType, NODE_INFO *info)
+{
+ switch (scanType)
+ {
+ case MINERAL_SCAN:
+ return (*solarSys->genFuncs->generateMinerals) (
+ solarSys, world, node, info);
+ case ENERGY_SCAN:
+ return (*solarSys->genFuncs->generateEnergy) (
+ solarSys, world, node, info);
+ case BIOLOGICAL_SCAN:
+ return (*solarSys->genFuncs->generateLife) (
+ solarSys, world, node, info);
+ }
+
+ assert (false);
+ return 0;
+}
+
+bool
+callPickupForScanType (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT node, BYTE scanType)
+{
+ switch (scanType)
+ {
+ case MINERAL_SCAN:
+ return (*solarSys->genFuncs->pickupMinerals) (
+ solarSys, world, node);
+ case ENERGY_SCAN:
+ return (*solarSys->genFuncs->pickupEnergy) (
+ solarSys, world, node);
+ case BIOLOGICAL_SCAN:
+ return (*solarSys->genFuncs->pickupLife) (
+ solarSys, world, node);
+ }
+
+ assert (false);
+ return false;
+}
+
+static void
+ScanPlanet (COUNT scanType)
+{
+#define SCAN_DURATION (ONE_SECOND * 7 / 4)
+// NUM_FLASH_COLORS for flashing blips; 1 for the final frame
+#define SCAN_LINES (MAP_HEIGHT + NUM_FLASH_COLORS + 1)
+#define SCAN_LINE_WAIT (SCAN_DURATION / SCAN_LINES)
+
+ COUNT startScan, endScan;
+ COUNT scan;
+ RECT r;
+ static const Color textColors[] =
+ {
+ SCAN_MINERAL_TEXT_COLOR,
+ SCAN_ENERGY_TEXT_COLOR,
+ SCAN_BIOLOGICAL_TEXT_COLOR,
+ };
+ static const Color tintColors[] =
+ {
+ SCAN_MINERAL_TINT_COLOR,
+ SCAN_ENERGY_TINT_COLOR,
+ SCAN_BIOLOGICAL_TINT_COLOR,
+ };
+
+ if (scanType == AUTO_SCAN)
+ {
+ startScan = MINERAL_SCAN;
+ endScan = BIOLOGICAL_SCAN;
+ }
+ else
+ {
+ startScan = scanType;
+ endScan = scanType;
+ }
+
+ for (scan = startScan; scan <= endScan; ++scan)
+ {
+ TEXT t;
+ SWORD i;
+ Color tintColor;
+ // Alpha value will be ignored.
+ TimeCount TimeOut;
+
+ t.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ t.baseline.y = SIS_SCREEN_HEIGHT - MAP_HEIGHT - 7;
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+
+ t.pStr = GAME_STRING (SCAN_STRING_BASE + scan);
+
+ SetContext (PlanetContext);
+ r.corner.x = 0;
+ r.corner.y = t.baseline.y - 10;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = t.baseline.y - r.corner.y + 1;
+ // XXX: I do not know why we are repairing it here, as there
+ // should not be anything drawn over the stars at the moment
+ RepairBackRect (&r);
+
+ SetContextFont (MicroFont);
+ SetContextForeGroundColor (textColors[scan]);
+ font_DrawText (&t);
+
+ SetContext (ScanContext);
+
+ // Draw a virgin surface
+ BatchGraphics ();
+ DrawPlanet (0, BLACK_COLOR);
+ UnbatchGraphics ();
+
+ tintColor = tintColors[scan];
+
+ // Draw the scan slowly line by line
+ TimeOut = GetTimeCounter ();
+ for (i = 0; i < SCAN_LINES; i++)
+ {
+ TimeOut += SCAN_LINE_WAIT;
+ if (WaitForAnyButtonUntil (TRUE, TimeOut, FALSE))
+ break;
+
+ BatchGraphics ();
+ DrawPlanet (i, tintColor);
+ DrawScannedStuff (i, scan);
+ UnbatchGraphics ();
+#ifdef SPIN_ON_SCAN
+ RotatePlanetSphere (TRUE);
+#endif
+ }
+
+ if (i < SCAN_LINES)
+ { // Aborted by a keypress; draw in finished state
+ BatchGraphics ();
+ DrawPlanet (SCAN_LINES - 1, tintColor);
+ DrawScannedStuff (SCAN_LINES - 1, scan);
+ UnbatchGraphics ();
+ }
+ }
+
+ SetContext (PlanetContext);
+ RepairBackRect (&r);
+
+ SetContext (ScanContext);
+ if (scanType == AUTO_SCAN)
+ { // clear the last scan
+ DrawPlanet (0, BLACK_COLOR);
+ DrawScannedObjects (FALSE);
+ }
+
+ FlushInput ();
+}
+
+static BOOLEAN
+DoScan (MENU_STATE *pMS)
+{
+ BOOLEAN select, cancel;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (cancel || (select && pMS->CurState == EXIT_SCAN))
+ {
+ return FALSE;
+ }
+ else if (select)
+ {
+ if (pMS->CurState == DISPATCH_SHUTTLE)
+ {
+ COUNT fuel_required;
+
+ if ((pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ || (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity ==
+ GAS_GIANT_ATMOSPHERE))
+ { // cannot dispatch to shielded planets or gas giants
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ fuel_required = getLandingFuelNeeded ();
+ if (GLOBAL_SIS (FuelOnBoard) < fuel_required
+ || GLOBAL_SIS (NumLanders) == 0
+ || GLOBAL_SIS (CrewEnlisted) == 0)
+ {
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ SetFlashRect (NULL);
+
+ if (!PickPlanetSide ())
+ return FALSE;
+
+ DrawMenuStateStrings (PM_MIN_SCAN, pMS->CurState);
+ SetFlashRect (SFR_MENU_3DO);
+
+ return TRUE;
+ }
+
+ // Various scans
+ if (pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ { // cannot scan shielded planets
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ ScanPlanet (pMS->CurState);
+ if (pMS->CurState == AUTO_SCAN)
+ {
+ pMS->CurState = DISPATCH_SHUTTLE;
+ DrawMenuStateStrings (PM_MIN_SCAN, pMS->CurState);
+ }
+ }
+ else if (optWhichMenu == OPT_PC ||
+ (!(pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ && pSolarSysState->SysInfo.PlanetInfo.AtmoDensity !=
+ GAS_GIANT_ATMOSPHERE))
+ {
+ DoMenuChooser (pMS, PM_MIN_SCAN);
+ }
+
+ return TRUE;
+}
+
+static CONTEXT
+CreateScanContext (void)
+{
+ CONTEXT oldContext;
+ CONTEXT context;
+ RECT r;
+
+ // ScanContext rect is relative to SpaceContext
+ oldContext = SetContext (SpaceContext);
+ GetContextClipRect (&r);
+
+ context = CreateContext ("ScanContext");
+ SetContext (context);
+ SetContextFGFrame (Screen);
+ r.corner.x += r.extent.width - MAP_WIDTH;
+ r.corner.y += r.extent.height - MAP_HEIGHT;
+ r.extent.width = MAP_WIDTH;
+ r.extent.height = MAP_HEIGHT;
+ SetContextClipRect (&r);
+
+ SetContext (oldContext);
+
+ return context;
+}
+
+CONTEXT
+GetScanContext (BOOLEAN *owner)
+{
+ // TODO: Make CONTEXT ref-counted
+ if (ScanContext)
+ {
+ if (owner)
+ *owner = FALSE;
+ }
+ else
+ {
+ if (owner)
+ *owner = TRUE;
+ ScanContext = CreateScanContext ();
+ }
+ return ScanContext;
+}
+
+void
+DestroyScanContext (void)
+{
+ if (ScanContext)
+ {
+ DestroyContext (ScanContext);
+ ScanContext = NULL;
+ }
+}
+
+void
+ScanSystem (void)
+{
+ MENU_STATE MenuState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ GetScanContext (NULL);
+
+ if (optWhichMenu == OPT_3DO &&
+ ((pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ || pSolarSysState->SysInfo.PlanetInfo.AtmoDensity ==
+ GAS_GIANT_ATMOSPHERE))
+ {
+ MenuState.CurState = EXIT_SCAN;
+ }
+ else
+ {
+ MenuState.CurState = AUTO_SCAN;
+ planetLoc.x = (MAP_WIDTH >> 1) << MAG_SHIFT;
+ planetLoc.y = (MAP_HEIGHT >> 1) << MAG_SHIFT;
+
+ initPlanetLocationImage ();
+ SetContext (ScanContext);
+ DrawScannedObjects (FALSE);
+ }
+
+ DrawMenuStateStrings (PM_MIN_SCAN, MenuState.CurState);
+ SetFlashRect (SFR_MENU_3DO);
+
+ if (optWhichCoarseScan == OPT_PC)
+ PrintCoarseScanPC ();
+ else
+ PrintCoarseScan3DO ();
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoScan;
+ DoInput (&MenuState, FALSE);
+
+ SetFlashRect (NULL);
+
+ // cleanup scan graphics
+ BatchGraphics ();
+ SetContext (ScanContext);
+ DrawPlanet (0, BLACK_COLOR);
+ EraseCoarseScan ();
+ UnbatchGraphics ();
+
+ DestroyDrawable (ReleaseDrawable (eraseFrame));
+ eraseFrame = NULL;
+}
+
+static void
+generateBioNode (SOLARSYS_STATE *system, ELEMENT *NodeElementPtr,
+ BYTE *life_init_tab, COUNT creatureType)
+{
+ COUNT i;
+
+ // NOTE: TFB_Random() calls here are NOT part of the deterministic planet
+ // generation PRNG flow.
+ if (CreatureData[creatureType].Attributes & SPEED_MASK)
+ {
+ // Place moving creatures at a random location.
+ i = TFB_Random ();
+ NodeElementPtr->current.location.x =
+ (LOBYTE (i) % (MAP_WIDTH - (8 << 1))) + 8;
+ NodeElementPtr->current.location.y =
+ (HIBYTE (i) % (MAP_HEIGHT - (8 << 1))) + 8;
+ }
+
+ if (system->PlanetSideFrame[0] == 0)
+ system->PlanetSideFrame[0] =
+ CaptureDrawable (LoadGraphic (CANNISTER_MASK_PMAP_ANIM));
+
+ for (i = 0; i < MAX_LIFE_VARIATION
+ && life_init_tab[i] != (BYTE)(creatureType + 1);
+ ++i)
+ {
+ if (life_init_tab[i] != 0)
+ continue;
+
+ life_init_tab[i] = (BYTE)creatureType + 1;
+
+ system->PlanetSideFrame[i + 3] = load_life_form (creatureType);
+ break;
+ }
+
+ NodeElementPtr->mass_points = (BYTE)creatureType;
+ NodeElementPtr->hit_points = HINIBBLE (
+ CreatureData[creatureType].ValueAndHitPoints);
+ DisplayArray[NodeElementPtr->PrimIndex].
+ Object.Stamp.frame = SetAbsFrameIndex (
+ system->PlanetSideFrame[i + 3], (COUNT)TFB_Random ());
+}
+
+void
+GeneratePlanetSide (void)
+{
+ SIZE scan;
+ BYTE life_init_tab[MAX_LIFE_VARIATION];
+ // life_init_tab is filled with the creature types of already
+ // selected creatures. If an entry is 0, none has been selected
+ // yet, otherwise, it is 1 more than the creature type.
+
+ InitDisplayList ();
+ if (pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ return;
+
+ memset (life_init_tab, 0, sizeof life_init_tab);
+
+ for (scan = BIOLOGICAL_SCAN; scan >= MINERAL_SCAN; --scan)
+ {
+ COUNT num_nodes;
+ FRAME f;
+
+ f = SetAbsFrameIndex (MiscDataFrame,
+ NUM_SCANDOT_TRANSITIONS * (scan - ENERGY_SCAN));
+
+ num_nodes = callGenerateForScanType (pSolarSysState,
+ pSolarSysState->pOrbitalDesc, GENERATE_ALL, scan, NULL);
+
+ while (num_nodes--)
+ {
+ HELEMENT hNodeElement;
+ ELEMENT *NodeElementPtr;
+ NODE_INFO info;
+
+ if (isNodeRetrieved (&pSolarSysState->SysInfo.PlanetInfo,
+ scan, num_nodes))
+ continue;
+
+ hNodeElement = AllocElement ();
+ if (!hNodeElement)
+ continue;
+
+ LockElement (hNodeElement, &NodeElementPtr);
+
+ callGenerateForScanType (pSolarSysState,
+ pSolarSysState->pOrbitalDesc, num_nodes,
+ scan, &info);
+
+ NodeElementPtr->scan_node = MAKE_WORD (scan, num_nodes + 1);
+ NodeElementPtr->playerNr = PS_NON_PLAYER;
+ NodeElementPtr->current.location.x = info.loc_pt.x;
+ NodeElementPtr->current.location.y = info.loc_pt.y;
+
+ SetPrimType (&DisplayArray[NodeElementPtr->PrimIndex], STAMP_PRIM);
+ if (scan == MINERAL_SCAN)
+ {
+ NodeElementPtr->turn_wait = info.type;
+ NodeElementPtr->mass_points = HIBYTE (info.density);
+ NodeElementPtr->current.image.frame = SetAbsFrameIndex (
+ MiscDataFrame, (NUM_SCANDOT_TRANSITIONS * 2)
+ + ElementCategory (info.type) * 5);
+ NodeElementPtr->next.image.frame = SetRelFrameIndex (
+ NodeElementPtr->current.image.frame,
+ LOBYTE (info.density) + 1);
+ DisplayArray[NodeElementPtr->PrimIndex].Object.Stamp.frame =
+ IncFrameIndex (NodeElementPtr->next.image.frame);
+ }
+ else /* (scan == BIOLOGICAL_SCAN || scan == ENERGY_SCAN) */
+ {
+ NodeElementPtr->current.image.frame = f;
+ NodeElementPtr->next.image.frame = SetRelFrameIndex (
+ f, NUM_SCANDOT_TRANSITIONS - 1);
+ NodeElementPtr->turn_wait = MAKE_BYTE (4, 4);
+ NodeElementPtr->preprocess_func = object_animation;
+ if (scan == ENERGY_SCAN)
+ {
+ NodeElementPtr->mass_points = MAX_SCROUNGED;
+ DisplayArray[NodeElementPtr->PrimIndex].Object.Stamp.frame =
+ pSolarSysState->PlanetSideFrame[1];
+ }
+ else /* (scan == BIOLOGICAL_SCAN) */
+ {
+ generateBioNode (pSolarSysState, NodeElementPtr,
+ life_init_tab, info.type);
+ }
+ }
+
+ NodeElementPtr->next.location.x =
+ NodeElementPtr->current.location.x << MAG_SHIFT;
+ NodeElementPtr->next.location.y =
+ NodeElementPtr->current.location.y << MAG_SHIFT;
+ UnlockElement (hNodeElement);
+
+ PutElement (hNodeElement);
+ }
+ }
+}
+
+bool
+isNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr)
+{
+ return (planetInfo->ScanRetrieveMask[scanType] & ((DWORD) 1 << nodeNr))
+ != 0;
+}
+
+COUNT
+countNodesRetrieved (PLANET_INFO *planetInfo, BYTE scanType)
+{
+ COUNT count;
+ DWORD mask = planetInfo->ScanRetrieveMask[scanType];
+
+ // count the number of bits set
+ // Caution: 'mask' must be unsigned
+ for (count = 0; mask != 0; mask >>= 1)
+ {
+ if (mask & 1)
+ ++count;
+ }
+ return count;
+}
+
+void
+setNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr)
+{
+ planetInfo->ScanRetrieveMask[scanType] |= ((DWORD) 1 << nodeNr);
+}
+
+void
+setNodeNotRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr)
+{
+ planetInfo->ScanRetrieveMask[scanType] &= ~((DWORD) 1 << nodeNr);
+}
+
diff --git a/src/uqm/planets/scan.h b/src/uqm/planets/scan.h
new file mode 100644
index 0000000..f66fb0e
--- /dev/null
+++ b/src/uqm/planets/scan.h
@@ -0,0 +1,72 @@
+//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.
+ */
+
+#ifndef UQM_PLANETS_SCAN_H_
+#define UQM_PLANETS_SCAN_H_
+
+typedef struct scan_desc SCAN_DESC;
+typedef struct scan_block SCAN_BLOCK;
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "planets.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct scan_desc
+{
+ POINT start;
+ COUNT start_dot;
+ COUNT num_dots;
+ COUNT dots_per_semi;
+};
+
+struct scan_block
+{
+ POINT *line_base;
+ COUNT num_scans;
+ COUNT num_same_scans;
+ SCAN_DESC *scan_base;
+};
+
+extern void ScanSystem (void);
+
+extern void RepairBackRect (RECT *pRect);
+extern void GeneratePlanetSide (void);
+extern COUNT callGenerateForScanType (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT node, BYTE scanType, NODE_INFO *);
+// Returns true if the node should be removed from the surface
+extern bool callPickupForScanType (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT node, BYTE scanType);
+
+extern void RedrawSurfaceScan (const POINT *newLoc);
+extern CONTEXT GetScanContext (BOOLEAN *owner);
+extern void DestroyScanContext (void);
+
+bool isNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr);
+COUNT countNodesRetrieved (PLANET_INFO *planetInfo, BYTE scanType);
+void setNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr);
+void setNodeNotRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_SCAN_H_ */
diff --git a/src/uqm/planets/solarsys.c b/src/uqm/planets/solarsys.c
new file mode 100644
index 0000000..11bd4c0
--- /dev/null
+++ b/src/uqm/planets/solarsys.c
@@ -0,0 +1,2021 @@
+//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"
+
+
+//#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 first.
+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)
+{
+ return MAKE_DWORD (star->star_pt.x, star->star_pt.y);
+}
+
+// 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;
+}
+
+static void
+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);
+ }
+ }
+}
+
+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);
+}
diff --git a/src/uqm/planets/solarsys.h b/src/uqm/planets/solarsys.h
new file mode 100644
index 0000000..0f86fdd
--- /dev/null
+++ b/src/uqm/planets/solarsys.h
@@ -0,0 +1,34 @@
+//Copyright (C) 2011, Scott A. Colcord
+
+/*
+ * 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.
+ */
+
+#ifndef SOLARSYS_H
+#define SOLARSYS_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void LoadIPData (void);
+extern void FreeIPData (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SOLARSYS_H */
+
diff --git a/src/uqm/planets/sundata.h b/src/uqm/planets/sundata.h
new file mode 100644
index 0000000..6d7888e
--- /dev/null
+++ b/src/uqm/planets/sundata.h
@@ -0,0 +1,73 @@
+//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.
+ */
+
+#ifndef UQM_PLANETS_SUNDATA_H_
+#define UQM_PLANETS_SUNDATA_H_
+
+#include "plandata.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*------------------------------ Global Data ------------------------------ */
+
+#define NUMBER_OF_SUN_SIZES (SUPER_GIANT_STAR - DWARF_STAR + 1)
+
+#define DWARF_ENERGY 1
+#define GIANT_ENERGY 5
+#define SUPERGIANT_ENERGY 20
+
+typedef struct
+{
+ BYTE StarSize;
+ BYTE StarIntensity;
+ UWORD StarEnergy;
+
+ PLANET_INFO PlanetInfo;
+} SYSTEM_INFO;
+
+#define GENERATE_ALL ((COUNT)~0)
+
+extern COUNT GenerateMineralDeposits (const SYSTEM_INFO *, COUNT whichDeposit,
+ NODE_INFO *info);
+extern COUNT GenerateLifeForms (const SYSTEM_INFO *, COUNT whichLife,
+ NODE_INFO *info);
+extern void GenerateRandomLocation (POINT *loc);
+extern COUNT GenerateRandomNodes (const SYSTEM_INFO *, COUNT scan, COUNT numNodes,
+ COUNT type, COUNT whichNode, NODE_INFO *info);
+// Generate lifeforms from a preset lifeTypes[] array
+extern COUNT GeneratePresetLife (const SYSTEM_INFO *,
+ const SBYTE *lifeTypes, COUNT whichLife, NODE_INFO *info);
+
+#define DWARF_ELEMENT_DENSITY 1
+#define GIANT_ELEMENT_DENSITY 3
+#define SUPERGIANT_ELEMENT_DENSITY 8
+
+#define MAX_ELEMENT_DENSITY ((MAX_ELEMENT_UNITS * SUPERGIANT_ELEMENT_DENSITY) << 1)
+
+extern void DoPlanetaryAnalysis (SYSTEM_INFO *SysInfoPtr,
+ PLANET_DESC *pPlanetDesc);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_SUNDATA_H_ */
+
diff --git a/src/uqm/planets/surface.c b/src/uqm/planets/surface.c
new file mode 100644
index 0000000..79f9e75
--- /dev/null
+++ b/src/uqm/planets/surface.c
@@ -0,0 +1,251 @@
+//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 "lifeform.h"
+#include "planets.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+//#define DEBUG_SURFACE
+
+const BYTE *Elements;
+const PlanetFrame *PlanData;
+
+static COUNT
+CalcMineralDeposits (const SYSTEM_INFO *SysInfoPtr, COUNT which_deposit,
+ NODE_INFO *info)
+{
+ BYTE j;
+ COUNT num_deposits;
+ const ELEMENT_ENTRY *eptr;
+
+ eptr = &SysInfoPtr->PlanetInfo.PlanDataPtr->UsefulElements[0];
+ num_deposits = 0;
+ j = NUM_USEFUL_ELEMENTS;
+ do
+ {
+ BYTE num_possible;
+
+ num_possible = LOBYTE (RandomContext_Random (SysGenRNG))
+ % (DEPOSIT_QUANTITY (eptr->Density) + 1);
+ while (num_possible--)
+ {
+#define MEDIUM_DEPOSIT_THRESHOLD 150
+#define LARGE_DEPOSIT_THRESHOLD 225
+ COUNT deposit_quality_fine;
+ COUNT deposit_quality_gross;
+
+ deposit_quality_fine = (LOWORD (RandomContext_Random (SysGenRNG)) % 100)
+ + (
+ DEPOSIT_QUALITY (eptr->Density)
+ + SysInfoPtr->StarSize
+ ) * 50;
+ if (deposit_quality_fine < MEDIUM_DEPOSIT_THRESHOLD)
+ deposit_quality_gross = 0;
+ else if (deposit_quality_fine < LARGE_DEPOSIT_THRESHOLD)
+ deposit_quality_gross = 1;
+ else
+ deposit_quality_gross = 2;
+
+ GenerateRandomLocation (&info->loc_pt);
+
+ info->density = MAKE_WORD (
+ deposit_quality_gross, deposit_quality_fine / 10 + 1);
+ info->type = eptr->ElementType;
+#ifdef DEBUG_SURFACE
+ log_add (log_Debug, "\t\t%d units of %Fs",
+ info->density,
+ Elements[eptr->ElementType].name);
+#endif /* DEBUG_SURFACE */
+ if (num_deposits >= which_deposit
+ || ++num_deposits == sizeof (DWORD) * 8)
+ { // reached the maximum or the requested node
+ return num_deposits;
+ }
+ }
+ ++eptr;
+ } while (--j);
+
+ return num_deposits;
+}
+
+// Returns:
+// for whichLife==~0 : the number of nodes generated
+// for whichLife<32 : the index of the last node (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+COUNT
+GenerateMineralDeposits (const SYSTEM_INFO *SysInfoPtr, COUNT whichDeposit,
+ NODE_INFO *info)
+{
+ NODE_INFO temp_info;
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[MINERAL_SCAN]);
+ return CalcMineralDeposits (SysInfoPtr, whichDeposit, info);
+}
+
+static COUNT
+CalcLifeForms (const SYSTEM_INFO *SysInfoPtr, COUNT which_life,
+ NODE_INFO *info)
+{
+ COUNT num_life_forms;
+
+ num_life_forms = 0;
+ if (PLANSIZE (SysInfoPtr->PlanetInfo.PlanDataPtr->Type) != GAS_GIANT)
+ {
+#define MIN_LIFE_CHANCE 10
+ SIZE life_var;
+
+ life_var = RandomContext_Random (SysGenRNG) & 1023;
+ if (life_var < SysInfoPtr->PlanetInfo.LifeChance
+ || (SysInfoPtr->PlanetInfo.LifeChance < MIN_LIFE_CHANCE
+ && life_var < MIN_LIFE_CHANCE))
+ {
+ BYTE num_types;
+
+ num_types = 1 + LOBYTE (RandomContext_Random (SysGenRNG))
+ % MAX_LIFE_VARIATION;
+ do
+ {
+ BYTE index, num_creatures;
+ UWORD rand_val;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ index = LOBYTE (rand_val) % NUM_CREATURE_TYPES;
+ num_creatures = 1 + HIBYTE (rand_val) % 10;
+ do
+ {
+ GenerateRandomLocation (&info->loc_pt);
+ info->type = index;
+ info->density = 0;
+
+ if (num_life_forms >= which_life
+ || ++num_life_forms == sizeof (DWORD) * 8)
+ { // reached the maximum or the requested node
+ return num_life_forms;
+ }
+ } while (--num_creatures);
+ } while (--num_types);
+ }
+#ifdef DEBUG_SURFACE
+ else
+ {
+ log_add (log_Debug, "It's dead, Jim! (%d >= %d)", life_var,
+ SysInfoPtr->PlanetInfo.LifeChance);
+ }
+#endif /* DEBUG_SURFACE */
+ }
+
+ return num_life_forms;
+}
+
+// Returns:
+// for whichLife==~0 : the number of lifeforms generated
+// for whichLife<32 : the index of the last lifeform (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+COUNT
+GenerateLifeForms (const SYSTEM_INFO *SysInfoPtr, COUNT whichLife,
+ NODE_INFO *info)
+{
+ NODE_INFO temp_info;
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[BIOLOGICAL_SCAN]);
+ return CalcLifeForms (SysInfoPtr, whichLife, info);
+}
+
+// Returns:
+// for whichLife==~0 : the number of lifeforms generated
+// for whichLife<32 : the index of the last lifeform (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+// lifeTypes[] is terminated with -1
+COUNT
+GeneratePresetLife (const SYSTEM_INFO *SysInfoPtr, const SBYTE *lifeTypes,
+ COUNT whichLife, NODE_INFO *info)
+{
+ COUNT i;
+ NODE_INFO temp_info;
+
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+
+ // This function may look unnecessarily complicated, but it must be
+ // kept this way to preserve the universe. That is done by preserving
+ // the order and number of Random() calls.
+
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[BIOLOGICAL_SCAN]);
+
+ for (i = 0; lifeTypes[i] >= 0; ++i)
+ {
+ GenerateRandomLocation (&info->loc_pt);
+ info->type = lifeTypes[i];
+ // density is irrelevant for bio nodes
+ info->density = 0;
+
+ if (i >= whichLife)
+ break;
+ }
+
+ return i;
+}
+
+void
+GenerateRandomLocation (POINT *loc)
+{
+ UWORD rand_val;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loc->x = 8 + LOBYTE (rand_val) % (MAP_WIDTH - (8 << 1));
+ loc->y = 8 + HIBYTE (rand_val) % (MAP_HEIGHT - (8 << 1));
+}
+
+// Returns:
+// for whichNode==~0 : the number of nodes generated
+// for whichNode<32 : the index of the last node (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+COUNT
+GenerateRandomNodes (const SYSTEM_INFO *SysInfoPtr, COUNT scan, COUNT numNodes,
+ COUNT type, COUNT whichNode, NODE_INFO *info)
+{
+ COUNT i;
+ NODE_INFO temp_info;
+
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[scan]);
+
+ for (i = 0; i < numNodes; ++i)
+ {
+ GenerateRandomLocation (&info->loc_pt);
+ // type is irrelevant for energy nodes
+ info->type = type;
+ // density is irrelevant for energy and bio nodes
+ info->density = 0;
+
+ if (i >= whichNode)
+ break;
+ }
+
+ return i;
+}