Mystery House, Excavated


Preamble

For this project I wanted to submerge myself in not simply "rebuilding" a piece of historically significant software, but to really dig into the process of building that software accurately. In Mystery House we have a piece of software whose source code has been lost to time. To my way of thinking, understanding its origins deeply during the building process necessarily requires trying to overcome similar challenges to the ones Ken and Roberta Williams faced. (see below "Accurate Remake")

Further, I wanted the rebuilding process to reveal new (?) information about how the game works, and to contribute that knowledge back to the interactive fiction and software archaeology communities.

As is typical for me, I chose the Pico-8 as my target platform. It's a great tool for code exploration, offering a quick, simple way to get up and running on a modern system with tools similar to those of older, classic machines. In particular for this project, the Pico-8 ROM storage limitations require of the developer similarly creative approaches to storing data that the Williams's faced with the Apple 2 floppy disk. It is easier to put oneself into a "developer for classic machines" mindset without also having to go through the rigor of learning 6502 assembly.

One note: I bring up the Mystery House Taken Over project many times. I want to be clear that my references to MHTO are not in the spirit of "look how much more accurate my version is" but in the spirit of cross-comparing our approaches. MHTO brought Mystery House into the Inform era, opening the door to easy adapations and alterations. My project intends to be much more of a slave to the original. Additionally, this project benefits from an analysis that didn't exist when MHTO was done, so I have quite a bit of extra knowledge to build my version upon.

What Does "Accurate Remake" Even Mean?

What do I mean when I say I wanted to build an "accurate" recreation of the original game? This rebuild, and really any rebuild/port that doesn't occur in 6502 assembly on an Apple 2, can only ever strive to achieve "accuracy" in a spiritual sense. Without having much more precise information extracted from the compiled original (which may yet be possible) certain internal mechanisms can only be guessed at. I think many of my guesses are accurate, while others need to be refined. I discuss those matters later in this post.

As a simple example, consider the target platform. In choosing the Pico-8 I am already crossing an "accuracy" boundary by sheer pixel resolution alone! The original used 256px-wide "hi-res" graphics and the Pico-8 is limited to 128px horizontal. I found it useful to redraw all images from scratch using a custom illustration tool built just for this project, Versawriter-8. The drawing process allowed me to be "accurate" to the process used to build the original (as the Williams's did on the Apple 2 Versawriter), but the artwork can only ever be a facsimile.

Comparison of original Apple 2 Mystery House opening scene with the Pico-8 remake.
Is this "accurate?"

My approach to "accuracy" basically adhered to the following philosophy:

  1. Can I implement my version in such a way that the same bugs occur for manifestly similar reasons?
  2. In-game text must be identical, including typos.
  3. If I need to change something for technical reasons, am I keeping in the spirit of the choices made during the production of the original?
  4. Because the original is tied so directly to the Apple 2 hardware, which hardware quirks contributed to the feeling of the software, and can/should those be brought over to this port? In other words, with software written in machine language, how/when shall we separate "machine" from "language"?

Bug Preservation

A primary goal in reverse-engineering Mystery House is the preservation of bugs. There are two approaches we can take to bug preservation:

  1. Observe a bug in the original and add code to simulates that bug in the remake
  2. Implement solutions to problems in such a way that the same bug must *naturally* occur for the same reasons.

#1 is easy enough to do, but at the expense of bloating the code with a lot of hyper-specific checks and. This requires a huge amount of testing in the original game, finding the bugs, figuring out exactly when they occur, and coding a hack that simulates things.

#2 is harder, but also kind of easier. If we implement our version "accurately," then we should be able to replicate entire CLASSES of bugs without having to worry about each one on an individual basis. However, this also means that we need to choose our implementations carefully, considering what was possible at the time of original source code creation, and finding the most accurate representation on our target platform.



How Mystery House Works

This section is subject to change as new information comes to light.

Title Screen

I retypeset the original splashscreen at native Pico-8 resolution, keeping in mind font availability in the years 1979-1980. I believe this set to be accurate: Gilles Gothic, Brush Script, and Charter Oak. Of course "fonts" weren't available to the Apple 2, but the splashscreen is clearly presenting a bitmap representation of such fonts.


Object Scoping (basically doesn't exist)

Mystery House doesn't have the concept of "scope" per se. This can be proven at the start of the game very easily by "LOOK <NOUN>" on anything in the game. If the noun matches the vocabulary list, you will get "THERE IS NOTHING SPECIAL" even if that object is nowhere to be seen. Unknown nouns will receive a "I DONT KNOW HOW TO LOOK A <NOUN>". If a different response is seen (for example "LOOK NOTE"), that means a unique response was explicitly coded for the current conditions and is not the result of any general "scope handling" routine. This causes any number of bugs in the game.

"GET" or "TAKE" or "LOOK" on an object does, at the very least, check for object availability in a given room, but this is very primitive. For example, if a NOTE is in the room and the player is not carrying any NOTE, "READ NOTE" will not understand that this action refers to the one in the room. "READ" action only works "by default" if the player is explicitly carrying a NOTE or if the room has provided specific responses to READ something else (as in the Cemetary). Some simple rules could be written to allow for this action, but those rules do not exist in the game.

In the Mystery House Taken Over project, which ported Mystery House to the Inform language, we see comments that talk to the concept of bug preservation. For example, with the gun object definition we see this comment:

!; NB: this is a bug preserved from the original game. Even when
!; the gun has been shot, it is described as containing one bullet.

In this particular case the Inform solution closely resembles the original bug, which is revealed to us through the work of Inspecting the Foundation of Mystery House. In the file "dumpgame.out",a kind of psuedo-code translation of the executing machine language, we see that the parser simply checks

if VERB = "LOOK" and NOUN = "GUN" then
print "THERE IS ONE BULLET IN THE GUN"

There is no VAR check to see if the gun has been shot (a VAR which does exist!). There is no inventory check to see if the gun even exists in the same location as the player. It is just a generic response to "LOOK GUN" and playing the game proves this to be true.

Where the Inform and Pico-8 implementations differ is in the side-effects of their respective implementations. Inform, by default, keeps object "scope" in mind at all times and overriding that scope check requires extra effort. "LOOK GUN" in the Inform version will return "THERE IS NOTHING SPECIAL" which is the default Mystery House response against default Inform scoping. In this case, Inform is being a little too smart.

In implementing the Pico-8 version, the parser is written to be as "dumb" as the original, which was akin to a series of if/then checks which fall through to the next condition that might catch it. "Scope" in the original was something that needed to be checked explicitly at the room level (usually). If an override doesn't explicitly implement its own scope check then scope basically never comes into play. If we "LOOK GUN" in this rebuild we get the proper "THERE IS ONE BULLET IN THE GUN" response.

Inform version running in Glulxe, original version running in an Apple 2 emulator, Pico-8 version; who needs scope anyway?

Another interesting case with scoping is with the picture/button and fire/hole elements. It might not be obvious, but because scope doesn't exist in the general sense we can exploit that fact to bypass some actions. In the Study, "PRESS BUTTON" will always work, no butterknife required. In the Dining Room, "LOOK HOLE" will always work, no fire or pitcher of water required.

Rooms are King

Again we turn to "dumpgame.out" to see how the game logic is arranged. What we find is a structure where the current room is always given the first shot at handling player actions, after which control is expected to "fall through" to the generic handlers, which use the verb as a primary key.

What this means in practice is that if a room has a condition which overrides the generic handlers only under certain circumstances those conditions must be carefully written to not block the natural fall-through to the generic handlers. This is the source of any number of bugs in the game; here's two.

Teleporting jewels

In the logic for the basement we see the following

if ROOM = 21 and VERB = "GET" and NOUN = "BRICKS" and OBJECT "BRICKS" IN ROOM 21 then
    print "YOU HAVE FOUND THE JEWELS!"
    take (NOUN)
    object JEWELS (32) location = 21

If the room is #21 (the basement) and the object "BRICKS" are in that room, then "GET" will reveal the object "JEWELS". Notice the only condition for finding the jewels is that the "BRICKS" are in the room. There is no check (nor VAR) for "the jewels were discovered." What this suggests, and in practice actually happens, is that wherever the jewels are in the game world, so long as the bricks are in the basement (even by "DROP BRICKS"), picking up the bricks again will reveal the jewels again, teleporting them to the basement.

This is a case where the room logic has superceded the normal "GET" flow without much care given to what the player might do *after* finding the jewels for the first time. This pattern of logic, which supposes that players will only do things in a very specific order ("Why would anyone GET BRICKS a second time?") is prevalent throughout the game (see above section regarding picture/button and fire/hole).

Teleporting shovel

A similar bug exists with the "GET SHOVEL" command when at the cemetary. The following appears to suggest that if Joe is alive (VAR 7 = 0) then he'll stop us from taking the shovel. Otherwise... ?

if ROOM = 18 and VERB = "GET" and NOUN = "SHOVEL" and VAR 7 = 0 then     
    print "JOE WON'T LET YOU" 
if ROOM = 18 and VERB = "GET" and NOUN = "SHOVEL" then
    carry object SHOVEL (28))

Well it certainly seems to suggest that the shovel will teleport into our inventory as long as Joe is dead. This is true. We can verify this by taking the shovel (after Joe is dead), dropping it anywhere in the game map, then going to the cemetary and running "GET SHOVEL." The shovel will pop into your inventory.

Verbs and Verbosity

It may seem that I rely a lot on "dumpgame.out" to verify things. I have found it to be accurate enough that one can read through, use deductive reasoning to predict bugs, and verify those bugs exist in the original game. The following example illustrates the case which strengthened my trust in that file.

Schrodinger's Entry Hall

At the start of the game, when entering the house for the first time, people are shown in the Entry Hall. If the player leaves the entry hall and returns, the people are gone. Looking at the "gamedump.out" file, we see something unusual

if ROOM = 3 and VERB = "U" and NOUN = "STAIRS" then
   goto room 24
if ROOM = 3 and VERB = "U" and NOUN = "" then
   set room images to 4
   goto room 24

This says that if the player types "UP" the room image will swap to "room image 4" (the entry hall, minus the people). But if the player types "UP STAIRS" the room image is NOT swapped out. This bug does in fact exist. Going "UP STAIRS" then back "DOWN" continues to errantly show the Entry Hall full of people, even if you go upstairs and see dead bodies lying about.

In fact, even the behavior for interacting with the people changes. "LOOK TOM" will first complain "THE PEOPLE WERE DESCRIBED AT THE BEGINNING OF THE GAME." Leaving the room, coming back to an empty room, then "LOOK TOM" results in the normal (though inaccurate) "THERE IS NOTHING SPECIAL".

If we exploit the room image bug, we see a room full of people but we still receive the "THERE IS NOTHING SPECIAL" message, not the "PEOPLE WERE DESCRIBED" complaint we might otherwise expect. This behavior is preserved for this rebuild.

So what we learn here is that "UP" as a concept means nothing. It would be easy to assume that going "UP" no matter the specific words typed, would result in the same behavior for the same verb. Here we see that every individual combination of words must be explicitly accounted for, even at the sake of code verbosity. The Pico-8 version strives to do exactly as the original and does properly contain this bug.

Notice also that in this code snippet "goto room 24" had to be explicitly written, even though that room has a general set of exits (including up) defined for it and there is a general room-to-room traversal mechanism built into the game. As mentioned above under "Rooms Are King," overrides at the room level must also do scoping work that would normally be handled later in the code. Here we see that it is not just scoping, but *ALL* work necessary must be explicitly accounted for, including even the simple act of setting the new room number.

Killed by a dead woman

Mishandled verbs are a definite source of bugs. The most interesting one I found was how Daisy can kill the player even after being killed herself. "KILL DAISY" results in the prompt "WITH WHAT?" to which the player may respond in four ways:

  • WITH SLEDGEHAMMER
  • WITH BUTTERKNIFE
  • WITH DAGGER
  • WITH GUN

Only the gun will kill Daisy (a necessary win condition), and trying to shoot her again with the gun results in "SHE IS ALREADY DEAD". Trying "WITH DAGGER" after shooting her has the same response.

However, the butterknife and sledgehammer were NOT accounted for in the "WITH" verb logic and trying "WITH HAMMER" or "WITH BUTTERKNIFE" after Daisy is dead will cause the player to die and the game to end. Again, the concept of some kind of "generic" handling of verbs simply does not exist. The room has control over the verb handling and is expected to handle ALL conditions and combinations of conditions for the verb "WITH" but a couple were overlooked, leading to this funny bug.

A tragedy in three acts.

Inventory and Objects

All objects, both takeable and not, are assigned a number, and this numeric order is strictly preserved when listing objects, whether it is in the player inventory or drawn to a room. Whatever subset of objects you have in your inventory, they will always display in the assigned numerical order, no matter what order you add them inventory. Whatever objects you drop into a room, no matter the order, will always display in that numerical order.

Three states

Looking through the memory dump, I've been able to extract an additional detail about objects. They contain a value at object address + 7 bytes that starts as one of two values for all objects:

  • 0 : scenery
  • 1 : (standard inventory; items must transition to this state from 0 first)
  • 2 : static

When an object is taken or dropped, this value is flipped to 1. Taking should be enough, but there are times in the game where an inventory object is given directly to the player, rather than going through the default "GET" routine. This seems to bypass setting the value from 0 to 1, so the same value is set on "DROP" (appears to be a failsafe) to ensure an object is never returned to scenery status again.

Chaos from order

The takeable object list is as follows (extracted from the original game memory dump)

OBJECTS
#9    NOTE at location 3, (x,y) = (41,126), picture 4
    "A NOTE"
#13    MATCHES at location 4, (x,y) = (94,118), picture 6
    "MATCHES"
#14    PITCHER at location 4, (x,y) = (75,107), picture 58
    "AN EMPTY PITCHER"
#15    PITCHER at location 253, (x,y) = (0,0), picture 59
    "A PITCHER FULL OF WATER"
#16    BUTTERKN at location 4, (x,y) = (125,66), picture 60
    "A BUTTERKNIFE"
#17    NOTE at location 14, (x,y) = (61,79), picture 4
    "A NOTE"
#18    CANDLE at location 253, (x,y) = (0,0), picture 61
    "A LIT CANDLE"
#19    CANDLE at location 16, (x,y) = (102,75), picture 62
    "AN UNLIT CANDLE"
#22    KEY at location 16, (x,y) = (107,59), picture 65
    "A SMALL KEY"
#28    SHOVEL at location 18, (x,y) = (49,97), picture 71
    "A SHOVEL"
#30    KEY at location 21, (x,y) = (175,83), picture 73
    "A SKELETON KEY"
#31    BRICKS at location 253, (x,y) = (162,27), picture 74
    "A BRICK"
#32    JEWELS at location 253, (x,y) = (162,27), picture 75
    "JEWELS"
#35    NOTE at location 32, (x,y) = (91,82), picture 4
    "A NOTE"
#36    PICTURE at location 35, (x,y) = (63,10), picture 79
    "A PICTURE"
#39    TOWEL at location 38, (x,y) = (127,24), picture 82
    "A TOWEL"
#42    SLEDGEHA at location 39, (x,y) = (102,124), picture 85
    "A SLEDGEHAMMER"
#45    GUN at location 40, (x,y) = (104,61), picture 88
    "A GUN"
#49    NOTE at location 41, (x,y) = (99,103), picture 4
    "A NOTE"
#53    DAGGER at location 26, (x,y) = (171,86), picture 95
    "A DAGGER"

Here's how my inventory looks, after wandering around being a pack-rat. We can see clearly that the player inventory is displayed in the same order as the object number assigned to it. (I am leaving all NOTE objects off this list to illuminate the ordering more clearly)


These same objects, when dropped into a room, don't appear to match this ordering, but this is only because the underlying mechanism for arranging objects obscures it. Here's an illustration of what a player may reasonably expect to happen vs. the confusing reality of what actually happens, as a result of this ordering.


There is a fixed set of drop coordinates and objects are placed into the first ordered drop slot. So if we drop objects 39, 9, and 16. Those will fill slots 1, 2, and 3 in numerical order such that slot 1 receives object 9, slot 2 receives object 16, and slot 3 receives object 39. We can reveal these drop coordinates with square-ish objects in the room. I have also added the numbering system to the slots.


Slot positions begin at the x == 0 mark and each slot is 32 pixels (x == 0, x == 32, x == 64, etc...) to the right of the previous one. If we overlay these slots onto the pitcher drop inventory, comparing the slot ordering with the object number ordering makes drop location predictable.


Some things to draw your attention toward:

  • Object sizes are not restricted to the size of the drop slots. Note that many of them extend beyond that slot space; this explains the overlapping cluttered look of the drop inventory.
  • Notice that some objects like the NOTEs (see first grid image, slots 1, 3, 6) are aligned at the relative "0,0" upper-left corner of the slot. Other objects are aligned with the "1,1" corner of the slot. (see second grid image, slots 7, 8)
  • We can count through the slot numbers and see that slot order matches the object definition order precisely.

This suggests that every time a listing of objects is required, the full list of all objects is iterated, one at a time in order, checking for a room (or player inventory) number match and is simply listed in that order.

UPDATE: I have all but confirmed this by looking through a capture of the memory state during gameplay. There is a specific developer comment that pops up during object manipulation events. "LOOP THRU OBJECT TABLE"

Peeking into Apple 2 memory during runtime. Interesting comments.

A note about "A Note of Caution" about carrying NOTEs

In the "Instructions" for the game, the player is warned

a note of caution: carrying more than
one note may be confusing as the 
computer will arbitrarily decide which
one to read or drop.

The use of "arbitrary" is not true. Given the strict use of the object ordering we can now see that what *actually* happens upon "GET NOTE" or "READ NOTE" or "DROP NOTE" is that the first numerically ordered note in the targeted inventory is gotten/read/dropped. The two keys are the same. It can "feel" arbitrary to the player, especially when the room inventory layout seems so bewildering ("I dropped the candle and everything moved around!") but there is a very strict, simple set of ordering rules at play.

The loooooooooooong gun

With the room inventory above we know that there are 7 drop slots on each row of the display, where x == <multiple of 32>. We also know that some objects are wider than 32 pixels. We also know that some objects are aligned to the true upper-left corner of a given spot and others are drawn with a 1 pixel inset. Discussed later, but a point of some fame in the original implementation, is that room graphics are restricted to 256 pixels wide for the sake of keeping x,y coordinates to single-byte values.

Knowing these things, we can now understand the long gun bug.


It's a little hard to see, but the gun is drawn to be exactly 64 pixels wide. You can see this in a previous image, grid squares 10 & 11, where the handle of the gun is not obscured by the grid lines, but the tip of the barrel is (foreshadowing for the bug). So a 64 pixel-wide gun is placed at drop position where x == 192. In a 0,0 coordinate system, as the Apple 2 uses (and the Pico-8) the rightmost tip of the gun barrel should position itself at x == 255.

However, recall that some objects are positioned with a relative 1 pixel inset (like the towel). The gun is one of these objects (this is why we see the leftmost pixels of the handle even with the grid overlay). This forces the gun to be 65 pixels wide and pushes the rightmost points that define the tip of the gun into x == 256.

All room graphics are constrained to 256 pixels wide, meaning x <= 255. What happens when an object is asked to be drawn on screen at an offset that causes its points to exceed x == 255? x is clipped to 0. With the gun barrel now drawing part of itself to the x == 0 position, this forces it to draw <em>backward</em> across the screen resulting in the looooooong gun.

In this Pico-8 implementation I maintain the Pico-8 128 pixel limit by peforming x &= 0x7FFF to clip the bits that exceed value 127. I suspect something similar is happening within the Apple 2 implementation as bit manipulation like this would be easier and faster in machine language than modulus functions. As such, this drawing bug occurs in the Pico-8 version for the (manifestly) same reason as the original.

This reveals to us that not only were room graphics drawn to stay within the one-byte x,y coordinate limitation, but the drawing routine itself doesn't allow for values to exceed 255, even though the Apple 2 hi-res display could show points with x up to 279. For DEFINING images it makes perfect sense to enforce a 255 maximum value. But for DRAWING images I am unclear exactly why Ken felt it necessary to enforce the same limit. 

My theory is that it acted as a kind of visual debugging for the room images. If a room drew itself oddly (similar to the error we see with the gun), then that would mean something was wrong with the picture definition and it needed to be redone, perhaps even redrawn from scratch (see "Why Draw Once..." below). I theorize this because that is what I used it for; it proved to help ME while putting this together. I found a few rooms where I had moved lines beyond the Pico-8 draw area and the draw bug helped me find those. The Pico-8 doesn't care about those cases, but it was an interesting side effect of trying to replicate the original development process.

All Objects Are Addressable (even the secret ones)

All objects have nouns attached to them, and these are used to manipulate room state in certain cases. There are four objects called "DOTS" (one for Joe, one for Daisy) and "X's" (one for Joe, one for Daisy) in the original game file. These are used to draw a body in the cemetary or tower as being either alive ("DOTS" for eyes) or dead ("X's" for eyes). There is also a "LINE" object which is the dashed line when the dagger is first encountered.

A side effect of this is that DOTS, X's, and LINE are recognized as legitimate objects. Trying to "TAKE" an undefined noun like "COFFEE" will result in "I DON'T KNOW HOW TO GET A COFFEE". However, because DOTS, X's, and LINE actually ARE real objects in the game, trying to TAKE those results in "I DONT SEE IT HERE". This behavior is preserved in this Pico-8 version.

Why Draw Once What Can Be Drawn Twice (or four times)

This is one of the larger mysteries to me surrounding the construction of the game. Every room that has doors is completely drawn from scratch anew for the "open" and "closed" states. If we look at the And, in fact, the full/empty pitcher object has two completely different, yet almost identical, drawings for the two states. Considering the limited storage space of the Apple 2 floppy disk, and considering the work and ingenuity that was put into image compression for the game, why was so much storage wasted on redundant picture data? How did that help development?

Library open/closed images
Some changes are very obviously different, as with the Library. Cobweb missing, image not positioned right, bookshelf items. Rightmost image diff generated with ImageMagick.
Pitcher, ostensibly empty and full
Other images are far more subtle, as with the pitcher in the empty (left) and full (right) states. Image diff generated with ImageMagick.
A four-way comparison between the four states the fenced back yard can be in.
4-way comparison between house/gate, open/closed. White pixels are shared by all four, pink is shared by at least two, red is unique to all four. Click for animated gif.

When it comes to object reuse, there was clearly a mechanism in place for that. Just look at the opening scene in the Entry Hall and the people gathered there. There are seven (7) people objects, but they share images. There is "picture 52" for the men, and "picture 54" for the women. Why didn't the pitcher reuse the same image, since they look identical?

For the Pico-8 version I did in fact redraw all rooms to represent their alternate states (pitcher image change is currently not accounted for). Doing so pushed my storage to the last 14 bytes. In truth, if the images were more smartly arranged as composible pieces (i.e. a base room composited with the appropriate door image) we could add quite a few more rooms to the house.

Was the Versawriter difficult to use?

Why were the rooms done this way? Without access to an actual Versawriter, as was used by the Williams's, it is difficult to know. My suspicion is that the Versawriter data format was difficult to take apart and rearrange into reusable blocks that could also be absolutely ensured to align properly during state changes. 

If we look at the Library pictures above, the open doors are drawn to be opaque, obscuring the bookshelf behind them. Drawing in such a way as to allow the doors to be an isolated object that could be swapped for open/closed states while also preserving this illusion of opaqueness may have been an unweildy, frustrating exercise. For one room perhaps it wouldn't have been such trouble, but multiply that out by the number of rooms with alternate states and perhaps drawing the image again was simply faster and less error-prone.

Consider that the Kitchen actually *does* do a kind of image composition for the open/closed states of things. The fridge and cabinet in the kitchen, are not part of that background illustration. Those are composited to the scene separately, as evidenced by how they do not occlude the kitchen background, unlike the open/closed doors. So they clearly understood how to build a larger scene out of smaller objects, but something about the process must have made it not worth doing.

The empty full pitcher

I cannot explain the pitcher except to think that something like a "water line" was intended to be included and forgotten. I believe there is a common misconception that there is a "full pitcher" image, because the pitcher in the fridge appears to have a water line. I say this because the "full pitcher" inventory object in MHTO is shown with the water line. That, however, is a side-effect of the drawing routine. Images are drawn in a vector style, layering images on top of each other (see room inventory). There is no such thing as opaqueness or fill color; everything is just lines. The line that appears to be "water" is actually just a line defining a shelf of the fridge being drawn through the pitcher image.

When Hardware Supports Sofware

Some aspects of this version of Mystery House are not explicitly "game" related but are rather platform related. I have opted to preserve certain aspects that contributed to the "feel" of the original.

Mystery House was first and foremost an Apple 2 game, and its "venetian blinds" screen clear technique is tied directly to the atmosphere of the game. That transition is a side effect of the slow Apple 2 `HGR` command and not something explicitly programmed into Mystery House. Still, this is a case where the hardware informs the nostalgia of the software.

I have replicated that transition effect in this Pico-8 edition. In a future update, I will likely include a switchable user preference to enable/disable Apple 2-style effects.

The other aspect I've tried to maintain is "pacing." This was, most likely, a side-effect of certain code being slow to execute on the Apple 2. As discussed in the Inventory section, it seems that every request to display an inventory of some sort has to go through all known objects checking to see if each one is in scope for the request. I have simulated this "slowness" with artificial pauses to kind of pace my rebuild similarly to the original. I found, in practice, that without those pauses the whole game felt too "fast," for lack of a better term. It felt counter to the mood of the game, I suppose is what I mean, so it is my opinion that this is another case where the original hardware contributed to the impact of the software on players.

The Bug List

Here's a list of the bugs I discovered, which are also included in the Pico-8 rebuild.

  • The "long gun" drawing bug (discussed earlier)
  • "THERE IS ONE BULLET IN THE GUN" no matter when you "LOOK GUN"
  • The hole in the Dining Room is available even without setting the rug on fire
  • Opening/closing an already open/closed door in the Backyard doesn't alert the player
  • "UP STAIRS" from the Entry Hall doesn't clear the room of people
  • In the Side Yard, "WEST" doesn't check for open house doors; it always works
  • Shovel teleportation (discussed earlier)
  • Killing Joe doesn't replace his eyes (DOTS object) for dead eyes (X's object); they overlay
  • "WITH DAGGER" in the Cemetary when not holding the dagger makes Joe's eyes disappear
  • The Pantry shows a staircase going down, but "UP STAIRS" works and "DOWN STAIRS" doesn't
  • Jewel teleportation (discussed earlier)
  • From the Treetop, "UP TREE" will take you down and "DOWN TREE" does not
  • In the Study, the button does not need to be revealed. "PRESS BUTTON" works anyway
  • From the Study, go "EAST" to the Bathroom but cannot go "WEST" to leave; must "GO DOOR"
  • The trunk in the Storage Room, when unlocked, reports being locked if you try to "OPEN CHEST" when its already open
  • In the Tower, "LOOK BODY" and "KILL DAISY" do not check if Daisy is already dead
  • After Daisy is dead, "WITH BUTTERKNIFE" and "WITH HAMMER" do not check if Daisy is dead and will kill player

Lingering Mysteries

There are still two large aspects of the game that elude my understanding. If anyone has thoughts, I'd love to hear from you.

What mechanism suppresses room object display?

This is a tricky one to explain, though I'm closer to understanding it now than I was at the start of this project. I have enough knowledge to make my implementation MORE accurate, but not 100% accurate. Here's the problem.

Objects in rooms can be in one of two states which I'm going to call "decorative" and "inventory" states. When an object is a "decoration" it sits in a unique location in the room to give the illusion it is part of the room. When an object is in "inventory" it adheres to the rules outlined in the Objects & Inventory section above.

Let's look at the picture hanging on the wall in the Study. We could talk about the button behind the picture, but the button isn't a takeable object, and the picture being takeable illustrates the conundrum better.

The picture begins life as a decoration at coordinates {63,10}. If we take the picture, the drop the picture, the picture displays at the first room inventory slot in order of its object numbering. This is as we would expect, and in fact the behavior was a little baffling to me at first. In thinking about how the original implementation must have worked, we want the simplest implementation that duplicates the behavior.

Objects like the fridge in the Kitchen cannot be taken and so their fixed position in the room makes sense. Objects that can transition between "decoration" and "inventory" are a little trickier. In thinking about how Inform handles this, it would be logical to me that objects must possess a boolean flag for "has_moved" and if the object hasn't moved, it is drawn at its "decoration" coordinates, otherwise at the inventory coordinates.

This sounds logical until we do something strange. Without taking the picture we can "PRESS BUTTON" anyway (mentioned above in "Object Scoping (doesn't exist)". Pressing the button draws the room with the open wall and does not draw the picture. "PRESS BUTTON" again, and the wall closes and the picture reappears.

In the known logic we see

if ROOM = 35 and VERB = "PRESS" and NOUN = "BUTTON" and CURPIC = 41 then
    set room images to 40
if ROOM = 35 and VERB = "PRESS" and NOUN = "BUTTON" then
    set room images to 41
    print "PART OF THE WALL OPENS"

Notice there is nothing happening with the manipulation of object location. The button and picture are not spirited away to "the void" (location #253). The picture starts at room 35, and the button is moved to room 35 when the picture is taken. And yet, pressing the button changes the room image and *also* prevents the picture/button from drawing.

What is happening? Is there some mechanism that ties decorations to specific room images? Do room images have the ability to suppress decorative objects? It is my belief that there must be a simple implementation of objects and rooms that explains all observed behavior naturally, but I cannot see that path forward. 

For the Pico-8 version I've duplicated this behavior as a workaround. In general, I need to revamp the Pico-8 inventory system using late-development intuition about how objects and inventory work.

I'M NOT CARRYING THAT

 In "gamedump.out" there is an explicit rule that reads (as translated from the machine code into human-readable psuedo-code)

if VERB = "LOOK" and NOUN = "NOTE" and OBJECT "NOTE" CARRIED then
    print "THERE IS WRITING ON IT"
if VERB = "LOOK" and NOUN = "NOTE" and OBJECT "NOTE" CARRIED then
    print "THERE IS WRITING ON IT"
if VERB = "LOOK" and NOUN = "NOTE" and OBJECT "NOTE" CARRIED then
    print "THERE IS WRITING ON IT"
if VERB = "LOOK" and NOUN = "NOTE" then
    print "YOU DONT HAVE IT"
if VERB = "READ" and NOUN = "NOTE" then
    print "YOU DONT HAVE IT"

In the game proper we do get the "THERE IS WRITING ON IT" but we do not get "YOU DONT HAVE IT." Instead, "LOOK NOTE" and "READ NOTE" when not carrying a note results in "I'M NOT CARRYING THAT," a string that is not contains in the array of message strings.

Where does this message come from? What logic is overriding the rule we see above? I haven't yet identified any other cases where the logic is magically overridden like this. There is no other logic for LOOK NOTE and READ NOTE in the general verb handling routines that I've seen. 

Checking the hexdump of the ADVENTURE game file we see the string, which seems to be clustered with other strings that are kind of "generic handlers" (failure to move, darkness coming, unknown verb). The mystery is that other objects report the typical generic message "THERE IS NOTHING SPECIAL." So, clearly NOTEs have something special going on, but I do not understand the precise mechanism that triggers "I'M NOT CARRYING THAT." In other words, I do not know for certain that *ONLY* notes trigger this message or if there is some other logic check or logic fall-through that my rebuild is missing.


Get Mystery House (Remodeled)

Leave a comment

Log in with itch.io to leave a comment.