Style | StandardCards

Planet Interactive Fiction

Monday, 14. July 2025

Zarf Updates

Hadean Lands in the Boston Indies Steam bundle

Hadean Lands is on sale as part of the Boston-Based Indies bundle on Steam. That's a 15% discount for five titles: Hadean Lands (me!) Monster Loves You Too! (Dejobaan) MewnBase (Cairn4) Million Monster Militia (Space Capsule) Loki's Revenge ...

Hadean Lands is on sale as part of the Boston-Based Indies bundle on Steam. That's a 15% discount for five titles:

Not on Steam? I've set up a one-week sale on Itch.IO as well. (No bundle there, just Hadean Lands.)

Enjoy! And thanks for supporting Boston local game developers.

Sunday, 13. July 2025

My So Called Interactive Fiction Life

The Parser is Language Specific

Now sure if this is true in other platforms like Inform or TADS, but while designing Sharpee it became clear that the Parser makes a ton of assumptions about vocabulary and command structure that are directly tied to the language the author is using to tell their interactive story.I

Now sure if this is true in other platforms like Inform or TADS, but while designing Sharpee it became clear that the Parser makes a ton of assumptions about vocabulary and command structure that are directly tied to the language the author is using to tell their interactive story.

I already have a Language Provider package that hosts English messages and constants as 'lang-en-us'. It wasn't a huge leap to refactor the parser to be language specific as 'parser-en-us' and in its own package.

Then it was another very simple leap to how this works in a story. Here's an unimplemented sample piece of code that shows where this is going. Note this is using the standard library directly. We're still going to add another fluent layer so it's a simpler interface for authors.

The important part to highlight is the config object. You see language: 'en-us', which tells Sharpee to use the lang-en-us Language Provider package for messages and the parser-en-us Parser package for parsing player commands. If there are packages for lang-es-mx and parser-es-mx, then the author could configure language: 'es-mx' and create a Spanish language story.

// my-story.ts
import { Story, StoryConfig } from '@sharpee/engine';
import { WorldModel, IFEntity, EntityBuilder } from '@sharpee/world-model';

export class SpaceAdventureStory implements Story {
  config: StoryConfig = {
    id: 'space-adventure',
    title: 'Space Station Mystery',
    author: 'Jane Developer',
    version: '1.0.0',
    language: 'en-us',  // Uses English parser and language
    description: 'A mystery aboard a space station',
    tags: ['sci-fi', 'mystery', 'puzzle']
  };

  initializeWorld(world: WorldModel): void {
    // Create space station rooms
    const bridge = new EntityBuilder('bridge')
      .withName('Bridge')
      .withDescription('The command center of the space station.')
      .withTrait('if.trait.room')
      .build();
      
    const corridor = new EntityBuilder('corridor')
      .withName('Main Corridor')
      .withDescription('A long corridor with viewports showing stars.')
      .withTrait('if.trait.room')
      .build();
      
    world.addEntity(bridge);
    world.addEntity(corridor);
    
    // Connect rooms
    world.relate(bridge, 'if.rel.exit.south', corridor);
    world.relate(corridor, 'if.rel.exit.north', bridge);
  }

  createPlayer(world: WorldModel): IFEntity {
    const player = new EntityBuilder('player')
      .withName('Commander Blake')
      .withDescription('You are Commander Blake, investigating the mystery.')
      .withTrait('if.trait.player')
      .withTrait('if.trait.container')
      .build();
      
    // Start in the bridge
    const bridge = world.getEntity('bridge');
    if (bridge) {
      world.relate(player, 'if.rel.within', bridge);
    }
    
    return player;
  }

  getCustomActions() {
    // Story-specific actions
    return [
      {
        id: 'space-adventure.scan',
        patterns: ['scan', 'analyze'],
        execute: (context) => {
          // Custom scanning action
        }
      }
    ];
  }
}

I'm still working through custom messages and actions and how they get registered, mainly so the eventual Text Service can query everything and report the current state properly.


Zarf Updates

Occlude: design ruminations

It's unfair to describe Occlude as "Solitaire... but eeevil!" But it's also funny so I will. No, no, let's start over. Someone has constructed a ritual which allows you to change the past. One could use it to, oh, for example, obviate the sin ...

It's unfair to describe Occlude as "Solitaire... but eeevil!" But it's also funny so I will.

No, no, let's start over. Someone has constructed a ritual which allows you to change the past. One could use it to, oh, for example, obviate the sin of murder. Is that a good idea? Will there be repercussions? Take a guess -- but don't worry, you can UNDO any wrong guesses...

Mechanically, this leads with the solitaire. It's an interesting solitaire variant: you can stack both up and down on the tableau, and build both up and down on the foundation. (No spoilers, the tutorial screen explains this stuff.) Not too difficult once you've played a couple of hands -- but! There's a secret rule. Seven secret rules, in fact: Occlude presents seven scenarios, each with its own secret. Your clues are a set of four coins which shift and flip as you play the game. To complete a scenario, you must win the card game and satisfy its secret rule.

On the narrative side, well, no spoilers, but each scenario is a different person in a different year. You will see how they fit together.

You could compare this to Inscryption: card game on the surface, surprises in the depths. Inscryption was ambitiously huge, though. (I didn't finish it!) Occlude is much smaller. It'll take you a few evenings -- or more, depending on how good you are at figuring out the rules. That's the trick, of course.

Really the better comparison is Zendo, the classic experiment-and-hypothesize challenge game[*]. You have to watch the coins, guess what their movements mean -- and then verify your guess. Much as in Zendo, success depends on having both an open mind and the fiddly persistence to test your wild guesses. And just like Zendo, if you get stuck, you're pretty much stuck. No amount of experimentation can make the right idea pop into your brain -- particularly if you've gotten focused on a blind alley.

(* A moment in memory of Kory Heath, please.)

Occlude's first scenario is straightforward but the later ones are fiendish. You will soon find yourself setting aside the solitaire goal (stacking the cards) to figure out the secret goal. You have to fling the cards around and hammer UNDO repeatedly just to see what the coins do in every possible situation.

And that gets thematic, doesn't it? Sneaky.


The interesting question is how much UNDO you get. Occlude has two difficulty levels. In "Story" mode, you have unlimited UNDO. This means that you can pretty much brute-force each scenario: just UNDO any move that doesn't lead to the secret solution. You can bang your way to the end without ever figuring out what the secret rules are.

In contrast, "Classic" mode restricts your UNDO to prevent that. You can't revert a move that violates the secret rule; you have to start from scratch. Thus, you actually have to understand the rule to complete the scenario. This is of course how the designers intend you to play.

This gives Occlude a rather unusual characteristic. You can play through all the puzzles in "Story" mode, complete the game, see the end of the narrative -- and the puzzles haven't been spoiled! You have no idea what you did.

We usually take for granted that watching someone solve a puzzle will spoil the solution for you. This makes "easy mode" a bit of a paradox. Do you write a whole second set of easier puzzles? (Authors hate doing that.) Do you put in a SKIP button? (Authors hate doing that too but at least it's easy to implement!) Can of Wormholes took the utterly elegant (but labor-intensive) tack of providing an easier puzzle as a hint -- you don't get to skip the real puzzle, but you've been walked up to the key insight.

Occlude dodges the whole question. The "Story" mode isn't an easy mode; it's exactly what it says on the tin. It lets you see the whole story. The puzzles are still there waiting -- if you want them.

But I am not sneering at "Story" mode, not at all. It has another perfectly valid use. Infinite UNDO makes experimenting with the cards much more convenient! You can still tackle the logic puzzle that the designers intended. It's not any easier to figure out. (Not an easy mode!) There's just less friction. You can set up any card arrangement you like, check the coins, UNDO, repeat.

And this is exactly what I did (in the later trickier scenarios). I started in "Story", played around with the cards and coins until I understood the secret rule -- and then didn't finish the hand. Switched back to "Classic", started a new hand, and applied my new knowledge. If I was right (and I was!) then I got full credit for solving the scenario in "Classic". It wasn't cheating at all because I really figured out the rule.


So how hard is this thing? I can only give you my own sense. The first three scenarios were easy. The fourth was straightforward once I played through a hand.

The fifth and sixth got tricky. I was stuck on the fifth scenario for an evening; I came back the next day and spotted my false assumption, and then it was tractable. The sixth went quicker.

As for the seventh scenario, I will confess that I got totally stuck and looked at a walkthrough. But the puzzle was fine! This wasn't a Blue Prince situation where I read the solution and said "Meh, I would never have gotten that, I'm glad I cheated." (Everybody hits this point in Blue Prince, although where you hit it varies wildly.)

No, I had just failed to consider all the available information. If I'd slept on it again, I'm sure I'd have cracked it the next day. So, yeah, I was annoyed at myself, but whatever. Now I'm writing this review. Mission accomplished.

Anyhow, good game. Short but tasty. I like the snippets of occult storyline. Go play it.


Renga in Blue

Marooned: Playable (For the First Time)

Anyway, here’s this skinny blonde kid, around 6 feet tall, wearing silver Ray-Ban sunglasses, driving a royal blue Formula Firebird that says he’s Kim Watt! Can you believe it? Oh well, he sounds like he does on the phone, so I guess it’s him. — From Inside Super Utility Plus This is written seven years […]

Anyway, here’s this skinny blonde kid, around 6 feet tall, wearing silver Ray-Ban sunglasses, driving a royal blue Formula Firebird that says he’s Kim Watt! Can you believe it? Oh well, he sounds like he does on the phone, so I guess it’s him.

— From Inside Super Utility Plus

This is written seven years after my previous post on Marooned.

I got a surprise in my comments when El Explorador de RPG mentioned he got the game — previously completely non-functional — into a fully playable form. It works all the way to the end, and I have a download here. (Run ADVENTUR, then pick 2 when prompted for which adventure to play.)

Via Ira Goldklang.

Kim Watt was a pre-med student in 1978 when he got his first computer, a TRS-80, and started to build a consulting service around it. He eventually dropped out of college altogether to focus on computers. He became well-known as a “genius” and never did outlines, simply writing directly from his brain to assembly language.

While he started in Michigan (and lived there in 1980, when today’s game was likely written), he later moved to Texas and took on a look to match:

To underscore his reputation as a renegade guide through the wilderness of home computing, Watt has wholeheartedly adopted the role of Software Cowboy. It is not unusual to find him at computer shows throughout the country, dressed in a ten-gallon hat and boots, signing autographs and counseling his followers. Though he is originally from Michigan, he looks the part. He has steel-blue eyes, disheveled hair, and a serpentine tattoo on each bicep. His demeanor is also on target; the strong, silent type, he is wary about discussing his work. His comments and answers are clipped and guarded, expressed in a quiet monotone.

Kim Watt is mostly remembered in the TRS-80 community for his utility software like Super Utility, used for salvaging data off broken diskettes or even copying protected disks (Super Utility itself was protected, and refused to copy itself; it was eventually pirated anyway). Watt later wrote a book disclosing the secrets of Super Utility for an eye-popping price tag of $500; those who would buy it (including MIT, the Navy, and Radio Shack employees) would discover the secrets of Watt’s methods. Although:

Watt points to a stack of computer printouts, almost two feet high, on a bookshelf on the other side of his office. “Couldn’t fit all of it into the book,” he says.

In his early days he worked on multiple games, mostly arcade action, and even had one published by Adventure International (Scott Adams’s company) in 1980.

The curious thing about this is that the same year, Watt also wrote his “Intercept” program which essentially broke the copy protection that Scott Adams started applying to his adventure games. This seems to be before his generalized copy-program, meaning Scott Adams games were essentially a testbed for the ideas that went into Super Utility. He gave a similar treatment to Microsoft Adventure (that is, the Microsoft port of Crowther/Woods for TRS-80).

From the Super Utility 4 manual.

As part of all this, Watt wrote a game in the Scott Adams database format, Marooned. Now, this isn’t unusual; Pyramid of Doom was written by Alvin Files who simply figured out the format himself and sent the game over to Scott Adams. Allan Moluf described the file format in 1980 and used that as a basis for an editor program; this was expanded upon by Bruce Hanson and published commercially as The Adventure System the next year.

The game starts by asking for which adventure you want to play, 1-9 (this is standard for Scott Adams interpreters 7.8 through 8.5) but by typing in 10 we can start Marooned, which describes itself as adventure 10. Less glamorously, you can type 1, because it only is taking the first character. (Also, as I already mentioned, if you’re playing the fixed version, pick adventure 2.)

This is not a game damaged from a bad tape read, like Mystery House II; rather, this is a game that had development abandoned midstream, and one of the treasures you’re required to collect didn’t even get a name (the fixed version calls it a MYSTERY TREASURE, although I haven’t gotten to it yet). It is buggy far out of proportion to other games we’ve played, and El Explorador had to manually add the commands to pick particular objects up; in an effort to make minimal changes, only items that are needed to be taken are takeable.

Before getting to any treasures, our hero(ine) needs to get past their immediate dilemma, which is a crashing airplane.

Action starts in a very restrictive 3-room area, the place I was stuck at 7 years ago.

To the south is the “tail end” of the plane with a broken cable, knapsack and toolbox. The knapsack turns out to be a parachute, but you can’t pick it up, you can only WEAR it (and while taking inventory, it has no name, you just get “being worn” message by itself). The toolbox has a wrench, screwdriver, and cutters, but you can only take the cutters.

If he had finished debugging the game, I would guess the other two items would be takeable but remain as red herrings.

For some reason, you can PULL the cable, but only while holding the cutters (?) and this reveals a knob. TURN KNOB then reveals a secret door, and you can OPEN DOOR to find some *TOP SECRET PLANS*, the first treasure of the game.

To escape, the sequence is truly bizarre. Back in the center of the plane, you can LOOK AROUND to find a RING on the ceiling. (Not LOOK UP, which we have at least had a few times. LOOK AROUND is brand-new for this game.) Then — I assume some descriptions never got done — you can CONNECT CORD/TO RING resulting in “ripcord attached to ring”. OPEN DOOR will then jerk you out of the plane; if you want to, you can DROP PARACHUTE before leaving and somehow survive anyway as long as you’ve done the ripcord action.

Neither the geese nor plane have a description, because what is supposed to happen (assuming you still have the parachute) is that this is a “timed view” room which doesn’t allow and commands, but just sends the player down to the ocean.

I was stuck for a while until El Explorador mentioned I needed another item for swimming, and I realized that for some reason TAKE SEATS back on the plane in the starting room yields the player a floatation device. (No description when looking at the seats — again, I assume the author bailed before even getting to that part. Remember, taking items doesn’t even work at all, this had to be fixed.)

With the floatation device, you can bail on the parachute and then SWIM multiple times to reach a shore.

This leads to a progression through a Forest, where every directions loops; you can climb up a tree, then LOOK AROUND (that new command again) to see a VINE, and SWING VINE reaching another forest, with one more SWING VINE reaching a new beach (I assume on the other side of the same island).

From the new beach you can dive into the ocean again and SWIM until reaching a slightly more elaborate island, and this is where I am stuck. My map so far:

The west shore has a coconut tree (top: coconuts + leaves) and palm tree (top: dates + leaves) and in both cases only the leaves are takeable, and you can take both of them, meaning Leaves. and Leaves. are both in inventory.

This doesn’t mean the coconuts and dates are useless, but whatever happens to them has to happen on the spot.

To the south is are branches to a Jungle and Cave. The Cave is dark and I have no light source, while the jungle has overgrown brush (which suggests it can be whacked away) and quicksand (which is a one way trip and quite possibly another red herring).

To the east is a lake and going in there is some floating jelly which is described as “awfully slimy” and trying to get it is fatal.

Finally, to the north is a dead man. Trained by other painful games, I used FRISK to find a STRING and used MOVE to reveal a BAG. While the bag cannot be taken, EMPTY BAG reveals some *PEARLS*.

Given the number of other issues with the code (it isn’t even a “private game” as I usually have defined it but a “work in progress”) I would normally follow this with code diving or even assuming the game can’t be finished, be El Explorador has now played all the way to the end, so I’m going to treat it as a “real game” for a bit longer and try to solve puzzles as if most of the item descriptions weren’t missing.

If anyone else wants to join in, perhaps they can have the glory of being the first person (outside El Explorador who had to modify the code) to finish the game, ever. This includes the original author! When I finish I’ll dive down into the technical layer and try to diagnose why this happened.

Saturday, 12. July 2025

My So Called Interactive Fiction Life

Is it Core or Capability?

In the midst of testing the platform (everything is building and Core, IF Domain, Event Processor, World Model, lang-en-us, Standard Library, and Engine all compile), there were some actions without clear implementations.For scoring, where do we keep that data? In the player entity? In the world model at some

In the midst of testing the platform (everything is building and Core, IF Domain, Event Processor, World Model, lang-en-us, Standard Library, and Engine all compile), there were some actions without clear implementations.

For scoring, where do we keep that data? In the player entity? In the world model at some global level? After circling on it for a few hours, I finally came up with "capabilities".

The idea is that some things really own their own data (and structure). Scoring is a good example of this and by making it an intentional capability with its own data structure, we can allow future extensions to replace the scoring capability with an alternate data structure. Conversations would also fit into this model.

// Scoring capability registers its data schema
world.registerCapability('scoring', {
  schema: {
    scoreValue: { type: 'number', default: 0 },
    maxScore: { type: 'number', default: 0 },
    achievements: { type: 'array', items: 'string', default: [] },
    history: { type: 'array', items: 'object', default: [] }
  }
});

Inventory would not. It's an action that queries the current state and that is as it should be.

This discussion led to new architecture decision records as seen on the Sharpee github issues page: https://github.com/ChicagoDave/sharpee/issues

Thursday, 10. July 2025

Choice of Games LLC

Out now: “Specters of the Deep” demo!

We’re proud to announce that the demo of our upcoming Choice of Games title Specters of the Deep is now available to play on Steam and on choiceofgames.com! In life, you were a legendary hero. Now, you’ll rise from the grave as a ghost to defend your country in its hour of greatest need! Defy dragons, duel the dead, and face the nightmare at the bottom of the sea! Specters of the Deep i
Specters of the Deep

We’re proud to announce that the demo of our upcoming Choice of Games title Specters of the Deep is now available to play on Steam and on choiceofgames.com!

In life, you were a legendary hero. Now, you’ll rise from the grave as a ghost to defend your country in its hour of greatest need! Defy dragons, duel the dead, and face the nightmare at the bottom of the sea!

Specters of the Deep is an interactive epic fantasy novel by Abigail C. Trevor, author of Heroes of Myth and Stars Arisen. It’s entirely text-based, 1 million words and hundreds of choices, without graphics or sound effects, fueled by the vast, unstoppable power of your imagination.

You can play the first four chapters today, for free, either on Steam or on our website, and wishlist it on Steam hereSpecters of the Deep will release Thursday, September 25th.


Renga in Blue

Herrick Venture #1: Escape (1983)

You may have noticed a general pattern with my 1983 picks: January 1983: S. S. Poseidon by Bill and Debbie Cook January 1983: The Final Countdown by Bill and Debbie Cook Might be 1982 or 1983: Cauchemard-House by Anonymous 1983, based on a game written much earlier: Nosferatu by Mike Taylor and Myles Kelvin January […]

You may have noticed a general pattern with my 1983 picks:

January 1983: S. S. Poseidon by Bill and Debbie Cook
January 1983: The Final Countdown by Bill and Debbie Cook
Might be 1982 or 1983: Cauchemard-House by Anonymous
1983, based on a game written much earlier: Nosferatu by Mike Taylor and Myles Kelvin
January 1983: Caveman Adventure by Dave Carlos
Intended for 1982 launch but slipped to 1983: The Dark Crystal by Roberta Williams

In other words, I was writing about games with at least a foot in 1982. I do not have a complete month-by-month breakdown of all 500ish games for the year and I never held the restriction that I have to be chronological by month, especially in cases like today’s game where I don’t know what month it was published, or if it was published at all. (Kevin Bunch at Atari Archive tries to get more exact dates with his chrono-playing, but even though the Atari 2600 had much wider audience base than any computer from the time, he still sometimes has an error bar.)

It notably uses the TRS-80 with 32K-of-memory only. This is non-trivial as the Model I generally went to 16K.

A 32K expansion interface for the Model I. They started being sold in 1978, and allowed plugging in more memory boosting the original 16K of the TRS-80 to 48K, but you would never assume a standard user had one of these in 1978 or even 1982. The Model III could be expanded internally. Source.

The instances we’ve had before of the 32K TRS-80 have nearly all had alternatives; for example, Asylum had 16K and 32K versions (with notable changes to the text) and while Hog Jowl Mansion was printed as a 32K game, it gave specific advice for condensing the code to fit on a 16k computer. Stuart Rush’s 1982 game Survival was intended to have two versions of the room descriptions printed in the magazine, except an error led to both of them having the “short versions” and the full code wasn’t printed until David Ahl’s Big Computer Games from 1984. Xenos (published in the tail end of 1982) is the big exception.

From a 1983 catalog. Both of these are 16K models, with $50 for each additional 16K. To adjust for inflation to 2025 money, prices should roughly be tripled.

In other words, authors have been struggling against the limits of their computers — or at least computers they expected their target market to own — but 1983 is when authors of home-computer adventures just might have a little more space to work with generally. The ZX Spectrum — with greater default capacity — now will notably be much more prominent than the ZX81.

In this specific case the increased space’s effect is less in allowing longer sentences and more in allowing a bizarre coding style, but we’ll get to that.

The text of the intro is clearly cadged mostly wholesale from the text in Scott Adams games, including, ironically, the bit about piracy. Such a line would normally indicate this game was sold commercially, and while I think this is possible, I haven’t found any print-magazine ads. It is also possible this was “aspirational” writing, although in this case, there is a second game in the Herrick Venture series (Land of Odysseys) and that second game mentions a third (Ghosttowns of Nevada). No copies of the third exist so it may be vaporware; still, on top of that there’s a second version of Escape, re-titled The Building and written in machine code. I stuck with the BASIC original to start, but then switched to machine code later for reasons I’ll get into later.

I have been unable to narrow down who Richard E. Herrick Jr. is from multiple candidates. It’d be fun if he was related to Richard J. Herrick, the first person to get a kidney transplant (with a donation from his twin brother in 1954; the surgeon Joseph Murray later won a Nobel Prize) but alas.

You are trapped in a building and your objective is to get out.

The start is fairly brisk. The door is locked and the rug is nailed down; checking the shelf (twice) reveals a SAW and HAMMER. The hammer can be applied to remove the nails, allowing removing the rug and revealing a hole in the floor.

This leads to a cellar (see above) which seemingly only has a brick wall, but some bricks can be taken, revealing both some planks and a key. The hole above is too high to reach but assuming you kept the saw, hammer, and nails from upstairs you can put together a LADDER with the parts, allowing egress back up the hole.

This leads to a wider-open space, but not too wide because this is a escape-building game and not a Roberta Williams landscape. There are multiple holes and passages that open up, but I’ll give the version of the map without any of those first:

You stumble from the ROOM out into a hallway and then a LOBBY with a PADLOCKED DOOR and STAIRS.

The machine language version adds a bricked-up door intended to represent the front door.

Wandering around the easily accessible spots: there’s a BEDROOM to the west with a CLOSET to the north and a BATHROOM to the south. The closet has an ELECTRIC CAN OPENER, the bed has a PILLOW and BLANKETS, and the bathroom has a sink, tub, and toilet, which all seemingly at first glance did nothing.

Upstairs, there’s a supply room (MATCHES, TIN CAN) and a maintenance room with a KEROSENE LANTERN and a window. You can BREAK WINDOW (the game asks WITH WHAT? and you need to respond WITH BRICK, using the ones from the cellar).

You can step outside to find some broken glass (not useful) and a security badge (useful later). You can also try to jump and die (I attempted to use the pillow and blankets to soften the landing, no dice).

Heading back downstairs, the padlock isn’t too hard to deal with using a tool from the start: SAW PADLOCK. This breaks open a library with a bookcase. After some parser struggle I managed to PULL BOOK, revealing a lever.

The book mentions A COMPUTER NEEDS POWER TO RUN (hint for something in the next room) but also says TRY HERRICK VENTURE #2’LAND OF ODYSSEYS.

The secret passage leads to a COMPUTER ROOM.

Doing FLIP SWITCH has no effect; doing MOVE DESK reveals a frayed cord and plug, but trying to insert the plug electrocutes you. While I’m at it, I should mention GO DESK lets you see a DRAWER which you can open to find a DISKETTE (grr things you can clearly see that the game refuses to mention unless you are positioned in a particular way).

Making progress now requires more unconventional thinking, or at least “I assume nothing is a red herring, so what haven’t I used yet”. For example, there’s a whole “room” dedicated to the bed: why? Well, because you can SLEEP, and then for no logical reason whatsoever, some insulated gloves appear.

This isn’t exactly “moon logic”, although it does hover near my prior definition (where effect doesn’t derive from cause). The author’s thought process seems to be that the player should try all things that seem linked to regular behavior (like sleeping in a bed); even if the cause isn’t immediately apparent it doesn’t seem that outrageous someone would do the action in the first place.

Keeping that in mind, there are three things you can do in the bathroom:

a.) RUN WATER to turn on the sink. This will spill out not water but some oil-like substance. If you take the can opener, use the plug at the computer to operate it, and open the can, you can fill it with oil.

b.) RUN WATER again while in the tub. Some keys come out. I needed to disassemble the source code to figure this one out.

c.) FLUSH TOILET will cause the toilet to lift up revealing a hole. The hole goes under the manhole in the street (seen earlier through the window) and it leads to freedom, with the small catch there are bolts at the bottom of the manhole.

So to recap, we now have: insulated gloves from the bed, oil from the tap (fracking, I guess), keys from the bathtub, and a way out which requires removing bolts. Now back to the computer room, where the gloves work to plug the computer in.

Pressing ENTER causes a secret door to swing open. Upon trying to enter, the game locked up, and I ended up restarting and retracing all my steps with the machine code version of the game in order to get farther. Past the secret door is a room with a lift.

You can SHOW BADGE (from up on the windowsill) to trigger the next part, but the machine isn’t quite working.

The lift has a gauge indicating it is empty of fluid. It took a lot of parser fussing to realize I could FILL LIFT (POUR OIL just pours it on the floor) and then the door will open, allowing entrance to the last room.

The final obstacle is simply a room with a file cabinet. It simply requires the SECURITY KEYS, and for some reason a WRENCH is inside. I did not have the miracle of trying RUN WATER in the tub, hence the source diving.

850 IFA=9THENWA=1:GOSUB3220:GOTO3420
860 IFA=10ANDOB(36)=0THENOB(36)=A:GOSUB3220:GOTO3340

(A = 9 is the bathroom, and 3420 displays a message about oil coming out of the sink, so I knew the command here was RUN WATER. Hence this told me I needed to RUN WATER while in the tub, room 10.)

With the wrench in hand, you can head back to the manhole, GET BOLTS, then PUSH COVER to go out to the street… and get hit by a car.

YOU FORGOT TO CHECK FOR TRAFFIC AND I GOT HIT BY A CAR!

The computer DID say to check for traffic. Unfortunately, in the machine code version of the game, the victory display at the end is confused:

It’s supposed to say

CONGRADULATIONS! YOU HAVE ESCAPED!!!

with that exact spelling. Having the hard lock crash and the mangled text suggest to me this wasn’t a commercial game but just aspirationally oriented that way, but it’s hard to know for certain without more information. I’m going to save off moving forward to game 2 (World of Odysseys) a bit just in case a magazine ad magically pops up.

Regarding what I said about the unusual source code earlier: it is structured with a bunch of manually-applied synonyms early on…

250 IFA$=”HIT”ORA$=”STRI”ORA$=”TOUC”ORA$=”FEEL”THENV=18
260 IFA$=”PRES”ORA$=”MOVE”THENV=17
270 IFB$=”OPEN”THENN=31
280 IFB$=”FLUI”ORB$=”OIL”THENN=38
290 IFB$=”PADL”THENN=54
300 IFA$=”LOAD”THENV=32
310 IFB$=”LAMP”ORB$=”LIGH”THENN=50

…and then the verb implementation proper has GOTO statements standing in for what normally would be PRINT statements.

1500 IFA=1ANDOB(1)=1THENOB(1)=0:OB(2)=1:GOTO3220
1510 IFA=1ANDOB(0)=AGOTO3290
1520 IFA=4ANDOB(18)=ATHENOB(18)=0:OB(19)=4:GOTO3220
1530 IFA=1ANDOB(2)=ATHEN3430:ELSEIFA=4ANDOB(19)=ATHEN3430
1540 IFA=5ANDOB(21)=ATHENOB(21)=0:OB(22)=5:GOTO3220
1550 IFA=5ANDOB(20)=ATHEN3290:ELSEIFA=5ANDOB(22)=ATHEN3430
1560 IFA=19AND(OB(68)=AOROB(69)=A)THEN3930:ELSEIFA=19THEN3430
1570 IFA=17ANDOB(63)=ATHEN3430:ELSE3330

The idea is that then all the messages are sorted together in a big list.

3520 PRINT@PP,”AND THEN GOES OUT.”:RETURN
3530 PRINT”LANTERN IS GOING OUT. KEROSANE IS LOW.”:RETURN
3540 PRINT”LANTERN WENT OUT.”:RETURN
3550 PRINT”LANTERN HAS NO KEROSANE.”:RETURN
3560 PRINT”I CAN’T, IT WON’T IGNITE.”:RETURN
3570 PRINT”LANTERN IS FILLED AND READY FOR USE.”:RETURN
3580 PRINT”WITH WHAT?”:RETURN
3590 PRINT”IT DOESN’T SEEM TO WORK.”:RETURN

The indirection made the code quite tricky to follow, and quite notably “wasted” a lot of characters. Rather than just printing LANTERN HAS NO KEROSANE where it ought to go in-game, there’s an extra four characters used for jumping to the appropriate line, characters are wasted at the start of the line, and then the code needs a “:RETURN” after the message is displayed. It’s more common (and tighter on character count) to avoid the jump. It appears the author was enamored with Scott Adams, all the way up to how the messages get sorted together in a Scott Adams database file, but didn’t know how to copy the implementation so just simulated the same thing with BASIC source code. There’s still some savings because of re-used messages, but sometimes parts you would think you’d re-use a message (and Scott Adams surely would) but there’s repeats of text nevertheless:

3240 PRINT”I’M NOT THAT STRONG!”:RETURN
3250 PRINT”I ALREADY HAVE IT.”:RETURN
3260 PRINT”I CAN’T, I’M NOT STRONG ENOUGH.”:RETURN

This is the sort of luxury of space only enabled by working with 32K of memory rather than 16K. Rather than thinking of the fact that items could now be described in fuller prose more akin to Infocom, the author copied what they knew; that is, the original system that was written to fit a particular technological form now had its form outdated, but was being mimicked anyway.

Coming up: The proprietor of El Explorador de RPG has taken some broken source code from a game I wrote about 7 years ago and fixed it. It happens to also be in Scott Adams format, so I’ll cover the history of what I think happened.

Wednesday, 09. July 2025

My So Called Interactive Fiction Life

ADRs (Architecture Decision Records)

I've copied the ADRs to the GitHub repository.You can view them here: https://github.com/ChicagoDave/sharpee/issues♦

I've copied the ADRs to the GitHub repository.

You can view them here: https://github.com/ChicagoDave/sharpee/issues

Tuesday, 08. July 2025

My So Called Interactive Fiction Life

ADR 014 - Unrestricted World Model Access

So we ran into a goofy problem that would only happen in a modern architecture. Encapsulation is something IF development has rarely adhered to and Sharpee leans into it.The event source and spatial indexing (the implemented world model) have event-based interfaces and you currently can't change things

So we ran into a goofy problem that would only happen in a modern architecture. Encapsulation is something IF development has rarely adhered to and Sharpee leans into it.

The event source and spatial indexing (the implemented world model) have event-based interfaces and you currently can't change things willy-nilly in the world model. But the initial setup of a world and then changing things based on PC actions is very important. We can't always adhere to physics in a fictional world.

One single integration test exposed the problem. We have a test to have medicine in a closed medicine cabinet. The test couldn't put medicine in the closed cabinet! The API to the world model required the test to OPEN the cabinet and then put the medicine inside. This is dumb.

So Architecture Decision Record 014 arrives:


ADR-014: Unrestricted World Model Access

Status

Proposed

Context

During the implementation of the visibility system, we discovered that the WorldModel's moveEntity method prevents moving entities into closed containers. While this is correct behavior during gameplay, it creates significant problems for:

  1. Initial world construction - Authors need to populate containers regardless of their open/closed state
  2. Save/load operations - Restoring world state should bypass gameplay rules
  3. Special game mechanics - Magic, teleportation, or debug commands may need to bypass normal restrictions
  4. Story file authoring - Authors shouldn't need to worry about container states during world setup

Currently, to place an item in a closed container during world setup, authors must:

// Current workaround
cabinet.add(new OpenableTrait({ isOpen: true }));  // Start open
world.moveEntity(medicineId, cabinet.id);          // Now it works
(cabinet.getTrait(TraitType.OPENABLE) as any).isOpen = false; // Close it

This is cumbersome and error-prone.

Decision

We will provide two separate interfaces to the same underlying world state:

  1. WorldModel - Enforces game rules, generates events, used during gameplay
  2. AuthorModel - Unrestricted access, no events, used for world building

Both models will share the same underlying data structures (SpatialIndex, EntityStore) but expose different methods and behaviors.

Example Usage

// During world building (story files, setup)
import { AuthorModel } from '@sharpee/world-model';

const author = new AuthorModel();
author.createEntity('Medicine Cabinet', 'container');
author.createEntity('Medicine', 'item');
author.moveEntity(medicineId, cabinetId); // Works even if cabinet is closed
author.setState('game-started', false);

// During gameplay
import { WorldModel } from '@sharpee/world-model';

const world = new WorldModel(author.getDataStore()); // Same underlying data
if (world.moveEntity(playerId, cabinetId)) {
  // Only succeeds if cabinet is open
  // Generates movement events
}

Implementation Details

  1. Shared State: Both models reference the same SpatialIndex and EntityStore instances
  2. No Events in AuthorModel: Author operations never generate events or trigger handlers
  3. No Validation in AuthorModel: Author operations bypass all game rule checks
  4. Clear Separation: Different import paths make the distinction explicit
  5. Type Safety: Each model can have methods optimized for its use case

AuthorModel Methods

The AuthorModel will provide unrestricted versions of WorldModel methods plus author-specific conveniences:

class AuthorModel {
  // Unrestricted basics
  moveEntity(entityId: string, targetId: string | null): void
  createEntity(name: string, type: string): IFEntity
  removeEntity(entityId: string): void
  
  // Author conveniences  
  populate(containerId: string, entities: string[]): void
  connect(room1: string, room2: string, direction: string): void
  fillContainer(containerId: string, itemDescriptions: ItemSpec[]): void
  placeActor(actorId: string, locationId: string, posture?: string): void
  
  // Bulk operations
  import(data: StoryData): void
  clear(): void
  
  // State access
  getDataStore(): { spatialIndex: SpatialIndex, entities: EntityStore }
}

Consequences

Positive

  • Clear Mental Model: Authors know when they're building vs. when game rules apply
  • No Workarounds: No need to temporarily change object states during setup
  • Clean Event History: Event source only contains actual gameplay events
  • Better Testing: Can test game rules separately from world construction
  • Type Safety: Each API can be optimized for its specific use case
  • Future Flexibility: Can add author-specific methods without polluting gameplay API

Negative

  • Two APIs to Maintain: Must keep both models in sync with shared functionality
  • Learning Curve: Developers must understand when to use which model
  • Potential Confusion: Using the wrong model could lead to subtle bugs
  • Import Complexity: Need to import from different paths for different use cases

Neutral

  • Explicit Mode Switching: Makes it very clear which "mode" the code is operating in
  • Parallel APIs: Similar method names but different behaviors

Alternatives Considered

1. Force Parameter

Add an optional force flag to existing methods:

world.moveEntity(entityId, targetId, { force: true });
  • Pros: Single API, backwards compatible
  • Cons: Easy to misuse, clutters API, still generates events

2. Mode Switching

Add a mode flag to WorldModel:

world.setAuthorMode(true);
world.moveEntity(entityId, targetId); // Bypasses rules
world.setAuthorMode(false);
  • Pros: Single API, explicit mode
  • Cons: Stateful, easy to forget to switch back, threading issues

3. Method Prefixes

Different method names for unrestricted operations:

world.authorMoveEntity(entityId, targetId);
world.directMoveEntity(entityId, targetId);
  • Pros: Single API, clear distinction
  • Cons: Doubles API surface, naming conventions unclear

4. Direct Property Access

Allow direct manipulation of entity properties:

entity.location = targetId;
  • Pros: Simple, no API needed
  • Cons: Breaks encapsulation, no validation possible, inconsistent state

References

  • Similar pattern in game engines: Unity's Editor vs Runtime APIs
  • Inform 7's distinction between "setting up" and "play begins"
  • MUD codebases often have "wizard" vs "player" commands

Monday, 07. July 2025

Renga in Blue

The Dark Crystal: A Thousand Years of Solitude

I’ve finished the game, and my previous posts are needed to understand this one. Last time I had jumped into the chasm/the last disk side of the game. I’ve also watched the movie now, so I can compare a little: figuring out to jump here would have been easier from the movie. There was a […]

I’ve finished the game, and my previous posts are needed to understand this one.

From the 1983 Sierra Hi-Res Adventure catalog, via Sierra Chest. The Dark Crystal is the last of the series.

Last time I had jumped into the chasm/the last disk side of the game.

I’ve also watched the movie now, so I can compare a little: figuring out to jump here would have been easier from the movie. There was a vague hint in the game about the wings from the prophecy wall, but the graphics didn’t make it super clear; I had originally just thought it would end in a “you landed on X fantasy critter which let you fall safely” result. It’s not much better in the movie, though! The wings appear for the first time in the falling scene, and when Jen asks about why he doesn’t have wings and Kira does, Kira points out of course he doesn’t, he’s a boy. And that’s the only time the wings appear and that’s all that is said about them. (I’m jumping ahead a little, but by the end I wasn’t thrilled with the game generally, and while I think while some of this is the fault of Sierra, some is the fact that the movie they’re adapting often just has things happen. It’s non-optimal for an adventure conversion where understanding the circumstances is key to figuring out puzzles.)

The movie’s design had unexpected lore pop up whenever it was needed. It’s fair to treat this as a stylistic choice, but it’s hard to cope with in adventure game form.

Landing, Jen and Kira are at a stone face. You can circle around the castle (east or west) to find a second stone face. The choice of face is simply based on the symbol seen on the prophecy panel.

Wrong.

Right. Just differentiating a binary choice doesn’t make for a great puzzle.

Things get worse from here, as there’s a locked door that can’t be reached because of some bars.

THE “TEETH” OF THE STONE MONSTER ARE ACTUALLY THE METAL BARS OF A GATE. JUST OUT OF REACH BEHIND THE BARS IS A CLOSED DOOR.

This is the worst puzzle in the game. At least conceptually I got what to do next: we can’t reach through the bars, but we’re holding someone small (Fizzgig) so they should go through instead. However, no command I attempted worked to try to get Fizzgig in (see the growling on my second screenshot). I finally gave up and checked hints again, and found the right command was SEND FIZZGIG.

Argh! Again we have an “isolate”, a verb I’ve never seen before in an adventure and there’s a fair chance I will never see it again. The troubles aren’t over yet, though.

WHERE DOES JEN WANT TO SEND HIM?

You can’t type IN BARS (like the manual specifies) or BARS. You have to instead type SEND BARS. This is the most absurd parser command in the entire Hi-Res Adventure catalog. It’s not over yet, though! Fizzgig comes back with a key, but the door is described as “just out of reach”. UNLOCK DOOR? No. UNLOCK BARS? No. You need to USE KEY, then OPEN BARS.

This is followed by a set of tunnels that serve no purpose other than filling space.

This was fairly standard for Time Zone — and sometimes even kind of worked to give a sense of atmosphere — but here, despite the graphic quality, this really comes off as designed for a different game.

Expressive hand-drawn characters encountering a dead end.

Once in the right spot, things are back on track with the movie as the Chamberlain appears, who drops rubble on Jen and takes Kira.

Heading south (now alone) again follows the movie as Jen encounters a Garthim nest.

Again, the parser is highly irritating here. I tried all possible directions (N/S/E/W/U/D) and failed to escape. I finally broke down and checked hints again (my resistance being much lighter by now) and found out RUN is now suddenly special, and is given without specifying a direction. (I want to emphasize RUN otherwise says IN WHICH DIRECTION DOES JEN WISH TO GO?, strongly implying it isn’t really understanding the word.) The Garthim smash a hole that Jen can then escape out of (GO HOLE).

In the movie, while all this is happening, SkekNa, the Skeksis slave master, is trying to drawn Kira’s life essence in a room full of caged animals; Aughra is in the same room (also caged up) and encourages her to talk to the animals, who escape and attack, eventually causing the downfall of SkekNa. By my subjective opinion (just rewatching it yesterday) it is the best moment of the movie, having one of the characters (Kira) use an ability that was fully introduced earlier (talking to animals) combined with help from another character.

None of this happens in the game. Aughra is tied up and Jen can untie her, but there’s no particular drama here:

Again, to be fair, Jen doesn’t do much other than run from things, so with the game switched to his perspective only, that’s about all that’s possible. Past Aughra is another scene where you need to RUN…

…and after a couple more steps evading Skeksis, you can come across a SCEPTER which will be useful shortly…

..and the Skeksis, eating food.

GO CURTAIN (fortunately strongly implied) lets Jen get close enough to hear, where for some reason they talk about a secret passage in a tower. The tower in question is not that far away, and as long as you’ve heard the conversation, a panel will be available. In order to reach it, you need to LOOK SCEPTER, then refer specifically to the HOOK, because why not put yet another parser headache in at the end of the game.

USE HOOK reveals a passage to the Great Conjunction ritual with the Skeksis and Kira.

Following the movie (and fortunately clear even just playing the game) the right command is JUMP. Ads for the game even show Jen sitting on the crystal so it is a fairly iconic pose.

The crystal goes flying, and Kira goes to throw it back, but the RITUAL-MASTER warns Kira she will die if she throws it.

The right response here is to say NO. Kira throws the crystal to Jen, gets stabbed, and Jen can now insert the crystal…

…causing the Skeksis to transform into the urSkeks, and Kira is dead, or well, “dead”. The final command to win is KISS KIRA.

In the movie, things make a little more sense: early on, the Mystics all travel together slowly to the castle, clearly driven by some sense of ritual. They arrive right at the Great Conjunction and when the crystal is healed they merge back into the Skeksis, which is how the urSkeks get made.

From the Dark Crystal Wiki.

The two races have been separated since the damage to the stone, the Mystics (the urSu) moving to the Valley of the Stones to seal themselves in safety for a thousand years. I have some issues with the movie but this cycle ends up being suitably epic; it’s hard to understand what’s going on with the game without the context.

This isn’t quite the weakest of the Hi-Res games, but I think it is the weakest of the ones designed by Roberta Williams. Her style really lends itself to a more exploratory structure, and the part of the game I enjoyed myself most was the middle section where I was discovering new things in the landscape; with the highly linear series of events loosely following the movie, it most ran head-first into the twin issues of boring puzzles and the bad parser. There were some high aspirations; Cerf called the player both the “hero” and “a kind of movie director”. Unfortunately, there just isn’t enough flexibility for that to really hold out, and I struggled to find any extra “fun” actions or extra routes to take.

The only moment I could find in the later part of the game.

I don’t think the movie adventure-game concept is untenable; Lucasarts with Indiana Jones and the Last Crusade did a serviceable (even excellent) job of it, but Indiana Jones is full of both action and the possibility of alternate routes. When Indy fails to bluff the butler at the castle and punches him instead (movie version) you can do the bluff successfully. When Hitler signs the Grail diary (in movie), in game you can trick him into signing a travel pass instead making later parts of the game easier. It’s possible to get the Grail back to the Grail Knight at the end without any destruction. I don’t think Roberta Williams would have knocked things out of the park given easier-to-work with source material (not Jen Runs Away From Things: The Movie), but The Dark Crystal may have been just the wrong outlet for a movie game (or at least following the script — maybe a prequel would be better, along the lines of the recent Age of Resistance series on Netflix).

While The Dark Crystal was being wrapped up, IBM was already in contact with Sierra and work was starting for a new project for the upcoming PCjr computer: King’s Quest. Not long after, Sierra would take another whack at a movie game (The Black Cauldron) so we’ll see if King’s Quest’s environmental style makes for a more compelling delivery method.

The front door of the Williams’s custom-built house made around the time of the game. I’m not sure it’s accurate to say they were apathetic about it given the stained glass. Source.


My So Called Interactive Fiction Life

Integration Testing Exposes Flaw

So far the unit testing had gone very smoothly. The architecture was holding up. Until we got to the integration testing, there were no major flaws.But these last tests showed a shallow, but wide problem in our entities. We had been using the name as the id, but that
Integration Testing Exposes Flaw

So far the unit testing had gone very smoothly. The architecture was holding up. Until we got to the integration testing, there were no major flaws.

But these last tests showed a shallow, but wide problem in our entities. We had been using the name as the id, but that won't scale and causes all kinds of testing and integration problems. So we discussed how we could update the entity.ID so that it would be stable.

We ended up with a new ADR (Architecture Decision Record) that explains it:


ADR-005: Entity ID System Design

Date: 2025-07-05
Status: Implemented (2025-07-06)
Implementation: Complete

Context

The world-model package needs a consistent system for identifying entities. Currently, tests use human-readable strings as IDs (e.g., 'player', 'kitchen', 'door1'), but this approach has several issues:

  1. Ambiguity: No clear distinction between entity IDs and display names
  2. Collision Risk: Multiple entities could accidentally use the same ID
  3. Author Experience: Future Forge authors will want to use meaningful names, not manage IDs
  4. Reference Resolution: Exits and doors reference other entities by string, unclear if ID or name
  5. Type Indication: IDs don't indicate what type of entity they represent

Decision Drivers

  • Developer Experience: Tests should be readable and easy to write
  • Author Experience: Forge authors should never see or manage IDs
  • Debugging: IDs should be short and indicate entity type
  • Uniqueness: System must guarantee no ID collisions
  • Flexibility: Support both programmatic and authored content

Considered Options

Option 1: Keep Current System (Human-Readable IDs)

  • Use meaningful strings as IDs ('player', 'kitchen')
  • Enforce uniqueness at creation time
  • Let Forge layer handle name-to-ID mapping

Pros:

  • Tests remain readable
  • No refactoring needed
  • Simple to understand

Cons:

  • Risk of collisions
  • No type indication
  • Mixes concerns (ID vs display name)

Option 2: Auto-Generated Numeric IDs

  • Generate IDs like 'entity_1', 'entity_2'
  • Maintain name-to-ID mapping
  • All methods use IDs only

Pros:

  • Guaranteed uniqueness
  • Clear separation of ID and name

Cons:

  • Hard to debug
  • Tests become unreadable
  • No type indication

Option 3: Type-Prefixed 3-Character IDs

  • Format: [type-prefix][2-char-base36] (e.g., r01, d01, i01, a01)
  • Auto-generated with type indication
  • Maintain bidirectional name-ID mapping

Pros:

  • Readable in logs and debugging
  • Type indication built-in
  • Guaranteed uniqueness
  • Short and efficient

Cons:

  • Requires refactoring
  • Tests need updating

Decision

We will implement Option 3: Type-Prefixed 3-Character IDs with the following design:

ID Format

[type-prefix][2-character-base36-counter]

Examples:
- r01, r02, r03 (rooms)
- d01, d02 (doors)  
- i01, i02 (items)
- a01 (actors)
- c01 (containers)
- s01 (supporters)

Type Prefixes

const TYPE_PREFIXES = {
  'room': 'r',
  'door': 'd',
  'item': 'i',
  'actor': 'a',
  'container': 'c',
  'supporter': 's',
  'scenery': 'y',
  'exit': 'e'
};

API Design

class WorldModel {
  // Entity creation returns entity with generated ID
  createEntity(displayName: string, type: string = 'object'): IFEntity;
  
  // All methods use IDs only
  getEntity(id: string): IFEntity | undefined;
  moveEntity(entityId: string, targetId: string | null): boolean;
  
  // Helper for name resolution
  getId(name: string): string | undefined;
  getName(id: string): string | undefined;
}

Usage Pattern

// Creation
const kitchen = world.createEntity('Kitchen', 'room'); // Returns entity with id='r01'
const player = world.createEntity('Player', 'actor');  // Returns entity with id='a01'

// Usage with returned references
world.moveEntity(player.id, kitchen.id);

// Usage with name lookup (when needed)
world.moveEntity(world.getId('Player')!, world.getId('Kitchen')!);

Implementation Plan

  1. Phase 1: Implement ID generator and update WorldModel.createEntity
  2. Phase 2: Add name-to-ID mapping and helper methods
  3. Phase 3: Update all integration tests to use new pattern
  4. Phase 4: Update stdlib to use IDs consistently
  5. Phase 5: Document patterns for Forge integration

Consequences

Positive

  • Unique IDs: No collision risk
  • Debugging: Type-prefixed IDs make logs readable
  • Clean API: Clear separation between IDs and names
  • Future-Proof: Forge can build on top without changes

Negative

  • Refactoring Required: All tests need updating
  • Learning Curve: Developers must understand ID vs name
  • Verbosity: Some test code becomes slightly longer

Neutral

  • Test readability changes from 'kitchen' to kitchen.id
  • Error messages will show IDs like 'r01' instead of 'kitchen'

Notes

  • The 3-character limit provides 46,656 possible IDs per type (36^2), far exceeding any IF game needs
  • Case-insensitive name lookup will prevent common errors
  • Forge will completely hide this ID system from authors
  • Migration can be done incrementally by updating tests one at a time

Implementation Details (Added 2025-07-06)

What Was Built

  1. ID Generation System
    • Auto-generates IDs like r01, d02, i03 based on entity type
    • Maintains per-type counters in WorldModel
    • Throws error on overflow (>1295 entities per type)
  2. Name/ID Mapping
    • Bidirectional maps: nameToId and idToName
    • Case-insensitive name lookup
    • Automatic mapping on entity creation
  3. Entity Name Resolution
    • entity.name getter with priority chain:
      1. attributes.displayName (highest)
      2. Identity trait name
      3. attributes.name
      4. ID (fallback)
  4. Backwards Compatibility
    • Old createEntity(id, name) signature still works with deprecation warning
    • Save/load system preserves ID mappings
    • Automatic ID system rebuild for old saves
  5. Test Infrastructure
    • Helper functions: getTestEntity, expectEntity, moveEntityByName
    • Updated all integration tests
    • Updated fixture functions

Key Insights

  1. Separation of Concerns Works: The existing architecture already separated names (for users) from IDs (for system), making the refactor smooth.
  2. Command System Unchanged: The CommandValidator already used entity.name for resolution and entity.id for operations, so no changes needed.
  3. Test Readability Maintained: Helper functions keep tests readable while using the new system.

Actual ID Limits

  • Format: [prefix][00-zz] gives 1296 IDs per type (36^2)
  • More than sufficient for any IF game
  • Clear error on overflow

Migration Path

  1. Update entity creation calls
  2. Use entity references or name resolution
  3. Update test assertions
  4. Old saves auto-migrate

We decided to go ahead with the refactoring which touched a lot of code and tests, but that's now completed and we're able to move forward. Cost us a day, but it was worth it.

I still need to re-run all of the unit tests and then we go back to the engine and story to finish the entire chain of architecture.

Saturday, 05. July 2025

My So Called Interactive Fiction Life

World Model Testing Pt 1.

There were some challenges here, specifically visibility. I have about 10 ADRs (Architecture Decision Records) that I'll share that address complex issues like lighting. We implemented a baseline standard isDark lighting system and this can be replaced with future extensions.PASS tests/unit/world/visibility-behavior.test.ts (18.

There were some challenges here, specifically visibility. I have about 10 ADRs (Architecture Decision Records) that I'll share that address complex issues like lighting. We implemented a baseline standard isDark lighting system and this can be replaced with future extensions.

PASS tests/unit/world/visibility-behavior.test.ts (18.625 s)
  VisibilityBehavior
    canSee
      ✓ should always see self (372 ms)
      ✓ should see entities in same room (1 ms)
      ✓ should not see entities in different room (1 ms)
      ✓ should see the room observer is in
      ✓ should not see invisible entities
      ✓ should see entities in transparent containers
      ✓ should see entities in open opaque containers (9 ms)
      ✓ should not see entities in closed opaque containers (1 ms)
      ✓ should handle nested containers
      ✓ should block sight through any closed container in path (1 ms)
    dark rooms
      ✓ should not see anything in dark room without light
      ✓ should only see lit light sources in dark room (1 ms)
      ✓ should see everything when carrying lit lamp
      ✓ should not benefit from light in closed container
      ✓ should handle room lighting toggle
    getVisible
      ✓ should return all visible entities
      ✓ should include carried items (1 ms)
      ✓ should handle empty room
      ✓ should handle observer not in room (1 ms)
    isVisible
      ✓ should return true for uncontained entities
      ✓ should return false for invisible scenery
      ✓ should return true for entity in transparent container
      ✓ should return true for entity in open opaque container
      ✓ should return false for entity in closed opaque container
      ✓ should handle opaque container without openable trait
    complex scenarios
      ✓ should handle deeply nested visibility
      ✓ should handle supporter visibility
      ✓ should handle visibility in nested containers (1 ms)
      ✓ should handle circular containment gracefully
    edge cases
      ✓ should handle missing entities gracefully (1 ms)
      ✓ should handle entities with no location
      ✓ should handle max depth in containment path

PASS tests/unit/world/world-model.test.ts (19.921 s)
  WorldModel
    initialization
      ✓ should create empty world model (415 ms)
      ✓ should accept configuration
    entity management
      ✓ should create entity (1 ms)
      ✓ should throw on duplicate entity id (163 ms)
      ✓ should get entity by id (1 ms)
      ✓ should return undefined for missing entity (1 ms)
      ✓ should check entity existence
      ✓ should remove entity
      ✓ should return false when removing non-existent entity
      ✓ should get all entities
      ✓ should update entity (1 ms)
      ✓ should handle updating non-existent entity
      ✓ should throw in strict mode when updating non-existent entity
    spatial management
      ✓ should get entity location (1 ms)
      ✓ should get container contents (1 ms)
      ✓ should move entity
      ✓ should remove entity from world
      ✓ should check if move is valid
      ✓ should prevent moving to non-container
      ✓ should prevent containment loops
      ✓ should get containing room (1 ms)
      ✓ should get all contents recursively
      ✓ should handle max depth limit
    world state management
      ✓ should get and set state (1 ms)
      ✓ should get and set state values
      ✓ should handle nested state values
    query operations
      ✓ should find entities by trait (1 ms)
      ✓ should find entities by type
      ✓ should find entities with predicate
      ✓ should handle find options (1 ms)
    visibility and scope
      ✓ should get entities in scope
      ✓ should include carried items in scope
      ✓ should check visibility (1 ms)
    relationships
      ✓ should add relationship
      ✓ should get related entities
      ✓ should remove relationship
      ✓ should handle multiple relationship types
      ✓ should handle non-existent entities in non-strict mode
      ✓ should throw in strict mode for non-existent entities (1 ms)
    utility methods
      ✓ should calculate total weight
      ✓ should detect containment loops
      ✓ should find path between rooms
      ✓ should get and set player
      ✓ should throw when setting non-existent player (1 ms)
    persistence
      ✓ should serialize to JSON (1 ms)
      ✓ should load from JSON (1 ms)
      ✓ should clear world
    event sourcing
      ✓ should register and apply event handler (2 ms)
      ✓ should validate events (1 ms)
      ✓ should throw when applying invalid event
      ✓ should preview event changes
      ✓ should track event history
      ✓ should get events since timestamp (1 ms)
      ✓ should clear event history
      ✓ should unregister event handler
      ✓ should handle unregistered events silently
    edge cases
      ✓ should handle empty world operations (1 ms)
      ✓ should handle removing entity with contents
      ✓ should handle circular references in toJSON

PASS tests/unit/world/spatial-index.test.ts (62.272 s)
  SpatialIndex
    basic operations
      ✓ should add child to parent (788 ms)
      ✓ should add multiple children to parent (1 ms)
      ✓ should remove child from parent (1 ms)
      ✓ should move child to new parent
      ✓ should handle non-existent parent (1 ms)
      ✓ should handle non-existent child
    remove operations
      ✓ should remove entity and its relationships
      ✓ should remove only specified child
      ✓ should handle removing non-existent child
      ✓ should clean up empty parent sets
    hasChildren
      ✓ should return true for parent with children
      ✓ should return false for parent without children
      ✓ should return false after removing all children
    getAllDescendants
      ✓ should get all descendants
      ✓ should respect max depth (1 ms)
      ✓ should handle entity with no descendants
      ✓ should handle circular references
      ✓ should collect all descendants up to max depth
    getAncestors
      ✓ should get all ancestors (1 ms)
      ✓ should get ancestors up to depth
      ✓ should handle entity with no ancestors
      ✓ should handle missing entity
    clear
      ✓ should clear all relationships
    persistence
      ✓ should serialize to JSON (1 ms)
      ✓ should load from JSON
      ✓ should handle empty JSON
      ✓ should clear before loading (1 ms)
    edge cases
      ✓ should handle adding same child multiple times
      ✓ should handle removing child from wrong parent
      ✓ should handle self-parenting
      ✓ should handle very deep hierarchies
      ✓ should maintain consistency when moving entities (1 ms)

Test Suites: 3 passed, 3 total
Tests:       123 passed, 123 total
Snapshots:   0 total
Time:        84.38 s

Zarf Updates

Discoggin: an IF bot for Discord

Here's a new toy: a Discord bot that plays IF games. Say you've got a group of people who want to play an IF game together. You'd log into the IFTF Discord and go to the #zarfbot-9000 channel. (That's where the bot is currently running.) Type ...

Here's a new toy: a Discord bot that plays IF games.

Say you've got a group of people who want to play an IF game together. You'd log into the IFTF Discord and go to the #zarfbot-9000 channel. (That's where the bot is currently running.)

Type /games to see a list of available games, then /select GAME to select one. Or if you want to install one off the IF Archive, say, you could type a command like

/install https://ifarchive.org/if-archive/games/zcode/huntdark.z5

(These are regular Discord slash commands. Hit / in the #zarfbot-9000 channel to see a list of bot commands. You'll have to scroll down to "Discoggin-IF", mind you. The IFTF Discord has a couple of different bots installed.)

After selecting or installing a game, type /start to start it up. Or maybe the game is already running! If someone else installed it, they might have been in the middle of a session. This is meant for social play, so you can jump back in and continue seamlessly where the last player left off. Type /recap to see the last few commands.

What are commands? Simple: type >GET LAMP. No slash; that's a regular channel message starting with the > character. The bot will accept the command and respond. It ignores non-> messages, so you can freely discuss the game with your friends in the channel.

A a couple of commands from a session of Lost Pig by Admiral Jota. From Lost Pig by Admiral Jota.

Note that anybody in the channel can give a game command. There's no built-in "driver" system. This is a friendly Discord; the code of conduct is linked in the #rules channel. Chill with your friends and work out a scheme that works for the group.

That's it! But I bet you have questions.

Why?

I was inspired by ClubFloyd, which has been meeting on IFMud every week for most of twenty years to group-play IF games.

There's nothing wrong with a MUD. But Discord is a lot more popular these days; more people know how to use it.

What game formats are supported?

The bot can handle Inform games (Z-code and Glulx), Ink games, and YarnSpinner games.

For the choice-based formats, you'll see numbered selections. Type a command like >#3 to select a choice. This also works for hyperlink-based Glulx games.

The bot supports only plain text, a bit of text styling (italics and bold), and hyperlinks. No graphics, not even for Glulx games that include graphics.

What other slash commands does the bot understand?

See the README file. The most interesting ones are:

  • /status : Display the current status line.
  • /recap COUNT : Recap the last few commands (max of 10).
  • /files : List save files (and other data files) recorded in this session.
  • /forcequit : Shut down a game if it's gotten stuck for some reason, or if it doesn't accept >QUIT. (You will then need to /start it again.)

What if the game is waiting for a keystroke?

You can type >space, >return, or a key name like >A.

Can I save my position?

The bot autosaves every turn. You can take a break, or even /select a different game, and then come back to your existing session. You'll be where you left off.

If you want to do a manual >SAVE, that will work too. Enter the save filename as the next command: >inside-house. Use the slash command /files to see the list of manual save files.

Can I save a transcript?

The standard >SCRIPT ON command should work for Inform games. However, I haven't yet implemented a way to download transcripts.

Really, the correct plan is for the bot to save a transcript of every game, like Lectrote does. (Without requiring a >SCRIPT command.) Then it could generate a two-column transcript -- game output and channel discussion -- like ClubFloyd does.

I should warn you that I've started implementing this plan. That means that the bot is recording discussion in the #zarfbot-9000 channel! Anything you say there may be archived for posterity as part of the active game's transcript.

Are there bugs?

Certainly! But I'm not aware of many, because the bot hasn't gotten serious testing yet. Perhaps this post will uncork the flood.

The biggest known bug is that UNDO doesn't work in Z-code games. Whoops, misread some tests last month! UNDO works fine.

The choice-based formats don't support UNDO, SAVE, or RESTORE.

Some games don't test properly for lack of graphics. For example, Alabaster throws a lot of "graphics call not supported" errors. This is technically a bug in the game, not the bot, but I realize that cuts no ice.

Can I get this bot on my Discord?

I am not currently allowing public installations of the bot. The back end is running on a machine I own. (A Mac Mini in my office, in fact.) It's not a big load on the machine or my home network -- but if lots of people started playing games, it would become a big load.

However, the bot is open source. You're welcome to run your own copy and install it wherever you want. It's fairly easy to set up the bot if you're familiar with Python. See the README for instructions.

Unfortunately the game interpreters are more of a hassle. They're open-source too, but they're variously written in C, JS/Node, and C#/.NET. So there's a bit of an adventure there, no pun intended.

What about TADS, Hugo, Alan...?

Adding these formats won't be entirely easy. Allow me to get technical.

To support a game format, the bot needs an interpreter with two features:

  • A Glk I/O interface which generates the GlkOte JSON protocol;
  • The ability to autosave every turn.

The first part is easy. Most parser IF tools support Glk. (The popular Gargoyle interpreter has almost every current IF system as a Glk-enabled component. There's fiddly details but I'll skip them.)

The second part is more of a pain. The traditional IF interpreter expects you to start up a game and play for a while. Saves are a manual process. (It was the 80s, remember!) The interpreter keeps all its state in memory.

But the Discord bot doesn't want to run persistent interpreter processes. It could have dozens or even hundreds of "live" game sessions. It's only playing one at a time; the rest are saved in the background. Therefore, the bot only launches an interpreter when a player command arrives! The interpreter's job is:

  • Load the game file;
  • Silently restore the last position;
  • Receive the player command from the bot (as a JSON message);
  • Carry out that command;
  • Send the game output for that turn to the bot (JSON again);
  • Silently autosave the new position;
  • Shut down.

This all happens in a moment. By the time you see the game response, the interpreter has already autosaved and exited.

Most IF interpreters don't support this style of play. I added the capability to Glulxe and Bocfel, back when I was working on iPhone IF apps. (Mobile games don't have this one-turn-at-a-time style, but they must autosave -- players insist on it.) That covers Glulx and Z-code games. But other formats will be more work.

But what about Twine? Surely you can support Twine.

Oh, geez, I wish.

Ink and YarnSpinner are components which generate text. They were designed to be plugged into a game framework. Their save systems are completely accessible and modular. So it was very easy to write wrappers to fit them into the bot framework.

(Okay, YarnSpinner's save system required some tweaking -- I don't think it intended for the game to save at every single choice point. But I got it working.)

Twine was not designed to be a component. Or rather, it was designed to be a component of a web page. It interacts with its environment through JavaScript and DOM manipulation.

In other words, it doesn't really make sense to talk about the plain-text output of a Twine game. The game is intertwi-, um, let's say entangled, with its story format. And the format is all about macros, which are Javascript calls.

One could imagine a Twine story format which is deliberately limited to plain text-and-choice interactions. (Like the WritingFantasy format limits you to what works in a printable book.) It would have variables and a bit of formatting, but no macros. Then you could have a tool like Gordian which converts that format to work with the Discord bot.

But you couldn't take an arbitrary playable Twine game and shove it in there. It would have to be written for that style.

Why is it called "Discoggin"?

Man, I don't know.

I suspect people are going to call it "the zarfbot", because that's the channel name. Oh well. Someday I'll write a second bot, and then you'll I'll be sorry.


Renga in Blue

The Dark Crystal: Evil Triumphs Over All

(Continued from my previous posts on The Dark Crystal.) To continue directly from last time, I had unearthed a spiral but with some confusion as to what to do with it. One of my readers (RavenWorks) suggested GAZE off my verb list, referencing the odd reaction where Jen was refusing to look at it closely. […]

(Continued from my previous posts on The Dark Crystal.)

To continue directly from last time, I had unearthed a spiral but with some confusion as to what to do with it.

One of my readers (RavenWorks) suggested GAZE off my verb list, referencing the odd reaction where Jen was refusing to look at it closely.

This was easy to miss, and someone who later is trying to solve the riddle “legit” (by thinking what normal word might be an answer) would get incredibly frustrated.

From here I got stuck for a very long time and I ended up breaking my streak on Roberta Williams games: I looked up a hint. (I beat Mystery House, Mission: Asteroid, and Time Zone without needing any. Alas.) It turned out that back where the lily pad could be cut (we’ll use that shortly) there is another secret.

I had tried GET FLOWER and got the same response I had gotten in some other locations that mention flowers, namely:

JEN PICKS A FLOWER AND SAVORS ITS LOVELY FRAGRANCE. NOTICING NOTHING ELSE REMARKABLE ABOUT IT, HE DROPS IT.

I was still slightly suspicious of the flowers, but I had treated “chattering” as a mere metaphor (like a “babbling” brook). I should have done TALK FLOWERS.

To be fair, just to the west there is a scene with “THE CHATTERING OF FLOWERS AND CALLING OF CREATURES IS ALMOST DEAFENING”. LISTEN FLOWERS also gives the message (in both rooms) “WITH THEM ALL TALKING AT ONCE, HE CAN’T UNDERSTAND WHAT THEY ARE SAYING.” I guess it’s a “fair” puzzle but it was a very strange one (in terms of narrative momentum) to be stuck for several hours on.

Taking the advice to listen to the brook:

This is just directions. Normally going EAST and then EAST again has the path blocked by foliage, but after hearing from the brook, the path is open.

Trying to go farther north (following the directions of the brook) the game says “THE SWAMP LOOKS DEEP AND DANGEROUS” and that it appears suicidal to attempt a crossing. I had already tried RIDE PAD earlier with the river; this is the real place to use it. After using it to swim across it floats away (one less inventory item to test everywhere, good).

Going north and then west gets the player stuck in a bog; that’s a good place to go, but it’s too early. You’re supposed to instead go north and east (the last part of the directions) and get caught in some vines.

After several moves, Aughra appears.

You can SAY YES followed by typing MOON DAUGHTER. Imagine being stuck at this point!

She leads you/Jen north to her observatory, where she asks what you want. Hopefully players paid attention during that info-dump at the start so they know to SAY SHARD. She will put four colored shards up on the table, and say that she doesn’t know which is the real one. Finally, my obsessive playing of the flute pays off.

I vaguely recall something like this happened in the movie, but I don’t recall detail. I’m still waiting until I finish the game to go back to rewatch.

That’s three puzzles in a row that require a piece of information or item from somewhere that isn’t trivial to get:

a.) first, the lily pad for swimming obtained by cutting the stem

b.) then, the riddle answer obtained from an extremely random spot in the game (the spiral hidden under moss), where it seems like you ought to answer the riddle normally

c.) then, the flute which is buried and which I got via luck.

The linear structure with secret requirements is rather different from the previous Roberta Williams games. You could argue the entirety of Time Zone was a treasure hunt intended to allow making it through a long linear set of puzzles in the finale, but it is clear from the start you’re going to need to build up a collection; here, it is unclear if such a hunt is needed in the first place. This really comes into focus with the next puzzle: after you get the shard, the observatory is attacked. You have one move to react.

I tried some natural and intuitive ways to escape, but failed to have any luck, so I spent some time combing over locations for yet another missed item. (What’s especially suggestive is that the eye-bat shows up when you land after passing through the swamp using the pad; I thought maybe I needed to kill the bat so the enemies wouldn’t show up after, leading me on a fruitless hunt for slingshot ammo.)

It turned out an early command I tried (CLIMB WINDOW) was right. I was just supposed to type GO WINDOW instead.

Since the pad is gone Jen can’t swim back, so the only choice is the bog where Jen gets stuck. Fortunately, there’s help this time.

I knew immediately this had to be Kira, but assuming someone who hasn’t seen the movie at all, they’d have trouble here because her (and her pet Fizzgig) don’t get mentioned in the text. I imagined I was a player who didn’t know her name and finally hit upon LOOK GELFLING.

After the rescue: “I THOUGHT I WAS THE ONLY LIVING GELFLING. BUT THEN, I GUESS YOU MUST HAVE THOUGHT THE SAME THING!”

The remainder of my gametime has Jen and Kira travelling together, and all commands affect both of them. This is very, very, unconventional, although it works; I never got confused because of the dual-person controls, although I was a bit boggled by the fact that all the previous scenes (barring the opening area blocked off) get re-rendered with both characters in them.

This is emphasized by the very next act, which requires flipping a shell, and the game states it is too heavy for just Jen to move, and both Jen and Kira need to work together to FLIP SHELL. This reveals a pouch of “SMOKE SEEDS”, and the shell itself can be used as a boat.

The two land in the village from earlier, and while the scene shows merriment, a bat also shows up; the player needs to type EAST or WEST (or some direction to leave) quickly enough and they’ll be able to escape. When they come back the village is destroyed.

I could make fun of how blasé this is (especially with the commands just being one to leave, one to go right back), but track back to all our previous adventure games (1982 and before): when have any tried to do a moment like this? Nobody — not even Infocom, yet — had previously had a main character have everyone they grew up with suddenly get suddenly wiped out (or at least captured). The closest I can think of is Saigon: The Final Days. So while to modern eyes this seems clumsy, I do want to emphasize it was getting into new game design territory (in order to follow the plot of the movie, I assume).

For some reason, having Kira with us makes the Landstriders friendly enough to ride, but before I show that, I want to mention one of the other scenes has a difference:

A Skeksis appears and says he is tired of killing and wants peace, and says to follow him south.

Ha ha no of course not. Fortunately you can just avoid going south and you won’t have the death scene (alternately, do the ruin viewing before the chaos starts).

Hopping on our new rides (no explanation is given why they are fine with being ridden now, I assume Kira helped):

The Landstriders easily make it over the chasm. This then leads to a long and I think empty span of rooms.

This seems to be reaching back to Time Zone rather than forward to King’s Quest. The art is atmospheric, at least

Fizzgig looks unhappy.

Eventually you come across a combination castle/ravine that you can circle all the way around if you like, but must eventually approach.

Approaching results in another Gathrim attack, and then you only have one turn to react. If you do poorly, you get a front-line seat at the Great Conjunction.

Maybe I can call this BAD END and end the game here?

It took me a beat to realize Jen and Kira are near the ravine so the right action is to JUMP. This causes a disk swap to the final side.

I assume this will be the final stretch, so … one more post? Two? It depends if I have to talk to any more flowers.

Since we’ve run into a Skeksis in-game now, I wanted to show this. The illustration comes from Leonard B. Lubin, via a book of Lewis Carroll poems. This was Jim Henson’s original inspiration in 1975. “It was the juxtaposition of this reptilian thing in this fine atmosphere that intrigued me.”

Friday, 04. July 2025

My So Called Interactive Fiction Life

Sharpee Platform Architecture

Is very nearly complete.I have a baseline story (Cloak of Darkness) somewhat working as expect. Enough to believe the architecture is solid. So how do we prove it outside of a big game? Unit testing.I have completed unit test suites for the packages Core, Event Processor, and am
Sharpee Platform Architecture

Is very nearly complete.

I have a baseline story (Cloak of Darkness) somewhat working as expect. Enough to believe the architecture is solid. So how do we prove it outside of a big game? Unit testing.

I have completed unit test suites for the packages Core, Event Processor, and am working through World Model. So far, one minor refactor to manage circular dependencies. I moved a bunch of shared types to a new package if-domain.

The structure is holding strong. This is where my past iterations always failed and massive architecture gaps appeared. This is why unit testing is so critical.

These are the currently passing test suites:

dave@DCSurface:/mnt/c/repotemp/sharpee/packages/core$ pnpm test

> @sharpee/[email protected] test /mnt/c/repotemp/sharpee/packages/core
> cross-env NODE_NO_WARNINGS=1 jest

 PASS  tests/setup.test.ts (28.131 s)
 PASS  tests/debug/types.test.ts (28.407 s)
 PASS  tests/events/simple-event-source.test.ts (35.03 s)
 PASS  tests/integration/event-rule-integration.test.ts (36.448 s)
 PASS  tests/types/result.test.ts (45.175 s)
 PASS  tests/events/semantic-event-source.test.ts (43.965 s)
 PASS  tests/events/event-system.test.ts (49.058 s)
 PASS  tests/language/registry.test.ts (48.823 s)
 PASS  tests/language/language-requirements.test.ts (60.276 s)
 PASS  tests/rules/simple-rule-system.test.ts (74.458 s)

Test Suites: 10 passed, 10 total
Tests:       122 passed, 122 total
Snapshots:   0 total
Time:        100.522 s
Ran all test suites.
> @sharpee/[email protected] test /mnt/c/repotemp/sharpee/packages/event-processor
> cross-env NODE_NO_WARNINGS=1 jest

PASS tests/unit/processor-reactions.test.ts (30.709 s)
PASS tests/unit/processor.test.ts (30.717 s)
PASS tests/unit/handlers/registration.test.ts (101.462 s)

Test Suites: 3 passed, 3 total
Tests:       17 passed, 17 total
Snapshots:   0 total
Time:        133.609 s
Ran all test suites.
> @sharpee/[email protected] test /mnt/c/repotemp/sharpee/packages/world-model
> cross-env NODE_NO_WARNINGS=1 jest

PASS tests/unit/entities/entity-store.test.ts (25.391 s)
  EntityStore
    basic operations
      ✓ should add and retrieve entities (530 ms)
      ✓ should return undefined for non-existent entities (10 ms)
      ✓ should remove entities and clear traits (1 ms)
      ✓ should clear all entities
    querying
      ✓ should get all entities (1 ms)
      ✓ should get entities by type (1 ms)
      ✓ should find entities with specific trait (1 ms)
      ✓ should find entities with all specified traits
      ✓ should find entities with any specified traits (1 ms)
    iteration
      ✓ should be iterable (1 ms)
    serialization
      ✓ should serialize to JSON (1 ms)
      ✓ should deserialize from JSON
    size property
      ✓ should reflect number of entities (1 ms)
    edge cases
      ✓ should handle removing non-existent entity
      ✓ should handle duplicate adds gracefully
      ✓ should work with empty store

PASS tests/unit/behaviors/behavior.test.ts (26.541 s)
  Behavior
    trait requirements
      ✓ should validate entity has required traits (565 ms)
      ✓ should get list of missing traits (1 ms)
      ✓ should work with behaviors having no requirements (1 ms)
    require helper
      ✓ should return trait when present
      ✓ should throw error when required trait is missing (209 ms)
    optional helper
      ✓ should return trait when present (1 ms)
      ✓ should return undefined when trait is missing
    behavior patterns
      ✓ should support behaviors that check state
      ✓ should support behaviors with no requirements (1 ms)
    inheritance
      ✓ should support behavior inheritance
    error messages
      ✓ should provide clear error messages for missing traits (1 ms)
    static nature
      ✓ should not require instantiation

PASS tests/unit/entities/if-entity.test.ts (91.592 s)
  IFEntity
    constructor
      ✓ should create entity with id and type (947 ms)
      ✓ should accept creation params (1 ms)
    traits
      ✓ should add trait
      ✓ should remove trait (1 ms)
      ✓ should replace existing trait of same type
      ✓ should check multiple traits with hasAll (1 ms)
      ✓ should check multiple traits with hasAny
      ✓ should get all traits
      ✓ should get all trait types (1 ms)
      ✓ should clear all traits
      ✓ should support trait aliases (getTrait, hasTrait)
    convenience properties
      ✓ should identify rooms
      ✓ should identify containers
      ✓ should identify takeable items
      ✓ should get name from identity trait (1 ms)
      ✓ should get weight from attributes
    cloning
      ✓ should create deep copy with new ID (1 ms)
    serialization
      ✓ should serialize to JSON
      ✓ should deserialize from JSON (1 ms)
    openable/lockable properties
      ✓ should detect openable trait (10 ms)
      ✓ should detect lockable trait (1 ms)
    light source properties
      ✓ should detect light provision
    switchable properties
      ✓ should detect switchable state
    actor properties
      ✓ should detect actors and players (1 ms)
    error handling
      ✓ should throw error for invalid traits (305 ms)

Test Suites: 3 passed, 3 total
Tests:       53 passed, 53 total
Snapshots:   0 total
Time:        119.596 s
Ran all test suites.
> @sharpee/[email protected] test /mnt/c/repotemp/sharpee/packages/world-model
> cross-env NODE_NO_WARNINGS=1 jest -- tests/unit/traits/

PASS tests/unit/traits/container.test.ts (24.436 s)
  ContainerTrait
    initialization
      ✓ should create trait with default values (499 ms)
      ✓ should create trait with provided data (1 ms)
    capacity constraints
      ✓ should handle weight limit
      ✓ should handle volume limit (1 ms)
      ✓ should handle item count limit (1 ms)
      ✓ should handle multiple constraints
      ✓ should handle unlimited capacity
    transparency
      ✓ should default to opaque
      ✓ should handle transparent containers (1 ms)
    enterable containers
      ✓ should default to not enterable
      ✓ should handle enterable containers (1 ms)
    type restrictions
      ✓ should handle allowed types (1 ms)
      ✓ should handle excluded types (1 ms)
      ✓ should handle both allowed and excluded types
      ✓ should handle no type restrictions
    entity integration
      ✓ should attach to entity correctly
      ✓ should replace existing container trait (1 ms)
    special container types
      ✓ should handle transparent container setup
      ✓ should handle secure container setup (1 ms)
      ✓ should handle nested container setup
    edge cases
      ✓ should handle empty capacity object
      ✓ should handle empty arrays for type restrictions (1 ms)
      ✓ should handle zero capacity values

PASS tests/unit/traits/room.test.ts (25.102 s)
  RoomTrait
    initialization
      ✓ should create trait with default values (536 ms)
      ✓ should create trait with provided data (1 ms)
    exits management
      ✓ should handle simple exits (6 ms)
      ✓ should handle exits with doors (1 ms)
      ✓ should handle blocked exits (1 ms)
      ✓ should handle custom exits (1 ms)
    lighting
      ✓ should handle dark rooms (1 ms)
      ✓ should handle lit rooms
      ✓ should handle outdoor lighting (1 ms)
      ✓ should handle underground rooms
    visit tracking
      ✓ should start unvisited (1 ms)
      ✓ should track visited state
      ✓ should handle initial description (1 ms)
    ambience
      ✓ should handle ambient sounds (1 ms)
      ✓ should handle ambient smells
      ✓ should handle both sound and smell (1 ms)
    regions and tags
      ✓ should handle region assignment (3 ms)
      ✓ should handle multiple tags (3 ms)
      ✓ should handle rooms without regions or tags (10 ms)
    entity integration
      ✓ should attach to entity correctly (1 ms)
      ✓ should work with container trait
    complex room setups
      ✓ should handle maze-like connections (1 ms)
      ✓ should handle multi-level connections (1 ms)
      ✓ should handle outdoor/indoor transitions

PASS tests/unit/traits/exit.test.ts (23.902 s)
  ExitTrait
    initialization
      ✓ should create trait with required values (588 ms)
      ✓ should throw error if required fields are missing (201 ms)
      ✓ should create trait with all optional values (1 ms)
    standard directional exits
      ✓ should handle north direction (1 ms)
      ✓ should handle south direction (1 ms)
      ✓ should handle east direction
      ✓ should handle west direction
      ✓ should handle up direction
      ✓ should handle down direction
      ✓ should handle in direction
      ✓ should handle out direction
      ✓ should handle diagonal directions
    custom exits
      ✓ should handle magic words (1 ms)
      ✓ should handle action-based exits
      ✓ should handle object-interaction exits
    bidirectional exits
      ✓ should handle simple bidirectional exit
      ✓ should handle bidirectional portal
    visibility and listing
      ✓ should handle hidden exits
      ✓ should handle visible but unlisted exits
      ✓ should handle discovered exits
    conditional exits
      ✓ should handle simple condition (1 ms)
      ✓ should handle complex condition
      ✓ should handle time-based condition
    messages
      ✓ should handle custom use messages
      ✓ should handle custom blocked messages
      ✓ should allow no custom messages (1 ms)
    entity integration
      ✓ should attach to entity correctly
      ✓ should replace existing exit trait
    special exit types
      ✓ should handle one-way exit
      ✓ should handle teleporter (1 ms)
      ✓ should handle vehicle-based exit

PASS tests/unit/traits/identity.test.ts (23.914 s)
  IdentityTrait
    initialization
      ✓ should create trait with default values (621 ms)
      ✓ should create trait with provided data (1 ms)
    article handling
      ✓ should handle "a" article
      ✓ should handle "an" article
      ✓ should handle "the" article (1 ms)
      ✓ should handle "some" article for plural/mass nouns
      ✓ should handle empty article for proper names
    aliases
      ✓ should start with empty aliases
      ✓ should store multiple aliases
    descriptions
      ✓ should handle full description
      ✓ should handle brief description separately (1 ms)
      ✓ should allow empty descriptions
    concealment
      ✓ should default to not concealed
      ✓ should handle concealed objects
    physical properties
      ✓ should handle weight (1 ms)
      ✓ should handle volume
      ✓ should handle size categories
      ✓ should allow undefined physical properties
    entity integration
      ✓ should attach to entity correctly (1 ms)
      ✓ should replace existing identity trait
    special cases
      ✓ should handle proper names correctly
      ✓ should handle mass nouns
      ✓ should handle unique items

PASS tests/unit/traits/entry.test.ts (68.884 s)
  EntryTrait
    initialization
      ✓ should create trait with default values (1006 ms)
      ✓ should create trait with provided data (1 ms)
    prepositions
      ✓ should handle "in" preposition for containers
      ✓ should handle "on" preposition for surfaces (1 ms)
      ✓ should handle "under" preposition (1 ms)
      ✓ should handle "behind" preposition
    occupancy management
      ✓ should track single occupant
      ✓ should track multiple occupants
      ✓ should handle unlimited occupancy
      ✓ should track occupancy state
    visibility and perception
      ✓ should handle visible occupants (1 ms)
      ✓ should handle hidden occupants
      ✓ should handle one-way visibility
      ✓ should handle soundproofing
    posture requirements
      ✓ should handle standing entries
      ✓ should handle sitting entries
      ✓ should handle lying entries (1 ms)
      ✓ should handle no posture requirement
    custom messages
      ✓ should handle enter message
      ✓ should handle exit message
      ✓ should handle full message
      ✓ should handle blocked message
    mobile entries
      ✓ should handle stationary entries (1 ms)
      ✓ should handle mobile entries
      ✓ should handle rideable animals
    access control
      ✓ should handle open access
      ✓ should handle blocked access
      ✓ should handle conditional access (1 ms)
    entity integration
      ✓ should attach to entity correctly (1 ms)
      ✓ should replace existing entry trait
    complex entry scenarios
      ✓ should handle nested entries
      ✓ should handle multi-purpose entries (1 ms)
      ✓ should handle vehicle with compartments
      ✓ should handle theatrical entries

Test Suites: 5 passed, 5 total
Tests:       135 passed, 135 total
Snapshots:   0 total
Time:        90.018 s, estimated 93 s
Ran all test suites matching

The next set of tests are /world-model/world and there are issues, so the next posting will continue the progress reports.

Test Suites: 3 failed, 3 total
Tests:       5 failed, 27 passed, 32 total
Snapshots:   0 total
Time:        119.66 s
Ran all test suites matching

Interactive Fiction – The Digital Antiquarian

EverQuest

This article tells part of the story of MMORPGs. It isn’t always or even usually the pioneers who reap the rewards of the trails they blaze. As often as not, some pragmatic Johnny-come-lately pops in to make off with the booty. Such was the case in the MMORPG space in the late 1990s. There Ultima […]


This article tells part of the story of MMORPGs.

It isn’t always or even usually the pioneers who reap the rewards of the trails they blaze. As often as not, some pragmatic Johnny-come-lately pops in to make off with the booty.

Such was the case in the MMORPG space in the late 1990s. There Ultima Online demonstrated that there was an audience for a persistent fantasy world where people could live out alternative existences together through the magic of the Internet. Yet it was another game called EverQuest that turned the proof of concept into a thriving business that enthralled hundreds of thousands of players for years on end, generating enormous amounts of money in the process. For, while the first-mover advantage should not be underestimated, there’s something to be said for being the second mover as well. EverQuest got to watch from backstage as Ultima Online flubbed line after line and stumbled over assorted pieces of scenery. Then, with a list in hand of what not to do, it was able to stride confidently onto center stage to a standing ovation. No one ever said that show business is fair.



EverQuest came to evince a markedly different personality than Ultima Online, but its origin story bears some uncanny similarities to that of the older rival it demolished. Like Ultima OnlineEverQuest was born as a sort of skunk-works project within a larger company whose upper management really wasn’t all that interested in it. Like Ultima OnlineEverQuest enjoyed the support of just one executive within said company, who set it in motion and then protected and nourished it like the proverbial mother hen. And like the executive behind Ultima Online, the one behind EverQuest plucked a pair of designers out of utter obscurity to help him hatch the egg.

Perhaps the most surprising aspect of the EverQuest origin story is the name of the company where it all went down: Sony Interactive Studios America. Suffice to say that, if you were to guess circa 1996 which publisher and studio would launch a market-transforming MMORPG later in the decade, Sony would not be high in your rankings. The Japanese mega-corp was flying high at the time, with a prominent footprint in most sectors of home electronics and mainstream entertainment, but it had hardly any presence at all on personal computers. The Sony PlayStation, launched in September of 1995 in North America and Europe, was on its way to becoming the most successful single games console of the twentieth century, a true mass-market cultural sensation that broadened the demographic for videogames and forever changed the way that the public perceived them. With a mainstream pile driver like that to hand, why should Sony want to waste its time with a wonky virtual world for nerds cosplaying as dwarves and mages?

It wound up doing so thanks to one man. At the beginning of 1996, John Smedley had been working for a few years as a producer at Sony Interactive, which focused almost exclusively on sports games for the PlayStation. Just 28 years old, Smedley already had a corner office with a view and a salary to match, as he and his colleagues rode the wave of the console’s incredible early success.

There was just one problem: Smedley didn’t particularly like sports, whether they happened to be played on the field or on the television screen. He had grown up as one of the kids that the jocks made fun of, the kind who walked to school every day with a Dungeons & Dragons rule book or two under his arm. It was only thanks to opportunism and happenstance that he had wound up helming projects aimed at gamers who worshiped John Madden rather than Gary Gygax. Now, he thought that the burgeoning Internet would soon make it possible to realize an old dream of 1980s nerds like him: that of playing Dungeons & Dragons online, whenever it suited you, instead of only when you could arrange to meet in person with five or so like-minded friends — assuming you even had such friends. He had a rough blueprint for how it might work, in the form of Neverwinter Nights, a game on America Online that let you effectively play one of the old single-player SSI Gold Box CRPGS over the Internet, taking a persistent character through a series of adventures with friends and strangers. It was limited in a thousand ways, but it was, so Smedley believed, the harbinger of a whole new category of game. And, after working for so long on games he really didn’t care about, he wanted to make one that he could feel passionate about.

Smedley took his idea to his boss Kelly Flock, the newly arrived head of Sony Interactive. It was a crazy thing to propose on the face of it, having absolutely nothing to do with anything the studio had ever done before nor any of the strategic priorities of the mother corporation; the PlayStation didn’t have any online capabilities whatsoever, meaning this game would have to run on personal computers. But Sony was flush with PlayStation cash and bravado, and Flock was apparently in a generous mood. He told Smedley that he could take $800,000 and hire a team to investigate the feasibility of his idea, as long as he continued to devote the majority of his time to his primary job of churning out crowd-pleasing sports games.

Those of you familiar with the tale of Ultima Online will recognize Sony Interactive standing in for Origin Systems, and John Smedley taking the role of Richard Garriott. EverQuest’s equivalent of Raph and Kristen Koster, who swept into Origin from the obscure world of textual MUDs to create Ultima Online in their image, was a pair of friends named Brad McQuaid and Steve Clover. They were programming automation and bookkeeping systems for a San Diego plant nursery during the early 1990s, working on a single-player CRPG of their own during their off hours. They called it WarWizard. Unfortunately, it was for the Commodore Amiga, a dying platform in North America. Unable to interest a publisher in a game in an unfashionable genre for a computer that was fast disappearing, they released WarWizard under the shareware model in 1993; the following year, they made an MS-DOS port available as well. By McQuaid and Clover’s own later reports, it garnered about 1500 registrations — not bad for a shareware game, but definitely not enough to let the friends quit their day job.[1]There may be grounds to question this figure. For a game with 1500 registrations — far more than the vast majority of shareware games — WarWizard had a weirdly low online profile; there is virtually no contemporary trace of it to be found. Most of the limited interest it did generate appears to be retroactive, coming after McQuaid and Clover became known as the minds behind EverQuest. An actual registered copy that lets one complete the game didn’t turn up in public until 2009.

Undaunted, they pushed ahead with a WarWizard 2. Desperate for feedback, they uploaded a preview of the sequel to the Internet. On a lark, McQuaid appended a note: “We are releasing this demo as a business card of sorts, in order to introduce games publishers, developers, and investors to our company, MicroGenesis. If you have any question whatsoever, please contact Brad McQuaid.” This hopeful — not to say naïve — shot in the dark would change both of their lives.

For one day not long after his meeting with his boss, John Smedley stumbled across the demo, thought it was pretty impressive for the work of two guys with a day job, noticed that the two guys in question were living in Sony Interactive’s hometown of San Diego, and decided to take them up on their offer and contact them. Thus Brad McQuaid picked up his phone one rainy evening to hear a Sony producer on the other end of the line, asking him and his partner to come visit him in his slick glass-walled office downtown. It seemed too incredible to be true — but it was.

So, McQuaid and Clover, feeling uncomfortable and thoroughly out of place, were ushered by a secretary past the PlayStations in the anterooms and the NFL and MLB posters lining the walls at Sony Interactive, to see the star producer in his native habitat. What did these people want with the likes of them, two scruffy misfits hustling to make a buck peddling turn-based monster-fighting games on the shareware market? Then, as soon as the door shut behind the secretary, they felt suddenly at home. John Smedley was, they learned to their relief, one of them: a kid who had grown up playing Dungeons & Dragons in his school’s cafeteria and Ultima on his Apple II. It turned out that Smedley didn’t want them to finish WarWizard 2 for Sony Interactive; he wanted them to make something even more exciting. He explained his vision of a CRPG that you could play online, and asked them whether they’d like to help him make it. They said that they would. Smedley now learned that McQuaid and Clover were, like the Kosters over at Origin, passionate MUDders as well as semi-professional single-player CRPG developers. They knew exactly what kind of experience Smedley was envisioning, and were overflowing with ideas about how to bring it to fruition. Smedley knew right then that he’d hit pay dirt.

McQuaid and Clover were hired by Sony Interactive in March of 1996. They then proceeded to spend about six months in a windowless office far less plush than that of John Smedley, creating a design document for the game that they were already calling EverQuest; the name had felt so right as soon as it was proposed by Clover that another one was never seriously discussed. Smedley insisted that the document describe the game down to the very last detail. Here we see a marked contrast to the development process that led to Ultima Online, which came into its own gradually and iteratively, through a long string of playable design prototypes. Smedley’s background as a producer of games that simply had to ship by a certain date — the National Football League was not likely to delay its season opener in order to give that year’s NFL videogame an extra week or two in the oven — had taught him that the best way to make software efficiently was to know exactly what you were intending to make before you wrote the first line of code.

At this point, then, we’re already beginning to see some of the differences in personality between Ultima Online and EverQuest emerge. The Kosters were idealists and theorists at heart, who treated Ultima Online almost as a sociological experiment, an attempt to create a virtual space that would in turn give birth to a genuine digital society. Smedley, McQuaid, and Clover, on the other hand, had less highfalutin ambitions. EverQuest was to be a place to hang out with friends and a fun game to play with them, full stop. The more grandiose of the dreams nursed by the Kosters — dreams of elections and governments, of a real economy driven by real people playing as shopkeepers, tailors, tour guides, and construction foremen, of a virtual world with a fully implemented natural ecology and a crafting system that would let players build anything and everything for themselves — were nowhere to be found in the final 80-page design document that McQuaid and Clover presented and Smedley approved in September of 1996. They all agreed that a blatantly artificial, gamified virtual world wasn’t a problem, so long as it was fun. In these priorities lay most of what would make their game such a success, as well as most of what idealists like the Kosters would find disappointing about it and the later MMORPGs that would mimic its approaches.

In both the broad strokes and many of the details, the thinking of McQuaid and Clover was heavily influenced by an open-source MUD toolkit called DikuMUD that had been released by a group of students at the University of Copenhagen in 1991. Its relationship to other MUDs foreshadowed the relationship of the eventual EverQuest to Ultima Online: DikuMUD was all about keeping the proceedings streamlined and fun. As the game-design theorist Flatfingers has written on his blog, “it emphasized easy-to-understand and action-oriented combat over other forms of interaction [and] simplified interactions down to easily trackable, table-driven statistics.” The simplicity and accessibility of the DikuMUD engine from the player’s perspective, combined with the equal ease of setting a new instance of it up on the server side, had made it the dominant force in textual MUDs by the mid-1990s, much to the displeasure of people like the Kosters, who preferred more simulationally intense virtual worlds. This design dialog was now about to be repeated in the graphical context.

Then, too, there is one other important influence on EverQuest that we can’t afford to neglect. While McQuaid and Clover were still working on their design document, they saw 3DO’s early, halfheartedly supported graphical MMORPG Meridian 59 go through beta testing. It convinced them that first-person 3D graphics were the way to go — another point of departure with Ultima Online, which clung to an old-school overhead third-person view, just like the single-player Ultima CRPGs before it. In the age of DOOM and Quake, McQuaid and Clover judged, nothing less than immersive 3D would do for their game. And so another keystone and differentiator fell into place.

With the design document completed, Smedley found a larger room to house the project in Sony Interactive’s building and slowly put a team into place around his two wunderkinds. Some of the programmers and artists who joined them were hired from outside, while others were moved over from other parts of the company as their current projects were completed. (It turned out that Smedley hadn’t been the only closeted nerd at Sony Interactive condemned to make sports games…) As the more outgoing and assertive of Smedley’s original pair of recruits, Brad McQuaid took the role of producer and day-to-day project lead, while Steve Clover became the lead programmer as well as designer. Perhaps the most important of the newcomers was Rosie Cosgrove (now Rosie Strzalkowski), the lead artist. She shaped the game’s visual aesthetic, a blending of the epic and the whimsical, full of bright primary colors and pastels that popped off the screen. Recognizing that photo-realism wasn’t going to be possible with the current state of 3D-graphics technology, she embraced the jankiness. The graphics would become just one more sign that EverQuest, in contrast to that other big MMORPG, was all about straightforward, even slightly silly fun, with no degree or interest in sociology required.

While the team was coalescing, they had the priceless opportunity to observe the successes and tribulations of their rival virtual world from Origin Systems, which, true to the iterative approach to game development, was conducting a series of small-scale public testing rounds. A watershed was reached in June of 1997, when Ultima Online conducted a two-month beta test, its biggest one ever and the last one before the game’s official release. Needless to say, everyone on the EverQuest team watched the proceedings closely. What caught all of the interested observers by surprise — not least the idealists at Origin Systems — was the quantity of players who found their fun neither as noble adventurers nor as shopkeepers, tailors, tour guides, politicians, or construction foremen, but rather as mass murderers, killing their fellow players the second they let their guard down. It ought to have been a five-alarm wake-up call for Origin, being the first indubitable harbinger of a persistent problem that would pave the way for EverQuest to replace its older, better credentialed rival as the MMORPG du jour. But they refused to countenance the obvious solution of just making it programmatically impossible for one player to kill another.

After Ultima Online launched for real in September of 1997, the developers behind it continued to struggle to find a way of addressing the problem of player murder without compromising their most cherished ideals of a fundamentally player-driven online society. They encouraged their citizens to form police forces, and implemented small changes to try to help the law-and-order contingent out, such as printing the names of those player characters who had killed at least five other player characters in scarlet letters. None of it worked; instead of a badge of shame, the scarlet letters became a badge of honor for the “griefers” who lived to cause chaos and distress. In his own words, Raph Koster put his players “through a slow-drip torture of slowly tightening behavior rules, trying to save the emergence while tamping down the bad behavior. The cost was the loss of hundreds of thousands of players.” After a wildly vacillating start, Ultima Online stabilized by mid-1998 at about 90,000 active subscribers. That wasn’t nothing by any means — on the contrary, it represented about $1 million worth of revenue for Origin every single month — but it nevertheless left a huge opening for another game that would be more pragmatic, less ideological, and by extension less murderous, that would be more focused on simple fun.

Steve Clover signed up for Ultima Online and logged on as soon as he could do so. His first hour in the world was much the same as that of countless thousands of players to come, many of whom would never log in again.

I created my own sword. I crafted my own armor and all that. I put all this stuff on, I head out to do some adventuring, and all of a sudden the screen starts slowing down. I’m like, oh, this is weird. What’s going on? And about a hundred guys run on screen and [beat] me to death, right?

I said, that will not happen in our game. That absolutely will not happen.

So, in the emerging parlance of the MMORPG, EverQuest would be strictly a “PvE,” or “player versus environment,” game, rather than a “PvP” game.[2]After its launch, EverQuest did experiment with a few servers that allowed unrestrained PvP combat, but there proved to be little appetite for it among the player base. The most important single key to its extraordinary success was arguably this one decision to make it literally impossible to attack your fellow players. For it would give EverQuest’s world of Norrath the reputation of a friendly, welcoming place in comparison to the perpetual blood sport that was life in Ultima Online’s Britannia. Perhaps there is some political philosophy to be found in EverQuest after all: that removing the temptation to commit crime serves to make everyone a little bit nicer to each other.

In the meantime, while Ultima Online was capturing headlines, the nascent EverQuest kept a low profile. It was seldom seen in the glossy gaming magazines during 1997 and 1998; the journal-of-record Computer Gaming World published only one half-page preview in all that time. Instead EverQuest relied on a grass-roots, guerrilla-marketing effort, led by none other than Brad McQuaid. He was all over the newsgroups, websites, and chat channels populated by hardcore MUDders and disgruntled refugees from murderous Britannia. One of his colleagues estimated that he spent half his average working day evangelizing, querying, and debating on the Internet. (Because McQuaid’s working days, like those of everyone else on the team, tended to be inordinately long, this was less of a problem than it might otherwise have been.) His efforts gradually paid off. EverQuest was voted Best Online Only Game by critics who attended the annual E3 show in May of 1998, despite having had only a backroom, invitation-only presence there. The people making it believed more than ever now that there was a pent-up hunger out there for a more accessible, fun-focused alternative to Ultima Online. They believed it still more when they moved into the public beta-testing stage, and were swamped by applicants wanting to join up. The last stage of testing involved fully 25,000 players, more than had participated in Ultima Online’s final beta.

In the midst of the run-up to launch day, John Smedley was plunged into a last-minute scramble to find a new home for his brainchild. Sony Interactive had by now been rebranded 989 Studios, a punchier name reflecting its ongoing focus on sports games. Meanwhile the Sony mother ship had begun questioning the presence of this online-only computer game at a studio whose identity was single-player PlayStation games. EverQuest would not be just another ship-it-and-move-on sports title; it would require a whole infrastructure of servers and the data pipelines to feed them, along with a substantial support staff to maintain it all and generate a never-ending stream of new content for the players. Considered in this context, the name of EverQuest seemed all too apropos. What did 989 Studios know about running a forever game? And was it really worth the effort to learn when there was so much money to be made in those bread-and-butter sports games? One day, Kelly Flock called John Smedley into his office to tell him that he couldn’t continue to feed and nurture his baby. If he wanted to keep EverQuest alive, he would have to find another caregiver.

Luckily, there was another division at Sony known as Sony Online Entertainment that was trying to make a go of it as an Internet gaming portal. Through a series of corporate contortions that we need not delve into too deeply here, Smedley’s skunk works was spun off into a nominally independent company known as Verant Interactive, with Sony Online as its chief investor.

All of this was happening during the fevered final months of testing. And yet, remarkably, the folks on the front lines were scarcely aware of the crisis at all; knowing that they had more than enough to worry about already, Smedley chivalrously shielded them from the stress that was keeping him awake at night. “I don’t remember a, ‘Hey, guys, we’re getting cancelled,'” says EverQuest “World Builder” — that was his official title — Geoffrey Zatkin. “What I remember is, ‘Hey, guys, we’re spinning out to our own studio. You’re no longer going to be Sony employees. You’re going to be employees of Verant Interactive.'” The best news of all was that Smedley was finally able to give up his hated sports games and join them full-time as the head of Verant.

EverQuest went live on March 16, 1999, a day that ought to go down in history as marking the end of the early, experimental phase of graphical MMORPGs and marking their arrival as a serious commercial force in gaming. To be sure, that original EverQuest client doesn’t look much like we expect a piece of polished commercial entertainment software to look today; the 3D view, which fills barely half the screen as a sneaky way of keeping frame rates up, is surrounded by garish-looking buttons, icons, and status bars that seemed to have been plopped down more or less at random, with a scrolling MUD-like text window that’s almost as large as the world view taking pride of place in the middle of it all. But at the time, it was all very cutting edge, making the MMORPGs that had come before it look positively antiquated in comparison. A late decision to require a 3D-accelerator card to even start the client had caused much debate at Verant. Would they be giving up too many potential subscribers thereby?

They needn’t have worried. A healthy 10,000 people signed up on the first day, and that pace was maintained for days afterward.

Like the worlds of Ultima Online and all of the early MMORPGs, EverQuest’s world of Norrath was actually many separate instances of same, each running on its own server that was capable of hosting no more than a few thousand players at one time. Verant had thought they were prepared for an onslaught of subscribers — the best of all possible problems for a new MMORPG to have — by having plenty of servers set up and ready to go. But they had failed to follow the lead of Ultima Online in one other important respect: whereas Origin Systems scattered their servers around the country, Verant ran all of theirs out of a single building in San Diego. As urban legend would have it, EverQuest consumed so much bandwidth after its launch that it disrupted Internet connections throughout the city, until more cables could be laid. This is almost certainly an exaggeration, but it is true that the pipes going directly into Verant’s offices at least were woefully inadequate. Everyone scrambled to address the emergency. John Smedley remembers “personally logging into the Cisco routers” to try to tweak a few more bytes worth of throughput out of the things: “I could actually work with the Versatile Interface Processor cards almost as well as any of our network engineers at the time.” Again, though, too many customers is always a better problem to have than the alternative, and this one was gradually solved.

Computer Gaming World didn’t publish its EverQuest review until the July 1999 issue. This was a surprisingly late date, even given the standard two-month print-magazine lead time, and it pointed to the emerging reality of the glossy magazines becoming estranged from their traditional readership, who were now getting more and more of their news and reviews online, the same place where they were doing more and more of their actual gaming. Nevertheless, Thierry Nguyen’s belated review for the magazine was a fair and cogent one, especially in the inevitable comparison with Ultima Online — and in another, less inevitable comparison that makes more sense than you might initially think.

Ultima Online is a world simulation; EverQuest is a social hack-and-slash. Ultima Online has more freedom built into it, and you can actually make a living off of trade skills. EverQuest is more about sheer adventure and combat, and the trade skills are useful, but you can’t really be a tailor or a baker.

EverQuest is the Diablo of 1999. An odd comparison, you say? Well, here’s how they’re alike: they both offer a very simple premise (“go forth and thwack many creatures to gain levels and loot”), and despite this simple premise (or maybe because of it), they’re both damn addictive and fun.

Diablo in a vastly larger, truly persistent world really isn’t a terrible way to think about EverQuest. While the folks at Origin Systems expected their players to make their own fun, to see what lay behind yonder hill for the sake of the journey, Verant gave theirs a matrix of pre-crafted quests and goals to pursue. While Ultima Online’s world of Britannia belonged to its inhabitants, EverQuest’s world of Norrath belonged to Verant; you just got to play in it. Happily for everybody, doing so could be a lot of fun. Sometimes the most delicious sort of freedom is freedom from responsibility.

By October of 1999, EverQuest had more than 150,000 subscribers, leaving Ultima Online in its dust. Raph Koster believes, probably correctly, that this trouncing of his own virtual world was driven as much by the “safety” of having no players killing other players as it was by EverQuest’s trendy 3D graphics. Ultima Online would finally relent and open safe servers of its own in 2000, but that was bolting the gate after the mounted murderers had already galloped through.

That same October of 1999, Microsoft launched Asheron’s Call, another 3D MMORPG that prevented its players from killing other players. Yet even with all of the ruthless marketing muscle and the massive server infrastructure of the biggest monopoly in technology behind it, it never came close to rivaling EverQuest in popularity. It would be a long time before any other virtual world would. By the end of 2000, EverQuest was closing in on 350,000 subscribers. The following year, it hit 400,000 subscribers. Its growth then slowed down considerably, but still it did not halt; EverQuest would peak at 550,000 subscribers in 2005.

In May of 2000, Verant Interactive’s brief-lived period of nominal independence came to an end, when the spinoff was absorbed back into Sony. Soon after, the old Sony Online Entertainment subsidiary was shut down, having failed to set the world on fire with its own simple online games based on television game shows like Wheel of Fortune and Jeopardy!, and Verant appropriated its name.

In addition to charging its subscribers a recurring fee of $10 per month, this new edition of Sony Online discovered a valuable secondary revenue stream in boxed expansion packs for EverQuest. No fewer than ten of these were released between 2000 and 2005, introducing new regions of Norrath to explore, new monsters to fight, new races and classes to fight them as, new spells to cast, and new magic items to collect, whilst also refining the graphics and interface on the client side to keep pace with competing MMORPGs. Some argued that a paying customer was reasonably entitled to expect at least some of this additional content and refinement to be delivered as part of the base subscription package. And indeed, those looking for a measure of poetic justice here were perchance not entirely deprived. There is reason to suspect that all these expansions began in time to act as a drag on the game’s growth: the need to shell out hundreds of dollars and carry home a veritable pile of boxes in order to become a fully vested citizen of Norrath was likely one of the reasons that EverQuest’s growth curve leveled off when it did. Sony Online could still profitably sell expansions to the faithful, but those same expansions made the barrier to entry higher and higher for newcomers.

Still, the fact remains that EverQuest was for six years the most popular MMORPG of them all, in defiance of a gamer culture whose appetite for novelty was notorious. There was no shortage of would-be challengers in its space; by a couple of years into the new millennium, scarcely a month went by without some new MMORPG throwing its hat into the ring. And small wonder: to publishers, the idea of a game that you could keep charging people for was tempting to say the least. Some of the newcomers survived, some even thrived for a while with subscriber counts as high as 250,000, but none came close to matching EverQuest in magnitude or longevity. A virtual world like Norrath had a peculiar stickiness about it that wasn’t a factor with other types of games. To leave EverQuest and go play somewhere else meant to leave behind a character you might have spent years building up, and, even more poignantly, to leave behind an entire circle of online friends that you had assembled over the course of that time. This was a tough pill for most people to swallow, no matter how enticing Arthurian Britain, the galaxy far, far away of Star Wars, or a world out of Japanese anime might sound in comparison to the fairly generic, cookie-cutter fantasy world of Norrath.

The huge numbers of subscribers led to knock-on effects that EverQuest’s developers had never anticipated. Within months of the game’s launch, enterprising players began selling in-world loot on sites like eBay; soon the most successful of these virtual auctioneers were making thousands of dollars every month. “What’s crazy? Me playing for twelve hours a day or someone paying real money for an item that doesn’t exist?” asked one member of this new entrepreneurial class who was profiled in The Los Angeles Times. “Well, we’re both crazy. God bless America.”

A journalist named R.V. Kelly 2, who had never considered himself a gamer before, tried EverQuest just to see what all the fuss was about, and got so entranced that he wound up writing a book about these emerging new virtual worlds.

This isn’t a game at all, I realized. It’s a vast, separate universe. People explore here. They converse. They transact business, form bonds of friendship, swear vows of vengeance, escape from dire circumstances, joke, fight to overcome adversity, and learn here. And it’s better than the real world because there are no physical consequences for making mistakes. You can derive the same sense of satisfaction for doing things well that you find in the real world, but you don’t suffer any pain or anguish when you fail. So, the game contains most of the good found in real life, but none of the bad.

Yet there were also dangers bound up with the allure of a virtual world where failure had no consequences — especially for those whose real lives were less than ideal. On Thanksgiving Day, 2001, a young Wisconsinite named Shawn Woolley was discovered by his mother sitting in front of his computer dead, the rifle he had used to shoot himself lying nearby. The monitor still displayed the EverQuest login screen. He had been playing the game rabidly for months, to the exclusion of everything else. He’d had no job, no studies, no friends in the real world. He’d effectively uploaded his entire existence to the world of Norrath. And this had been the result. Had his lonely isolation from the world around him come first, or had EverQuest caused him to isolate himself? Perhaps some of both. One can’t help but think of the classic addict’s answer when asked why he doesn’t give up the habit that is making his life miserable: “Because then I’d have no life at all.” It seemed that this was literally true — or became true — in the case of Shawn Woolley.

This tragedy cast numbers that Sony Online might once have been proud to trumpet in rather a different light. Not long before Woolley’s death, one Edward Castronova, an associate professor of economics at California State University, Fullerton, had conducted a detailed survey of the usage habits of EverQuest subscribers. He found that the average player spent four and a half hours in the game every day, and that 31 percent played more than 40 hours every week — i.e., more than a typical full-time job. Surely that couldn’t be healthy.

Widespread coverage of the the death of Shawn Woolley ignited a mainstream conversation about the potentially detrimental effects of online videogames in general and EverQuest in particular. A father was reported to have smothered his infant son without realizing it, so distracted was he by the world of Norrath on his computer screen. A couple was reported to have left their three-year-old behind in a hot car to die, so eager were they to get into the house and log into EverQuest. Parents said that their EverQuest-addled children behaved “as if they had demons living inside them.” Wives told of life as EverQuest widows: “I do not trust him [to be alone] with our daughter, simply because when I am here she will be crying and he will not do anything about it.”

The stories were lurid and doubtless quite often exaggerated, but the concern was valid. Unlike the debates of the 1980s and 1990s, which had principally revolved around the effects of videogame violence on the adolescent psyche and had relied largely on flawed or biased studies and anecdotal data, this one had some real substance to it. One didn’t need to be a Luddite to believe that playing a single videogame as much as — or to the exclusion of — a full-time job couldn’t possibly be good for anyone. Elizabeth Woolley, the mother of Shawn Woolley, became the face of the Everquest opposition movement. She was certainly no Luddite. On the contrary, she was a computer professional who had laughed at the hearings on videogame violence conducted by Joe Lieberman in the United States Senate and likewise dismissed the anti-game hysteria surrounding the recent Columbine school shootings that had been carried out by a pair of troubled DOOM-loving teenagers. All that notwithstanding, she saw, or believed she saw, a sinister intentionality behind this addictive game that its own most loyal players called EverSmack or EverCrack: “I know the analysis that goes into a game before they even start writing the code; everything is very intentional. And people would go, ‘Ah, that’s so funny, how addicting.’ And I’m like, no, it’s not funny at all.”

She wasn’t alone in vaguely accusing Sony Online of being less than morally unimpeachable. According to one reading, popular among old-school MUDders, the EverQuest team had co-opted many of the ideas behind MUDs whilst tossing aside the most important one of all, that of a truly empowered community of players, in favor of top-down corporate control and deliberate psychological manipulation as a means to their end of ever-increasing profits. One of the earliest academic treatments of EverQuest, by Timothy Rowlands, posits (in typically tangled academic diction) that

from the outset, EverQuest’s designers, motivated by profit, were interested in trying to harness (read co-opt, commoditize) the sociality that had made the virtual worlds of MUDs so successful. Resisting the linearity of older single-player games in which the players move their avatars through a series of predetermined levels, MMOs present a space in which the hero narrative, predicated upon the potential for climax — though present in the form of quests and the accumulation of avatar capital — is ultimately unrealizable. Because the aim is to keep subscribers playing indefinitely, even the arbitrary end points (level caps) are without closure. In Campbellian language, there can be no epiphany, no moment of apotheoses as the hero overcomes his trials…

For me, the existential hamster wheel described by Rowlands — himself a recovering EverQuest addict — smacks a bit too much of the life I lead offline, the one that comes down to, to paraphrase Roy Rogers, just one damn thing after another. Combine this with my awareness of the limitations of online socializing, and we can perhaps begin to see why I’ve never been much interested in MMORPGs as a gamer. Literary type that I am, if offered a choice between a second life on the computer and an interactive story of the kind that I can actually finish, I’ll take the story — the one with the beginning, middle, and end — every single time. I can’t help but think that I may have been lucky to be born with such a predilection.

Lest we be tempted to take all of this too far, it should be noted that EverQuest in its heyday was, however psychologically perilous it might or might not have been, a potential problem for only a vanishingly small number of people in relation to the population as a whole: by the metrics of television, movies, or even others forms of gaming, 550,000 subscribers was nothing. Nevertheless, the debates which EverQuest ignited foreshadowed other, far more broad-based ones to come in the fast-approaching epoch of social media: debates about screen time, about the grinding stress of trying to keep up with the online Joneses, about why so many people have come to see digital spaces as more attractive than real ones full of trees and skies and flowers, about whether digital relationships can or should ever replace in-person smiles, tears, and hugs. Meanwhile the accusations of sinister intent which Elizabeth Woolley and Timothy Rowlands leveled against EverQuest’s designers and administrators were, even if misplaced in this case, harbingers of games of the future that would indeed be consciously engineered not to maximize fun but to maximize engagement — a euphemism for keeping their players glued to the screen at all costs, whether they wanted to be there in their heart of hearts or not, whether it was good for them or not.

Gijsbert van der Wal’s famous 2014 photograph of Dutch teenagers ignoring a Rembrandt masterpiece in favor of staring at their phones has become for many psychologists, social theorists, and concerned ordinary folks a portrait of our current Age of Digital Addiction in a nutshell.

By the time those subjects really came to the fore, however, EverQuest would no longer be the dominant product in the MMORPG market. For in 2004, another game appeared on the scene, to do to EverQuest what the latter had done to Ultima Online half a decade earlier. Against the juggernaut known as World of Warcraft, even EverQuest would battle in vain.



Did you enjoy this article? If so, please think about pitching in to help me make many more like it. You can pledge any amount you like.


Sources: The books EverQuest by Matthew S. Smith, Video Game Worlds: Working at Play in the Culture of EverQuest by Timothy Rowlands, Synthetic Worlds: The Business and Culture of Online Games by Edward Castronova, Gamers at Work: Stories Behind the Games People Play by Morgan Ramsay, Legend of the Syndicate: A History of Online Gaming’s Premier Guild by Sean Stalzer, Postmortems: Selected Essays Volume One by Raph Koster, Massively Multiplayer Online Role-Playing Games: The People, the Addiction, and the Playing Experience by R.V. Kelly 2, and The Age of Addiction: How Bad Habits Became Big Business by David T. Courtwright. Computer Gaming World of December 1997, July 1999, and June 2000; Retro Gamer 263.

Online sources include “Better Together: Stories of EverQuest by David L. Craddock at ShackNews“The Game Archaelogist: How DikuMUD Shaped Modern MMOs” by Justin Olivetti at Massively Overpowered, and “Storybricks + DikuMUD = Balance in MMORPGs” at Flatfingers’s theory blog. The truly dedicated may want to listen to aLovingRobot’s 50-plus hours (!) of video interviews with former EverQuest developers. And, although it’s quite possibly the most insufferable thing I’ve ever watched, the documentary EverCracked has some interesting content amidst the constant jump cuts and forced attempts at humor.

Where to Play It: EverQuest is not what it once was in terms of subscriber numbers, but it’s still online under the stewardship of Darkpaw Games, a sort of retirement home for aged MMORPGs.

Footnotes

Footnotes
1 There may be grounds to question this figure. For a game with 1500 registrations — far more than the vast majority of shareware games — WarWizard had a weirdly low online profile; there is virtually no contemporary trace of it to be found. Most of the limited interest it did generate appears to be retroactive, coming after McQuaid and Clover became known as the minds behind EverQuest. An actual registered copy that lets one complete the game didn’t turn up in public until 2009.
2 After its launch, EverQuest did experiment with a few servers that allowed unrestrained PvP combat, but there proved to be little appetite for it among the player base.

Thursday, 03. July 2025

IFComp News

IFComp 2025 Now Accepting Intents & Entries

Hello, everyone, and happy (slightly belated) start of July! With a new July comes a new season of the Interactive Fiction Competition!From now through August 1st 2025, 11:59pm Eastern (23:59 ET), the IF Comp website will be open for authors to declare their intent to enter this year’s competition!Final entries are due on August 28th 2025, 11:59pm Eastern, but you must register by August 1st! If yo

Hello, everyone, and happy (slightly belated) start of July! With a new July comes a new season of the Interactive Fiction Competition!

From now through August 1st 2025, 11:59pm Eastern (23:59 ET), the IF Comp website will be open for authors to declare their intent to enter this year’s competition!

Final entries are due on August 28th 2025, 11:59pm Eastern, but you must register by August 1st!

If you miss it, there’s always 2026… If you register and then can’t complete your game in time, you may always back out of the competition and enter the game elsewhere or next year.

As with the previous iterations of the IFComp, authors will be allowed to participate as judges, vote, and review entries other than their own.

If you have any questions about the competition or its rules, you can contact us at [email protected]

In addition to entries, we are also accepting prizes to award contestants! If you would like to donate a prize for this year’s competition, you can email us at [email protected] — no prize is too humble or too grand.

If you would prefer to donate money, our Colossal Fundraiser will launch by August. Another announcement will be made then.

Thank you, everyone. We’re looking forward to another fun year.


Choice of Games LLC

World War II Armored Recon—Africa, 1942. Command a tank. Fight the Axis.

Hosted Games has a new game for you to play! Take the commander’s seat of an American tank. Plunge into the deserts of North Africa. Every decision counts as you battle Nazis, master logistics, and strive to keep your crew—and yourself—alive. World War II Armored Recon is 33% off until July 10th! Allen developed this game using ChoiceScript, a simple programming language for writing multiple-c
World War II Armored Recon

Hosted Games has a new game for you to play!

Take the commander’s seat of an American tank. Plunge into the deserts of North Africa. Every decision counts as you battle Nazis, master logistics, and strive to keep your crew—and yourself—alive.

World War II Armored Recon is 33% off until July 10th!

Every gallon of gas matters. Every round might be irreplaceable as your travels take you far from friendly lines and into a storm of secrets and maneuvers unknown to all but a handful of combatants.

World War II Armored Recon is an interactive novel of approximately 900,000 words by Allen Gies, the lead writer for Burden of Command.  It’s entirely text-based, without graphics or sound effects, and fueled by the vast, unstoppable power of your imagination.

  • Play as male, female, or nonbinary, but don’t expect romance in the Army.
  • Experience exotic North Africa as a wide-eyed American soldier.
  • Fight in historical battles with all the chaos and improbability therein.
  • Shoot Nazis.
  • The Stuart tank you’ll command can be upgraded in numerous ways.
  • Three crewmen to bond with: Gunner, Driver, and Mechanic.
  • Personal stats, tank stats, activity stats, and relationship statuses.

Allen developed this game using ChoiceScript, a simple programming language for writing multiple-choice interactive novels like these. Writing games with ChoiceScript is easy and fun, even for authors with no programming experience. Write your own game and Hosted Games will publish it for you, giving you a share of the revenue your game produces.


To Ashes You Shall Return—You died! You’re back. For how long?

Hosted Games has a new game for you to play! You died young. Sorry about that. But now you’re back! Your wife’s magic yanks you from death’s clutches, but power always comes with a cost. Decide if you can love the heart that doomed you before it’s too late. To Ashes You Shall Return is free to win, and paying to turn off ads is 33% off until July 10th!  Kaitlyn developed this game using ChoiceScrip
To Ashes You Shall Return

Hosted Games has a new game for you to play!

You died young. Sorry about that. But now you’re back! Your wife’s magic yanks you from death’s clutches, but power always comes with a cost. Decide if you can love the heart that doomed you before it’s too late.

To Ashes You Shall Return is free to win, and paying to turn off ads is 33% off until July 10th! 

To Ashes You Shall Return is a 31,000-word interactive novel of sapphic love and loss by Kaitlyn Grube. It’s entirely text-based, without graphics or sound effects, and fueled by the vast, unstoppable power of your imagination.

Explore the wonders of:

  • Queer romance
  • Tragedy
  • Witchcraft
  • A kitty named Tabitha
  • An unstoppable tide of existential dread

The dirt claims us all in the end. How will you live in the meantime?

Kaitlyn developed this game using ChoiceScript, a simple programming language for writing multiple-choice interactive novels like these. Writing games with ChoiceScript is easy and fun, even for authors with no programming experience. Write your own game and Hosted Games will publish it for you, giving you a share of the revenue your game produces.


Zarf Updates

Hybrid is hard

We have wrapped NarraScope 2025! It went great. Everybody loved it, on-site attendees and remote folks. Hybrid conferences are hard, my friend. This is our third hybrid conference -- but really this is the first time we got a grip on the problem. ...

We have wrapped NarraScope 2025! It went great. Everybody loved it, on-site attendees and remote folks.

Hybrid conferences are hard, my friend.

This is our third hybrid conference -- but really this is the first time we got a grip on the problem. I think we got it right this time... at a cost. We spent way more effort on tech/AV than in any previous year.

(I say "I think we got it right" because we have not yet posted the videos to Youtube. That's the final step, and it's where we stumbled hard in 2024. More on this below. But we should be on track this year.)

I get no credit for any of this, to be clear. I was not on tech. Our success was due to (a) the planning and foresight of Logan Clare, who made the long voyage down from NYU to be our on-site tech lead; and (b) a cadre of volunteers who ran themselves ragged setting up, testing, and debugging every single talk session.

So I don't have every detail. But I want to write up The Way Things Went, for the benefit of future hybrid conferences everywhere.


The first year (2019)

Let me start with the backstory. In 2019 we decided to run a conference! Well, no, we decided that in 2017. The conference happened in 2019, in Boston. We had no concept of "hybrid" at all; it was an in-person event.

The AV setup was, by and large, trivial. Every MIT classroom had a projector, a screen, and a podium with a microphone and an HDMI cable. You plug the cable into your laptop and talk into the microphone. Your slides appear on the screen and your voice goes over the PA. This is a solved problem.

I know, there's no perfectly solved problem. Some people had laptops with weird video connectors. (I brought a bag of HDMI dongles.) I think some people had trouble getting audio from their laptop into the room -- HDMI is supposed to support that but the dongles don't always.

We have video of some of the 2019 talks. This is thanks to our participants from Articy, who volunteered to bring a hand-held camera and film in one room. That wasn't live-streamed; they posted the videos after the event.

On-line time (2020-2022)

2020 was all-remote, obviously.

We came up with a pretty simple setup. A volunteer ran a machine with OBS, Discord, and Zoom installed. (I did this job for some of the conference.) The volunteer started a Zoom call with the speaker (or speakers). The Zoom window got fed into OBS (with a nice frame!) The OBS virtual camera streamed out to a Discord "stage" channel. Attendees watched that.

(I think I used RogueAmoeba's Loopback to get the Zoom audio into OBS. Macs require a bit of third-party support for that sort of thing.)

This required a fair amount of setup for the volunteers, but the conference speakers just needed to join a Zoom call. And then share their screen for slides. By mid-2020 that was familiar territory.

In 2022 we repeated that plan, except that we had two tracks instead of one, so it required two volunteers on duty. (Two computers, two Zoom calls, etc.) Also we streamed to a platform called Gather.town instead of Discord. Technically it was the same process, though.

Tackling hybridity (2023-2024)

2023 brought us to Pittsburgh. We wanted to keep the remote audience we'd built up, though. "We'll just go hybrid!" we said. "It'll be easy!"

It was kind of easy! But only because we did it badly.

We repeated what we did in 2019. (Pitt classrooms are set up about the same as MIT.) Then we got Pitt's AV crew to bring in three cameras and three laptops. Set up a camera in each room, pointing at the podium and the screen. The camera is plugged into the laptop; the laptop streams to Discord.

Note that the speaker's laptop (almost every speaker brought their own laptop) was completely separate from the room laptop. The speakers never touched Discord or the room camera. (Heck no.) They just plugged into HDMI and talked.

This meant that the streaming of the slides was pretty lossy. It was a videocamera pointed at a projector screen, after all. Washed-out, terrible quality. It worked, but the remote attendees got a second-class experience.

A few speakers presented remotely -- that is, the speaker did not travel to Pittsburgh. We asked them to submit their talks as pre-recorded video. For these, a volunteer (me) ran up to the front of the room and plunked down their own (my own) laptop, plugged in, and played the video file. Just like the other talks, the streaming consisted of pointing the room camera at the projector screen.

We had one surprise remote speaker. Surprise! Again, I used my own laptop, and set up a Zoom call. (Much like 2020.) Same streaming deal as before.

I wanted to improve that plan in 2024. Unfortunately, it didn't improve. In fact it got worse. The hope was to have both a camera feed and a direct feed from the speaker's laptop. A volunteer was supposed to switch back and forth in OBS. That didn't happen -- only the camera feed was ever used. I don't know whether the direct feed was infeasible or if the volunteers just weren't briefed.

Also the room mics were set up badly; some of the talks had bad audio and some were almost unusable. We still, as I write this, don't have most of the videos processed. I am deeply ashamed of this failure. We are working on it.

Lesson: if you decide to follow this plan, give the speakers hand mics or lapel mics. Room mics are just too risky. Also test that what's being streamed is also being recorded, exactly as-is.

The new plan (2025)

Welcome to Philadelphia. We once again have two laptops (per room): one for the speaker, one to record. We also provide a high-quality USB webcam for each. (Better than the laptop built-in webcams, which were pretty cruddy.)

Some of the rooms also have a built-in room camera, hand mics, and a computer to manage them. We make use of these where possible.

The speaker machine is logged into Discord -- using a conference account with stage streaming privileges. So the livestream is run directly from that machine. The speaker can share slides or smile into the webcam, whichever they like. That machine is also plugged into the room projector, so local attendees can see the slides up on the classroom screen.

The recording machine has OBS. Unlike last year, OBS isn't streaming out; it's purely used for recording. It's logged into Discord (as a regular viewer), and OBS is simply recording the Discord window. Note that this machine isn't directly plugged into any hardware, so it can live in a back room rather than the presentation room.

Now, this requires a fair bit of setup on the speaker machine. So we'd really like to provide the speaker machine. (As opposed to having each speaker bring a laptop and configuring it for their talk.) Set up all the hardware in the morning, make sure everything works, and then don't touch it. The speaker sends us their slides in advance; we'll make sure they're available on the speaker machine.

You can already see some pitfalls.

  • We need to provide two laptops per room! We had five rooms this year -- which is a lot of rooms, yes -- that's a total of ten machines. (We found a place that would rent us ten laptops.) (Actually we bought them with the explicitly-spoken intent to return them afterwards. So, rented with a 100% deposit. Scary but it worked out.)
  • We didn't provide our own room cameras; we relied on what Drexel had available. Hope it fits our setup! Also, not every room had a computer to run the camera. We had to source one extra machine to cover that.
  • Speakers hate sending stuff in advance. Some of them will still be editing their slides on Saturday morning.
  • What format do we accept for slides? Speakers want to use Google Docs, Apple Keynote, good old Powerpoint, PDF, Canva -- what the heck is Canva? I'm sure there's more. We shoved everything into Google Docs for consistency, but of course there was friction. I saw messed-up fonts, messed-up layouts, animated transitions got lost... you can imagine.
  • What if the speaker wants to do a live software demo? We've had those in past years. Sometimes you really do need to bring your own laptop. Then the tech crew has to swap that in for the conference-provided speaker machine. Log it into Discord, make sure it streams, oh god the webcam/wifi/battery isn't working, now what? Argh! Many risk factors here.

It worked! We made it work. (And, again, by "we" I mean Logan and JD and the tech volunteers.) However, making it work was, well, a ton of work. The volunteers were swapping machines and testing and verifying the setup in every room, every session. They were overloaded.

At one point on Saturday, we put the whole conference on pause for 15 minutes so that the tech people could catch up. Just pushed the whole rest of the day's schedule by a quarter-hour. It's good that we were able to do that, but we shouldn't have had to.

And for 2026?

At the end of the conference, JD said "Next year: all slides in advance." That is, no more laptop swapping. Speakers may not present with their own machines. Use the provided speaker machine and like it! (Mind you, we'll try have PowerPoint available.)

This makes me sad! I love the live software demos. I love weird hardware. NarraScope hasn't had much weird hardware, but I sometimes go to @party in Boston and they have, like, Commodore 64 demos. Amiga demos. Oscilloscope demos! I mean, I'm sittin' there on the Group W bench and the biggest, glowiest oscilloscope demo of them all sits down next to me, and he says, "Kid..."

(I couldn't poke my head into @party this year because it was the same weekend, dammit. Maybe next year.)

Now, this isn't a final decision. We're all sad about the idea of strictly requiring Google Docs. We've talked about it in the past week. The current idea is to strongly encourage turning in slides in advance, in a known format. Then the exceptions (live demos, etc) will be a short, known list, and the tech team can focus their efforts appropriately.

More updates as they happen.

(The question of how many tracks we will have is beyond the scope of this post. Like I said, five rooms was a lot. There's a strong sentiment to drop back to three next year. But that decision is over the horizon still.)

More edge cases for the 2025 plan

I list these for completeness. I don't think we planned these in advance, but none was a major headache as far as I know.

A speaker who just wants to talk, no laptop or slides or anything: Point the room camera at them and stream that to the Discord stage. (In this case, we only use one machine. The recording OBS machine will also stream to Discord.)

A live panel discussion: Same as above; you point the camera at three people. They may have to pass a mic back and forth.

A remote speaker: They will log into Discord from home and present from there. You will have to give Discord stage streaming privileges to the speaker's personal account. The presentation machine will have to view Discord and push its display out to the room projector.

An all-remote panel discussion: Everybody logs into Discord and you have an N-way Discord chat. Again, the presentation machine pushes that out to the room projector.

A panel discussion with both local and remote participants: Doesn't work! (Okay, this was a headache.) If the local participants sit in front of the screen they're being projected on, it's audio feedback hell. We had to move the local participants into the room next door, thus making them "remote" -- so it turned into all-remote panel. Don't roll your eyes; it worked.

Wednesday, 02. July 2025

Renga in Blue

The Dark Crystal: They Lit the Fires of Prophecy and Took Counsel From the Flames

(Continued from my last post.) I have attracted a few readers who are interested in The Dark Crystal (the movie) and maybe don’t know about The Dark Crystal (the game) and are new to this blog. So to clarify for their benefit: I am doing a playthrough where I blog about every step; because this […]

(Continued from my last post.)

I have attracted a few readers who are interested in The Dark Crystal (the movie) and maybe don’t know about The Dark Crystal (the game) and are new to this blog. So to clarify for their benefit: I am doing a playthrough where I blog about every step; because this is an adventure game, sometimes I make a lot of progress, sometimes I make very little, but I still find documenting either is important in that it encompasses the real experience of playing adventure games circa 1983. This is still before Sierra had official hint books.

I did not make much progress, but I still have a lot of details to go through and theories.

The first thing I tried was simply to replay from the beginning to see if there were any details I missed. The stones that I ended my post with do have a description…

…and that description is meant to indicate the tree is something important.

I’d like to say I thought through in the same direction as Roberta Williams, but in the end I was simply using my regular adventurer reflexes built over time. While in the cave mucking about with the urSu scene again I tried DIG just in case there was some secret item left over, and the game responded:

USING THE SHALE, JEN DIGS IN THE GROUND FOR AWHILE, BUT FINDS NOTHING.

Huh. Sometimes “you dig around a bit and don’t find anything” is just the author’s way of putting off a common verb, but in this case specifically holding the shale enabled (for me, inadvertently) the act of digging, so that meant digging had to be relevant somewhere. I thus went about digging every single room I had accessible in the game, and as part of that I hit that tree.

The shadow graphics even kind of point at the digging spot.

The flute from the start of the movie! I had been wondering where that ran off to. I do want to emphasize I solved this purely by lawnmowering and only realized a clue was intended after the fact.

I think I’m otherwise finished with the starting area, but I can’t be 100% sure. However, for now I went to the area past and tried DIG and PLAY FLUTE in every single room, with no use at all. Still, I eventually unearthed some interesting spots on the map, which I have marked below.

Blue indicates points of interest. Green marks points of interest where I haven’t gotten anything to happen.

The southmost point is at the lily pad I was suspicious of: “VERY THICK STEMS” where “TRY AS HE MIGHT, JEN CANNOT TEAR ONE OF THE PADS LOOSE.” I realized not long after hitting “send” on my last post that the shale is described as sharp, so I ought to be able to apply it to cut the pad.

This landed a LILY PAD in my inventory that is described as having a “THICK, RUBBERY FEEL”. I thought briefly it might work as a raft on the flowing river but no verb I tried worked, even though FLOAT is an accepted verb.

While I’m at it, I should mention I did create my verb list. The game boots on the first side of the first disk (1A), the early area and the wilderness before the Pod Person town is on the back side of the first disk (1B). The disk swap then requires flipping to 2A (second disk, front side), and I assume 2B has the end parts of the game. I mention this because in Time Zone the verbs were not consistent between the disks, but here I think they might all be from the same set:

I tested every verb on the list; green means they were understood by the parser. The oddball I have marked in blue — UNTIE — seems to be a bug:

JEN SHOUTS, “HELP!” UNFORTUNATELY, HIS CALL IS NOT ANSWERED.

You can get the same result from HELP.

While some of the verbs are clearly “fake” (CRAWL, ENTER, JUMP, and LEAVE all ask what direction, but the game is just steering you to the fact it wants cardinal movement directions) this is still a quite substantial list. Working my way up to where the SLING is just lying on the ground, I went through all the possibilities to try to get the sling to work with the shale, but no dice.

IT LANDS HARMLESSLY SEVERAL YARDS FROM JEN’S FEET

I tried this on the flying eye in particular (which really seemed begging for a good sniping)…

…but I always got the same result. With a little noun-hunting (trying to GET items that aren’t there to see if the parser at least understands them) I found this game has the existence of a PEBBLE, but I have no idea where it is.

(And yes, Jen comes from the Valley of the Stones. No good-sized pebbles around? This is worse than the quest for a ladder in Time Zone; at least in that game, one gets a sense that you have to follow the unspoken “rules of the time machine” for it to operate properly which is why you can’t just swing by a store and pick one up.)

The Village of the Pod People, incidentally, gets a few interesting reactions:

  • You can TALK PEOPLE and get the information that the name they call themselves is APOPIAPOIPIDIAPPIDIDIAPIAPOH, which translates into “master gardeners who live in bulging plants”.
  • This is the only place I’ve found (so far) DANCE will work. (“WHY NOT? ALL WORK AND NO PLAY MAKE JEN A DULL GELFLING.”)
  • This is the only place I’ve found (so far) SING will work.

Maybe it’s just here for color. Would Roberta Williams do that? (Given the amount of empty space and red herrings in Time Zone, yes she would.)

To the west of the village is a mossy rock, where you can de-moss it (GET MOSS) while holding the sharp shale to reveal an interesting spiral.

Rather cryptically, looking at the spiral then just gets the response that Jen “GLANCES BRIEFLY” at the spiral but “LOOKS AWAY WHEN HE FAILS TO NOTICE ANYTHING SPECIAL ABOUT IT.” The boulder is too heavy to move and you can’t take the spiral with you. Maybe it’s a hint to a direction puzzle later.

Just south of the boulder are the ruins I was having frustration with before. The room seemed significant (including two flat stones) but I couldn’t get any verbs to work. I returned with my full list in green and tried every single one before hitting paydirt with RIDE STONE. Hah! (Yes, SIT STONE works, it just counts as a synonym, but I found RIDE first.)

Examining the hieroglyphics gives mention of a two-pronged flute, a crystal shard, a female Gelfling, a castle, and a triangle in a circle. I suspect the triangle/circle combo will somehow be used later (I am already trying PLAY FLUTE in every single room so if it’s a clue as to where it gets used, I’m going to sweep it up by default anyway).

I got curious if this had an equivalent in the movie, since this seemed like a weirdly specific room. I’m still avoiding spoilers, but I managed via Internet search to hit a page on the official Dark Crystal site that explained:

When the Skeksis began to take Gelfling, as well as Pod People, as slaves, the Gelfling were dismayed. For once they thought of the future. The Gelfling sought to know if the Crystal might be healed and if the Skeksis rule must continue. They lit the fires of prophecy and took counsel from the flames. Seven circles of seven Gelfling lay on the hilltops all night; their faces to the stars. Their dreams were made of stone; the Wall of Destiny still stands.

In a history of game-design sense, I’d like to point out despite this first seemingly the Big Empty Grid passed down from Time Zone, this is much more dense, and in fact I’m started to be reminded more of the layout of the King’s Quest games (which all the way up I-V had the landscape divided into a grid). Again, we seem to be closing in on the standard point-and-click layout, partly enabled by the use of Henson’s artists allowing for somewhat richer landscapes.

In terms of me being stuck, well, hmmf. I’ve still got the Landstriders who don’t want to be ridden…

The sound at the end is a Garthim barging in. I’ve started to suspect the Garthim attacks are evadable in a real time sense, that is, if you go in a direction fast enough you get away, and if you wait, you won’t. Which is sadly again like King’s Quest 1.

…and the eye that stubbornly refuses to be sniped, and the river that doesn’t want to be crossed, and the chasm, and the spiral (maybe), and the village. I still feel like I’m missing a piece. Even if I summon up the missing PEBBLE, will what I get from shooting down the eye really help with the other puzzles? I need to comb through the rooms again to check if I’m missing a detail.

Tuesday, 01. July 2025

Renga in Blue

The Dark Crystal (1983)

One of the great challenges of designing The Dark Crystal was to create a world that had never been seen and yet could be instantly accepted as a real place with a history and an ancient philosophy. I created a cosmology with meaningful symbols that could penetrate the very fabric of the costumes and the […]

One of the great challenges of designing The Dark Crystal was to create a world that had never been seen and yet could be instantly accepted as a real place with a history and an ancient philosophy. I created a cosmology with meaningful symbols that could penetrate the very fabric of the costumes and the film’s architecture, every visual element important information of this particular world’s past, its ideas, and its destiny. It had always been our intention to create a tale with the weight of myth; a story that felt as though it had been told many times before to another land.

— Brian Froud from The World of The Dark Crystal, 2020 reprint

Jim Henson had his initial concept for the feature film The Dark Crystal start to form in 1975; through the rest of the 70s he did world creation and visualization with the artist Brian Froud, and made a initial script while waiting out a snowstorm. He made the feature film The Muppet Show first, and was only able to get initial funding on The Dark Crystal by agreeing to make a Muppet film follow-up (The Great Muppet Caper). Work from co-director Frank Oz on The Empire Strikes Back also intervened.

A thousand years ago the Dark Crystal was damaged, starting an age of Chaos; during this time the world was ruled by lizards known as the Skeksis. Jen, an orphan from the oppressed race known as Gelfings, is sent on a quest for the missing shard in order to save the world. Poster source.

These delays meant shooting didn’t happen until 1981. It’s tempting to think, then, that the production was “tortured” — especially given the technical hurdles of a live-action movie made entirely from puppets — but it’s more accurate to say it was a slow burn due to financial priorities. Still, the final movie was and is polarizing, somehow being declared magnificent and terrible at the same time. I think the best explanation of what happened can be seen with an excerpt from the test-screening voice track to the movie. The video lasts two-and-a-half-minutes and while it’s usually just fine to breeze on by whenever I drop a video clip, in this case I highly recommend a watch before moving on.

The clip has the Skeksis — the villains of the movie — gathering around the dying Emperor. All the dialogue is hissing in the Skeksis language, with no subtitles. This was Jim Henson’s original vision, and it is the one that showed in the “first edit” that played to an audience in Washington, DC. Henson wrote in his journal:

First preview Dark Crystal in Washington DC – not great.

He had already been warned beforehand that trying to have the Skeksis only talk in their own language without subtitles (with people understanding it “like an opera“) was not going to go well, but the baffled audience of March 19th, 1982 reinforced this; the script underwent a round of edits to have English dialogue added to dub over the fantasy language, where the words had to be lip-synched the best the team could.

Annotations by Jim Henson (on top) and Frank Oz (on bottom).

Still, these changes happened after the scenes were filmed, meaning the essential action was already locked into place. Given that the goal was to have the scenes understandable without knowing the words, the scenes were already done in an “elemental” way, and the dub-over process could not help being awkward. Perhaps more importantly, it was well within Jim Henson’s vision to have parts of the movie understood only partially, where the mood and the world universe was more important than individual lines of dialogue. (If you want to try the original March 1982 experience, there’s a fan reconstruction online called The Darker Crystal.)

Even after these changes the studios involved still wanted modifications, and Jim Henson ended up buying the movie outright with his own money (obtained via Muppet merchandising) for $15 million so he could release it on his own terms. Still, just based on the limits of feature-film length, the deep backstory didn’t really make it to the film as intended; Froud notes what ended up on screen was only “a fragment of this other world.”

Jen the Gelfling, from the original movie.

At the same time as the original test screening, Sierra On-Line finally came out with Time Zone, a game intended for the prior holiday season. That was Roberta Williams’s attempt at a magnum opus, a game that would go on forever. (Concatenating my time spent, I beat it in 24 hours, but it was over a period of two months.) During this same time Sierra was trying to reach past their free-wheeling early years into something more “professional”.

The first few years of Sierra could be described as total anarchy. It is easy to survive (and, thrive!) when you have no competition and your customer base is experiencing explosive growth. And, to be fair, at the very beginning, most of Sierra’s employees were barely out of high school. The party atmosphere was probably appropriate to the time.

By 1982, it was obvious that the “free for all” craziness of Sierra was not going to work. We needed discipline.

— Ken Williams, Not All Fairy Tales Have Happy Endings

While the growth of the emerging market competitors was scaring Ken Williams, he was also spooked by a lawsuit with Atari. In 1981 Sierra had released the game Jawbreaker for Apple II, one of the many many Pac-man clones, and Atari went after clones with a giant legal hammer. Sierra won on the basis of differentiating themselves from the “look and feel” of Pac-Man; in order to justify this they brought a full-sized Pac-Man machine in court along with giant Pac-Man posters to compare with Jawbreaker’s branding.

To be fair, I think Sierra had a point. Pictures from Mobygames and eBay.

However, winning also involved legal fees, and in January 1982 Ken Williams cites having to spend $30,000, and remarked

It’s been real expensive to fight Atari. I don’t know whether I would do it again. If they decide to come after me with appeals, at some point I might have to lie down and die.

which is a frankly odd admission to be making in public, but I think gets a good sense that Ken knew there was the potential for tangling with larger forces on the horizon.

In the spring came a call from Jackie Morby of Boston-based TA Associates offering a million dollars for a percentage of the company and a place on the board. Roberta Williams was hesitant at the possibility of losing some independence, but as Ken writes:

I, on the other hand, thought that it would be good for us. There was a side of me that knew that, for the company to realize whatever potential it had, it would need to stop just being “kids behind a print shop” and take steps to become a real company. Also, Ms. Morby was promising something I dearly needed; someone to talk to about business. I would be free to pick her brain and to speak with the heads of the other companies she invested in.

Ken also mentions, somewhat ominously: “Once we had accepted venture capital, it became like any other drug. No one stops after the first hit.” Even more ominously, quoting Jackie Morby from 1984: “There are investments that only double in value: they aren’t very exciting.”

The two end results were the aforementioned “professionalization” as planned, but also — at the coaxing of the new board — an entrance into the cartridge realm. This was where the “real money” was; for Atari 2600s alone, there was an install base of more than 15 million by this time. (For context, Sierra’s main platform of the Apple II eventually reached an install base of about 6 million… by 1990. Picking one of the more generous estimates I’ve seen, by the end of 1982 Apple had sold less than half a million.)

Jawbreaker got an Atari 2600 version already by the end of 1982, but through a different publisher; Sierra started making their own cartridges in 1983. This ended up being right when the market crash started so while profit doubled the year before, the whole fiasco ended up almost sinking the company with unsold cartridges, but that’s a story for another time.

The elevated profile of Sierra On-Line also extended to film companies. For The Dark Crystal, the instigator of contact was Christopher Cerf, longtime songwriter for Sesame Street.

Trivia: Cerf got named in a lawsuit over the song above when the Beatles catalog was owned by Northern Song of Australia (desired payout: $5.5 million) but then Michael Jackson bought the company and the lawsuit was settled for $500.

Cerf was an Apple II superfan and by 1979 had already given the Apple II bug to Jon Stone (writer for Sesame Street) and Jerry Juhl (writer for The Muppet Show); both started using a word processor for their scripts.

It became a familiar sight to see Jon Stone on the set directing a “Sesame Street” episode with a rolled up copy of the latest script, hot off his Epson printer, in his back pocket.

Cerf had a professional connection to Sierra as the publicity firm he worked with also had Sierra as a client. He convinced the Henson group to connect with Sierra On-Line for the project, and flew to California with Mary Ann Horstmeyer (project manager for Henson) to meet Roberta Williams directly. Cerf called the resulting product “interactive fiction”.

Quoting from a 1982 TV interview with Ken and Roberta:

Roberta Williams: He [Jim Henson] has a new movie coming out called The Dark Crystal and it’s coming out in December and him and a few of his friends have played my adventure games in the past and really liked them a lot and they thought that they wanted an adventure game based on their movies. So they’ve been working with me on the design of this game. Their artists have been doing the pictures, and they’ve supplied me with all the information I could ever ever need, and it follows the storyline of the Dark Crystal really really close.

Three points from that last sentence worth isolating:

a.) Their artists have been doing the pictures

We no longer have Roberta Williams herself or a lone 19-year old producing a gigantic amount of art. Quoting Williams from a different interview:

This adventure isn’t like any we’ve done before. Jim Mahon, the art director at Henson Associates, sketches each page of the action and sends it to me. My people translate the sketches onto the Apple with graphics tablets.

Then the hi-res pages are sent to Jim Mahon for his approval and suggestions. Actually, everyone in New York helps out. Harriet [Yassky], Mary Ann [Horstmeyer], and Chris [Cerf] all review each screen and make suggestions

This is good to highlight because you will see a marked jump in quality compared to Sierra’s previous work.

b.) they’ve supplied me with all the information I could ever ever need

As I’ve already alluded to, Henson Associates created truckloads of backstory; and Sierra got their hands on it. Ken Williams was “shocked at the number of binders full of drawings that provided the minute details behind the movie.”

A Skeksis from the cover of The World of the Dark Crystal, a book by Brian Froud of conceptual art.

Ken also writes that:

Every character had a character sheet providing a full description of the character, their back story, illustrations of how they would look in various clothes and animations, and even samples of how they might speak.

The important thing to highlight (for our story) is that there was more to draw on than what made it to screen, which ties into…

c.) it follows the storyline of the Dark Crystal really really close.

In the same interview Roberta returns to the idea of “how close an adaptation is it”.

…it is primarily based on the movie. The storyline is there and you definitely get the feeling of the story and what’s happening just like in the movie, but a lot of the time there are puzzles that I added that weren’t in the movie but still have the same feeling of the story. There might be things that did happen in the movie but I changed them around a little bit so the same the basic stories there but but obviously we didn’t want them watching the movie and then just come home and play the game and solve it.

Christopher Cerf again:

You run into all the characters from the movie, and you can reply to them in different ways. But you can do things differently than the way they happen in the movie. Your game can end differently than in the movie. You can try out other possibilities. You can say, “What would happen if I tried this.”

While The Dark Crystal was not the first official movie-tie in game (both Raiders of the Lost Ark and E.T. for Atari 2600 came out in 1982, and you can extend an argument to Superman from 1979) it was the first one on a platform where it was possible to follow the plot of the movie in a step-by-step way; this turned out to be an overarching concern, either spoiling the movie plot by playing the game, or having the game follow the movie plot closely enough to be spoiled. Henson Associates were agreeable to the idea of modifications to the story, but given how early this is in videogame history, this isn’t an obvious standpoint to have. Two years later, this became a giant pain point with Disney as Sierra was working on their adaptation of The Black Cauldron.

A week later Al [Lowe] and Roberta received back their design, with major portions of it removed. Many of the removals were because they had included things that “didn’t happen in the movie.” For example, if there was a ladder in a room, and in the movie the central character never climbed the ladder, then Disney’s representatives didn’t understand why they should be able to do it in the game.

All this became relevant for my playthrough. Roberta Williams claimed it was fine to either watch the movie first or play the game first. My memory of the movie is from 25 years ago when I last saw it, so I don’t remember internal details; I remember being confused, or to paraphrase one review, it felt made up as it went along. Hence, I’ve sort of both watched and not-watched the movie at the same time. I refreshed my memory up to where the main character Jen gets his quest, but I’ve stopped there by the theory the game is supposed to be solvable without mimicking what was seen on screen. This may end up being a bad idea, but it’s the sort of thing I’m here to test.

Regarding “1982”, this game slipped the Christmas season just like Time Zone. A mention at the The American Toy Fair in February 1983 calls it a “preview”; it seems to have hit shelves not long after.

As the manual indicates:

…you will become Jen, hero of “The Dark Crystal.” You must find and restore a shard to its rightful place in the Crystal before the Great Conjunction of the Three Suns. Fail, and the world is doomed to live forever under the rule of the ruthless Skeksis. … The computer becomes your hands and feet, eyes and ears.

This game marks, importantly, the Sierra shift to a third-person perspective. Jen is visible in all scenes. All that’s needed is more direct character movement and a more zoomed-out perspective (akin to Castles of Darkness) in order to arrive at the King’s Quest 1 style perspective that would remain the paragon of standard point-and-click games ever after.

No flute, even though Jen has one at the start of the movie.

No matter what you type, the next scene is forced:

That’s all the directions you get. I originally thought we’d have a linear design from here (like Mission: Asteroid) but this is back to Roberta Williams doing a wide-open space, and it is quite easy to go the wrong way.

You start in a 3×3 area where as far as I can tell all of it is scenery…

It’s not obvious there’s an object here, but you can take some shale.

…but if you go farther north (and it isn’t marked this will happen) you end up taking a one-way trip (“JEN FALLS HEADS OVER HEELS DOWN A STEEP SLOPE”).

If you avoid seeing urSu long enough the game will end because he will not pass on the important knowledge about stopping the end of the world, but you’re already softlocked if you’re past the one-way arrows anyway. (His “counterpart” is skekSo the Emperor, the Skeksis who died in that no-English-or-subtitles scene I linked earlier. According to the official site, the lore goes that urSu “allowed himself to die” because this also would kill the Emperor.)

After enough alternate-Jen lives I mapped things out and found out I was supposed to be going due west (no hint, really!) to find the cave which also shows up in the movie.

You can LOOK BOWL to see an image of a crystal, but TALK URSU is needed to get an explanation. It’s in all-caps Apple II style, so I’ve made it a little more readable:

urSu sighs and says, “At the time of the Last Conjunction, or coming together, of our world’s three suns, the evil Skeksis gained control of the Great Crystal that rules our destiny. The Crystal cracked and darkened. And Dark it will remain until a piece that broke off — the Crystal Shard — is restored.

“There is a prophecy that the shard can be replaced only by Gelfling hand, and only at the time of the next Great Conjunction. If this prophecy is not fulfilled, the Skeksis will grow even more powerful, and their reign will last forever.

“Jen, to you has fallen the task of healing the crystal. And it is time for your quest to begin, for very soon the three suns will once again be joined in a Great Conjunction. You will find Aughra, Keeper of Secrets and Watcher of the Heavens. She may have the shard you seek.

We’re not done! Next screen:

“Gelfling, I leave you with a final puzzle: what do the Sun Brothers quarrel about?”

“Find the answer to this mystery and present it to Aughra. Only then can you gain entrance to her observatory.”

“And now Gelfling, our roads must curve apart. We may meet in another life … but not again in this one …”

With these words, urSu dies, and his lifeless body vanished from the sleepframe.

This doesn’t come off that bad written out on a normal screen, but on an Apple II — to my modern eyes — it looks like an info-dump. I’m unsure if there was a better way to handle the scene, though.

I haven’t found anything else in the starting area, but it’s easily possible I’m missing another object like the shale. However, moving on for now, the only way forward is past the one-way barrier on the map.

The purple markings indicate disk swaps. Not only are we in another open area, but rather arbitrarily the game instructs you to swap from disk 1, side B over to disk 2, side A, and while exploring this might mean flipping back and forth multiple times in quick succession.

When entering the Village of the Pod People, I wanted to immediately turn around and go south again, resulting in a disk swap back. I incidentally have found nothing yet I can do here. Maybe the movie would help but we are past the point (roughly 7.5 minutes in) I stopped watching.

There are two monsters that appear, in the style of Roberta’s beloved Crowther/Woods adventure. First is a Garthim, a creature that serves the Skeksis.

You can flee the first encounter safely, but not the second.

Second is a crystal bat with an “eye” that follows. You wander a bit and it goes away. I’m not sure if it has a particular effect in a particular room, or if you’re meant to leverage it to help with a puzzle.

The only item I’ve found (other than the shale) is a sling. You might think the sling would help with either encounter; I can SHOOT SHALE but either “IT LANDS HARMLESSLY SEVERAL YARDS FROM YEN’S FEET” (with the bat) or “TOO LATE!” (with the Garthim; I suspect you can only run).

It may be that both encounters are meant simply to be avoided. With things mapped out it isn’t necessary to hang out long, but I truly am stuck so I don’t want to discount anything. My only two potential points of progress are a chasm…

…and a GREAT RIVER with a SWIFT CURRENT that may not be traversable at all.

There are a couple more places where I am suspicious there is more to do, most primarily a hill with LANDSTRIDERS. You can type RIDE LANDSTRIDERS and the response is “THEY KEEP THEIR DISTANCE AND WON’T LET JEN APPROACH”.

This also shows the bat, which is following along.

There’s also some ruins with two flat stones that look like they ought to mean something but stubbornly refuse to be helpful.

You can try to CLIMB TREE in some places, and I’ve also found spots where TAKE FLOWER and TAKE LILY work, but none have been helpful either.

This is suspicious, at least.

I won’t discount a random seemingly-bland filler room containing a secret item (like with the SHALE) so I need to comb over everything again carefully. Despite the negative parts (bizarre opening where you can get lost and lose right away, rapid disk swaps from just moving around the landscape) the art is genuinely pleasant at times and I do get the vibe of Weird I got from the original Dark Crystal. Mind you, I could just keep the movie playing and it’d get all the way to the end without me understanding everything, and here that likely is not the case.

Hopefully over the chasm next time!

Still noticeably Sierra with the occasional jank, like the Pod People faces from earlier, but the professional artists help immensely.

Monday, 30. June 2025

My So Called Interactive Fiction Life

Sharpee's Architecture Solidifies

RewindA little over three years ago I started playing around with elements of a new IF Platform built in C#. The vision included a graph data store, text service, and not much else. Then when ChatGPT arrived, I was able to make some progress, especially learning new patterns, but

Rewind

A little over three years ago I started playing around with elements of a new IF Platform built in C#. The vision included a graph data store, text service, and not much else. Then when ChatGPT arrived, I was able to make some progress, especially learning new patterns, but those early models were very bad. Each time a new model was introduced, I'd start over and see where it would lead, but the capability of the model, the context window, and the amount of time given was nowhere near enough to handle a complex system.

When Anthropic released its first model under Claude, I switched over to their paid plan. The context window was still a serious limitation and the interface was limited. You could upload your files to a project, but eventually you'd reach the limit and have to figure out ways to cheat that limitation. Eventually I paused, realizing the model and context windows were close, but not good enough.

I also struggled to get Claude to write C# well and so I asked it why this seemed to be a problem. Shockingly, it admitted that C# was a second-tier language in its database and that the optimal languages were Typescript and Python. SO after doing some R&D and switching to Typescript, it became clear that Typescript was a better platform in any case. And if I want, I can do a secondary project to convert a working Typescript version back to C#.

Then Claude Opus 4 arrived as well as higher subscriptions. I started with Opus 4, but almost instantly upgraded to the MAX $100/month subscription. I also downloaded Claude Desktop which allows you to use MCP (Model Context Protocol) which is an obscure method of talking to "other things". The built-in MCP is file_system, which allows Claude Desktop to see a directory on your local file system. I'd tried using before the MAX upgrade, but it was cranky. There's a known bug where Claude will respond to a prompt, then wipe out your prompt and its response before you can read it. Upgrading to MAX has mostly eliminated this issue and restarting the app seems to clear it up.

With Opus 4 and MCP and MAX to Success

This combination is the game changer for generating code with GenAI. There are still a lot of general engineering disciplines you have to adhere to, but you can get extraordinary results when you learn these guardrails.

The guardrails include:

  • Write design documents with development standards.
  • Write ADRs (Architecture Decision Records)
  • Repeatedly request "professional" assessments. (You are a professional IF Platform Designer and Developer but also know my vision)
  • State your vision and criteria clearly to Claude. For Sharpee, this was:
    • queryable world model
    • event source story and system messages
      • story messages will allow a post-turn text service to construct output
      • system messages show everything that happened within the turn (parser, validation)
    • loosely-coupled architecture stack
    • fluent user DSL for authors
    • no virtual machine
    • typescript (with some dev standards like don't use enums)
    • no unit tests until a layer/module is "design stable"
  • Repeatedly request check lists of bursts of work to keep the model on track. I can stress this enough. These models will run amok if you don't contain them. Even with these guardrails, Claude will sometimes get confused about "where we are" and start undoing or reworking solved problems. Luckily it's very good when you scold it and it will revert changes correctly.

Today

There is still a lot of work to do. I have no indulged in the Forge DSL authoring layer yet, but I occasionally will ask Claude to validate what something might look like and that we're not breaking our vision. The Text Service is still a theory, though I am confident the event source will be more than sufficient to emit text in standard IF form. The challenge of combing text will be an interesting effort.

The diagram is slightly out of date, but it's close enough to share. I don't see any major changes at this point. The internal pattern of Validate->Execute and external pattern of Parse->Validate-Execute seem to offer an elegant design.

Traits and Behaviors

One thing that came through in the design process was stepping away from "objects" and moving to something different. From this Traits and Behaviors were born. Traits are things in the traditional sense. So RoomTrait is what you add to an Entity to make it a Room. The RoomTrait contains the data. RoomBehavior contains the logic. Claude and I have thoroughly examined how this extends to story development and its quite beautiful.

Summary

I would say Sharpee is about 75% complete with no major architecture changes in its future. The remaining work is integration, testing, and developing the Text Service and a few sample games.

Saturday, 28. June 2025

Key & Compass Blog

New walkthroughs for June 2025

On Friday, June 27, 2025, I published new walkthroughs for the games and stories listed below! Some of these were paid for by my wonderful patrons at Patreon. Please consider supporting me to make even more new walkthroughs for works of interactive fiction at Patreon and Ko-fi. Cut the Sky (2025) by SV Linwood In […]

On Friday, June 27, 2025, I published new walkthroughs for the games and stories listed below! Some of these were paid for by my wonderful patrons at Patreon. Please consider supporting me to make even more new walkthroughs for works of interactive fiction at Patreon and Ko-fi.


Cut the Sky (2025) by SV Linwood

In this science fiction story, you play as a swordsman. You’ve been wandering for a long time. In the Oeserl Plains, a brigand demands your valuables. You should examine what lies here, and you should talk to him, but you solve most problems with your sword.

This game was an entry in the Main Festival of Spring Thing 2025, where it was awarded Best in Show.

IFDB | My walkthrough and map


Fat Bear (2025) by Charles Moore, Jr.

In this game, you play as a mature adult North American brown bear, also known as a grizzly. And what does a bear in the woods do? Eat everything he can. After you’ve eaten twelve meals, there’s one last place to explore.

This game was written in Dialog and was an entry in the Text Adventure Literacy Jam (TALP) of 2025, placement to be determined.

IFDB | My walkthrough and maps


Fixing Time: A Hack & Makerspace Adventure (2025) by Richard Pettigrew

In this adventure, it’s 2025 and you’re visiting the Hack & Makerspace, a place where everyone can build, repair, and experiment with technology. Someone’s even made a temporal relocator, but it’s missing a few parts. Where and when will this quest take you? Who will you meet? And what will you make?

This game was written in Adventuron and was an entry in the Text Adventure Literacy Jam (TALP) of 2025, placement to be determined.

IFDB | My walkthrough and maps


Escape from the SS Borgarís (2003) by Kevan Davis

In this escape game, you play as a stowaway on a cruise ship, but when you emerge from your hiding place, you find the ship deserted except for an abandoned baby and a cat. What happened? The decks outside are freezing!

This game was one of three participants in the Second 24 Hours of Inform.

IFDB | My walkthrough and map


Escape from the Troll’s Cave (2024) by Garry Francis

In this short and very easy game, you play as a student who skipped school to visit a carnival. But the “Troll’s Cave” wasn’t a free ride. It’s an administration building, and the resident troll demands you pay a toll of 41 cents to leave.

IFDB | My walkthrough and map


Guilded Youth (2012) by Jim Munroe

In this game that switches between a BBS guild of RPG-themed adventurers and their real-life teenage counterparts, you play as Tony the Thief. In a few days, the Oakville Manor will be demolished. You intend to make several midnight raids to loot the place of its remaining valuables before then, but you may need a little help.

This game was an entry in IF Comp 2012 where it took 3rd place; it also won the Miss Congenality award. At the 2012 XYZZY Awards, it won the Best NPCs award; it was also a finalist in the Best Use of Innovation category.

IFDB | My walkthrough and map


Retool Looter (2025) by Charm Cochran

In this sci-fi espionage wordplay game, you play as Agent Quintrell, armed with a portable reverser that can reverse maps into spam. Seven agents have been reversed by your adversaries into inanimates. Your mission is to infiltrate their compound, locate the reversed agents, and rescue them.

This game was an entry in the Main Festival of Spring Thing 2025, placement to be determined.

IFDB | My walkthrough and map


Stealth Adventure (2024) by not me

In this very short and deliberately bad and broken game, you play as a secret agent. Your mission is to find the secret papers and destroy them before they fall into the wrong hands. Not recommended.

This was an entry in the Really Bad IF Jam of 2024. By the author’s request, this game is not listed on IFDB.

My walkthrough and map