summaryrefslogtreecommitdiff
path: root/src/uqm/planets/calc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/planets/calc.c')
-rw-r--r--src/uqm/planets/calc.c530
1 files changed, 530 insertions, 0 deletions
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 */
+ }
+}
+