South of the supply room lies a nocturnal animals exhibit, and it is pitch black. Walk in without a light and you can't see a thing: no description, no animals, nothing to interact with but the way back out. The fix is sitting in the supply room: a flashlight. Switch it on, carry it in, and the darkness lifts to reveal sugar gliders, bush babies, and a barn owl.
Darkness is one of the oldest mechanics in interactive fiction, and it's really two ideas working together: rooms that can be dark, and objects that can light them.
Any room becomes dark by setting isDark: true on its RoomTrait. Here's the one
line that matters on the nocturnal exhibit (we build the whole room at the end of
the chapter):
nocturnalExhibit.add(new RoomTrait({
exits: {},
isDark: true, // this room is pitch black
}));Enter a dark room with no light and the player sees a darkness message instead of the room description. They can't examine, take, or touch anything; the only move available is to leave. The objects are still there; they're just unreachable until there's light.
LightSourceTrait marks an entity as something that can illuminate a dark room:
flashlight.add(new LightSourceTrait({
brightness: 8, // how powerful, 1–10
isLit: false, // starts unlit
}));When the player is carrying an entity whose isLit is true, dark rooms light
up: the description appears normally and every object becomes accessible again.
SwitchableTrait gives an entity an on/off state and the switch on / switch off actions:
flashlight.add(new SwitchableTrait({
isOn: false, // starts off
}));On its own it just tracks on/off. Combined with LightSourceTrait, flipping the
switch is what lights the device.
A flashlight is three traits stacked, the composability lesson from earlier chapters applied again:
| Trait | What it provides |
|---|---|
SwitchableTrait |
On/off toggle via switch on / switch off |
LightSourceTrait |
Illumination for dark rooms |
IdentityTrait |
Name, description, aliases |
When the player switches it on:
SwitchableTrait.isOnbecomestrue.LightSourceTrait.isLitbecomestrue; the engine links the two.- Any dark room the player carries it into is now lit.
const flashlight = world.createEntity('flashlight', EntityType.ITEM);
flashlight.add(new IdentityTrait({
name: 'flashlight',
description: 'A heavy rubberized flashlight with a bright halogen bulb.',
aliases: ['flashlight', 'torch', 'light'],
}));
flashlight.add(new SwitchableTrait({ isOn: false }));
flashlight.add(new LightSourceTrait({ brightness: 8, isLit: false }));
world.moveEntity(flashlight.id, supplyRoom.id);The mistake everyone makes once: expecting
SwitchableTraitalone to banish the dark. A switch with noLightSourceTraitjust toggles on and off and lights nothing, and aLightSourceTraitwith no switch is always lit. A controllable light needs both.
The flashlight is the simplest case. The same trait covers others by changing which pieces you include:
Always-on light (a glowing gem, an enchanted sword): no switch, just lit:
gem.add(new LightSourceTrait({ isLit: true, brightness: 5 }));Consumable light (a candle or torch that burns down):
candle.add(new LightSourceTrait({
isLit: false,
brightness: 3,
fuelRemaining: 50, // burns for 50 turns
fuelConsumptionRate: 1, // one fuel per turn
}));Adjustable light (a lantern with a dimmer): set brightness high and let
story code change it dynamically:
lantern.add(new LightSourceTrait({ brightness: 10 }));Objects inside a dark room exist the whole time; they're simply inaccessible until there's light. That makes darkness a natural gating mechanism: put something worth finding behind it, and the light source becomes the key that opens it. The flashlight here, a candle elsewhere, a magic spell in another game: same shape, different flavor.
Two new traits arrive this chapter, so add them to your world-model import:
import { LightSourceTrait, SwitchableTrait } from '@sharpee/world-model';The dark exhibit hangs off the Supply Room from Chapter 7. Build the room in full, connect it south of the supply room (with the way back north), and populate it with the animals the flashlight will reveal:
const nocturnalExhibit = world.createEntity('Nocturnal Animals Exhibit', EntityType.ROOM);
nocturnalExhibit.add(new RoomTrait({ exits: {}, isDark: true }));
nocturnalExhibit.add(new IdentityTrait({
name: 'Nocturnal Animals Exhibit',
description:
'A hushed, cavern-like hall lit by faint blue moonlight panels. Sugar ' +
'gliders leap between branches, wide-eyed bush babies cling to a rope, ' +
'and an enormous barn owl perches motionless on a stump.',
aliases: ['nocturnal exhibit', 'nocturnal animals', 'dark exhibit', 'exhibit'],
article: 'the',
}));
// Connect it south of the Supply Room, with the way back north. This adds the
// south passage to the Supply Room exits from Chapter 7.
supplyRoom.get(RoomTrait)!.exits = {
[Direction.NORTH]: { destination: mainPath.id, via: staffGate.id },
[Direction.SOUTH]: { destination: nocturnalExhibit.id },
};
nocturnalExhibit.get(RoomTrait)!.exits = {
[Direction.NORTH]: { destination: supplyRoom.id },
};
// The animals: scenery, examinable only once the room is lit.
const sugarGliders = world.createEntity('sugar gliders', EntityType.SCENERY);
sugarGliders.add(new IdentityTrait({
name: 'sugar gliders',
description: 'A family of tiny sugar gliders with enormous dark eyes, gliding between branches.',
aliases: ['sugar gliders', 'gliders', 'sugar glider'],
article: 'some',
}));
world.moveEntity(sugarGliders.id, nocturnalExhibit.id);
const bushBabies = world.createEntity('bush babies', EntityType.SCENERY);
bushBabies.add(new IdentityTrait({
name: 'bush babies',
description: 'Two bush babies with impossibly large round eyes, clinging to a rope.',
aliases: ['bush babies', 'bush baby', 'galagos'],
article: 'some',
}));
world.moveEntity(bushBabies.id, nocturnalExhibit.id);
const barnOwl = world.createEntity('barn owl', EntityType.SCENERY);
barnOwl.add(new IdentityTrait({
name: 'barn owl',
description: 'An enormous barn owl with a heart-shaped white face, watching you without blinking.',
aliases: ['barn owl', 'owl'],
article: 'a',
}));
world.moveEntity(barnOwl.id, nocturnalExhibit.id);The flashlight from earlier in the chapter sits in the Supply Room, ready to
carry in, so examine owl and examine gliders in the walkthrough below both
resolve once the light is on.
> take keycard Get the key
> south Main Path
> unlock gate with keycard Unlock the staff gate
> open gate Open it
> south Supply Room
> take flashlight Grab the flashlight
> south Nocturnal Exhibit — dark!
> look "It is pitch dark…"
> north Retreat to the Supply Room
> switch on flashlight Let there be light
> south Nocturnal Exhibit — now lit!
> examine owl Look at the barn owl
> examine gliders Look at the sugar gliders
> switch off flashlight Darkness returns
> look Dark again
isDark: true on a RoomTrait makes a room pitch black, locking out interaction
until light arrives. LightSourceTrait lets an entity illuminate the dark, and
SwitchableTrait adds the on/off control. A flashlight is just an item carrying
both: switch it on, take it in, and the darkness lifts. Vary which traits you
include for always-on, consumable, or adjustable lights.