adv3Lite implementation

Howdy folks;

I have a WIP – coded in T3 – and am considering switching over to adv3Lite.

Has anyone gone through this process yet? Pros/Cons?

I’ll be sure to post my trials and tribulations, if there are any, as I attempt this in the upcoming days.

– Mike

I’m delighted to hear that you’re interested in giving it a go, and if you do give it a try, I’d be glad of any feedback you can give, so I look forward to hearing/reading of your experiences.

I’ve not tried translating a game from adv3 to adv3Lite. My suspicion is that what might prove tricky are the things that are not quite the same in the two; for example a piece of code from adv3 that compiles in adv3Lite but no longer quite does what you expect. On the other hand you may find that there’s enough in common between the two that, especially if you haven’t got very far with your WIP, you may be able to copy and paste code from your adv3 WIP and then just edit it to work with adv3Lite. Without having tried it, I’m not sure whether that will prove easier or harder than coding it again from scratch. Potentially it could save you a lot of retyping, but as intimated above, you’d have to keep a careful lookout for potential snares.

One other thing I should mention: I have discovered one more bug since posting the 0.7 update. It concerns the use of the <.inform > conversational tag. Since this doesn’t exist in adv3 it may not matter to you if you’re just doing a straight port, but if you are planning on using the <.inform > tag you might want to get in touch so I can send you the fix that will make it work properly (the bug is that I had inadvertently set it up so that all actors use the same inform tag table, so that informing one actor of something has the effect of informing them all, which rather defeats the purpose!).

Hi Eric; first, thanks for all your hard work and documentation – great stuff.

So yeah, this is the running log I have so far. Note that this is from a porting perspective.

  1. Install adv3Lite into my T3 work folder, then in the workbench, added the adv3Lite folder to the library path. This is documented in Chapter 2, “Tools of the Trade – Setting it all up” document.

  2. Since I failed miserably into merging adv3Lite into my WIP, I decided to start from scratch. Therefore, I recreated my WIP folder and then followed the instructions in Chapter 2.

  3. The first time I tried to start the Workbench, I got: “TADS Workbench could not find the following source file: adv3Lite.tl” It’s not in the adv3Ltemplate folder which you copy into your game folder, but it’s in the adv3Lite folder. I clicked “FIND” and, well, found it. Note that I did add the adv3Lite folder in my workbench library path – step 1 – but that didn’t help.

  4. Once I found the file, I was able to compile the template sample game.

  5. I then proceeded to import all my code into the new project. I like to break up source files so the first thing I had to do was get rid of the T3 library include and replace it with the adv3Lite library.

  6. I then built the project to see the damage. No errors! Okay, I’m kidding, I still haven’t got it to compile cleanly.

The problem I’m facing is that I’ve forgotten Tads 3 syntax since it’s been quite some time since I put it on the shelf. So not only do I not know the Lite syntax, I don’t remember the Adv syntax. If I was writing it from scratch, I think it would be easier. But I plow ahead.

This is where I’m looking for help, Eric.

a) The game starts in Past Tense (I’m all set here) and in first person (need help), so in the PC object, I have
pcReferralPerson = FirstPerson

… but I get “FirstPerson” is undefined.

b) I know there is a remap section, but my Adv format of:
dobjFor(Shake) asDobjFor(WakeUp)

doesn’t work.

c) I was hoping to retain some of the format from Adv but I can’t find the right syntax for a shuffled list. Code from Adv:

dobjFor(VERB) { verify() {} check() { if (!OBJECT.examined) { // ADV used Described, LITE uses examined "Nothing happen{s|ed}. "; } else { showMSG.doScript(); } } } showMSG : ShuffledEventList {[ 'The iguana thrash{s/ed} her tail! ', 'The iguana bobbed her head up and down! ', 'The iguana moved faster than I thought any other organism ever could. All the way to the other side of the room. ', 'The iguana chirped rudely. ' ]}

I don’t mind moving the shuffled event list into the check, but I prefer to keep it outside of scope for cut/paste/reuse.

d) In Adv, I was used to MainReport for displaying text and stopping the action, so lots of references like this:

dobjFor(Scrunch) { action() { mainReport('Scrunch{|ed}! '); }}

So is it safe to say this is how to convert that code?

dobjFor(Scrunch) { action() { DMsg(Scrunch,'Scrunch{|ed}! '); }}

I’ve made other progress, converting minor property values (readable, container, etc…) and so far so good. I still have a ways to go to port my WIP, but I’m getting the feeling that the Lite version will make my job easier as I go along.

I’m curious how the conversation port is going to work, but I’ll save that for another day.

Thanks for reading this far, Eric!

– Mike

Thanks for giving adv3Lite a go and sharing your experiences.

Concerning the matters about which you requested help:

(a) There’s no pcReferralPerson property or FirstPerson macro in adv3Lite. Instead you need to set the person property of your PC object to 1 (meaning first person) and its vocab property to ‘I’, something like:

+ me: Thing 'I'   
    isFixed = true    
    proper = true
    ownsContents = true
    person = 1   
    contType = Carrier    
;

(b) asDobjFor() should work in adv3Lite much as it does in adv3, so the problem is likely to lie elsewhere. Neither WakeUp nor Shake is defined as an action in the adv3Lite library, so if they’re not defined in your game code that could be the problem. Note also that action definitions, especially VerbRules, aren’t quite the same in adv3Lite as they are in adv3.

© I’m not entirely sure I fully understand the problem you’re having with ShuffledEventList, since this should work exactly the same in adv3Lite as it does in adv3 (as I recall I simply copied the code straight from the adv3 library). I can see one problem with the code sample you give, namely that it seems to rely on a non-existent template for ShuffledEventList (non-existent in adv3Lite, at any rate). In particular, you need to specify explicitly that you’re defining
the eventList property, like so:

 showMSG : ShuffledEventList {
       eventList =    [
        'The iguana thrash{s/ed} her tail! ',
        'The iguana bobbed her head up and down! ',
        'The iguana moved faster than I thought any other organism ever could.
         All the way to the other side of the room. ',
        'The iguana chirped rudely. '
    ]} 

Another problem you’ll have with the above code in adv3Lite is that ‘{s/ed}’ won’t work as a message parameter substitution. I suspect you could just write:

'The iguana thrashed her tail! '

Since none of your other messages seem to have any mechanism for changing tense. (If you do need a message that changes tense, I can explain how to do it, but since I suspect you don’t need it here I shan’t clutter this reply with an explanation).

I confess to being a bit mystified by your comment about not minding moving this code into check, since on the face of it, it looks like it belongs in action() (Your messages seem to be reporting the outcome of an action, not reasons why the action can’t go ahead).

(d) You can use DMsg to display a message at the action stage, but it’s almost certainly overkill in game code. DMsg is really only needed in library code (or in a library extension) to allow other users to customize default messages. In game code it’s simpler and more direct to use a double-quoted string.
Note also that the tense switching parameter substitution {|ed} won’t work in adv3Lite. So what you probably need is something like:

dobjFor(Scrunch) { action() { "Scrunch<<tSel('','ed'>>! "; }}

If your game never changes tense, however, there’s no need to use the tSel() macro; you’d simply write:

dobjFor(Scrunch) { action() { "Scrunched! "; }}

Another point to consider is whether this response belongs in the action() or the report() method. If you’re defining it on a particular object, then using action() is the right way to go. If this is the generic response defined on the Thing class it might be better to use report(), i.e.:

dobjFor(Scrunch) { report() { "Scrunched! "; }}

Or, even better perhaps:

dobjFor(Scrunch) { report() { "Scrunched! |{I} {scrunch} <<gActionListStr>>. "; }}

For which you’d also need to define:

myCustomVocab: CustomVocab
   verbParams = [
        'scrunch/scrunches/scrunched'
   ]
;

This is one area in which adv3Lite is quite a bit different from adv3. Note, for example, that adv3Lite has no transcript, hence the absence of the mainReport() macro, since there’s no transcript to add a report to. Instead, most of the time, output is displayed straight to the game window (there are exceptions, e.g. to deal with implicit action reports, but that’s true in the main).

Another area in which adv3Lite is quite a bit different from adv3 is in the conversation system, despite superficial resemblances. This is one of the parts of the adv3Library I more or less wrote from scratch, with the aim of coming up with something rather more flexible (at the cost of giving game authors a bit more work to do in one or two places). So unless you’ve only used the very basics of the adv3 conversation system you’ll probably find you need to make a lot of changes to your conversation code to make it work as expected in adv3Lite. Among some of the things to look out for are:

(1) There are no subclasses of ActorState in adv3Lite. Every actor state is simply an ActorState (you can get the same effects as you can from adv3’s various different ActorState classes, but you have to go about it differently).

(2) There is no AltTopic class in adv3Lite. If you have any AltTopics you’ll have to convert them to the equivalent AskTopics etc., and control when they’re used via their matchScore and/or isActive (and/or activated) properties.

(3) There are no SuggestedTopic classes (SuggestedAskTopic and the like) in adv3Lite. Instead, to make a topic entry act like a SuggestedTopic, you just define its name property.

(4) The adv3Lite SpecialTopic class is rather different from the adv3 SpecialTopic class. In particular you don’t use SpecialTopic directly in game code, instead you’d use one of its subclasses: QueryTopic or SayTopic. Unlike an adv3 SpecialTopic, adv3Lite SayTopics and QueryTopics are not restricted to Conversation Nodes.

(5) The adv3Lite ConvNode class is much simpler than the adv3 ConvNode class. Instead of defining methods like npcContinuationList or canEndConversation() on your ConvNodes you need to define seperate NodeContinuationTopic and NodeEndCheck objects.

Coming from adv3 you may thus find the adv3Lite conversation system a bit of a challenge to get to grips with at first, but once you’ve got the hang of it you’ll find it lets you do quite a few things that would be difficult or impossible in adv3 (such as sharing the same topic entry among a number of conversation nodes).

Once again, thanks for giving adv3Lite a go. I hope that your porting goes reasonably smoothly once you get the hang of the differences, but do get back to me if you have any more problems or queries.

Great feedback – it resolved my issues. Some responses follow…

You are quite right; I had missed my own extension module in the new port and since that was one of the first errors I encountered, I assumed incorrectly it was something more than just missing code.

It was as simple as that. I guess I was assuming the switch to Lite needed more overhaul and overlooked the obvious.

The first part of the game is past tense. It then switches to present. That’s why some of the sample code has tense switching parameter substitution. I’ll post more on that once it bubbles up to the top of the todo list.

The entire opening scene is driven by SpecialTopic so thanks for the heads up.

More updates tonight.

– Mike

I made decent progress tonight. I have 8 source modules, and I’ve gotten through 4 with few sections commented out. I wanted to get to a clean compile so where it wasn’t obvious or I couldn’t locate a reference in the doc, I just commented it out. That doesn’t mean it doesn’t exist in the supplied documentation, it’s just that I couldn’t find it.

Some general observations. Creating new verbs were simple enough to modify and so was a lot of the mainreport/failcheck changes.

Some classes don’t exist (Person, Chair, etc…) but again, not much work to simplify them using adv3Lite. I continue reading more of the doc so that’s good.

Here are the obstacles I ran into:

a) How do you now modify a verb action? For instance, if I have:

modify YellAction execAction() { "That's not really in my nature. "; }
[/code]but since YellAction doesn't exist now, the compiler complains.  Should I just modify the Yell verb?

b) Here is a transcript from my WIP before the port to adv3Lite
[quote]
>order food
I wasn't hungry, but I was in the mood for a drink. 

>order coke
The aye-aye just stared at me with those soul-piercing eyes of his. I changed my mind and got myself a Pibb Xtra instead. 

>undo
Taking back one turn: “order coke”.

>order chair
I wasn't sure how to do that. 
[/quote]
Since the PC could order quite a bit of items, the adv3Lite code now looks like this (case code greatly reduced for readability):
[code]VerbRule(Order) ('order' singleLiteral) | ('order'  ('a' | 'an' | 'the' | 'some') singleLiteral): VerbProduction
   verbPhrase = 'order/ordering (what)'
   action = Order
;

VerbRule(OrderThing) 'orderthing' singleDobj : VerbProduction
   verbPhrase = 'order/ordering (what)'
   action = OrderThing
;
 
DefineLiteralAction(Order)
    execAction() {
        switch(getLiteral.toLower) {
            case 'food':
                "I wasn't hungry, but I was in the mood for a drink. ";
                break;
            case 'drink':
                if (!client.arrives) 
                    aye.orderDrink(true);
                else
                    "I only needed one drink. ";
                break;
            default:
                local toks = Tokenizer.tokenize('orderthing ' + getLiteral.toLower);
                executeCommand(gActor, gActor, toks, true);
                break;
      }
   }
;[/code]getLiteral is not defined anymore.  I think the compiler complained about the tokenizer line as well...

c) I had lifted a good share of TourGuide code from a few years ago during my first attempt at T3 but sadly most of that code appears to be incompatible.  I'm assuming that I won't need a lot of it with adv3Lite!

d) Am I correct in assuming that the following code is the proper way to move the PC/NPC around now?
[code]
client.actionMoveInto(pettingZoo);
[/code]
e) When my NPC arrives, conversation is initiated.  This is how the WIP did it, but adv3Lite is looking for something else:
[code]
client.initiateConversation(clientChatting, 'client-greeting'); 
[/code]
f) I need to read up on the NPC section now, but I'm guessing this is what you were talking about earlier in terms of conflicts since adv3Lite does not know about InConversationState.
[code]+ clientDoneChatting : InConversationState
    specialDesc = "My client kept checking his watch, patiently waiting to wrap up our conversation. "
    stateDesc = "He scanned the zoo and looked totally bored. "
;

g) This one might be simple but it was getting late and didn’t get to it. What’s the new syntax – is it lookAroundWithin?

gPlayerChar.lookAround(true);

I’m going to soak up more of the doc tomorrow. Thanks for your help, Eric!

– Mike

I’m glad to hear things are still going reasonably smoothly. Here’s some quick off-the-top-of-my-head responses to some of the obstacles you encountered, in case that helps you make a bit more progress; anything else will have to wait until I’m back home from work this evening.

Yes, you just modify the Yell verb. While actions are classes in adv3, in adv3Lite they are simply objects (I inherited this part of the implementation from Mike Roberts’s Mercury code), and they don’t have ‘Action’ in their name.

You need gLiteral, got getLiteral, and the token you need in your VerbRule(Order) is literalDobj, not singleLiteral (see the manual section on Literal Actions).

You assume correctly. There’s probably not much adv3 code that will directly compile in adv3Lite unchanged, and the Tour Guide covers a lot of classes that don’t exist in adv3Lite (one of the design objectives of adv3Lite was to reduce the class hierarchy).

You can use actionMoveInto(newLocation) as here, though in my own code I tend to use simply moveInto(newLocation). The difference is that actionMoveInto() triggers various before and after move notifcations such as notifyRemove() and notifyInsert(), which could potentially veto the move. If you want the NPC to move come what may, use moveInto(); if you want the notifications, with the possibility that they may veto the move, use actionMoveInto().

There’s a whole section on NPC-Initiated Conversation in the manual, which describes the various methods available in adv3Lite. Unfortunately, calling initiateConversation() on the NPC isn’t one of them. I’d probably use a ConvAgendaItem to do the job; the adv3Lite version of ConvAgendaItem takes care of quite a bit of the housekeeping work that adv3’s initiateConversation method does, so that if you define an invokeItem() method to display whatever it was you want your NPC to say, it should all work fine. It can also contain a <.convnode> or <.convnodet> tag to set a Conversation Node (<.convnodet> does the same as <.convnode> but also lists suggested topics).

You do indeed need to read up on the NPC section, since this is one area where things do work rather differently in adv3 and adv3Lite. As I mentioned in a previous post, in adv3Lite there are no subclasses of ActorState (such as ConversationReadyState or InConversationState). There is a mechanism for changing ActorState when beginning and ending a conversation, but it’s basically done by specifying the ActorState you want to change to on the changetoState property of the relevant HelloTopic or ByeTopic. For example:

+ HelloTopic
    "<q>Hello,</q> you say.\b
    <q>Hi there!</q> Bob replies. <.agenda fireAgenda> "
    
    changeToState = bobTalking
;

For a full discussion, see the manual section on Hello and Goodbye.

It is lookAroundWithin(), but you need to call it on the player character’s location, not on the player character object.

Hope that helps. If you need any further explanations do get back to me.

I’ll admit to commenting out 98% of the existing conversation code, but after a few more collisions with the compiler, I got this:

So yeah, I’ve made it to runtime! Of course, the first command I entered crashed the 'terp, but I’m not complaining.

I am going to spend time reading the supplied doc before I tackle the conversation code. I’ll keep you updated on the progress, Eric.

Thanks for your help so far…

– Mike

Great! That’s good to hear.

Hopefully that was due to some easily-discoverable error in your game code, not to some nasty library bug lurking below the surface of adv3Lite!

I look forward to hearing about it.

In the meantime I’ve made a couple of small tweaks to the next version of adv3Lite in the light of your experience so far.

(1) I’ve added templates for EventList and ShuffledEventList (they can’t do any harm and the snag you ran into with needing to define the eventList property explicitly seems an unnecessary obstacle to put in the way of people porting from adv3).

(2) I’ve added a couple of lines of code to the initVocab() method of LMentionable (which is part of the preinitialization of Thing), the effect of which is to automatically set the name of a Thing to ‘I’ or ‘you’ if the person is 1 or 2. This gives game authors one less thing to do when defining the player character object (it just struck me as a bit inelegant that I had to tell you to change the name of the player character object to ‘I’ as well as changing its person to 1 to create a first-person player character; one change should be sufficient since the other clearly follows from it).

It was rather simple – just a typo. Instead of missingQ I had misingQ.

In the meantime, I am making progress. More updates later this week…

Eric, my legacy code containing the message parameter substitution text is causing me to chase my tail. I was going to wait to take you up on your offer to explain it, but I think I need to get this off my list now. As previously mentioned, the first few locations are in past tense and then it changes to present.

So yeah, I’m all ears!

I replaced getLiteral with gLiteral and changed the token accordingly. And I read up on that section, but I’m still stumped. :slight_smile:

It now compiles, but when I type ‘order drink’, I get Wrong number of arguments runtime error in action.t at: /* Execute the main action handling. */ execAction(cmd); This is how Order is currently defined:VerbRule(Order) ('order' literalDobj) : VerbProduction verbPhrase = 'order/ordering (what)' action = Order missingQ = 'what do you want to order' ;
Also, I really liked this extension in T3:// To use this extension, which was created by Ben Cressey and Eric Eve, define Test objects like so: /* * The 'list tests' and 'list tests fully' commands can be used to list * your test scripts from within the running game. */ Is it compatible or is there something similar in adv3Lite? I’ve looked, but admittedly not too hard since I’ve had other pressing issues.

Thanks!

There are two ways you could tackle this, one using the tSel() macro, the other using message parameter substitutions.

Using the first method you’d have to write strings like:

'The iguana thrashe<<tSel('s','d')>> her tail. '

Using the second you’d write:

'The iguana {dummy} {thrashes} her tail. '

And you’d probably have to tell adv3Lite how to conjugated ‘thrashes’ by also adding:

CustomVocab
    verbParams = 
    [
        'thrash/thrashes/thrashed'       
    ]   
;

The {dummy} parameter doesn’t actually display anything, but it’s needed in order to provide a subject for {thrashes}, which might more normally be used in constructions like:

  "{The subj dobj} {thrashes} you half to death. ";

There’s a reasonably full explanation of this in the Messages section of the manual (under the Actions chapter).

I can see that this may all look a bit long-winded for your needs, so I’ll look into adding something like the {es|ed} syntax to allow simple tense switching in the next version (one problem is that the adv3Lite messaging system already used | for another purpose), but I don’t guarantee anything (at least, I shan’t until I’ve looked into it more closely), and for now you’ll have to use one of the two methods suggested above.

This may have to wait till I’m back at my own machine and can try a couple of things out. Off the top of my head the two most likely causes of this error are either (1) a bug in the library code for LiteralAction (though I can’t see it just by looking at it) or (2) an error in the way you’ve defined your own Order action, which should follow a pattern like:

DefineLiteralAction(Order)
   execAction(cmd)
   {
        "Okay, {I} {order} <<gLiteral>>. ";
   }
;

One LiteralAction that’s defined in the library is TYPE, so if you read this message before I have a chance to look into this further you could try entering the command TYPE DRINK and see if it gives you the same error. If it does, then there’s probably a bug in the library I need to look into. If instead the game responds with the expected “What do you want to type on?” then the error is more likely to be in your own code, and if you’re having difficulty tracking it down then perhaps you could post the definition of your Order action here so I can take a look at it.

I’ve not yet looked at converting any extensions to adv3Lite as yet, though I can see this would be a useful one to have, and I did have it vaguely in mind to add a set of useful extensions at some point, so this might be a good one to start on. Again I’d have to be back at my own machine to look into this. I suspect it may not work without some conversion, but that it probably shouldn’t be too difficult to convert. I’ll let you know once I’ve had a chance to take a look at it.

Darn it! User error (per usual). In the numerous iterations of this code during the port, I reverted back to execAction() which works fine in T3 but requires (cmd) in adv3Lite. False alarm on the Wrong number of arguments, however, I have two issues with it still. This is the code I have.

[code]VerbRule(Order) (‘order’ literalDobj) | (‘order’ (‘a’ | ‘an’ | ‘the’ | ‘some’) literalDobj): VerbProduction
verbPhrase = ‘order/ordering (what)’
action = Order
missingQ = ‘what do you want to order’
;

VerbRule(OrderThing) ‘orderthing’ singleDobj : VerbProduction
verbPhrase = ‘order/ordering (what)’
action = OrderThing
missingQ = ‘what do you want to order’
;

DefineLiteralAction(Order)
execAction(cmd) {

    switch(gLiteral.toLower) {
        case 'food':
            "I wasn't hungry, but I was in the mood for a drink. ";
            break;
        case 'drink':
            if (!client.arrives) 
                "I was originally going to help myself to a Mr. Pibb, but you know what? Let's party; I got myself a Pibb Extra. ";
            else
                "I only needed one drink. ";
            break;
        default:
            local toks = Tokenizer.tokenize('orderthing ' + gLiteral.toLower);
            executeCommand(gActor, gActor, toks, true);
            break;
  }

}
;

DefineTAction(OrderThing)
includeInUndo = nil
actionTime = 0
;

modify Thing
dobjFor(OrderThing) { verify() { "{You/he} aren’t sure how to do that. "; } }
}
;[/code]
This is the transcript from that code:

order food
I wasn’t hungry, but I was in the mood for a drink.

order drink
I was originally going to help myself to a Mr. Pibb, but you know what? Let’s party; I got myself a Pibb Extra.

order something

order
What do you want to order?

food
I don’t understand that command.

order
What do you want to order?

drink
The terp crashes with “property pointer required” at remapResult = obj.(remapProp);

Also, executeCommand doesn’t exist in adv3Lite; is there a replacement? That’s why ‘order something’ returned nothing – I’ve ignored the compile error that it doesn’t know what executeCommand is.

For what it’s worth, I relied heavily on this and it would be terrific to have again as I plow through adv3Lite.

Thanks!

I’ve now tried this and I don’t get an error. I also don’t get an error when I try using your VerbRule plus:

DefineLiteralAction(Order)
    execAction(cmd)
    {
        "Okay, so you order <<gLiteral>> ";        
    }
;

So the most likely culprit is your version of the Order action (defined with DefineLiteralAction(Order)). For example, if you did this:

DefineLiteralAction(Order)
    execAction() // missing cmd parameter
    {
        "Okay, so you order <<gLiteral>> ";        
    }
;

You’d get precisely the run-time error you describe. Moreover, if you’ve ported adv3 code you could easily have something like the above, since I suspect adv3 doesn’t have a cmd parameter in the equivalent method. NOTE: I see you’ve just discovered this as your new post appeared as I was writing this one!

As an aside, depending on what you’re trying to do here, it might be easier to implement this via a Topic action rather than a Literal one, but perhaps that’s a subject for another day, once you’ve finished porting what you’ve got.

I’ve done a quick and dirty conversion which is attached herewith. I’ve only tested it very quickly, but it seems to work.
testsLite.zip (1.4 KB)

Well, that verify routine is wrong for a start, and could well be the culprit. You have to use one of the illogical/logical macros in a verify routine; it’s not like check() where you can just use a double-quoted string. What you need is:

[code]modify Thing
dobjFor(OrderThing) { verify() { illogical(’{I} {aren’t} sure how to do that. '); } }

;
[/code]

Note that I also had to change your message substitution parameters here, which were still adv3 style instead of adv3Lite style.

The nearest equivalent is probably Parser.parse(str), which takes str, parses it as a command and then executes it. A quick tests suggests the following works reasonably okay:

VerbRule(OrderThing) 'orderthing' singleDobj : VerbProduction
   verbPhrase = 'order/ordering (what)'
   action = OrderThing
   missingQ = 'what do you want to order'
;

DefineLiteralAction(Order)
    execAction(cmd) {

        switch(gLiteral.toLower) {
        case 'food':
            "I wasn't hungry, but I was in the mood for a drink. ";
            break;
        case 'drink':
            if (!client.arrives)
                "I was originally going to help myself to a Mr. Pibb, but you
                know what? Let's party; I got myself a Pibb Extra. ";
            else
                "I only needed one drink. ";
            break;
        default:
            
            Parser.parse('orderthing ' + gLiteral.toLower);
            break;
      }
   }
;

DefineTAction(OrderThing)
   includeInUndo = nil
   actionTime = 0
;

modify Thing
   dobjFor(OrderThing) 
        { verify() { illogical('{I} {aren\'t} sure how to do that. '); } }
 
;

The original code had illogical there and it still failed. I had put double quoted strings in an attempt to get different results to help me debug. I should have put illogical back when I pasted the code - sorry about that misleading code. But yeah, illogical in the verify isn’t the problem, I still get the same error.

Interestingly enough, though, if I type this (something that doesn’t exist in the game world):

order
What do you want to order?

wine
You saw no wine there.

But if enter an object in the game world (in scope) I get the error.

Maybe I need to rethink this whole “order” thing. I had spent a lot of time getting it to work; the code was actually lifted from Bcressey’s example in a previous thread and Jim Aikin and RealNC were very helpful in tweaking it right. I’d be interested in hearing your Topics thought with adv3Lite.

But to your response, I can now type “order something” or “order a drink” or “order some food” but I get “You saw no [something, a drink, some food] there.”

The original code trapped for all that in and handled it accordingly.

And thanks for the Test extension! I will try it out tonight and let you know how it works.

– Mike

Okay, I can now reproduce this error, and it’s due to a library bug. If you want to fix the bug you need to make some changes in action.t. First change the definition of IAction so it reads:

class IAction: Action
    /* 
     *   There's usually no point in parsing an IAction again when it's repeated
     *   since there are no objects to have changed.
     */
    againRepeatsParse = nil
    
    /* 
     *   For an IAction there's no point in trying to score anything but the
     *   Actor object; attempting to score objects via their verify properties
     *   will cause a run-time error, since IActions don't define verify
     *   properties and the like.
     */
    scoreObjects(cmd, role, lst)
    {
        if(role == ActorRole)
            inherited(cmd, role, lst);
        else
        {
            /* apply verb-specific adjustments */
            foreach (local i in lst)
                scoreObject(cmd, role, lst, i);
            
            /* apply object-specific adjustments */
            foreach (local i in lst)
                i.obj.scoreObject(cmd, role, lst, i);
        }
    }
;

It’s the overridden scoreObjects() method that’s new here.

Next, change the definitions of LiteralAction and SystemAction so that they inherit from IAction instead of Action.

I may need to look into this more thoroughly, but this seems to fix the problem (and it reflects the changes I’ve now made in the library).

What’s happening here is that the ‘a’ or ‘some’ is getting swallowed by the literalDobj token, so it’s matching “ORDER (SOME FOOD)” not “(ORDER SOME) FOOD”

One way round this might be to trap all the possibilities in your switch statement, along the lines of:

DefineLiteralAction(Order)
    execAction(cmd) {

        switch(gLiteral.toLower) {
        case 'food':
        case 'some food':
            "I wasn't hungry, but I was in the mood for a drink. ";
            break;
        case 'drink':
        case 'a drink':
            if (!client.arrives)
                "I was originally going to help myself to a Mr. Pibb, but you
                know what? Let's party; I got myself a Pibb Extra. ";
            else
                "I only needed one drink. ";
            break;
        default:
            
            Parser.parse('orderthing ' + gLiteral.toLower);
            break;
      }
   }
;

I’ll take a look at doing this with topics instead (which I think may be simpler) and get back to if and when I have time.

Okay, here’s the promised implementation using a TopicAction. I think it does the same as you were trying to do with your code, but I think it’s a bit neater:

VerbRule(Order)
    'order' topicDobj
    : VerbProduction
    action = Order
    verbPhrase = 'order/ordering (what)'
    missingQ = 'What do you want to order'
;

DefineTopicAction(Order)
    execAction(cmd)
    {
        local topic; 
        
        if(cmd.dobj.ofKind(ResolvedTopic))
            topic = cmd.dobj.topicList[1];
        else
            topic = cmd.dobj;
        
        switch(topic)
        {
        case tFood:
             "I wasn't hungry, but I was in the mood for a drink. ";
            break;
            
        case tDrink:
            if (!client.arrives)
                "I was originally going to help myself to a Mr. Pibb, but you
                know what? Let's party; I got myself a Pibb Extra. ";
            else
                "I only needed one drink. ";
            break;
            
        default:
            if(Q.scopeList(gActor).toList().indexOf(topic) == nil)
                "{I} {see} no <<topic.name>> here. ";
            else
                "{I} {aren\'t} sure how to do that. ";               
            
        }
    }
;

tDrink: Topic 'drink';
tFood: Topic 'food; some';

This avoids the need for a separate OrderThing action and the modification to Thing to respond to it. It also leverages the parser’s ability to match the vocab of Things and Topics without the need to try to build possible variations into the VerbRule grammar.

I’ve also taken a look at the problems you’ve had with exchanges like:

>Order
What do you want to order?

>Food
I don't understand the command.

I’ve attempted a fix in the library code, which hopefully won’t give rise to any new bugs (though only time will tell!). If you want to patch your own version of the library code in the meanwhile, then make the following change in command.t, at around line 573 or so, where you need to add the following code right at the beginning of the addNounProd() method:

addNounProd(role, prod)
    {
        /* 
         *   The type of missing phrase we're looking for depends on the type of
         *   action.
         */
        if(action.ofKind(TopicAction))
            prod.npClass = TopicPhrase;
        else if(action.ofKind(LiteralAction))
            prod.npClass = LiteralPhrase;
        else 
            prod.npClass = NounPhrase;

This seems to work a bit better than before, at any rate.

Thank you, that worked great!

That works quite nicely. Most attempts to order now resolve themselves quite nicely. Much appreciated!

More updates tonight…

Eric, the Test extension you tweaked is working. Thank you very much for that – it has sped up my cycles…

I’m currently redoing the opening NPC interaction so that’s going to take some time.

I know this is only valid for anybody else that decides to port their WIP, don’t forget that execAction now needs a parm (ie, execAction(cmd)). I can’t tell you how many times I’ve tripped on that… And so does Inherited().

I think it’s still too early to provide useful feedback on adv3Lite vs. T3 but I’ll eventually get there!

– Mike