Managing things with numbers i/o names

Hi there,

A probably self-answered question: I believe the nature of LISP/ZIL makes the concept of INDIRECTION useless. Coming from the table-oriented game engines community (DAAD and likes), I’ve always named locs and objects using numbers instead of text (it makes my handwritten maps more comfortable to the eye) and written routines to automatise things using indirection with arithmetics on variables, saving tons of programming work. e.g. having only up to 256 entities in 8-bit systems, I could create fake locs beyond 256 using coordinates with maths and messages at tables for descriptions. Or creating a fast NPC movement system with a grid of location numbers and location connections created by indirecting and adding 10 to loc number to move n, -10 to move s, +1 to move e…

Wondering if ZIL would support this style too. I intuitively guess it doesn’t but…

Much to learn, yet.

Thanks in advance!

I can’t test code at the moment and I’ll return later with some examples, but…

Every object (locations or rooms are also objects) have an internal number. The last defined is object 1 and the second to last is object 2 and so on. All commands that takes an object as a parameter will accept that you use this number instedad of the name of the object.

Again, from ZILF Reference Guide:

MOVE

<MOVE object1 object2>

Move object1 to be the first child of object2. Children of object1 move with it.

Example:

<OBJECT ANIMAL>
<OBJECT CAT>

<MOVE ,CAT ,ANIMAL>
<IN? ,CAT ,ANIMAL> --> T

The above example could also be written as:

<OBJECT ANIMAL>
<OBJECT CAT>

<MOVE 1 2>
<IN? 1 2> --> T

I wouldn’t recommend using these numbers because they are bound to change during development. Instead I’ll build a table of rooms or a table of tables of rooms for NPC movements.

2 Likes

Yeah, the Z-machine (and thus ZIL which is pretty “close to the metal” of the Z-machine’s virtual hardware) has no concept of types. Everything is a 16-bit (usually-signed) integer.

Object names are, effectively, just constants. At compile time they’re replaced with the (automatically-assigned) index number of that object. Property names, attribute names…they all compile into 16-bit signed integers. This is in fact the source of some weird bugs in the original Infocom games, when math that was supposed to calculate the index number of an object or property goes wrong.

This is also the source of most of the intrinsic limitations of the Z-machine. Addresses needing to fit into 16 bits puts a pretty onerous limit on the amount of memory you can use. Later versions mitigated this by coming up with more and more complex translations applied to ROM addresses; in version 6, for example, the first argument to a @call opcode is translated by multiplying the 16-bit value by 4 and then adding 8 times the word at address 0x28, and then jumping to the result.

But since RAM (and the data part of ROM rather than the code part) always needs to be byte-addressable, no fancy translation scheme can ever get around the hard limit of 64K. This is the main reason Glulx was invented: switching to a 32-bit word size is the only real solution.

Well, when you say “in version 6, for example”, note that version 6 was the only time someone developed a more complex scheme. And no surprise there - version 6 is complicated and full of special cases for everything.

Version 1, 2, 3, 4, 5, 8 have exactly the same level of complexity when it comes to mapping between packed addresses and physical addresses. Version 6 has a slightly more complex algorithm for this, and it may have served its purpose well for games developed with ZIL. Version 7 (which was invented in the nineties, for use with Inform) copied the version 6 scheme, but it was a mistake as this ended up giving version 7 no advantages over version 5 when used with Inform, and so version 7 never saw any serious use.

I’m being a bit hyperbolic to make a point, but versions 1 through 3 just used word addresses (which were also used for other types of pointers, like in abbreviations), which is about as simple as you can get.

Then version 4 changed it so routine and text pointers point to four-byte segments instead of words, which is a bit unusual, since four-byte units weren’t used anywhere else in the machine. I would say this is an increase in complexity because now instead of just words and bytes you also have double-words to think about, though the implementation is pretty much the same.

Version 6 changed it to “four-byte units indexed from a fixed offset stored in the header and also that offset is different for calling vs printing”, which is another increase in complexity (but, as you mention, is not surprising given how version 6 was designed). Version 8 abandoned this and just changed the double-word addressing to quad-word addressing, but by that point (to my knowledge) the shortage of addressable memory was becoming a more pressing issue and that couldn’t easily be changed without fundamentally rewriting how memory is accessed. Hence the invention of Glulx with its larger word size.

It can, but it’s probably more trouble than it’s worth.

As @heasm66 said, every object, room, verb, etc. does have an internal number assigned by the compiler. It’s hard to use those numbers directly, since they change during development (and some are basically impossible to predict before compiling).

You can call your rooms ROOM-1, ROOM-2, and so on, but that won’t be enough to make indirection work: ROOM-1 and ROOM-2 might actually end up being objects #56 and #101.

One approach that would theoretically work would be to make a table that lists all the rooms in order, then do your indirection by indexing into the table.

But in practice, it’ll be simpler to make specialized tables for the specific features that need them (like NPC movement routes), listing only the specific rooms each one needs.

With ZIL, you won’t be able to have a table of objects or rooms, per se. But you could use multiple tables to represent multiple objects/rooms and their specific characteristics. Infocom uses this method for rooms in at least 5 games.

For example, the Royal Puzzle in Zork III is made of 36 virtual rooms laid out in a 6x6 grid:

1	2	3	4	5	6
7	8	9	10	11	12
13	14	15	16	17	18
19	20	21	22	23	24
25	26	27	28	29	30
31	32	33	34	35	36

Each of these rooms uses the same base ROOM object. But a virtual room’s location is used to calculate an offset in a table. The first room in the upper left has an offset of 1. The last room in the bottom right is 36. So moving from room to room will change the offset like you described. In the Royal Puzzle, moving NORTH or SOUTH decreases or increases, respectively, the offset by 6. This offset, then, points to the appropriate entry in other tables which describe the desired room characteristics.

For the Royal Puzzle, the CPOBJS table is 36x8 table which each stores the objects (up to 7) in each virtual room:

#ofObjs  Obj1  Obj2  Obj3  Obj4  Obj5  Obj6  Obj7     ;Room 1
#ofObjs  Obj1  Obj2  Obj3  Obj4  Obj5  Obj6  Obj7     ;Room 2
.
.
.
#ofObjs  Obj1  Obj2  Obj3  Obj4  Obj5  Obj6  Obj7     ;Room 36

So all the objects in the 15th room (which is the 3rd room from the left in the 3rd row) would be stored in the 15th entry of CPOBJs. When moving into a new room, the game will first remove all the objects from the base ROOM object and copies those object numbers into the current virtual room’s entry in CPOBJs. Then, the new room’s offset is calculate based upon the direction moved. The objects listed in the new room’s entry in CPOBJs will be moved back into the base ROOM object. So moving rooms constantly changes the objects in the base ROOM to simulating moving from to different rooms.

Seastalker did try to save space but storing the location of objects in the virtual rooms as data pairs in a table, DESERT-TABLE:

Room# Obj# Room# Obj# Room# Obj# ... Room# Obj# 

So 2 objects in the 20th room would have 2 entries with 2 values:

Room1 ball Room2 letter Room20 hammer Room20 flashlight Room30 rock ... 

When moving from virtual room to virtual room, the game would first copy each object in the current virtual room (base ROOM object) and save it into DESERT-TABLE as the room number and object number pair (filling up blank entries in the table first). Then it would search for any entries with the new virtual room number and copy the paired objects into the base ROOM object.

Leather Goddesses of Phobos had a 10x29 table, CATACOMBS-TABLE, to describe the exits in each virtual room:

  N    NE     E    SE     S    SW     W    NW     U     D
  3     0     0     3     0     1     0     0     0     0    ;Room 2
  0     4     0     0     2     0     2     0     0     0    ;Room 3
  3     0     5     0     0     0    99     0     0     0   ;Room 4
.
.

So moving SE from Room2 would access the 4th entry in the second row. The value there, 3, indicates the new room is Room3. For this example, 0 is no exit and 99 is a wall.

I hope the examples help you design virtual rooms.

1 Like