Include I6 code for doors in I7

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.

Pseudo-methods for Pseudo-object-oriented programming was me briefly thinking I was clever for shoe-horning a way to sort-of do methods in I7 and having it gently pointed out to me that I7 had a good way to do that already.

So for your Size example, I’ll assume that size-value is a kind of value.

Sizing is a thing based rulebook producing a size-value.

To decide what size-value is the size of (t - a thing): 
  decide on the size-value produced by the sizing rules for t.

Sizing a shirt (called the garment):
 [...]

Sizing the tuxedo t-shirt:
  [...]

Like @Draconis said, you might waste some space by assigning properties to all things. But if you don’t have complicated dependencies on extensions, you could also do:

A garment is a kind of thing.
A garment is always wearable.

And then assign your specific properties to garment and loop through garments instead of wearable things.

1 Like

I actually like the rules-based approach in principle, but the main problem I have with it is the one he mentions at the end of his presentation: There’s no notion of encapsulation for rules. Sure, I can define rulebooks and move individual rules into them, but ultimately all of these rules are sloshing around at the global level. How do I know which rules could potentially apply a given game object? (If the answer is ‘all of them’, then coding and debugging the game is going to be pretty difficult.) When I use someone else’s external library, how can I be sure it’s going to place nicely with the code I have? What’s the precedence for rules? (The documentation lays out its idea for a hierarchy, but it’s quite complicated; and given some of the other unpredictable quirks of I7, I’m not sure I actually believe it.) Rules don’t necessary just replace a command completely or fail; there can be a complicated feeding order. And so on.

Well, yes, I7 has poor encapsulation in general. And that is problematic for extensions and has limited their utility somewhat. Looking at published games’ versions, it seems like a few expert authors use dozens of extensions at a time and most everyone else uses zero to two. Most extensions are fairly short and easy to read and you can eyeball potential problems… but some aren’t.

Have you spent a lot of time debugging rule order problems? As a long-time programmer I was really, really dubious of the “More specific goes before more general and most of the time that’s good enough and you don’t have to worry about it” thing. I expected huge amounts of nuisance specifying exact orders for rules. And one of the things I’ve found really astoundingly amazing about I7 is… most of the time “more specific goes before more general” is good enough and you don’t have to worry about it. I slap first and last on things sometimes but have only rarely had to be explicit about “Rule X is listed before Rule Y”, other than modifying existing sequences like the turn sequence.

Have you spent a lot of time debugging rule order problems?

In some cases, yes. It’s uncomfortable to deal with the idea that I7 programming involves just hoping that the I7 internals happen to decide to the right thing. I know the language comes in with the explicit ideology that interactive fiction is a narrative primarily and only code secondarily, but I can’t shake the feeling that it has a fairly tight upper bound on the amount of complexity that can be comfortably put into a game (at least, without spending far more time and play-testing rounds on it than I’m willing to). And sure, no one is looking to code the next version of emacs in I7; but given the other idiosyncracies and general weirdness of I7, I’m not willing to just trust that everything will work out, and I don’t see any convenient way of testing all of it.