Formalising aural and visual adjacency between rooms.

Hi, folks.

I’m trying to set up a bit of code in Inform 7 (6G60) to formalise aural and visual adjacency between rooms. In a nutshell, what I want to do is define a concise mechanism for saying:

  • a player in room A can see everything in room B, and vice versa (A is visually adjacent to B)
  • a player in room A can hear everything in room B, and vice versa (A is aurally adjacent to B)

In particular, I want things to work so that if room A is aurally adjacent to room B, then a player in room A can ask, tell, and give orders to an NPC in room B. There are some other bits and bobs, but those are the major ones.

(It’s intentionally possible for one, but not both of these to be true. Examples of visual adjacency without aural adjacency would include soundproof glass, or a video screen with no audio; examples of aural adjacency without visual adjacency would basically include thin walls.)

I’ve attached a sample of my code - I was wondering if someone could give me a hand with a few issues I’m running into? These are:

  • When examining a person or object in a visually adjacent room, the action reports success, but the description message isn’t generated, and I can’t figure out why.

  • Likewise, when listening to a person or object in an aurally adjacent room, the action reports success, but there’s no output text, not even the stock “You hear nothing unexpected.” message.

  • Asking someone to do something (“wendy, take whistle”, for example) only seems to check scope, allowing orders to be given via a visual adjacency relation. This isn’t intended, but I can’t think of an elegant way to block it; it seems like kind of a weird edge case.

I imagine I’m probably going about this all backwards - I’m very much a newbie, having written my first bit of Inform code this past Monday - so I’d by very grateful if anyone could offer a bit of guidance.
adjacency.txt (2.09 KB)

Argh. I hate accessibility rules. They’re poorly documented and inexplicably complex. What’s happening is that you’re mixing up the expected results of different rulebooks. “Rules for reaching inside/outside” have “allow/deny access” results, but “accessibility rules” either stop the action or they don’t. “Allow access” is considered success, but it still stops the rulebook, which apparently stops all the action processing right there. You can either use “continue the action” or move your rule into the “reaching inside” rulebook, where “allow access” will be interpreted correctly. See the “Carnivale” example.

Ah - that makes sense. Well, apart from the fact that the “basic accessibility” rule doesn’t “allow access”, but syntax is a funny thing.

This actually helps another issue I was having: how to override the generic “you can’t reach that” message if you’re trying to take an aural action pertaining to something in a room that’s visually but not aurally adjacent. It occurs to me that I’ll need to distinguish between listening and asking/telling, since the messaging for the former needs to indicate that the player can’t hear the noun, but the messaging for the latter needs to indicate that the noun can’t hear the player.

Revised code attached - can anyone spot any obvious mistakes?

Now there are just two issues remaining that I’m aware of:

  • How to prevent the player from issuing orders to NPCs in rooms that are visually but not aurally adjacent.

  • How to fix the misleading “you can’t reach into” message when asking an NPC to interact with an object they can’t access. For example, in the attached example, “erin, get mirror” responds with “You can’t reach into the East Room.”, which is likely to be puzzling to anyone who doesn’t know the exact logic of the accessibility rules.

I’ll keep tinkering with it myself, but I’m open to suggestions.
adjacency-2.txt (2.89 KB)

Hokay, I think I have the first issue sorted. This is what I came up with:

Instead of asking someone to try doing anything: if the player cannot touch the actor and the location of the actor is not aurally adjacent to the location of the player: say "[the actor] can't hear you."; otherwise: continue the action;
That seems to work in conjunction with the above modifications - it blocks issuing orders to actors in other rooms who can see, but not hear the player, while continuing to allow orders to be issued to actors in other rooms who can both see and hear the player. I may be overlooking some edge cases, though - it almost seems too simple. ^^;

Now… does anyone know, offhand, how to tinker with scope for an entity other than the player? I suspect that the second problem in my preceding post is a scoping issue, but I can’t seem to figure out how to adjust scope for an NPC to test that theory - all of the scoping examples I’ve found refer exclusively to the player.

I think you just replace “scope of the player” with “scope of [NPC’s name]” in the “After deciding the scope of” rule. I have this rule lying around, which seemed to work:

After deciding the scope of folks: repeat with avatar running through visible things: place avatar in scope.

(“Folks” was an NPC who was part of a backdrop, for reasons that don’t bear going into, probably.)

Thanks - that got me a lot closer. Unfortunately, I’m still having an issue. I have these two bits of code:

[code]After deciding the scope of the player:
repeat with locale running through rooms which are visually adjacent to the location of the player:
place locale in scope;

After deciding the scope of a person:
repeat with locale running through rooms which are visually adjacent to the location of the person:
place locale in scope;[/code]
The first one works fine, but the second one complains that “person” is not an object on the “repeat with” line.

EDIT: Hah! Silly me. I just had to name the actor:

After deciding the scope of a person (called the viewer): repeat with locale running through rooms which are visually adjacent to the location of the viewer: place locale in scope;
Well, that’s two mysteries solved. Now I just need to puzzle out how to get some more informative messaging than “[actor] is unable to do that” when the reason is that they can see, but not reach an item.

Thanks again, everyone. :slight_smile:

Yeah, a tricky thing is that “the person” is something you never want to say; it’s interpreted the same as “a person” would be. Getting a compiler error as you did is probably the best outcome; it’s pretty common to spend time tearing your hair out about why something like “if the person is in a visually adjacent location” isn’t working, when that actually means the same thing as “if a [that is, any] person is in a visually adjacent location.”

ETA: And on your substantive question, try looking at the Unsuccessful attempt rules, in section 12.5; though I don’t know enough about “the reason the action failed” to tell you how easy it would be to work this out.

I’m a fan of putting rules into the most appropriate rulebooks. I’d recommend using the Persuasion rulebook for visible people who can’t hear commands. And the “reaching into” rulebook to override the “you can’t reach into” message (which is where it comes from in the first place).

Sound advice. The “instead of” rule posted upthread was just a quick hack to check if my reasoning was sound. The interpreter seems to think so.

On an unrelated note, while I was working on the modified feedback messages, a notion for doing stupid tricks with doors struck me:

[code]Instead of examining a door (called the barrier):
If the front side of the barrier is the location of the player and the back side of the barrier is visually adjacent to the location of the player:
let the far side be the back side of the noun;
say “Peering through [the noun], you see…”;
try examining the far side;
rule succeeds;
Otherwise if the back side of the barrier is the location of the player and the front side of the barrier is visually adjacent to the location of the player:
let the far side be the front side of the noun;
say “Peering through [the noun], you see…”;
try examining the far side;
rule succeeds;
Otherwise:
continue the action;

Instead of examining a direction (called the bearing):
let the adjoining room be the room bearing of the location of the player;
if the adjoining room is visually adjacent to the location of the player:
if there is a door (called the barrier) and ((the front side of the barrier is the location of the player and the back side of the barrier is the adjoining room) or (the back side of the barrier is the location of the player and the front side of the barrier is the adjoining room)):
try examining the barrier instead;
rule succeeds;
otherwise:
say “Peering to [the bearing], you see…”;
try examining the adjoining room;
rule succeeds;
otherwise:
continue the action;[/code]
The idea is that if there’s a door between you and a visually adjacent location, and you look at the door or in the direction of the door, the game should describe peering through that door into the room beyond. This is another instead rule hack, but I’d be willing to take a shot at stuffing it into the correct rulebook - though I may need a little guidance as to which rulebook that would be.

My questions are threefold:

  1. Is there anything wrong with doing it like this that I’m not seeing?

  2. Is there a more elegant way to approach that big, gnarly multiply-bracketed conditional that tests whether there exists a door between the player’s location and the visually adjacent room?

  3. Is there any way to get it to print out the full “look” results for the room (including header and contents) where it’s currently just doing “try examining”?

  1. Doesn’t it matter whether doors are open or closed?

  2. Two things you can do:

First, you can reduce duplication by finding the far side first, and then checking if the far side is visually adjacent.

Second, you don’t need to bother with the front side or the back side at all. Doors are considered adjacent to the rooms they connect.

Third, you can encapsulate complicated conditions into phrases, adjectives and relations.

I’d suggest something like this:

[code]To decide which room is the far side of (the portal - a door):
If the portal is not adjacent to the location, decide on nothing;
Decide on a random room adjacent to the portal that is not the location.

Viewporting relates a door (called portal) to a room (called vista) when the vista is the far side of portal and the vista is visually adjacent to the location.

The verb to provide a view of (insert conjugation here) implies the viewporting relation. The verb to be viewable through implies the reversed viewporting relation.[/code]

That’s not tested, and you may have to fiddle with the syntax to make it work.

As for 3., printing room descriptions are quite complicated. I recommend you look at the Standard Library to see how it’s done.

As I’m picturing it, sometimes yes, sometimes no. The player might be able to see through a closed door - it could be a glass door, for example, or a grating. I don’t recall, offhand, whether doors take the transparent property; if so, I might throw an “and (barrier is open or barrier is transparent)” in there.

Actually, now that I think of it, I should really have the option of making visual adjacency dependent on the status of any intervening door, in order to prevent players from visually interacting with objects on the other side of a closed, opaque door. Hmm.

Thanks for the tips on the rest. It didn’t occur to me to take that route, but you’re right - there really should be some simple way of declaring a door such that it automatically makes the rooms it connects visually adjacent to each other while it’s open (or visually adjacent to each other 100% of the time, for a transparent door).

EDIT: Incidentally, I just discovered that I don’t need a decide rule, either - “let the far side be the other side of the portal” works just fine. Whoops. I was making that way more complicated than it needed to be.

I usually try to avoid tossing this particular suggestion into I7 threads, because it seems indecently snotty. But in this case (and as you seem to be a newcomer to the forum, who might not be totally committed to one authoring system over another) … if you were using TADS 3, this business would be handled by the library’s sense-passing mechanism. You would still need to do a little work to get it running properly, but the ability to see and hear things in adjacent locations is built-in in TADS.

Okay, goofy question on my part.

[code]A viewport is a kind of door.

Visual contact relates a room (called A) to a room (called B) when there is viewport (called X) and ((A is the front side of X and B is the back side of X) or (B is the front side of X and A is the back side of X)).

Visual contact relates a room (called A) to a room (called B) when there is viewport (called X) and A is adjacent to X and B is adjacent to X.

Visual contact relates a room (called A) to a room (called B) when a viewport which is adjacent to A is adjacent to B.

The verb to be in visual contact with implies the visual contact relation.

The Gloomy Room is a room. The Bright Room is a room. The Metal Grille is a viewport. The Metal Grille is east of the Main Room and west of the Gloomy Room.[/code]
All three of these conditional definitions of the visual contact relation are accepted by the interpreter (well, not all at the same time, obviously), but only the first one - the gnarly-looking one that uses front side and back side - actually works as expected, i.e., by making the Gloomy Room adjacent to the Main Room.

I feel like I’m missing something obvious here.

I have no particular commitment to any particular authoring system. However, my ultimate objective here is to learn I7, not to select the best authoring system for any particular project. Thus far, I think it’s been a success.

(Besides, the fact that I7 doesn’t have a sense-passing extension at the moment doesn’t mean it’ll never have one. I may write one myself, once I have a firmer grasp of it.)

I think it’s simply that the built-in “adjacent” relation relates rooms to rooms, and it doesn’t relate rooms to doors. So the conditions on the non-gnarly ones are never true.

You could make your definition less gnarly-looking by defining a relation that relates rooms to doors when the room is one side of the door – even if you’re only using the definition for this purpose, it’d make your code read more straightforwardly, which is nice.

Yeah, but that’s why it’s so exciting that David J Prokopetz is making this extension. This is one of the things that has always seemed cool to me about TADS, but not, you know, cool enough to learn a whole other language for.

I’ll give that a shot - thanks.

Incidentally, does anyone know offhand where the visibility relation is defined in the standard rules? It occurs to me that in order to do a proper job of this, I need stuff like “if the player can sees X” to function transparently with respect to visual adjacency, and I’d like to double-check the conditions for the standard relation underlying the verb “to be able to see”.

Hmm… should really also set things up so that actions can be defined in terms of audible things. Then I could override the usual ask/tell/etc. actions with “applying to one audible thing and one topic”, and avoid tinkering with the reachability rules in cases where it’s not necessary.

All the verbs for the fundamental relations are defined at the beginning of Part 1 of Standard Rules (Section SR1/0) (together with some adjectives defined in terms of these verbs).
But it seems the fundamental relations themselves (including visibility) are inaccessible through I7 and I6, defined as they are in C at the very level of the I7 compiler itself. (I don’t think the source code of that part of the compiler is even published yet.)

The code that implements most of them is I6 code. E.g. there’s a SupporterOf() function, a ContainerOf() function, and so on. The visibility relation is the TestVisibility() function. But it’s true that these function calls are generated directly by the I7 compiler. You could override the function definitions, but not the calls.

(And some relation expressions are simple property lookups rather than function calls. E.g. incorporation is “X == Y.component_parent”.)

Yet? After five years of development?

You would never have this problem in TADS. This underlines my basic view of the difference between I7 and T3, which is that it’s easier to do basic, garden-variety stuff in I7 than in T3 – but when you start wanting to do complex, not-so-basic stuff, it’s easier in T3 than in I7.

Ah, well. Is there any documentation regarding what criteria the visibility relation uses? At the moment, my mechanism is “if the room is visually adjacent to the actor’s location and the room is not dark, place it in scope” - is that overlooking anything?