Continuing the discussion from Arcturus - a new programming language & compiler for the Z-machine:
Can you tell more about grains in Arcturus or pseudo-objects in ZIL? Or the examine-cheap extensions in PunyInform?
Continuing the discussion from Arcturus - a new programming language & compiler for the Z-machine:
Can you tell more about grains in Arcturus or pseudo-objects in ZIL? Or the examine-cheap extensions in PunyInform?
I am happy to cover the Arcturus side ![]()
The idea behind grains is that a lot of scenery in a game never needs to be a real object. The player might examine the dunes, touch the sand, smell the air, and all you really want is a good answer, not a thing with attributes and a property table sitting in memory. So in Arcturus you just write the answers, one line each, and the compiler takes care of the rest:
grains
examine, touch "dunes" or "sand" say "Fine, pale sand, still holding the day's heat."
examine "sky" say "Bleached almost white, going to violet in the east."
smell "air" or "wind" say "Dry, and sharp with the cold coming down."
Each line says which verbs it answers, which words the player can type for it, and what happens. The response can be a short say like above, or a call to a block (blocks are the routines in Arcturus), or a whole indented body with real code in it if the grain needs to do something. And you’re not limited to examine: a grain answers whatever verbs you put on the line, so touch, smell, whatever fits.
You can reuse the same word across the whole game: “steps” can be scenery in the hallway and again down in the cellar with a completely different answer, and the right one speaks depending on where you are. It lets you make a world feel dense and reactive without paying for it.
The two major benefits I see:
Grains are very efficient for environments where every byte counts, if you write z5 stories that are supposed to run on retro machines for example (like the Commodore 64).
In Arcturus, defining a grain is super easy. Just a few lines to add after the room code and that’s it. A grain is as easy as a single line of code, so way less trivial and faster to create than an object for each and every scenery reference in the room’s description text.
(post deleted by author)
Worth pointing out that Inform 7’s understanding as a mistake feature is similar, though without the per-room specificity.
The I7 mistake seems quite different to me, but I might be overlooking something.
The grains seem to have two further advantages:
They’re pretty simple.
A room can have a line like
(PSEUDO "DOOR" DOOR-PSEUDO "PAINT" PAINT-PSEUDO)>
(Zork 1, in the Studio.)
This effectively defines a list of one-word fake objects. Any player command like OPEN DOOR or EXAMINE PAINT will direct to the appropriate routine (DOOR-PSEUDO or PAINT-PSEUDO), which can provide custom responses for one or more verbs. In this case DOOR-PSEUDO looks like
<ROUTINE DOOR-PSEUDO ()
<COND (<VERB? OPEN CLOSE>
<TELL "The door won't budge." CR>)
(<VERB? THROUGH>
<DO-WALK ,P?SOUTH>)>>
Each pseudo-object is two words in the room’s PSEUDO property (one dict word, one routine address), so you can fit two in a room in v3. In v5 I think they switched to a different format (THINGS) which I have not yet investigated.
We should note that I6 has a very simple form of this feature. You can put dict words in a room’s name property. These form pseudo-objects which can only give a single fixed response:
That’s not something you need to refer to in the course of this game.
The manual says:
This room has a
nameproperty even though rooms are not usually referred to by players. The nouns given are words which Inform knows “you don’t need to refer to”, and it’s a convention of the genre that the designer should signpost the game in this way.
The simple yet powerful grains/pseudo objects are much cooler than the strict “don’t need to refer” message I think. Does TADS3 have something similar?
As a player, I find that the power of a convention that clearly flags “this is not something that I need to include in my calculations as I try to solve puzzles” can be really sweet and alluring over a bunch of scenery that doesn’t and ends up complicating. But this will ultimately boil down to preference and style.
It’s also worth thinking about why they exist. I will welcome corrections; as far as I6, I always thought it was a way for the game to recognise nouns which the game itself uses in descriptions, so as not to fall into the situation of the player trying to interact with something that the game just explicitly said was here and get an answer like “I don’t know what that is” when the game literally just said it was here. So it’s a way to get those into the player’s universe, but at the same time gently dissuade the player from doing anything to them, because it’s really not important and the game would prefer that you focus your attention elsewhere. And doing so taking up as little space as possible.
I actually appreciate this an awful lot, to tell you the truth. Again; personal preference.
I see what you mean. The few times I game-mastered a tabletop RPG I got lost in side-aspects, so this might be a pattern with me. Maybe a clear “not important” is helpful for the player.
But, and now to argue FOR your point, this is not the only way to direct a player’s attention, so the convention is useful but a designer who is attentive can certainly avoid this response altogether over more refined customised ones. ![]()
The new version of PSEUDO (which was around for much of the Infocom games) works like this:
(THINGS GREEN (PEAR APPLE) FRUIT-F
<> BIN BIN-F)
So, it goes in groups of 3, and pairs of brackets identify synonyms (ie. Pear and apple used interchangably), <> to identify nothing going there. In the groups of three, first is adjectives, then nouns, then the routine name.
(Please call me out if this doesn’t make sense! It’s kind of weird to explain…)
Am I right in thinking that in Arcturus, each grain can only have one custom response?
I tried this:
room lab
name "Laboratory"
desc "It's a lab full of junk."
grains
examine "junk" say "It's useless."
touch "junk" say "It's sticky."
But trying to TOUCH the junk gives the generic response “Just some scenery. Don’t worry about it.” - i.e. the same response as you get when you try to interact with the junk in some other way such as taking it, smelling it, etc.
It feels like a bug to me. I tried this
grains
touch, burn "junk"
let act = resolve_verb
if act is action_id("touch")
say "Touch"
else
say "Burn"
which seems to work, but feels a bit hacky, if not just plain wrong.
Experimenting further, using examine “junk”, touch “junk” and burn “junk”, it looks like the order is important and that only the first one in the list actually works and any others that match “junk” return the scenery message.
Actually THINGS have a few more quirks.
First argument in group are adjectives. Could be <>, a single adjective or a list of adjectives.
Second argument in group are nouns. Could be <> (unlikely), a single noun or a list of nouns.
Third argument in group could be a string (respons to EXAMINE), a routine or a list pairing multiple verbs (in a table) to a string.
<> means FALSE or an empy list.
Lists are surrounded by ().
Tables are surrounded by [].
Examples:
(THINGS <> (HILL BUMP INCLINE) "It's just a typical hill." - Prints string when X HILL, X BUMP or X INCLINE.
(THINGS TIRE TRACKS "A couple of different sets of tracks mar the earth." - Prints string when X TIRE, X TRACKS or X TIRE TRACKS.
(THINGS (SNAKE HEAD SNAKE-HEADED) (CANE) CANE-F) - Calls routine CANE-F when [verb] SNAKE, [verb] SNAKE-HEADED CANE, [verb] CANE and so on. The routine then have to handle the different verbs.
(THINGS CRUDE NOTE ([READ EXAMINE] "The note says, \"You won't get it up the steps.\"") - Prints string when verb on CRUDE NOTE is EXAMINE or READ.
Cheap scenary isn’t limited to only examine. @fredrik may explain it in more detail.
Two things here. Grains are intended to be a single line of grammar code, which can be used on one verb and one noun or multiple verbs and muliple nouns, but in a single line. I am copying this straight from the Arcturus documentation and there is also a grains example in the example code if I recall correctly. Basically the below is all valid:
grains
touch, examine "chandeliers" or "hall" say "Pretty nice."
examine "gold" say "Holy crap, that is worth a fortune."
examine "carpet"
say "Threadbare in the corners." // this is an inline block
change foyer.noticed to true
examine "ceiling" do describe_ceiling // this calls an external block
Grains may also be attached from outside the object’s body, which lets extensions or language packs add them:
foyer.grains
examine "molding" or "cornice" say "Ornate plasterwork."
The code you used never was valid but the real issue is that the compiler hasn’t warned you about two grain grammar lines referring to the same noun. That’s on me and it is fixed now.
If you really want to have different responses, @EdwardianDuck’s approach is indeed a little bit hacky. Instead you should use if action is...
grains
examine, touch, burn "junk" or "garbage"
if action is touch
say "Sticky."
else
if action is burn
say "It smoulders."
else
say "Just junk."
My suggestion would be that you run arcc --update to update your compiler to the latest version.
EDIT: For anything that gets more complex than this, you should rather consider creating a scenery object instead.