sim
The tide, the labour, the food and water, the six adjacency rules, the triage economy and the score. Everything here is a pure function of the season number: no random numbers, no per-frame integration, no clock of its own. The scene entrypoint owns the clock and emits `season`;…
sim — the rules of Saltcrown
The tide, the labour, the food and water, the six adjacency rules, the
triage economy and the score. Everything here is a pure function of the
season number: no random numbers, no per-frame integration, no clock of its
own. The scene entrypoint owns the clock and emits season; the whole
simulation happens inside tickSeason(n). Two runs with the same decisions
give the same ending, every time.
local Sim = require("game.sim")
The player's four verbs
The ledger is the only slice that calls these. Each returns (ok, why) so a
refusal can be shown as a sentence.
| Call | What it does |
|---|---|
Sim.place(archId) | Raise a course of that kind on the first empty course. Spends stone and commits hands for several seasons. |
Sim.carry(index) | Send a gang to shift that course's stores up to the highest standing course. The people climb at once; the sacks move CARRY_RATE a season, so a carry begun too late is a partial rescue and a course that drowns mid-haul keeps the rest. |
Sim.abandon(index) | Walk away. The people leave, the stores stay and go down with it, and the course stops producing and consuming. |
Sim.sacrifice(index) | Scuttle it as a breakwater. Everything on it is lost at once and the waterline is bought back — refused on the last course standing. |
Read-only, for drawing the ledger
| Call | Returns |
|---|---|
Sim.summary() | Everything the HUD needs in one table: season, waterline, stone, food, water, population, idleHands, workforce, beds, morale, will, housed, camping, campIndex, shrines, carried, lost, carrying, quay state, surge, bank, nextStorm, outcome, card. |
Sim.floorReport(i) | One course, with the adjacency verdicts already applied: watered, spoiling, staffing (0..1), isolated, kilnBoost, seasonsLeft, progress/seasonsOfLabour, and notes — a list of ready-to-print sentences. |
Sim.previewPlace(archId) | What the next course would cost (stone, hands, seasons — all rise with height) and what it would mean: warnings and boosts, both lists of sentences. Show these BEFORE the player commits; they are how the adjacency rules become a decision rather than a post-mortem. |
Sim.costOf(archId, index) | { stone, hands, seasons } for a course of that kind at that height. |
Sim.canPlace(archId) | (ok, why) without mutating anything — for greying out a button. |
Sim.carryEstimate(i) | { ok, why, amount, seasons, hands, to } for the carry prompt. |
Sim.jobs() | The carry gangs currently working. |
Sim.chronicleLines(n) | The last n lines of the run's chronicle, already written as prose. |
Sim.floorAt(i), Sim.morale(), Sim.score(), Sim.card() | The floor record, the morale, the scorecard now, the scorecard as it was when the run ended. |
Run control
Sim.beginRun() leaves the opening and starts the clock — call it when the
title card is dismissed. Sim.holdOpening(true) suppresses the failsafe
that starts the run on its own if nothing else has after 45 seconds.
Events it raises
Beyond the contract's own list: carryStarted(from, to, amount),
floorAbandoned(floor), peopleLost(n, reason), peopleBorn(n),
needsUnmet(kind, amount), storm(season, surge), quayDrowned(food, water),
seasonSettled(season). runEnded carries a second argument — the
scorecard — which a handler taking only the outcome ignores.
The six rules, as the sim actually scores them
- Cistern — waters only the courses BENEATH it. Souls with no cistern above them go thirsty however full the tower's casks are.
- Granary — directly above a working kiln, it spoils
SPOIL_RATEof its stores every season and yields nothing. - Workshop — its stone yield scales with the hands living within three courses. Nobody in reach, no stone.
- Dormitory — with no workplace within three courses its people drift away, a share of them a season.
- Shrine — softens every loss, and lifts morale each season.
- Kiln — every kiln beneath a rising course speeds it, and burns stone for mortar while it works.
Two things that are not in the contract
The landing store. The run starts with provisions stacked on the shoulder, not in a granary. It drowns around season 14, which is the real deadline on the opening: get a granary and a cistern standing before the sea takes the pile you started with.
The camp. floor.people is who SLEEPS there, and only a dormitory ever
has any. Everyone without a bed camps on the lowest course still above
water. That camp staffs the first courses before any dormitory exists, and
it sinks with the bottom of the tower, so work high up needs dormitories
high up.
Tuning
Every number is in RULES at the top of init.luau, exported as
Sim.RULES. The pacing control is COST_RISE: each course costs more stone
than the one below it, because it has to be hoisted past all of them. That
is what keeps a good early economy from topping out the pillar in forty
seasons and leaving fifty with nothing to do but drown.
Scoped to this part · feeds back into the world's score.