[TADS] Model World Puzzle #1: Theatre Lobby

8 views
Skip to first unread message

Kevin Forchione

unread,
Jan 19, 2000, 3:00:00 AM1/19/00
to
Once we begin digging into the model world we soon discover that, among
other things, it's a game of two halves. The first half consists of scope,
containment, and accessibility rules. The second half, equally as important,
concerns object display. Sense.t will hopefully take you a long way with
regard to the first half, but is still developing in terms of the second.

To illustrate both points: The Inform classic "Theatre: An Interactive Night
of Horror", (c) 1995, Cave Rock Software Ltd. poses several interesting
puzzles for a game player and some wonderful effects. One of these is the
brief reference, when standing upon the Eastern Landing, of being able to
see into the lobby below. When you do <<examine the lobby>> the following
message is displayed:

>x lobby
(looking down)
Below you is the theatre lobby.


=============================
Looking Into The Lobby Using Sense.t
-----------------------------------------------------

While this effect could doubtless be generated in many different ways, both
in Inform and TADS, Sense.t adopts a containment path approach that allow
you to join the Eastern Landing and the Theatre Lobby using the seeInto
attribute.

easternLanding.seeInto = lobby

Once these rooms are joined in this way it becomes necessary (although it
has taken me some time to realise this) to treat "rooms" just as any other
object as though the actor had stepped outside of the room and picked it up
in his hands for examination. So, after joining the rooms through seeInto,
the next thing you must do is prevent the lobby from being listed as an
extended location in the Eastern Landing room description. This is done
using the isListed attribute.

lobby.isListed = nil

Now the lobby ldesc will not display when we enter or <<look at>> the
Eastern Landing. But the lobby contents will still display, just as though
the lobby were a fixeditem container. The result of entering the Eastern
Landing would be:

>e
Eastern Landing
This landing circles around the upper level of the lobby allowing you to
see down into it. The landing continues south and west, where it goes
down the lobby stairs. A set of double doors lead north to the theatre
auditorium itself, while a single finely carved wooden door leads east.
In the Theatre Lobby you can see your pager.

But we would like to suppress the display of the lobby contents, so we make
the lobby a "quiet" containment by setting the isqVisible attribute:

lobby.isqVisible = true

And in order to actually examine it we'll need to give it vocabulary
properties. Our definition thus far looks like:

startroom: room
noun = 'lobby'
adjective = 'theatre'
isListed = nil
isqVisible = true
sdesc = "Theatre Lobby"
ldesc = "This room is a tribute to the now-faded glory of the
theatre. The still-smiling faces of forgotten productions stare
out from faded posters at you. An old grand staircase leads up,
while the exit lies to the south. Small private doors lead
northeast and northwest."
north = staircaseLanding
up = staircaseLanding
;

This definition results in the following displays:

>e
Eastern Landing
This landing circles around the upper level of the lobby allowing you to
see down into it. The landing continues south and west, where it goes down
the lobby stairs. A set of double doors lead north to the theatre auditorium
itself, while a single finely carved wooden door leads east.

>x lobby
This room is a tribute to the now-faded glory of the theatre. The
still-smiling faces of forgotten productions stare out from faded posters at
you. An old grand staircase leads up, while the exit lies to the south.
Small private doors lead northeast and northwest.

But this isn't quite what we'd like. We would prefer the message "(Looking
down) ..." to display here. We might also like to see any objects that are
in the lobby. By using the extended descriptors of Sense.t we can have
different descriptions when we are in different containment proximities. But
Sense.t 3.0 doesn't implement this for containment class, so we'll modify it
slightly -- a one line change.

modify containment
xtndLkAround(actor, verbosity) = {
local i;
local lookList = self.contents;

if (parserGetMe().location == self)
lookList += parserGetMe();
lookList -= [actor];
lookList -= [actor.location];

if (verbosity
&& self.isListed)
{
if (proptype(self, &insideDesc) != DTY_NIL)
{
self.dispBeginLdesc;
self.insideDesc;
self.dispEndLdesc;
}
else if (self.location == nil)
{
self.dispBeginLdesc;
self.proxDoInspect(actor); // The change is here!
self.dispEndLdesc;
}
}

if (self.isqVisible) return;

if (verbosity)
{
for (i = 1; i <= length(self.contents); ++i)
{
if (self.contents[i].isfixed)
self.contents[i].heredesc;
}
}

self.dispParagraph;
if (itemcnt(lookList))
{
"\^<<self.statusPrep>> <<self.thedesc>> %you% can see ";
listcont(lookList); ". ";
}
listcontcont(lookList); "\n";

//
// list all ACTORS in the room except the actor
//
for (i = 1; i <= length(lookList); ++i)
{
if (lookList[i].isactor)


self.dispParagraph;
lookList[i].actorDesc;
}
}

"\n";
}
;

modify lobby
ldesc_remote = "Below you is the theatre lobby."
doInspect(actor) =
{
if (self.isSensible(actor, &access_visible) == PRX_REMOTE)
{
"(Looking down)\n";
locale(actor, self, &access_visible);
}
else pass doInspect;
}
;

A locale() function along the lines of that implemented by Inform, simply
gives us full access to the extended room display. It's not defined in Sense
3.0, so we define it below.

locale: function(actor, loc, access_rule) {
local lsave, qsave;

switch(access_rule)
{
case &access_visible:
lsave = loc.isListed;
qsave = loc.isqVisible;
loc.isListed = true;
loc.isqVisible = nil;
loc.xtndLkAround(actor, true);
loc.isListed = lsave;
loc.isqVisible = qsave;
break;
case &access_audible:
lsave = loc.isListedAudible;
qsave = loc.isqAudible;
loc.isListedAudible = true;
loc.isqAudible = nil;
loc.xtndLnAround(actor, true);
loc.isListedAudible = lsave;
loc.isqAudible = qsave;
break;
case &access_olfactory:
lsave = loc.isListedOlfactory;
qsave = loc.isqolfactory;
loc.isListedOlfactory = true;
loc.isqOlfactory = nil;
loc.xtndSmAround(actor, true);
loc.isListedOlfactory = lsave;
loc.isqOlfactory = qsave;
break;
}
}

This modification produces the following display when we are in the lobby:

>x lobby
The landing continues south and west, where it goes down the lobby stairs. A
set of double doors lead north to the theatre auditorium itself, while a
single finely carved wooden door leads east.

And this display, when examining the lobby from the Eastern Landing.

>x lobby
(Looking down)
Below you is the theatre lobby.
In the Theatre Lobby you can see your pager.

Notice that our examination of the lobby while inside it produces only the
ldesc, suggesting that further modification might be desirable. You could
produce the same results as a <<look>> without the short description by
calling nrmLkAround() instead of simply passing doInspect().

Since the Eastern Landing is not joined to the lobby we can't reach the
pager when it's in the lobby and we're viewing it from the landing, or smell
it, or even hear it. But a similar process as that used above would be
involved if you wish to add more senses.

=======================
Advantages Of Sense Passing
-------------------------------------------

One of the biggest advantages to this approach is that you're not simply
creating a dummy lobby object that is located in the Eastern Landing with a
simple ldesc message or even a call to an xxxxLkAround() method. Sense
passing allows you access to objects within the lobby also. So examining the
pager described in the the view of the lobby from the Eastern Landing is
possible, and because of the extended descriptors (ldesc_remote) you can
have the pager display a different message than it does when you're in the
lobby or holding it.

It would be possible for you to <<wave to Joe>> from the landing, and using
the testScope() function Joe's daemon could be used to have him wave to you
when he's at a distance.

--Kevin

Sean T Barrett

unread,
Jan 19, 2000, 3:00:00 AM1/19/00
to
Kevin Forchione <Lys...@email.msn.com> wrote:
>Once we begin digging into the model world we soon discover that, among
>other things, it's a game of two halves. The first half consists of scope,
>containment, and accessibility rules. The second half, equally as important,
>concerns object display. Sense.t will hopefully take you a long way with
>regard to the first half, but is still developing in terms of the second.
[snip]

>=======================
>Advantages Of Sense Passing
>-------------------------------------------
[snip]

>It would be possible for you to <<wave to Joe>> from the landing, and using
>the testScope() function Joe's daemon could be used to have him wave to you
>when he's at a distance.

There are two components to sense propogation in the sense of
"object display": active sensing and passive sensing.

Active sensing is when you issue a command, such as 'look'
or 'examine lobby' or 'smell'. Passive sensing is when
an event occurs and text is displayed because you sensed it.
Seeing other characters moving around in Deadline is the
classic example of it.

In 1993 I worked on a pretty significant and complex system
for a MUD to do passive sense passing (based on ideas and inspired by
another MUD development group, TEAM CTHULHU). I haven't talked about
it here because I'm uncertain that this degree of complexity
in the world simulation is valuable for the general direction
IF seems to be going. Because it was a MUD, event descriptions
were already decoupled from the actor (an event had to be
describable from the POV of the actor or from that of others), so it
wasn't *that* heinous to make the method of describing descriptions
a bit more programmatic. But it shifts things away from the
classic narrative style to a much more mechanical simulationy
style--although perfectly acceptable for a high-simulation *game*.

This system was pretty complex due to its power, but by the
time we stopped working on it we hadn't yet solved a way of
writing new descriptions that was really ideal. But the system
we had could do things like:

> z
In the cage on the boat to the north, Linda shouts, "Help!"

> e
You walk down the alley to the east.

From somewhere to the west, you hear a female voice faintly
shouting, "Somebody help me!"

Note that the above is implemented using ordinary containers
which are specially tagged for what sense data they propogate,
and a generic spoken-voice propogator/description scheme.
There is no custom code required for the above scenario: just
three ordinary rooms, two objects, and a Linda character with
a daemon generating shouts. The spoken-voice propogator just
used the regular sound propogator with the addition of testing
line-of-sight to decide whether to identify the speaker or not, etc.

The three/four of us working on it spent endless hours brainstorming
the logic for how to generate distance-descriptors given nested
containers and room transitions, accounting for line of sight when
hearing a sound, etc.

The other side of this coin was that the propogated event
descriptors were also propogated to NPCs, and the event
descriptors included non-text descriptors which were easily
understood by NPCs, so NPCs could react to events which
occured around them. We didn't get to writing anything very
significant for this, though, just some demonstration things:
A character who every time he saw an event, moved one square
towards that event. A character who picked up objects if you
dropped them in the same room. Etc.

Sean B

Kevin Forchione

unread,
Jan 20, 2000, 3:00:00 AM1/20/00
to
"Sean T Barrett" <buz...@world.std.com> wrote in message
news:FoLvK...@world.std.com...

> Kevin Forchione <Lys...@email.msn.com> wrote:
> In 1993 I worked on a pretty significant and complex system
> for a MUD to do passive sense passing (based on ideas and inspired by
> another MUD development group, TEAM CTHULHU). I haven't talked about
> it here because I'm uncertain that this degree of complexity
> in the world simulation is valuable for the general direction
> IF seems to be going. Because it was a MUD, event descriptions
> were already decoupled from the actor (an event had to be
> describable from the POV of the actor or from that of others), so it
> wasn't *that* heinous to make the method of describing descriptions
> a bit more programmatic. But it shifts things away from the
> classic narrative style to a much more mechanical simulationy
> style--although perfectly acceptable for a high-simulation *game*.

Some decoupling is quite natural even with IF as authors begin to bend the
systems! Personally I wouldn't want "Sim-City IF", but the model world is
slowly evolving toward a more sophisticated approach. I remember Gareth Rees
talking about the state of the model several months ago, as it evolved from
the early Zork days through its current development. Increasing complexity
is probably unavoidable.

As stated in other posts, passive sense passing is a topic I've yet to
address with Sense.t. It'll probably evolve into a library before that,
however, as it's far easier to work with libraries than extensions (and
there's no question of hacking!)

> This system was pretty complex due to its power, but by the
> time we stopped working on it we hadn't yet solved a way of
> writing new descriptions that was really ideal.

Yes, I agree, it's very difficult. Very interesting ideas you've presented
here though.

>But the system
> we had could do things like:
>
> > z
> In the cage on the boat to the north, Linda shouts, "Help!"
> > e
> You walk down the alley to the east.
>
> From somewhere to the west, you hear a female voice faintly
> shouting, "Somebody help me!"
> Note that the above is implemented using ordinary containers
> which are specially tagged for what sense data they propogate,
> and a generic spoken-voice propogator/description scheme.
> There is no custom code required for the above scenario: just
> three ordinary rooms, two objects, and a Linda character with
> a daemon generating shouts.

One's definition of custom code is, of course, entirely dependent upon the
structure of the library. With ADV.T, for example, many of Inform's effects
require custom code. ADV.T's rooms aren't defined as containers, but
WorldClass' are.

In the message sending system I've been working on, for instance, producing
display effects such as:

>put the book and the coins on the table
You put the book and the three coins on the table

requires no custom code, but that's only because the library definitions
make this behaviour the norm.

However, custom code will probably always be required for sense passing in
Sense.t because the structure of the world model would be too rigid for IF.
There've been discussions about the potentials for a more simulationist
approach. It will be interesting to see how far it goes.

>The spoken-voice propogator just
> used the regular sound propogator with the addition of testing
> line-of-sight to decide whether to identify the speaker or not, etc.
>
> The three/four of us working on it spent endless hours brainstorming
> the logic for how to generate distance-descriptors given nested
> containers and room transitions, accounting for line of sight when
> hearing a sound, etc.

This is very difficult under the current conditions, given both the nature
of the parser and the lack of macro pasting... however the next generation
of TADS will make this kind of thing much easier. But the work continues.

> The other side of this coin was that the propogated event
> descriptors were also propogated to NPCs, and the event
> descriptors included non-text descriptors which were easily
> understood by NPCs, so NPCs could react to events which
> occured around them. We didn't get to writing anything very
> significant for this, though, just some demonstration things:
> A character who every time he saw an event, moved one square
> towards that event. A character who picked up objects if you
> dropped them in the same room. Etc.

To some extent of course If does this, Inform and now TADS has the capacity
for NPC reactions to player commands. Gareth Rees' The Thief does things
similar to your last example. TADS has the added advantage that commands
generated by execCommand() can also be reacted to by NPCs and have a
cascading effect upon the game.

But in general IF is mainly a command-oriented and dominated process, with
events taking a lesser role. Sense.t and other libraries will no doubt reach
some happy compromise between the two extremes. Well, maybe not a happy
compromise...

You've given me food for thought.

--Kevin


Lysseus

unread,
Jan 22, 2000, 3:00:00 AM1/22/00
to
Kevin Forchione <Lys...@email.msn.com> wrote in message
news:Os9BAUpY$GA.191@cpmsnbbsa03...

> Once we begin digging into the model world we soon discover that, among
> other things, it's a game of two halves. The first half consists of scope,
> containment, and accessibility rules. The second half, equally as
important,
> concerns object display. Sense.t will hopefully take you a long way with
> regard to the first half, but is still developing in terms of the second.
>
> To illustrate both points: The Inform classic "Theatre: An Interactive
Night
> of Horror", (c) 1995, Cave Rock Software Ltd. poses several interesting
> puzzles for a game player and some wonderful effects. One of these is the
> brief reference, when standing upon the Eastern Landing, of being able to
> see into the lobby below. When you do <<examine the lobby>> the following
> message is displayed:
>
> >x lobby
> (looking down)
> Below you is the theatre lobby.

Progress has been made since the posting of this puzzle. The cumbersome
modifications required to generate the above effect has been reduced and
without reliance on too cumbersome a mechanism. Sense 3.1 should be able to
handle this sort of situation with the following code:

theatreLobby: room


noun = 'lobby'
adjective = 'theatre'
isListed = nil
isqVisible = true
sdesc = "Theatre Lobby"

sdesc_remote = "lobby"


ldesc = "This room is a tribute to the now-faded glory of the
theatre. The still-smiling faces of forgotten productions stare
out from faded posters at you. An old grand staircase leads up,
while the exit lies to the south. Small private doors lead
northeast and northwest."

ldesc_remote = "Below you is the theatre lobby."

north = staircaseLanding
up = staircaseLanding

doInspect(actor) =
{
if (proximity(actor, self) == PRX_REMOTE)


{
"(Looking down)\n";
locale(actor, self, &access_visible);
}
else pass doInspect;
}
;

pager: item
location = startroom
noun = 'pager'
adjective = 'your' 'my'
sdesc = "your pager"
adesc = "your pager"
adesc_remote = "what appears to be your pager"
thedesc = "the pager"
ldesc_remote = "Your pager is barely recognisable from this
distance."
;

staircaseLanding: room
sdesc = "Staircase Landing"
ldesc = "Here the grand staircase splits to the east and west. You
can also go back down to the south."
south = theatreLobby
east = easternLanding
down = theatreLobby
;

easternLanding: room
sdesc = "Eastern Landing"
ldesc = "This landing circles around the upper level of the lobby


allowing you to see down into it. The landing continues south
and west, where it goes down the lobby stairs. A set of double
doors lead north to the theatre auditorium itself, while a
single finely carved wooden door leads east."

west = staircaseLanding
seeInto = theatreLobby
passToLocation(plist, filter) = {
if (filter == &access_visible) pass passToLocation;
return nil;
}
;

The effect of the above is:

>x lobby
(Looking down)
Below you is the theatre lobby.

In the lobby you can see what appears to be your pager.

>x pager
Your pager is barely recognisable from this distance.

The changes in 3.1 remove the cumbersome proxDoXXX methods and the defines
of all those ldesc_remotes, ldesc_locals, ldesc_immediates, etc from thing
class. Where sense description methods are called in ADV.T sense.t replaces
them with a call to a corresponding method router. For example, the message
"You see nothing unusual about the foo." is generated with:

"\^You see nothing unusual about <<self.thedescR>>.

The thedescR method calls a function, proxdesc(), which determines the
extended sense description to use based on the proximity of the actor and
the object, and matching it to a list of extended description methods. The
list for thedesc looks like this:

thedescList = [ &thedesc_reflexive,
&thedesc_possessive, &thedesc_contained, &thedesc_immediate,
&thedesc, &thedesc_remote ]

The order of this list corresponds to the order of the proximity flag
generated for the actor and target object. The default, &thedesc, occurs at
the "local" level of proximity, where both actor and object share the same
top-level location. The proxdesc() function will work its way up the list if
no description method exists for the corresponding proximity flag. Since
most object definitions will have a "local" default (in our example,
thedesc) it will normally settle on the default. If it reaches the end of
the list it works its way downward from the expected proximity flag. If no
description method is found at all it returns nil.

Description routers have been created to handle sdesc, adesc, thedesc,
pluraldesc, ldesc, listendesc, smelldesc, readdesc, thrudesc, and actorDesc
(None yet has been created for heredesc). The routers build the message up
recursively: thedescR calls sdescR.

Using the router produces an extended or relative message, not using a
router simply means you get the default. So if you forget, and use actorDesc
instead of actorDescR in a display you will get the default.

--Kevin


Reply all
Reply to author
Forward
0 new messages