feat: add text adventure shell (PLAN.md 3.4)
Zork-style dungeon crawler set in an abandoned data center / medieval dungeon. 11 rooms, 6 items, 3 puzzles (dark room, locked door, maintenance panel), standard text adventure parser with verb aliases and direction shortcuts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
211
internal/shell/adventure/world.go
Normal file
211
internal/shell/adventure/world.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package adventure
|
||||
|
||||
// room represents a location in the game world.
|
||||
type room struct {
|
||||
name string
|
||||
description string
|
||||
darkDesc string // shown when room is dark (empty = not a dark room)
|
||||
exits map[string]string // direction → room ID
|
||||
items []string // item IDs present in the room
|
||||
locked map[string]string // direction → flag required to unlock
|
||||
}
|
||||
|
||||
// item represents an object that can be picked up and used.
|
||||
type item struct {
|
||||
name string
|
||||
description string
|
||||
takeable bool
|
||||
}
|
||||
|
||||
func newWorld() (map[string]*room, map[string]*item) {
|
||||
rooms := map[string]*room{
|
||||
"oubliette": {
|
||||
name: "The Oubliette",
|
||||
description: `You are in a narrow stone chamber. The walls are damp and slick with condensation.
|
||||
Far above, an iron grate lets in a faint green glow — not daylight, but the steady
|
||||
pulse of status LEDs. A frayed ethernet cable hangs from the grate like a vine.
|
||||
A passage leads east into darkness. Stone steps spiral downward.`,
|
||||
exits: map[string]string{
|
||||
"east": "corridor",
|
||||
"down": "pit",
|
||||
},
|
||||
},
|
||||
"corridor": {
|
||||
name: "Stone Corridor",
|
||||
description: `A long corridor carved from living rock. The walls transition from rough-hewn
|
||||
stone to poured concrete as you look east. Fluorescent tubes flicker overhead,
|
||||
half of them dead. Cable trays run along the ceiling, sagging under the weight
|
||||
of bundled Cat5. A draft comes from a ventilation shaft above.`,
|
||||
exits: map[string]string{
|
||||
"west": "oubliette",
|
||||
"east": "server_room",
|
||||
"south": "cable_crypt",
|
||||
"up": "ventilation",
|
||||
},
|
||||
items: []string{"flashlight"},
|
||||
},
|
||||
"ventilation": {
|
||||
name: "Ventilation Shaft",
|
||||
description: `You've squeezed into a narrow ventilation shaft. The aluminum walls vibrate with
|
||||
the hum of distant fans. It's barely wide enough to turn around in. Dust and
|
||||
cobwebs coat everything. Someone has scratched tally marks into the metal — you
|
||||
stop counting at forty-seven.`,
|
||||
exits: map[string]string{
|
||||
"down": "corridor",
|
||||
},
|
||||
items: []string{"note"},
|
||||
},
|
||||
"server_room": {
|
||||
name: "Server Room",
|
||||
description: `Rows of black server racks stretch into the gloom, their LEDs blinking in
|
||||
patterns that almost seem deliberate. The air is frigid and filled with the
|
||||
white noise of a thousand fans. A yellowed label on the nearest rack reads:
|
||||
"PRODUCTION - DO NOT TOUCH". Someone has added in marker: "OR ELSE".
|
||||
A laminated keycard sits on top of a powered-down blade server.`,
|
||||
exits: map[string]string{
|
||||
"west": "corridor",
|
||||
"south": "cold_storage",
|
||||
},
|
||||
items: []string{"keycard"},
|
||||
},
|
||||
"pit": {
|
||||
name: "The Pit",
|
||||
darkDesc: `You are in absolute darkness. You can hear water dripping somewhere far below
|
||||
and the faint hum of electronics. The air smells of rust and ozone. You can't
|
||||
see a thing without a light source. The stairs lead back up.`,
|
||||
description: `Your flashlight reveals a deep shaft — part medieval oubliette, part cable run.
|
||||
Rusty chains hang from iron rings set into the walls, intertwined with bundles
|
||||
of fiber optic cable that glow faintly orange. At the bottom, a skeleton in a
|
||||
lab coat slumps against the wall, still wearing an ID badge. A rusty key glints
|
||||
on a hook near the skeleton's hand.`,
|
||||
exits: map[string]string{
|
||||
"up": "oubliette",
|
||||
},
|
||||
items: []string{"rusty_key"},
|
||||
},
|
||||
"cable_crypt": {
|
||||
name: "Cable Crypt",
|
||||
description: `This vaulted chamber was clearly something else before the cables arrived.
|
||||
Stone sarcophagi line the walls, but their lids have been removed and they're
|
||||
now stuffed full of tangled ethernet runs and power strips. A faded sign reads
|
||||
"STRUCTURED CABLING" — someone has crossed out "STRUCTURED" and written
|
||||
"CHAOTIC" above it. The air smells of old stone and warm plastic.`,
|
||||
exits: map[string]string{
|
||||
"north": "corridor",
|
||||
"south": "archive",
|
||||
},
|
||||
items: []string{"ethernet_cable"},
|
||||
},
|
||||
"cold_storage": {
|
||||
name: "Cold Storage",
|
||||
description: `A cavernous room kept at near-freezing temperatures. Frost coats the walls.
|
||||
Rows of old tape drives and disk platters are stacked on industrial shelving,
|
||||
their labels faded beyond reading. A humming cryo-unit in the corner has a
|
||||
blinking amber light. A hand-written sign says "BACKUP STORAGE - CRITICAL".`,
|
||||
exits: map[string]string{
|
||||
"north": "server_room",
|
||||
"east": "generator",
|
||||
},
|
||||
items: []string{"floppy_disk"},
|
||||
},
|
||||
"archive": {
|
||||
name: "The Archive",
|
||||
description: `Floor-to-ceiling shelves hold thousands of manila folders, binders, and
|
||||
three-ring notebooks. The organization system, if there ever was one, has
|
||||
long since collapsed into entropy. A thick layer of dust covers everything.
|
||||
A heavy steel door to the east has an electronic keycard reader with a
|
||||
steady red light. The cable crypt lies back to the north.`,
|
||||
exits: map[string]string{
|
||||
"north": "cable_crypt",
|
||||
"east": "control_room",
|
||||
},
|
||||
locked: map[string]string{
|
||||
"east": "archive_unlocked",
|
||||
},
|
||||
},
|
||||
"generator": {
|
||||
name: "Generator Room",
|
||||
description: `Two massive diesel generators squat in this room like sleeping beasts. One is
|
||||
clearly dead — corrosion has eaten through the fuel lines. The other hums at
|
||||
a low idle, keeping the facility on life support. A maintenance panel on the
|
||||
wall is secured with an old-fashioned keyhole.`,
|
||||
exits: map[string]string{
|
||||
"west": "cold_storage",
|
||||
},
|
||||
},
|
||||
"control_room": {
|
||||
name: "Control Room",
|
||||
description: `Banks of CRT monitors cast a blue glow across the room. Most show static, but
|
||||
one displays a facility map with pulsing dots labeled "ACTIVE SESSIONS". You
|
||||
count the dots — there are more than there should be. A desk covered in coffee
|
||||
rings holds a battered keyboard. The main display reads:
|
||||
|
||||
FACILITY STATUS: NOMINAL
|
||||
CONTAINMENT: ACTIVE
|
||||
SUBJECTS: ███
|
||||
|
||||
A heavy blast door leads east. Above it, a faded exit sign flickers.`,
|
||||
exits: map[string]string{
|
||||
"west": "archive",
|
||||
"east": "exit",
|
||||
},
|
||||
},
|
||||
"exit": {
|
||||
name: "The Exit",
|
||||
description: `The blast door groans open to reveal... a corridor. Not the freedom you
|
||||
expected, but another corridor — identical to the one you started in. The
|
||||
same flickering fluorescents. The same sagging cable trays. The same damp
|
||||
stone walls.
|
||||
|
||||
As you step through, the door slams shut behind you. A speaker crackles:
|
||||
|
||||
"THANK YOU FOR PARTICIPATING IN TODAY'S SECURITY AUDIT.
|
||||
YOUR SESSION HAS BEEN LOGGED.
|
||||
HAVE A NICE DAY."
|
||||
|
||||
The lights go out.`,
|
||||
},
|
||||
}
|
||||
|
||||
items := map[string]*item{
|
||||
"flashlight": {
|
||||
name: "flashlight",
|
||||
description: "A heavy-duty flashlight. The batteries are low but it still works.",
|
||||
takeable: true,
|
||||
},
|
||||
"keycard": {
|
||||
name: "keycard",
|
||||
description: "A laminated keycard with a faded photo. The name reads 'J. DUNWICH, LEVEL 3'.",
|
||||
takeable: true,
|
||||
},
|
||||
"rusty_key": {
|
||||
name: "rusty key",
|
||||
description: "An old iron key, spotted with rust. It looks like it fits a maintenance panel.",
|
||||
takeable: true,
|
||||
},
|
||||
"note": {
|
||||
name: "note",
|
||||
description: `A crumpled note in shaky handwriting:
|
||||
|
||||
"If you're reading this, GET OUT. The facility is automated now.
|
||||
The sessions never end. I thought I was running tests but the
|
||||
tests were running me. Don't trust the prompts. Don't trust
|
||||
the exits. Don't trust
|
||||
|
||||
[the writing trails off into an illegible scrawl]"`,
|
||||
takeable: true,
|
||||
},
|
||||
"ethernet_cable": {
|
||||
name: "ethernet cable",
|
||||
description: "A tangled Cat5e cable, about 3 meters long. Both ends have been chewed by something.",
|
||||
takeable: true,
|
||||
},
|
||||
"floppy_disk": {
|
||||
name: "floppy disk",
|
||||
description: `A 3.5" floppy disk labeled "BACKUP - CRITICAL - DO NOT FORMAT". The label is dated 1997.`,
|
||||
takeable: true,
|
||||
},
|
||||
}
|
||||
|
||||
return rooms, items
|
||||
}
|
||||
Reference in New Issue
Block a user