Every new colony gets a set of conditions, in the spirit of Against the Storm's map modifiers: fixed for the colony's whole life, announced by a "Map Conditions" notification the player clicks once to read (reading clears it for good), and listed permanently on the Mission Goals page below the commander profile. Five are rolled per colony; one (Topography) is not rolled at all but read from the map the run landed on. Every run lands on exactly two medium and four extreme across the six. This page is the mod's design; the shipped build rolls and shows these conditions and applies each one's gameplay effect - all six built and tested (see Status). It gets its own page because the system is large: six conditions, each with its own game-code translation.
This replaces and expands the old three-slot "Map modifiers" feature listed under Randomization on the feature index. The roll, persistence and notification are inherited from Code/StS_MapModifiers.lua; the new conditions add their own couplings into the disaster, funding, exploration and landscaping systems.
Two kinds. Rolled conditions (five) are drawn per colony and independent of the map. The one derived condition (Topography) is a deterministic function of the map's rating - it looks random only because the landing site is random. Every condition has one medium value (the default, no special change) and two extremes. The roll constrains the colony to exactly two medium and four extreme across all six (see the roll).
| # | Condition | Extreme | Medium | Extreme | Kind |
|---|---|---|---|---|---|
| 1 | Rocket | fast: 1 rocket, 1-day travel | pods: 1 rocket, 2-day travel, +5 Pods | big: 2 rockets, 3-day travel | rolled |
| 2 | Dust Storm vs Cold Wave | Dust Storms +33% / Cold Waves -33% | even: roughly 1:1 | Cold Waves +33% / Dust Storms -33% | rolled |
| 3 | Funding | Rare Metals +20% / Tourists -20% | even: both unchanged | Tourists +20% / Rare Metals -20% | rolled |
| 4 | Sensor Towers | +33% range / -33% boost | even: as defined | -33% range / +33% boost | rolled |
| 5 | Vistas | +33% more Vistas | even: default split | +33% more Research Sites | rolled |
| 6 | Topography | Flat: Metal & Rare Metal -20% | Rough: 0% | Steep/Mountainous: +20% | derived from map |
Across all six conditions, every run lands on exactly two medium and four extreme. So a colony is never fully neutral and never extreme on every axis - it always shows four extremes and two defaults. Topography is not rolled (the map fixes it: rough is its medium, flat and steep its extremes), but it counts toward the target, so the five rolled conditions fill the rest: if Topography lands rough (medium), the roll produces one medium and four extreme among the five; if it lands flat or steep (extreme), the roll produces two medium and three extreme. Either way the colony ends on two medium, four extreme.
Because Topography is only known reliably in-game (not necessarily at the roll, which fires at
OnMsg.NewGame), the roll does not resolve concrete values up front. It persists a plan:
a shuffled priority order of the five rolled conditions plus a chosen extreme direction for each. Each
condition's value is resolved on demand by StS_GetMapCondition against the live Topography -
the first N in the priority order are medium (N = 1 if Topography is rough, else 2), the
rest take their chosen extreme. Every reader goes through StS_GetMapCondition and only reads
in-game (on CityStart/LoadGame and later), when Topography is available, so the
two-medium-four-extreme guarantee always holds.
The plan lives in one GlobalVar, which the engine serializes into the save
automatically (Docs/LuaSavegame.md.html:11,19), rolled once in OnMsg.NewGame
(fires on a new colony, 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). The const-modifier effects are re-applied on every
CityStart and LoadGame, keyed by our ids so re-applying never compounds. A
"Map Conditions" notification (AddCustomOnScreenNotification,
Lua/UI/OnScreenNotification.lua:708) announces them; clicking opens a CreateMessageBox
with the full readout, then the notification clears itself (close_on_read plus a persisted
StS_MapConditionsRead flag) so it never re-shows. The same readout is built once
(StS_MapConditionsLines) and also appended to the Mission Goals page below the commander
profile (Code/StS_GoalsPage.lua), so the two never drift.
Length budget and voice. Each readout is one plain sentence that names the cause and lets its
effect follow (no Label: value colon), with a verb that carries the direction (mountains
offer more ore, flat land thins it, a region leans to one disaster). It must fit
on one line on the Mission Goals page, so no condition line runs past 60 characters (the on-screen
width is that plus the "- " bullet). The current longest are 57: the funding "tourist economy" line and
the vistas "strange geology" line. Any readout that would read longer must be shortened.
The colony's starting fleet and rocket travel time, as one of three trade-off packages. Its
medium is the middle package (pods: 1 rocket, 2-day travel, +5 free Supply Pods); the
extremes are fast and big.
| Package | Starting rockets | Travel time per leg | Extra |
|---|---|---|---|
| A | 1 | 1 day (base, 25 h) | - |
| B (middle) | 1 | 2 days (+100%, 50 h) | +5 free Supply Pods |
| C | 2 | 3 days (+200%, 75 h) | - |
The abstraction "N-day travel" is N times the base leg. The base Earth-Mars leg is
25 h (TravelTimeEarthMars = 750000 at scale HourDuration = 30000,
Lua/_const.lua:793-807), about one sol (a sol is 24 h). So the three tiers are the base leg,
the leg doubled, and the leg tripled.
GetStartingRockets (Lua/City.lua:310)
is re-wrapped to substitute a fixed sponsor-side base of 1 (A, B) or 2 (C) for the sponsor's
initial_rockets; the Rocket Scientist commander's +1 still stacks on top, and
City:CreateSupplyShips only fills up to that count and counts existing rockets
(City.lua:305), so it never duplicates on load. Since the base sponsor profile rebalance
already forces every sponsor's initial_rockets to 1, the big tier is what adds
the 2nd rocket.+0% / +100% / +200% modifier on TravelTimeEarthMars /
TravelTimeMarsEarth via g_Consts:SetModifier - the two consts every regular
supply rocket reads (Lua/Buildings/RocketBase.lua:240,826). SetModifier is
idempotent by id and recomputes from base_<prop>, so re-applying on every
CityStart/LoadGame never compounds. The +200% tier is the exact "Long Ride" game-rule
mechanism, so it aggregates additively with the Plasma Rocket breakthrough's -50% the same way Long Ride
does. Some rockets carry their own custom_travel_time_mars / custom_travel_time_earth
that bypasses the const when set (RocketBase.lua:240,826): the Dragon Rocket (SpaceY), the Zeus
Rocket, and the Expedition Rocket (Data/BuildingTemplate.lua:2800-2801, gagarin
Presets/BuildingTemplate.lua). Those custom times are scaled by the same
×1 / ×2 / ×3 from a pristine base - on the template (so newly built rockets
are born scaled) and on the live fleet - selecting rockets by class (IsKindOf "SupplyRocket" and
not IsKindOf "SupplyPod", since SupplyPod is a SupplyRocket
subclass). A fast rocket keeps its edge (its base is below the const) but slows with the tier. Supply
Pods are excluded (Supply Pod, Large Supply Pod / DropPod): pod travel speed is fixed in every tier.RewardSupplyPods (Lua/ClassDefs/ClassDef-Effects.generated.lua:1769-1785): its
Execute (:1781) drops Amount pods with
PlaceBuildingIn(GetMissionSponsor().pod_class or "SupplyPod", MainMapID) and charges no
funding - the same effect the Long Winter events use (Data/StoryBit/LongWinter_BigExtension_*.lua,
Name "free_pods"). The pods sit on Earth; the player loads and launches each one in the
resupply screen exactly like a bought Supply Pod (CreateRefundablePod,
Lua/Buildings/SupplyPod.lua:165-171, reached from
Lua/XTemplates/ResupplyCategories.lua:276) - free only in that the pod itself cost nothing.
Granted once at colony start with Amount 5.Mid-capable. Which of the two weather disasters is more prevalent, as a ratio of how often each is on
the map. This replaces the old weather slot, which biased storm duration (the +20%
STS_WEATHER_LENGTH_BONUS_PCT stretch); the new condition is about frequency.
| Option | Dust Storm | Cold Wave |
|---|---|---|
| extreme | +33% | -33% |
| medium | as defined | as defined |
| extreme | -33% | +33% |
The Harsher disasters escalation splits its occupancy
target 50/50 between Dust Storm and Cold Wave - each carries half the curve
(StS_DisasterParams, StS_HarsherDisasters.lua).
This condition re-weights that split by ±33%, scaling each disaster's length up and its gap
down by its share, inside the same OverrideDisasterDescriptor wrap that already writes each
storm's min_duration / max_duration and spawntime. The combined
weather load stays on the escalation curve; only the mix of the two shifts.
The colony rolls one of three outcomes: a coupled +/-20% trade-off between the two funding streams, or an even split. It never raises both.
| Roll | Effect | Levers |
|---|---|---|
metals | Rare Metals price +20%, Tourist funding -20% | ExportPricePreciousMetals +20%, TouristFundingMultiplier -20% |
tourism | Tourist funding +20%, Rare Metals price -20% | TouristFundingMultiplier +20%, ExportPricePreciousMetals -20% |
even | both unchanged (neutral) | both modifiers 0 |
g_Consts:SetModifier("ExportPricePreciousMetals", "StS_Funding", 0, ±20)
- the same call the shipped RewardExportPrice effect makes
(Lua/ClassDefs/ClassDef-Effects.generated.lua:1638). The const (game default 25 M per unit,
Lua/_const.lua:628-632; StS sets every sponsor to that same 25 M via
StS_BaseSponsorProfile.lua) is read in Funding:CalcBaseExportFunding
(Lua/Funding.lua:38-40) when a rocket or space elevator exports Rare Metals
(RocketBase.lua:854, SpaceElevator.lua:171).TouristFundingMultiplier ±20% (default 100,
Lua/_const.lua:203-207), a percent modifier on the const that scales each tourist's
payout on return: MulDivRound(reward.money, TouristFundingMultiplier, 100)
(Lua/HolidayRating.lua:59,63). This multiplies on top of the raised per-tourist base
payout set in StS_TouristFunding.lua; the two are independent.g_Consts modifier takes effect.
SetModifier's percent argument is a delta on a base of 100 (Lua/Modifiers.lua:62,99):
+20 → ×1.20, -20 → ×0.80, 0 removes the modifier. It is self-idempotent by id
(Lua/Modifiers.lua:180-203), so each apply on CityStart/LoadGame sets
both consts absolutely from the current roll and never compounds. A colony that predates this feature (or
an old save that rolled the removed research value) reads as neutral. In StS as
Code/StS_ConditionFunding.lua.Trades how far a sensor tower's scan boost reaches against how strong that boost is. The boost speeds up map-sector scanning (shown in the overview as "Sensor towers boost X%"), not disaster warning (that is the separate sensor-tower prediction time, left untouched).
| Option | Range | Boost |
|---|---|---|
| extreme | +33% | -33% |
| medium | as defined | as defined |
| extreme | -33% | +33% |
Range is const.SensorTowerScanBoostMaxRange, the zero-boost outer radius; the boost amount
is const.SensorTowerScanBoostMax (390, Lua/_GameConst.lua:109-111). A sector's
boost from the nearest working tower is
MulDivRound(SensorTowerScanBoostMax, min(maxR-minR, maxR-dist), maxR-minR)
(Lua/Exploration.lua:284-306). The options set range ×1.33 / ×1 / ×0.67
and boost ×0.67 / ×1 / ×1.33, applied from a stashed base on CityStart and
LoadGame so a mod reload never compounds. (Not ClassesBuilt: the roll is set in
OnMsg.NewGame, which fires after ClassesBuilt, so at that point the roll does not
exist yet.) Because both are plain consts recomputed from the stashed base, each
factor is inherently multiplicative after every other modifier. The full-boost radius
(SensorTowerScanBoostMinRange) is left untouched, so only the falloff length and the peak boost
move. Tested v2.13 as
StS_RandomTowerRange.lua, which supersedes the
flat-50% range-cut proof of concept that file previously held.
Shifts the map's mix of two scenic surface deposits: Vistas (comfort) and Research Sites (research).
| Option | Effect |
|---|---|
| extreme | +33% more Vistas (some Research Sites flip to Vistas) |
| medium | default split (unchanged) |
| extreme | +33% more Research Sites (some Vistas flip to Research Sites) |
"Vista" is BeautyEffectDeposit (Lua/Buildings/EffectDeposit.lua:108,
display_name "Vista", +10 dome comfort); "Research Site" is ResearchEffectDeposit
(:154, display_name "Research Site", +10/15/20% research). They are sibling
EffectDeposit classes. The map places them as markers at generation; a marker becomes a real
deposit only when its sector is scanned, through RevealDeposits
(Lua/Exploration.lua:417), which calls the marker's
EffectDepositMarker:SpawnDeposit (EffectDeposit.lua:10) to build
PlaceObjectIn(self.deposit_type, ...). There is no ratio field, so the swap hooks that
reveal path: RevealDeposits is wrapped to raise a flag around itself, and
SpawnDeposit is overridden to re-point deposit_type from the source class to the
target class on an AsyncRand(100) < 33 chance while the flag is up. So the swap lands as
each source-type deposit is uncovered and flips about a third of them, which yields roughly +33% more of
the target deposit type (and about a third fewer of the source) when the two are similarly common on the
map. Story-event deposits (the
SpawnEffectDeposit effect) call PlaceDeposit directly, outside
RevealDeposits (Lua/ClassDefs/ClassDef-Effects.generated.lua:2189,2412), so they
never see the flag and are never swapped. Both hooks install once at ClassesBuilt, guarded so a
mod reload cannot double the coin flip. In StS as
Code/StS_ConditionVistas.lua.
Not rolled. Read from the map the run landed on, so it looks random only because the landing site is random. The hillier the map, the more Metal and Rare Metal it carries: Topography shifts every Metals and Rare Metals total on the map by a flat percent.
| Topography | Map rating | Metal & Rare Metal shift |
|---|---|---|
| Relatively Flat | challenge_rating ≤ 59 | -20% |
| Rough | ≤ 99 | 0% (baseline) |
| Steep or Mountainous | ≥ 100 | +20% |
The label is the game's own Topography readout, MapChallengeRatingToDifficulty(rating)
(Lua/UI/PlanetUI.lua:93-103), keyed off the map's challenge_rating
(ActiveMapData.challenge_rating). The percent is a single source of truth in
Code/StS_ConditionTopography.lua
(StS_TopographyMetalPercent); Resource Parity reads it
and scales the Metals and Rare Metals targets in every tier (loose surface piles, shallow deposits,
deep deposits) by (100 + pct)/100 as it normalizes the map on MapSectorsReady.
Water and Concrete are not shifted. Applied totals, on top of the rough baseline (loose 1200, shallow
6400, deep 13000; Rare 3000 / 5000):
| Topography | Loose | Shallow | Deep | Metal total | Rare total |
|---|---|---|---|---|---|
| Relatively Flat (-20%) | 960 | 5120 | 10400 | 16,480 | 6,400 |
| Rough (0%) | 1200 | 6400 | 13000 | 20,600 | 8,000 |
| Steep or Mountainous (+20%) | 1440 | 7680 | 15600 | 24,720 | 9,600 |
Landscaping time is no longer topography-driven. It is now fixed at +100% (2x drone work
time) on every map, set in the same file via
g_Consts:SetModifier("DroneTimeToWorkOnLandscapeMultiplier", ..., 100) (base 100,
Lua/_const.lua:557-563; the multiplier drone landscaping reads at
LandscapeConstructionSiteBase.lua:140), applied on CityStart and
LoadGame. It is the same const the Green Planet landscaping techs modify, and const
modifiers aggregate additively, so those techs keep their full effect measured from a raised baseline.
This map-wide rule is listed in the General Rules readout, not here. Design and prior verification:
docs/landscaping-concrete-cost.md.
The foundation (StS_MapModifiers.lua)
rolls this page's six-condition plan (the two-medium-four-extreme rule above), stores it in one
GlobalVar, resolves each value through StS_GetMapCondition, shows the read-once "Map
Conditions" notification, and lists the same readout on the Mission Goals page below the commander profile.
All six condition effects are built and tested individually; each lives in its own file and registers its own
readout. The unified two-medium-four-extreme roll and the removal of the former Meteors vs Dust Devils
condition are Tested v2.222. Sensor Towers and Vistas moved to +/-33% and Funding and Topography to
+/-20%, and the base Rare Metals price is 25 M, all Tested v2.222. The foundation writeup is the
Map conditions foundation section on the feature page. Per condition:
| Condition | Status |
|---|---|
| 1 Rocket | Tested v2.9 (Code/StS_ConditionRocket.lua): fixed starting fleet (1 / 1 / 2 rockets), +0/+100/+200% travel on the two travel consts (regular rockets) plus the same ×1/×2/×3 on the custom-travel rockets (Dragon / Zeus / Expedition, template + live fleet), and +5 free Supply Pods on pods. Supply Pods keep a fixed travel speed. Rolled by the foundation (fast / pods / big). |
| 2 Dust Storm vs Cold Wave | Tested v2.143 (in Code/StS_HarsherDisasters.lua): re-weights the Dust:Cold prevalence by +/-33% by scaling each disaster's target duty by its share inside the disaster-descriptor wrap, the combined load staying on the escalation curve. Rolled by the foundation. |
| 3 Funding | Tested v2.222, edge +/-20% (Code/StS_ConditionFunding.lua): a coupled +/-20% trade-off per colony - metals raises the Rare Metals export price and lowers the tourist payout, tourism the reverse, even leaves both unchanged. Applied as two g_Consts modifiers on CityStart/LoadGame. The earlier +50% single-edge design (and its research tech-scaling edge) is removed. Rolled by the foundation. |
| 4 Sensor Towers | Tested v2.222, edge +/-33% (Code/StS_RandomTowerRange.lua): rolled range / even / boost scales the scan-boost reach (x1.33 / x1 / x0.67) against the boost amount (x0.67 / x1 / x1.33) from a stashed base on CityStart/LoadGame. Supersedes the flat-50% range-cut proof of concept. |
| 5 Vistas | Tested v2.222, swap chance 33% (Code/StS_ConditionVistas.lua): on an extreme roll each map-placed Research Site (or Vista) has a 33% chance to flip to the other as it is uncovered (roughly +33% more of the target type); the medium roll leaves the split unchanged. Story-event deposits are left untouched. |
| 6 Topography | Tested v2.222, shift +/-20%. Derived live (flat / rough / steep) and shifts every Metals and Rare Metals total -20% / 0% / +20% (applied by Code/StS_ResourceParity.lua, percent from Code/StS_ConditionTopography.lua). Landscaping time is now a fixed +100% on every map, no longer topography-driven. |
Base game, paths relative to external/SurvivingMars; Green Planet in
armstrong (decompiled under external/dlc_unpacked/armstrong). Rockets and travel
Lua/City.lua, Lua/Buildings/RocketBase.lua, Lua/_const.lua; supply
pods Lua/Buildings/SupplyPod.lua, Lua/ClassDefs/ClassDef-Effects.generated.lua;
funding Lua/Funding.lua, Lua/HolidayRating.lua, Lua/Modifiers.lua;
disasters Lua/MapSettings.lua and the per-disaster presets;
sensor towers Lua/Exploration.lua, Lua/_GameConst.lua; vistas and research sites
Lua/Buildings/EffectDeposit.lua, Lua/Exploration.lua; topography
Lua/UI/PlanetUI.lua, Lua/_const.lua. Mod files: map roll
Code/StS_MapModifiers.lua, disasters
Code/StS_HarsherDisasters.lua, sensor
Code/StS_RandomTowerRange.lua, funding
Code/StS_ConditionFunding.lua, vistas
Code/StS_ConditionVistas.lua, landscaping
Code/StS_ConditionTopography.lua. Design
notes: docs/landscaping-concrete-cost.md.