From 7f6002caba3f0a6749820c2772161caf55b8d267 Mon Sep 17 00:00:00 2001 From: neonloop Date: Fri, 7 May 2021 20:00:12 +0000 Subject: Initial commit (uqm-0.8.0) --- src/uqm/planets/calc.c | 530 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 src/uqm/planets/calc.c (limited to 'src/uqm/planets/calc.c') 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 */ + } +} + -- cgit v1.2.3