Style | StandardCards

Planet Interactive Fiction

Saturday, 19. July 2025

My So Called Interactive Fiction Life

LA LA LA Testing Testing

In a previous post I got a little carried away with all the great testing results, but that ran into an expected wall.Unit and integration tests are meant to validate your architecture and code and expose gaps. Well we discovered a few gaps and it took us a minute

In a previous post I got a little carried away with all the great testing results, but that ran into an expected wall.

Unit and integration tests are meant to validate your architecture and code and expose gaps. Well we discovered a few gaps and it took us a minute to refactor the parser, language, and then the text service, which included adding a new package 'if-services'.

With all of that settled, we're back to testing.

We had to run back through core, world-model, lang-en-us, and if-domain before continuing on to stdlib which has all of the action handlers.

And I had forgotten we had only started testing stdlib and basically needed to start over with a new testing template. Then Claude quietly implemented the wrong test pattern for the actions and we had to redo every set of tests for every action. Then we found remnants of the bad testing and to go through everything a third time and this led to some actual design issues, which are now resolved.

The main issue was we have RoomTrait, ActorTrait, and ContainerTrait. Do we create a dependency that RoomTrait always also auto-adds ContainerTrait?

No. We're just forcing the assumption that RoomTrait and ActorTrait are automatically containers without the ContainerTrait.

I love it when we get into design issues. That's the fun part of all of this.

Friday, 18. July 2025

Zarf Updates

My Worldcon schedule

I'm going to the World Science Fiction Convention in Seattle! And speaking! It's the first Worldcon I've attended since 2009 (Montreal). After the Hadean Lands kickstarter, I figured I should spend my travel budget on game dev conferences rather ...

I'm going to the World Science Fiction Convention in Seattle! And speaking!

It's the first Worldcon I've attended since 2009 (Montreal). After the Hadean Lands kickstarter, I figured I should spend my travel budget on game dev conferences rather than science fiction conferences. I missed Worldcon, but GDC was better biz networking.

But hey, in this futuristic era of 2025, we can have both! Worldcon has a solid track of videogame programming now, and yers truly will be on it.


The Indie Video Game Scene

  • Wed Aug 13  •  6:00–7:00 pm  •  Room 420
  • Justin Bortnick (M), Andrew Plotkin, Martin Klima, Sam Stark, Vivian Abraham

Independent studios are creating some amazing works of fiction and adventure using the medium of video games. What makes a studio “indie”, and what are some of the stand-out titles that fandom can sink their teeth into?

Best Game or Interactive Work Hugo Potentials

  • Thu Aug 14  •  10:30–11:30 am  •  Room 423
  • Eleri Hamilton (M), Andrew Plotkin, Erica L Frank, Justin Bortnick, Remy Siu

The “best game or” Hugo Award is pretty new, and unlike stories and TV shows, there’s not a long, well-documented history of discussion of games in literary sci-fi communities. We’ll discuss what makes something a good “best game” nominee and review some of the games of 2024 that might make your nomination list for next year in Los Angeles.

Interactive Fiction: Video Game Storytellers

  • Sat Aug 16  •  12:00–1:00 pm  •  Room 420
  • Justin Bortnick (M), Andrew Plotkin, Langley Hyde, Stephen Granade, Tina Connolly

Interactive Fiction (or IF) is narrative storytelling through computer text adventures, where players navigate the story, influence the scene, and converse with characters using text commands. With its origins in classic Infocom games like Zork and Planetfall, the IF community is alive and well today, creating adventures that have even been called “Game of the Year” by Time Magazine.

As you see, I will be joined by Justin Bortnick, IFTF president and NarraScope co-conspirator. Also Stephen Granade, who you know from IF history but he's also been doing science programming at DragonCon forever. And Eleri Hamilton, who's been on-and-off involved with Mysterium for just as long. (And is the programming lead for Worldcon's gaming track! Thanks to Eleri for inviting Justin and me to participate in the first place.)

And other people, just as cool, some of whom I've even met.

Of course I'm interested in the whole con, not just the videogame track. I'll be running around the whole week, attending panels and science talks and browsing the used-book tables and chatting with people. Say hi! It'll be awkward but I promise we'll get through it. I'll be wearing the green hyperspace jacket.


To address the inevitable question: yes, there was a kerfuffle about the use of AI tools in program participant selection. I wrote about this back in May. At that point, I was already registered for Worldcon as an attendee and a potential speaker. You can read the Worldcon admins' statement on the subject, which is the most recent update as far as I know.

Anyhow, I haven't changed my position, which is that (a) this is an emotionally charged subject, but (b) not very important in the grand scheme of how Worldcon is run, and (c) Worldcon is a lot of fun so I want to go and be on panels and stuff. So there we are.

Also I have to finish a couple more books before the Hugo voting deadline. And games! I can play through Caves of Qud real fast before next Wednesday, right?


Interactive Fiction – The Digital Antiquarian

The Year of Peak Might and Magic

This article tells part of the story of New World Computing. Some passions are lifelong, but many others fade away over the course of a life, to be replaced by others. Not long after Jon Van Caneghem, the founder of New World Computing, sold his company to 3DO, it started to become obvious to everyone […]


This article tells part of the story of New World Computing.


Some passions are lifelong, but many others fade away over the course of a life, to be replaced by others. Not long after Jon Van Caneghem, the founder of New World Computing, sold his company to 3DO, it started to become obvious to everyone who worked for him that the torch he carried for computer games no longer burned as brightly as it once had. A younger version of Van Caneghem had spent almost three years designing and programming Might and Magic I, the CRPG that introduced New World to our world, virtually all by himself. Now, he was coming into the office just two or three times a week, leaving even on most of the days when he did make an appearance by shortly after lunchtime. His all-dominating current passion, it was becoming clear, was racing sports cars on tracks all over California and beyond. His colleagues sensed that, in his mind, he had already created his magnum-opus CRPG with Might and Magic IV and V, two big games that could be combined into one to form The World of Xeen, an absolutely massive one. Meanwhile he had poured all of his best strategy ideas into The King’s Bounty and Heroes of Might and Magic. He was ready to take two steps back from the day-to-day at New World, to become a part-time designator emeritus and spend the rest of his time driving his cars.

In due course, Van Caneghem’s disengagement would become a problem for the company, arguably even one of the direct causes of its downfall shortly after the millennium. Right now, though, at the end of the 1990s, there was still sufficient momentum to keep things gliding along reasonably well. In fact, 1999 would become New World’s best year of all in purely commercial terms, being the first with major new releases in both the Heroes of Might and Magic and Might and Magic franchises.


A sequel to a game as successful as 1996’s Heroes of Might and Magic II seemed like a no-brainer by industry logic. And yet New World was oddly nervous about spending too much money on it. For the Heroes games were turn-based experiences with hand-drawn pixel art, in an era when real-time 3D was more and more the rage. Accordingly, Heroes III was handled cautiously, allocated only a limited budget and window of time to come to fruition.

Production began in September of 1997, with the hiring of two key figures. David Mullich, who was to be director and project leader, was a grizzled games-industry journeyman whom we’ve met twice before in the course of these histories, at widely separated intervals: once for his highly experimental 1980 game The Prisoner, an enduring icon of the early Apple II scene, and once for I Have No Mouth, and I Must Scream, the equally uncompromising point-and-click adventure game he made in 1995 with the visionary and infamously irascible science-fiction writer Harlan Ellison. Both of these projects lived well out on the artsy end of a career that also encompassed plenty of less challenging, more straightforward fare. Needless to say, Heroes III would belong more to the latter category than the former. For neither of Jon Van Caneghem’s signature franchises had ever set out to touch hearts and minds in any profound way, just to show their players a good time.

Whereas Mullich had been in the games industry almost since before said industry had existed, the man selected as Van Caneghem’s “co-designer” on Heroes III — in reality, this meant that he was a lead designer subject to his largely absentee boss’s veto power — was a rank beginner. Greg Fulton had never shipped a game before; relatively short stints at Activision and the interactive division of Steven Spielberg’s DreamWorks had culminated only in cancellations, as the industry navigated the shrinking market for multimedia-heavy adventure games and the growing one for 3D-modelled action. Nevertheless, Van Caneghem saw something — perhaps something of his own younger self? — in the games-addled, absurdly enthusiastic twenty-something who turned up for an interview with a notebook full of ideas for where to take the Heroes franchise after the departure of Phil Steinmeyer, Van Caneghem’s previous co-designer. Against all the odds, the kid got the job.

Fulton’s brief was most emphatically not to reinvent a wheel that was still rolling along perfectly well. He was rather to take the game that Jon Van Caneghem had originally designed under the name of The King’s Bounty back in 1990, then expanded upon twice with the help of Steinmeyer to create Heroes I and II, and expand upon it yet one more time by adding more monsters, more treasure, more factions, and whatever else seemed advisable without mucking up the rather brilliant core design. On his side, he fully understood what was expected of him.

We had to use the existing Heroes II engine within a tight window of time.  On top of this, we had a passionate fan base who absolutely loved Heroes II, and, for its sequel, didn’t necessarily want a radical departure.  I could not play fast and loose with the design of Heroes of Might and Magic III.  I needed design evolution, not design revolution.  Thus, my overarching guideline for Heroes III became, “If it isn’t in the spirit of Heroes of Might and Magic… kill it.”

So, over the course of seventeen months, Heroes II was transformed into Heroes of Might and Magic III: The Restoration of Erathia. The graphics resolution was bumped up from 640 X 480 to 800 X 600, and the game was given a moodier, more realistic, less cartoon-like look to suit changing aesthetic fashions. The space where tactical combat took place was made about twice as large to allow more room for maneuvering. Scenario designers were given the option of adding an underground level to their overland maps. Two new playable factions were added to the six of Heroes II, and more buildings were made available to construct in each faction’s towns. Each faction got a new unit type as well, and all units instead of just a select few were made upgradable. The heroes who led the armies were allowed to bring up to seven rather than five different types of units with them. More CRPG elements were added to the game, with each hero being given one innate special ability and a wider range of secondary skills to learn. The magic system was expanded, with the spells at the player’s command now divided into separate, elemental schools that magic-oriented heroes had to learn separately.

Half a dozen campaigns were created in lieu of the single one — playable from either side — of Heroes I and II. Not only were these campaigns given more fully fleshed-out story lines — the one that gave the game its subtitle followed directly on from the story of Might and Magic VI — but the individual scenarios that comprised them were given more continuity by allowing players to bring heroes and equipment with them from scenario to scenario. And for those who preferred to play with or against other humans, the networked multi-player mode was given an extra coat of spit and polish.

In short, Heroes III became a case study in smart, evolutionary game design, elaborating on those aspects of its predecessor that seemed to need elaboration — or at least that seemed tolerant of it — whilst leaving the core systems that already worked so well as they were. The overriding intention was that Heroes III should be perfectly comprehensible to any Heroes II player from the moment she fired up her first scenario. This fourth iteration on the same basic design was to be the last word on it.


Queen Catherine, the star of the lead Heroes III campaign, appears in the opening movie dressed in a brassiere, chainmail thong, and high heels. She looks more like a pole dancer at a fetish club than a noble queen and warrior.

Thankfully, the rest of the game tones down the horny-adolescent clichés. Veterans of previous Heroes games will feel right at home right away in this interface.

A town waiting to be developed. Certain design choices, such as the addition of trading posts as cheap buildings everywhere, have made gold and other resources easier to come by in the early game, which makes the scenarios play somewhat faster.

Tactical combat now takes place in a space with four times the quantity of hexes. As a result, most melee attackers can no longer reach ranged attackers on the first turn, altering the balance of power among the units considerably.

Beginning a new campaign. A lot of effort has been put in to make the campaigns feel less like a string of standalone scenarios and more like a seamless whole. (Alas, the benefits are kind of lost on me. I’m usually a story guy, who goes right for the single-player campaigns. Yet I’ve never been able to work up much interest in Heroes campaigns, having my best fun with single scenarios — especially multi-player ones. Go figure. Maybe it’s just down to Heroes of Might and Magic being among the few games that my wife really wants to play with me instead of just commenting from the sidelines — itself a testament to their broad-based appeal.)

Every Heroes of Might and Magic game made by New World sports an exceptional orchestral soundtrack. Heroes III may have been the first game ever to utilize a newfangled audio-compression format known as MP3, which would ignite an international furor when the Napster MP3-sharing service made its debut later in 1999.


David Mullich and Greg Fulton thoroughly succeeded in their brief by most reasonable standards. Today, Heroes of Might and Magic III is widely regarded as the entry from the franchise to get.

Contrarian that I am, though, I do find that I want to push back against that idea just a little bit. At the end of the day, I actually think that I slightly prefer Heroes II. Part of my preference comes down to raw aesthetics. I like the more cartoon-like, cheerfully goofy graphics of Heroes II better than the more conventionally “gamer dark” visuals of Heroes III; I’ll take Heroes II’s Count Chocula vampires, who unleash a theatrical Blahhh! every time they attack, over Heroes III’s Nosferatu grotesques any day. I was amused in a “how different people are” sort of way when I read one of Greg Fulton’s recollections about the Heroes III project, saying that he and David Mullich considered “fixing the artwork” to be their most important priority going in, behind only the all-encompassing one of “don’t screw a good thing up.” If you ask me, the fun and frothy Heroes II art has aged ironically better than that of Heroes III. In any of its incarnations, Heroes of Might and Magic is a game to be enjoyed, but never one to be taken too seriously.

Then, too, those of you who’ve been around here a while will know that I tend to be skeptical of strategy-game sequels that simply add more stuff. Mind you, I don’t think that any of the additions to Heroes III ruins the experience; their saving grace is that they tend to add variety rather than turn-to-turn complexity. Fulton was wise enough not to do away with the limit of eight active heroes per player. Presumably a result of technical limitations in the earlier games, it ensures that Heroes III plays snappily, never getting bogged down with dozens of armies in the way that any veteran of late-stage Civilization II or Alpha Centauri will all too readily recognize.

I do think, however, that Heroes III pushes right up the edge of diminishing returns in other areas. The new faction ordering, for example, destroys the lovely symmetry of the two good, two neutral, and two evil factions in Heroes II. Even after playing the game for many hours, I still can’t really keep the Heroes III factions straight in my head. I don’t have that problem with Heroes II.

Still, there is context to these judgments — and I mean that unusually literally in this case. If there’s one thing I’ve learned over the years I’ve been writing these histories, it’s how well-nigh impossible it is to separate our judgments of games from the times and places in which we encounter them. The beginning of my time with Heroes II corresponded with the height of the pandemic, when I didn’t have a whole lot else to do but write, play games, and go for long walks. Then I played it a lot more while I was working on my “Web Around the World” series, which left me with no big pressure to play other games for a good six months. I have fond memories of sitting on the terrace in weather fair and foul, talking with the neighbors from time to time over the hedge, and otherwise just happily playing away. Then my wife discovered that she liked the game a lot as well, and we started playing it together. All told, I’m sure that I put more hours into Heroes II than I have into any other game since I started this website.

Heroes III was not like that; it was just another game to have a look at, in a different, more distracted context. I still put quite some hours into it, more than I could strictly justify in the name of “research.” And almost all of those hours were once again spent in multi-player mode with my wife. We greatly appreciated the smoother networked play of Heroes III, as well as the abundance of cooperative multiplayer scenarios. (We much prefer to play that way rather than trying to kill one another.) But I didn’t feel the urge or the compulsion to play every scenario, as I had with Heroes II. There was a slight twinge of been-there, done-that for us both even when we were having fun.

All of which is to say that you shouldn’t take my word as gospel truth here, any more than you should for any game I write about. If I’d come to Heroes III first, and/or in a different time, I might very well be expressing a different opinion right now.

Even as it is, I cannot deny that Heroes of Might and Magic III is an immensely satisfying game in its own right, one which I feel no hesitation over inducting into my personal hall of fame. I still want to tell you that the best way to enjoy the series is to start at the beginning with the rather rudimentary but charming Heroes I — or perhaps even The King’s Bounty if you want to be really thorough about it — and make your way forward from there. But I do understand as well that life is short, time is limited, and not everyone is a digital antiquarian who can justify any portion of his playing time in the name of research. Heroes III is a perfectly fine place to jump in as well.

Released in February of 1999, Heroes of Might and Magic III was a solid early seller in its homeland, but not a massive, chart-busting one. It would prove more of a marathon runner than a sprinter. Aided by two expansion packs that added yet more campaigns, scenarios, monsters, treasures, and other goodies, along with one more faction to play and even a random map generator for those who had plowed through all of the set-piece content, it sold steadily for years on end, defying the conventional wisdom that turn-based, pixel-graphics games were a hopeless proposition in the marketplace. 3DO aided its cause greatly by investing in widespread language localization and making sure with the aid of various partners that Heroes III was readily available in shops all over the world.

It proved especially popular in Russia, where, unusually for a turn-based game, it even found a place in e-sports leagues. For decades now, people have been asking themselves just what is behind the special love that Heroes III sparked in the hearts of Russians. A possible answer might begin by noting that the traditional high-fantasy tropes that were always New World’s stock-in-trade seemed fresher in the lands that had been walled off behind the Iron Curtain than they did elsewhere, and might conclude by noting that Heroes III’s modest system requirements — no fancy 3D card required! — gave it a leg-up in countries where the average personal computer was not so powerful as in the richer West. Between these two data points lies a no man’s land of rank speculation. But for whatever combination of reasons, if you go to any online Heroes III scenario archive today, you’ll still find that a crazy percentage of the maps bear names written in Cyrillic. In a time of increasing global discord such as our own, it’s nice to be reminded that the sort of good, clean fun promised by Heroes remains universal.

I don’t believe that credible hard sales figures have ever emerged for any of New World’s games. But based on circumstantial evidence alone, I wouldn’t be at all surprised to learn that this one sold 2 million or even 3 million units before all was said and done. It most assuredly has to be the most successful single game New World ever made, as well as the most successful ever published by 3DO. (Granted, the latter is not a particularly high bar to clear…) When we consider the modest amount of money that was put into making Heroes III, we have a return on investment to die for, regardless of whether we calculate the bottom line in dollars and cents or hours of fun delivered.


Might and Magic VII: For Blood and Honor would prove less enduringly iconic than the third installment from its spinoff franchise, but it was a rock-solid effort in its own right. Bryan Farina and Paul Rattner, the principal designers of the previous game in the CRPG series, were joined this time by one James W. Dickinson. Might and Magic VII shipped in June of 1999, just thirteen months after Might and Magic VI and three months after Heroes III. On a schedule like that, there was no time for technological or philosophical reinvention — and, indeed, Might and Magic VII is even more of a strictly evolutionary step than Heroes III. It uses the same engine as its predecessor, with only a few minor tweaks here and there. Nevertheless, it’s a slightly better game than Might and Magic VI in my opinion, retaining most of the latter’s gonzo charm whilst feeling more manageable and self-consciously designed, as opposed to grown organically in some sort of nerdy laboratory of excess.

By now, it had become standard practice at New World to integrate the plots of the CRPGs and the strategy branches of the Might and Magic family tree. So, the story of Might and Magic VII follows on from that of the headline campaign of Heroes III, taking place on the same newly discovered continent of Erathia. Most of the old gang is back together again, including Queen Catherine, the star of Heroes III, and the dastardly Archibald, the villain you already defeated once in Might and Magic VI. I’d tell you more about the story, but, honestly, if you’re playing a Might and Magic game for the plot, you’re doing it wrong.

That said, there are a few more nods here toward giving you some agency over the narrative beyond deciding where to go and kill monsters next; New World may have felt a sense of obligation to at least gesture in that direction after seeing the groundbreaking Interplay CRPGs Fallout and Baldur’s Gate. You’re forced to make a moral choice about whether to join the forces of light or darkness about one-half to two-thirds of the way through Might and Magic VII. Even so, a single hard branch like this is pretty weak sauce compared to the choices and consequences that are woven throughout a CRPG like FalloutMight and Magic VII remains a fundamentally different type of game. And I for one wouldn’t have it any other way.

Where Might and Magic has always excelled is in giving you a giant, generous, unabashedly silly, blatantly artificial sandbox of a world to go forth and explore, exploit, and exterminate. The seventh game justifies its existence by giving you an even broader swath of fun stuff to do in the sandbox than its predecessor. You get a home base this time, a castle that you get to renovate step by step as you earn social credit from your adventures. Meanwhile the addition of an Invisibility spell opens up the possibility of sneaking your way through some areas instead of falling back on the standard approach of methodical monster genocide; Thief this game is not, but skulking about in a demon town whose inhabitants would reduce your party to a handful of smoking cinders in about half a second if they could only see them is not without its thrills.

Perhaps best of all — definitely so if you ask my wife — is a new card game called Arcomage, obviously inspired by Magic: The Gathering, that you can play in any and all of Erathia’s taverns; there’s even a quest that demands that you win a round of Arcomage in every single watering hole. I played Might and Magic VII on our living-room television, while my wife did her embroidery and mocked my ineptness and the game’s abject nerdiness — business as usual in our house over most of the seventeen years we’ve been married, in other words. But as soon as she saw Arcomage, she was smitten. I learned to avoid going into taverns unless I absolutely had to, because she would invariably demand the mouse in order to play a round or two or three. For this reason, I’ve never actually gotten the chance to play Arcomage myself, but it sure looks like good fun to me.

Certainly plenty of people have come to share my wife’s opinion of it over the years. In the immediate aftermath of Might and Magic VII‘s release, fans set up entire websites dedicated strictly to this game-within-a-game; the level of interest was so high that New World later published a standalone version of Arcomage, with networked multiplayer support. It would be going too far to say that Might and Magic VII is a mediocre CRPG that you have to tolerate in order to play an excellent card game, as people sometimes say of Final Fantasy VIII and its own built-in card game of Triple Triad. But Arcomage does add considerably to the already ample fun that awaits in Might and Magic VII. I know that it added quite some hours to my total play time in the CRPG, what with my wife’s addiction to it.

Other additions to the Might and Magic VI template are more commonsense than inspired, but nevertheless reflect a development team who were listening to the wishes and complaints of their players. It’s now possible to move your party after you’ve entered turn-based mode for combat, in just the same way that the monsters they’re fighting are allowed to move; names of important locations automatically appear on the auto-map; the auto-notes and quest log are little more thorough; the cheerfully janky graphics engine has become somewhat less so without losing its personality, with monsters being less likely now to get themselves stuck in walls and floors; a tutorial zone has been added for getting new players up to speed; etc, etc. Evolution rather than revolution, yes, but no less welcome for it.

The game is smaller than its predecessor — not small, but smaller — with more self-awareness of what it’s on about from moment to moment. The crazily ginormous dungeons of Might and Magic VI have become a bit more constrained, and each one here has a clear plot function, rather than existing just to exist. The sense that dogged Might and Magic VI that work on that game had not been so much completed as halted when the day to ship came is not present here; Might and Magic VII manages to feel like a fully realized, reasonably polished whole. All of these things make it the better game of the two by any objective measure, even if the monument to Monty Haul excess that is Might and Magic VI is in some cock-eyed way the more lovable creation. If you’re at all curious about the series today, the seventh game is probably the best place to start. If you still want more after you’re done with it, the lumpier pleasures of what came before — and after, for that matter — will still be there waiting for you.


Walking on water. When I say this game is less janky than its predecessor, the “less” does a great deal of heavy lifting.

If you choose the path of light, you get to literally ascend to Heaven. (The opposite also applies.)

Here we go. Arcomage in all its pseudo-Magic: The Gathering glory. Do not, under any circumstances, let my wife see this screenshot, or I’ll be forced to boot up the game again.

It wouldn’t be a Might and Magic CRPG if you didn’t wind up fighting robots in a dungeon that’s more George Lucas than J.R.R. Tolkien. By the seventh go-round, this was no longer quite the shock it once was, but series tradition must be served.


Magic and Magic VII was never expected to sell as well as Heroes III, and it delivered no surprises in this regard. In this era of rampant audiovisual one-upsmanship, many reviewers complained that it looked essentially the same as the previous game in the series, which had been no audiovisual wonder in itself. Enough gamers looked beyond the surface, however, to make it profitable. Again, firm New World sales figures are nonexistent, but 100,000 copies strikes me as a reasonable ballpark. Such a number was enough to ensure that New World would invest in a Might and Magic VIII, but not enough to encourage them to devote a great deal of resources to it. Indeed, as we’ll see in a later article, both Might and Magic and New World as a studio would become victims of the cruel logic of diminishing returns after this peak year of 1999.

Right now, though, we’ll leave New World on top of the world, or at least as close to it as they would ever get. For, as usual, we have much else to talk about before we can go from here to there.



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: Computer Gaming World of July 1999, October 1999, and January 2000; Retro Gamer 49.

Those wishing for a more detailed chronicle of the development of Heroes III may want to look to the newsletter that Greg Fulton published in conjunction with a now-abandoned attempt to create a modern successor to the game. All of the standard caveats about single-source history apply — Fulton is not without axes to grind — but the spirit of the age, as they say, is there. For my part, I must admit that I’m most intrigued by Fulton’s ability to remember meals he ate a quarter-century ago, complete with cheesy fast-food branding. (“Following a quick run to the nearby Jack In The Box, for a Sourdough Jack and a couple Vile Tacos, I returned to my office.”) In addition to giving me a vicarious bellyache (“I gorged myself on a chili-cheese burger, two chili-cheese hot dogs, and a vanilla milkshake…”), this leaves me wondering why I can’t seem to remember what I ate for dinner last week. Anyway, Fulton starts to reminisce in earnest in the tenth issue of his newsletter, so that may be the best place to jump in.

Where to Get Them: Heroes of Might and Magic III: Complete and Might and Magic VII: For Blood and Honor are both available as digital purchases at GOG.com.

Thursday, 17. July 2025

Renga in Blue

Marooned: Diamonds to Dust

(My previous posts on Marooned are needed for context.) I have finished the game, using the time-old technique known as “reading the source code”. I am fairly sure I would have made zero progress otherwise. This just gets absurd, and not due to bugs. I’m not going to sequence in order of how I “solved” […]

(My previous posts on Marooned are needed for context.)

I have finished the game, using the time-old technique known as “reading the source code”. I am fairly sure I would have made zero progress otherwise. This just gets absurd, and not due to bugs.

I’m not going to sequence in order of how I “solved” things or in narrative order, but rather from most to least reasonable.

Money from under the big W.

Let’s start with the treasure under the trees I couldn’t get to. As I suspected, it was a straight parser issue.

Things that don’t work: DIG, DIG TREES, DIG UNDER, DIG BETWEEN, GO TREES, DIG W, FIND SPOT, FOLLOW MAP, DIG SPOT, DIG TREASURE, FIND TREASURE, LOCATE TREASURE.

The game was fishing for GO W, and then DIG.

This was the treasure with no name originally, hence the fix calling it MYSTERY TREASURE. Now I know the context, there’s a fair chance this was intended as SUITCASE OF CASH.

Not terrible for a work in progress, but it still stuck me entirely. To grab from the source code, I used software called Scottdec:

141) 1 56 [GO W] /* MISSING STRING */
     ? PLAYER_IN (13 = *I’m on the west shore of a deserted island)
      ? CLEARED_BIT (22)
      -> 1 = PRINT(OK)
      -> 58 = SET_BIT (22)

This is much cleaner than trying to read off the database file directly, which has “1 56” on its own without the verb and noun linked to it. The “missing string” comment is supposed to go somewhere else, but it looks like something in the sequence of comments is out of sync.

Next up is the cave, with the tiny hole and the chisel.

I had additionally tried LOOK HOLE, FEEL HOLE, RUB HOLE, and pretty much anything on my standard verb list that seemed reasonable, but unfortunately, the game uses a brand-new verb I have never seen before in a text adventure: REACH HOLE.

This isn’t done yet! Despite the diamond being a treasure with asterisks, the treasure can be made into two treasures, via CUT DIAMOND / WITH CHISEL.

If you get greedy and try to get yet more treasures by repeating the process, the diamond gets ground into dust.

The really wacky thing here is that one is not necessarily more valuable than the other (except in a black-market sense, except we aren’t going to make it that far). No, it’s simply taking the fact that the game wants, abstractly, 7 treasures, and can only get all the way there by turning one distinct treasure into two.

There is incidentally a “clue” earlier about the diamond, but it is a complete red herring clue (in the Ferret sense of being actively misleading).

There is no boat. BOAT is listed as a noun so I wonder if the author considered this, changed his mind, and never got around to cleaning up the clue (work in progress!) as opposed to creating an intentional red herring.

Nearly to the end now, to the most absurd jump of all. I knew from the start of the Scottdec file what the goal was:

TGoal: store 7 treasures in room 24

Room 24 is a HUT, but we haven’t seen one, because you’re supposed to make it. With the leaves from the trees and the string from the dead body, you can (on the west side of the island, at the W) use the command MAKE HUT.

Now, you can GO HUT and deposit treasures. We’re one short, but after a little time here is a “quick flash of red light”; LOOK LIGHT reveals a RUBY. Amusingly, it doesn’t even need to be picked up, just revealed.

Oh, on the darkness: from the source code I found there is an overall light timer, so it wasn’t the flashlight turning off the sun, just the game being mean. It gets so dark at night you literally can’t see anything at all.

Like Strange Adventure, we are king of an island at the finale with no visible way off. Enjoy your two diamonds!

129) 52 35 [MAKE HUT] /* 2 DIAMONDS TO DUST */
     ? IS_AVAIL (65 = String)
     ? IS_AVAIL (45 = Leaves)
     ? PLAYER_IN (13 = *I’m on the west shore of a deserted island)
     ? IS_AVAIL (47 = Leaves)
     -> 1 = PRINT(OK)
     -> 73 = CONTINUE:
130) [Cont’d] /* NOTHING ELSE WORKS */
     -> 53 = MOVE_INTO_AR (63 = Hut)
     -> 59 = REMOVE (45 = Leaves)
     -> 59 = REMOVE (47 = Leaves)

If this was a published game, the hut puzzle would enter the all-time most absurd list; it gets an asterisk due to the work-in-progress nature of the game, since the author may have had some plan in mind before running out of space.

I do think, now, regarding “why this was unfinished”: it was a matter of running out of memory space. In order to fix the TAKE commands, the code went up to 19k, and that’s excluding items like the screwdriver and leaving in numerous other erroneous parts that the author clearly intended to get back to later. The game doesn’t seem large/impressive but Watt did try to write a list of features that started to extend past the game’s reach. SCREW is intended as a verb (unused); the BOAT is listed but doesn’t show; there’s ICE and a BOOK for some reason. I tried to cross-correlate with other Scott Adams games (in case one served as a template and these are “vestigial words” left in) but no dice: I’m pretty sure everything listed is something the author intended eventually. Hitting a wall like this from an original plan is bound to be frustrating for development and it is a miracle at all the game was left close to a state that could be played all the way through.

Coming up: Three Britgames, followed by games from Japan, New Zealand, and Denmark. This will be our first 1983 game in Japanese, and neither New Zealand nor Denmark have appeared on this blog before.


My So Called Interactive Fiction Life

GenAI is both Popular and Unpopular

To no one's surprise, a lot of people using GenAI for anything get quite a bit of negative and sometimes abusive feedback. I've stated this other places, but want to share my feelings on the matter.Art, Video, Audio, and Narrative TextIt's clear
GenAI is both Popular and Unpopular

To no one's surprise, a lot of people using GenAI for anything get quite a bit of negative and sometimes abusive feedback. I've stated this other places, but want to share my feelings on the matter.

Art, Video, Audio, and Narrative Text

It's clear that all LLMs have used copyrighted material to train their models and when used for these types of output, it's not fair use. It's a clear violation of copyright. I did turn some of my own selfies into images using AI, but I stopped immediately after reading about the methods used to build those apps. There are only ten of these images, one of which heads this post. I did not pay for these images to be created so the app owner made no money from me, but there were in-app enhancements, so the app owner was trying to make money off of other people's art and photography. This is clearly wrong and should be illegal. The sad truth is that under this administration, a toothless congress, and a compromised judiciary, copyrighted material used in training large language models is going to get a fair use pass. It's not 100% yet, but with all of the money flying around, it would seem to be inevitable. I think efforts to embed training corruption into your digital art is now a necessity. Luckily this is easy to do.

Programming and Interactive Fiction Concepts

Since there is an enormous amount of open source and public domain code, it would seem that trained models ability to generate code is acceptable, but here we don't know all of the details. It's possible that Anthropic Opus 4 is trained on private or copyrighted code, but I'm not sure it would be necessary. There's just too much public code to bother. I primarily use Claude to generate code that I design, and there is an important distinction in the phrase, "that I design".

Interactive Fiction has a lot of open source code, though some of it is copyrighted and protected. There is also a lot of theory and deep examination of how to build an IF platform that crosses into public and private domain. Some may view my work on Sharpee as crossing that line. But at heart I am an IF hobbyist and have wanted to build my own platform for 40 years and never understood the moving parts well-enough to hand-code one. Claude, and specifically the latest Opus 4 with a full MAX subscription, has afforded me the ability to both learn about those moving parts and to construct a new Typescript-based IF platform. I am comfortable that I am not hurting anyone since the entire code base is on GitHub with an MIT license.

I'm also comfortable because I have introduced some interesting changes to the way we think about authoring IF. This will become apparent when I have a released and tested story built on Sharpee. Something as simple as using Jest to run tests against the code and your story is incredibly useful.

Some of those new things are not all that new. Traits and Behaviors are a traditional static object split into data and logic. Capabilities are actions with custom data. Using an Event Source is nothing new in the business world, but it is new in the IF world. Separating the Text construction from state change logic is also new to IF.

I strongly believe Sharpee will enable more authors to build bigger stories faster, if it's ever embraced (I'm not holding my breath...IF folks are moody 11yo tabby cats). (I love 11yo tabby cats)

If nothing else, it has made me enjoy building something for the last two years (off and on) and I have learned a LOT.

The last part is that none of Sharpee is possible without decades of knowledge of IF and system development. You can't just prompt, "Make an IF platform in Typescript." and get a complex, well-designed, tested platform. No AI is capable of that and is highly unlikely to ever have that ability.

No, AGI is not coming and we're not going to be enslaved by robots. The tech to enable thinking and reasoning has not been invented and no one knows how. You'd need a Positronic Brain from Asimov's Robot novels or Star Trek Next Generation and we don't know what that would look like or how to construct it.

I have been able to build Sharpee with many hundreds of hours of iterative design, generating code and plans, testing, refactoring, and restarting from scratch more than a few times. Even using GenAI you have to be knowledgeable, passionate, and dedicated to your purpose.

I hope to write something long about this process that includes a lot of the frustration with earlier models that would fall off a cliff when the context window became too large. I think I full stopped half a dozen times until Opus 4 + MAX + Desktop Claude + MCP.

In any case, for me, generating code using GenAI gets a pass. If you don't think so, I apologize. I think it's a massive game changer even though it does stupid things when you forget to pay attention.

Back to testing Sharpee. I think we're 90% ready for a full story. Maybe even 95%.

Tuesday, 15. July 2025

Renga in Blue

Marooned: Into the Glitchiverse

(Continued from my previous post.) I’m not done with the game yet. It is a unique experience in that I normally would be wildly upset at the number of bugs if this was a published product — it has more than even our worst offenders — but as-is, I consider something of a window in […]

(Continued from my previous post.)

I’m not done with the game yet. It is a unique experience in that I normally would be wildly upset at the number of bugs if this was a published product — it has more than even our worst offenders — but as-is, I consider something of a window in time. In terms of history of game design: what limits did authors have they were running into? What was the fault of bad design and what was the fault of authors just working with what they had? What concepts did they have that ended up undercooked just because of technical issues? My most comparable other playthrough was Irvin Kaputz which was a game abandoned because of running out of memory space, where adding even one more character to the text causes a crash; the author there had some ambitions of object-modeling that were rare for the time, but the fact this extra detail caused failure is a good lesson in why bare-bones was more the norm.

Scott Adams stated with Adventureland the reason the game has the span it does he kept writing until he ran out of space (versions vary of the data file, but they all are around ~16000 bytes).

I’m not sure if the memory issue applies here. Mr. Watt certainly had access to 32k (see: his Microsoft Adventure copy program) but he may have still had 16k as a target goal. There are actually two versions of the data file for the game, one at 12576 bytes and one at 15588 bytes, the former having spaces taken out for compression purposes. This suggests he realized he was reaching his limit and did a pass, but was still running short accounting for everything else to make this feel like a polished product. I’ll study the issue more once I’m done with the game.

After Kim Watt went to Texas, Super Utility was published in partnership with Powersoft. Cover of catalog via Ira Goldklang.

My progress essentially involved combing over everything seen and finding extra items along the way. To start with, when grabbing the seat as a floatation device from the plane, it turns out LOOK PRESERVER reveals some batteries. Of course, the batteries don’t let you TAKE them (of course) so you can be in the middle of the ocean and have it happen and they’re just floating there. Don’t worry, they’re ok!

A flashlight turns out to be nearby as well. On the first beach I had done DIG to get the response

With what?
I don’t have a shovel.

Quite often this means a shovel is coming later, but Rob in the comments suggested trying HANDS anyway.

——-^ Tell me what to do? WITH HANDS
OK

So “With what?” is meant to be a parser prompt! Also noteworthy: unlike some games that include the WITH syntax, you have to get the DIG-prompt first for the WITH HANDS to work. So there’s also a diggable item on the next beach you come across (a rusty knife) but I originally just tested WITH HANDS and only discovered later DIG was required first.

Back to the first beach, digging gets a hole and the hole has a flashlight. I took the flashlight back to the ocean and was fortunately about to LOAD FLASHLIGHT / WITH BATTERIES and it worked. The unfortunate thing is that this starts the light timer (…sometimes?…) and when the timer goes off, everything goes dark, including if you are outside in sunlight. The better thing to do is to wait for until you are next to the cave later to LOOK PRESERVER so the batteries get dropped close to where they are used. (Having said all that, one of my test-playthroughs the flashlight timer just didn’t seem to cut off at all even after many turns. The timer is just busted generally.)

That’s still not everything in the first area; in the ocean where you land you can DIVE.

OPEN CHEST results in

I can’t
it’s locked.

Getting stumped here, I got around to making my verb list. Notice neither LOCK nor UNLOCK are understood verbs.

Having noting in the way of HIT verbs, I kept trying around things until I realized the game lets you refer to LOCK as a noun (…just assuming a visible lock on the locked chest…) and CUT LOCK / WITH CUTTER works to pop it open, yielding *GOLD COINS*.

All this being done while floating in the middle of the ocean since you can’t take the chest, of course.

Just to recap, I had newly-found: FLASHLIGHT (from beach), RUSTY KNIFE (from other beach), and GOLD COINS (from chest). The progression is to land at the first beach, swing through the jungle to the second beach, then go into the ocean again, where yet again DIVE works to find something.

The smeared map, if you wait enough turns, will clear up enough that it dries out and you can read it. (I think the smearing would not undo itself? It felt clever anyway.)

Unfortunately, DIG doesn’t work like it did before. I have no idea what parser command to use here.

It’s pretty clear what movie is being referenced, though. From It’s a Mad Mad Mad Mad World, near the ending under the “big W” where the treasure is buried.

As implied earlier, the flashlight/batteries now allow entrance into the cave on the south side of the island.

There’s a large cavern with hole (that you can enter), a small cavern with a hole (that you can enter), and a tiny cavern with a hole (that is too tiny to enter). In the third room LOOK AROUND reveals a CHISEL (not takeable). I haven’t been able to use the chisel on anything. It is possible there is a HAMMER that goes with it but I haven’t found it.

I still have had no luck getting by the quicksand or overgrown bush in the jungle, or the floating jelly in the lake. I suspect I am not far from wrapping things up but the game is not even close to playing fair.


The People's Republic of Interactive Fiction

July meeting (online)

The Boston IF meetup for July will be Monday, July 21, 6:30 pm Eastern time. We will post the Google Meet link to the mailing list on the day of the meeting.  We’ll talk about NarraScope, a possible in-person meeting in August or Sept., and games people are playing.

The Boston IF meetup for July will be Monday, July 21, 6:30 pm Eastern time. We will post the Google Meet link to the mailing list on the day of the meeting. 

We’ll talk about NarraScope, a possible in-person meeting in August or Sept., and games people are playing.

Monday, 14. July 2025

The Rosebush

Studies of ZIL, Part 2: How Does ZIL Work Anyway?

The basics of coding in Infocom's ZIL: Zork Implementation Language

ZIL: Zork Implementation Language. Used by Infocom for their wide range of well-known games, including classics such as its namesake, Zork, as well as Trinity, Plundered Hearts, and The Hitchhiker’s Guide to the Galaxy. In the previous article in this series, I studied how the Z-machine – a virtual machine compiled from ZIL code – evolved from its first version, z1, through to z6, with new capabilities added all the time amidst market competition. I examined the improvements made from z-machine version 3 to 5, including colors, status lines, file size, and finally – due to increases in RAM and file capacity, as well as more complex capabilities – the release of the new-parser of version 6. This included the ability to reference objects from other rooms, and style more complex input structures – such as three objects in one command, a feat I accomplished for my debut game, Milliways: the Restaurant at the End of the Universe.

In the following articles, I will be writing about my personal experiences in coding the 2023 IFComp game, Milliways: the Restaurant at the End of the Universe. But for you to understand anything I say, I’m going to have to teach you how ZIL works first.

CODE SYNTAX & FLOW CONTROL

To understand the complexities of ZIL, you’re going to need to know a bit about the very basics. As a start, ZIL syntax relies heavily on angle brackets (“<”, “>”), as it is based off of the programming language MDL, itself based off of LISP. Most of the time, you’ll find yourself using multiple layers of nested brackets in complex lists, in order to write sufficient code.

For people like me who spend a lot of time listing things, and enjoy neat order of lists and nesting, you will love ZIL. Flow control (i.e. if-else statements and iteration) relies on lots of nesting, sometimes reaching up to 5-8 layers just for a simple routine, and we will look at some examples of this in a few moments.

To begin with, we need to know some simple ZIL+MDL code functions. The very first thing to note is that in general, functions (known as ROUTINEs) are written inside matched angle brackets to the left and right. Any parameters that are passed go after the function, separated by nothing more than a space.

Before we begin, I would like to introduce the equivalent of true and false in ZIL. T by itself is the ZIL version of true, and a pair of empty angle brackets ( <> ) is the equivalent of false. This will come in very handy as you continue to use ZIL.

ROUTINE

This is the thing that you run throughout the game, and is essentially the flesh, muscles and blood of any game. Without them, nothing would be able to happen in the game.

All parameters go in a pair of parentheses after the routine name. Then everything after the parentheses are the instructions that are run. E.g.:

<ROUTINE KILL-ALIENS (X "AUX" MSG) 
     <COND (,ALIENS 
            <SET MSG "T-minus 2">) 
           (<=? .X 5 7 9> 
            <TELL "Run!!!" CR> 
            <SETG HARDCORE T>) 
           (T 
            <SETG ALIENS .X>)>>

Anything in the parameter list after "AUX" (with the quotation marks) are auxiliary variables, which are a form of local variable which are created to be used later, but aren’t passed as arguments when the routine is called. For example, when you run the routine later on in the game, you could write <KILL-ALIENS 5>, which sets X to 5 and runs the routine, but you never need to define the variable MSG there. Auxiliary variables are the equivalent of using “let MSG be nothing” to define a variable in Inform 7, except for the fact that you can’t just make variables on the fly. ZIL requires that you create local variables in the first parentheses.

SET & SETG

These set local (only in the current routine) and global (everywhere in the code) variables, respectively, to the value of the second argument. Then the second argument is returned. So:

<SET FAKE 7>

will make the local variable FAKE equal to 7 and then return 7 to the rest of the code, so you can use this command in a conditional statement, whereas:

<SETG ALIENS <>>

sets the global variable ALIENS to <> and returns <> (false). Another thing to note is that when retrieving the value of a local or global variable, you do so by putting “,” immediately before a global variable (e.g. ,ALIENS) and “.” immediately before a local variable (.FAKE).

COND

This is the general if statement. All if-else statements are each surrounded by parentheses (“()”). The first command in a pair of parentheses is the condition. The rest of the commands are the code being run if that evaluates to true. An example of a working condition statement is:

<COND (,ALIENS 
       <SET MSG "T-minus 2">) 
      (<=? .X 5 7 9> 
       <TELL "Run!!!" CR>) 
      (T 
       <SETG ALIENS .X>)>

Here, it sets the local variable MSG to the string "T-minus 2" if the global variable ALIENS is not false. Otherwise, if X equals 5, 7 or 9, prints "Run!!!"1 followed by a carriage return (“CR”), which ZIL does not automatically print. Otherwise, set (global) ALIENS to (local) X.

Notice the T in the last condition? Yes, although T means “true”, if you use it in a condition, it just returns true. In this way, it’s essentially a way of saying “else”.2

You will pick up other important bits in routines along the way, but those are the most important things you need to understand. I’ll come onto loops much, much later.

VERBS

Creating verbs come in two parts: definition (the syntax) and action (carrying it out).

The syntax part contains information on which words to use to call a verb. These are compile-time macros, meaning they can’t be inside routines – they are stored in static memory and can’t be created or changed during play. Creating one is fairly straightforward, actually:

<SYNTAX TAKE OBJECT = V-TAKE>

In the SYNTAX, there is the PRSA (TAKE), and the PRSO (OBJECT), then after the = sign is the name of the routine that will be run. In case you’re curious, PRSA stands for PaRSer Action, otherwise known as the verb. The others are PRSO (PaRSer Object) and PRSI (Indirect object, i.e. second noun – we’ll get onto this in a second), and all of these are also global variables that hold the current turn’s objects and routines. You can refer to in the code to identify which verbs and objects were used.

You can have a preposition in the syntax definition as well, which always has to be followed by an object definition. The second OBJECT here will be what the PRSI holds:

<SYNTAX PUT OBJECT ON OBJECT = V-PUT-ON>

You can also create synonyms:

<SYNONYM PUT PLACE THROW>

When writing SYNTAX definitions, the first argument is the word that you will write in all the verb definitions (so never write PLACE or THROW in verb definitions, only PUT! It can cause unnecessary problems otherwise3).

Now, on to the verbs:

<ROUTINE V-TAKE () 
     <COND (<IN? ,PRSO ,PLAYER> 
            <TELL "You're holding it!" CR>) 
           (<NOT <FSET? ,PRSO ,TAKEBIT>> 
            <TELL "You can't take that!" CR>) 
           (T 
            <MOVE ,PRSO ,PLAYER> 
            <TELL "Taken." CR>)>>

A very simple routine for V-TAKE (the real taking action is much more complex) checking if the player is holding the object – you can see this in the definition of <IN? ,PRSO ,PLAYER>, which literally means “is the PRSO inside the PLAYER?”. Failing that, if the thing is untakeable, comment on this – <NOT <FSET? ,PRSO ,TAKEBIT>> – which checks if the PRSO has not had the takeable flag (FSET means Flag-SET) set to it. Note, for later, that FSET sets a flag, whereas FSET? checks whether a flag is present. In this case, the TAKEBIT flag defines whether an object is takeable or not. If all else fails, move the PRSO to the PLAYER. One thing to know is that the verb routine must always start with “V-”. Just because.4

To make “Get What I Mean” verbs (it assumes what you are talking about if you don’t specify the object), you need to use parentheses after an OBJECT in the syntax definition. For example:

<SYNTAX TAKE OBJECT (ON-GROUND IN-ROOM) = V-TAKE>

All the tags you can use are: ON-GROUND, IN-ROOM, HELD, CARRIED, HAVE, TAKE and EVERYWHERE. My understanding of them is:

  • ON-GROUND and IN-ROOM mean similar things: anywhere in the game that is currently visible to the player, however ON-GROUND tends to prefer things not already held. Even so, you generally always use them together.
  • HELD and CARRIED are the same as the first point, but when you use this if there is a choice between an object on the floor and being held, it will choose being held.
  • HAVE and TAKE are used together to allow the game to use an implicit taking action if the object is not held – “(first taking …)”.
  • EVERYWHERE is a new-parser addition which means the object can be from anywhere in the game. I mentioned it briefly in the first article, as it only appears in the new-parser.

Later, we’ll come back to handling verbs, when we study objects. Speak of the devil, here we have…

OBJECTS & ROOMS

If routines are the flesh, muscles, and blood of a ZIL game, then the objects are the bones. You could, in theory, make a ZIL game without objects at all. In theory it would definitely work. But it would be a completely different sort of game. A different creature.

Again, objects are defined at compile-time, so you can’t create new ones on the fly during play, like you can in TADS. A well known example of an object is here:

<OBJECT BRASS-LANTERN 
    (LOC KITCHEN) 
    (DESC "brass lantern") 
    (LDESC "There is a brass lantern here.") 
    (SYNONYM LANTERN LAMP TORCH) 
    (ADJECTIVE BRASS METAL SMALL) 
    (FLAGS LIGHTBIT TAKEBIT)>

Every pair of parentheses contains first the name of the property (such as LDESC) and then the according value.

  • LOC is always followed by the room or object that contains it.
  • DESC is the string that the game uses to refer to the object. Although it looks like a property, it’s slightly weird in that DESC specifically can’t be changed during play. It’s also not technically a property, but it’s close enough that it can be considered one for all purposes.
  • LDESC doesn’t have to be used, but it allows an object to have its own line in a room description.
  • SYNONYM here is not the routine that you use in verb definitions. It lists all the nouns that are used to refer to the object. ADJECTIVE is the same, but with adjectives – in ZIL, synonyms, or nouns, can only be used as the final word in the object part of a command and everything else is an ADJECTIVE – for example, if you try to EXAMINE BRASS LANTERN, the word “BRASS” is an adjective, and so therefore is any word before the final in that object section, which would be “LANTERN”. Only one synonym is allowed per object part of a command. Neither of the two require you to list the words in any particular order: you can call it SMALL METAL LANTERN or METAL SMALL BRASS TORCH – but not TORCH BRASS nor BRASS, since BRASS isn’t under the SYNONYM property.
  • FLAGS is very important. This lists what capabilities the object has.
    • LIGHTBIT in an object means it can be turned on and off.
    • ONBIT means it actually is producing light/turned on.
    • TAKEBIT means you can pick up the object.
    • OPENBIT means the object is open.
    • CONTBIT means it is a container.
    • TRANSBIT means the container is transparent.
    • INVISIBLE is the only flag bit that doesn’t end in BIT – it means it is invisible.

There are many more, which you can find by studying ZIL code. Many are specific to certain games. Earlier I mentioned FSET? under the verbs section. Well, here’s it’s whole family. You can add a flag to an object during runtime using <FSET ,[obj] ,[flag]>;5 you can remove using the same syntax with FCLEAR; and check whether objects contain a certain flag using FSET? – the question mark is important. This routine only allows one object and one flag parameter each.

Rooms are similar, though some other properties can be added. They are fairly straightforward, actually. See:

<ROOM KITCHEN 
    (LOC ROOMS) 
    (DESC "Kitchen") 
    (LDESC "This is your kitchen, where you cook and eat.")
    (NORTH TO BEDROOM)
    (SE PER OPEN-ALCOVE) 
    (FLAGS LIGHTBIT ONBIT)>
  • LOC should always be ROOMS in a room.
  • LDESC is for room description here.
  • All the directions (which will be defined somewhere at the start of the game) can be used here. The parameters that can be passed are:
    • TO [rm] – move player to room [rm]. Don’t use the comma syntax on objects here.
    • PER [rtn] – run the routine [rtn], and if anything is returned (using <RETURN ,KITCHEN> for example), move them to that room – otherwise (if false is returned using <RFALSE>) nothing happens. You should never return true in a movement routine (<RTRUE>).
    • SORRY "[str]" – do nothing but print the string [str].
    • TO [rm] IF [var] – if global variable [var] is true (or at least non-zero), move the player to room [rm]. You can add ELSE "[str]" to the end of that to make it function like SORRY if it fails.
    • TO [rm] IF [door] IS OPEN – fairly self-explanatory. You can also add ELSE "[str]" here.
  • FLAGS is important, as LIGHTBIT and ONBIT should always be set in a non-dark room.

Here’s a basic example of the (PER [RTN]) counterpart routine in action, using the (SE PER OPEN-ALCOVE) from the example room above:

<ROUTINE OPEN-ALCOVE () 
     <COND (<FSET? ,ALCOVE ,ONBIT> 
            <RETURN ,ALCOVE>)
           (T
            <TELL "It’s too dark over there." CR>
            <RFALSE>)>>

Objects, rooms and flags can be referred to in routines using the “,” before the name, just like in global variables and constants, but not if you’re in a ROOM definition (anything physically inside the <ROOM> code).

One more thing regarding objects and rooms. Now you’ve got the objects, and rooms, and the like, but we need to know how you interact with objects: how to implement them. And it doesn’t require editing the verb routine.

Unlike in Inform where all rules (such as “Instead of” or “Carry out”) exist separately from the objects and verbs, in ZIL you must go through the objects or verbs. Every object and room can have its own routine attached, so when a verb is going to be carried out, first it’ll run the PRSI’s routine, and if nothing happens there, it runs the PRSO’s routine. If that fails, it runs the PRSA routine (such as the V-TAKE routine I showed earlier).

You can set up an object’s routine through its object definition. At the end of the brass lantern object definition, we’re going to edit it:

... 
    (FLAGS LIGHTBIT TAKEBIT) 
    (ACTION BRASS-LANTERN-F)>

Then we define a mini routine:

<ROUTINE BRASS-LANTERN-F ()
     <COND (<VERB? EXAMINE>
            <TELL "The brass lantern is small and is turned ">
            <COND (<FSET? ,BRASS-LANTERN ,ONBIT>
                   <TELL "on">)
                  (T
                   <TELL "off">)>
            <TELL "." CR>)>>

Note that the VERB? condition must take the exact name of the verb routine (but take off the “V-”). If you have a verb that has both a PRSO and PRSI, you can check whether this is the right object by using <PRSO? ,[obj] (restofobjs…)> and <PRSI? ,[obj] (restofobjs…)>. It will return true only if one of the objects matches either the PRSI or PRSO (respectively).

A lot of times, you’ll have many objects that are similar in functionality, so you can use a generic object routine called something like “FROBS-F” to give a similar response for your routine, and then you can get more specific using the PRSO? and PRSI? routines, which check the PRSO or PRSI for one of the arguments passed, respectively. It saves time, storage, and if you want to change something, you can change all in one go.

Room routines are used if you want an adaptive room description or something specific to the room you’re in, such as entering or leaving the room in general (instead of specific to one exit, which you can do with PER). I’m not going to go too deep into it, but it’s easier for me to show you than it is to explain:

<ROUTINE KITCHEN-F ("OPT" (RARG <>))
     <COND (<=? .RARG ,M-LOOK>
            <TELL "This kitchen is very small, save
for a large cupboard in the other corner which is ">
            <COND (<FSET? ,CUPBOARD ,OPENBIT>
                   <TELL "open">)
                  (T
                   <TELL "closed">)>
            <TELL "." CR>
            <RTRUE>)>>

There’s a lot that’s new here, but in summary, ("OPT" (RARG <>)) creates an optional parameter that either is set when the routine is run (as would a normal argument), or is set to <>. In the case that the player tries to look around, this routine will be called with the global variable ,M-LOOK as a parameter. The only use for this parameter is to differentiate between looking and others, such as M-ENTER and M-EXIT. The actual value of M-LOOK is not important, as long as none of the others have the same value.

NEW-PARSER SPECIFICS

In Part 1, I studied how version 6 introduced the new-parser for ZIL. After Infocom closed and the z6 games stopped being created, the new parser went unused as it was even less documented than normal ZIL, which was already barely documented. As well as that, the new parser required some work to get fixed for more public use, since there were some significant bugs in the code that went unnoticed. The few ZIL games that have been released since the end of Infocom have mostly been choice games (by an author named “SD Separa” on IFDB), as well as a few smaller parser games – but again, none using the new-parser. All of that is until Milliways, but that’s for later.

The new parser games have the EVERYWHERE syntax flag, which is an addition to the grammar area. The grammar area defines the different command structures (eg. “JEFF, OPEN THE BOX” vs “OPEN THE BOX” vs “OPEN THE BOX WITH THE GLOVES”). The non-new-parser (such as the – ironically – newer ZILF parser, which is a cleanup of the pre-z6 Infocom old parser) doesn’t have the exact same design on identifying these structures, but it is similar. It’s a very confusing area that even I haven’t fully sorted through, but I’ve gained enough knowledge to understand what each part was doing and where to look for whatever I need.

So far, we have studied objects, routines and basic syntax, flow control to a very limited extent, and verbs. We’re off to a pretty good start – you have enough knowledge now for the next part, in which I will explore some of Milliways’s code and its alternatives in other coding languages. See you next time.

  1. When typing strings like “Run!!!”, curly quotes are not recognised by ZILF, so best to use something like Visual Studio Code so that problem doesn’t arise (which also includes a ZILF extension with bracket highlighting, so it’s highly recommended). ↩
  2. ZIL also understands ELSE in place of T specifically in conditional statements, but I just don’t use it because T is faster to type. ↩
  3. There are really easy workarounds, such as checking in the routine whether you used the right verb, and if not, rejecting the command. But there are always workarounds. ↩
  4. Because when the parser is correlating input to verbs, verbs are defined by their routine name, and the requirement (in simplified terms) is that it begins with “V-”. ↩
  5. Just to clarify: you place a comma before a global variable, an object, or a flag – and a full stop before a local variable. I placed a command before each in the example as it felt the most common use in that case. ↩

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.