Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Inform] Nice simple straightforward question

8 views
Skip to first unread message

David Given

unread,
Aug 8, 2001, 6:59:15 AM8/8/01
to
...for a change.

I have a chair.

Object Chair "chair"
has
enterable open static transparent;

The player is in the chair. I want the player to be able to drop stuff,
but have the stuff end up on the floor, not in the chair.

What do I do?

There doesn't appear to be any hooks where the chair can refuse to accept
the object, which is the way I'd normally think about this. I could
subclass all objects to check to see if they're being put in the chair,
but that's horrifically ugly. I tried adding a Before to the player to
translate Drop actions to <Put noun d_obj>, but weird things I didn't
understand happened.

Suggestions?

--
+- David Given --------McQ-+
| Work: d...@tao-group.com | "You might think there's an explanation for this.
| Play: d...@cowlark.com | You would be wrong." --- Ally MacBeal
+- http://www.cowlark.com -+

Richard Bos

unread,
Aug 8, 2001, 7:41:02 AM8/8/01
to
d...@pearl.tao.co.uk (David Given) wrote:

> I have a chair.
>
> Object Chair "chair"
> has
> enterable open static transparent;
>
> The player is in the chair. I want the player to be able to drop stuff,
> but have the stuff end up on the floor, not in the chair.
>
> What do I do?
>
> There doesn't appear to be any hooks where the chair can refuse to accept
> the object, which is the way I'd normally think about this.

Doesn't the receive property handle this?

Richard

Adam Cadre

unread,
Aug 8, 2001, 8:45:31 AM8/8/01
to
> Object Chair "chair"
> has
> enterable open static transparent;
>
> The player is in the chair. I want the player to be able to drop stuff,
> but have the stuff end up on the floor, not in the chair.
>
> What do I do?
>
> There doesn't appear to be any hooks where the chair can refuse to
> accept the object, which is the way I'd normally think about this.

Hmm. "Receive:" doesn't take care of it?

If not, you could put a nameless scenery object in the room that looks
something like the following (warning, untested code):

Object -> floor_putter_onner
with react_before [;
Drop: if (player in chair) {
move noun to floor;
"Clunk. The floor gets more cluttered.";
}
],
has scenery;

-----
Adam Cadre, Brooklyn, NY
web site: http://adamcadre.ac
novel: http://www.amazon.com/exec/obidos/ASIN/0060195584/adamcadreac

Neil Cerutti

unread,
Aug 8, 2001, 10:10:50 AM8/8/01
to
David Given posted:

>...for a change.
>
>I have a chair.
>
>Object Chair "chair"
> has
> enterable open static transparent;
>
>The player is in the chair. I want the player to be able to drop stuff,
>but have the stuff end up on the floor, not in the chair.
>
>What do I do?

I might use an after routine in the room.

Object Room "The Room"
with
after [;
Drop:
move noun to self;
],
;

This silently moves successfully dropped items to the floor.

--
Neil Cerutti <cer...@together.net>
*** Your mule won the colony tap-dancing contest. You collect
$100. ***

Andrew Plotkin

unread,
Aug 8, 2001, 10:31:28 AM8/8/01
to
Adam Cadre <gri...@cascadia.drizzle.com> wrote:
>> Object Chair "chair"
>> has
>> enterable open static transparent;
>>
>> The player is in the chair. I want the player to be able to drop stuff,
>> but have the stuff end up on the floor, not in the chair.
>>
>> What do I do?
>>
>> There doesn't appear to be any hooks where the chair can refuse to
>> accept the object, which is the way I'd normally think about this.

> Hmm. "Receive:" doesn't take care of it?

No, Receive doesn't catch Drop actions under any circumstances. (The
logic I use is that Receive catches cases where something *enters* the
container. If the player is already inside, the object is already
contained. Imagine a birdcage rather than an armchair.)

(Of course, Receive isn't triggered when the *player* enters the
birdcage/armchair, but logic only takes us so far. :)

> If not, you could put a nameless scenery object in the room that looks
> something like the following (warning, untested code):

> Object -> floor_putter_onner
> with react_before [;
> Drop: if (player in chair) {
> move noun to floor;
> "Clunk. The floor gets more cluttered.";
> }
> ],
> has scenery;

Or just put the react_before on the chair, ei?

--Z

"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the borogoves..."
*
* Make your vote count. Get your vote counted.

Sean T Barrett

unread,
Aug 8, 2001, 1:49:30 PM8/8/01
to
Neil Cerutti <cer...@together.net> wrote:
>I might use an after routine in the room.
>This silently moves successfully dropped items to the floor.

Good point to avoid intefering with before-s, but you probably want to
put it on the chair, not the room, in case there's some other enterable
there:

Object chair "char"
with
react_after [;
Drop:
move noun to parent(self);
]
has enterable supporter;

Does the action "EMPTY CONTAINER" call "DROP" in a loop,
or do you have to also handle that case explicitly?

SeanB

Neil Cerutti

unread,
Aug 8, 2001, 2:32:55 PM8/8/01
to
Sean T Barrett posted:

>Neil Cerutti <cer...@together.net> wrote:
>>I might use an after routine in the room.
>>This silently moves successfully dropped items to the floor.
>
>Good point to avoid intefering with before-s, but you probably
>want to put it on the chair, not the room, in case there's some
>other enterable there:

Good idea.

>Object chair "char"
> with
> react_after [;
> Drop:
> move noun to parent(self);
> ]
> has enterable supporter;
>
>Does the action "EMPTY CONTAINER" call "DROP" in a loop,
>or do you have to also handle that case explicitly?

Empty calls the Transfer action which calls Drop for the case of Empty
(since Empty sets second to d_obj). So trapping Drop in the
chair's react_after will work just fine for the Empty action.

--
Neil Cerutti <cer...@together.net>

David Given

unread,
Aug 8, 2001, 12:39:41 PM8/8/01
to
In article <slrn9n2i69...@fiad06.norwich.edu>,
cer...@together.net (Neil Cerutti) writes:
[...]

> Object Room "The Room"
> with
> after [;
> Drop:
> move noun to self;
> ],
> ;

That works.

It's a bit ugly, isn't it? I successfully put the item down in the chair,
and the floor quietly whisks it away when I'm not looking.

I tried:

react_before
[;
Drop:
<<PutOn noun MyRoom>>;
]

...to try and translate the Drop action into a PutOn, but PutOn doesn't
work on rooms. I tried using d_obj instead, because PutOn does work on the
floor, but that just crashed obscurely.

--
+- David Given --------McQ-+

| Work: d...@tao-group.com | I think, therefore you are.
| Play: d...@cowlark.com |
+- http://www.cowlark.com -+

Andrew Plotkin

unread,
Aug 8, 2001, 4:40:47 PM8/8/01
to
David Given <d...@pearl.tao.co.uk> wrote:
> In article <slrn9n2i69...@fiad06.norwich.edu>,
> cer...@together.net (Neil Cerutti) writes:
> [...]
>> Object Room "The Room"
>> with
>> after [;
>> Drop:
>> move noun to self;
>> ],
>> ;

> That works.

> It's a bit ugly, isn't it? I successfully put the item down in the chair,
> and the floor quietly whisks it away when I'm not looking.

It might also play badly with react_before/react_after Drop clauses on
nearby objects.

> I tried:

> react_before
> [;
> Drop:
> <<PutOn noun MyRoom>>;
> ]

> ...to try and translate the Drop action into a PutOn, but PutOn doesn't
> work on rooms. I tried using d_obj instead, because PutOn does work on the
> floor, but that just crashed obscurely.

Possibly because that works by calling <<Drop noun>>, causing an
infinite loop.

I'd go with something like a chair react_before or react_after. I
guess react_after is better, unless you want to copy all the code from
DropSub into your routine. (Not that it's very much code.)

react_after [;
Drop:
if (player in self) {
if (noun in self) {
move noun to parent(self);
}
}
],

(Untested.) Note the two tests: the first ensures that the routine
stays quiet when the player *isn't* in the chair, and the second makes
sure the Drop actually succeeded. (If the object is glued to your
fingers, this shouldn't be a way around it.)

Sean T Barrett

unread,
Aug 8, 2001, 4:54:42 PM8/8/01
to
Neil Cerutti <cer...@together.net> wrote:
>Sean T Barrett posted:

>>Good point to avoid intefering with before-s, but you probably
>>want to put it on the chair, not the room, in case there's some
>>other enterable there:
>
>Good idea.

Too bad I forgot to flesh it out.

>>Object chair "char"
>> with
>> react_after [;
>> Drop:
>> move noun to parent(self);
>> ]
>> has enterable supporter;

I imagine the above still reacts to other drops, what I meant
to do was condition it this way:

Object chair "char"
with
react_after [;
Drop:

if (noun in self)


move noun to parent(self);
]
has enterable supporter;

Maybe there are other cases that will help with too, I dunno.

SeanB

Sean T Barrett

unread,
Aug 8, 2001, 5:02:34 PM8/8/01
to
In article <d0qrk9...@127.0.0.1>, David Given <d...@pearl.tao.co.uk> wrote:
>It's a bit ugly, isn't it? I successfully put the item down in the chair,
>and the floor quietly whisks it away when I'm not looking.

Well, yes. Inform isn't consistent enough to do it right. I mean, you could
move the player to the room, do <Drop noun>, and then move the player back...
but that's REALLY ugly.

Using an after has the advantage that it allows all the before code to run
first and do special processing, e.g. if you had an undroppable object it
would still work. Trying to hook react_before will prevent that.

But hooking after has the disadvantage that it occurs after the "physics"
have already been run. However, you know exactly what those physics are,
since they're defined in DropSub, so while it's a hack, I think it's a
trustworthy hack (unless you have some other object with an after for ##Drop
which has some significant effect but returns FALSE to continue with normal
processing).

As noted in another thread, a more ideal solution would involve a library
that had a 'before', 'do-action', 'after', and 'final print', each of which
could be intercepted and stop the whole process early. But that's not how
Inform's library works (and in practice it's plausible to want to break it
down with even more granularity, and yet none of this necessarily copes
with interactions between overrides at different stages).

SeanB

Andrew Plotkin

unread,
Aug 8, 2001, 6:36:26 PM8/8/01
to
Sean T Barrett <buz...@world.std.com> wrote:

> As noted in another thread, a more ideal solution would involve a library
> that had a 'before', 'do-action', 'after', and 'final print', each of which
> could be intercepted and stop the whole process early. But that's not how
> Inform's library works (and in practice it's plausible to want to break it
> down with even more granularity, and yet none of this necessarily copes
> with interactions between overrides at different stages).

Yes, it's amazing how many levels of granularity you can add and still
not have covered every plausible case. :(

The whole mess begs to be rethought into a more general form. Whenever
I try, my head falls off and I have to stop. How does Platypus deal
with it?

Sean T Barrett

unread,
Aug 9, 2001, 12:10:31 AM8/9/01
to
Andrew Plotkin <erky...@eblong.com> wrote:
>react_after [;
> Drop:
> if (player in self) {
> if (noun in self) {
> move noun to parent(self);
> }
> }
>],

On further reflection, one problem with this is if you have
something else in your game that react_afters and returns TRUE
before this gets to run. But I can't think of too many reasons
you'd do that. (To cross threads, one reason would be to have a
character print a reaction to the drop event, with the reaction
printed after the "Dropped." message.)

SeanB

David Given

unread,
Aug 9, 2001, 8:02:40 AM8/9/01
to
In article <9kseta$k0k$1...@news.panix.com>,
Andrew Plotkin <erky...@eblong.com> writes:
[...]

> The whole mess begs to be rethought into a more general form. Whenever
> I try, my head falls off and I have to stop. How does Platypus deal
> with it?

I don't know about Platypus, but the way I would have expected things to
work is:

Player tries to drop the object. This is translated to <PutIn object
player.location>.
PutIn:
Is the player willing to drop the object?
Is the object willing to be dropped?
Is the destination willing to accept the object?
Move the object.
Does the player want to react?
Does the object want to react?
Does the destination want to react?

In my case, all I'd do is override the chair's Accept action. Rather than
just refuse ("You can't put that there"), I'd restart the action with
<PutIn object chair.location>.

--
+- David Given --------McQ-+ "P.S. If you do not receive this, of course it
| Work: d...@tao-group.com | must have been miscarried; therefore I beg you to
| Play: d...@cowlark.com | write and let me know." --- Sir Boyle Roche, in a
+- http://www.cowlark.com -+ letter

Neil Cerutti

unread,
Aug 9, 2001, 10:31:56 AM8/9/01
to
David Given posted:

>In article <9kseta$k0k$1...@news.panix.com>,
> Andrew Plotkin <erky...@eblong.com> writes:
>[...]
>> The whole mess begs to be rethought into a more general form. Whenever
>> I try, my head falls off and I have to stop. How does Platypus deal
>> with it?
>
>I don't know about Platypus, but the way I would have expected things to
>work is:
>
>Player tries to drop the object. This is translated to <PutIn object
>player.location>. >PutIn:
> Is the player willing to drop the object?
> Is the object willing to be dropped?
> Is the destination willing to accept the object?
> Move the object.
> Does the player want to react?
> Does the object want to react?
> Does the destination want to react?
>
>In my case, all I'd do is override the chair's Accept action.
>Rather than just refuse ("You can't put that there"), I'd
>restart the action with <PutIn object chair.location>.

Unfortunately, DropSub just does:

move noun to parent(player);

If you change that line to your suggestion:
<<PutOn noun (parent(player))>>;

Then you unfortunately get an infinite loop because PutOn calls
<<Drop noun>> for when second == d_obj || player in second. :-(

Perhaps instead we just need to call the Receive fake action for
when parent(player) != real_location (since locations generally
do not receive fake actions, but other objects do)?

in DropSub:

if (parent(player) ~= real_location)
{ action=##Receive;
if (RunRoutines(parent(player),before)~=0) { action=##Drop; return; }
action=##Drop;
}

Then you could trap Receive in the chair:

Object -> chair "chair"
has supporter enterable scenery
with
name 'chair',
before [;
Receive:
move noun to real_location;
"Dropped.";
],
;

Unfortunately this results in bizarre behavior if the player
really does try to put things in the chair.

Here's another idea.

How about instead using a chair that can't be sat on when there
are items in it, and which causes items to fall off when the
player is sitting in it?

Object -> chair "chair"
has supporter enterable scenery
with
name 'chair',
before [;
Receive:
if (player in self) {
"There's no room with your big ass in the way.";
}
],
each_turn [ x y;
if (player in self && children(self)) {
new_line;
x = child(self);
while (x) {
print (The) x, " falls to the floor.^";
y = sibling(x);
move x to location;
x = y;
}
}
],
;

It might still be confusing. Oh well.

--
Neil Cerutti <cer...@together.net>

Andrew Plotkin

unread,
Aug 9, 2001, 11:26:33 AM8/9/01
to
David Given <d...@pearl.tao.co.uk> wrote:
> In article <9kseta$k0k$1...@news.panix.com>,
> Andrew Plotkin <erky...@eblong.com> writes:
> [...]
>> The whole mess begs to be rethought into a more general form. Whenever
>> I try, my head falls off and I have to stop. How does Platypus deal
>> with it?

> I don't know about Platypus, but the way I would have expected things to
> work is:

> Player tries to drop the object. This is translated to <PutIn object
> player.location>.
> PutIn:
> Is the player willing to drop the object?
> Is the object willing to be dropped?
> Is the destination willing to accept the object?
> Move the object.
> Does the player want to react?
> Does the object want to react?
> Does the destination want to react?

Now the fun starts.

* Sometimes an action limitation is really "the player is unwilling"
-- that is, it should override other limitations because the
protagonist never tried -- but you want the code to be in the
container or object anyway.

Example: a red-hot mailbox. "put letter in mailbox" returns "you can't
get anywhere near the mailbox", which occurs before the letter's
flammability-checking code causes it to burst into flame. The system
should not force you to put that message on the player -- player
objects would become horribly bloated.

* If the letter is glued to your fingers (an object-willing-to-
be-dropped check) where should that come in the sequence of checks? It
actually depends on the game; the author should be able to choose
either way. Furthermore, that code could be either on the letter or on
the player, depending on whether the game has one gluey player stuck
to many objects, or one gluey letter that can stick to many things.

* What order do those last three lines come in? I would have reversed
them (destination / object / player), and I can't see any compelling
argument either way.

* How do you make sure that exactly one result message is printed?
Inform's rule (that you should print-and-return-true, and that this
ends the action), feels clumsy at times -- but it does tend to prevent
the error of three "you can't do that" messages strung together
followed by "okay". You give a sequence of checks that can abort at
any time -- but is there an implicit "print 'okay'" at the end?

* If so, how do you deal with one action invoking another? Again,
Inform's convention is that you do <<action>>, which returns true --
all responsibility for printing falls to the called action. But there
*are* implicit takes, implicit drops, and fake actions like Receive.
Inform's handling of these is, as we know, ugly.

* Your model makes sense for a *supporter* if the player is either on
or off of it. ("put object on chair" should go through the same
sequence of checks whether the player is sitting there or not.) But
for a *container*, such as the ever-popular giant birdcage, the
sequence is different depending on whether the player is locked in or
out.

The general case is that an object has to move out of every container
the player is in, until it reaches the common ancestor of the player
and destination, and then it has to move into every container the
destination is in. If the player is in the destination, the object
never moves out of or into the destination at all -- it's already
contained. But that doesn't match how we think about armchairs.

Inform mostly unifies supporters and containers (except for how
they're described), but that leads to problems like this.

There. That's off the top of my head, which has now fallen off.

Neil Cerutti

unread,
Aug 9, 2001, 11:31:50 AM8/9/01
to
Andrew Plotkin posted:
>Now the fun starts.

I just love the Subject line of this thread.

--
Neil Cerutti <cer...@together.net>
*** Your mining mules have deteriorated from heavy use and cost
$100 each to repair. The total cost is $400. ***

David Given

unread,
Aug 9, 2001, 12:31:49 PM8/9/01
to
In article <9kua39$1ei$1...@news.panix.com>,

Andrew Plotkin <erky...@eblong.com> writes:
[...]
> Now the fun starts.

Sigh. Well, it sounded good at the time...

How about a physics-based model?

Put x in y:
1. Move the player next to the y
2. Move x from the player's inventory into the player's hand
3. Move x from the player's hand into y

...where each `move' consists of:

a. Is the source willing to let go?
b. Is the object willing to move?
c. Is the destination willing to receive?
d. Do the move.

Red-hot mailbox problem: override 1c. The mailbox will not let you near.

Glue-covered letter: override 3b. The letter won't leave your hand.

Birdcage: override 1a. You can't get out of the birdcage.

I'm not sure about the order of a, b, c. I think that we've broken it down
sufficiently that the order shouldn't matter. If anything fails, that
stage of the operation is cancelled; so with the glue-covered letter, you
end up standing next to the mailbox with the letter in your hand.

If the mailbox is on the other side of a vast chasm, then 1a fails (the
source for the player being the room). It won't let you across the chasm.

If you want to throw the letter instead, you'd use a different order of
events.

Throw x at y:
1. Move x from the player's inventory into the player's hand
2. Move x from the player's hand to the air next to the player
3. Move x from next to the player to the air next to y
4. Hit y with x

If there's a glass wall in the middle of the room, 3a will fail. If the
letter is covered in glue, 2a will fail. If the mailbox is red-hot, then
4c will fail in a different way; it'll destroy the letter, print a message
stating that the letter has burst into flaims, and fail.

One action invoking another... hmm. I think it ought to be possible to
insert actions into the 1, 2, 3... chain. So, if you're in the birdcage,
moving the player to an object outside the birdcage might get expanded:

Put x in y:
1. Move player next to y
(opening the door)
1. If door is locked, unlock it
(unlocking the door)
1. Move player next to door
1. Move key from player's inventory into hand
2. Unlock door
2. Open door
...

..resulting in:

> put birdseed in goldfish bowl
(opening the door first)
(taking the key)
(unlocking the door)
(putting the key back into your pocket)
(taking the birdseed)
You feed the goldfish.

Okay, contrived, and you probably wouldn't want to automate to this
extent[1]. But possibly powerful.

So where are the obvious flaws above?


[1]
> solve game
(doing all the puzzles first)
*** You have won! ***

Neil Cerutti

unread,
Aug 9, 2001, 1:21:40 PM8/9/01
to
Joe Mason posted:
>In article <3b711e58....@news.worldonline.nl>,
>Richard Bos <in...@hoekstra-uitgeverij.nl> wrote:
>>d...@pearl.tao.co.uk (David Given) wrote:
>In my game I use this. It's a copy of Drop from the standard
>library with the two blocks starting "if (parent(player) ~=
>RealLoc())" dropped in:
>
>Replace DropSub;
>
>...
>
>[ RealLoc;
> if (location==TheDark)
> return real_location;
> else
> return location;
>];

Since Inform lib 6/10 you don't need the RealLoc function any more.
Just use real_location. It now always holds location.

>[ DropSub;
> ! if the player is in a container or supporter, causes it to run a
> ! <Receive> action, as if the player had typed 'PUT object ON/IN
> ! container/supporter'
>
> if (noun == player) return L__M(##PutOn, 4);
> if (noun in parent(player)) return L__M(##Drop,1,noun);
> if (noun notin player) return L__M(##Drop,2,noun);
> if (noun has worn)
> { L__M(##Drop,3,noun);
> <Disrobe noun>;
> if (noun has worn) rtrue;
> }
>
> if (parent(player) ~= RealLoc() ) {
> receive_action=##Drop;

Ah! I didn't think of that (receive_action). Good solution.

--
Neil Cerutti <cer...@together.net>
*** One of your mules lost a bolt. Repairs cost you $150. ***

Joe Mason

unread,
Aug 9, 2001, 12:38:22 PM8/9/01
to

Receive: only catches the action "put x on y", not "drop x" (when the player
is on y). (Speaking of which, I'd probably give the chair "supporter" as
well as "enterable".)

In my game I use this. It's a copy of Drop from the standard library with
the two blocks starting "if (parent(player) ~= RealLoc())" dropped in:

Replace DropSub;

...

[ RealLoc;
if (location==TheDark)
return real_location;
else
return location;
];

...

[ DropSub;
! if the player is in a container or supporter, causes it to run a
! <Receive> action, as if the player had typed 'PUT object ON/IN
! container/supporter'

if (noun == player) return L__M(##PutOn, 4);
if (noun in parent(player)) return L__M(##Drop,1,noun);
if (noun notin player) return L__M(##Drop,2,noun);
if (noun has worn)
{ L__M(##Drop,3,noun);
<Disrobe noun>;
if (noun has worn) rtrue;
}

if (parent(player) ~= RealLoc() ) {
receive_action=##Drop;

action=##Receive;
if (RunRoutines(parent(player), before)~=0) { action=##Drop; return; }
else action = ##Drop;
}

move noun to parent(player);
if (AfterRoutines()==1) rtrue;

if (parent(player) ~= RealLoc()) {
action=##Receive;
if (RunRoutines(parent(player), after)~=0) { action=##Drop; return; }
else action=##Drop;
}

if (keep_silent==1) rtrue;
return L__M(##Drop,4,noun);
];

Joe

John Colagioia

unread,
Aug 10, 2001, 8:34:46 AM8/10/01
to
David Given wrote:
[...]

> The player is in the chair. I want the player to be able to drop stuff,
> but have the stuff end up on the floor, not in the chair.
> What do I do?

I could swear I've seen this in the IDM somewhere, but I can't track it down at
the moment. However, I seem to recall that the example used the each_turn()
method, basically, to remove dropped objects from scope. The idea was that
obj.each_turn() runs after everything else--before()s, after()s, daemon()s, etc.,
and thus has the "final say" in the matter (unless, I assume, there's more than
one each_turn, at which point they battle it out or something...).

Ah--exercise 49, p150. Not actual programming. Just a "why would you." Answer
on p462.

Anyway, I'd suggest giving the chair an each_turn() method which simply takes its
contents (minus any animates) and places them in its own parent (presumably the
room, though not necessarily).

I'm steering clear of the related "what order should things happen in" discussion,
not too far from here...


OKB -- not okblacke

unread,
Aug 19, 2001, 3:02:24 PM8/19/01
to
This is one of those late posts we all hear so much about, and I
apologize, but, just some general thoughts on action-checking sequences. . .

The main problem I have with Inform's action handling is that it does not
seem to be consistent for all objects in all cases. For example, as has been
mentioned, Receive does not apply to the situation of the player moving into a
container.

In general the fake-action system seems silly to me. The DM mentions a
system that I think would be far better: before and after-like routines on the
"second" as well as the "noun". Why should you have to explicitly define a
fake action whenever you want this kind of checking?

>* What order do those last three lines come in? I would have reversed
>them (destination / object / player), and I can't see any compelling
>argument either way.

This is the really tough question. We know there are tons of things that
need to be checked; the question is what order we should check them in.

It's looking more and more like a once-through "checklist" just isn't
going to work. It doesn't make sense to require the programmer to juggle
things like room before, player before, and react_before to fit the world
model, which is often the case now (i.e., things that people think "really
should" go in a react_before wind up having to go in the room's before because
of the way the checks are sequenced).

It seems like what we need is some kind of contiuum of checks in which
order is not a concern. (Actually, I think I just like the word "continuum".)
The basic idea is this: if nothing says an action is supposed to fail, it
should succeed. Each check for failure should get its say, and THEN, after
everything's done, we should worry about what error message to print.

A common and annoying concern when writing before/after/etc. rules is
that where you place your custom check (for things like gluey hands and
birdcages) often must be decided by what error messages you want your custom
message to precede and/or follow. What I think would be cool is a situation
where react_before, before, and other such things differed only in the scope of
their reactivity (i.e., what they react to), not in their dependencies on one
another.

I guess another way of putting this (or maybe just a very closely related
idea) is that we need to separate the internals of what fails and what succeeds
from the printing of the messages informing the player of those successes and
failures. Right now, when you do something like return true from a before
routine, you have no choice but to print your error message right then and
there. Your code basically says to the library "STOP RIGHT NOW and the reason
is: You can't see any such thing." What we need is a way for your code to say
"This action shouldn't succeed because: You can't see any such thing." -- but
NOT be forced to take the momentous step of actually printing those fateful
words on the screen.

This would mean having three stages: a check stage, a "do it" stage, and
a "print messages" stage. A routine in the check stage could indicate that the
action should fail, but the error message shouldn't be printed until all the
other routines get their say. If ANY of the checks came up negative, the
action would fail, but only after all of this had been resolved would we get
down to the task of sorting out exactly what caused it to fail and what message
we should print.

--OKB (Bren...@aol.com) -- no relation to okblacke

"Do not follow where the path may lead;
go, instead, where there is no path, and leave a trail."
--Author Unknown

Sean T Barrett

unread,
Aug 19, 2001, 6:40:24 PM8/19/01
to
OKB -- not okblacke <bren...@aol.comRemove> wrote:
> It's looking more and more like a once-through "checklist" just isn't
>going to work. It doesn't make sense to require the programmer to juggle
>things like room before, player before, and react_before to fit the world
>model, which is often the case now (i.e., things that people think "really
>should" go in a react_before wind up having to go in the room's before because
>of the way the checks are sequenced).
> It seems like what we need is some kind of contiuum of checks in which
>order is not a concern. (Actually, I think I just like the word "continuum".)
>The basic idea is this: if nothing says an action is supposed to fail, it
>should succeed. Each check for failure should get its say, and THEN, after
>everything's done, we should worry about what error message to print.

The problem with the continuum of checks where order is not a
concern is that you either need two copies of your tests (one
which reports the error condition and one which prints the
error message), you need one copy with a global variable that
says whether to print or not (a la 'keep_silent'), or you need
the one place to be able to return both an error code and a
printable message. Since that printable message might want to
print things like the name of the object, it really needs to
return a little callable function which prints.

The first one is going to lead the bugs, the second one is an
ugly hack, and the third one isn't feasible in Inform--or Tads 2--
but it will be in Tads 3.

>If ANY of the checks came up negative, the
>action would fail, but only after all of this had been resolved would we get
>down to the task of sorting out exactly what caused it to fail and what message
>we should print.

Realistically, I suspect we'll still find problems with this task
of "sorting out...what message we should print". We can assign each
error a priority number, and print the one with the highest priority,
but then that's not much different from deciding what order to call
things in. (We could achieve it directly by having you call one function
to determine the calling order, then call in that order and
print-and-return-true like we do now.) In other words, this is just
the same old n-ary dispatch problem: traditional object-orientation works
by pushing processing out onto ONE object, but the interaction of two
objects doesn't belong to just one object.

One thing I would argue for is more simulationism: putting all the work
(or more of it, anyway) in one place (e.g. the verb routine) can actually
allow the verb routine to work out the prioritizations of rules in a
much more natural way. We take this for granted with things like very
basic object manipulation; and this is how a lot of n-ary object
processing that isn't exception-driven works--there's a driver algorithm
which calls into all the involved objects to get at their properties.

On a certain level, some amount of error prioritization can be done
from a sort of simulationist perspective:

That isn't an action that's feasible in any world.
That isn't an action that the PC is willing to try.
That is an action the PC thinks about trying, but realizes is doomed.
That is an action the PC starts to try, but an NPC prevents it.
That is an action the PC starts to try, but can't bring to fruition.
That is an action that the PC brings to fruition.

Here, each of the items higher on the list would come first, because
they come earlier in the causal chain that allows the action to occur.
(Player conceives a rational action, PC considers it, PC attempts to
execute it, etc.) But even there I've kind of arbitrarily decided
that NPCs influence "earlier" than other objects, or the order of decision
with the PC being scared to try something versus the PC realizing it
won't work.

I guess my design intent with the former is the NPC reaction stage
represents non-physical reactions--NPCs perceive the beginning of
the action and anticipate the outcome, and prevent it earlier, due
to anticipation--whereas the following stage represents physical laws
that purely follow from once you actually get to trying the action.

So that makes sense for

> TAKE GLASS
Bob grabs your hand. "Don't touch that!"

versus

> TAKE GLASS
It's nailed to the table.

but it leaves out a lot of subtle timing that's possible:

> TAKE GLASS
You reach out for it, but your hand bumps into an invisible
wall of force surrounding it.

(Note the obviously desired priority relative to the above two.)

Do we want to track the "distance" at which the action fails?
Represent the time from starting the action as t=0, the time from
completing the action as t=100, and have each failure say "when"
the failure would occur?

And what about when the action is carried nearly to completion,
but *then* at the last minute the PC decides not to carry it out?

Or what about when you've carefully tweaked your numbers, and
reaction A always wants to happen at time 50 relative to everything
else, reaction B always wants to happen at time 75 relative to
everything else, oh, but in this one case, B wants to happen before A?
Now we need a special-case system for handling the special cases
not handled by the general system with which we'd tried to replace
the old special-case system.

Argh.

SeanB

Dan Shiovitz

unread,
Aug 20, 2001, 12:03:38 AM8/20/01
to
In article <GIC6z...@world.std.com>,

Sean T Barrett <buz...@world.std.com> wrote:
>OKB -- not okblacke <bren...@aol.comRemove> wrote:
>> It's looking more and more like a once-through "checklist" just isn't
>>going to work. It doesn't make sense to require the programmer to juggle
>>things like room before, player before, and react_before to fit the world
>>model, which is often the case now (i.e., things that people think "really
>>should" go in a react_before wind up having to go in the room's before because
>>of the way the checks are sequenced).
[..]

>Realistically, I suspect we'll still find problems with this task
>of "sorting out...what message we should print". We can assign each
>error a priority number, and print the one with the highest priority,
>but then that's not much different from deciding what order to call
>things in. (We could achieve it directly by having you call one function
>to determine the calling order, then call in that order and
[..]

>On a certain level, some amount of error prioritization can be done
>from a sort of simulationist perspective:
>
> That isn't an action that's feasible in any world.
> That isn't an action that the PC is willing to try.
> That is an action the PC thinks about trying, but realizes is doomed.
> That is an action the PC starts to try, but an NPC prevents it.
> That is an action the PC starts to try, but can't bring to fruition.
> That is an action that the PC brings to fruition.
[..]

>So that makes sense for
>
> > TAKE GLASS
> Bob grabs your hand. "Don't touch that!"
>
>versus
>
> > TAKE GLASS
> It's nailed to the table.
>
>but it leaves out a lot of subtle timing that's possible:
>
> > TAKE GLASS
> You reach out for it, but your hand bumps into an invisible
> wall of force surrounding it.
>
>(Note the obviously desired priority relative to the above two.)
>
>Do we want to track the "distance" at which the action fails?
>Represent the time from starting the action as t=0, the time from
>completing the action as t=100, and have each failure say "when"
>the failure would occur?

I assume this has been suggested before, but I think you'd probably
simplify things by admitting that taking an object is non-atomic, and
breaking it down into actions which are (ie, free a hand, reach for
the glass, touch the glass, grasp the glass, lift the glass, put the
glass in your inventory, release the glass). Then let the programmer
put before/after/during rules on each of those actions and you can get
most of the stuff you want without making it substantially more
complex or requiring a lot of special-casing (and you could
potentially hack in 'think of the action' and 'agree to start the
action' as atomic operations at the start of the list).

These kind of atomic actions look like they increase the work for the
programmer but of course they don't really because they're shared
across verbs -- all verbs which require touching the object run the
atomic reach-for and touch actions, etc. So instead of

if (v = touchVerb or v = pushVerb or v = pullVerb or v = eatVerb ...
{
"It's too far away.\n";
exit;
}

you'd just need

if (v = reachAtom)
{
"It's too far away.\n";
exit;
}

>SeanB
--
Dan Shiovitz :: d...@cs.wisc.edu :: http://www.drizzle.com/~dans
"He settled down to dictate a letter to the Consolidated Nailfile and
Eyebrow Tweezer Corporation of Scranton, Pa., which would make them
realize that life is stern and earnest and Nailfile and Eyebrow Tweezer
Corporations are not put in this world for pleasure alone." -PGW


Sean T Barrett

unread,
Aug 20, 2001, 12:49:34 AM8/20/01
to
Dan Shiovitz <d...@cs.wisc.edu> wrote:
>I assume this has been suggested before, but I think you'd probably
>simplify things by admitting that taking an object is non-atomic, and
>breaking it down into actions which are (ie, free a hand, reach for
>the glass, touch the glass, grasp the glass, lift the glass, put the
>glass in your inventory, release the glass).

I suspect that when we read about Implementors talking about how
modern IF hasn't really moved beyond what they were doing, this is
the sort of thing they're thinking of.

And as a simulationist, I'm all for it. But "taking" is one of the
things we simulate decently already; the fact that people might be
more comfortable with a more simulationist implementation of TAKE
doesn't really clear up to me how to handle less simulationy-verbs
(SING, BLOW ON COW), and especially verbs that authors create
themselves (which shouldn't require them to make such detailed
breakdowns)--for which I think we'd still want a convenient-override
scheme.

Admittedly, I have sketched out designs for implementing both
a general concept of "nearness" (useful for a breakdown of large
scale actions, for implementing puzzles like "MOVE CHAIR NEAR
BOOKSHELF", and a few other things), a notion of "connectedness"
(useful for ropes and a few other things), and then a detailed
model of hands (using nearness to move hands near things, and
then connectedness to attach hands to things when they are gripped);
but I felt like the system would be overall too unwieldy, too
complex to generate text for (need to print the details if something
interesting happens, but not otherwise), and too baroque for
authors to understand when they just want to make a simple
override; and they would require a brand new library anyway,
so it would be far too likely to be an experiment that would
founder in the end. My apologies for the length of that sentence.

Perhaps because such techniques would also provide sufficiently
benefits for sane overridability they would still be worth it, but
I don't think I'm willing to invest the time in it. I guess it
would be interesting to try a small-scale implementation of it,
but I'm not sure there's any such thing.

SeanB

OKB -- not okblacke

unread,
Aug 20, 2001, 9:52:05 AM8/20/01
to
buz...@world.std.com (Sean T Barrett) wrote:
>The problem with the continuum of checks where order is not a
>concern is that you either need two copies of your tests (one
>which reports the error condition and one which prints the
>error message), you need one copy with a global variable that
>says whether to print or not (a la 'keep_silent'), or you need
>the one place to be able to return both an error code and a
>printable message. Since that printable message might want to
>print things like the name of the object, it really needs to
>return a little callable function which prints.

Basically, I agree that getting a system like this to work is going to
take some doing, but your specific criticism seems to imply that we're using a
sequence like Test->DoAction->TestAgain, whereas I'm thinking more like
Test->DoAction->PrintMessage. The key here is that the post-action bit does
not re-test; its sole task is the printing of the message. Now, we're
definitely going to need some kind of storage for the results of the checks; it
could be a bunch of globals, or it could be an array, or the stack.

To clarify what I mean, I'm talking about an internal parser routine with
a sequence of steps something like this:
1. Loop through all the check routines and call them, keeping track of which
routines returned which codes.
2. If all of the checks came up positive, "do the action" (i.e., run the verb
routine), otherwise skip to the next step.
3. Analyze the list of error codes returned in step 1 to determine an
appropriate error message. (This could make calls to separate routines which
are NOT the actual check routines, but instead are desgined to return
information about the priority of this error in this situation.)
4. Print the appropriate error message.

The actual checks only happen ONCE, before the action, but their
significance is evaluated later.

The thing that bothers me a bit is where to fit "after" rules into this
scheme. It seems that they could go in the "prioritizing" routines mentioned
in step 3, but it might be better to add a separate step (or even two, one
before the printing of the error message and one after).

John Colagioia

unread,
Aug 20, 2001, 11:11:39 AM8/20/01
to
Sean T Barrett wrote:
[...]

> The problem with the continuum of checks where order is not a
> concern is that you either need two copies of your tests (one
> which reports the error condition and one which prints the
> error message), you need one copy with a global variable that
> says whether to print or not (a la 'keep_silent'), or you need
> the one place to be able to return both an error code and a
> printable message. Since that printable message might want to
> print things like the name of the object, it really needs to
> return a little callable function which prints.

[...]

Hm. What about taking an entirely different approach? I realize this would be a
nightmare to code, but what about "broadcasting" the action to all objects in scope,
where responding objects would pre-empt the action.

That is, if the player attempts to take an object, all objects are notified by the
player, "I'm going to take the box--anyone have any problems?" Then, an object (any
object, really) intending to block that action responds, and repeats the process,
until the pre-empting object is no longer pre-empted. At that point, it completes
the action.

As I said, it'd probably be a nightmare to code, but it would certainly make it
relatively easy to handle all cases.


Andrew Plotkin

unread,
Aug 20, 2001, 12:07:02 PM8/20/01
to
Sean T Barrett <buz...@world.std.com> wrote:
> Dan Shiovitz <d...@cs.wisc.edu> wrote:
>>I assume this has been suggested before, but I think you'd probably
>>simplify things by admitting that taking an object is non-atomic, and
>>breaking it down into actions which are (ie, free a hand, reach for
>>the glass, touch the glass, grasp the glass, lift the glass, put the
>>glass in your inventory, release the glass).

> I suspect that when we read about Implementors talking about how
> modern IF hasn't really moved beyond what they were doing, this is
> the sort of thing they're thinking of.

> And as a simulationist, I'm all for it. But "taking" is one of the
> things we simulate decently already; the fact that people might be
> more comfortable with a more simulationist implementation of TAKE
> doesn't really clear up to me how to handle less simulationy-verbs
> (SING, BLOW ON COW), and especially verbs that authors create
> themselves (which shouldn't require them to make such detailed
> breakdowns)--for which I think we'd still want a convenient-override
> scheme.

I think Inform's conception of class 1, class 2, and class 3 actions
turns out to be closer to the mark than one might think.

A few actions (get, drop, put-in) have a lot of library support --
they're the ones whose library routines are long and complicated and
deal with a lot of weird cases. Those are the actions which can be
(and already are, to some extent) broken down into subactions. *But*
there aren't that many of those. The fact that "take" is broken down
into five phases (or whatever) doesn't mean that "push" will be --
we're not creating an exponential explosion of action types.

Most common actions, like "push" or "touch", boil down to a couple of
subactions, and they really all use the same sequence. "See obj; touch
obj; fiddle with obj" is about it. These are the class 2 actions, and
Inform's system of before/react_before/after/react_after is pretty
well tuned for those. (Except that the "touch" stage is handled by the
hacked-in and extremely unpleasant ObjectIsUntouchable() system.)

A few actions (notably "examine", "read", and "search", although not
for every object) just use "see obj; fiddle with obj"; touching is
unnecessary.

What I find I really need is

- Overriding one part of a sequence must not break another part. (If
you put a before:Touch clause on an object, you have to call
ObjectIsUntouchable() yourself, or that mechanism breaks. This is why
ObjectIsUntouchable() is the wrong approach.)

- It must be easy to add stages for a particular object. (For all
actions that pass through a given stage.)

- It must be easy to add stages for a particular action. (For all
objects subject to that action.)

- It must be easy to change the default sequence for *a particular
action/object combination*. (Most objects can be examined without
touching, but a scroll must be unrolled in your hands to be read, and
"examine scroll" should do that implicitly.)

- The implicit take (in many actions) should be handled by the
mechanism of this sequence/subaction stuff. This means, in particular,
that I can override implicit take for a particular action/object
combination. (See the perennial "how do I turn off implicit taking for
'eat'?" FAQ in Inform.)

- Behavior *must* be predictable. Broadcasting to all objects in scope
is a good idea (as suggested in another post, and also as implemented
in Inform's react_before mechanism). But if the order in which objects
respond (pre-empting each other) is unpredictable, I can't use that.
(Inform's react_before does suffer from this problem. The only reason
it's tolerable is that I obsessively avoid having two different
react_before effects on the same action which can apply in the same
room. That's a pretty severe restriction.)

OKB -- not okblacke

unread,
Aug 20, 2001, 3:27:03 PM8/20/01
to
Andrew Plotkin erky...@eblong.com wrote:
>I think Inform's conception of class 1, class 2, and class 3 actions
>turns out to be closer to the mark than one might think.

I don't like this classification system, mainly because it makes a
distinction between verbs which differ only in the actual code contained within
the verb routines. (For example, taking something and pulling something are
physically very similar actions in real life, but they are arbitrarily
differentiated in Inform just because the default PullSub doesn't change the
game state.)

>A few actions (get, drop, put-in) have a lot of library support --
>they're the ones whose library routines are long and complicated and
>deal with a lot of weird cases. Those are the actions which can be
>(and already are, to some extent) broken down into subactions. *But*
>there aren't that many of those. The fact that "take" is broken down
>into five phases (or whatever) doesn't mean that "push" will be --
>we're not creating an exponential explosion of action types.

I would prefer that ALL actions have access to the most complex array of
machinations. If the programmer decides that the new action he's creating
doesn't need them all, he should be able to pick and choose the ones he needs.

>Most common actions, like "push" or "touch", boil down to a couple of
>subactions, and they really all use the same sequence. "See obj; touch
>obj; fiddle with obj" is about it. These are the class 2 actions, and
>Inform's system of before/react_before/after/react_after is pretty
>well tuned for those. (Except that the "touch" stage is handled by the
>hacked-in and extremely unpleasant ObjectIsUntouchable() system.)

Hmm, this makes me think that it might be cool to have a "toolbox" of
different phases which could be applied to actions at will. So you'd have
things like "See obj" and "touch obj" and "manipulate obj" and you could say,
"okay, this object's sequence is See->Touch->Manipulate".

>- Overriding one part of a sequence must not break another part. (If
>you put a before:Touch clause on an object, you have to call
>ObjectIsUntouchable() yourself, or that mechanism breaks. This is why
>ObjectIsUntouchable() is the wrong approach.)

This is why I think it would be cool to have a separate stage at the
beginning where all the hitches in the getalong are logged. Maybe have a
routine which checks a bunch of stuff (is the object touchable, is it
carryable, etc.) and stores the results of those checks away somewhere so that
later code can refer back to it.

Also, although I haven't actually used Platypus, its documentation
describes a very cool-sounding sequence. Basically, there are three "internal"
parts: validation (the action is determined to be feasible), action (the action
actually happens, and notification (the player is informed of the result). In
between each of these, and at the beginning, and at the end, the game author
can butt in with his own code. So you basically have custom validation ->
validation -> custom action -> action -> custom notification -> custom
notification -> custom mop-up.

One problem with this (as with the standard library) is that the "after"
bits do not apply if you give a custom action, so there's no way to do what you
want, namely, override just one part without disturbing those later in the
sequence. Another problem is that if the action is NOT possible, the message
is printed right there in the validation stage.

<snipped some more wishes>


>- The implicit take (in many actions) should be handled by the
>mechanism of this sequence/subaction stuff. This means, in particular,
>that I can override implicit take for a particular action/object
>combination. (See the perennial "how do I turn off implicit taking for
>'eat'?" FAQ in Inform.)

So you're saying implicit take should be a part of the sequence just like
"see" and "touch"? Sure. The fact that it's not that way as it stands is just
mind-boggling.

I think one thing which would make the design a lot cleaner would be
separating the "error found" step from the "stop the sequence" step and the
"print error message step". These are conflated almost everywhere in the
standard library -- if an error is found, everything suddenly stops and a
message is printed right away. The programmer should to be able to intercede
and say "Ignore this error, don't stop the sequence." or "Okay, it's an error,
but print this message instead of the default" or even "It's not an error, but
print this message just to let the player know what's going on".

As an aside, I think a closely related issue is the strangeness of the
library message system. The current library message system only allows the
author to do one thing safely: intercept a specific error condition for which a
specific message is about to be printed, and print another message instead. It
is not safe to print nothing, because in some situations spurious punctuation
will appear, or because the library has already printed a partial message.
Moreover, you cannot intercept generalized error categories, only specific
messages.

Sean T Barrett

unread,
Aug 20, 2001, 3:50:48 PM8/20/01
to
OKB -- not okblacke <bren...@aol.comRemove> wrote:
>buz...@world.std.com (Sean T Barrett) wrote:
>>The problem with the continuum of checks where order is not a
>>concern is that you either need two copies of your tests (one
>>which reports the error condition and one which prints the
>>error message), you need one copy with a global variable that
>>says whether to print or not (a la 'keep_silent'), or you need
>>the one place to be able to return both an error code and a
>>printable message. Since that printable message might want to
>>print things like the name of the object, it really needs to
>>return a little callable function which prints.
>
> Basically, I agree that getting a system like this to work is going to
>take some doing, but your specific criticism seems to imply that we're using a
>sequence like Test->DoAction->TestAgain, whereas I'm thinking more like
>Test->DoAction->PrintMessage. The key here is that the post-action bit does
>not re-test; its sole task is the printing of the message. Now, we're
>definitely going to need some kind of storage for the results of the checks;
>it could be a bunch of globals, or it could be an array, or the stack.

I was describing three scenarios for implementing "storage for the
results of the check". The first two do it by using multiple calls,
because that's a realistic option to deal with the need for the
error messages to be functions-which-print-strings. You *could*
preprint all those strings (in *some* interpreters), but it would
be inefficient and you would quickly run into storage limitations,
since tens or twenties of messages could fire, and they could be of
arbitrary length. I don't think you can store the necessary info in
globals or on the stack, since there's an unknown series of them.

Thinking more carefully about this, I think focusing on the message
may actually miss the fact that overrides can and do sometimes have
side-effects; consider this scenario, taken from my unreleased
masterpiece, "Pick Up The Glass of Water and Die":

> TAKE GLASS
It's nailed down.

> PUSH BUTTON
A shimmering green wall of force flickers into existence around
the pedestal (and the glass upon it).

> TAKE GLASS
Bob grabs your hand. "You idiot! Don't you know that force field
is deadly?"

> BOB, NORTH
Bob departs to the north.

> TAKE GLASS
You reach for the glass... it happens so fast you can scarecely
make out the details, but your hands seems to have entirely
disappeared at the surface of the field. You try to pull your
hand... well, your arm back, but it's stuck to the field, in
fact it's disappearing further and further into it, sucked in
by the field itself. The velocity at which your arm (and you) is
sucked in increases, until you discover...

*** You have died. ***

How do we defer the "deadflag=1" behavior, or some other side-effect
which the _override_ wants to have (this being the equivalent of a
react_before on the force field), if we don't call a function after
deciding which effect comes first?

SeanB

OKB -- not okblacke

unread,
Aug 20, 2001, 4:59:36 PM8/20/01
to

If we're talking about a better library design here, I'd say forget about
deadflag. Use something like a Die(deathcode) function instead.

As for the more general situation of overrides wanting to alter game
state: it's definitely a concern, but I think good design can overcome it. If
what we want is an immediate effect, we call another routine immediately. If
we just want to set some variable to indicate that something has changed, we
can do so. The tricky bit is if we want to set a variable which is going to be
used "almost immediately" (as in, later this turn), but it seems to be that
could be handled by setting a flag on the object which is going to do that
thing later in the turn. When the time comes for that object to decide to do
its thing, it will note that its flag has been set and will spring into action.

I would guess that the most common type of override is one that tries to
stop an action which would otherwise happen (e.g., "You can't take that because
the frumious bandersnatch is guarding it."). Second most common is probably
one which intercepts an otherwise innocuous event and causes something special
to happen right away (e.g., "As you puff air across the cow's back, it moos in
annoyance and wanders off the the north pasture."). Those which silently note
some condition for later use are rarer.

Sean T Barrett

unread,
Aug 20, 2001, 9:19:45 PM8/20/01
to
Andrew Plotkin <erky...@eblong.com> wrote:
>- Overriding one part of a sequence must not break another part. (If
>you put a before:Touch clause on an object, you have to call
>ObjectIsUntouchable() yourself, or that mechanism breaks. This is why
>ObjectIsUntouchable() is the wrong approach.)
[snip]

>- It must be easy to change the default sequence for *a particular
>action/object combination*. (Most objects can be examined without
>touching, but a scroll must be unrolled in your hands to be read, and
>"examine scroll" should do that implicitly.)

I'm not sure that this simple set of desires has a consistent solution.
If I understand your initial point, you want to be able to have a
before-like override that only gets called if the object is untouchable,
assuming that the verb has that constraint. (We can imagine, say,
that we have a 'before-touchable' override and an 'after-touchable'
override to allow both sorts of behaviors.)

Now, let's say, to connect to touchability, there's an examining-like
action called "MEXAMINING", where to MEXAMINE something you need to
be able to touch it.

So now we build a room with an object with a react_before-like behavior
which reacts to "MEXAMINE" on other objects with some sort of response
based around the idea that you are going to reach out and touch a MEXAMINEd
object (e.g. Bob grabbing your wrist).

Now, we support your second desire, and you build an object which, being
a rather special object, can be MEXAMINED without being touched (say,
a gravicle-emitter).

Now, how do we satisfy both the first and second constraint for Bob? Bob's
react_before-like MEXAMINE intercept naturally assumed that the object
has already been checked for touchability, because that is a precondition
on MEXAMINE. But now the gravicle-emitter disables that precondition of
MEXAMINE, and prevents it from being checked.

One option would be to only call Bob's react_before-like MEXAMINE intercept
if the object is indeed touchable; but without some explicit code on Bob's
override, we (the library) have no idea whether it assumes that the object
is touchable. In other words, Bob's code will need to explicitly declare
whether it depends upon touchability. Which is, in some ways, rather like
the current system, although arguably the current system makes the easy
cases hard and the hard cases hard, instead of making the easy cases easy.

There are three reasonable semantics for Bob's override to have with
respect to touchability and the gravicle emitter's exceptional touchability:

Bob's override doesn't care about touchability
Bob's override only happens if the object is touchable
Bob's override only happens if the object is touched

The last two distinguish between two cases not yet distinguished by
the gravicle-emitters declaring it doesn't care about touchability:
is it saying "MEXAMINE of this object never involves touchability".
or is it saying "if the object is touchable, MEXAMINE behaves normally,
but if it's untouchable, go ahead and MEXAMINE it anyway" (which
would imply Bob's override still occurs if the gravicle-emitter is
touchable)?

SeanB

Daniel Barkalow

unread,
Aug 21, 2001, 12:36:00 AM8/21/01
to
On Tue, 21 Aug 2001, Sean T Barrett wrote:

> Andrew Plotkin <erky...@eblong.com> wrote:
> >- Overriding one part of a sequence must not break another part. (If
> >you put a before:Touch clause on an object, you have to call
> >ObjectIsUntouchable() yourself, or that mechanism breaks. This is why
> >ObjectIsUntouchable() is the wrong approach.)
> [snip]
> >- It must be easy to change the default sequence for *a particular
> >action/object combination*. (Most objects can be examined without
> >touching, but a scroll must be unrolled in your hands to be read, and
> >"examine scroll" should do that implicitly.)
>
> I'm not sure that this simple set of desires has a consistent solution.
> If I understand your initial point, you want to be able to have a
> before-like override that only gets called if the object is untouchable,
> assuming that the verb has that constraint. (We can imagine, say,
> that we have a 'before-touchable' override and an 'after-touchable'
> override to allow both sorts of behaviors.)

Consider the following idea:

Each action, library and otherwise, specifies a number of subactions. The
subactions may themselves have subactions.

Each object may replace the direct set of subactions for a given action
with a set of its own choice. This corresponds to a change is what it
means to do X to the object. Of course, the subactions may also have their
steps replaced, or not.

Each object in scope can react before an action takes place, during it
(replacing the normal library implementation), or after it.

So...

MEXAMINE consists of "CONTACT", followed by the core action (which prints
the description, by default).

CONTACT, by default, checks for model world accessibility.

Objects which have interesting information written in them react during
the core action to print their special thing.

An object which is very hot will react before CONTACT. An object that may
be touched through walls and TV will simply not check anything. An object
which is poisoned will react after CONTACT.

Bob reacts before CONTACT, stopping you from touching the object.

The gravicle-emitter has a set of MEXAMINE actions which lack the initial
CONTACT, so Bob doesn't do anything about this action, which does not
involve touching anything.

The scroll has a set of EXAMINE actions, on the other hand, which has an
initial CONTACT, so Bob stops you from examining the scroll.

The gravicle-emitter, on the other hand, could have a set of actions which
do start with CONTACT, but if the CONTACT fails in a non-catastrophic
fashion (you bump into a wall or bob stops you, rather than the emitter
turning to dust when you touch it), its MEXAMINE action continues.

Note that such an action can have failure messages before the final
result. I.e.,

] MEXAMINE EMITTER

Bob grabs your wrist. "Don't touch that!"
The feel of the emitter enters your mind as if you had examined it with
your hands.

] NORTH

(first openning the north door)
The door seems to be locked.
You can't go that way because the door is in your way.

There remains the problem, of course, of the order that various objects
get to respond. If you try to touch a hot object in Bob's room, does he
stop you, or do you stop yourself?

In any case, we have four methods on each object:

action_meaning: replaces the conventional set of subactions with a new
set.
before: happens after the initial subactions, before the core function
on: replaces the core function
after: happens after the core function has happened

At least the last three apply to objects in scope and indirect (and
implicit indirect) objects.

The library has for each action an action_meaning function and an on
function.

Side note: perhaps "examine scroll" should have the following logic:
For a scroll, "examine" normally means read-while-holding
Try to touch it.
If you succeed, read it.
If you fail, just examine it. ("You can't reach it to read it, but it
looks like your average scroll")
If you fail catastrophically, stop. ("When you touch the scroll it turns
to dust.")

Platypus does some of this, although, IIRC, the only implicit actions are
hardcoded and get printed out; touch is not one of them.

-Iabervon
*This .sig unintentionally changed*

Andrew Plotkin

unread,
Aug 21, 2001, 9:48:32 AM8/21/01
to
Sean T Barrett <buz...@world.std.com> wrote:
> Andrew Plotkin <erky...@eblong.com> wrote:
>>- Overriding one part of a sequence must not break another part. (If
>>you put a before:Touch clause on an object, you have to call
>>ObjectIsUntouchable() yourself, or that mechanism breaks. This is why
>>ObjectIsUntouchable() is the wrong approach.)
> [snip]
>>- It must be easy to change the default sequence for *a particular
>>action/object combination*. (Most objects can be examined without
>>touching, but a scroll must be unrolled in your hands to be read, and
>>"examine scroll" should do that implicitly.)

> I'm not sure that this simple set of desires has a consistent
> solution.

I'm sure it has a consistent solution. Any design problem, I've found,
has a consistent solution -- although the first try (or the first ten
tries) often leads to a deeper understanding of the problem, which
leads to changing one's desires to be more consistent.

I have never tried to solve this problem, so it's possible that could
happen. And, in any case, I'm *not* sure it can be implemented in less
than N-cubed time. :)

To answer your specific question: if two overrides seem to conflict,
then eventually I (as the game author) will have to decide which
should take precedence. The system I want will let me make that choice
(perhaps as simply as setting a numeric precedence value somewhere),
and test the code, and then change the precedence if I don't like the
behavior.

This applies not just to order of overrides, but in deciding which
sequence will be followed at all. The problem is tricky, of course,
but not in principle impossible.

One possible approach -- I'm not sure this covers all cases, but I'd
try it first -- is to divide the action system up into two phases: one
where the parser decides what you *want* to do, and one where the
parser has you *do* it. That would distinguish between "I didn't try
to pick up the tablet because I can read it at a distance", and "I
tried to pick up the scroll to read it but Bob grabbed my hand."

OKB -- not okblacke

unread,
Aug 21, 2001, 9:48:46 AM8/21/01
to
buz...@world.std.com (Sean T Barrett) wrote:
>So now we build a room with an object with a react_before-like behavior
>which reacts to "MEXAMINE" on other objects with some sort of response
>based around the idea that you are going to reach out and touch a MEXAMINEd
>object (e.g. Bob grabbing your wrist).
>
>Now, we support your second desire, and you build an object which, being
>a rather special object, can be MEXAMINED without being touched (say,
>a gravicle-emitter).
>
>Now, how do we satisfy both the first and second constraint for Bob? Bob's
>react_before-like MEXAMINE intercept naturally assumed that the object
>has already been checked for touchability, because that is a precondition
>on MEXAMINE. But now the gravicle-emitter disables that precondition of
>MEXAMINE, and prevents it from being checked.

This is an interesting question. The first thought that comes to mind is
that the root verb definition, with its associated constraints, should be the
foundation for resolving disputes like this. So, if the unadulterated verb
requires touchability, special-casing it to provide untouchability is obviously
a substantive change, and the programmer should recognize that and write
related code accordingly. Clearly, we can't expect the library to magically
adapt to special-casing, because if we could there would be no special cases.

In this case, I think you'd have to decide whether Bob's react_before
just needs to know if the action is possible, or if it actually needs to know
if the object is touchable. If the former, we can use a Platypus-like
validation stage and do our thing if the action turns out to be feasible. If
the latter, we can write our own validation override which checks if the object
is touchable.

Also, if we're talking about verbs and special-cases which can define
their own sequence of touchability-esque constraints, I think we should
stipulate that these constraints be accessible to ALL code. So Bob's
react_before could do something like:
[;
Mexamine:
if (HasConstraint(Mexamine, Emitter, 0, Touchability)
"Bob grabs your hand, etc.";
else "Bob warns you not to do that.";
];

. . . where HasConstraint(action, noun, second, constraint) returns
true/false depending on whether that action on those objects has that
constraint.

Of course, this opens up a whole new can of worms about exactly what
constraints are and how they are specified: What is that Touchability in that
code up there? Is it an object? A routine? What about user-defined
constraints? How are they defined? I'm getting the feeling that we'd be doing
a lot if we came up with a good scheme for specifying constraints on verbs and
objects.

0 new messages