Allow dialogue in a screen without using show screen?

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.
Message
Author
jepp21
Regular
Posts: 74
Joined: Fri Feb 10, 2023 3:46 pm
Contact:

Allow dialogue in a screen without using show screen?

#1 Post by jepp21 »

Hi all, I've been working on a sandwich making minigame where the player could make customer order for a while. Aside from stuff like transitions, the first draft is almost done. I just have one major aspect left. I'd like for it to be possible for the customer to talk while playing this minigame. Normally Id just use show screen, but the system returns to labels a lot at specified situations to ensure that the screen and ingredients behave as they should, so it seems like I have to find an alternative.

I'll start with some context:

When the customer makes an order, the script calls a label that sets ingredient parameters.

Code: Select all

label day1:
    r "I would like a sandwich with bread_roll and bacon. toasted."
    $ reset_order_vars()

    #sets customer order to one of the orders from the menu (aka one of the limited amount of defined sandwich objects)
    $ order_full = test_order

    #sets part of the object to another variable with just the list of needed ingredients. 
    #is checked when the complete sandwich is being prepped
    #also checked when counting amount of mistakes
    $ order = test_order.order

    #whether or not the customer wants the order toasted
    $ customer_toasted = True

    #timers for various moods.
    $ set_counts(10.0, 10.0, 10.0, 10.0)

    #shows how much money the player has
    $ currency_display = True
    
    call sandwich_minigame
   
    ################# ####################### this code runs after player finishes the minigame
    if sandwich_mistakes == 0:
        jump day1_order1_correct

    elif sandwich_mistakes < 3:
        jump day1_order1_wrong

    else: 
        jump day1_order1_very_wrong

    label day1_order1_correct:
        r "correct"

        #give mc his extra money if he gets order right
        $ shift_commission = shift_commission + order_full.commission
        $ shift_total_tips = shift_total_tips + 100

        jump day1_order1_done

    label day1_order1_wrong:
        r "wrong"
        $ shift_mistakes = shift_mistakes + sandwich_mistakes
        jump day1_order_done

    label day1_order1_very_wrong:
        r "very wrong"
        $ shift_mistakes = shift_mistakes + sandwich_mistakes
        jump day1_order_done

    label day1_order1_done:
        $ reset_order_vars()
        "Customer 1 done"
After ingredients parameters are set, this label jumps to a third label that calls the screen itself and also handles a lot of the logic with making sure the ingredients behave the way they should while using the drag and drop system.

Code: Select all

## Parameters for each draggable. Go here before going to deli screens. 
label sandwich_minigame: #label used so the variables can be reset by running the initial set variables. screen returns here to reset stuff
    # Setting drag variables.
    python:
        
        #ingredients of a certain type should all have a common tag. ex: bread_wedge, bread_roll, etc. needed for the filter to work
        #list of dictionaries
        drag_param = [

                ## Breads
                {
                    "name": "bread_roll",
                    "child": "images/deli/bar/breads/bread_roll.png",
                    "hover_child": "images/deli/bar/meats/meat_bacon/meat_bacon_hover.png",
                    "selected_hover_child": "images/deli/bar/meats/meat_bacon/meat_bacon_dragging.png",
                    "x": 550,
                    "y": 200,
                    "draggable": True,
                    "anim": None
                },
                ....
     #after setting parameters, go to a label that calls the deli screen and also controls other parts of screen flow
    call .minigame
    return 

Code: Select all

## Controls deli screen flow. deli screens return here whenever an ingredient is dragged to stop placement anims from repeating 
#also returns here after serving to calculate number of mistakes player made
#also returns when resetting sandwich mini-game
label .minigame:

    call screen sandwich_interface with in_25
   
    #this code ensures that ingredient placement animations do not play again when returning to screen
    python:
        ...
        
   if _return == "meat_before_bread":
        show screen sandwich_interface
        "I can't put meat before bread{w=10.0}{nw}"
        jump .minigame #go back to beginning of flow
   
    elif not _return:
        jump .minigame #go back up to beginning of label
    
  
    #checks for mistakes
    python:   
        ...
     
    return ##when the player is done with the minigame, this returns to the first label where the character reacts depending on the amount of mistakes made
I don't think I can change it to call screen because it would mess with all the returns that take place. The screen returns every time an ingredient is placed so that the parameters in the second label can be up to date. It would also cause an infinite loop in the third label.

One thing I tried was making labels for the conversations themselves and calling those labels when the screen is loaded:

Code: Select all

screen sandwich_interface():

    zorder -2 #make sure it's on top of customer and under hud
    modal False

    # aynchronous customer dialogue using interpolation 
    if fresh_prep:
        Call("shift_[current_shift]_sandwich_dialogue_[current_convo]")
.......
label shift_1_sandwich_dialogue_1:
    show screen sandwich_interface

    pause 5.0

    r "This is asynchronous dialogue{w=3.0}{nw}"

    hide screen sandwich_interface
    return
I got an error though because I can't use Call in the screen by itself like that. Im also not sure if this would solve the problem yet since the error is preventing me from seeing what happens

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#2 Post by jeffster »

I recall a game that had something similar. Player could move through different locations with auxiliary "screens" and continue the dialog independent from locations.

Sorry I can't get that game now to check the details and don't remember its name, but I recall there was a loop that called dialog lines. I'm not sure that the other screens were "called" though, maybe they were "shown".

But it's doable. You make a loop, there you show some screens (perhaps depending on some flags), "dispatching" calls to various labels depending on conditions.

PS. Or you can show dialog on your custom screen.

When player clicks in just say screen, an "interaction" happens which means "dismiss" (and move on to the next line). When you call a custom screen, there would be some other interaction, like "put sauce on a sandwich". If you combine dialog with cooking, you will need to accept those different interactions and dispatch results.

User avatar
_ticlock_
Miko-Class Veteran
Posts: 910
Joined: Mon Oct 26, 2020 5:41 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#3 Post by _ticlock_ »

jepp21 wrote: Wed Jun 07, 2023 1:47 pm Normally Id just use show screen, but the system returns to labels a lot at specified situations to ensure that the screen and ingredients behave as they should, so it seems like I have to find an alternative.
You can use non-interactive version of the screen for that (in case if you want to "freeze" the mini_game state during the dialogue):

Use a variable that would control if the screen sandwich_interface is interactive or not. Or create another non-interactive version of the screen sandwich_interface (basically disable all buttons, draggs, timers, etc). You can use this non-interactive version as background for the dialogue, so the player can still see the mini-game during the dialogue lines. Something like:

Code: Select all

label .minigame:
    call screen sandwich_interface with in_25
    ...
    if condition_if_dialogue_should_be_shown:
        show screen non_interactive_sandwich_interface
    	call some_dialogue_label(argument_to_control_what_dialogue_to_show)
    	hide screen non_interactive_sandwich_interface
You can also use renpy.call_in_new_context to call the label directly from the screen. (You can show non-interactive version of the screen inside the called label)

jepp21
Regular
Posts: 74
Joined: Fri Feb 10, 2023 3:46 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#4 Post by jepp21 »

_ticlock_ wrote: Wed Jun 07, 2023 4:29 pm
jepp21 wrote: Wed Jun 07, 2023 1:47 pm Normally Id just use show screen, but the system returns to labels a lot at specified situations to ensure that the screen and ingredients behave as they should, so it seems like I have to find an alternative.
You can use non-interactive version of the screen for that (in case if you want to "freeze" the mini_game state during the dialogue):

Use a variable that would control if the screen sandwich_interface is interactive or not. Or create another non-interactive version of the screen sandwich_interface (basically disable all buttons, draggs, timers, etc). You can use this non-interactive version as background for the dialogue, so the player can still see the mini-game during the dialogue lines. Something like:

Code: Select all

label .minigame:
    call screen sandwich_interface with in_25
    ...
    if condition_if_dialogue_should_be_shown:
        show screen non_interactive_sandwich_interface
    	call some_dialogue_label(argument_to_control_what_dialogue_to_show)
    	hide screen non_interactive_sandwich_interface
You can also use renpy.call_in_new_context to call the label directly from the screen. (You can show non-interactive version of the screen inside the called label)
What if I want the sandwich screen to remain interactive while using the dialogue? Im able to do this when triggered with a return

Ex:

Code: Select all

label .minigame:
	....
	if _return == "meat_before_bread":
        	show screen sandwich_interface
        	"I can't put meat before bread{w=10.0}{nw}"
        	jump .minigame 
        ...
Mainly I just want a way to do it without having to use triggers which is why I was just thinking of doing the call I mentioned earlier with the interpolation to use the correct label. I just tried using renpy.call_in_new_context and I got this error on my label

Code: Select all

I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/screens/deli/deli_labels.rpy", line 821, in script
    r "This is asynchronous dialogue{w=3.0}{nw}"
Exception: ui.interact called with non-empty widget/layer stack. Did you forget a ui.close() somewhere?
Stack was <Layer: 'transient'>
<Many: <renpy.display.layout.Fixed object at 0x0000000019e6dfa0>>
This was the label

Code: Select all

label shift_1_sandwich_dialogue_1:
    show screen deli_buttonsEEE

    pause 5.0

    r "This is asynchronous dialogue{w=3.0}{nw}"

    hide screen deli_buttonsEEE
    return

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#5 Post by jeffster »

I understand you want both minigame and "say" dialog be interactive. So IMHO checking _return is the right way. You can check was that an interaction from a dismissed dialog line or from cooking. It doesn't look like you could or should use "new context" in that case.

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#6 Post by jeffster »

Here is an example how to combine say screen and some other screen that interacts with player and returns values.

1. You add "use cooking" to say screen:

Code: Select all

screen say(who, what):
    use cooking
    style_prefix "say" # and so on
2. You add screen cooking and a function to control "say" screen interactivity:
https://www.renpy.org/doc/html/config.h ... ow_dismiss

Code: Select all

init python:
    act = None

    def say_n_cook():
        if not act:
            return True
        return False

define config.say_allow_dismiss = say_n_cook
default act = None

screen cooking():
    dismiss action [SetVariable("act", None), Return()]
    frame:
        modal True
        align (0.5, 0.0)
        vbox:
            textbutton "Interact 1":
                action [
                    SetVariable("act", 1),
                    Function(renpy.notify, "Act 1")
                    ]
                text_hover_color "#F00"
            textbutton "Interact 2":
                action [
                    SetVariable("act", 2),
                    Function(renpy.notify, "Act 2")
                    ]
                text_hover_color "#F00"
            textbutton "Interact 3":
                action [
                    SetVariable("act", 3),
                    Function(renpy.notify, "Act 3")
                    ]
                text_hover_color "#F00"


label start:
    "1st say"
    $ renpy.notify(act)
    "2nd say"
    $ renpy.notify(act)
    "3rd say"
    $ renpy.notify(act)
    jump start
This is a working example of script.rpy, you can test it.

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#7 Post by jeffster »

PS. If you actually return from such combined screen, e.g. by

Code: Select all

            textbutton "Interact 3":
                action [
                    SetVariable("act", 3),
                    Function(renpy.notify, "Act 3"),
                    Return(act)
                    ]
then the interaction of the "say" screen gets finished, and the dialog moves on. If you want to circumvent that, it might require some tricks but it's possible.

User avatar
_ticlock_
Miko-Class Veteran
Posts: 910
Joined: Mon Oct 26, 2020 5:41 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#8 Post by _ticlock_ »

jepp21 wrote: Thu Jun 08, 2023 1:29 pm What if I want the sandwich screen to remain interactive while using the dialogue? Im able to do this when triggered with a return
Do you want both sandwich_interface and say screen interactive? I am not sure I understand how you want them to work together.

You can try something like jeffster suggested, but I think it may require "some" work (or re-work) for a minor feature.

Possibly, it's easier to create a queue of dialogue statements that should be shown with the sandwich_interface:

Code: Select all

default dialogue_queue = []

Code: Select all

label .minigame:
	....
	if _return == "meat_before_bread":
		dialogue_queue.append([narrator, "I can't put meat before bread, 10.0"])
        	jump .minigame 
        ...
Show the dialogue line before you call the sandwich_interface:

Code: Select all

label .minigame:
        if dialogue_queue:
            $ renpy.say(dialogue_queue[0][0], dialogue_queue[0][1], interact=False)
        call screen sandwich_interface with in_25
Additional check (possibly with timer) in the sandwich_interface to remove dialogue line from the dialogue_queue (Like using dialogue_queue[0][2] for timer). Also, you can add a button to remove the dialogue line from the dialogue_queue before the timer (like clicking to continue to the next line)

PS: You might want to disable history in this case.

jepp21
Regular
Posts: 74
Joined: Fri Feb 10, 2023 3:46 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#9 Post by jepp21 »

jeffster wrote: Thu Jun 08, 2023 7:14 pm PS. If you actually return from such combined screen, e.g. by

Code: Select all

            textbutton "Interact 3":
                action [
                    SetVariable("act", 3),
                    Function(renpy.notify, "Act 3"),
                    Return(act)
                    ]
then the interaction of the "say" screen gets finished, and the dialog moves on. If you want to circumvent that, it might require some tricks but it's possible.
This seems good. Since it's just a mini-game, I don't intend on using the sandwich_interface (or cooking in your example) screen for the entire game. It's just that when I do I'd like there to be dialogue. When I'm not using the mini-game, would I have to use an alternative say screen that doesn't use cooking?

jepp21
Regular
Posts: 74
Joined: Fri Feb 10, 2023 3:46 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#10 Post by jepp21 »

_ticlock_ wrote: Thu Jun 08, 2023 11:22 pm
jepp21 wrote: Thu Jun 08, 2023 1:29 pm What if I want the sandwich screen to remain interactive while using the dialogue? Im able to do this when triggered with a return
Do you want both sandwich_interface and say screen interactive? I am not sure I understand how you want them to work together.

You can try something like jeffster suggested, but I think it may require "some" work (or re-work) for a minor feature.

Possibly, it's easier to create a queue of dialogue statements that should be shown with the sandwich_interface:

Code: Select all

default dialogue_queue = []

Code: Select all

label .minigame:
	....
	if _return == "meat_before_bread":
		dialogue_queue.append([narrator, "I can't put meat before bread, 10.0"])
        	jump .minigame 
        ...
Show the dialogue line before you call the sandwich_interface:

Code: Select all

label .minigame:
        if dialogue_queue:
            $ renpy.say(dialogue_queue[0][0], dialogue_queue[0][1], interact=False)
        call screen sandwich_interface with in_25
Additional check (possibly with timer) in the sandwich_interface to remove dialogue line from the dialogue_queue (Like using dialogue_queue[0][2] for timer). Also, you can add a button to remove the dialogue line from the dialogue_queue before the timer (like clicking to continue to the next line)

PS: You might want to disable history in this case.
To answer your question, yes, I want the dialogue to be fully intractable. Essentially, I want the player to be able to make sandwiches and speak to customers at the same time. The dialogue queue might be in the right direction, but what if I want to change the customer's expressions to match the dialogue or use transforms? That wouldn't work with the queue right? I figured using a label would be necessary but maybe im mistaken

jepp21
Regular
Posts: 74
Joined: Fri Feb 10, 2023 3:46 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#11 Post by jepp21 »

Been playing around with the code and I was wondering if it may be more simple to re-work the flow of the system so that it could all work with show screen instead of trying to fight the engine? Before trying that out, I tried out the return system from earlier to see if there were any problems.

Code: Select all

label .minigame:
	....
	if _return == "meat_before_bread":
        	show screen sandwich_interface
        	"I can't put meat before bread{w=10.0}{nw}"
        	jump .minigame 
        ...
One problem I found is that the draggable ingredients behave weirdly when I try to drop them when the dialogue is up. Upon placing a drag, a copy is supposed to be made and added to the parameter list so that the player does not run out of ingredients. When the dialogue box is up, the copy is made, BUT it spawns right where I dropped the original drag instead of where the ingredient should be. This is only a problem when the dialogue box is active. Maybe I should make the dialogue non-interactive and have it advance automatically without accepting player inputs? Im not sure how to do that

User avatar
_ticlock_
Miko-Class Veteran
Posts: 910
Joined: Mon Oct 26, 2020 5:41 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#12 Post by _ticlock_ »

jepp21 wrote: Sat Jun 10, 2023 8:40 pm The dialogue queue might be in the right direction, but what if I want to change the customer's expressions to match the dialogue or use transforms? That wouldn't work with the queue right? I figured using a label would be necessary but maybe im mistaken
You can do it using dialogue queue approach. Although, you’ll need more complicated logic for that. You’d need to track time for each dialogue line to apply correctly transforms and other things. Or you can implement it similarly to notifications but with dialogues (queue would track how many notifications are left).
jepp21 wrote: Sat Jun 10, 2023 8:40 pm To answer your question, yes, I want the dialogue to be fully intractable. Essentially, I want the player to be able to make sandwiches and speak to customers at the same time.
I don’t really see an easy way to do it, because both the dialogue and the minigame logic requires interactions that interfere with each other (the new dialogue line can interrupt dragging, the minigame event can interrupt the the dialogue flow)

To avoid it one of them should not interfere with the other. Some options:
1) Using dialogue queue approach as suggested above. Possibly, process dialogue queue in a separate screen, that is shown all the time during the minigame. This approach requires building logic for dialogue queue and displaying it correctly.

2) Reworking minigame, that it would not interrupt the dialogue flow. In this case, only show screen can be used for the minigame, rewrite the minigame logic to avoid ending interaction or other things that can effect the dialogues.

It’s hard to say what is easier to do. The first approach, seems more straightforward.
jepp21 wrote: Sun Jun 11, 2023 12:08 am Maybe I should make the dialogue non-interactive and have it advance automatically without accepting player inputs? Im not sure how to do that
To avoid sudden interruption of the dialogue lines, I’d suggest show the dialogues like notifications. But you still need some queue to display the dialogue line one by one.

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#13 Post by jeffster »

Yes, it is possible to use more than one version of "say" screen, or just have some flag like

Code: Select all

screen say(who, what):
    if miniGame:
        use cooking
    # Or just show normal dialog:
    # ...
But the easiest way indeed seems to be with show screen's, like you suggested:
jepp21 wrote: Sun Jun 11, 2023 12:08 am Been playing around with the code and I was wondering if it may be more simple to re-work the flow of the system so that it could all work with show screen instead of trying to fight the engine?
As you use callback functions from dragged, you could process those interactions and direct control flow as necessary (hide screens, jump etc.)
jepp21 wrote: Sun Jun 11, 2023 12:08 am Before trying that out, I tried out the return system from earlier to see if there were any problems.

Code: Select all

label .minigame:
	....
	if _return == "meat_before_bread":
        	show screen sandwich_interface
        	"I can't put meat before bread{w=10.0}{nw}"
        	jump .minigame 
        ...
One problem I found is that the draggable ingredients behave weirdly when I try to drop them when the dialogue is up. Upon placing a drag, a copy is supposed to be made and added to the parameter list so that the player does not run out of ingredients. When the dialogue box is up, the copy is made, BUT it spawns right where I dropped the original drag instead of where the ingredient should be.
Maybe it's easier to not make a copy but just set x,y of that drag to the original position. The ingredient dropped on the sandwich would be processed with another animation or something, so the original drag wouldn't be needed there.

jepp21
Regular
Posts: 74
Joined: Fri Feb 10, 2023 3:46 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#14 Post by jepp21 »

jeffster wrote: Mon Jun 12, 2023 9:31 am Yes, it is possible to use more than one version of "say" screen, or just have some flag like

Code: Select all

screen say(who, what):
    if miniGame:
        use cooking
    # Or just show normal dialog:
    # ...
But the easiest way indeed seems to be with show screen's, like you suggested:
jepp21 wrote: Sun Jun 11, 2023 12:08 am Been playing around with the code and I was wondering if it may be more simple to re-work the flow of the system so that it could all work with show screen instead of trying to fight the engine?
As you use callback functions from dragged, you could process those interactions and direct control flow as necessary (hide screens, jump etc.)
jepp21 wrote: Sun Jun 11, 2023 12:08 am Before trying that out, I tried out the return system from earlier to see if there were any problems.

Code: Select all

label .minigame:
	....
	if _return == "meat_before_bread":
        	show screen sandwich_interface
        	"I can't put meat before bread{w=10.0}{nw}"
        	jump .minigame 
        ...
One problem I found is that the draggable ingredients behave weirdly when I try to drop them when the dialogue is up. Upon placing a drag, a copy is supposed to be made and added to the parameter list so that the player does not run out of ingredients. When the dialogue box is up, the copy is made, BUT it spawns right where I dropped the original drag instead of where the ingredient should be.
Maybe it's easier to not make a copy but just set x,y of that drag to the original position. The ingredient dropped on the sandwich would be processed with another animation or something, so the original drag wouldn't be needed there.
I think for now ill try getting it to work with show since it seems like the most straight forward and familiar method to me. One thing I noticed was that sometimes when I want to drag something, the game instead moves on to the next line of dialogue instead of clicking on the drag. I've opted to make the say screen non-interactive when a variable is set to True. The aim is to enable this variable whenever I'm using the mini-game.

Code: Select all

screen say(who, what):
    style_prefix "say"

    #make dialogue non-interactible if variable block_inputs is true
    if block_inputs:
        dismiss action NullAction()
     .... #rest of say screen

For dialogue, I was planning on using tags so the screen could dismiss itself without player input

Code: Select all

sb "I can't put meat before bread{w=2.0}{nw}"
I thought this would be fine but there were some unexpected issues. Starting off, if I use cps to make each character appear one by one, clicking still makes it so the full dialogue appears even though I expected this to be ignored. Also, the dialogue box never disappears even after waiting or clicking. The dialogue box stays on screen and I cannot interact with the minigame either. I can still use the quick menu, pause, etc. I tried testing it without any screens and the I still had the same issues.
Is there something wrong with my dismiss action? I cant tell if I did something wrong

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Allow dialogue in a screen without using show screen?

#15 Post by jeffster »

jepp21 wrote: Wed Jun 14, 2023 10:12 pm One thing I noticed was that sometimes when I want to drag something, the game instead moves on to the next line of dialogue instead of clicking on the drag.
In order to prevent that effect we can try to disable key "dismiss" action.

Screen "say" has by default something like

Code: Select all

screen say(who, what):
    key "dismiss" action Return()
Try this:

Code: Select all

screen say(who, what):
    key "dismiss" action None
Regarding dismiss as displayable, it is to process clicks outside interactable elements:
https://renpy.org/doc/html/screens.html#dismiss

I imagine that you could make a modal screen (or modal frame in "say" screen) that would cover all the minigame area or even the whole screen, and prevent dismissing the dialog.
https://renpy.org/doc/html/style_proper ... erty-modal

The dialog area could be covered by a transparent button (with focus_mask None)
https://renpy.org/doc/html/style_proper ... focus_mask
to dismiss the dialog:

Code: Select all

    button:
        pos (0, 900)           # or something
        xysize (1920, 180) # or something
        focus_mask None
        background None
        keysym [ 'K_RETURN', 'K_SPACE', 'K_KP_ENTER', 'K_SELECT' ]
        action Return()
"keysym" would allow dismissing dialog with those keys.
https://renpy.org/doc/html/keymap.html

Post Reply

Who is online

Users browsing this forum: No registered users