can't port GiveTopic code from adv to adv3lite

Using the adv library, I have an ATM object that dispenses 5 $20 dollar bills at a time. When Harry gives something to a street person, I am able to use a GiveTopic to provide dialog…

[code]+ GiveTopic
matchTopic(fromActor, obj)
{
return obj.ofKind(Thing) ? matchScore : nil;
}

handleTopic(fromActor, obj)
{
    if(obj.ofKind(twentyDollarBill))
        obj.moveInto(getActor());
    gTranscript.addReport(new AcceptItemReport(obj));
}

;
class AcceptItemReport: MainCommandReport
construct(obj)
{
if(obj.ofKind(twentyDollarBill))
{
gMessageParams(obj);
inherited('The man quickly snatched {the obj/him} from Harry's hand. ');
}
else
{
gMessageParams(obj);
inherited('The homeless man reached to grab it, but Harry
reconsidered giving away {the obj/him} and withdrew his hand.
<.p>
“Erghhh,” the other man snarled as his fist closed on empty
air. ');
}
}
;[/code]

But when I try to port this to work with adv3lite, I find that GiveTopic code has been redefined in a new library file called TopicEntry.t, and matchTopic() now takes only one argument, obj. When I fix that, I find that handleTopic() now takes no arguments. When I fix that, I am able to give 4 20s to a store clerk , but when I then try to look inside Harry’s wallet, I get a runtime error that says

The Workbench code pointer points to local entry = words|i| in thing.t

[code]matchToken(tok, words, cmp)
{
/* we don’t have a match for this token yet */
local strength = 0, partOfSpeech = 0;

/* try matching this token against our vocabulary list */
for (local len = words.length(), local i = 1 ; i <= len ; ++i)
{
    /* get this vocabulary word entry */
    local entry = words[i];

[/code]

Yes, I shifted milieus, Harry’s now in a store buying something, no longer giving money away to the street person, but the coding problem exists in both places. The store scene is where I really need to solve the problem. How can I get it to work so that Harry is able to give away an arbitrary subset of the number of 20s he has in his wallet? I also want to control the dialog exchange while this is going on, which is why I’m trying to use a GiveTopic.

It’s getting a bit long, but here it is anyway, the current test-bed game code…

[code]#charset “us-ascii”

#include <tads.h>
#include “advlite.h”

versionInfo: GameID
IFID = ‘243748b1-5310-4916-8436-890e9ccc16fd’
name = ‘test’
byline = ‘by Jerry Ford’
htmlByline = ‘by
Jerry Ford

version = ‘1’
authorEmail = ‘Jerry Ford jerry.o.ford@gmail.com
desc = ‘Testing adv3lite GiveTopic.’
htmlDesc = ‘Testing adv3lite GiveTopic.’
;

gameMain: GameMainDef
/* the initial player character is ‘harry’ */
initialPlayerChar = harry
paraBrksBtwnSubcontents = nil
usePastTense = true
;

// harry, main character
harry: Actor ‘Harry;;man self’ @streetCorner
“”
contType = Carrier
globalParamName = ‘harry’
isFixed = true
isHim = true
isInitState = true
ownsContents = true
person = 3
proper = true
bulkCapacity = 5000
hasShowered = nil
;

  • wallet: Container ‘wallet;;wallet’
    "The bifold wallet showed frayed corners and cracks of age etched
    into the brown leather. "
    ;
    // items hidden in harry’s wallet
    ++ debitCard: Surface, Thing ‘debit card;;card’
    "Harry’s debit card gave him instant access to his checking
    account. "

    dobjFor(PutIn)
    {
    action()
    {
    if(gIobj.ofKind(atm))
    {
    say('When Harry inserts the debit card into the machine, it
    becomes operational. Harry enters his PIN and withdraws
    $100 in 20s, retrieves the card, and puts it back into
    his wallet. ');
    atm.loadWallet();
    self.moveInto(wallet);
    }
    }
    }
    ;

// the street corner and the ATM machine
streetCorner: Room ‘Street Corner’ ‘street corner’
"The corner of the street. An ATM was built into the wall of the apartment
building. "

south = store

;

  • atm: Container, Immovable ‘an ATM;automated teller ;machine ATM’
    "An automated teller machine. "
    initiallyOpen = true
    canPutIn(obj) { return obj.ofKind(debitCard); }
    loadWallet()
    {
    for(local i = 0; i < 5; i++)
    {
    local twentyDollarBill = new TwentyDollarBill;
    twentyDollarBill.moveInto(wallet);
    }
    }
    ;

// money class
class Dollar: Thing
vocabWords = ‘dollar bill;bills’
;

class TwentyDollarBill: Dollar ‘20; twenty’
isEquivalent = true
dollarValue = 20
dollarGroupBaseName = ‘twenty’
;

class TenDollarBill: Dollar ‘10; ten’
isEquivalent = true
dollarValue = 10
dollarGroupBaseName = ‘ten’
;

class FiveDollarBill: Dollar ‘5; five’
isEquivalent = true
dollarValue = 5
dollarGroupBaseName = ‘five’
;

class OneDollarBill: Dollar ‘1; one’
isEquivalent = true
dollarValue = 5
dollarGroupBaseName = ‘one’
;

// the store
store: Room, Container ‘Store’ ‘store’
“"Harry, my man. Special price, today only, for
you, just $100. What’dya say? Gimme some plastic, I’ll wrap it
right up. Make it cash, and the price is just $80."”

south = streetCorner

;

  • gizmo: Thing ‘gizmo’
    "It’s a gizmo. "
    ;

// the store clerk
harv: Actor ‘Harv;heavy heavyset hefty bearded shop;man clerk;him’ @store
“”
contType = Carrier
globalParamName = ‘harv’
isFixed = true
isInitState = true
ownsContents = true
person = 3
proper = true
bulkCapacity = 5000
;

  • GiveTopic
    // matchTopic(fromActor, obj)
    matchTopic(obj)
    {
    // return obj.ofKind(Thing) ? matchScore : nil;
    return obj.ofKind(TwentyDollarBill) ? matchScore : nil;
    }

// handleTopic(fromActor, obj)
handleTopic()
{
// if(obj.ofKind(TwentyDollarBill))
// obj.moveInto(getActor());
TwentyDollarBill.moveInto(getActor());
}
;

[/code]

The problem is with your GiveTopic, which should be defined as:

+ GiveTopic @TwentyDollarBill
    topicResponse()
    {
        gDobj.moveInto(getActor());
        "Harry hands Harv a twenty dollar bill. ";
    }
;

There’s no need to override matchTopic on the GiveTopic, since in adv3Lite (as opposed to adv3) a TopicEntry can match a class as well as an object (in other words it already does what you were overriding matchTopic to do).

Secondly, this code is wrong because it tries to move a class, not an object:

handleTopic()
    {
//        if(obj.ofKind(TwentyDollarBill))
//            obj.moveInto(getActor());
        TwentyDollarBill.moveInto(getActor());
    }

It’s not the TwentyDollarBill class that you want to move into Harv, it’s the particular instance of that class that’s been matched by the command GIVE TWENTY TO HARV, i.e. gDobj, the direct object of the command.

Also, it’s not handleTopic() but topicResponse() you should override to do this.

There are several other things not quite right with your code, by the way. For one thing, you’re defining much more than you need to on the harv object (since the Actor class already does most of this already). All you need is:

harv: Actor 'Harv; heavy heavyset hefty bearded shop; man clerk; him' @store    
    globalParamName = 'harv'    
    bulkCapacity = 5000
;

There are several other things that aren’t quite right, but on the assumption there only that way because your code is quick-and-dirty test bed stuff, I shan’t bother you with them for now! But investigating your problem did lead me to discover I needed to block attempts by actors to give things to themselves!

Okay, thanks.

That works better, but something’s still not right.

When I use the code you suggested…

[code]+ GiveTopic
topicResponse()
{
gDobj.moveInto(store);
"Harry hands Harv a $20 bill. ";
}

[/code]
…I get this…

What do I need to define in order to get rid of the “did not respond” message?

Also, when I examine Harv, he does not have the 20s. As an experiment, instead of moving the 20s into Harv an actor, I tried moveInto(store) (a room) and I still don’t see the 20s. They were there in Harry’s wallet after using the atm, and now there is only 1 in the wallet.

But I can’t find the other five.

Ah, okay, got it. I had left off the @TwentyDollarBill.

With that in place, it’s working now.

Thanks.

Jerry

You may also be interested in experimenting with this variant, which I had forgotten I had allowed for:

+ GiveTopic @TwentyDollarBill
    topicResponse()
    {
        gDobj.moveInto(getActor());

        gAction.giveReport = 'Harry hands Harv {1}. ';
    }
;

It probably needs more refinement (in the library, I mean), but it results in:

[code]>give harv a 20

(first taking the 20)
Harry hands Harv the 20.

give harv 3 20s

(first taking the 20)
(first taking the 20)
(first taking the 20)
Harry hands Harv three 20s.
[/code]

This is okay so long as Harry doesn’t try to give Harv anything else at the same time; for example GIVE 3 20s AND WALLET TO HARV doesn’t work so well, so more work is needed on this, but I thought you might be interested in taking a look.

Hmmm, interesting. I like the accumulated total at the end. I worked out my own workaround, which expects a hard coded total purchase of $80 and requires a custom count property in the harv object.

For this one special case, my approach works…

[code]+ GiveTopic @TwentyDollarBill
topicResponse()
{
gDobj.moveInto(harvsCashRegister);
if(harv.count++ < 3)
{
acceptCash.doScript();
}
else
{
“"Eighty bucks," Harry said.<.p>
"So it is, sir, and a most excellent purchase, I’m sure,"
Harv replied as he moved to wrap the box of handcuffs.
<.p>
"Don’t bother." Harry removed the bracelets from their
container and shoved them into his left pants pocket. "Here, you
can keep the box."
<.p>”;
handCuffs.moveInto(cargoPantsPocketLeft);
}

}

;
acceptCash: ShuffledEventList
[
‘"Excellent," Harv murmurred, plucking the bill off of the counter. \b’,
‘"Thank you, sir." \b’,
‘"Ah, hmmm," the clerk whispered appreciatively, grabbing the bill out of
the air as it floated down from Harry's hand. \b’,
‘"That's the ticket." \b’,
‘"Very good." \b’
];

[/code]

Yours is more generic and I like that. I’ll fiddle with it some and see which one works best. Probably both…I think my solution does pretty well in this one specific instance, but the library approach probably has longer legs.

Jerry

Your solution looks like it may well indeed work well for your specific case, and may work better in the context of your game because it is less generic (and so provides players with a more colourful response); it very much depends on the effect you want to create here, and there’s certainly nothing wrong with the effect your code creates.

In the meantime I’ve done a bit more work on the library version so it can now produce:

>give three 20s to harv

(first taking three 20s)
Harry hands Harv three 20s. 

That is, it combines the three implicit action reports into one, in the case where you just get a summarized report at the end. I just hope I haven’t broken anything else by doing this!

Again, as much (or more) to satisfy my own curiosity as to make a suggestion you’ll necessarily want to follow, I just experimented with this:

class TwentyDollarBill: Dollar '20; twenty'
    isEquivalent = true
    dollarValue = 20
    dollarGroupBaseName = 'twenty'
    
    dobjFor(GiveTo)
    {
        report()
        {
            inherited;
            gActor.currentInterlocutor.billCount();
        }
    }
;


// the store clerk
harv: Actor 'Harv; heavy heavyset hefty bearded shop; man clerk; him' @store    
    globalParamName = 'harv'    
    bulkCapacity = 5000
    
    billCount()
    {
        local count = gAction.reportList.countWhich({x:
            x.ofKind(TwentyDollarBill)});
        
        totalGiven += 20 * count;
        
        "<.p><q>Right, that's <<spellNumber(count * 20)>> dollars,</q> Harv
        remarks. ";
        
        if(totalGiven >= 80)
        {
            "<q>That will do nicely. Here I'll wrap...</q> ";
        }
        else
            "<q>So I just need another <<spellNumber(80 - totalGiven)>>.</q>";
    }
    
    totalGiven = 0
;

From which you can get, for example:

>give three 20s to harv

(first taking three 20s)
Harry hands Harv three 20s. 

“Right, that’s sixty dollars,” Harv remarks. “So I just need another twenty.”

But that’s no better than your approach; it’s just different.