This is a nondescript part of a coal mine.
Someone carrying a large bag is casually leaning against the wall. It is clear that the bag will only be taken over his dead body
> examine the thief
The thief carries a large bag and a vicious stiletto, whose blade is aimed menacingly in your direction.
> go north
> go south
> go down
I’ll deal with that guy later. He’s always hanging around the mazes!
Code for this episode can be found at https://github.com/ericlippert/flathead/tree/blog18
We’ve made it as far as printing. The different versions of the Z-machine made increasingly sophisticated use of the printing facilities of the host operating system. In version 1 and 2, the stories basically assumed that the output was being dumped to a printer one line at a time. Text was word-wrapped, but the model was basically just appending to a transcript. Most version 3 stories were the same, though one, Seastalker, could optionally print a little radar screen on the top few lines of the screen while the regular game text continued to scroll below. Version 4 games like Trinity made much heavier use of this “split window” concept. Trinity moves the cursor all around the screen, overwriting existing text. These later games also used boldface, italic, and non-fixed-width characters.
In this series we’re going to implement more than just basic teletype printing, but less than the full requirements of the specification. In particular I’m going to not bother to implement text effects like boldface, italic, reverse-colour, and so on. The reasons being
- These aspects of text rendering do not particularly interest me.
- OCaml’s graphical capabilities out of the box are, as we’ll see in later episodes, pretty primitive. My aim here is not to learn how to construct a graphical package in OCaml; nor is it to learn how to use the GUI tools that other people have built to work with OCaml.
- There is plenty of interesting stuff just in emulating a fixed-width terminal window with multiple cursors.
- I always thought that Infocom’s attempt to embrace fancier graphics and text rendering was a step in the wrong direction. The stories succeed or fail on their merits as interactive stories.
Anyway: eventually we will build a proper word-wrapping purely-functional screen model and a rendering engine for it. Right now though, I want to get past the printing instructions. Our helper function will start out life trivial:
let print interpreter text = Printf.printf "%s" text; flush stdout; interpreter
I have it return the interpreter because of course later on we will have printing produce a new interpreter, rather than a side effect; the rendering engine will render the interpreter.
The printing instructions we’ll implement here are very straightforward.
print_objtakes an object number and prints its short name
print_paddrtake an address of a zstring in either unpacked or packed form and print the string.
print_retprints the string embedded in the instruction, followed by a newline, and returns from the current routine.
new_lineall do exactly what you would expect.
These are super boring instructions; with the exception of
print_ret they all simply produce a string and print it. For example:
let handle_print_paddr packed_address interpreter = let packed_address = Packed_zstring packed_address in let address = Story.decode_string_packed_address interpreter.story packed_address in let text = Zstring.read interpreter.story address in print interpreter text
I won’t show the rest; they are not very interesting.
I’m going to turn off printing every instruction now that we can actually output game text. If we run our little program now we get:
MINI-ZORK I: The Great Underground Empire Copyright (c) 1988 Infocom, Inc. All rights reserved. ZORK is a registered trademark of Infocom, Inc. Release Exception: Failure "TODO: 58d4: and sp 07ff ->sp \n ".
Exciting progress! Apparently computing the release number requires doing some bit twiddling, and we never implemented those instructions. Next time on FAIC: we’ll continue to make progress on implementing more instructions.