The people involved with today’s game are Nick Hampshire and Carl Graham, both who have important parts to play in the history of technology. Nick Hampshire’s early college experiences (1970-1975) bounced around a bit as he went from Zoology to Computer Science and moved between college studies in England, Sweden, and the United States. He […]
11 hours ago
The people involved with today’s game are Nick Hampshire and Carl Graham, both who have important parts to play in the history of technology.
Nick Hampshire’s early college experiences (1970-1975) bounced around a bit as he went from Zoology to Computer Science and moved between college studies in England, Sweden, and the United States. He followed this with some technology work in San Francisco; using the S-100 bus (the backbone of the Altair and many other early computers) he made his own 8-bit computer he called the System 8, then went back to the UK to try to sell it in 1977.
From Personal Computer World, August 1978.
In order to promote his new system (and other S-100 bus systems he became a dealer of), he made his own magazine he called Computabits. This ended up determining his life trajectory as the System 8 completely failed (I can’t even find it even mentioned in any of the usual “museum of obscure computers” sites) but Computabits as a magazine did quite well for itself, being swept into the early magazine Practical Computing as a “magazine within a magazine” starting in its second issue; Hampshire was later listed as “technical editor” and was set on his career as a writer. He ended up writing 9 books between 1979 and 1984, some being (relative to the market) best-sellers; Vic Revealed made it to 400,000 copies worldwide.
ASIDE: Of his early Computabits articles, the most relevant for today is one he wrote on “epic games” where he discusses textual compression techniques (similar to those Infocom and Level 9 would come up with) like using single characters to represent whole words. He also theorizes about extending the “epic game” into an “electronic novel” where “no longer does the reader identify himself with the hero — he or she is now the hero.” Jack Pick read the article and inquired for further help on text compression, trying to apply this to his mainframe game Adventure II. (Also, keep in mind that February 1979 issue, as we’ll come back to it for something else later.)
Mr. Hampshire still tried his hand at hardware for the Commodore PET, but really had more success with his publishing ventures, including the launch of Commodore Computing International in 1982.
One of the employees throughout 1982-1985 was Carl Graham, listed as a “programmer”; he was a “staff writer” in the sense of being focused on writing type-in code for Commodore BASIC to be printed in each issue. Carl Graham himself was also quite hardware-minded, with earlier projects starting in the 70s including rewiring a Texas Instruments calculator in to be a digital timer (“looking at thin films of water breaking up into beads on glass using photo sensors to measure propagation speed”), making a Z80 assembler in BASIC, and and making his own plotter for his TRS-80.
Today’s game, On the Way to the Interview, is a type-in starting on page 14 of the magazine above where the article (not game) title is Anatomy of an Adventure, another one of the articles allegedly teaching how to make an adventure game. This sometimes is an excuse for a sub-par adventure game. This one is just … unusual.
It is written in Commodore BASIC and supports the line in general (PET, VIC-20, C64), although I ended up playing the BBC Micro port in the Nick Hampshire-edited book BBC Programs Volume 1.
Your goal: make your way through streets to your scheduled interview at Slave Driver International. Your starting inventory: a CHEQUE, presumably from your last job. Perhaps winning is losing in this game.
The graphics shown are an automap. The reason the game can do this is the entire thing is oriented on a grid.
Even more unusually, the way the game’s data is stored is by roads. That is, each road is listed and the elements of that road are listed in sequence, but from the player’s perspective a road is a whole sequence of rooms.
There are nearly no puzzles. If Pythonesque and Mad Martha are British degenerate games (where one goes about an urban environment causing havoc) this is an anti-degenerate game. The goal is to be a “responsible citizen”. The route — starting at 24 on the map, winding over to the northeast — consists mainly of things you need to ignore, lest you throw off this title. For example, early on there is a shopping bag you pass by, and it will kill you.
The game keeps track of minutes passing in real time. You need to make it to the interview in less than 7 minutes. With this screenshot, I had left the emulator running overnight.
Later there is a TRAMP. Simply talking to the tramp (ASK TRAMP) ends the game. (“I’ve just had a bad experience with a tramp.”)
Just a bit farther is a GUN, which has all sorts of game-ending possibilities. First off, you can simply try backtracking and shooting the tramp, but it doesn’t even let you operate the gun (“I’m not having anything to do with shooting things.”)
There’s a policewoman later that will arrest you if you’re holding the gun in the open. You can just have the gun randomly go off while holding it. There’s a bank along the way where if you step in with a gun they’ll think you’re doing an armed robbery.
The right thing to do in the bank is, responsibly, CASH CHEQUE (yet another “isolate” verb I haven’t seen yet, but I had my eye on the source code by now in my playthrough for reasons I’ll get into). This gives you MONEY, and lets you later pass through a private park.
Once through the park, it’s a straight shot to the interview, making a beeline for the northeast corner of the map.
Ending, with an exit to the operating system. You don’t even find out the result.
Here’s the full route, with the side trip to the bank marked in green:
Again, this is a “learning game” so there’s some latitude for simplicity, and the article actually does try to make the layout of the source code crystal clear. It’s still a very curious model given that most action besides movement is bad, really, and how many adventure games are on grids like this? (Very, very, few. Even Asylum and friends use “razor walls” as opposed to having a grid with blocks filled in, also known as “worm tunnels”. The only other example we’ve had here is the unfinished/unpublished game Castle Fantasy.)
I still haven’t gotten yet into the worst part of the game. Every once in a while, at random, you get run over by a bus.
It doesn’t matter if you are on a road, in a park, or in a police station: the bus will come. There is absolutely nothing you can do about the bus. This is essentially a slot machine game. Perhaps the RNG on Commodore is better, but on the BBC Micro version, I could not beat the game: I consistently got run over before reaching the park. I had to cheat by changing the source code and modifying my starting location. This admittedly enhances the feeling of being hapless (as opposed to Fighting the Machine) but is a deeply odd choice to make for a learning game.
We’ll see both authors again in 1984, as there was adventure custom-written for the BBC book (one that looks on the face of it slightly more traditional, but I know never to trust Britgames to stick to normal). As that will take some time to reach, I should mention both had distinguished careers after; Hampshire kept writing books and doing journalism, as he “contributed to magazines such as Personal Computer World, PC Magazine, Byte, Interface Age, as well as newspapers such as the Times, FT, Telegraph, and Mail”. While Graham left games for a while (making database software from 85-90) he eventually picked up a job at Argonaut.
You may know them better as the makers of the FX Chip for Super Nintendo games; Graham was a coder on the original Star Fox and has a long list of credits after.
Regarding the unusual grid, I do have one suspicion where it might come from. I said I would come back to the February 1979 issue of Practical Computing. Earlier in the same magazine there is an article by T. J. Radford which doesn’t present a full game, just an “idea” which spans two pages (early computer magazines sometimes had this, I assume they had trouble filling space). It involves creating a D&D-type map as part of the theorizing:
Long shot, I know, but it gives me the same crossword-grid-like feel, and I wonder if Hampshire internalized it as a possible way to make an adventure game map.
Coming up: The somewhat-related but much more substantial escape-from-British-suburbia game, Urban Upstart.
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 ...
2 days ago
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.
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?
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.
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 IFhistory 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?
(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” […]
4 days ago
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!
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.
(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 […]
5 days ago
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 basics of coding in Infocom's ZIL: Zork Implementation Language
6 days ago
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.:
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:
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).
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:
<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.
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). ︎
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. ︎
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. ︎
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-”. ︎
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. ︎
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
7 days ago
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.
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 […]
8 days ago
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.
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).
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.
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
11 days ago
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 here! Specters of the Deep will release Thursday, September 25th.
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 […]
14 days ago
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.
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.
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
(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. […]
16 days ago
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.”
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
a day ago
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.
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 […]
3 days ago
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 Bluhhh! 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 Heroes II. 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 Fallout. Might 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.
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
4 days ago
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%.
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.
6 days ago
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.
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 ...
7 days ago
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 ...
8 days ago
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.
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.
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
8 days ago
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.
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 […]
11 days ago
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.
(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…
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.
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
13 days ago
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:
Initial world construction - Authors need to populate containers regardless of their open/closed state
Save/load operations - Restoring world state should bypass gameplay rules
Special game mechanics - Magic, teleportation, or debug commands may need to bypass normal restrictions
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:
WorldModel - Enforces game rules, generates events, used during gameplay
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
Shared State: Both models reference the same SpatialIndex and EntityStore instances
No Events in AuthorModel: Author operations never generate events or trigger handlers
No Validation in AuthorModel: Author operations bypass all game rule checks
Clear Separation: Different import paths make the distinction explicit
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:
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
14 days ago
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:
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:
Ambiguity: No clear distinction between entity IDs and display names
Collision Risk: Multiple entities could accidentally use the same ID
Author Experience: Future Forge authors will want to use meaningful names, not manage IDs
Reference Resolution: Exits and doors reference other entities by string, unclear if ID or name
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')
Separation of Concerns Works: The existing architecture already separated names (for users) from IDs (for system), making the refactor smooth.
Command System Unchanged: The CommandValidator already used entity.name for resolution and entity.id for operations, so no changes needed.
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
Update entity creation calls
Use entity references or name resolution
Update test assertions
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.
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 ...
17 days ago
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
(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.
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.
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 writewrappers 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.
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
16 days ago
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