Real time countdown in Inform6?

Hello,
in Inform 6 is it possible to implement a real-time countdown (no turns) displaying the remaining seconds, or a recurring message when time is about to expire?

In my adventure I often require the player to enter a numeric code, and I would like to add a (real) time limit.

I’m not an expert programmer, any easy code snippet will be a great help… thank you so much!

In a Z-code game at least:

It’s possible to have a routine called once a second while the game is waiting for input. This routine can switch output to the top window, position the cursor, print something, and then return output to the bottom window.

It’s not possible to measure the time or do something at certain intervals while the game is processing a move, printing text, or the game has displayed a MORE prompt and is waiting for the player to respond.

1 Like

You can be notified up to 10 times a second in Z-Code.

However not all interpreters support it. I still haven’t added it to Parchment yet.

Thank you so much for your kind feedback.

My current code, adapted from a tutorial by Vincenzo Scarpa, is:

Constant Story "Test Question";
Constant Headline "^An Interactive Fiction^";

Include "Parser";
Include "VerbLib";

Array Ginput1->10;  ! Array for input
Array Ginput2->10;  ! Array for input

[ GetNumber n k;
    if (n > 5) n = 5;
    if (n < 1) n = 1;
    for (k = 0: k < 10: k++) Ginput1->k = 0; 
    Ginput1->0 = n;
    KeyboardPrimitive(Ginput1, Ginput2);

    if (Ginput1->2 < 48 || Ginput1->2 > 57) rfalse; ! If not a number, exit
    if (Ginput1->3 < 48 || Ginput1->3 > 57)
        return Ginput1->2 - 48;
    if (Ginput1->4 < 48 || Ginput1->4 > 57) 
        return ((Ginput1->2 - 48) * 10) + (Ginput1->3 - 48); 
    if (Ginput1->5 < 48 || Ginput1->5 > 57)
        return ((Ginput1->2 - 48) * 100) + ((Ginput1->3 - 48) * 10) + (Ginput1->4 - 48); 
    if (Ginput1->6 < 48 || Ginput1->6 > 57)
        return ((Ginput1->2 - 48) * 1000) + ((Ginput1->3 - 48) * 100) + ((Ginput1->4 - 48) * 10) + (Ginput1->5 - 48); 

    if ((Ginput1->2 > 51) || (Ginput1->3 > 50) || (Ginput1->4 > 55) || (Ginput1->5 > 54) || (Ginput1->6 > 55)) 
        rfalse; ! If it's a number greater than 32767

    return ((Ginput1->2 - 48) * 10000) + ((Ginput1->3 - 48) * 1000) + ((Ginput1->4 - 48) * 100) + ((Ginput1->5 - 48) * 10) + (Ginput1->6 - 48);
];

[ Question x;
    do   
    {
        print "^How many years does it take for Halley's comet to appear? ";
        x = GetNumber(2);
        if (x == 76) 
            print "The answer is correct!!!^";
        else 
            print "Sorry, the answer is incorrect...^";
    } 
    until (x == 76);
];

[ Initialise;
    ClearScreen(); ! Clear the screen at the start
    Question(); 
    print "^Press any key to exit^";
    KeyCharPrimitive(); ! Reads a character from the keyboard
    quit; ! End of the program 
];

Include "Grammar";

Since the interpreter isn’t moving, isn’t printing text, and isn’t waiting for MORE, it should be doable… but I wonder if it might disturb user input.

To simplify, rather than a countdown every second it would be enough for a message to be printed (e.g. every 30 seconds) such as “Hurry up!” directly in the actual cursor position.

Is there a routine I could start from as a starting point?
Thank you so much!

I don’t have the time to write it right now.

You need to make you own version of KeyboardPrimitive, which takes a routine address as an argument.

Here’s KeyboardPrimitive: https://gitlab.com/DavidGriffith/inform6lib/-/blame/master/parser.h?ref_type=heads&page=2#L1116

You change read to @aread (see syntax etc at The Z-Machine Standards Document )

If your routine prints something and returns false, the interpreter should detect it automatically and print the input line again. If it returns true, the KeyboardPrimitive routine should return a special value to say the routine ordered input to be halted.

Sorry but I’m not sure I understood correctly.

I already have working code that handles input, correct response etc.

To add a time countdown (as a stopwatch) do I necessarily have to modify KeyboardPrimitive? I was thinking about a separate, concurrent function that could start when the question is asked…

Yes you have to modify the input routine.

This is the only mechanism that the Z-machine provides to run a routine periodically.

1 Like

Ah OK, now everything is clear.
Thank you very much for the suggestion!
Honestly it seems rather “advanced” for me in terms of implementation, but I’ll do some of scouting to understand how to proceed.

1 Like

The library has no support for this, so it doesn’t come with a convenient way to use this functionality.

That being said, it’s not crazy hard to implement. It’s easy to call an assembler instruction right in the middle of Inform-code, and the documentation I pointed to is good.

I think your new input routine might look something like this (not tested):

[ KeyboardPrimitiveWithRoutine  a_buffer a_table a_time a_routine ret;

    ! If there's leftover input from a previous read which was interrupted, print it
    if(a_buffer->1 > 0) {
        for(ret=0: ret<a_buffer->1: ret++) print (char) a_buffer->(2+ret);
    )

    @aread a_buffer a_table a_time a_routine -> ret;
    if(ret==0) return -1; ! Interrupt routine returned true
];
1 Like

Thank you so much Fredrik!!!

1 Like