Include I6 code for doors in I7

I’m trying to set up a door that really wants to also be part of a custom class (or kind) I’ve defined in the code. The particular details of the situation make it a much better fit for multiple inheritance rather than composition, but I7 doesn’t allow the former. On the other hand, It compilles to down to I6, which does. I’ve therefore tried variations on code like:

The north object translates into I6 as "DIROBJ_N".
The south object translates into I6 as "DIROBJ_S".

The Origin Room object translates into I6 as "ROOM_A".
The Destination Room object translates into I6 as "ROOM_B".

There is an object in Origin Room called a magic door.
Include (-
with
	door_to
	[;
	    if (self in ROOM_A) return ROOM_B;
	    else return ROOM_B;
	],
	door_dir
	[;
	    if (self in ROOM_A) return DIROBJ_N;
	    else return DIROBJ_S;
	],
	found_in ROOM_A ROOM_B,
 has 	static door ~open openable,
-) when defining magic door.

but the resulting object isn’t actually a door: It can’t be entered or opened; it doesn’t take over the GO NORTH command from its entrance; it doesn’t create a connection on the auto-generated map; etc. I tried setting up an I6 equivalent of the door kind (even though it’s not actually a class in I6), but that causes the code to fail to build. Is there a solution, or at least a workaround, here?

1 Like

My first thought would be to let I7 handle the “door” side of things, since it uses various special-one-off code to make it part of the map, and do the other part of the multiple inheritance in I6.

1 Like

Thanks, that seems to have worked. (I had tried it before without success, but I think that was due to some other bit of I7 nonsense that in the original code that didn’t make its way into the minimal standalone example.)

Oops, spoke too soon. I have other I7 code to deal with the magic door as my custom kind, but I7 refuses to compile it: “…that seems to contradict ‘There is a door called the magic door’, as a [my custom class] and a door are incompatible.” I guess I could potentially strip all the I7 rules I have for dealing with this custom class and just deal with it in native I6, but I7’s support for including chunks of I6 code isn’t as robust as advertised.

Can’t you make your custom class a “kind of door” in I7 and proceed from there?

1 Like

No, some of the objects in custom-class aren’t doors. (This custom class is used for the magic system in the game, so there’s about a dozen mechanics that it ties into. On the other hand, doors have so many rules and effects attached to them— intercepting movement commands, showing up on the auto-generated map, etc.— that it’s hard to make an object formally a kind of door at the class level but not have any of the functionality of a door.)

The I7 compiler doesn’t understand multiple inheritance. At all. The problem message seems to indicate that you’re declaring the magic door as two different kinds. That’s not allowed (unless one is a subkind of the other, if I recall correctly – in which case it will end up as the subkind).

I assume that you’re trying to get door-like interaction with something that is not formally a door. Without additional details about what you’re trying to accomplish, it’s hard to offer better guidance. I suspect that you might find the Easy Doors by Hanon Ondricek extension to be useful, as a reference for some techniques if nothing else.

1 Like

Yeah, I was hoping that I7 would do the equivalent of testing for class membership via (X ofclass C) rather than (class(X) == C) and allow me to insert I6 code to force multiple inheritance. That doesn’t seem to be the case. Fundamentally, what I want is to insert I6 code for a multiply-inherited class and have it play nicely with the rest of the I7 code, but that doesn’t seem to be possible. It arguably should be possible, since ultimately the I7 code spits out a large auto.inf file in I6 that doesn’t have any problems with multiple inheritance, but it’s at the very least nontrivial to set up in practice.

The workaround suggested in the documentation is effectively composition: create a (presumably invisible, here) custom-kind object attached to a door-object that handles the custom-kind processing. That’s a pain for various reasons relating to this particular project, but it might be doable.

Alternatively, would it work if I just write the game in I6 and put in a massive Include (- […] -) statement around the entire program? Ideally, I’d like to get the good parts of I7 development (the easy distribution, the testing setup, finding and importing external libraries, auto-generated maps, etc.) without the bad parts (the I7 language itself). I don’t want to spend months writing a game entirely in I6 and find out at the end that I have to rewrite it from scratch.

How about the following?

"Door?"

Origin Room is a room.

There is an object in Origin Room called a magic door. [might consider thing instead of object]

[These let the I7 compiler know that the properties will be valid for the magic door; simple I6-level declaration isn't enough]
The magic door can be enterable. The magic door is enterable.
The magic door can be open or closed. The magic door can be openable. The magic door is openable.
The magic door is fixed in place.

The Destination Room is a room.

[These continue to work with the remnants of the Standard Library found in 6M62 templates]
Include (-
with
    door_to
    [;
        if (self in (+ Origin Room +)) return (+ Destination Room +);
        else return (+ Origin Room +);
    ],
    found_in (+ Origin Room +) (+ Destination Room +)
-) when defining the magic door.

Include (- class (+ door +) -) when defining the magic door. [forces multiple inheritance at I6 level so it will pass the I7 "is a door" test; I6 "door" attribute should be inherited but I'm not sure it matters anywhere]

[need these because magic door won't be part of internal map]
Instead of going north in the presence of the magic door when the player is in Origin Room:
    try entering the magic door.

Instead of going south in the presence of the magic door when the player is in Destination Room:
    try entering the magic door.

A word of warning: I enjoy tinkering with this sort of stuff, but it is considered bad practice. The remnants of the Standard Library that still exist in the I7 templates are subject to revision at any time, so depending on patterns that exist at the I6 level is dubious.

1 Like

Thanks, I think that works! (There’s a minor issue with the door not appearing on the auto-generated map, but whatever, that’s trivial.) I guess the difficulty I had earlier when I tried a similar solution was figuring out how exactly to get at the I7-defined door class. I tried variants on redefining it or adding “…translates into I6 as …” commands, but apparently just (+ door +) does the right thing. There may be some other weird issues later on, given just how much goes into the door kind, but I’m reasonably confident this solution works.

As long as it works for now, I can wrap up this game and be done with it.

Glad to have helped; I learned something, too. Welcome to the mad scientists’ club! (@Zed will send your membership card.)

For the (+ ... +) notation, see WWI 27.19 Longer extracts of Inform 6 code. FYI - I think that this notation is no longer allowed in 10.1; if that’s the case then your technique of forcing I6 names from your original post might be an alternative.

2 Likes

This was a feature of at least one of the IDEs at one point—I think the Mac one. You could make an entire project in I6 inside the I7 IDE.

I’m not sure if this ever got past the experimental stage, though. And it wouldn’t give you an auto-generated map or handle external libraries, since those are more linked to the I7 compiler than the IDE.

1 Like

I’ll also note that the standard way to emulate multiple inheritance in I7 is to make one of the “classes” a boolean property instead. For example, scenery is often treated like a class, but it’s actually a property any object can have—which is why you can have a scenery door, a scenery supporter, and so on.

From an I7 perspective, the difference between a property and a class isn’t huge, since you can “repeat with the item running through scenery things” or “instead of examining a scenery thing” or whatever. The real differences have to do with the type checker and certain types of under-the-hood optimizations.

1 Like

What about class variables and functions? Suppose I want to define a Magic Spell kind for which objects have a mana cost, a number of targets, a function that’s executed when they’re cast, etc. I can define a boolean “magic-spellable” and set it equal to true for what would ordinarily be instances of a Magic Spell class, but I wouldn’t be able to get access to any of those variables or functions in a strongly-typed language like I7 (even in a rules-based system) without some sort of class structure.

Why not? You could even check to see if the object in question “provides the property mana cost” or whatever before checking what it is if you want to be safe. You could also give objects a phrase or rule name variable that executes whatever function (or method if you prefer) you want to attach to it. Or am I misunderstanding something?

I think what tmack is saying is that you can declare a property for a kind, but you can’t declare a property for a class defined by an adjective (Boolean property). For instance I can’t say

A wearable thing has a number called size.  

Is that about right @tmack?

1 Like

Yes, and thus I can’t write something like:

Carry out showing disguises:
    repeat with S running through every wearable thing:
       say "There's a disguise for a [size of S]-sized person here."

I might be able to dispense with classes altogether and just code an explicit size property for every wearable object in game, but that’s assuming I don’t want to use any classes to do…well, most of what classes are meant to do. This is all very frustrating. Still, I might be able to construct a workaround by a combination of (1) defining an adjective for my custom class; (2) attaching an invisible object to each instance of the custom class to do the heavy lifting; (3) use “X provides invisible_custom_class_object” to get access to that object; (4) hope I didn’t forget to add any of those invisible objects to things that need it or not add it to ones that don’t.

It does.

if Obj provides the property P:

Yeah, realized that belatedly.

You can also define it as a property of “things” and just let it go unused for anything that’s not magickable. This wastes a handful of bytes, but on Glulx that’s not an issue and if you’re aiming for Z-machine you’ll probably want to switch to pure I6 (or Dialog or some other language).

You can also define these properties separately for every individual magickable thing, if the handful of bytes becomes a problem. Tedious and annoying, but easier than mimicking multiple inheritance in I6.

1 Like

I7 doesn’t understand classes, either. At least, not in the object-oriented sense that you seem to mean.

Based on what you’re finding frustrating, I think that you might want to take a closer look at relations (see all of WWI 13) as an alternate to properties that hold pointers. You might also find WWI 27.13 Implications to be of interest.

Also, zarf delivered a presentation at a conference in 2009 that helped me to appreciate the perspective shift from object-oriented (I6) to rules-based (I7) programming; you might find it helpful, too. It can be found at: Rule-Based Programming in Interactive Fiction.