Finding things in the world
Scripts constantly ask "who is here?", "what am I carrying?", "is there a player nearby?". The answer always comes as an iterable — an on-demand sequence of things. You get one from a producer built-in and then walk it with a consumer. A collection of creatures or objects is always an iterable, so this pattern is everywhere.
Producers: getting a collection
These hand you an iterable. The common ones:
| Producer | Yields |
|---|---|
creatures / people |
living creatures in a room (or a creature's room) |
objects |
objects in a room, carried, or contained |
contents |
the contents of a container or room |
inventory |
a creature's carried objects |
equipment |
a creature's worn gear |
implants |
a creature's implants |
tattoos |
a creature's tattoos |
fighting |
a creature's living combatants |
alias |
a creature's or object's name words |
words |
the whitespace-split words of a string |
list |
an iterable over its arguments |
A bare string is not iterable — to walk its words you must convert it
with words first. To write a sequence literally, use the
( … ) list form:
let dirs (north south east west)
(The list built-in is the command-form
equivalent, handy when the elements are computed rather than written out;
see Numbers, text, and logic.)
Consumers: walking a collection
Do something per element — each
each runs a block once per element, binding each
to the block's parameter:
after command (roll-call) {
each [/> $self room people] { <person>
do "say I see [name $person]."
}
}
How many, the first, the nth — count, first, nth
after command (headcount) {
let here [/> $self room creatures]
do "say There are [count $here] of us; the first is [name [first $here]]."
}
count gives the size,
first the first element (or null if empty),
and nth an element by 0-based index (null if
out of range):
let second [nth [words $arg] 1] # the second word the player typed
Is it empty? — empty
empty tells you whether an iterable yields
nothing — cleaner than comparing a count to zero:
after command (look-around) {
if [empty [/> $self room objects]] {
do "say The floor is bare."
}
}
Filtering — select
select keeps only the elements whose predicate
returns true, producing a new (lazy) iterable. When the predicate is a
single built-in, pass it by name with & (see
Structuring scripts):
after command (census) {
let players [select [/> $self room creatures] &isplayer]
do "say There are [count $players] players here."
}
When the test is more than one built-in, write a block — a parameter declared inside its own braces, bound to each element, yielding a bool:
after command (veterans) {
let vets [select [/> $self room creatures] { <c> [ge [level $c] 5] }]
do "say There are [count $vets] veterans here."
}
Any? all? — some and every
some is true if the predicate holds for at
least one element; every is true if it holds
for all (and is vacuously true on an empty iterable):
after command (check) {
require [some [/> $self room creatures] &isplayer]
do "say A player is present."
}
after command (allmobs) {
if [every [/> $self room creatures] &ismob] {
do "say No players here — just us monsters."
}
}
Membership without a block — ismember, hasabbrev
When you just need "is this value in the collection?", reach for
ismember (exact equality) or
hasabbrev (string-prefix match) instead of
writing a some predicate:
after command (door) {
let dirs (north south east west)
if [ismember "north" $dirs] {
do "say North is a valid direction."
}
}
after command (gesture) {
if [hasabbrev "wav" [alias $actor]] {
do "say Your name starts like 'wave'."
}
}
A range — slice
slice keeps a positional range: a start only
takes everything from there on; a start and end take start up to (but
not including) end:
after command (top3) {
each [slice [/> $self room people] 0 3] { <p>
do "say Front rank: [name $p]."
}
}
Pick one at random — choose
choose returns a uniformly-random element, or
null over an empty iterable:
after enter {
let target [choose [select [creatures $self] &isplayer]]
unless [isnull $target]
do "wave $target"
}
The unless guard skips the wave when no player was present and choose
returned null.
Lazy filtering pays off
select and slice
are lazy: they compute each element only as a consumer pulls it. So
[first [select … ]] does only as much work as it takes to find the first
match — handy when you want "the first player in the room" without
materializing the whole filtered set:
after command (greet) {
let p [first [select [creatures $self] &isplayer]]
unless [isnull $p]
do "bow $p"
}
Visibility — cansee
Producers list what is present; they do not account for what the owner
can perceive. To respect darkness, invisibility, and hiding, filter with
cansee:
after command (peer) {
let visible [select [creatures $self] { <c> [cansee $self $c] }]
do "say I can make out [count $visible] figure(s) in here."
}
Gear, implants, and container contents
Beyond a creature's carried inventory, three
producers reach the things attached to it:
equipment (worn slots),
implants (implanted devices), and
tattoos (tattoos). They consume the same way
as any other iterable:
after command (frisk) {
echo $actor "Worn: [count [equipment $actor]], implants: [count [implants $actor]], tattoos: [count [tattoos $actor]]."
each [equipment $actor] { <piece>
echo $actor "You are wearing [name $piece]."
}
}
For what is inside a container — an object or a room —
contents gives the items it holds:
after command (rummage) {
let chest [first [/> $self room objects]]
unless [isnull $chest]
do "say The [name $chest] holds [count [contents $chest]] thing(s)."
}
Built-in word matching — keyword and abbrev
A handler's $args is a word iterable, so two predicates read it directly:
keyword matches whole words (case-insensitive)
and abbrev matches prefixes. These are the
idiomatic way to branch on what a player typed:
after command (say) {
require [keyword $args help assist aid]
echo $actor "$self listens attentively."
}
See also
- Inspecting entities — the predicates and accessors used inside these predicate blocks.
- Iterables · Blocks
- Numbers, text, and logic — building lists
with the
( … )literal.