the-infocom-files/lurkinghorror

Inventory is broken, at least when compiling with ZILF 0.9

Opened this issue · 8 comments

At the start of the game:

>INVENTORY
 You are wearing an assignment. You are wearing an assignment.

I think I know what's happening. The Lurking Horror (along with Stationfall, Plundered Hearts and Border Zone) prints the inventory in a more compact way than most other games. This is V-INVENTORY in The Lurking Horror:

<ROUTINE V-INVENTORY ()
	 <SETG D-BIT <- ,WEARBIT>>
	 <COND (<NOT <DESCRIBE-CONTENTS ,WINNER
					<>
					<+ ,D-ALL? ,D-PARA?>>>
		<TELL "You are empty-handed.">)>
	 <SETG D-BIT ,WEARBIT>
	 <DESCRIBE-CONTENTS ,WINNER
			    <>
			    <+ ,D-ALL? ,D-PARA?>>
	 <SETG D-BIT <>>
	 <CRLF>>

I think what it means is that it sets D-BIT to a bit mask indicating which objects to print. First all objects that do not have WEARBIT (by using the negative value of WEARBIT), i.e. the ones you are carrying. Then only the objects that do have WEARBIT, i.e. the ones you are wearing.

However, if you look at the generated lurkinghorror_data.zap file, you'll see this:

        WEARBIT=0
        FX?WEARBIT=32768

I.e. WEARBIT was assigned the value 0, so D-BIT is 0 for both calls to DESCRIBE-CONTENTS. In the released versions of The Lurking Horror, it seems that ZILCH assigned it the value 3, and that's why it works there.

ZILF works with Stationfall, Plundered Hearts and Border Zone because it assigned WORNBIT (as it's called in those games) the values 21, 22 and 39 respectively there.

It seems like bad luck, but I'm not sure what to do about it...

Try adding <ORDER-FLAGS? LAST WEARBIT>.

Thanks, that does seem to work. It should perhaps be added to all games that use this inventory trick, to avoid surprises in the future?

Other than the games mentioned above, it looks like this would also include Zork Zero and Shogun.

It should perhaps be added to all games that use this inventory trick, to avoid surprises in the future?

That works as a short term solution.

But ORDER-FLAGS? is a ZILF extension, and kind of an ugly one, so IMO it's better to modify the way DESCRIBE-CONTENTS uses ,D-BIT: for example, to indicate "no filter" by setting it to -1 instead of 0.

But it filters in both cases, doesn't it? First it shows only objects that don't have WEARBIT, then only objects that do have it.

It's supposed to filter both times, but the bug is that it isn't filtering at all. Here's where the filter is implemented:

lurkinghorror/desc.zil

Lines 234 to 254 in 415b804

"determines if an object is describable at all."
<GLOBAL D-BIT <>> ;"bit to screen objects"
<ROUTINE DESCRIBABLE? (OBJ "OPT" (CONT? <>))
<COND (<FSET? .OBJ ,INVISIBLE> <RFALSE>)
(<EQUAL? .OBJ ,WINNER> <RFALSE>)
(<AND <EQUAL? .OBJ <LOC ,WINNER>>
<NOT <EQUAL? ,HERE <LOC ,WINNER>>>>
<RFALSE>)
(<AND <NOT .CONT?> <FSET? .OBJ ,NDESCBIT>>
<RFALSE>)
(,D-BIT
<COND (<G? ,D-BIT 0>
<COND (<FSET? .OBJ ,D-BIT> <RTRUE>)
(ELSE <RFALSE>)>)
(<NOT <FSET? .OBJ <- ,D-BIT>>>
<RTRUE>)
(ELSE <RFALSE>)>)
(ELSE
<RTRUE>)>>

So, the possible values of D-BIT are:

0 (no filter: show all objects)
1 to 47 (include: only show objects with flag 1-47 set)
-1 to -47 (exclude: only show objects with flag 1-47 not set)

And the cause of the bug is that this scheme has no way to represent "include" or "exclude" for flag 0.

Forget the change I suggested in #5 (comment); that wouldn't work. But since a flag number will fit in 6 bits, you could use the top 2 bits for the filter mode, e.g.:

$$00000000 (no filter)
$$10xxxxxx (include)
$$01xxxxxx (exclude)

Ah, that makes more sense. I did a quick test and came up with this:

diff --git a/desc.zil b/desc.zil
index 71d62a1..c238075 100644
--- a/desc.zil
+++ b/desc.zil
@@ -202,7 +202,7 @@ Prints nothing and rfalses if there was nothing to list.
 	 <COND (<EQUAL? .OBJ ,HERE>
 		<TELL "There is ">)
 	       (<EQUAL? .OBJ ,PLAYER>
-		<COND (<EQUAL? ,D-BIT ,WEARBIT>
+		<COND (<EQUAL? ,D-BIT <BOR ,WEARBIT 128>>
 		       <TELL " You are wearing ">)
 		      (T
 		       <TELL "You are carrying ">)>)
@@ -243,11 +243,11 @@ Prints nothing and rfalses if there was nothing to list.
 		<RFALSE>)
 	       (<AND <NOT .CONT?> <FSET? .OBJ ,NDESCBIT>>
 		<RFALSE>)
-	       (,D-BIT
-		<COND (<G? ,D-BIT 0>
-		       <COND (<FSET? .OBJ ,D-BIT> <RTRUE>)
+	       (<BAND ,D-BIT 192>
+		<COND (<BAND ,D-BIT 128>
+		       <COND (<FSET? .OBJ <BAND ,D-BIT 63>> <RTRUE>)
 			     (ELSE <RFALSE>)>)
-		      (<NOT <FSET? .OBJ <- ,D-BIT>>>
+		      (<NOT <FSET? .OBJ <BAND ,D-BIT 63>>>
 		       <RTRUE>)
 		      (ELSE <RFALSE>)>)
 	       (ELSE
diff --git a/verbs.zil b/verbs.zil
index e3a4ee9..c502ddd 100644
--- a/verbs.zil
+++ b/verbs.zil
@@ -178,12 +178,12 @@
 	"about to keel over from exhaustion">>
 
 <ROUTINE V-INVENTORY ()
-	 <SETG D-BIT <- ,WEARBIT>>
+	 <SETG D-BIT <BOR ,WEARBIT 64>>
 	 <COND (<NOT <DESCRIBE-CONTENTS ,WINNER
 					<>
 					<+ ,D-ALL? ,D-PARA?>>>
 		<TELL "You are empty-handed.">)>
-	 <SETG D-BIT ,WEARBIT>
+	 <SETG D-BIT <BOR ,WEARBIT 128>>
 	 <DESCRIBE-CONTENTS ,WINNER
 			    <>
 			    <+ ,D-ALL? ,D-PARA?>>

It would probably be a bit nicer if I had made the 128 and 64 constants, but I just wanted to test your idea. And it does seem to work.

Something like this, perhaps:

diff --git a/desc.zil b/desc.zil
index 71d62a1..4c21e6f 100644
--- a/desc.zil
+++ b/desc.zil
@@ -202,7 +202,7 @@ Prints nothing and rfalses if there was nothing to list.
 	 <COND (<EQUAL? .OBJ ,HERE>
 		<TELL "There is ">)
 	       (<EQUAL? .OBJ ,PLAYER>
-		<COND (<EQUAL? ,D-BIT ,WEARBIT>
+		<COND (<EQUAL? ,D-BIT <BOR ,WEARBIT ,D-INCLUDE>>
 		       <TELL " You are wearing ">)
 		      (T
 		       <TELL "You are carrying ">)>)
@@ -234,6 +234,8 @@ Prints nothing and rfalses if there was nothing to list.
 "determines if an object is describable at all."
 
 <GLOBAL D-BIT <>>	;"bit to screen objects"
+<CONSTANT D-INCLUDE 128>
+<CONSTANT D-EXCLUDE 64>
 
 <ROUTINE DESCRIBABLE? (OBJ "OPT" (CONT? <>))
 	 <COND (<FSET? .OBJ ,INVISIBLE> <RFALSE>)
@@ -243,11 +245,12 @@ Prints nothing and rfalses if there was nothing to list.
 		<RFALSE>)
 	       (<AND <NOT .CONT?> <FSET? .OBJ ,NDESCBIT>>
 		<RFALSE>)
-	       (,D-BIT
-		<COND (<G? ,D-BIT 0>
-		       <COND (<FSET? .OBJ ,D-BIT> <RTRUE>)
-			     (ELSE <RFALSE>)>)
-		      (<NOT <FSET? .OBJ <- ,D-BIT>>>
+	       (<BAND ,D-BIT ,D-INCLUDE>
+		<COND (<FSET? .OBJ <BAND ,D-BIT 63>>
+		       <RTRUE>)
+		      (ELSE <RFALSE>)>)
+	       (<BAND ,D-BIT ,D-EXCLUDE>
+		<COND (<NOT <FSET? .OBJ <BAND ,D-BIT 63>>>
 		       <RTRUE>)
 		      (ELSE <RFALSE>)>)
 	       (ELSE
diff --git a/verbs.zil b/verbs.zil
index e3a4ee9..d6b1554 100644
--- a/verbs.zil
+++ b/verbs.zil
@@ -178,12 +178,12 @@
 	"about to keel over from exhaustion">>
 
 <ROUTINE V-INVENTORY ()
-	 <SETG D-BIT <- ,WEARBIT>>
+	 <SETG D-BIT <BOR ,WEARBIT ,D-EXCLUDE>>
 	 <COND (<NOT <DESCRIBE-CONTENTS ,WINNER
 					<>
 					<+ ,D-ALL? ,D-PARA?>>>
 		<TELL "You are empty-handed.">)>
-	 <SETG D-BIT ,WEARBIT>
+	 <SETG D-BIT <BOR ,WEARBIT ,D-INCLUDE>>
 	 <DESCRIBE-CONTENTS ,WINNER
 			    <>
 			    <+ ,D-ALL? ,D-PARA?>>

The value 63 is still hard-coded, but at least it's only in desc.zil. (And could of course be fixed, too.)

And perhaps it would be clearer to write <BOR ,D-INCLUDE ,WEARBIT> than <BOR ,WEARBIT ,D-INCLUDE>?