summaryrefslogtreecommitdiff
path: root/src/town/words.rs
diff options
context:
space:
mode:
authorskylar <me@casper>2026-11-11 15:24:37 -0800
committerskylar <me@casper>2026-11-11 15:24:37 -0800
commit9ccf951473e21a8b90c25161156fa09a9b227a7b (patch)
treee7bd9858a4cdbc5ae50069db8ca0cf868e436dec /src/town/words.rs
parentd36c54d996f3c14ae811312dbbe621e12a39edde (diff)
Orrery 0.1.0: the galaxy, flight, cities, inventory and saves
Grows the derelict crawler into the whole game, and gets it ready to publish. - A galaxy of star systems joined by lanes, split between four peoples' territories, with a star map and a system map. Systems are modelled with Kepler orbits, moons inside their Hill spheres, belts past the frost line and stations at Lagrange points. - Flight around one place at a time: arcade flight with a throttle fallback where held keys can't be detected, mining rocks for ore, landing, docking at stations, and boarding wrecks that come and go by the calendar. - Your ship to walk about, and capital cities to land at: generated plans with walls, quarters and streets, building interiors, shops, taverns, scholars and armourers. - Fighting with a weapon and a gun ready, six damage types and how each monster takes them, and armour that guards, wears and can be mended. - Everything you own has a weight: a vim-keyed inventory, a pack and the ship's lockers, and trade in ore, salvage, arms and fuel. - Characters with a people and a ship of their own, saved as versioned JSON on every scene change and on quit, with a character sheet, a saves list and a pause menu wherever you are. - `orrery weapons`, `armour`, `galaxy`, `system` and `town` print the generators' output without the TUI. - Release: README install steps for macOS and Linux, a Makefile whose install also works with macOS's install, an MIT licence, and Cargo metadata with rust-version 1.88. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/town/words.rs')
-rw-r--r--src/town/words.rs633
1 files changed, 633 insertions, 0 deletions
diff --git a/src/town/words.rs b/src/town/words.rs
new file mode 100644
index 0000000..251f2b2
--- /dev/null
+++ b/src/town/words.rs
@@ -0,0 +1,633 @@
+//! What things in a city are called, and what people there say.
+//!
+//! Names use the same templates as `names`: `{w}` is a new word in the
+//! culture's tongue, `{n}` a number, `{r}` a Roman numeral.
+
+use super::{Kind, Land, Pond, Purpose, QuarterKind, Role, ShopKind, Thing, Tile};
+use crate::names::{Culture, pick};
+use crate::seed::Rng;
+use crate::space::system::Terrain;
+
+fn fill(culture: Culture, templates: &'static [&'static str], rng: &mut Rng) -> String {
+ culture.expand(pick(templates, rng), rng)
+}
+
+pub fn quarter_name(culture: Culture, kind: QuarterKind, rng: &mut Rng) -> String {
+ use QuarterKind::*;
+ let templates: &'static [&'static str] = match (culture, kind) {
+ (Culture::Qaari, Market) => &["the {w} Souk", "the Great Souk", "the Spice Souk"],
+ (Culture::Qaari, Gate) => &["the Caravan Yards", "the {w} Caravanserai", "the Gate of {w}"],
+ (Culture::Qaari, Homes) => &["the {w} Quarter", "the Lanes of {w}", "the Old Wells"],
+ (Culture::Qaari, Poor) => &["the Lower Wells", "the Dust Quarter", "the {w} Warren"],
+ (Culture::Qaari, Grand) => &["the Rise of {w}", "the Shaded Heights", "the Palace Rise"],
+ (Culture::Qaari, Holy) => {
+ &["the Shrine Steps", "the Deep Well Quarter", "the {w} Sanctuary"]
+ }
+ (Culture::Qaari, Works) => {
+ &["the Dyers' Lanes", "the Glassblowers' Quarter", "the Tanneries"]
+ }
+ (Culture::Qaari, Gardens) => &["the Palm Gardens", "the {w} Oasis", "the Date Groves"],
+ (Culture::Cant, Market) => &["the Exchange Floor", "Barter Deck {n}", "the {w} Market"],
+ (Culture::Cant, Gate) => &["the Intake", "Gate Sector {n}", "the Receiving Yards"],
+ (Culture::Cant, Homes) => &["Hab Sector {n}", "the {w} Habs", "Dormitory Row {r}"],
+ (Culture::Cant, Poor) => &["the Undercroft", "the Sump", "Sector {n}-Low"],
+ (Culture::Cant, Grand) => &["the Primarch's Tier", "the Upper Spire", "the {w} Tier"],
+ (Culture::Cant, Holy) => &["the Litany Steps", "the Engine Precinct", "the Fane Sector"],
+ (Culture::Cant, Works) => &["the Foundry Ward", "Pipe Row", "the {w} Works"],
+ (Culture::Cant, Gardens) => {
+ &["the Coolant Pools", "the Fungus Vaults", "the Moss Terraces"]
+ }
+ (Culture::Houses, Market) => &["the {w} Market", "the Marble Exchange", "the Silk Arcade"],
+ (Culture::Houses, Gate) => {
+ &["the Gate of {w}", "the Customs Quarter", "the Carriage Yards"]
+ }
+ (Culture::Houses, Homes) => &["the {w} Quarter", "Lantern Row", "the {w} Walk"],
+ (Culture::Houses, Poor) => &["the Stews", "Tallow Lane", "the {w} Rookery"],
+ (Culture::Houses, Grand) => &["the Archon's Hill", "the {w} Heights", "the Court Quarter"],
+ (Culture::Houses, Holy) => {
+ &["the Cathedral Close", "the {w} Cloisters", "the Choir Quarter"]
+ }
+ (Culture::Houses, Works) => &["the Perfumers' Quarter", "the Ateliers", "Glass Street"],
+ (Culture::Houses, Gardens) => &["the {w} Gardens", "the Pleasure Gardens", "the Orangery"],
+ (Culture::Drift, Market) => &["the Swap", "the {w} Bazaar", "the Hanging Market"],
+ (Culture::Drift, Gate) => &["the Moorings", "the {w} Berths", "the Dockside"],
+ (Culture::Drift, Homes) => &["the Bunks", "{w} Row", "the Hull Quarter"],
+ (Culture::Drift, Poor) => &["the Scrap", "the Leaky End", "the {w} Tangle"],
+ (Culture::Drift, Grand) => &["Captains' Row", "the High Masts", "the {w} Deck"],
+ (Culture::Drift, Holy) => &["the Lantern Quarter", "the Rememberings", "the {w} Vigil"],
+ (Culture::Drift, Works) => &["the Splice", "the Sail Lofts", "the {w} Yards"],
+ (Culture::Drift, Gardens) => {
+ &["the Green Tanks", "the {w} Hydroponics", "the Garden Hulls"]
+ }
+ };
+ fill(culture, templates, rng)
+}
+
+/// Who works in a workshop.
+fn trades(culture: Culture) -> &'static [&'static str] {
+ match culture {
+ Culture::Qaari => &["weaver", "potter", "glassblower", "dyer", "coppersmith", "rugmaker"],
+ Culture::Cant => &["welder", "wire-drawer", "gear-cutter", "plater", "cogwright"],
+ Culture::Houses => &["perfumer", "jeweller", "tailor", "luthier", "mask-maker", "glazier"],
+ Culture::Drift => &["sailmaker", "splicer", "tinker", "chartmaker", "ropemaker"],
+ }
+}
+
+/// What a building is called: "a house", "House Vastre", "the Qasr Khaseem".
+pub fn building_name(kind: Kind, culture: Culture, rng: &mut Rng) -> String {
+ use Culture::*;
+ let templates: &'static [&'static str] = match (kind, culture) {
+ (Kind::Shop(shop), _) => return shop_name(shop, culture, rng),
+ (Kind::House, _) => &["a house"],
+ (Kind::Tenement, Qaari) => &["a courtyard tenement"],
+ (Kind::Tenement, Cant) => &["a hab-block"],
+ (Kind::Tenement, Houses) => &["a lodging house"],
+ (Kind::Tenement, Drift) => &["a bunkhouse"],
+ (Kind::Manor, Qaari) => &["the House of {w}", "the {w} Mansion"],
+ (Kind::Manor, Cant) => &["the {w} Habitation", "Residence {w}-{n}"],
+ (Kind::Manor, Houses) => &["House {w}", "the {w} Townhouse"],
+ (Kind::Manor, Drift) => &["the {w} Berth", "Captain {w}'s"],
+ (Kind::Palace, Qaari) => &["the Qasr {w}"],
+ (Kind::Palace, Cant) => &["the Primarch's Spire"],
+ (Kind::Palace, Houses) => &["the Archon's Palace"],
+ (Kind::Palace, Drift) => &["the Speaker's Hulk"],
+ (Kind::Temple, Qaari) => &["the Deep Well of {w}"],
+ (Kind::Temple, Cant) => &["the Fane of the Engine"],
+ (Kind::Temple, Houses) => &["the Basilica of {w}"],
+ (Kind::Temple, Drift) => &["the Lantern of the Lost"],
+ (Kind::Shrine, Qaari) => &["a well-shrine"],
+ (Kind::Shrine, Cant) => &["a litany-chapel"],
+ (Kind::Shrine, Houses) => &["a chapel"],
+ (Kind::Shrine, Drift) => &["a waystone shrine"],
+ (Kind::Workshop, _) => {
+ let trade = pick(trades(culture), rng);
+ return format!("a {trade}'s workshop");
+ }
+ (Kind::Warehouse, Qaari) => &["a caravan store"],
+ (Kind::Warehouse, Cant) => &["a depot"],
+ (Kind::Warehouse, Houses) => &["a warehouse"],
+ (Kind::Warehouse, Drift) => &["a cargo shed"],
+ (Kind::Bathhouse, Qaari) => &["the {w} Baths", "a bathhouse"],
+ (Kind::Bathhouse, _) => &["the {w} Baths", "a bathhouse"],
+ (Kind::Guardhouse, Qaari) => &["a gatehouse"],
+ (Kind::Guardhouse, Cant) => &["an enforcer post"],
+ (Kind::Guardhouse, Houses) => &["a guardhouse"],
+ (Kind::Guardhouse, Drift) => &["the watch-house"],
+ };
+ fill(culture, templates, rng)
+}
+
+pub fn shop_name(kind: ShopKind, culture: Culture, rng: &mut Rng) -> String {
+ use Culture::*;
+ use ShopKind::*;
+ let templates: &'static [&'static str] = match (kind, culture) {
+ (Broker, Qaari) => &["the {w} Assay", "the Ore Souk of {w}"],
+ (Broker, Cant) => &["the Ore-Tithe of {w}", "Assay Station {n}"],
+ (Broker, Houses) => &["the {w} Exchange", "the Royal Assayer"],
+ (Broker, Drift) => &["{w}'s Scales", "the Weigh-In"],
+ (Doctor, Qaari) => &["the House of Salves", "the {w} Infirmary"],
+ (Doctor, Cant) => &["the Mending Shrine", "Repair Bay {n}"],
+ (Doctor, Houses) => &["Physician {w}'s", "the {w} Hospice"],
+ (Doctor, Drift) => &["the Patchwright", "the Sick Bay"],
+ (Scholar, Qaari) => &["the Sand Archive", "the House of {w}'s Scrolls"],
+ (Scholar, Cant) => &["the Index of Relics", "Archive {r}"],
+ (Scholar, Houses) => &["the {w} Athenaeum", "the {w} Collection"],
+ (Scholar, Drift) => &["the Rememberer", "the Log-Keeper"],
+ (Tavern, Qaari) => &["the {w} Cistern", "the Thirsty Dune", "the {w} Tea House"],
+ (Tavern, Cant) => &["the Oil Hall", "the Burnt Gasket", "Refreshment Unit {n}"],
+ (Tavern, Houses) => &["the {w} Salon", "the Gilded {w}", "the Masquerade"],
+ (Tavern, Drift) => &["the Slack Line", "the {w} Mooring", "the Last Burn"],
+ (Shipyard, Qaari) => &["the Caravan-Wrights", "the {w} Yards"],
+ (Shipyard, Cant) => &["Hull Foundry {w}", "Drydock {n}"],
+ (Shipyard, Houses) => &["the Royal Yards", "the {w} Shipwrights"],
+ (Shipyard, Drift) => &["the Splicers", "{w}'s Slip"],
+ (Armoury, Qaari) => &["the {w} Blade-Souk", "the House of Edges"],
+ (Armoury, Cant) => &["Armoury {r}", "the Arc-Smithy of {w}"],
+ (Armoury, Houses) => &["the {w} Armourers", "the Duellists' Gallery"],
+ (Armoury, Drift) => &["{w}'s Iron", "the Gun Locker"],
+ };
+ fill(culture, templates, rng)
+}
+
+/// What a ruler is called.
+pub fn ruler_title(culture: Culture) -> &'static str {
+ match culture {
+ Culture::Qaari => "the Well-Warden",
+ Culture::Cant => "the Primarch",
+ Culture::Houses => "the Archon",
+ Culture::Drift => "the Speaker",
+ }
+}
+
+/// "A guard", for people without a name.
+pub fn role_name(role: Role, culture: Culture) -> &'static str {
+ match role {
+ Role::Passerby => "A passer-by",
+ Role::Child => "A child",
+ Role::Porter => "A porter",
+ Role::Pilgrim => "A pilgrim",
+ Role::Stallholder => "A stallholder",
+ Role::Guard => match culture {
+ Culture::Cant => "An enforcer",
+ _ => "A guard",
+ },
+ Role::Keeper(_) => "The keeper",
+ Role::Resident => "Someone who lives here",
+ Role::Servant => "A servant",
+ Role::Ruler => ruler_title(culture),
+ Role::Courtier => "A courtier",
+ Role::Priest => match culture {
+ Culture::Qaari => "A well-priest",
+ Culture::Cant => "A tech-cantor",
+ Culture::Houses => "A priest",
+ Culture::Drift => "A lamplighter",
+ },
+ Role::Worshipper => "A worshipper",
+ Role::Worker => "A worker",
+ Role::Patron => "A drinker",
+ Role::Patient => "A patient",
+ Role::Bather => "A bather",
+ Role::Watchman => "A watchman",
+ Role::Reader => "A reader",
+ }
+}
+
+/// What someone says when you bump into them.
+pub fn lines(role: Role, culture: Culture) -> &'static [&'static str] {
+ use Culture::*;
+ match (role, culture) {
+ (Role::Passerby, _) => chatter(culture),
+ (Role::Stallholder, _) => cries(culture),
+ (Role::Child, Qaari) => &[
+ "Is that your ship? Is it faster than a sandstorm?",
+ "I can hold my breath through a whole dust-wind.",
+ "My brother says wrecks have ghosts in them.",
+ ],
+ (Role::Child, Cant) => &[
+ "I am in my apprentice cycle. I'm not supposed to talk to you.",
+ "Do your engines pray? Ours do.",
+ "I counted all the bolts on the wall. There are four thousand and six.",
+ ],
+ (Role::Child, Houses) => &[
+ "Mother says spacers are common. Are you common?",
+ "I'm going to be an Archon when I grow up. Or a pirate.",
+ "Bet you can't catch me!",
+ ],
+ (Role::Child, Drift) => &[
+ "Have you been to the stars? Which one's the best?",
+ "I was born on a ship, you know. I'm only visiting.",
+ "Tag! You're it!",
+ ],
+ (Role::Porter, _) => &[
+ "Mind your back! Heavy load!",
+ "Two more crates and I can eat.",
+ "Out of the way, it's on my head, not yours.",
+ "They pay by the load, not by the step. Shift.",
+ ],
+ (Role::Pilgrim, _) => &[
+ "I've walked forty days to get here.",
+ "They say a prayer here is heard twice.",
+ "Have you come to pray too? You don't look it.",
+ "I promised my mother I'd come. She's been dead nine years.",
+ ],
+ (Role::Guard, Qaari) => &[
+ "Keep your hands where the sun can see them.",
+ "No blades drawn in the souk, offworlder.",
+ "The gate shuts when the third wind blows. Be in, or be out.",
+ ],
+ (Role::Guard, Cant) => &[
+ "Halt. Proceed. You were not stopped.",
+ "Unit. Your papers are in order. Probably.",
+ "Patrol cycle four hundred and twelve. All nominal.",
+ ],
+ (Role::Guard, Houses) => &[
+ "Move along, spacer. Nothing to see but your betters.",
+ "The Archon's peace is kept here.",
+ "Draw steel in this city and you'll leave it lighter.",
+ ],
+ (Role::Guard, Drift) => &[
+ "Easy, spacer. Nobody's looking for trouble.",
+ "Watch shift. Twelve hours of nothing, mostly.",
+ "If anyone asks, I was awake.",
+ ],
+ (Role::Keeper(_), _) => &["What can I do for you?"],
+ (Role::Resident, Qaari) => &[
+ "You'll take water? Everyone who comes in takes water.",
+ "My grandmother built this house with her own hands.",
+ "Shut the door behind you, the sand gets in.",
+ "A guest! Sit, sit. Not there. There.",
+ ],
+ (Role::Resident, Cant) => &[
+ "Unauthorised presence in a hab-unit. I'll allow it.",
+ "The walls hum at night. It's comforting.",
+ "Visitors are logged. You have been logged.",
+ "My shift starts in an hour. Be brief.",
+ ],
+ (Role::Resident, Houses) => &[
+ "We weren't expecting callers. Do forgive the state of things.",
+ "Mind the portraits. Every one of them is watching you.",
+ "If you're selling something, we already have two.",
+ "Oh! A spacer. How thrilling. Please don't touch anything.",
+ ],
+ (Role::Resident, Drift) => &[
+ "Come in, come in. Nobody locks a door in the Drift.",
+ "It's small, but it doesn't move. I'm still getting used to that.",
+ "Got any news from out there?",
+ "Sit anywhere. Everything's somebody's, nothing's anyone's.",
+ ],
+ (Role::Servant, _) => &[
+ "The household is not receiving.",
+ "I'm to polish the silver before the bell.",
+ "You didn't see me sitting down.",
+ "Wipe your feet. Please. I just did that floor.",
+ ],
+ (Role::Ruler, Qaari) => &[
+ "Every drop in this city is counted, offworlder, and I count them.",
+ "Sit in my shade. Few strangers are offered it.",
+ "The deep erg gives and the deep erg takes. I only keep the ledger.",
+ ],
+ (Role::Ruler, Cant) => &[
+ "The Engine wills, and I implement. State your function.",
+ "You are a variable. I dislike variables.",
+ "Efficiency is devotion. You are wasting both.",
+ ],
+ (Role::Ruler, Houses) => &[
+ "We so rarely receive spacers. How novel.",
+ "Kneel, if you know how. No? Then bow. No? Then stand there.",
+ "My ancestors held this city for eleven hundred years. I intend to beat that.",
+ ],
+ (Role::Ruler, Drift) => &[
+ "I don't rule. Someone has to hold the tiller, that's all.",
+ "The council meets when it meets. Sit, if you like.",
+ "Every ship that docks here is family until it isn't.",
+ ],
+ (Role::Courtier, _) => &[
+ "Petitions are heard at noon. It is never noon.",
+ "I've been waiting for an audience since the last eclipse.",
+ "Don't stand there. That's where the last ambassador stood.",
+ "Have you heard who dined with whom? No? Then what use are you?",
+ ],
+ (Role::Priest, Qaari) => &[
+ "The deep well answers every question with silence. Silence is an answer.",
+ "Drink, and remember who dug the well before you.",
+ "Water remembers. That is why we pray over it.",
+ ],
+ (Role::Priest, Cant) => &[
+ "Blessed be the torque. Blessed be the tolerance.",
+ "Every machine is a prayer that works.",
+ "The Engine turns. Will you turn with it?",
+ ],
+ (Role::Priest, Houses) => &[
+ "The saints loved this city once. We are trying to remind them.",
+ "Light a candle, if you have a coin. Light two, if you have a sin.",
+ "Even the Archon kneels here. Briefly.",
+ ],
+ (Role::Priest, Drift) => &[
+ "Every lantern here is someone who didn't come home.",
+ "We remember the lost so the void doesn't have to.",
+ "If you board wrecks, light one for whoever you find.",
+ ],
+ (Role::Worshipper, _) => &[
+ "Shh.",
+ "...and keep the void from the hull, and the hull from the void...",
+ "I'm praying. Go away. Kindly.",
+ "Pray with us, or step softly.",
+ ],
+ (Role::Worker, _) => &[
+ "Mind the edge, it's sharp.",
+ "Commissions take a month. Two if you're rude.",
+ "Every piece made by hand. My hands. Look at them.",
+ "Pass me that. No, the other one.",
+ ],
+ (Role::Patron, _) => &[
+ "Buy a round and I'll tell you a story. Two rounds, a true one.",
+ "This is my stool. It knows me.",
+ "The wrecks out past the belt? Cursed, the lot of them.",
+ "One more and I'll go home. I said that last night too.",
+ "You've got the look of someone who boards wrecks.",
+ ],
+ (Role::Patient, _) => &[
+ "Don't mind me. I'm dying, slightly.",
+ "The doctor says I'll mend. The doctor says that to everyone.",
+ "Got bitten by something aboard a wreck. Don't ask what.",
+ ],
+ (Role::Bather, _) => &[
+ "Ah. Heaven is hot water.",
+ "Close the door, you're letting the steam out.",
+ "Best thing about a planet: baths.",
+ ],
+ (Role::Watchman, _) => &[
+ "Nothing in here but crates. And me.",
+ "If you're thinking of stealing, think harder.",
+ "I count the crates every hour. Still forty-one.",
+ ],
+ (Role::Reader, _) => &[
+ "Shh. Some of us are reading.",
+ "Did you know the old empire had nine words for vacuum?",
+ "I've been on this page for three days.",
+ ],
+ }
+}
+
+/// What passers-by say when you bump into them.
+pub fn chatter(culture: Culture) -> &'static [&'static str] {
+ match culture {
+ Culture::Qaari => &[
+ "Water and shade to you, offworlder.",
+ "The caravan from the deep erg is three days late. Three!",
+ "Don't drink from the public cistern after dusk. Old law.",
+ "My cousin crews an ore hauler. He says the belt sings at night.",
+ "You smell of ship. Go to the tavern; everyone does.",
+ "The glass storms took the east road again.",
+ ],
+ Culture::Cant => &[
+ "Unit. Your hull is unblessed. Have it seen to.",
+ "The litany runs at the third bell. Attendance is noted.",
+ "Every bolt remembers the hand that turned it.",
+ "Salvage is sacrament. Do not waste it.",
+ "I was a pump-servitor before my promotion. I miss the pumps.",
+ "Do not touch the pipes. The pipes are listening.",
+ ],
+ Culture::Houses => &[
+ "Mind your boots on the marble, spacer.",
+ "A House is hosting again. Nobody was invited, so everybody will go.",
+ "They say the Archon's heir has taken up mining. Imagine.",
+ "The fountain was a gift. Everything here was a gift. Everything has a price.",
+ "Do you dance? No? Pity.",
+ "Never bow lower than the person you're bowing to.",
+ ],
+ Culture::Drift => &[
+ "Hey, spacer. Long burn?",
+ "Planets make me dizzy. Too much down.",
+ "Trade you a song for a ration bar.",
+ "My mother was born between stars. So was I. It's strange, being here.",
+ "Somebody's always leaving. That's the Drift.",
+ "If you see a blue hull with a broken mast, that's my aunt. Wave.",
+ ],
+ }
+}
+
+/// What stallholders call out.
+pub fn cries(culture: Culture) -> &'static [&'static str] {
+ match culture {
+ Culture::Qaari => &[
+ "Dates! Sun-dates, sweet as a promise!",
+ "Spiced locust, two for a ¤!",
+ "Rugs! Woven by hand, by grandmother's hand!",
+ "Water-skins, never leaked, never once!",
+ ],
+ Culture::Cant => &[
+ "Bearings, blessed and greased!",
+ "Lubricant, consecrated this very morning!",
+ "Prayer-wire by the metre!",
+ "Spare parts for spare souls!",
+ ],
+ Culture::Houses => &[
+ "Candied petals, fit for a court!",
+ "Silks from the Gardens!",
+ "Scent of the old capital, one drop a ¤!",
+ "Masks for the season's balls!",
+ ],
+ Culture::Drift => &[
+ "Patches, hull or heart!",
+ "Songs, fresh from the void!",
+ "Ration bars, hardly expired!",
+ "Charts of places that might still be there!",
+ ],
+ }
+}
+
+pub fn room_name(purpose: Purpose, culture: Culture) -> &'static str {
+ use Purpose::*;
+ match purpose {
+ Living => "the front room",
+ Bedroom => "a bedroom",
+ Kitchen => "the kitchen",
+ Store => "a storeroom",
+ Lodging => "someone's room",
+ Taproom => "the taproom",
+ Parlour => "the back room",
+ Cellar => "the cellar",
+ Counter => "the shop",
+ Ward => "the ward",
+ Stacks => "the stacks",
+ Assay => "the assay room",
+ Yard => "the yard",
+ Smithy => "the smithy",
+ Hall => "the hall",
+ Dining => "the dining room",
+ Library => "the library",
+ Bedchamber => "a bedchamber",
+ Gallery => "the gallery",
+ Throne => "the throne room",
+ Council => "the council chamber",
+ Treasury => "the treasury",
+ Nave => match culture {
+ Culture::Qaari => "the well-court",
+ Culture::Cant => "the engine hall",
+ Culture::Houses => "the nave",
+ Culture::Drift => "the lantern hall",
+ },
+ Vestry => "the vestry",
+ Cell => "a cell",
+ Shrine => "the shrine",
+ Workroom => "the workshop",
+ Storehouse => "the store",
+ Office => "the office",
+ Changing => "the changing room",
+ Baths => "the baths",
+ Steam => "the steam room",
+ Guardroom => "the guardroom",
+ Bunks => "the bunk room",
+ }
+}
+
+/// What bumping into a piece of furniture tells you.
+pub fn thing_about(thing: Thing, culture: Culture) -> &'static str {
+ use Culture::*;
+ use Thing::*;
+ match (thing, culture) {
+ (Table, _) => "A table, ringed with old cups.",
+ (Chair | Pew | Bench | Throne, _) => "A seat.",
+ (Bed, Qaari) => "A sleeping mat on a low frame, piled with rugs.",
+ (Bed, Cant) => "A cot of welded mesh. Exactly as comfortable as required.",
+ (Bed, Houses) => "A canopied bed, its curtains heavy with dust.",
+ (Bed, Drift) => "A hammock-bed, rigged as if the floor might go away.",
+ (Hearth, Cant) => "A heating coil glows behind a grille.",
+ (Hearth, _) => "A fire crackles in the hearth.",
+ (Stove, _) => "A stove, still warm, smelling of whatever was cooked last.",
+ (Shelf, _) => "Shelves crammed with books, jars and keepsakes.",
+ (Chest, _) => "A chest. Locked, and none of your business.",
+ (Crate, _) => "Crates, stencilled with marks you can't read.",
+ (Barrel, _) => "A barrel. It sloshes.",
+ (Altar, Qaari) => "The sacred well. The water is so deep it's black.",
+ (Altar, Cant) => "The Engine: a vast machine that only turns. It hums in your teeth.",
+ (Altar, Houses) => "An altar of white stone, gold-leafed, crowded with candles.",
+ (Altar, Drift) => "A lantern-mast, a lantern hung on it for everyone lost in the void.",
+ (Pillar, _) => "A pillar, carved all the way up.",
+ (Statue, Qaari) => "A statue of a caravan-master, worn smooth by sandstorms.",
+ (Statue, Cant) => "A statue of a Fabricant, its face a smooth steel plate.",
+ (Statue, Houses) => "A marble statue of someone important, looking down on you.",
+ (Statue, Drift) => "A figurehead, salvaged from some old ship.",
+ (Plant, Qaari) => "A potted palm, carefully watered.",
+ (Plant, _) => "A potted plant, carefully watered.",
+ (Bath, _) => "Steaming water. It smells of minerals.",
+ (Anvil, _) => "An anvil, bright where the hammer falls.",
+ (Forge, _) => "A forge, roaring. The heat pushes you back.",
+ (Loom, _) => "A loom, strung with half a rug.",
+ (Workbench, _) => "A workbench, scattered with tools and offcuts.",
+ (Rack, _) => "A rack of pikes and stun-staves.",
+ (Lamp, Cant) => "A glow-tube on a stand, flickering.",
+ (Lamp, _) => "A lamp on a tall stand.",
+ (Engine, _) => "Half a ship's engine, opened up like a patient.",
+ }
+}
+
+/// What bumping into something in the street tells you, if anything, in
+/// a city of `culture` on `land`. For `Tile::Wild`, see `wild_about`.
+pub fn tile_about(tile: Tile, culture: Culture, land: Land) -> Option<&'static str> {
+ use Culture::*;
+ Some(match (tile, culture) {
+ (Tile::Centrepiece, Qaari) => "The cistern's water is cool, dark and very still.",
+ (Tile::Centrepiece, Cant) => "The furnace roars. Prayers are welded to its flue.",
+ (Tile::Centrepiece, Houses) => {
+ "Coins glint at the bottom of the fountain. Wishes, or bribes."
+ }
+ (Tile::Centrepiece, Drift) => {
+ "A mast of welded hull-plate, hung with a hundred little bells."
+ }
+ (Tile::Statue, _) => thing_about(Thing::Statue, culture),
+ (Tile::Well, Qaari) => "A covered well, padlocked. Water isn't free here.",
+ (Tile::Well, Cant) => "A coolant tap, sealed with a litany-tag.",
+ (Tile::Well, Houses) => "A little fountain, a stone fish spitting into a basin.",
+ (Tile::Well, Drift) => "A water tank on legs, with a cup on a chain.",
+ (Tile::Water, _) => pond_about(land),
+ (Tile::Sea, _) => "The sea, grey-green and restless, all the way to the sky.",
+ (Tile::Tree, _) => tree_about(culture, land.terrain),
+ (Tile::Lamp, Cant) => "A glow-tube on a post, buzzing.",
+ (Tile::Lamp, _) => "A lamp post.",
+ (Tile::Decor, Drift) => "A heap of salvage, sorted by someone, for some reason.",
+ (Tile::Decor, _) => "A tangle of pipes, warm to the touch.",
+ (Tile::Roof, _) => return None,
+ _ => return None,
+ })
+}
+
+fn pond_about(land: Land) -> &'static str {
+ match (land.pond(), land.terrain) {
+ (Pond::Water, Terrain::Desert) => "A cistern pool, shaded and very still.",
+ (Pond::Water, _) => "A pond. Fish, or something like them, move under the surface.",
+ (Pond::Ice, Terrain::Ice) => "A pond frozen to the bottom, clear as glass.",
+ (Pond::Ice, _) => "A frozen pond. Something dark moves slowly under the ice.",
+ (Pond::Dry, _) => "A pond, dried to cracked mud. Someone's raked the cracks.",
+ (Pond::Lava, _) => "A pool of molten rock behind an iron rail. The heat is ferocious.",
+ }
+}
+
+fn tree_about(culture: Culture, terrain: Terrain) -> &'static str {
+ match (terrain, culture) {
+ (Terrain::Desert, Culture::Qaari) => "A date palm, rattling in the hot wind.",
+ (Terrain::Desert, _) => "A date palm, its fronds grey with dust.",
+ (Terrain::Ice, _) => "A glass-pine. Its needles chime when the wind moves them.",
+ (Terrain::Ocean | Terrain::Jungle, Culture::Houses) => {
+ "A clipped yew, shaped like a bird. Something keeps trying to unclip it."
+ }
+ (Terrain::Ocean, _) => "A salt-pine, leaning away from the sea wind.",
+ (Terrain::Jungle, _) => "A strangler fig, wound round what used to be another tree.",
+ (Terrain::Barren | Terrain::Clouds, _) => {
+ "A spire of violet crystal, taller than you. It's growing, very slowly."
+ }
+ (Terrain::Forge, _) => "A fungus stalk as tall as a lamp post, warm and faintly pulsing.",
+ }
+}
+
+/// What bumping into the `sort`th sort of thing outside the walls tells
+/// you (see `Town::wild_sort`).
+pub fn wild_about(terrain: Terrain, sort: usize) -> &'static str {
+ let [first, second] = match terrain {
+ Terrain::Desert => [
+ "A cactus, fat with stored water and bristling with spines.",
+ "A boulder, scoured smooth by the sand.",
+ ],
+ Terrain::Ice => [
+ "A spire of blue ice, taller than you and slowly leaning.",
+ "A boulder, glazed with ice.",
+ ],
+ Terrain::Ocean => {
+ ["Black rock, slick with spray.", "A heap of kelp, washed up and drying. It smells."]
+ }
+ Terrain::Jungle => [
+ "Trees, crowded so close you can't push between them.",
+ "A wall of ferns and creepers, alive with clicking.",
+ ],
+ Terrain::Barren | Terrain::Clouds => [
+ "A crag of grey rock, crusted with rust-red lichen.",
+ "A cluster of violet crystals, knee-high.",
+ ],
+ Terrain::Forge => {
+ ["A heap of black basalt, still warm.", "A vent, breathing hot, sulphurous air."]
+ }
+ };
+ if sort == 0 { first } else { second }
+}
+
+/// What the road out of a gate leads to.
+pub fn beyond(terrain: Terrain) -> &'static str {
+ match terrain {
+ Terrain::Desert => "The road runs on into the dunes. There's nothing out there for you.",
+ Terrain::Ice => {
+ "The road runs on across the snowfields. There's nothing out there for you."
+ }
+ Terrain::Ocean => {
+ "The causeway runs on out to sea, to the fishing piers. There's nothing out there \
+ for you."
+ }
+ Terrain::Jungle => "The road runs on under the canopy. There's nothing out there for you.",
+ Terrain::Barren | Terrain::Clouds => {
+ "The road runs on across the grey dust. There's nothing out there for you."
+ }
+ Terrain::Forge => {
+ "The road runs on between smoking lava fields. There's nothing out there for you."
+ }
+ }
+}