[ZILF] Extending to Owned Items

Hi, I know this is a really broad question, but I wanted to ask about how you would implement a feature into ZILF for ease of use. I’m thinking of how you can have objects (be it actual objects, like keys and such, or other things, like body parts like their hair) so that it (the game) automatically understands that object as being owned (in some way) by that actor.
For example say we had a female character, Jane, who had brown hair and has a key.

> Look at Jane's hair.
Filler-text-description-of-hair.

> Look at her hair.
Filler-text-description-of-hair.

> Look at her key.
Filler-key-text.

> Look at Jane's key.
Filler-key-text.

I imagine in some way this feature could be realized by having some code (perhaps in take and drop) which modifies the object’s adjective table to include the name of the owner and their pronoun? Still not sure how exactly this would work though, but I am curious as to the broad strokes of how such a feature would be implemented.

2 Likes

My approach would be to modify the REFERS? routine to understand a new kind of adjective. If .A isn’t in the object’s ADJECTIVE property, but it is a known “possessive” adjective, check whether the object is owned by the corresponding actor, and count it as a match if so.

For example, if you don’t mind hardcoding the actor, you could replace the condition

<IN-PB/WTBL? .O ,P?ADJECTIVE .A>

with something like

<OR <IN-PB/WTBL? .O ,P?ADJECTIVE .A>
    <AND <==? .A ,W?JANE\'S>
         <OR <==? <GETP .O ,P?OWNER> ,JANE> ;"it belongs to Jane"
             <HELD? .O ,JANE>>>>            ;"Jane is holding it"

And then use <VOC "JANE'S" ADJ> to add the word to the dictionary if it isn’t used anywhere else.

1 Like

Extending that to work with LOOK AT HER KEY would be a bit trickier, but basically you’d recognize one or more(*) possessive forms of each pronoun, and match the object’s owner against the tables of antecedents maintained by the pronoun system.


(*) e.g., HER is sometimes possessive, but HERS is always possessive.

2 Likes

So if I wanted to try to implement this feature, and I wanted it to be general, would you recommend the REFERS? route? Additionally, how might I be able to remove an adjective from an object’s adjective table?
(Note: I’m thinking of making a flag, OWNABLEBIT to denote an object’s adjective table will be modified if that object is owned.)

Also, and I apologize to be berating you with questions, but I started to dig into the implementation of exits (as I’d like to learn how exactly you implemented the syntax for exists with those additional keywords.) I noticed that it looks like you get the property table for the exit and get its length to tell you which exit it most likely is. My question is, you can’t use any word instead of those specific keywords (namely TO IF ELSE and so on.) How would one define these new keywords so that new property syntaxes can be defined?

1 Like

Yes.

The easiest way is to just overwrite it with zero, since property sizes can’t easily be changed. One reason to modify REFERS? instead is to avoid having to add and remove adjectives to each object as their ownership changes.

The compiler has a built-in PROPDEF for directions, which the game can override if needed. The documentation for PROPDEF in @heasm66’s ZILF Reference Guide includes this example:

;"Complex PROPDEF (DIRECTIONS from Zork Zero)"
<PROPDEF DIRECTIONS <>
(DIR TO R:ROOM = (UEXIT 1) (REXIT <ROOM .R>))
(DIR S:STRING = (NEXIT 2) (NEXITSTR <STRING .S>))
(DIR SORRY S:STRING = (NEXIT 2) (NEXITSTR <STRING .S>))
(DIR PER F:FCN = (FEXIT 3) (FEXITFCN <WORD .F>) <BYTE 0>)
(DIR TO R:ROOM IF F:GLOBAL "OPT" ELSE S:STRING = (CEXIT 4) (REXIT <ROOM .R>) (CEXITFLAG <GLOBAL .F>) (CEXITSTR <STRING .S>))
(DIR TO R:ROOM IF O:OBJECT IS OPEN "OPT" ELSE S:STRING = (DEXIT 5) (DEXITOBJ <OBJECT .O>) (DEXITSTR <STRING .S>) (DEXITRM <ROOM .R>))> 

For really unique property syntaxes, you can define a PROPSPEC function (as in pseudo.zil.) to parse and rewrite the property values before the compiler sees them.

2 Likes

I ran into another issue, I’m going down the path of writing a PROPSPEC (mostly because the property can have several values in it, like the pseudo objects) and the issue I am having is accessing the information needed. So for each value in the property list, it tries to produce a table containing a pair of a symbol (object name) and a string (response.)
So it looks, roughly like this:

<SET R 
   . . . 
   <MAPRET
   . . . 
  <PTABLE <PARSE <SPNAME .OBJ-NAME>> .STR>>>

;"Return the list of all these symbol-string pairs."
(<> <PTABLE !.R)>

However, the issue comes from taking this property and accessing and extracting both the string and object value that ought to be stored in that sub table. I also tried, instead of having <PTABLE <PARSE <SPNAME .OBJ-NAME>> ...> it being <PTABLE <FORM GVAL .OBJ-NAME> ...> but that didn’t seem to quite work either. How do I store the symbol (for the name of some object) into this table and be able to get it out with GET (or GETB or GET/B) so I can fiddle with the referenced object or the string?

Can you give a “before and after” example? That is, how do you want one of these properties to look when it’s used in an object definition, and what would it look like if you did the transformation by hand?

Sort of an esoteric property, but I’m just playing around. So basically I want to be able to have the description of an object change when looked at if the container (thing holding the object with this property) has some other object.
An Example:

(ALT-DESC 
        (OBJ-X "OBJ-X-HELD Response")
        (STRANGE-CUBE "You notice that since you've picked up the strange cube, this has begun to glow."))

So presumably what I would want the PTABLE to look like would be something like:

(ALT-DESC <PTABLE 
                 <PTABLE OBJ-X "OBJ-X-HELD Response">  
                <PTABLE STRANGE-CUBE  "You notice that since you've picked up the strange cube, this has begun to glow.">>)

Then since the property would look (roughly, I imagine, as the above example) I could just a combination of GET to get the particular PTABLE pair of ATOM-STRING and use GETB, and GET/B to extract the symbol that corresponds to the object needed to be checked (and I would then use GVAL or something to look up the atom) and then be able to print out the string.
I hope that makes any sort of sense, sorry if this is convoluted.

Here’s one possibility:

<DEFINE ALT-DESC-PROPSPEC (L)
    <LIST <>
          <MAPF ,PLTABLE
                <FUNCTION (M) <PLTABLE !.M>>
                <REST .L>>>>

<PUTPROP ALT-DESC PROPSPEC ALT-DESC-PROPSPEC>

Note, I’ve changed PTABLE to PLTABLE since you’ll need to be able to find the length at runtime (at least for the outer table).

How it works:

  • ALT-DESC-PROPSPEC is called with L set to the list defining an ALT-DESC, such as:
    (ALT-DESC (STRANGE-CUBE "You notice that since you've picked up the strange cube, this has begun to glow.")
              (RUSTY-TWINE "The rusty twine in your pocket fills you with confidence as you watch the gizmo's intricate rotations."))
    
  • The call to MAPF strips off the property name with REST, then transforms the remaining elements by piping them through an anonymous function.
  • The anonymous function makes a table out of each inner list by passing the list elements as arguments to PLTABLE.
  • MAPF collects the inner tables and passes them all as the arguments to PLTABLE one last time, creating an outer table.
  • Finally, LIST sticks a <> on the front to make the result look like a property definition again.

The resulting object has a single word in its ALT-DESC property, a pointer to a table (T?39 in this case):

?PTBL?ROTATING-GIZMO:: .TABLE
	.STRL "rotating gizmo"
	.PROP 1,P?ADJECTIVE
	.BYTE A?ROTATING
	.PROP 2,P?SYNONYM
	.WORD W?GIZMO
	.PROP 2,P?ALT-DESC
	.WORD T?39
	.BYTE 0
	.ENDT

That table contains a length word followed by pointers to the inner tables, and each inner table contains a length word followed by the object and string:

T?37:: .TABLE 6
	.WORD 2,STRANGE-CUBE,STR?32
	.ENDT

T?38:: .TABLE 6
	.WORD 2,RUSTY-TWINE,STR?33
	.ENDT

T?39:: .TABLE 6
	.WORD 2,T?37,T?38
	.ENDT

(Atoms don’t exist at runtime, so there’s no need to use GVAL to get the object. The first word of each inner table is the object number.)

I know this is a long time to come back to this, but I wanted to follow up on the PROPDEF specifications. So can an object have multiple instances of the same property, if they differ or something? If so, how would I extract each of them so I can look at them?

I’m thinking in terms of the DIRECTIONS property, in which you technically can have multiple on a room-- I mean that’s how we have rooms with multiple exits. So is there some unique mechanism that only allows this for exits?

I would say no. In the z-code each property is an unique number and it can only appear once on each object. You can, as above, have the same property with different formats but only one version of the property on each object. Technically DIR isn’t a property, each direction is. I.e. NORTH, SOUTH and so on are individual properties.

EDIT: Just to illustrate - This is an object from Trinity and how it is compiled to z-code (63/4, for example, is property 63 with length 4).

<OBJECT GBASE
	(LOC ROOMS)
	(DESC "Bottom of Stairs")
	(FLAGS LIGHTED LOCATION SHADOWY)
      ; (ODOR 0)
	(OVERHEAD STAIR)
      ; (HEAR 0)
	(NORTH PER WALK-UP-STAIRS)
	(NE TO UNDER-CLIFF)
	(EAST TO AT-BEND)
	(SE TO DOCKSIDE)
	(SOUTH TO AT-TRELS)
	(SW TO FCLEARING)
	(WEST TO SBOG)
	(NW TO NBOG)
	(UP PER WALK-UP-STAIRS)
	(DOWN SORRY "You're already at the bottom of the stairs.")
	(ACTION GBASE-F)
	(SEE-ALL FOREST)
	(SEE-N STAIR)
	(GLOBAL STRUCTURE STAIR FOREST DSHADOW VERTEX)>

--->

Object: 575
02298 00 01 00 00 04 08 00 58 01 3F 00 00 81 6A
  Attributes: 15, 37, 44
  Parent = 88
  Next   = 319
  Child  = 0
  Properties address  = 816A
    0816A 06 10 F4 67 34 48 01 20 98 64 CE DF 05
    Description = "Bottom of Stairs"
    08177 BF C4 8F 61 00 00             63/4  (NORTH PER R23D84)
    0817D 7E    01 90                   62/2  (NE TO OBJECT-400)
    08180 7D    02 2D                   61/2  (EAST TO OBJECT-557)
    08183 7C    02 35                   60/2  (SE TO OBJECT-565)
    08186 7B    01 3D                   59/2  (SOUTH TO OBJECT-317)
    08189 7A    01 AA                   58/2  (SOUTHWEST TO OBJECT-426)
    0818C 79    01 3F                   57/2  (WEST TO OBJECT-319)
    0818F 78    00 2A                   56/2  (NORTHWEST TO OBJECT-42)
    08192 B7 C4 8F 61 00 00             55/4  (UP PER R23D84)
    08198 B6 C3 FC 1A 00                54/3  (DOWN "You're already at the bottom of the stairs.")
    0819D 72    8F 38                   50/2  (PROP-50 R23CE0)
    081A0 A8 CA 02 0D 01 6E 00 03 00 88
                00 7B                   40/10 Data = 02 0D 01 6E 00 03 00 88 00 7B
    081AC 67    01 6E                   39/2  Data = 01 6E
    081AF 62    00 03                   34/2  Data = 00 03
    081B2 5E    01 6E                   30/2  Data = 01 6E

So in summation, in this case the only real option is using a routine or whatever to mess with it at compile time to deal with it?

EDIT: Also, is there a way I can in my propspec routine look at words in the prop and compare then to new words so it can have a syntax?

Also: I looked at your doc and what an amazing piece of work! The DEFPROP command didn’t really sit in my head but your doc really made it clear! Thanks so much!

1 Like

Oh real quick, I’m hoping to save some variables in a table that is formed by a PROPSPEC function, but I can’t seem to get it to work-- always complains about it being a non-constant initializer. Any suggestions on how to get this to work?