Logical Ranks and Implying iObjects

I’ve been working on creating a vending machine object in Tads3, and while I’ve finally gotten it to take the vending machine as an indirect object when none is listed (meaning “insert money” acts as “insert money in vending machine”), anytime logicalRanks is part of a conditional it doesn’t work.

So while this works:

vendingMachine: Heavy, Container 'vending machine' 'vending machine' "It's a vending machine." iobjFor(InsertInto) { verify() { logicalRank(120, 'takes money'); } check() { if(gDobj != money) failCheck('You can\'t insert that into the vending machine. '); } action() { "You stick a couple dollars in the vending machine. "; } } ;

This (for what I want to do) does not:

vendingMachine: Heavy, Container 'vending machine' 'vending machine' "It's a vending machine." iobjFor(InsertInto) { verify() { if(gDobj == money) logicalRank(120, 'takes money'); else illogical('You can\'t insert that. '); } action() { "You stick a couple dollars in the vending machine. "; } } ;

It still works when the player implicitly tries to insert anything into the vending machine, but it prevents the vending machine from being chosen when no iObj is given. While the condition being in check rather than verify isn’t that bad (and preferable to the text being difficult to deal with) it still really bugs me (because inserting a hat should really be illogical, rather than failed to be inserted).

Here’s how insert’s defined.

[code]DefineTIAction(InsertInto);

VerbRule(InsertInto)
(‘insert’ | ‘stick’) singleDobj (‘into’ | ‘in’ | ‘in’ ‘to’) iobjList
: InsertIntoAction
verbPhrase = ‘insert/inserting (what) (into what)’
;

VerbRule(Insert)
(‘insert’ | ‘stick’) singleDobj
: InsertIntoAction
verbPhrase = ‘insert/inserting (what) (into what)’
construct()
{
iobjMatch = new EmptyNounPhraseProd();
iobjMatch.responseProd = toSingleNoun;
}
;[/code]

Thanks.

I suspect the problem here may be that gDobj hasn’t yet been assigned a value when you test for gDobj == money in your verirfy stage, since at that point the parser is still trying to resolve objects (that’s part of what the verify stage is for, after all). If that’s the case, then gDobj may still be nil at this point so that gDobj == money will never be true.

If that is the problem, then you could try using gTentativeDobj instead. This will contain a list of ResolveInfo objects, so what you’d need to test for is something like:

iobjFor(InsertInto)
    {       
        verify()
        {
           if(gTentativeDobj.indexWhich({x: x.obj_ == money}) != nil)
               logicalRank(120, 'takes money');
           else
               illogical('You can\'t insert that. ');
        }

The machine I’m replying on doesn’t have TADS 3 installed, so I can’t actually test this right now, though.

Incidentally, doesn’t your InsertInto action more or less duplicate the existing PutIn action (not least since a player used to the standard set of IF commands would most likely try PUT MONEY IN VENDING MACHINE)? And do you really want iobjList in your InsertInto VerbRule (which allows the player to try inserting one object into several things at once)? You may want to consider making your InsertInto VerbRule a synonym for PutIn (since it essentially means the same thing) and then write special handling for the PutInAction:

VerbRule(InsertInto)
    ('insert' | 'stick') dobjList ('into' | 'in' | 'in' 'to') singleIobj
    : PutInAction
    verbPhrase = 'insert/inserting (what) (into what)'
;

VerbRule(Insert)
    [badness 500] ('insert' | 'stick') singleDobj
    : PutInAction
    verbPhrase = 'insert/inserting (what) (into what)'
    construct()
    {
        /* set up the empty indirect object phrase */
        iobjMatch = new EmptyNounPhraseProd();
        iobjMatch.responseProd = inSingleNoun;
    }
;

Otherwise, you’ll need to trap PUT HAT IN VENDING MACHINE and the like separately.

Alternatively, you might want to consider making the Vending Machine a RestrictedContainer, which does most of the work for you; the downside is that it carries out the restriction at the check stage rather than the verify stage, as you would prefer.

What’s odd though is the verify method actually works correctly if you directly try to give it objects; it takes the money and rejects everything else. It’s only when I just say “insert money” that it doesn’t pick up on the fact the vending machine’s the most logical. I don’t know how logical rank works, so it’s possible to find automatic objects it runs it without a gDobj and only when told does it take the gDobj (I will definitely try your answer in the morning).

The main reason I have Insert in addition to PutIn is PutIn puts an object in the vending machine’s “tray” (where the object gets dispensed) while Insert tries to put it in the little money slot (unless you try to put in money, in which case it remaps to Insert). I… have gone a little too detailed-oriented with some of the stuff.

I don’t actually know why it’s iobjList (and’ll change it) but what commands make use of it? I’m trying to figure out when I should use it and when I shouldn’t.

Again since I’m not in front of a machine with TADS on it, I can’t be definite about this, but I suspect it has to do with the fact that TADS 3 makes multiple passes through verify routines. In the case where you type INSERT X IN VENDING MACHINE the parser may well be able to resolve the two objects involved, so that gDobj may well have a value by the time the verify routine is used to decide whether or not to allow the action.

In the case where you type INSERT MONEY or INSERT HAT, however, your verify routine is needed to decide what the best indirect object might be. At that stage the parser is still trying to resolve objects, and may not yet have resolved the direct object, so gDobj may still be nil when the parser is trying to choose an indirect object. So it may only be at this stage that the problem manifests itself.

In sum, verify is executed more than once. In the INSERT HAT INTO VENDING MACHINE case, inserting the hat is ruled out at a late verify stage. In the case of INSERT HAT or INSERT MONEY you need your logicalRank to take effect at an earlier verify stage, when the parser is still choosing the objects to use in the command.

It may be that a sufficient solution would be to include the following in your definition of InsertIntoAction:

DefineTIAction(InsertInto)
   resolveFirst = DirectObject
;

This would force the parser to resolve the dobj first, so that gDobj should no longer be nil when it’s used in verifyIobj routines to resolve the iobj. But if that doesn’t work you’ll need to use the gTentativeDobj trick.

As for iobjList in a VerbRule, off the top of my head I can’t think of any action that uses it, nor can I immediately think of a situation in which it might be useful.

Eric has a couple of suggestions on how to handle this that I haven’t run into. What I used to do in order to prevent it was:

if gDobj && (gDobj == money)

I don’t know if this would work in your situation, but that’s a standard precaution when testing the value of gDobj and gIobj in a verify block.

Thank you Eric, both of your suggestions work wonderfully. I also thought I found a situation where you’d use dobjList, but it seems singleDobj works just as well.

I can’t get if (gDobj && gDobj == money) to work, but

logicalRank(120, 'takes money'); if (gDobj && gDobj != money) illogical('You can\'t insert that. ');

works, although it is a little weird since it tries to insert the hat before proceeding to say it can’t insert that.

Overall though, thanks guys.