summaryrefslogtreecommitdiff
path: root/src/town/words.rs
blob: 251f2b243d931c15c8edb3aa436b234cd7eaa180 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
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."
        }
    }
}