Reading unlimited player input

Hello there,
I’m currently trying to put together a journal for the player to write entries into. My problem with that is that inform seems to truncate the “typed command” input after 20 words, which is a bit short for proper journal entries. Can anyone help me out with a way of saving longer texts that the player types?

Here’s the way I tried to do it:

carry out WriteANote:
say “[bold type]You open your journal and write:[roman type][line break][line break]”;
get typed command as playerinput;

1 Like

Doing this will involve some Inform 6 work, I think. The Inform 7 parser logic defines a limit on the length of the input buffer at 260 characters, which is probably the limit you’re running into.

The most effective way to do what you’re aiming for is to write a snippet of Inform 6 that calls glk_request_line_event with a custom (longer) buffer, then calls glk_select() until the user hits enter. That would do what you want, at the cost of needing some understanding of Glk and Inform 6. I don’t see an easier way, sorry.

Could you work around this by describing the journal as “tiny” - like it’s a tiny spiral-bound reporter-style notepad or a pocket-sized moleskine book? Make the word limit part of the story and not have to goof with the input limits?

1 Like

The limitation actually seems to be two-fold: There’s the 260 character limit you mentioned, which shouldn’t be that much of an issue in my book. I doubt anyone will write really exceptionally long stuff in the journal. The more limiting factor is that any input is truncated after 20 “words”.

Here’s two examples that show this:

Input: (500 characters)
11111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000
Saved String: (260 characters)
1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444445555555555666666

Input: “1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30”
Saved String: “1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20”

The first limit I don’t really mind, the second one can really cut you off in the middle of just the second sentence.

Well, I currently have got the input prompt set up like this:

You write down a short note: (20 words or less)

…which is funtional, but getting beyond the limit would be more immersive.

“You write down a short note, grumbling that the pages are so small and won’t hold more than 20 words each. You should remember to get a bigger notebook.”

3 Likes

Why not just encourage the player to write one line at a time and concatenate the lines into the entry? That seems a bit more natural of a way to input entries anyhow, rather than typing entire paragraphs into the command line.

Here’s some code to do that (the code for examining the journal is very bad–you probably don’t want to dump the entire journal every time you read it–but I figure you have a plan for that):

Study is a room.

The player carries a notebook. The notebook has a list of texts called journal entries. The description of the notebook is "You can write in this (say 'WRITE (your text)' and continue adding one line at a time."

The current journal entry is a text that varies. Currently writing is a truth state that varies. 

Writing is an action applying to one topic. Understand "write [text]" as writing. Understand "[text]" as writing when currently writing is true.

Stopping writing is an action applying to nothing. Understand "stop" or "stop writing" as stopping writing. 

Carry out stopping writing:
	finish a journal entry.
	
Report stopping writing:
	say "You finish the notebook entry and turn the page."
	
First before doing something other than writing or stopping writing when currently writing is true:
	say "(finishing the notebook entry and turning the page)[command clarification break]";
	finish a journal entry.
	
To finish a journal entry:
	add the current journal entry to the journal entries of the notebook;
	now the current journal entry is "";
	now currently writing is false.
	
Carry out writing:
	now currently writing is true;
	if the current journal entry is "":
		now the current journal entry is the substituted form of "[the topic understood]";
	otherwise:
		now the current journal entry is the substituted form of the "[the current journal entry] [the topic understood]".
		
Report writing:
	say "You write a line in the notebook. (You can continue writing by simply typing in the next line; to stop writing type STOP.)"
	
Instead of examining the notebook:
	say "The notebook reads:[line break]";
	repeat with page running through the journal entries of the notebook:
		say page;
		say line break.

The player can finish an entry either by using the stop writing command or by entering another valid command. Otherwise, they can keep adding to their entry just by typing new lines, without needing to say “write” again. (If the new line starts with a valid verb or direction like “Up the republic!” they’ll have to use “write” because Inform will try to interpret it as a command, but that’s probably for the best.)

Note the use of “the substituted form”–that evaluates the text substitutions and freezes them at their current value. If we wrote

now the current journal entry is "[the current journal entry] [the topic understood]"

without “the substituted form,” that would basically say “To figure out what the current journal entry is, figure out what the current journal entry is, and then add the topic understood to it,” which would lead to an infinite loop.

There are actually three limits on player input length:

Constant INPUT_BUFFER_LEN = 260;    ! No extra byte necessary
Constant MAX_BUFFER_WORDS = 20;
Constant PARSE_BUFFER_LEN = 61;

The second of these is only invoked in one place, in VM_Tokenise:

if (numwords >= MAX_BUFFER_WORDS) break;

So it’s a limitation of the tokenizer, not of the input-reading routine itself, which makes sense: all the input has to be read from the keyboard before the concept of “words” is relevant at all.

VM_Tokenise is called at the end of VM_ReadKeyboard, the actual input-fetching routine. So in theory it shouldn’t be too hard to make a copy of that routine that doesn’t call the tokenizer, just returns a buffer with the raw text data in it.

The difficulty then is getting it into a format you, the author, can use. The right way to do this would involve TEXT_TY_CastPrimitive, but I’ve never fully understood the block-value system. The easier way is what Eric Eve’s Text Capture extension does: define a routine that prints the buffer, call that routine inside a string ("[my buffer-printing routine]"), and let Inform handle the messy bits.

The end result would be a phrase like “request a raw line of text” that you could call within Inform 7, and then a phrase “say the/-- raw captured text” to access the results. Does that sound like what you’d want? If so I can poke at this later today, it shouldn’t take too long.

Oh also, I’m assuming you’re on Glulx. If you want to write for the ZMachine, it’ll be done differently. But also if you want to write for the ZMachine, you should probably use something like ZILF or I6 instead of I7.

Right, this is my attempt. This involves rather more Inform 6 magic than I had hoped, including a routine stolen from Zarf’s Unified Glulx Input, but still … if you enter “write” the prompt changes to a “>>” and you can input a line up to “LONG_BUFFER_LEN” (500 in the below) characters. The action “get a long line of input” does the hard work of requesting an input line, which can then be referred to as “the current long line of input”.

"Write" by David Kinder

The Office is a room.

Writing is an action applying to nothing. Understand "write" as writing.

Carry out writing:
	say "[bold type]You open your journal and write...[roman type][line break]>>";
	get a long line of input;
	say "[line break][bold type]You wrote:[roman type][the current long line of input][line break]".

To get a long line of input:
	(- GetLongInput(); -).

To decide what text is the/-- current long line of input:
	(- DecideLongInput({-new:text}) -).

Include (-

Constant LONG_BUFFER_LEN = 500;
Array long_buffer buffer LONG_BUFFER_LEN;

[ GetLongInput done;
	done = false;
	glk_request_line_event(gg_mainwin,long_buffer+WORDSIZE,LONG_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) {
				long_buffer-->0 = gg_event-->2;
				done = true;
			}
		}
	}
];

[ DecideLongInput txt;
	TEXT_CopyFromByteArray(txt,long_buffer+WORDSIZE,long_buffer-->0);
	return txt;
];

! Taken from Unified Glulx Input
[ TEXT_CopyFromByteArray txt buf len ubuf ix;
	TEXT_TY_Transmute(txt);
	ubuf = VM_AllocateMemory((len+1)*WORDSIZE);
	for (ix=0 : ix<len : ix++) {
		ubuf-->ix = buf->ix;
	}
	ubuf-->len = 0;
	BlkValueMassCopyFromArray(txt,ubuf,4,len+1);	
	VM_FreeMemory(ubuf);
];

-)
2 Likes

Thank you kindly for the effort in putting this together :slight_smile:
I’ll try it out right away.

Returning to this because I realized I overlooked something…

If you actually alter MAX_BUFFER_WORDS (which none of the examples here do, and you shouldn’t have to do), never set it above 99. Bad Things will happen if you do.

Seeing as the default is 20, that shouldn’t be a problem—and changing a few bits of the I6 templates can work around it anyway. But if that limit is set too high, it’ll make anything that accesses snippets fail in mysterious and hard-to-debug ways.