Hi, I'm thinking of making a small text adventure game with a parser, a little bit like Zork and games like that. So far, I found this post to make a parser with a button next to it. But I was wondering if it was possible to make a parser input without the button, like if you could press the return key in the field widget to check the words in it. I'm guessing that since the field widget already use the return key to go to the next line in the widget, I'd need to replace that action with the parser input, but I'm a bit clueless how to do that, if it's possible.
Hmm. I think this would be rather tricky.
Decker is designed to try to accommodate text input on devices that don't have a physical keyboard, so it avoids exposing low-level keyboard events and doesn't (presently) allow scripts to modify the contents of a text field while it has user focus. Consider how the soft keyboard overlay introduced in Decker 1.19 interacts with unlocked field widgets. While it's easy enough to detect that a user has pressed return in a text field (and typed a newline),
on change do if "\n" in me.text # ... end end
That event handler wouldn't be able to clear out the user's line of input, which would make a scripting REPL or an IF parser input awkward.
I'll give this some serious thought and see if I can come up with a good compromise to allow the sort of thing you're interested in making
Decker 1.49 includes some subtle changes that I think will address this request.
It is now possible to clear a field via scripts while it is selected. In touch mode, this will additionally dismiss the on-screen keyboard and remove focus from the field.
In the above example, the script looks like this:
on change do if "\n" in me.text log.text:log.text,"\n %s%J" format me.text,list eval[me.text].value log.scroll:99999 me.text:"" end end
There are a few details to be aware of when using this technique:
- It takes 1/4 of a second without user input for the contents of a field to fire a change[] event, and it is also possible to fire a change[] event by pasting an arbitrary block of text into a field. Therefore it's possible for a user to type several characters following a newline, or even multiple newlines; input handling code will need to be robust to this.
- An alternative to looking for "\n" in a change[] event would be to use the run[] event, which is fired immediately when the user presses shift+return. This may be less intuitive for users, and therefore unsuitable for many applications, but it's much less error-prone and more flexible than newline-delimited input; the run[] event makes it possible to build UIs which behave like Decker's Listener.
- Since the user actually does type a newline, you'll need to carefully adjust the vertical sizing of the input field to match the font in order to maintain the illusion that the field is "cleared" instantly when the user presses return.
I hope this helps!
This seems to work exactly how I wanted to, thank you! I see in the exemple that it evaluates what is written in the input and I've tried to modify the code so that if a specific word or phrase is written in the input, that it would say a specific answer in the log, but I don't quite understant the code enough to make something that works. And I was wondering if this technique would also works with triggering events, like to toggle other widgets in the card of for alerts?
OK, let's make a simple Colossal-Cave-Adventure-style two-word parser.
Lil's parsing patterns can get a little bit cryptic, but essentially what we want to break up user input is:
- Skip any leading spaces.
- Grab any characters up to a space or newline and call them "verb".
- Skip any spaces.
- Grab any characters up to a newline and call them "obj".
Which can be expressed as the pattern:
pat:"%*r %[verb]-.2r \n%*r %[obj]s\n"
I did a few tests in the Listener to make sure it was working as intended:
pat parse "foo" {"verb":"foo","obj":""} pat parse " foo" {"verb":"foo","obj":""} pat parse "foo bar" {"verb":"foo","obj":"bar"} pat parse "foo bar\nbaz" {"verb":"foo","obj":"bar"} pat parse "foo bar\nbaz" {"verb":"foo","obj":"bar"}
Now we can rework the input field's change[] handler to bundle this up and send the verb/object to the card script:
on change do if "\n" in me.text c:"%*r %[verb]-.2r \n%*r %[obj]s\n" parse me.text me.text:"" typed[c.verb c.obj] end end
In the card script, we'll have our actual game logic, a twisty maze of if statements:
on println x do log.text:log.text,x,"\n" log.scroll:999999 end on typed verb obj do println["> %s %s" format verb,obj] if verb~"look" if obj~"flask" println["it is a flask."] elseif obj~"self" println["that's difficult unless your eyes are prehensile."] else println["ye see ye flask."] end elseif verb~"take" if obj~"flask" println["ye cannot take ye flask."] else println["i don't see a %s anywhere here." format obj] end else println["ye is speakin' nonsense."] end end
And our scintillating gameplay experience begins: