dial control not working in adv3lite

I have a speed control dial on a treadmill. I want entries limited to whole numbers, and I want to accept either numerals or words (1, 2, 3, etc. or one, two, three, etc.).

I have implemented the dial as a NumberedDial, with minSetting = 1 and maxSetting = 5.

When I enter an integer within the range 1…5, the dial accepts it. When I enter an integer outside the range, the dial refuses it as invalid.

But the only way I have found to accept words as well as numbers is to redirect the command if it has a word for an iobj, in the dobjFor(SetTo) { check() } method. But when I implement that dobjFor(SetTo), the dial accepts any integer, without regard to the min/max range. It accepts 6, for example, even though the range is 1…5.

That’s problem one.

The other more vexing problem is that the Dial will also accept fractions—up to a point—even though I’ve tried forcing the input to an integer with val = toInteger(val) in makeSetting(val)..

The dial acts on the whole number portion as one command, as though it were entered as an integer, then it apparently tries to run the same command a second time on the fractional portion and fails because of the period. This happens with or without the dobjFor(SetTo) method.

Here’s a run through with the dobjFor(SetTo) method commented out. If I put it back in, the results are the same except that the command Set dial to 6 works even though 6 is out of range.

#charset "us-ascii"

#include <tads.h>
#include "advlite.h"

versionInfo: GameID
    IFID = '445C38A3-AD1B-4729-957A-F584600DE5C1'
    name = 'test'
    byline = 'by Jerry Ford'
    htmlByline = 'by <a href="mailto:jerry.o.ford@gmail.com">
                  Jerry Ford</a>'
    version = '1'
    authorEmail = 'Jerry Ford <jerry.o.ford@gmail.com>'
    desc = 'Testing dial control.'
    htmlDesc = 'Testing dial control.'

;

gameMain: GameMainDef
    initialPlayerChar = me
    paraBrksBtwnSubcontents = nil
   
;

me: Actor 'me' @room
    "The main man.<.p>"
    isHim = true
    person = 2
;

room: Room 'room'
    "In the room, there is a dial.\b
    <<dialCtl.desc>><.p>"
;
+ dialCtl: NumberedDial 'dial'
    "The dial has settings from <<minSetting>> to <<maxSetting>>. <.p>"
    
    minSetting = 1
    maxSetting = 5
    
    makeSetting(val)
    {
        inherited(val);
        "The dial has been set to ";
        say(val);
        ". <.p>";
    }

    dobjFor(SetTo)
    {
        check()
        {
            if(gIobj.name == 'one')
                doInstead(SetTo, dialCtl, '1');
        }
        action()
        {
            inherited;
        }
    }
;

Any suggestions? I’m willing to give up on the input as number or word problem and just require one or the other (I have also tried implement this as a simple Dial, which takes words not integers, but the same problems occur). But that decimal problem is a, well, a problem. Not so much that I want to accept such entries, but I’m stymied on how to craft an intelligible error response (The default message I don’t understand that command is somewhat nonsensical in this context), since I cannot find a way intercept the fractional entry. The division into two commands happens before I can get to the input.

Well, I guess I could do it in a StringPreParser(), but I’m not sure how many different ways a user could set a dial, so I’m bound to miss at least one of them.

Is that my only option?

Jerry

I’ll have a look at this later. For starters, though, it appears that Settable has a validSettings property, a single-quoted list. Have you tried tinkering with that?

Dial, a subclass of settable, has a validSettings property that contains list of single quoted strings.

NumberedDial, a subclass of Dial, has minSetting and maxSetting properties, which take integers.

I have tinkered with both. They both produce the same results when a fractional value is entered (“set dial to 1.5” is parsed first as “set dial to 1” which sets curSetting to 1 and then—I’m guessing on this one—“set dial to .5” which produces an error message), and I have been unable to get either one to respond to both a number (entered as ‘1’ in Dial, or as 1 in NumberedDial) and a word (‘one’) correctly.

Jerry

The problem here (in the 1.5 case) is, I suspect, that the parser sees the decimal point in 1.5 as a full stop terminating the sentence, so it is parsing the command SET DIAL TO 1.5 as the two commands SET DIAL TO 1 and 5. The “I don’t understand that command” is then a response to the apparently nonsensical command 5. I don’t think there’s much you can do about that except intercept such input with a StringPreParser.

For a Dial to accept both letters and numbers, couldn’t you just use an ordinary dial and set its validSettings property to [‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘one’, ‘two’, ‘three’, ‘four’, ‘five’]?

Yes, a mixed settings list appears to be okay.

But now I have a worse problem—keeping the dial within range, which does not appear to be related to the valid settings list, but is a huge problem nonetheless.

The dial’s valid settings list is now ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘one’, ‘two’, ‘three’, ‘four’, ‘five’.

If the player enters the command Set dial to 65 the dial properly refuses.

But the TurnTo command—turn dial to 65—accepts the setting as valid. Nothing I’ve tried corrects this.

Here’s the newe code…

#charset "us-ascii"

#include <tads.h>
#include "advlite.h"

versionInfo: GameID
    IFID = '445C38A3-AD1B-4729-957A-F584600DE5C1'
    name = 'test'
    byline = 'by Jerry Ford'
    htmlByline = 'by <a href="mailto:jerry.o.ford@gmail.com">
                  Jerry Ford</a>'
    version = '1'
    authorEmail = 'Jerry Ford <jerry.o.ford@gmail.com>'
    desc = 'Testing dial control.'
    htmlDesc = 'Testing dial control.'

;

gameMain: GameMainDef
    initialPlayerChar = me
    paraBrksBtwnSubcontents = nil
   
;

me: Actor 'me' @room
    "The main man.<.p>"
    isHim = true
    person = 2
;

otherGuy: Actor 'other guy' @room
    "The other guy.<.p>"
;

room: Room 'room'
    "In the room, there is a dial.\b
    <<dialCtl.desc>><.p>"
;
+ dialCtl: Dial 'dial'
    "The dial can be set to one of <<orList(validSettings)>>. <.p>"
    
    validSettings = ['1','2','3','4','5','one','two','three','four','five']
    curSetting = '1'
    
    dobjFor(TurnTo)
    {
        check()
        {
            if(otherGuy.location == garage)
                "The dial doesn't work unless both of you are in the same room.
                <.p>";
        }
    }
;

garage: room 'garage'
    "The garage.<.p>"
;

I’m sure there’s a way to fix this … but alternatively, you could create a set of speed control buttons numbered 1 through 5 and let the player push a button. It’s a bit less realistic, but on the other hand, the treadmills at my gym don’t have dials either. They have inc/dec buttons for altering the speed, which is yet a third way to do it. Numbered buttons I know work, because I’m using them in my WIP.

That’s because Dial defines:

dobjFor(TurnTo) asDobjFor(SetTo)

And it’s the check() part of dobjFor(SetTo) that checks whether the value entered is in range; buto on dobjFor(Turn) you’ve overridden the check() method with your own code, effectively replacing the check that was there before.

What you need to do instead is something like this:

dobjFor(SetTo) { check() { inherited; if(otherGuy.location == garage) "The dial doesn't work unless both of you are in the same room. <.p>"; } }

Note that you need to define your check on dobjFor(SetTo) not dobjFor(TurnTo), and that you also need to call the inherited method.

Incidentally, here’s a StringPreParser that catches numbers with decimal points in them:

StringPreParser
    doParsing(str, which)
    {
        str = str.findReplace( R'<digit>+<period><digit>+',
                              { match, index, orig: '"'+match+'"' }  );
        
        return str;
    }
;

Note, however that to do anything with the decimal number caught by this you’d need to strip off the enclosing double quotes from it.

Excellent. Both the dial and the decimal number filter seem to be working perfectly. Thanks.

Jerry

Great, I’m glad that’s working for you.

I’m adding the decimal point filter to the next version of adv3Lite, and also some tweaks to the NumberedDial class so that it can accept either only integer settings (as now) or numbers with decimal points in as well.