Ren'Py Locking the mouse to a small area once it enters that area?

Atemsiel

Developer of Stormside
Game Developer
Jan 4, 2022
739
2,156
Hello Ren'Py nerds,

I'm using the below code from the lemmasoft forums, for a flashlight effect.
Code:
init python:

    class Flashlight(renpy.Displayable):
        def __init__(self):
            super(Flashlight, self).__init__()

            # This image should be twice the width and twice the height
            # of the screen.
            self.child = Image("GameImages/GhostHunting1/FlashlightMask.png")

            # (-1, -1) is the way the event system represents
            # "outside the game window".
            self.pos = (-1, -1)


        def render(self, width, height, st, at):
            render = renpy.Render(config.screen_width, config.screen_height)

            if self.pos == (-1, -1):
                # If we don't know where the cursor is, render pure black.
                render.canvas().rect("#000", (0, 0, config.screen_width, config.screen_height))
                return render

            # Render the flashlight image.
            child_render = renpy.render(self.child, width, height, st, at)
            # Draw the image centered on the cursor.
            flashlight_width, flashlight_height = child_render.get_size()
            x, y = self.pos
            x -= flashlight_width / 2
            y -= flashlight_height / 2
            render.blit(child_render, (x, y))
            return render

        def event(self, ev, x, y, st):
            # Re-render if the position changed.
            if self.pos != (x, y):
                renpy.redraw(self, 0)
            # Update stored position
            self.pos = (x, y)

        def visit(self):
            return [ self.child ]
I've got an event that gets triggered by a mousearea screen when the player moves the flashlight to a certain place. Once this event starts, it triggers a variable called something like "EVENTPLAYING", and once the event is over, the screen that shows the flashlight effect is hidden. Once the event is triggered, I want to lock the mouse to a small area of the screen, so the player can't move the flashlight such that the event can't be seen. Does anyone have a way of doing this?

Many thanks.
 

MidnightArrow

Active Member
Aug 22, 2021
500
451
I guess in theory you can do it by setting the mouse position on every frame, but this isn't really something Ren'py is meant for. Ren'py is a glorified website designer, it's made to display mostly-static screens defined with CSS users can click on. It's not the kind of thing where you should be capturing the mouse from them. You need a proper game engine for that.

Also you can actually make calls into SDL, which Ren'py/PyGame runs on and which has the ability to capture the mouse.



However this is bound to be extremely hacky and I would not recommend it.
 
Last edited:

ttrreess8

Newbie
Dec 30, 2019
35
19
I don't code in Ren'py but I asked the AI and it spit this out:

Me: Can you write code that will lock a mouse cursor in a certain area on the screen once it enters a specified location in Ren'py programming language?

AI:
Yes, it is possible to lock a mouse cursor in a specific area on the screen in Ren'py. Here is an example of how you could do it:

Code:
# Define the area in which the mouse cursor should be locked
left_boundary = 100
right_boundary = 200
top_boundary = 100
bottom_boundary = 200

# Store the current position of the mouse cursor
current_x = 0
current_y = 0

# Create a function that will be called every time the mouse is moved
# This function will update the current position of the mouse cursor and
# lock it within the specified boundaries if it moves outside of them
def on_mouse_move(x, y):
    global current_x, current_y

    # Update the current position of the mouse cursor
    current_x = x
    current_y = y

    # If the mouse cursor moves outside of the specified boundaries,
    # move it back to the nearest boundary
    if current_x < left_boundary:
        current_x = left_boundary
    elif current_x > right_boundary:
        current_x = right_boundary
    if current_y < top_boundary:
        current_y = top_boundary
    elif current_y > bottom_boundary:
        current_y = bottom_boundary

    # Set the position of the mouse cursor to the updated coordinates
    renpy.cursor.set_position(current_x, current_y)

# Register the on_mouse_move function to be called every time the mouse is moved
renpy.register_event_handler("mouse_move", on_mouse_move)
I'm not sure what the accuracy is on that thing, last I heard it was above 50%?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
Once the event is triggered, I want to lock the mouse to a small area of the screen, so the player can't move the flashlight such that the event can't be seen.
Why lock the mouse, when you can simply make the custom displayable stop following it ?

Python:
       def event(self, ev, x, y, st):
            #  If it shouldn't follow the mouse, do nothing.
            if store.dontFollowMouse:
                return

            if self.pos != (x, y):
                renpy.redraw(self, 0)
            self.pos = (x, y)
Now you just need include "dontFollowMouse":
Python:
default dontFollowMouse = False

label theEvent:
    [...]
    $ dontFollowMouse = True
    [...]
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,713
2,716
Why lock the mouse, when you can simply make the custom displayable stop following it ?

Python:
       def event(self, ev, x, y, st):
            #  If it shouldn't follow the mouse, do nothing.
            if store.dontFollowMouse:
                return

            if self.pos != (x, y):
                renpy.redraw(self, 0)
            self.pos = (x, y)
Now you just need include "dontFollowMouse":
Python:
default dontFollowMouse = False

label theEvent:
    [...]
    $ dontFollowMouse = True
    [...]

I helped him a little bit with this - I did something similar to accomplish the same effect but in a different way - but the way he explained it to me originally, The "stop moving the displayable" was, I guess you could say, his backup plan.

What he really wants, according to my understanding, is effectively this: You can move it anywhere on the screen, until you reach a specific area (I was triggering the variable toggle via a hovered action, which seems to line up with what he wants) but once you reach that area, you can still move it, only within a smaller bounds.

So he still wants to be able to move the "flashlight", but have it restricted to a smaller area of the screen, once you've initially illuminated that area.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
So he still wants to be able to move the "flashlight", but have it restricted to a smaller area of the screen, once you've initially illuminated that area.

Then it's the same, but just a bit differently:


/!\ I wrote it on the fly, there's perhaps typos /!\
Python:
defaulf restrictedMouse = False

init python:

   class Flashlight(renpy.Displayable):

        def __init__(self, left, right, down, up):
            [...]

            # Limits for the zone.
            self.zoneLeft = left
            self.zoneRight = right
            self.zoneDown = down
            self.zoneUp = up
     

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

            #  If the movement are now restricted:
            if store.restrictedMouse:

                # By default, the mouse position don't need to be updated.
                changed = False

                #  If the mouse leave the zone by the left side
                if x < self.zoneLeft:
                    # Move the mouse back to the left side.
                    x = self.zoneLeft
                    # The position of the mouse need to be updated.
                    changed = True
                # Else, look if it leaved the zone by the right side.
                elif self.zoneRight < x:
                    x = self.zoneRight
                    changed = True

                #  Do the same, but vertically this time
                if y < self.zoneUp:
                    y = self.zoneUp
                    changed = True
                elif self.zoneDown < y:
                    y = self.zoneDown
                    changed = True

                #  If the mouse need to be moved back in the zone
                if changed:
                    # just do it.
                    renpy.set_mouse_pos( x, y )

            #  Then proceed as usual.

            if self.pos != (x, y):
                renpy.redraw(self, 0)
            self.pos = (x, y)
Unless the mouse is configured to follow really big steps, this should works smoothly.