0002 — Terraforming uses habitability points, not growth-rate percentages¶
Status¶
Accepted.
Context¶
Two competing models for what a terraforming project does to a planet were on the table:
- %-growth model — each level adds a fixed percentage to the planet's daily population growth rate (e.g. Level 1 = +5% growth, Level 5 = +25%, cumulative). Hostile planets are "rescued" by lifting their growth from negative to positive.
- Habitability-points model — each level adds a fixed amount to the planet's
habitability_score(0–100). Habitability then drives population cap, growth multiplier, and morale through a single shared formula.
The %-growth model is intuitive ("terraforming makes the colony grow faster") but couples the terraforming subsystem to whatever growth formula is current. It also requires a separate "planet rescue" code path because a Barren planet's −0.1%/day baseline needs special handling to become viable.
The habitability-points model treats habitability_score as the single dimensionless quality measure for a planet. Terraforming raises it; everything downstream — max_population, growth multiplier, morale, life-support cost — derives from it via formulas owned by planetary_service.py. This avoids duplicating "what counts as a viable planet" logic across services.
The ground-truth code (services/gameserver/src/services/terraforming_service.py) implements the habitability-points model. The legacy design doc described the %-growth model.
A parallel decision was made about persistence shape:
- Option A — separate
terraforming_projectstable with status (ACTIVE / PAUSED / COMPLETED), monthly progress columns, ongoing resource burn, and a foreign key to the planet. Mirrors how Citadel upgrades are modelled. - Option B — flat columns on
planets(terraforming_active,terraforming_target,terraforming_start_time,terraforming_progress) plus per-level metadata in the existingactive_eventsJSONB column.
Option B was implemented (migration b2c3d4e5f6a7_add_terraforming_columns_to_planets).
A third related decision: whether terraforming is a long monthly-tick process driven by an assigned engineer profession, or a fire-and-forget project with up-front resource cost and gradual auto-progress. The implemented service uses up-front cost + per-tick progression based on planet population, with no profession requirement.
Decision¶
Terraforming is modelled as habitability-points additions to Planet.habitability_score, not as growth-rate percentages.
The 5-level project ladder, with names and credit costs preserved from the original design, applies a fixed +habitability_boost on completion:
| Level | Name | Boost |
|---|---|---|
| 1 | Basic Atmospheric | +10 |
| 2 | Climate Stabilization | +15 |
| 3 | Ecosystem Seeding | +20 |
| 4 | Biome Engineering | +25 |
| 5 | Full Terraformation | +30 |
Habitability is the single quality knob: planetary_service.get_habitability_effects derives effective max-colonists, growth multiplier, and morale bonus from habitability_score / 100. Raising habitability automatically raises all three.
Persistence is flat columns on planets plus a single dict in active_events (keyed type: "terraforming") for level metadata. There is no separate terraforming_projects table.
Resource recipe is credits + organics + equipment (drawn from the planet's own stockpile, not the player's cargo). The legacy ore-based recipe is not implemented.
Consequences¶
Positive:
- One quality field, one set of formulas. Anything that wants to know "how viable is this planet" reads
habitability_score. - Terraforming work doesn't need to know about growth formulas — it just nudges the score and
planetary_servicerecomputes. - No separate table to migrate, query, or join. Status fits in columns and JSONB.
- Cancelling and refunds are simple: clear four columns, drop the
active_eventsentry.
Negative:
- The %-growth narrative ("Level 5 gives +25% growth") in older design docs no longer maps cleanly to the live system. Conversion between the two models requires multiplying through
habitability_ratio = habitability / 100. - Multiple concurrent or queued terraforming projects on a single planet are not directly representable. The
active_eventsdict holds at most one terraforming entry; starting a second project before the first completes is rejected. - Hard caps are applied at write time (boost capped at
TERRAFORMING_MAX_HABITABILITY = 100), so above 90 the marginal value of a project drops sharply.
Neutral:
- The Terraform Engineers profession from the legacy design has no implementation hook. It is documented as
📐 Design-onlyinFEATURES/planets/terraforming.md. - Planet-type reclassification (D_CLASS → H_CLASS at Level 5, etc.) is not implemented. The class enum on the planet stays fixed; only the score changes.
- The cost-scaling formula from legacy
PLANETARY_COLONIZATION.md(×1.25 / ×1.5 / ×2.0 above 40 / 70 / 90 habitability) is not implemented. Costs are flat per level.
References¶
FEATURES/planets/terraforming.md— live behaviour reference.services/gameserver/src/services/terraforming_service.py:TERRAFORMING_LEVELS— authoritative constants.services/gameserver/src/services/planetary_service.py:get_habitability_effects— downstream effects of habitability score.services/gameserver/alembic/versions/b2c3d4e5f6a7_add_terraforming_columns_to_planets.py— schema migration.