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!
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.
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!
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.
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…
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.
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
];