Mechanics wiki / Survive the Storm

Survive the Storm

The overhaul mod this workspace is building: a crossover of Against the Storm and Surviving Mars, for the base game. These pages are the mod's design and current state: most features are built (untested in-game), a few still planned - each feature's Status says which.

Three pillars: randomize (each run is unique), rebalance (no single best path), escalate (harder over time). Each feature has a status (Planned, Confirmed with the designer, Built untested, Implemented) and the base-game code that identifies the target.

Feature groups

Cut features

Base-game features the mod removes entirely: the "No ..." list. The Status column tracks each feature from design to in-game test (Planned, Confirmed, Built untested, Tested). The Base-game code column names where the feature lives; the Implementation column records how the mod removes it.

FeatureStatusWhat it doesBase-game codeImplementation
No asteroidsTestedRemoves the Below and Beyond asteroid expeditions (mining temporary asteroid maps by rocket).Below and Beyond (whole DLC, picard.hpk)Below and Beyond stays loaded (its darkness overlay powers "Map black at start"), so the content is switched off in code instead of by disabling the DLC. Root gate: IsDlcAccessible("picard") reads the shared g_AccessibleDlc table (Lua/init.lua:29-30); setting g_AccessibleDlc["picard"] = false makes it return false for the whole session, so every runtime Below-and-Beyond check hides itself - the Exotic Minerals resource, the Asteroid Lander rocket, planetary and map-switch UI, and the tech/building preset filters. (Overriding the IsDlcAccessible function from a mod does nothing: mod Lua is sandboxed, only shared-table writes propagate.) The research field and build-menu buildings are stripped explicitly too as a belt. In Code/StS_NoBelowAndBeyond.lua.
No undergroundTestedRemoves the Below and Beyond underground map layer (caves and deep deposits explored with rovers).Below and Beyond (whole DLC, picard.hpk)Same g_AccessibleDlc["picard"] = false root gate as No asteroids, so the underground techs, buildings and map-switch UI all disappear. As a guarantee Colony:UnlockUnderground() is also neutralised (Lua/Cheats.lua:298), so the underground map can never open. Same Code/StS_NoBelowAndBeyond.lua.
No Game Rules selectorTested (v16)Removes the Game Rules row from the new-game setup screen, so the player never picks game rules. Inflation is applied separately as an always-on custom rule, not a selectable game rule (see the Inflation always on feature).GetMissionParamUICategories (Lua/X/XPGMission.lua:172-190), consumed at Lua/XTemplates/PGMissionSponsor.lua:131; the row is one key of the MissionParams table (Lua/PreGameMission.lua:47-54)Wrap GetMissionParamUICategories at ClassesBuilt and drop the entry whose id is idGameRules. Additive: MissionParams.idGameRules and all game-rule machinery stay intact (nil'ing it would crash the game-rule code), only the row is hidden. The table entry is not nil'd out, which would crash ReloadGameRules (Lua/GameRules.lua:1-7); CalcChallengeRating is already guarded for a missing param (Lua/PreGameMission.lua:582-584). In Code/StS_StartupMenu.lua.
No starting hintsTested (2.106); New Anomalies drop built untestedRemoves the base-game onboarding that appears as a run begins, so the mod's own opening notifications (sponsor pick, commander pick, map conditions, General Rules, pre-founder event) are not buried. Four things go: the beginner hints (Welcome to Mars, Rocket, Probes, and the first-landing batch - Building Construction, Camera Controls, Universal Depot, Sensor Tower, Resupply, Priority, Game Speed, Research Available); the automatic "Sector scanned" card for the one or two sectors scanned at the start; and the Green Planet terraforming start cards - the Terraforming Introduction, plus, with a terraforming-focused sponsor, the parameter and building cards (Atmosphere, Temperature, Vegetation, Water, Seeds, Buildings, Special Projects) that its start-granted techs raise during setup; and the "New Anomalies" card for the anomalies the start scan uncovers. This is the start-only set: later situational hints (Dust Storm, Cold Wave, Tourists, Renegades, Earthsick), the build-something-first suggestions, and any terraforming card or anomaly the player reveals later in the run all still appear. In Code/StS_NoStartVanillaHints.lua.Hints show through HintTrigger(id) (Lua/Hints.lua:13); the opening set is HintGameStart (Lua/Hints.lua:237), HintRocket (Lua/Buildings/RocketBase.lua:1361), HintProbes (Lua/OrbitalProbe.lua:143) and the first-landing batch gated by first_arrival (Lua/Buildings/RocketBase.lua:477-488). The cards are added via AddOnScreenNotification (Lua/UI/OnScreenNotification.lua:650): the scan card at Lua/Exploration.lua:119 for the starting sectors scanned in Exploration:InitialExplore (Lua/Exploration.lua:983-1000); the terraforming cards with preset group Terraforming (armstrong/Presets/OnScreenNotificationPreset.lua), the intro on CityStart and the parameter/building cards on TechResearched (armstrong/Code/Terraforming.lua, Vegetation.lua, Research.lua). The "New Anomalies" card is raised by HandleNewObjsNotif(g_RecentlyRevAnomalies, "NewAnomalies", ...), a 1s game-time batch loop (Lua/Buildings/Anomaly.lua:427, Lua/UI/OnScreenNotification.lua:525).Two mechanisms. The eleven opening hints are pre-seeded as disabled: HintTrigger early-returns when g_ActiveHints[id] already exists (Lua/Hints.lua:17) and the game's own HintDisable(id) sets that entry disabled (Lua/Hints.lua:116), so at PostNewGame and CityStart, before any hint fires, the mod calls HintDisable on exactly those eleven and every later trigger is a no-op while all other hints keep working. The two card types are dropped before they are ever created: the mod wraps AddOnScreenNotification (the same global Green Planet itself overrides, armstrong/Code/OnScreenNotification.lua) and, while game time is 100 or less, returns without adding the card for id SectorScanned and for any card whose preset group is Terraforming. The 100-or-less window is the game's own marker for the starting sectors (it withholds per-sector research below that, Lua/Exploration.lua:122); a terraforming tech cannot be researched that early, so only the start batch is caught. The "New Anomalies" card batches a game-second after the setup scan (past that window, and the game may be paused there), so NewAnomalies is dropped for the whole first game-hour instead (GameTime() at most const.HourDuration); the player cannot reveal a new anomaly by scanning that early, so only the start card is caught. Every scan, terraforming card or anomaly later in the run notifies normally. In Code/StS_NoStartVanillaHints.lua.
No Extractor AI breakthroughTested (v2.218)Removes the Extractor AI breakthrough entirely, so it never enters a game's breakthrough pool and the player can never find it. Extractor AI let Metals and Rare Metals Extractors run with no crew at 50 performance, which is overpowered for this mod. Its full effect is on the vanilla Breakthroughs page. In StS as Code/StS_BanExtractorAI.lua.Data/TechPreset.lua ExtractorAI (SortKey 47); the breakthrough pool is built from Presets.TechPreset.Breakthroughs filtered by Colony:TechAvailableCondition (Lua/Buildings/Anomaly.lua:583-598), which ANDs tech:condition() (Lua/Research.lua:180-183)On ClassesBuilt, set the ExtractorAI TechPreset's condition to return false - the same technique StS_NoBelowAndBeyond.lua (RemovePicardTechs) uses to strip Below-and-Beyond techs. A breakthrough enters the seeding pool only when tech:condition() is true, so it is dropped from both the subsurface-anomaly path (City:InitBreakThroughAnomalies) and the planetary / reward path (Colony:GetUnregisteredBreakthroughs).

Sources

Base game (Steam app 464920) plus the Space Race (gagarin.hpk) and Below and Beyond (picard.hpk) DLC, decompiled (method: docs/reading-surviving-mars-code.md). Asteroid and underground content is switched off while Below and Beyond stays loaded (its darkness overlay powers "Map black at start"): IsDlcAccessible("picard") reads the shared g_AccessibleDlc table (Lua/init.lua:29-30), so setting g_AccessibleDlc["picard"] = false turns off every runtime picard check at once (resource, rocket, UI, tech and building filters). The mod requires every DLC and warns at load if any is missing (Code/StS_RequireDlc.lua). See also docs/rival-tech-exchange.md.

Randomization

What makes each playthrough different: randomized values, and information hidden or chosen during play so no two runs start the same. Status is Planned, PoC built, or Built. The last column points to the base-game code, with UNVERIFIED for a spot not yet located.

FeatureStatusWhat it doesBase-game code
Map black at startTested (v2.22, needs B&B)The surface starts black; each scanned sector reveals, its neighbours partly reveal and stay scannable, and every farther sector is dark, shows no resource hint or buildable area, and cannot be selected for scanning - so exploration spreads outward from the start. Building is refused outside a build radius around the nearest scanned sector (130% of the sector width, 53248 world units on a standard map), so a Sensor Tower cannot be plopped into the dark for a free scan boost. In StS as Code/StS_HiddenMap.lua (see notes).hr.RenderRevealDarkness + RevealDarkness objects (Below & Beyond overlay forced onto the surface); rollover and scan gates via OverviewModeDialog:GenerateSectorRolloverContext, MapSector:CanBeScanned, OrbitalProbe:ScanSector; build block via ConstructionController:UpdateConstructionStatuses (Lua/Construction/Construction.lua:1582,1883)
Random colony siteTested v2.9The pre-game Colony Site screen (site pick plus the Threats and Resources readout) is skipped: NEXT on the rocket payload screen starts the game on a random landing spot instead of opening the site chooser, and that button is relabeled "START ON RANDOM SITE". Requirement: every existing landing spot must be equally likely - latitude and longitude are drawn uniformly over the whole-degree grid (poles included), not the game's 5 quickstart spots and not the equator-biased sphere pick. In StS as Code/StS_RandomColonySite.lua (see notes).LandingSiteObjectCreateAndLoad (Lua/UI/PlanetUI.lua:1515); uniform coordinate draw via AsyncRand, committed with GetOverlayValues (Lua/UI/PlanetUI.lua:1594); start via GenerateCurrentRandomMap (Lua/UI/PreGameMenus.lua:241)
Chaos Theory (random tech order)Tested v38Each new colony gets a custom weighted shuffle of every discoverable research field (not the base game's full Chaos Theory rule). For each tech an ordering value is computed as its average position (the middle of its designed rank range, (from+to)/2) plus the sum of ten random draws (each AsyncRand(9), an integer 0-8); the field's techs are then sorted by that value. Summing ten flat draws forms a bell curve (central limit theorem), so most techs land a handful of ranks from their usual spot and only rarely far - the order is randomized every game but still loosely follows the intended progression. Widening the draw or adding more draws increases the spread. Funding techs are clamped to their own range so their one-time funding grants still land at the intended research depth. Computed once at colony creation, saved, and never re-rolled on load. In StS as Code/StS_ChaosTheory.lua.reorder wraps Colony:InitResearch (Lua/Colony.lua:8,64); a field is shown in tech_field order via Research:UITechField (Lua/Research.lua:1027-1051); per-slot cost re-assigned from the field ramp (Lua/Research.lua:153-170); funding techs = presets with an Effect_Funding sub-object; armed at OnMsg.PreNewGame before Colony:Init (Lua/Mysteries/Mysteries.lua:46, Lua/Colony.lua:63-64)
In-game first rocketTestedThe pre-game Rocket Payload (loadout) screen is removed, so setup goes straight to the map: the mission "sponsor" screen's NEXT is retargeted from the payload screen to the landing/map step (and relabeled "START ON RANDOM SITE"). You then call your first rocket in-game from Resupply and it arrives with zero flight time; you still pay the normal cargo cost, and every rocket after it uses normal travel. That first rocket also opens pre-filled with a default loadout you can edit or clear (own page: First rocket loadout; Tested v2.146). In StS as Code/StS_FirstRocketInGame.lua.sponsor NEXT node retargeted "payload" to "landing" (Lua/XTemplates/PGMissionSponsor.lua:194-209); first-call promotion in City:OrderLanding (Lua/City.lua:316-329) to the instant path in RocketBase:FlyToMars (Lua/Buildings/RocketBase.lua:241,297-301)
RocketTested v2.9One of the six Map conditions, rolled once per colony (its medium is the middle package pods): it sets the starting fleet and rocket travel time as one of three packages - 1 rocket with standard travel (fast), 1 rocket with double travel time plus 5 free Supply Pods (pods), or 2 rockets with triple travel time (big). Travel is a +0/+100/+200% modifier on the two travel consts that regular supply rockets read, plus the same x1/x2/x3 on the custom-travel rockets (Dragon, Zeus, Expedition Rocket) so they slow with the tier while keeping their speed edge; Supply Pods keep a fixed travel speed. Applied on CityStart/LoadGame, idempotent. In StS as Code/StS_ConditionRocket.lua.GetStartingRockets (Lua/City.lua:310); travel consts read at Lua/Buildings/RocketBase.lua:240,826, modified via g_Consts:SetModifier (Lua/Modifiers.lua:180); free pods replicate RewardSupplyPods (Lua/ClassDefs/ClassDef-Effects.generated.lua:1781-1785)
Sensor TowersTested v2.222One of the six Map conditions, rolled once per colony: it trades how far a sensor tower's scan-speed boost reaches against how strong that boost is. The extreme options are wide reach with a weaker boost (reach x1.33, boost x0.67) or short reach with a stronger boost (reach x0.67, boost x1.33); the medium option leaves both as they are. Applied from a stashed base on CityStart/LoadGame so a mod reload never compounds; the full-boost radius is left untouched, so only the falloff length and the peak boost move. Supersedes the earlier flat-50% range-cut proof of concept. In StS as Code/StS_RandomTowerRange.lua.Lua/_GameConst.lua:109-111; Lua/Exploration.lua:284-306
VistasTested v2.222One of the six Map conditions, rolled once per colony: it shifts the map's mix of two scenic surface deposits, Vistas (dome comfort) and Research Sites (research boost). As each such deposit is uncovered by exploration, on an extreme roll each one of the source type has a 33% chance to flip to the other - one extreme yields about +33% more Vistas (a third of Research Sites flip), the other about +33% more Research Sites (a third of Vistas flip); the medium roll leaves the map's split unchanged. Only deposits placed by the map are swapped: deposits granted by story events are left alone. In StS as Code/StS_ConditionVistas.lua.RevealDeposits (Lua/Exploration.lua:417) wrapped; EffectDepositMarker:SpawnDeposit (Lua/Buildings/EffectDeposit.lua:10) overridden to re-point deposit_type between BeautyEffectDeposit and ResearchEffectDeposit (Lua/Buildings/EffectDeposit.lua:108,154); story deposits bypass it via direct PlaceDeposit (Lua/ClassDefs/ClassDef-Effects.generated.lua:2189,2412)
FundingTested v2.222One of the six Map conditions, always active, rolled once per colony: a coupled +/-20% trade-off between the two funding streams, or an even split. On metals the Rare Metals export price is +20% and tourist funding -20%; on tourism the reverse; on even both are unchanged. It never raises both. Applied as two g_Consts modifiers on CityStart/LoadGame, set absolutely each time so a reload never compounds. This replaces the earlier +50% single-edge design (a Rare Metals, tourist, or research tech-grant edge). In StS as Code/StS_ConditionFunding.lua.Rare Metals price ExportPricePreciousMetals (Lua/_const.lua:628-632) read in Funding:CalcBaseExportFunding (Lua/Funding.lua:38-40); tourist payout TouristFundingMultiplier (Lua/_const.lua:203-207) read in HolidayRating:RewardMoney (Lua/HolidayRating.lua:59,63); both set via g_Consts:SetModifier (Lua/Modifiers.lua:180, percent a delta on 100 at :62,99)
Choices during playTested (v2.41, in-game + log): Sponsor, Commander and Mystery each a setup-screen multi-select pool feeding the later 3-way choice; preselection and the "N of M possible" row summary verified in-game. v2.42 hides Below and Beyond options and dedups Space Race duplicates (built on top, not re-tested in-game). v2.50: the commander offer draws the three from distinct groups (Water/Colonist/Economy/Other), DLC commanders (Geo Engineer, Transport Tycoon) included - tested and done. v2.64: each sponsor option in the pick card lists its vehicle, building, perk and colonist trait, and the Sponsor notification sits above the Commander one - tested. v2.115: vanilla Politician (dropped from the roster) is hidden from the Possible Commanders checklist, matching the Russia sponsor drop - tested. (Commander/sponsor deposit reveal, tech top-up and rebalances still built untested.)The Sponsor, Commander and Mystery are each chosen in two stages. On the setup screen each is a multi-select checklist - "Possible Sponsors", "Possible Commanders", "Possible Mysteries" - of which options may appear this run; every option is checked by default, except mysteries, where only the ones you have not finished are pre-checked (read from AccountStorage.FinishedMysteries). The collapsed row shows "All possible" or "N of M possible". The mod's later random choice then offers three drawn from the checked pool: the Sponsor at game start and the Commander at colony start via a non-dismissable click notification (Commander notes here), and the Mystery on sol 50 via an event (notes) - one per difficulty tier where the pool allows. Below and Beyond options and vanilla Politician (unsupported) are hidden and Space Race's same-id duplicates deduped. Each sponsor option in the pick card lists its unique vehicle and building (each with a short description), its perk and its colonist trait, the four columns of the sponsor table. See the Run setup overview.sponsor GetMissionSponsor (Lua/PreGameMission.lua:254), commander GetCommanderProfile (Lua/PreGameMission.lua:268), mystery Mysteries:SelectMystery / CheatStartMystery (Lua/Mysteries/Mysteries.lua:11,114); choice WaitStoryBitPopup (Lua/MarsStoryBits.lua:58), notification AddCustomOnScreenNotification (Lua/UI/OnScreenNotification.lua:708); setup pool a multi-select checkbox item routed through PGMissionItem (Lua/XTemplates/PGMissionItem.lua), cloned from GameRuleItem with GetCheckboxImage (Lua/GameRules.lua:112), in Code/StS_MissionPoolUI.lua
Pre-Founder event choiceBuilt, untested (v48)The vanilla Pre-Founder Stage event pool is emptied and replaced by the mod's own set of 15 balanced hand-authored events (around 500 M each, no trap choice), each replacing the vanilla subset that rolled a 40% chance to be in the pool. In the night between Sol 2 and Sol 3 the mod force-fires one of them at random: you never pick which event you get, only how you answer its 3 or 4 options. The tech reveals are spread one per event across seven of them - the next 3 techs in a field for Terraforming, Biotech, Engineering, Robotics, Physics and Social, plus a Cross-Discipline Survey that reveals the next tech in 3 different random fields. The full event and reward list is in story-events.html. In StS as Code/StS_PreFounderEventChoice.lua (see notes). Rocket Short-circuit is untouched (it fires on a manual rocket launch, not the pre-founder tick). The vanilla pool is documented in story-events.html.the 19 Tick_BeforeFounders StoryBits (Data/StoryBit/Boost*.lua); per-game pool via EnableChance/Enabled (Docs/ModItemStoryBit.md.html), runtime list g_StoryBitStates (Lua/Buildings/PlanetaryAnomaly.lua:171); random pick via AsyncRand then force-fire ForceActivateStoryBit (Lua/Buildings/PlanetaryAnomaly.lua:338)
Any-time events disabledBuilt, untested (v2.261)The vanilla Any-time story-event group - the periodic events with no colony-stage gate - is not supported. It holds two events, Fickle Economics (a random import price rises) and Power Surge (a power network threatens to overload); the mod empties the whole group so neither ever fires, the same empty-pool method the Pre-Founder replacement uses. Detail on Story events. In StS as Code/StS_DisableAnyTimeEvents.lua.the Tick-category StoryBits (Data/StoryBit/IncreaseResourceCost.lua, PowerSurge.lua; group in Data/StoryBitCategory.lua); disabled via EnableChance/Enabled on each preset
Survey Data GatheredTested v2.79 (mechanic); blue frame v2.81; landing-gated notification tested 2.112A Sol 1 event. Once the first rocket lands on Mars a non-dismissable, clickable "Survey in Progress" notification (the built-in blue notification frame - the game ships no yellow one) counts down the disaster-warning way ("Survey completes in <countdown>"); clicking it opens a message box explaining the survey and the choice to come. At Sol 1 20:00 it resolves into a choice popup, "Survey Data Gathered", regardless of when the rocket landed. The player names one of the six research fields - Terraforming, Biotech, Engineering, Robotics, Physics, Social, in research-screen order - and its next 2 technologies are revealed, using the same reveal a Tech Anomaly uses (stopping early if a field has fewer than 2 left). Fires once, new colonies only; it announces itself, so it is not in the General Rules readout. In StS as Code/StS_SurveyDataGathered.lua.fields GetAvailablePresets(Presets.TechFieldPreset.Default) filtered by discoverable (Data/TechFieldPreset.lua; Terraforming armstrong/Presets/TechFieldPreset.lua); reveal DiscoverTechInField (Lua/Research.lua:214), count TechCount (:526); countdown notification AddCustomOnScreenNotification with display_countdown/game_time (Lua/UI/OnScreenNotification.lua:704-706,319-345); popup WaitStoryBitPopup (Lua/MarsStoryBits.lua:58); notification gated on the first Mars landing, Msg("RocketLanded") (Lua/Buildings/RocketBase.lua:381); clock UIColony.day/hour, game starts Sol 1 hour 6 (Lua/Colony.lua:10-11,183-192), NewHour (Lua/DayTime.lua:73)
Goal timelineBuilt, untested (v2.291)Replaces the sponsor's five fixed Mission Goals with five goal picks on a rising schedule (Sols 3, 6, 12, 20, 30). Each step draws six candidate goals - two timed, four untimed - and offers three at random (one timed, two untimed); you pick one and its reward is granted on completion. Every step carries one of each of six reward kinds: research points and research-per-Sol (the step's two timed goals, so only one research reward is ever offered together), a supply pod of advanced resources, a prefab building (worth half its resupply cost), Funding, and a flex slot (specialist applicants, a tech reveal, or a Rare Metal deposit - never a Water deposit). Reward values track a per-step band (untimed 500 / 750 / 1,000 / 1,250 / 1,500; a timed goal pays 30% more). Where no single building fits the prefab band the slot grants two (2 Stirling Generators, 2 Triboelectric Scrubbers, or an MDS Laser plus a Fusion Reactor). After Goal 5 the Sol 50 Mystery takes over. Full pool, values and rules on goals-and-timer.html; prefab reward values on prefabs.html. In StS as Code/StS_GoalTimeline.lua and Code/StS_GoalsPage.lua.goal machinery Lua/Colony.lua MissionGoalUpdate / SetupMissionGoals (goal:Completed then reward:Execute); objective types Data/SponsorGoals.lua; reward effects Lua/ClassDefs/ClassDef-Effects.generated.lua (RewardFunding, RewardResearchPoints, RewardSponsorResearch, RewardPrefab, RewardApplicants, RevealNextTechInField, SpawnRocketInOrbit, SpawnSubsurfaceDeposits); choice popup Lua/MarsStoryBits.lua:58
Map conditionsFoundation Tested v2.9; Mission Goals page readout + read-once green notification Tested v2.111; unified two-medium-four-extreme roll over six conditions Tested v2.222 (see the Map conditions page for per-condition status)Each new colony gets six conditions (like Against the Storm's map modifiers), fixed for its life, shown in a click-to-read "Map Conditions" notification (green frame) and listed permanently on the Mission Goals page below the commander profile: Rocket (fleet, travel time, free pods), Dust Storm vs Cold Wave prevalence, a Funding trade-off (Rare Metals vs tourist funding), Sensor Towers (range vs boost), Vistas vs Research Sites, and a map-derived Topography Metal & Rare Metal shift (flat -20% / rough 0% / steep +20%). Every condition has a medium (default) value and two extremes, and across all six the colony always lands on exactly two medium and four extreme; Topography is not rolled (the map fixes it) but counts toward that target, so the five rolled conditions fill the rest. The shipped foundation rolls the six-condition plan, resolves each value on demand through a read API (StS_GetMapCondition), and shows the notification (each line falls back to the raw rolled value until its condition file adds a readout); the same readout is appended to the Mission Goals page below the commander profile, and clicking the notification opens the readout then clears it for good (close_on_read plus a per-colony read flag keep it from reappearing once read); each condition's gameplay effect lives in its own follow-up file. In StS as Code/StS_MapModifiers.lua; full design and per-condition game-code translation on its own page: map-conditions.html.roll persisted in a GlobalVar (Docs/LuaSavegame.md.html:11,19), rolled at OnMsg.NewGame like SessionRandom (Lua/Colony.lua:89-94); notification AddCustomOnScreenNotification (Lua/UI/OnScreenNotification.lua:708, green frame priority = "NormalTerraforming" at :478, close_on_read at :230-233), click popup CreateMessageBox (Lua/UI/MarsMessageQuestionBox.lua:133); page readout appended in MissionProfileDlg:GetMissonProfileText (Lua/MissionProfileDlg.lua:24-58); Topography from GetMapChallengeRating (Lua/PreGameMission.lua:568) bucketed like the local MapChallengeRatingToDifficulty (Lua/UI/PlanetUI.lua:93-103)
General RulesTested v2.56; green frame v2.111; 86-char per-line cap Tested v2.117A companion to the "Map Conditions" notification: a single dismissable on-screen notification (green frame, like the terraforming notifications) whose readout lists ONLY the invisible, map-wide rules the player cannot learn anywhere else - removed systems (Below and Beyond off, rival exploits gone), tech-order randomization, the escalation curves (storms, meteors, inflation, research decay), and map-wide tweaks (idle/shut-down maintenance, resource parity, cold ground). Deliberately NOT listed, because each is already shown to the player: per-building and per-tech changes (now on the building's own description and on the tech that unlocks it), the in-play picks (sponsor/commander/mystery/Pre-Founder announce themselves when they fire), resupply costs (the Resupply screen), sponsor numbers (the sponsor panel), the per-run Map conditions (their own notification), and self-evident on-screen facts. The message box does not scroll (its XScrollArea is present but non-functional), so the readout is kept short enough to fit one screen, with each body line capped at 86 characters (the length of the "Rivals give no help" line) so it renders on one screen line without wrapping. Clicking opens the readout and removes the notification (close_on_read); a per-colony read flag keeps it from reappearing once read, and it re-shows on CityStart/LoadGame until read. In StS as Code/StS_GeneralRules.lua. Filter and no-scroll rule documented in CLAUDE.md "What belongs in the General Rules notification".AddCustomOnScreenNotification (Lua/UI/OnScreenNotification.lua:708, green frame priority = "NormalTerraforming" at :478, close_on_read at :230-233); click popup CreateMessageBox (Lua/UI/MarsMessageQuestionBox.lua:133); non-scrolling box Lua/XTemplates/MarsMessageBox.lua:44-64; read flag in a GlobalVar (Docs/LuaSavegame.md.html:11,19)
Setup screen "Effects" boxTested v2.44 (mechanism; the four-section text was expanded in later builds)On the mission setup screen the "Effects" box, in vanilla, lists the combined effects of the chosen sponsor, commander and game rules, and shows the word "Random" when that list is empty. Because the mod hides the sponsor and commander from the summary and removes the game-rules row, the box always showed the bare "Random". It is replaced with a four-section campaign summary - Randomized Setup, Randomized Map Conditions, Escalating Difficulty, Balanced Gameplay - that describes what the mod does. Tutorial and challenge runs keep the base text. In StS as Code/StS_SetupEffectsText.lua.GetDescrEffects (Lua/PreGameMission.lua:535-566; "Random" fallback at :562-564), rendered by PGMissionObject:GetEffects (Lua/X/XPGMission.lua:232-234)

Map black at start (hidden map) - full documentation

The surface starts fully black and is uncovered by scanning. Implemented and tested in-game as Code/StS_HiddenMap.lua. Requires Below & Beyond (picard): the darkness is that DLC's underground overlay, forced onto the surface.

Three stages of visibility

Every sector is in one of three stages, re-evaluated as sectors are scanned. A sector is stage 3 when it is unexplored and none of its eight neighbours is scanned; stage 2 when it is unexplored but touches a scanned sector; stage 1 when it is scanned.

The darkness overlay

Surviving Mars has no surface fog of war; the terrain is fully visible from the first frame. The only real terrain-darkness is the Below & Beyond underground overlay, and it can be driven on the surface:

The base game ships only stubs (Lua/RevealDarkness.lua:9,21, CanReveal returns false), which is why the DLC is required. It coexists with "No underground / No asteroids": that content is removed in code so the DLC can stay loaded for this overlay (see Cut features).

Rollover, scan, and build blocking

Four base-game methods are wrapped at ClassesBuilt:

Known caveat: zoom / camera

The darkness is a height-band fog, so it is camera-height dependent: zoomed in a sector reads black, zoomed far out more of the map reads revealed. Caves never show this because underground clamps the camera zoom; the surface allows full zoom-out. Tuning the band, or constraining zoom, is the open item.

Base-game alternative not used: camera lock

Considered but not used, kept as a fallback if the DLC dependency is ever dropped: LockCamera / UnlockCamera freeze all camera movement while leaving the UI clickable (Lua/Camera.lua:29-37, Lua/PhotoMode.lua:171-174), and cameraRTS.SetCamera aims at the starting sector (Exploration.InitialSector). That hides the map by pinning the view rather than darkening the terrain, and needs no DLC.

Sources

Base game, paths relative to external/SurvivingMars. Sensor scanning, sector reveal and the initial reveal are in Lua/Exploration.lua; the sector rollover and scan click in Lua/UI/OverviewModeDialog.lua; orbital probe scanning in Lua/OrbitalProbe.lua. The underground darkness system is in Lua/RevealDarkness.lua (base stubs), driven in the Below & Beyond DLC and via hr.RenderRevealDarkness (found in ChoGGi's "Show All Underground"); camera locking in Lua/Camera.lua and Lua/PhotoMode.lua.

Random colony site - full documentation

The pre-game Colony Site screen is skipped: the player never picks a landing spot and never sees the map's Threats and Resources in advance. Clicking NEXT on the rocket payload screen starts the game on a random landing spot, and that button is relabeled START ON RANDOM SITE. Implemented as Code/StS_RandomColonySite.lua. No DLC required.

Requirement: every existing landing spot equally likely

A landing spot is a whole-degree (latitude, longitude) coordinate on Mars, and the map is a deterministic hash of that coordinate (seed = xxhash(lat, long), Lua/UI/PlanetUI.lua:1591), so each coordinate is one fixed map. The requirement is that every such coordinate has the same chance of being drawn. Two of the game's own helpers are deliberately not used because they violate this:

Instead the mod draws latitude and longitude directly and uniformly over the whole-degree grid, so every coordinate (poles included) is equally likely: lat = (AsyncRand(181) - 90) * 60 gives -90..90° and long = (AsyncRand(360) - 180) * 60 gives -180..179° (180 and -180 are the same meridian, so 360 distinct values avoids double-counting). AsyncRand(n) returns 0..n-1 (Lua/PreGameMission.lua:664); coordinates are in arc-minutes (degree×60), the unit the presets and RoundCoordToFullDegrees use (Lua/UI/PlanetScene.lua:248).

The pre-game flow it changes

New Game runs one dialog (PGMission) with three internal screens in order: sponsor, payload, landing (Lua/XTemplates/PGMission.lua:11). The payload screen's NEXT action only switches the dialog to the landing screen (OnActionEffect="mode", OnActionParam="landing", Lua/XTemplates/PGMissionPayload.lua:141-147). That landing screen is the Colony Site (PGMissionLandingSpot), which lists THREATS (string id 4271) and RESOURCES (id 4270) and whose START button picks the map and begins the game (Lua/XTemplates/PGMissionLandingSpot.lua:192-197,361,401). The mod makes the game start at the point the Colony Site screen would open, so that screen never functions.

Skipping the screen

The Colony Site screen builds its context object through the global LandingSiteObjectCreateAndLoad (Lua/UI/PlanetUI.lua:1515). The same function feeds two other screens - the challenge landing screen and the in-game planetary view for expeditions and asteroids - so it must not be hijacked wholesale. The three callers are distinguishable by their flags: the challenge screen passes challenge_mode = true, the planetary view passes planetary_view = true, and only the pre-game mission call passes neither (Lua/XTemplates/PGMissionLandingSpot.lua:7, PGChallengeLandingSpot.lua:7, PlanetaryView.lua:7). The mod wraps the function and acts only when both flags are false, leaving the other two screens untouched.

Picking the site and starting

Relabeling the NEXT button

Because NEXT now starts the game, the payload button is relabeled. Its label is the string NEXT (id 5453), shared by the sponsor, payload and landing screens, so the shared string is left alone; instead the mod sets ActionName on the one payload next action node inside XTemplates.PGMissionPayload (found by a small recursive walk of the template tree). Setting a template action's ActionName at run time is the game's own idiom (Lua/XTemplates/GameCheatShortcuts.lua:440, Lua/XTemplates/PlanetaryView.lua:1165). The new label is Untranslated("START ON RANDOM SITE"), which sidesteps any translation-id clash. Both the wrap and the relabel install at ClassesBuilt.

What is given up

Skipping the screen also removes its CUSTOM-coordinates entry, which let a player type exact coordinates to replay a specific map (Lua/XTemplates/PGMissionLandingSpot.lua:247), and the up-front Altitude / Temperature / Topography readout, not only Threats and Resources.

Choose your Commander - full documentation

The Commander Profile is chosen in two stages. On the setup screen the Commander row is a multi-select "Possible Commanders" checklist of which commanders can appear this run (all checked by default; Below and Beyond commanders and vanilla Politician hidden). The colony then starts with no commander, and a non-dismissable "Select your Commander" notification appears on the map at colony start; clicking it opens a choice of a random three drawn from the checked commanders and the chosen one's bonuses are applied live. In Code/StS_CommanderChoice.lua, matching the sponsor choice; the setup checklist is the shared framework in Code/StS_MissionPoolUI.lua (mechanism described under Choose your Mystery). No DLC required.

The choice notification. A non-dismissable "Select your Commander" notification is added on CityStart and LoadGame (guarded, so it never shows once the commander is chosen). Clicking it opens the choice; picking a commander removes the notification. Same click-notification pattern as the sponsor and the "Map Conditions" readout (AddCustomOnScreenNotification / RemoveOnScreenNotification, Lua/UI/OnScreenNotification.lua:708,778); the callback is a named global so it persists by name across a save.

No commander at start

The commander is read everywhere through GetCommanderProfile() (Lua/PreGameMission.lua:268-276) and applied at colony start in Colony:Init (Lua/Colony.lua:53, :81) and Colony:GameStart (Lua/Colony.lua:396, :403); its bonus_rockets is read by GetStartingRockets (Lua/City.lua:310-313). The mod wraps GetCommanderProfile to return a custom no-effect placeholder (PlaceholderCommanderProfile, defined in items.lua) until the player has chosen, so every start-time read gets an empty profile and the colony begins with no commander bonus. The placeholder replaces the game's built-in None profile, a degenerate stub with no display name or effect that made the in-game Mission Profile dialog throw when it resolved the commander text, hiding the Mission Goals list; the placeholder carries the same no-effect behaviour but valid, neutral fields, so the dialog renders and the goals show. Reading at the consumption point makes this immune to the pre-game flow resetting mission params. The "chosen" state is a key on g_CurrentMissionParams, which is wiped per new game by InitNewGameMissionParams (Lua/PreGameMission.lua:187-209) and persisted by PersistSave (:221-225), so it is false for a fresh colony and restored on load.

Hiding the commander from the setup UI

Three separate places show the commander before the game, each patched additively:

The event and applying the bonuses live

Clicking the "Select your Commander" notification shows WaitStoryBitPopup (Lua/MarsStoryBits.lua:58-79), the game's own choice popup, with a random three drawn from the full commander pool (every profile except the None and Random placeholders, so the mod's added Prospector is included), re-rolled each game. On the pick it reproduces every bonus colony start would have applied:

Audit and status

The commander roster is built from scratch (full list: Commander Profiles): every offered profile is wiped and rebuilt to one perk, one free tech and one prefab or unit, sorted into four groups (Water, Colonist, Economy, Other), 16 profiles, 4 per group. The apply path is unchanged - EffectsApply runs the perk (Effect_ModifyLabel) and the prefab (Effect_GrantPrefab), tech1 grants the free tech, bonus_rockets spawns Rocket Scientist's rocket, the deposit reveal covers Prospector and Geologist, and a supply-pod cargo drop delivers the three rover units (RC Transport, RC Explorer, RC Commander). The vanilla preset ids are reused so id-coupled perks still fire - only Psychologist needs it, its +5 rest-sanity reading Lua/Units/Colonist.lua:1692. The pick mechanism (checklist, notification, popup) is tested; the from-scratch roster is Built, untested (v2.95). As a PoC this targets new colonies; on load of a picked save the choice and its baked-in bonuses persist and nothing re-applies.

Choose your Mystery - full documentation

The Mystery is chosen in two stages. On the setup screen the Mystery row is a multi-select "Possible Mysteries" checklist of which mysteries can appear this run; the colony then starts with no mystery, and on sol 50 an event offers three of the checked mysteries and the chosen one starts live. In Code/StS_MysteryChoice.lua, with the shared checklist framework in Code/StS_MissionPoolUI.lua. No DLC needed.

The setup-screen pool

The base game already appends every accessible mystery to the Mystery row's item list at ClassesBuilt (Lua/PreGameMission.lua:154-178). The mod replaces that row's list item with a checkbox item (cloned from the shipped Game Rules checkbox, Lua/XTemplates/GameRuleItem.lua, using GetCheckboxImage, Lua/GameRules.lua:112) whose checkbox toggles a set on g_CurrentMissionParams, and hides the "Random"/"None" pseudo-entries. Mysteries you have not finished are pre-checked and finished ones start unchecked, read from AccountStorage.FinishedMysteries (set on MysteryEnd, Lua/Mysteries/Mysteries.lua:102-110); finished mysteries keep their green check. The collapsed row shows "All possible" or "N of M possible" by wrapping PropChoice:OnPropUpdate (Lua/XTemplates/PropChoice.lua:28-56). The set persists and resets with the rest of the mission params (PersistSave / InitNewGameMissionParams, Lua/PreGameMission.lua).

No mystery at start

At colony start Colony:Init calls SelectMystery (Lua/Colony.lua:63), which reads g_CurrentMissionParams.idMystery and sets self.mystery_id (Lua/Mysteries/Mysteries.lua:11-44). The mod overrides it to select nothing for a normal game (tutorial and challenge keep the base behavior). The override must be installed on g_Classes.Colony, not the Mysteries parent: HGE flattens inherited methods into the subclass at build time, so overriding the parent after ClassesBuilt does not reach Colony's already-flattened copy.

The sol-50 20:00 event

At sol 50 20:00 - checked on NewHour, firing once day == STS_MYSTERY_DAY and hour >= STS_MYSTERY_HOUR (or any later sol, as a load backstop), the same evening hour as the Sol 1 survey and the goal picks - a WaitStoryBitPopup (Lua/MarsStoryBits.lua:58-79) offers three mysteries drawn from the checked pool: one random pick from each difficulty tier where the pool has one, then the remaining slots filled from the rest of the pool. Difficulty is the mystery class's challenge_mod - 20 Easy, 40 Normal, 60 Hard - and the label is already in the display name ("... (Easy/Normal/Hard)"). If the pool is empty (every mystery unchecked) it falls back to the full accessible list. Picking one starts it by replicating CheatStartMystery (Lua/Mysteries/Mysteries.lua:114-165) minus its cheat gate: set mystery_id, add the mystery's techs to research, then InitMysteries which creates the mystery object and fires MysteryChosen.

Status

Tested (v2.41, in-game). The colony starts with no mystery, the setup checklist pre-checks unfinished mysteries and shows the "N of M possible" summary, and the sol-50 event offers three from the checked pool. New colonies only; a chosen mystery persists in the save. The v2.42 follow-up hides Below and Beyond mysteries from the list (built, not re-tested in-game). Known base-game trait: mysteries are authored to begin at colony start, so a chosen mystery's opening event may fire right away when it starts on sol 50.

Pre-Founder event choice - full documentation

The vanilla Pre-Founder Stage event pool is emptied and replaced by the mod's own hand-authored events. In the night between Sol 2 and Sol 3 the mod force-fires one of them at random: the player never picks which event fires, only how to answer its replies. So exactly one pre-founder event happens per game. In StS as Code/StS_PreFounderEventChoice.lua. No DLC required. The vanilla pool - 19 events, triggers, choices and rewards - is documented in story-events.html.

Emptying the pool

The pre-founder events are the StoryBits with Category = "Tick_BeforeFounders" (Data/StoryBit/Boost*.lua, 19 in the base game). The engine adds each to the per-game triggerable list at game start only if it is Enabled and passes its EnableChance roll (Docs/ModItemStoryBit.md.html: EnableChance is "the initial chance for this StoryBit to be added to the list of StoryBits which may be triggered in each game"); the runtime list is g_StoryBitStates, keyed by id (Lua/Buildings/PlanetaryAnomaly.lua:171). At ClassesBuilt the mod sets EnableChance = 0 and Enabled = false on every such StoryBit, so none is added at start and none fires on its own. Verified by the CityStart log line, which lists the pre-founder ids still in g_StoryBitStates (expected: none). Rocket Short-circuit (Boost17) is not in this category - it fires on a manual rocket launch, its own trigger - so it is left untouched.

The event and firing one live

In the Sol 2 to Sol 3 window (checked each game hour and on load; skipped under the tutorial and the Story Bits Disabled game rule), the mod picks one of its own StS_PF_ events at random - only ones the player currently qualifies for, i.e. whose every StoryBit Prerequisite passes now (the same cond:Evaluate the engine runs to gate a normal tick; the pre-founder prerequisites are colony-wide, and the events with none always qualify), and drops any whose reward would be redundant. The chosen event is force-fired with ForceActivateStoryBit(id, MainMapID, false) - the same call shipped code uses (Lua/Buildings/PlanetaryAnomaly.lua:338) - which starts the event immediately, ignoring suppression, delay and prerequisites (Docs/ModItemStoryBit.md.html, ActivateStoryBit). The chosen preset is flipped back to Enabled / EnableChance 100 just before, so its one-time state records normally. The chosen id and a "shown" flag live on g_CurrentMissionParams (wiped per new game, persisted on load), exactly as the Commander and Mystery choices; a force-fired one-time StoryBit records its state, so nothing re-fires on load.

Status

Built, untested (v48). Two engine behaviours the build's logs confirm: that disabling keeps the events out of g_StoryBitStates at start (the CityStart log), and that ForceActivateStoryBit fires the chosen event despite the empty pool (the fire log prints state_before=false state_after=true).

Map conditions foundation - full documentation

The shipped code in Code/StS_MapModifiers.lua is the foundation of the map-conditions system: it rolls each colony's conditions, stores them, exposes them to the per-condition code, and shows the notification. It applies no gameplay effect - every condition's effect is built in its own follow-up file. The full per-condition design and game-code translation is on the Map conditions page; this section documents only what the foundation does. No DLC required.

The roll

Five conditions are rolled once per colony; the sixth (Topography) is derived from the map, not rolled. Each rolled condition has one medium value (rocket "pods", the others "even") and two extremes (rocket fast/big, weather dust/cold, funding metals/tourism, sensor range/boost, vistas vistas/research). Across all six conditions every run lands on exactly two medium and four extreme. Topography is not rolled but counts toward the target (rough is its medium, flat/steep its extremes), so the five rolled conditions fill the rest: rough Topography leaves one medium and four extreme among the five, an extreme Topography two medium and three extreme. Because Topography is only known reliably in-game, the roll persists a plan - a Fisher-Yates-shuffled priority order of the five plus a drawn extreme direction for each - and StS_GetMapCondition resolves the medium/extreme split on demand against the live Topography (the first N in the order are medium, N = 1 if rough else 2). Draws use AsyncRand(n) (0..n-1).

Persistence

The plan (the priority order plus each extreme's direction) lives in one GlobalVar("StS_MapConditions", false), which the engine serializes into the save automatically (Docs/LuaSavegame.md.html:11,19). It is rolled in OnMsg.NewGame - which fires on a new colony but not on load - guarded by an "already set" check, the same pattern the base game uses for its own session randomness (g_SessionSeed = g_SessionSeed or AsyncRand(), Lua/Colony.lua:89-94). This replaces the earlier three binary slots and their StS_GetMapModifier API; the only external reader (Code/StS_HarsherDisasters.lua) fetches the old function with rawget and treats it as absent, so dropping it does not crash.

The read API

Three globals, for the per-condition files to consume (they fetch via rawget(_G, "StS_GetMapCondition"), since reading an undefined global throws in the mod sandbox):

The notification

A pinned notification is added on CityStart and LoadGame with the game's modding hook AddCustomOnScreenNotification (Lua/UI/OnScreenNotification.lua:708) as dismissable = false, expiration = -1 so it stays for the whole run (idempotent by id, so re-adding on load does not duplicate). Clicking it opens a CreateMessageBox (Lua/UI/MarsMessageQuestionBox.lua:133) that walks StS_MapConditionOrder and prints one line per condition: the condition's own readout if it has registered one, otherwise a Label: value fallback so the notification is meaningful before any effect file ships. The click callback is a named global so it need not be persisted. Player-facing wording is a first draft.

Status

Tested v2.9. The roll, persistence, read API and notification are in place, and all seven per-condition effects are now built (untested) in their own files. Targets new colonies: a colony started before the foundation shipped has no roll, so it shows no notification until a new game.

Rebalance and difficulty

Retuned mechanics and rising pressure: no single best path, and a game that gets harder over time. Status is Implemented, Confirmed, or Planned. The last column points to the base-game code, with UNVERIFIED for a spot not yet located.

FeatureStatusWhat it doesBase-game code
Building and tech descriptionsTested v2.56Every building the mod rebalances states its new, non-vanilla values on its own in-game description, and the tech that unlocks it does the same, so the player reads the change where he builds or researches instead of guessing from remembered vanilla values. Covers MOXIE, Power Accumulator, Sensor Tower, the three large factories and the Ranch, plus the unlock techs Low-G Hydrosynthesis, Micro Manufacturing and 3D Machining. Set on BuildingTemplates[id], ClassTemplates.Building[id] and the tech's TechDef[id] at ClassesBuilt, with the base text stashed once so reloads never compound. In StS across the building rebalance files (for example Code/StS_MoxieRebalance.lua, Code/StS_FactoryStaffingCurve.lua, Code/StS_FoodRebalance.lua).description on BuildingTemplate (Data/BuildingTemplate.lua); tech description via the TechDef GlobalMap (ClassDef-PresetDefs.generated.lua:1116); translated-string append (CargoTransporter.lua:978)
Resupply cost rebalanceTested v2.9Retunes Earth import costs and weights plus the Supply Pod price. Singly-bought items (per unit): Orbital Probe 100 to 200 M and 1,000 to 2,000 kg (Adapted Probes still halves the price to 100 M), Supply Pod 100 to 200 M, Drone Hub 150 to 200 M, Drone weight 1,000 to 500 kg. Bulk resources (per 5 units): Food 20 to 40 M and 2,000 to 5,000 kg, Machine Parts 90 to 100 M, Polymers 70 to 150 M, Electronics 100 to 150 M; Metals and Concrete unchanged. In StS as Code/StS_ResupplyCosts.lua.Data/Cargo.lua; Lua/ResupplyItems.lua:19-72; sponsor pod_price (Lua/Buildings/SupplyPod.lua:165-171)
Rocket launch fuelTested v2.121Every rocket needs 40% more Fuel to launch, for every sponsor - extending to the whole map the launch-fuel drawback the Paradox Interactive sponsor carries alone in vanilla. It is a percentage on each rocket's own launch fuel, so a standard rocket rises from 50 to 70 Fuel and SpaceY's lower-fuel Dragon Rocket scales from its own base and stays proportionally lower; Paradox keeps its own flat +30 on top (a standard rocket 70 + 30 = 100). The Advanced Martian Engines tech's flat -20 then lowers a standard rocket to 50 (its description now states this). The map-wide rule is announced on the General Rules readout, not on a building. In StS as Code/StS_RocketFuelCost.lua.each rocket's launch_fuel (standard rocket base 50, Data/BuildingTemplate.lua:2910) via a percent modifier on the AllRockets label - the same Effect_ModifyLabel path the Paradox sponsor's flat +30 uses (Data/MissionSponsorPreset.lua:857-861, Lua/MarsGameEffects.lua:164-176); modifiers combine as base × (100 + Σpercent) / 100 + Σamount (Lua/Modifiers.lua:24,99); applied on CityStart/LoadGame, idempotent (Lua/LabelContainer.lua:38-43)
Inflation always onTested v54A custom inflation, always on for every colony (the player cannot turn it off, the Game Rules selector is removed). It replaces the base game's Inflation game rule (+10% every 20 sols) and is faster and broader: prices rise by 1% of their base each sol starting on sol 2 (sol 1 stays at base, +0%; sol N is +(N-1)%), capping at sol 201 (+200%, triple price) and held flat after - the shared sol-201 endpoint of the Escalate ramps. Applied to every import (all resources, all prefabs, rovers) plus the Rocket purchase price and the Supply Pod price. In StS as Code/StS_Inflation.lua.cargo via ModifyResupplyParams (Lua/ResupplyItems.lua:93); Rocket price g_Consts.RocketPrice (Lua/X/XPGMission.lua:398); Supply Pod price GetMissionSponsor().pod_price (Lua/Buildings/SupplyPod.lua:166); driven each sol on NewDay (Lua/DayTime.lua:56)
Research output decayTested v2.31Every research point the colony earns is worth less the longer the game runs - the output-side mirror of an additive cost inflation, replacing the old per-field cost ramp. Each tech keeps its honest base cost; instead the value of each point falls on the shared sol-201 curve: output = 100 / (100 + steps), steps = clamp(sol - 1, 0, 200), so 100% at sol 1, 50% at sol 101, 33% at sol 201, then held. Drives both real accrual and the on-screen "Research per Sol". In StS as Code/StS_ResearchOutputDecay.lua; overview Escalation to sol 201.Research:ModifyResearchPoints (Lua/Research.lua:624) - the chokepoint every earned point flows through (AddResearchPoints :680) and what GetEstimatedRP returns for the UI (:805); paired with UnmodifyResearchPoints (:634) so the leftover carryover (:698-699) stays exact. Wrapped on g_Classes.Colony (flattened).
Terraforming tech costTested v2.251Green Planet ships every Terraforming research tech at twice the standard per-tier cost: the field's 22-tier cost curve is the standard 1000..40000 curve every other field uses, doubled to 2000..80000, and only the Terraforming Initiative sponsor discounted it, so for every other sponsor terraforming cost double. This halves the field's base costs, so terraforming techs cost the standard per-tier amount for all sponsors. The Terraforming Initiative sponsor's own discount is a separate lever, retuned from 50% to 30% (in Code/StS_SponsorEffects.lua), so that sponsor pays 30% below a standard tech (0.7x) instead of merely cancelling the old doubling. New colonies only (an existing save baked each tech's cost at colony start). In StS as Code/StS_TerraformingTechCost.lua.field costs armstrong/Presets/TechFieldPreset.lua vs the standard curve Data/TechFieldPreset.lua:5-28, read once per colony by Research:InitResearch (Lua/Research.lua:153-166); the sponsor's Effect_TechBoost {Field="Terraforming"} applied via BoostTechField and combined as MulDivRound(cost, 100 - boost, 100) in Research:TechCost (Lua/Research.lua:356-382,400), field boosts summed and capped at 80% (:412)
Harsher disastersTested v2.9Dust Storms and Cold Waves escalate over the game, each rising linearly from 10% of sols active on day 1 to 50% on day 201 (20% by day 51, 30% by day 101), then capped. Intent: start around High and reach roughly Extremely High by day 51, then climb past any vanilla level - the numbers are kept round (10-50%) for readability, so they do not match vanilla exactly. From day 100 the two can run at the same time. Overrides the landing site's rating so every run escalates the same. Storm type is pinned to a third each (plain / Great / Electrostatic), ~67% dangerous. The build drives the linear duty-cycle curve above (see notes). Full matrix: disaster difficulty.disaster data (see disasters.html); Lua/MapSettings.lua, Lua/DustStorm.lua, Lua/ColdWave.lua
Meteor Storms scatter map-wideTested v2.14A regular (weather) Meteor Storm drops its meteors on independent random passable tiles across the whole map instead of concentrating them in one drifting ~1 km capsule. Vanilla covers only ~13% of the map in a single stripe, so a storm either misses the base or hammers one spot of it; this keeps the same meteor count and cadence (~58 per sol) but spreads the barrage map-wide, so most storms clip the colony in rough proportion to its footprint rather than wiping one area. Scripted story/mystery strikes (the "Meteor Storm on your colony" punishment, the Bomb, the Last War bombardment) are left aimed at their dome. In StS as Code/StS_MeteorScatter.lua; base mechanic and coverage math in meteors.html; difficulty scale in disaster difficulty.Lua/Meteors.lua:88-106 (SpawnMeteor: no pos uses GetRandomPassable, uniform whole-map, Lua/Pathfinding.lua:1-6); ambient storm call has no pos (:304) vs scripted strike passing a dome pos (Lua/ClassDefs/ClassDef-Effects.generated.lua:2504); thread keying via CurrentThread() (Lua/Buildings/BaseBuilding.lua:400)
Meteor and Dust Devil escalationTested v2.9The mod escalates Meteors and Dust Devils over the game to fixed abstract targets, scaling linearly from day 1 to day 201 then held: Meteors 0.1% to 1% of the map struck per sol and Dust Devils 0.1% to 1% of the map covered (both 0.325% by day 51, 0.55% by day 101). These are abstractions, not game settings - see Disaster abstractions for how they are translated into the spawn parameters that produce them (storm-driven meteors; devil wave interval). Alongside the amount, every meteor is pinned large and every devil Major and Electric. Sits alongside the live Meteor scatter and the Harsher disasters (Dust / Cold) curve. In StS as Code/StS_MeteorDevilEscalation.lua. Full matrix: disasters.html.Meteors storm_spawntime / spawntime / multispawn_chance (Data/MapSettings_Meteor.lua, Lua/Meteors.lua); Dust Devils spawntime / count_min/max / spawn_chance / major_chance (Data/MapSettings_DustDevils.lua, Lua/DustDevils.lua); driven per sol like Code/StS_HarsherDisasters.lua
Dust Devils during Dust StormsBuilt, untested (v1.9)In the base game Dust Devils cannot exist while a Dust Storm is up - the wave scheduler parks and a starting storm deletes every live devil. The mod lets the two coexist (as it already does for Dust Storm + Cold Wave), so the escalating devil coverage is not throttled by the mod's frequent storms. Done additively, without replacing the base scheduler or removing the deletion: on storm start a mod handler respawns and then maintains the day's target devil population until the storm clears. In StS as Code/StS_DevilsInStorms.lua (see notes).base pause on HasDustStorm (Lua/DustDevils.lua:192,203, markers :154); storm-start wipe OnMsg.DustStorm (Lua/DustDevils.lua:472) fired by StartDustStorm (Lua/DustStorm.lua:142); spawn via GenerateDustDevilIn
Cold ground rewardsBuilt, untested (v2.7)Turns cold ground (surface heat below 210, the vanilla cold-penalty threshold) from a pure penalty into a tradeoff: Solar Panels and Wind Turbines on it produce +20% power, research buildings in a dome on it produce +20% research, and every building on it accumulates 20% less dust (passive, storms and devils). Re-checked hourly so it follows cold waves, heaters and terraforming; the vanilla cold penalty is left in place. Full logic and hooks: Cold ground page. In StS as Code/StS_ColdGround.lua.see the Cold ground page Sources; key hooks Lua/Buildings/ColdSensitive.lua:40-42, Lua/Heat.lua:165, Lua/RequiresMaintenance.lua:178
Earthsick from MoraleBuilt, untested (v2.96)Earthsick no longer comes from Comfort hitting 0. Instead each sol a colonist below Morale 50 has a chance to turn Earthsick - chance = 100 - 2 × Morale percent, certain at Morale 0 and none at 50+ - and recovers once Morale reaches 70. Comfort still matters, but only as one of the three stats that feed Morale. Immunities are unchanged (Martianborn, Refugee, the IMM sponsor, Iron Colonists), and each fresh case still removes 2 applicants. In StS as Code/StS_EarthsickChance.lua; full page: earthsick.html.Colonist:ChangeComfort (Lua/Units/Colonist.lua:3256-3281) replaced to drop the Comfort trigger and clear; per-sol roll and recovery on NewDay (Lua/DayTime.lua:56) over UIColony:GetCityLabels("Colonist"); GetMorale (Lua/Units/Colonist.lua:3285-3287); warning text Lua/StatusEffects.lua:323

Research cost ramp - removed

This mechanic is no longer in the mod. It was replaced by research output decay (Code/StS_ResearchOutputDecay.lua): instead of making techs more expensive, the mod now shrinks research output over time. The detailed notes below describe the old cost ramp and are kept only until this section is rewritten.

Each technology already researched in a field adds a flat +10% to the research-point cost of every other not-yet-researched technology in that same field. It is additive, not compounding: with N techs already researched in the field, an unresearched tech in it costs base × (100 + 10×N) / 100 - so the 1st tech in a field is base cost, the 2nd +10%, the 3rd +20%, the 4th +30%, and on. Fields are independent: researching in Physics never changes a Biotech cost. Intent: an incentive to skip the techs you do not need, field by field. Implemented as Code/StS_ResearchCostRamp.lua. No DLC required.

One cost function, paid and shown together

Research:TechCost(tech_id) (Lua/Research.lua:356) is the only function that returns a tech's point cost. It is what the colony pays - the research-completion check reads it (AddResearchPoints, Lua/Research.lua:686; also the cheapest-tech pick at :654 and the queue-cost preview at :395) - and what the player sees: the "Research cost" line in a tech's rollover flows GetResearchInfo (Lua/Research.lua:751) -> TechPreset:Getcost (Lua/ClassDefs/ClassDef-PresetDefs.generated.lua:1140) -> the rollover text (Lua/Research.lua:1351). Wrapping this one function makes the ramp both real and visible with no second edit.

Counting per field

For the priced tech's field (its own status.field) the wrapper counts the other techs actually researched in that field: it walks the field's tech list, skips the priced tech itself, and counts only those whose status.researched is greater than 0. Both rules are necessary, and together they make the tech-select rollover and the research-queue line show the same number:

Implementation

One hook. At ClassesBuilt the mod wraps TechCost on g_Classes.Colony - the class UIColony is actually instantiated from (Colony:new(), Lua/Colony.lua:99) - not on the Research parent it is inherited from. HGE flattens inherited methods into the subclass at build time, so overriding the parent after ClassesBuilt does not reach Colony's already-flattened copy; this is the same log-confirmed reason StS_MysteryChoice overrides g_Classes.Colony.SelectMystery rather than the Mysteries parent. The wrapper runs the original, reads the field's count of OTHER researched techs (priced tech excluded, researched > 0, free sponsor/commander techs excluded), and returns MulDivRound(cost, 100 + 10×N, 100) (integer fixed-point, this is an integer-only Lua build). Because the result is a multiplier, the true original is stashed once on the class table and the wrapper is always rebuilt from that stash, so the re-firing ClassesBuilt never double-wraps or compounds. No cap: with many techs in a field the last few get expensive, which is the point.

Status

Tested. In-game on a fresh colony every field's first tech reads its base cost, identical in the tech-select rollover and the research queue (the earlier select-vs-queue +10% mismatch is gone), and the ramp is per field. Verified from the colony-start dump (base = select = queue in every field at zero researched) and confirmed in play.

Disaster abstractions - read before implementing

The mod's disaster difficulty is written as abstract target numbers, one per threat, chosen to be human-comparable (the matrix on the Disasters page):

For whoever implements the Meteor and Dust Devil scaling (LLM or human): these target numbers are not game settings and cannot be written into a preset directly - the game has no "meteors per sol" or "percent of map covered" field. You must translate each target into the spawn parameters that produce it, running the wiki's derivations in reverse:

Always verify the implemented spawn settings reproduce the target number (in-game or by the formula), not just that the code runs. The abstraction is the spec; the preset values are the means.

Harsher disasters - full documentation

Dust Storms and Cold Waves escalate over the whole game and, from sol 100, can run at the same time. The mod drives each of the two to a fixed duty-cycle curve - the fraction of the time that disaster is active on the map - and overrides whatever Dust Storm / Cold Wave rating the landing site rolled, so every run follows the same escalation. Built in v1.9 and awaiting its first in-game test - Code/StS_HarsherDisasters.lua. No DLC required (both are base-game disasters). The base mechanics and the numbers this builds on are in disasters.html.

Each disaster is active on a plain linear ramp: 10% of sols on day 1 to 50% on day 201 (20% at day 51, 30% at day 101), overlap unlocking at day 100. (Meteors and Dust Devils escalate on the same 0.1% -> 1% curve, in disaster abstractions / StS_MeteorDevilEscalation.lua.) The curve is the matrix; it is absolute, replacing the site's rolled intensity entirely.

The abstraction: duty cycle

Duty cycle is the share of the time one disaster is active: d = length / (length + gap). Dust Storm and Cold Wave each target the same d independently. In the base game the two never overlap and are not frequent or long enough to fill much of the time (see how much of the time a storm is running and how the two are kept apart); the mod raises each toward half the sols and lets them overlap past day 100 (below).

The escalation curve

Each disaster's duty rises on a fixed linear curve, the same for every landing site:

Day% of sols active
110%
5120%
10130%
201 and after50% (cap)

From the curve to storm numbers

To climb the curve the mod scales each storm's length up and the gap between storms down together (an equal split, so "longer" and "more frequent" contribute the same): the target ratio r = length/gap = d/(1 - d), and each storm is scaled from its day-1 baseline (Dust Storm 30 h / 180 h, Cold Wave 60 h / 360 h, both r0 = 1/6) by f = sqrt(r / r0) - length len0 * f, gap gap0 / f - with a +/-25% random band. The resulting averages:

Day% activeDust Storm (length / gap)Cold Wave (length / gap)
110%~24 h / ~9.2 sols~49 h / ~18.4 sols
5120%~37 h / ~6.1 sols~74 h / ~12.3 sols
10130%~48 h / ~4.7 sols~96 h / ~9.4 sols
20150%~74 h / ~3.1 sols~147 h / ~6.1 sols

There is no length step-down: each disaster's duty is independent, so overlap unlocking at day 100 (below) changes only whether the two may co-exist, not the length/gap math.

Overlap from sol 100

The base game allows only one weather disaster at a time through the shared IsDisasterActive() check in each scheduler (see disasters.html). From sol 100 the mod wraps that check so a lone Dust Storm or Cold Wave no longer reports the map as busy to the other, letting the two run together; a Mystery Dream or Rain disaster still blocks, as before. Every disaster effect - freezing, dust, solar penalty, grounded rockets, stalled construction - reads HasColdWave / HasDustStorm directly, not IsDisasterActive, so both storms keep full effect when they overlap; only the screen tint shows one at a time.

Storm type

Each Dust Storm rolls plain, great (double dust) or electrostatic (lightning) at about a third each, replacing the base game's low great / electrostatic chances. Set in the storm-type roll (Lua/DustStorm.lua:376-385) by raising both chances; the engine's 0-100 roll makes it 33.7 / 32.7 / 33.7.

Weather condition: Dust Storm vs Cold Wave prevalence

The Weather map condition re-weights the 50/50 split between the two disasters: a Dust:Cold prevalence edge of +/-33% (dust / even / cold). Inside the same OverrideDisasterDescriptor wrap, each disaster's target duty is multiplied by its share (dust: Dust x1.33, Cold x0.67; cold: the reverse; even: x1). The two shares always sum to 2, so the combined weather load stays exactly on the curve - only the mix shifts. Read from StS_GetMapCondition("weather"); it replaces the earlier duration-bias slot (the retired STS_WEATHER_LENGTH_BONUS_PCT / StS_GetMapModifier).

Implementation

Implemented as Code/StS_HarsherDisasters.lua (v1.9, first in-game test pending). Integer fixed-point throughout - this is an integer-only Lua build, so the curve math uses MulDivRound and the integer sqrt, no floats. Four parts, all on data the schedulers already read:

Test switches in the file: STS_DEBUG (extra logging plus a one-shot forced overlap at sol 6, via the schedulers' own trigger messages so no "already present" assert) and STS_OVERLAP_SOL (lower it, e.g. to 5, to reach overlap without waiting). Both ship at release values (false / 100); turning them on prints the per-sol numbers and forces a Dust Storm + Cold Wave overlap at sol 6, so the escalation can be checked without a 100-sol wait.

Dust Devils during Dust Storms - full documentation

The base game never lets a Dust Devil exist while a Dust Storm is on the map: the devil wave scheduler parks whenever HasDustStorm is true (Lua/DustDevils.lua:192,203, and the fixed-spot markers at :154), and a starting storm deletes every live devil through OnMsg.DustStorm (Lua/DustDevils.lua:472, fired by StartDustStorm's Msg("DustStorm"), Lua/DustStorm.lua:142). Because the mod drives Dust Storms up to 50% of sols by day 201, that suppression would keep realized Dust Devil coverage far below the escalation target, so the mod lets the two coexist - the same idea as the Dust Storm + Cold Wave overlap above. Built in v1.9, awaiting its first in-game test - Code/StS_DevilsInStorms.lua.

Why not the storm / cold-wave trick. That overlap was unlocked by wrapping one function, IsDisasterActive, which only the two schedulers read while the actual effects read HasDustStorm / HasColdWave directly. Dust Devils read HasDustStorm directly, and that function is shared with every storm effect, so it cannot be wrapped globally - and there is an active per-storm deletion to fight on top. So the same one-line trick does not apply.

The approach: respawn and maintain, fully additive. Rather than replace the base scheduler or remove the deletion handler (both need engine-internal reach), the mod leaves the base untouched and fills only the storm window:

How many to spawn. Not a live count - the base wipe runs before the mod handler, so the pre-storm number is already gone by the time we could read it, and it fluctuates anyway. Instead the target is the average devils-present N the escalation already aims for, derived from the live descriptor (count per wave x lifetime / wave interval, matching coverage = N x dust-ring area / map area). Because GenerateDustDevilIn always bakes a full fresh trajectory (no public way to seed a half-spent devil), the thread staggers its spawns by the descriptor's spawn delay so their deaths desync into the steady-state spread rather than a synchronized cohort. Minions add almost no area (dust-devils.html), so with the storm suppression removed the coverage target maps cleanly onto spawn rate x lifetime x ring area.

All through public calls the base game already uses - GenerateDustDevilIn, HasDustStorm, GetRandomPassableAwayFromBuilding - with no GlobalGameTimeThreadFuncs swap, no OnMsg removal and no bytecode probe. The base wipe is a one-shot at storm start (OnMsg.DustStorm, Lua/DustDevils.lua:472), so a devil spawned after that Msg("DustStorm") is not wiped and lives out the storm.

Sources

Base game, paths relative to external/SurvivingMars; Space Race in gagarin.hpk (decompiled). Disaster mechanics: disasters.html.