Question about renpy.input

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
Mark Thern
Newbie
Posts: 11
Joined: Sat Mar 18, 2006 5:19 am
Location: South Carolina, USA
Contact:

Question about renpy.input

#1 Post by Mark Thern »

Hi, this is my official de-lurking post. I came across the existence of Ren'py in January, and I can't tell you how happy I was to discover a tool for creating interactive stories that was designed for people who like to write rather than people who like to program.

Here's my problem: I'm trying to create a game that will allow the player to have relatively complex conversations with the other characters. To achieve this end, I've opted for a verb and keyword system of input, where entering "Ask Detective Snorkleby about murder weapon" or "Tell Sarah about myself" pops up a menu of dialogue options. My approach differs from a traditional text adventure in that the list of possible keywords is always displayed to the player.

I could (and will) implement this through text buttons and dynamic menus, but I think the ideal interface would be an input prompt with auto-complete and I'd like to offer this option as well. This is how I envision doing it:

1. Prompt appears.
2. Player types a letter
3. The typed letter is automatically assigned to a variable ("text_input", let's say) without the necessity of hitting enter.
4. The value of text_input is sent to the auto-completion plant for further processing.

It's number 3 that's proving to be the sticking point. After many hours of experimentation and poking around in Ren'py's innards, I finally came across the definition for renpy.input in behavior.py. Somewhere in here is the solution to my problem (the "self.content += ev.unicode" line looks pretty likely), but I can't figure out how to make it work.

Any thoughts? Am I even close?

shaja
Regular
Posts: 73
Joined: Sun Oct 16, 2005 8:34 am
Contact:

#2 Post by shaja »

The event method in Input is probably the right place for what you're describing. At the point where the "elif ev.type == KEYDOWN and ev.unicode:" block is processed, ev.unicode contains the character just entered, and self.content contains the previously entered text.

It may be tough to actually do anything with that outside the confines of the input control though, since no other renpy script statements are processed until the input control returns its contents (after hitting enter).

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#3 Post by PyTom »

Honestly, I think a better approach would be to go with a multiple level menu system, especially because Ren'Py doesn't have the support for parsing sentences found in some of the dedicated text adventure languages. Even if you were able to get a sentence in, how would you deal with it?

Anyway, not really my problem.

The problem with using Input to do this is that input doesn't restart an interaction when a key is pressed. You'd probably want the to restart the interaction, so more information can be placed on the screen.

To do this, we'd want a new widget, let's call it KeyInterceptor. This widget would take keypresses and call a callback you supply. Your callback can then restart the interaction, or do whatever else you want. You'd have to implement the logic of Input yourself, but that's not too difficult.

Unfortunately, I have a paper due tonight, so can't really write this today. But if you're interested, I can create KeyInterceptor tomorrow, if and when I wake up.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

Mark Thern
Newbie
Posts: 11
Joined: Sat Mar 18, 2006 5:19 am
Location: South Carolina, USA
Contact:

#4 Post by Mark Thern »

I've built a prototype that takes input and jumps to a matching label. It only works if the input is legal, but making a filtering system doesn't appear to be too difficult, especially with the allow and exclude parameters of the renpy.input function.

I initially thought that offering just a multiple level menu system would be enough, but I wrote a prototype in Tads 3 and I discovered a problem: I have too many keywords to display in a single menu. I currently have about 40 permanent keywords and I'll probably end up with a total of 80+. I tried a number of categorization schemes, but I found that the number of clicks could get as high as 5. I could lower it to 4 if I omit the confirmation phase, ie the user clicks enter, but I'm still not very happy with that. I don't know for sure yet, but I get the feeling that an auto-complete system that filters out illegal text might be more efficient.

My other reason for implementing an auto-complete system is that I plan to commercialize my game. Since it will be a text-based story, I figure my core market will be people who like to play interactive fiction, even if my game does emphasize relationships and branching plots over setting and puzzles. Players of i-f have a marked preference for command line interfaces, so it makes sense to offer them this choice. I may even put in a plain text parser for purists and fast typists.

The fact that I'm using Ren'py to make a commercial product does raise some concerns in me about asking for help. I plan to make my libraries available for public use and I will release a demo that contains a third of what will be a very long game, so I will be making a contribution to the community. With that being the case, I don't feel guilty about asking about trivial programming issues. But if I'm asking you to do something time-consuming, then I think it's only right and proper that you be paid for your work. There are a number of features that I probably can't implement myself, so I'm certain that money will exchange hands at some point in time.

So as far as writing a KeyInterceptor widget goes, that's up to you. If you feel that it's a significant amount of work on your part, then I'd appreciate some advice about to create my own custom keybindings, which is a more clunky approach to this problem. If it turns out that I can't get that approach to work either, then we'll discuss money :)

(Incidentally, programming in Tads 3 was a nightmare. Did I mention how much I appreciate the way Ren'py combines flexibility with ease of use? I did? Well let me mention it again!)

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#5 Post by PyTom »

Mark Thern wrote:I initially thought that offering just a multiple level menu system would be enough, but I wrote a prototype in Tads 3 and I discovered a problem: I have too many keywords to display in a single menu. I currently have about 40 permanent keywords and I'll probably end up with a total of 80+.
What I'm curious about is how many of those keyworks correspond to interesting story-parts. One of the problems I percieve with traditional IF is that the number of interesting things you can do is relatively small relative to the number of things you can express. So what you have is the ability to say something like:

eat gun

And the system responds with a boilerplate and uninteresting message, like:

You don't think the gun tastes very good.

So the question is not how many keywords you have, it's how many interesting paths there are from one scene to another. Since you'd have to write each path, I would think that's somewhat limited.

Anyway, I will write TextIntercept as you ask. Ren'Py is open source, so you can make a commercial game with it. (There are some licensing issues with the libraries Ren'Py uses, but nothing overly onerous.) I do ask that if your game makes a profit, you consider contributing some of it back to Ren'Py development... I've spent over $1000 on Ren'Py and VN related expenses, and getting some of it back would help me to port Ren'Py to new platforms.

* Counting hosting the archives and this forum, but not counting buying commercial games. I hobby hard.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

Mark Thern
Newbie
Posts: 11
Joined: Sat Mar 18, 2006 5:19 am
Location: South Carolina, USA
Contact:

#6 Post by Mark Thern »

Gee, thanks. That's very generous of you. And I have no problems with contributing some of my (crossing my fingers and wishing on a star for good luck) profits into Ren'py development. It will take me a good 2-3 years before I have a product to market though, so in the meantime you should put off buying that yacht.

I (hopefully) solved the combinational explosion problem that plagues writers of i-f by having a very small number of verbs. My game is a story of political intrigue set in a fantasy world similar to medival Japan, and the core gameplay revolves around conversations, with occasional side-trips to the library or the dueling arena. You don't manipulate objects, you can't stroll northwest to the tea room, and informational tidbits are the only things in your inventory. Ask, Tell, Think, and Switch (for changing conversational partners) are your primary workhorse verbs. Your keywords are mostly people, places, and terms related to magic or history. Yes, there will be a lot of paths to write, and it will probably be harder than I anticipated. I figure that once I truly come to understand how much work will be involved, I'll end up calling my friends at 3 o'clock in the morning, drunk and crying. Oh well :)

I hate the "you can't do that" responses in traditional i-f myself and I don't want them in my story at all. Early on in my design, I planned on having the option of "ask person A about the relationship between person B and person C" available to the player, but I had to throw that idea out when I realized that only a small proportion of npcs had an important connection to each other and the rest of the pairs were just so much dead space to explore.

I'm curious about the other platforms that you'd like to support. Doesn't Linux, Windows, and the Mac cover 90+% of what's out there? Is it worth investing time and money to cover the other 10%?

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#7 Post by PyTom »

Mark wrote:I'm curious about the other platforms that you'd like to support. Doesn't Linux, Windows, and the Mac cover 90+% of what's out there? Is it worth investing time and money to cover the other 10%?
Traditional platforms, yes. But I don't see why Ren'Py couldn't be made to run on, say, a Nokia S60 series cell phone. (Like the N-Gage.) Or perhaps it could be made to run on a Nintendo DS, using the wifi boot mode.

I develop Ren'Py because it's fun, and I think one thing I want to do is to expand it to things that aren't your traditional computer, if possible.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#8 Post by PyTom »

Here's some code that uses KeyInterceptor (included) to do an autocompleting input window.

Code: Select all

init:
    python:
        
        import pygame

        # A displayable that intercepts keys, and calls a callback. If
        # the callback returns a non-None value, that value is
        # returned from ui.interact(). Otherwise, the keypress is
        # ignored.
        class KeyInterceptor(renpy.display.layout.Null):

            def __init__(self, callback):
                super(KeyInterceptor, self).__init__()
                self.callback = callback

            def event(self, ev, x, y, st):

                if ev.type == pygame.KEYDOWN:
                    rv = self.callback(ev)

                    if rv is None:
                        raise renpy.display.core.IgnoreEvent()
                    else:
                        return rv

                else:
                    return None
                    

init:

    python:


        # This is called in response to keypresses, and is
        # responsible for updating store.current_input in response to
        # keypresses. It then returns True to terminate the input
        # sequence, False to restart it, or None to indicate that the
        # keypress had no effect.        
        def my_input_callback(ev):

            if ev.key == pygame.K_BACKSPACE:                    
                if store.current_input:
                    store.current_input = store.current_input[:-1]
                    return False
                else:
                    return None

            if ev.key == pygame.K_RETURN:
                return True

            if not ev.unicode:
                return None

            if ev.unicode == '{':
                return None

            store.current_input = store.current_input + ev.unicode
            return False


        # Takes a string as argument, and returns the auto-completion
        # of that string.
        def my_complete(s):

            completions = [
                u'PyTom',
                u'Dick',
                u'Harry',
                u'Ozymandius',

                u'The grail',
                u'To get a good night\'s sleep.',
                ]


            potentials = [ ]


            for i in completions:
                if i.startswith(s):
                    potentials.append(i)

            if len(potentials) == 1:
                return potentials[0]
            else:
                return s
        

        # Inputs a string using the supplied prompt, prompting for
        # input as it goes.        
        def my_input(prompt):

            
            store.current_input = u''

            while True:

                current = store.current_input

                # Figure out the completion of the current text, if any.
                complete = my_complete(current)                
                complete_rest = complete[len(current):]

                # Put a cursor if the completion is empty.
                if not complete_rest:
                    complete_rest = '_'

                print complete_rest
                
                display = u'{color=#ffff00}' + current + u'{/color}{color=#00ffff}' + complete_rest + u'{/color}'

                
                # Display things to the user.
                ui.window()
                ui.vbox()
                ui.text(prompt)
                ui.text("")
                ui.text(display)
                ui.close()
                
                ui.add(KeyInterceptor(my_input_callback))

                # Interact.
                if ui.interact():
                    break

            return complete

        
label splashscreen:

    scene black

    $ name = my_input("What is your name?")

    $ quest = my_input("What is your quest?")

    "name = %(name)s, quest=%(quest)s"

    return

Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

Mark Thern
Newbie
Posts: 11
Joined: Sat Mar 18, 2006 5:19 am
Location: South Carolina, USA
Contact:

#9 Post by Mark Thern »

This makes my own attempts at making an auto-complete function look like kindergarten arithmetic. Thank you very much for your help.

I did have one question though. I experimented some with the code and tried replacing the "if ev.unicode == '{':" line with "if ev.unicode == 'P':".
Nothing happened. '{' was still excluded and 'P' was still allowed. Is this change being overridden by some other part of the Ren'Py library?

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#10 Post by PyTom »

Have you tried to delete the corresponding .rpyc file? It could be some sort of clock skew issue.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#11 Post by PyTom »

I should point out, BTW, that { was excluded for a reason. It introduces a text tag, and if present could mess the text widget up. You can fix it by quoting it everywhere it can appear, but I think it's usually easier to avoid it totally.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

Mark Thern
Newbie
Posts: 11
Joined: Sat Mar 18, 2006 5:19 am
Location: South Carolina, USA
Contact:

#12 Post by Mark Thern »

Yep, that did the trick. Now I can get to work on my text filter. (Rubs hands together in anticipation.)

Once I reach my first milestone, I'll show you exactly what I'm going to do with this. Look forward to it!

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#13 Post by PyTom »

If it happens again, you may want to check your system for clock skew.

What sort of timeline are you thinking about for the first milestone? Days? Weeks? Months? Years? Decades? Centuries? Millennia?
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

User avatar
PyTom
Ren'Py Creator
Posts: 16096
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

#14 Post by PyTom »

BTW, I noticed I left a debugging print statement in the code. So you may want to take it out, it's not used for anything anymore.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

Mark Thern
Newbie
Posts: 11
Joined: Sat Mar 18, 2006 5:19 am
Location: South Carolina, USA
Contact:

#15 Post by Mark Thern »

Actually, my problem was not caused by anything as exotic as a clock skew. It turns out I was editing the RPYC file instead of the RPY file. This also explains why the syntax highlighting wasn't working...

("End users", mutters PyTom as he shakes his head and rolls his eyes.)

Hopefully, I'll reach the first milestone in the next 6 weeks to 3 months. I'm using a knowledge and a personality system to make my npcs more interactive and lively, so it will take some time to see what works and what doesn't.

Post Reply

Who is online

Users browsing this forum: No registered users