Glulx Inform 7: How to restart line input after canceling

Thanks for correcting my overthinking of global variables in I6–I don’t know why I started thinking that they were values rather than references (sheesh)–and also for the good points about the dangers of firing real-time events during disambiguation. I think you’re right that the worst anyone is likely to see out of it is “you can’t see that here,” but users should be aware of it all the same (some version of this code will end up in the next version of Sarah Morayati’s Basic Real-Time extension, which is why I’m phrasing this in terms of users).

I can think of a lot of reasons not to end the game from a real-time event. What happens if the player types UNDO during the final question, for example?

Anyway, here’s a beefed-up version of the sample code that will work across both disambiguation and yes-no questions (spoilered for length):

[spoiler][code]Include Glulx Entry Points by Emily Short.

Test is a room. The red ball is in Test. The blue ball is in Test.

The button is a device in Test.

Instead of pushing the button:
say “Click. Is that a clock ticking?”;
start a 2000 millisecond timer.

Instead of waving hands:
say “Do you want to press the button?”;
if player consents:
say “Well, push it. No one’s stopping you.”;
otherwise:
say “Don’t, then.”

A glulx timed activity rule:
cancel line input in the main window, preserving keystrokes;
say “[line break]”;
say “Boom! There’s an explosion somewhere else in the complex.”;
stop timer;
say “[line break]”;
say “[if we are disambiguating]Which?[end if]”;
say “[command prompt][run paragraph on]”;
re-request line event in main window.

To re-request line event in main window:
(- glk_request_line_event(gg_mainwin, stored_buffer + WORDSIZE, INPUT_BUFFER_LEN - WORDSIZE, stored_buffer–>0); -)

To cancel line input in the/-- main window, preserving keystrokes:
(- glk_cancel_line_event(gg_mainwin, gg_event); stored_buffer–>0 = gg_event–>2; -)

To decide whether we are disambiguating:
(- stored_buffer == buffer2 -)

[Timer code: pulled from Real-Time Delays]

To start a/-- (T - a number) millisecond/ms timer:
(- if (glk_gestalt(gestalt_Timer, 0)) glk_request_timer_events({T}); -)

To stop the/-- timer:
(- if (glk_gestalt(gestalt_Timer, 0)) glk_request_timer_events(0); -)

Include (-

[ VM_KeyChar win nostat done res ix jx ch;
jx = ch; ! squash compiler warnings
if (win == 0) win = gg_mainwin;
if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
done = glk_get_line_stream(gg_commandstr, gg_arguments, 31);
if (done == 0) {
glk_stream_close(gg_commandstr, 0);
gg_commandstr = 0;
gg_command_reading = false;
! fall through to normal user input.
} else {
! Trim the trailing newline
if (gg_arguments->(done-1) == 10) done = done-1;
res = gg_arguments->0;
if (res == ‘’) {
res = 0;
for (ix=1 : ix<done : ix++) {
ch = gg_arguments->ix;
if (ch >= ‘0’ && ch <= ‘9’) {
@shiftl res 4 res;
res = res + (ch-‘0’);
} else if (ch >= ‘a’ && ch <= ‘f’) {
@shiftl res 4 res;
res = res + (ch+10-‘a’);
} else if (ch >= ‘A’ && ch <= ‘F’) {
@shiftl res 4 res;
res = res + (ch+10-‘A’);
}
}
}
jump KCPContinue;
}
}
done = false;
glk_request_char_event(win);
while (~~done) {
glk_select(gg_event);
switch (gg_event–>0) {
5: ! evtype_Arrange
if (nostat) {
glk_cancel_char_event(win);
res = $80000000;
done = true;
break;
}
DrawStatusLine();
2: ! evtype_CharInput
if (gg_event–>1 == win) {
res = gg_event–>2;
done = true;
}
}
ix = HandleGlkEvent(gg_event, 1, gg_arguments);
if (ix == 2) {
res = gg_arguments–>0;
done = true;
} else if (ix == -1) done = false;
}
if (gg_commandstr ~= 0 && gg_command_reading == false) {
if (res < 32 || res >= 256 || (res == ‘’ or ’ ')) {
glk_put_char_stream(gg_commandstr, ‘’);
done = 0;
jx = res;
for (ix=0 : ix<8 : ix++) {
@ushiftr jx 28 ch;
@shiftl jx 4 jx;
ch = ch & $0F;
if (ch ~= 0 || ix == 7) done = 1;
if (done) {
if (ch >= 0 && ch <= 9) ch = ch + ‘0’;
else ch = (ch - 10) + ‘A’;
glk_put_char_stream(gg_commandstr, ch);
}
}
} else {
glk_put_char_stream(gg_commandstr, res);
}
glk_put_char_stream(gg_commandstr, 10); ! newline
}
.KCPContinue;
return res;
];

[ VM_KeyDelay tenths key done ix;
glk_request_char_event(gg_mainwin);
glk_request_timer_events(tenths*100);
while (~~done) {
glk_select(gg_event);
ix = HandleGlkEvent(gg_event, 1, gg_arguments);
if (ix == 2) {
key = gg_arguments–>0;
done = true;
}
else if (ix >= 0 && gg_event–>0 == 1 or 2) {
key = gg_event–>2;
done = true;
}
}
glk_cancel_char_event(gg_mainwin);
glk_request_timer_events(0);
return key;
];

[ VM_ReadKeyboard a_buffer a_table done ix;
if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
done = glk_get_line_stream(gg_commandstr, a_buffer+WORDSIZE,
(INPUT_BUFFER_LEN-WORDSIZE)-1);
if (done == 0) {
glk_stream_close(gg_commandstr, 0);
gg_commandstr = 0;
gg_command_reading = false;
! L__M(##CommandsRead, 5); would come after prompt
! fall through to normal user input.
}
else {
! Trim the trailing newline
if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1;
a_buffer–>0 = done;
VM_Style(INPUT_VMSTY);
glk_put_buffer(a_buffer+WORDSIZE, done);
VM_Style(NORMAL_VMSTY);
print “^”;
jump KPContinue;
}
}
done = false;
stored_buffer = a_buffer;
glk_request_line_event(gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0);
while (~~done) {
glk_select(gg_event);
switch (gg_event–>0) {
5: ! evtype_Arrange
DrawStatusLine();
3: ! evtype_LineInput
if (gg_event–>1 == gg_mainwin) {
a_buffer–>0 = gg_event–>2;
done = true;
}
}
ix = HandleGlkEvent(gg_event, 0, a_buffer);
if (ix == 2) done = true;
else if (ix == -1) done = false;
}
if (gg_commandstr ~= 0 && gg_command_reading == false) {
glk_put_buffer_stream(gg_commandstr, a_buffer+WORDSIZE, a_buffer–>0);
glk_put_char_stream(gg_commandstr, 10); ! newline
}
.KPContinue;
VM_Tokenise(a_buffer,a_table);
! It’s time to close any quote window we’ve got going.
if (gg_quotewin) {
glk_window_close(gg_quotewin, 0);
gg_quotewin = 0;
}
#ifdef ECHO_COMMANDS;
print "** ";
for (ix=WORDSIZE: ix<(a_buffer–>0)+WORDSIZE: ix++) print (char) a_buffer->ix;
print “^”;
#endif; ! ECHO_COMMANDS
];

-) instead of “Keyboard Input” in “Glulx.i6t”

Include (- Global stored_buffer = 0 ; -) after “Definitions.i6t”[/code][/spoiler]

To really make this work nicely, a few more features would be nice: allow for the repetition of the disambiguation question (could be done by printing it to a buffer, or using the Text Capture extension) and for the use of custom prompts for yes/no questions so that it will be obvious to the player that the game is still awaiting special input. It would probably also be good if we could distinguish line input from char input, to treat things differently in the timer event rulebook. This would probably require setting more flags in the I6 code.

–Erik