I have a situation in which I want…or at least I think I want…to have a bare noun phrase typed as a command to be parsed as an action applying to the named object.
That’s straightforward enough to implement. If we want to create a new action, call it FooAction
, and we want to have it used when the name of an object is typed by itself as a command, then all we have to do is:
DefineTAction(Foo);
VerbRule(Foo) singleDobj: FooAction verbPhrase = 'foo/fooing (what)';
Then we can just put dobjFor(Foo)
handlers on any objects we want to do stuff when invoked this way, probably putting a fallback on Thing
so we don’t get a “Nothing obvious happens.” by default.
Okay, so far so good. Now how about if we want to have two actions, each of which applies in different situations? Different actions for different kinds of object, for example. And what if you also want the parser to default to the normal behavior (that is, not treating noun phrases as actions) for everything except a specific class of objects?
The naive solution:
DefineTAction(Foo);
VerbRule(Foo) singleDobj: FooAction verbPhrase = 'foo/fooing (what)';
DefineTAction(Bar);
VerbRule(Bar) singleDobj: BarAction verbPhrase = 'bar/barring (what)';
pebble: Thing 'small round pebble' 'pebble' "A small, round pebble. "
dobjFor(Foo) {
verify() { nonObvious; }
action() { "You foo the pebble. "; }
}
;
rock: Thing 'ordinary rock' 'rock' "An ordinary rock. "
dobjFor(Bar) {
verify() { nonObvious; }
action() { "You bar the rock. "; }
}
;
+stone: Thing 'nondescript stone' 'stone' "A nondescript stone. ";
…doesn’t work: >PEBBLE
, >ROCK
, and >STONE
will all be interpreted as calling FooAction
.
This turns out to be a somewhat complicated problem. I’m not entirely convinced that I have the optimal solution, but I have something that works without having to hammer too much on the adv3 parser.
The trick, if you want to call it that, is to modify each action’s resolveNouns()
method to mark noteWeakPhrasing()
on the results object. So if we want FooAction
to only apply to instances of the class Pebble
:
DefineTAction(Foo);
VerbRule(Foo) singleDobj: FooAction verbPhrase = 'foo/fooing (what)'
resolveNouns(srcActor, dstActor, results) {
local r;
inherited(srcActor, dstActor, results);
if(dobjList_ == nil)
return;
r = nil;
dobjList_.forEach(function(o) {
if(o.obj_ && o.obj_.ofKind(Pebble))
r = true;
});
if(r != true)
results.noteWeakPhrasing(100);
}
;
The important bits to note are that you have to call inherited()
first, because that’s where dobjList_
gets populated. We then just test that it contains an instance of the class we care about, and if it doesn’t we call noteWeakPhrasing()
on the results object.
That gets us part of the way there. If we do this on all of our noun-as-verb actions, then this will work to disambiguate between them. Pebble
instance names typed on the command line will always be handled with FooAction
, for example. We could do something similar for Rock
instances and BarAction
. But then if we have a Stone
that’s not a Pebble
or a Rock
, then it will end up handled by whichever noun-as-verb action happened to be declared last. Which is probably not what we want (it’s not what I want, anyway).
The trick here is that we can define an additional noun-as-verb action that handles no specific class. But we can’t declare it exactly the same way we declare the other noun-as-verb actions (because it would be handled the same way, so we’d still have the same problem). Instead we set an arbitrary “badness” flag on the results that’s not “bad” enough to throw an exception. This will make the parser silently drop us on the floor and continue processing, which is exactly what we want. In this case we use noteBadPrep()
, which is normally used for ambiguous prepositional phrases:
DefineTAction(Kludge);
VerbRule(Kludge) singleDobj: KludgeAction verbPhrase = 'kludge/kludging (what)'
resolveNouns(srcActor, dstActor, results) {
inherited(srcActor, dstActor, results);
results.noteBadPrep();
}
;
This creates an additional action that will never be used, but it will catch all of the noun-as-verb statements that aren’t handled by a different noun-as-verb action (that is, one that doesn’t mark the noun phrase as having weak phrasing).
Adding a couple macros to make declaring things easier and rolling it into a module that you can find here, we can get all of the behaviors we want with:
#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>
#include "nounAsVerb.h"
DefineNounAsVerb(Foo, Pebble);
DefineNounAsVerb(Bar, Rock);
class Pebble: Thing
dobjFor(Foo) { action() { "You foo the pebble. "; } }
;
class Rock: Thing
dobjFor(Bar) { action() { "You bar the rock. "; } }
;
startRoom: Room 'Void' "This is a featureless void. ";
+me: Person;
+pebble: Pebble 'small round pebble' 'pebble' "A small, round pebble. ";
+rock: Rock 'ordinary rock' 'rock' "An ordinary rock. ";
+stone: Thing 'nondescript stone' 'stone' "A nondescript stone. ";
versionInfo: GameID;
gameMain: GameMainDef initialPlayerChar = me;
That is, >PEBBLE
is handled as “foo the pebble”, >ROCK
is handled as “bar the rock”, and >STONE
is handled as “parse this as if none of this noun-as-verb nonsense was here”:
Void
This is a featureless void.
You see a stone, a pebble, and a rock here.
>pebble
You foo the pebble.
>rock
You bar the rock.
>stone
The story doesn't understand that command.
This is a bit of a kludge and, notably, it doesn’t have any disambiguation method for objects that match multiple noun-as-verb actions (in our example, an object that’s an instance of both Pebble
and Rock
).
But figuring out this much was involved enough that I figured I’d put it out there anyway.