Create your AI Cum Slut -70% for Mother's Day
x

Ren'Py Renpy Variables values: how to set the limit to a negative number?

xrknz

Newbie
Game Developer
Apr 28, 2024
20
42
Like title
With this code:

$ kil= 40
if kil > 101:
$ kil= 100
elif kil< -101:
$ kil= -100

when trigger
$ kil -= 200

the result is $ kil = -300

How to limit the value to -100 max?
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,639
2,306
One way to do it would be to use a custom function.
You'd need to call the function any time that the value changes.
Although you could write a function that changes the value for you and then limits things as part of that.

(bahh, bobdickgus beat me to it). :devilish:


First, the easy to read version:

Python:
init python:
    def clamp1(parm_n, minn, maxn):
        if parm_n < minn:
            return minn
        elif parm_n > maxn:
            return maxn
        else:
            return parm_n

default numvar1 = 0

label start:

    scene black with fade

    $ numvar1 = 150
    $ numvar1 = clamp1(numvar1, -100, 100)
    "Testing 01 : [numvar1]."

    $ numvar1 = -999
    $ numvar1 = clamp1(numvar1, -100, 100)
    "Testing 02 : [numvar1]."

    $ numvar1 = 75
    $ numvar1 = clamp1(numvar1, -100, 100)
    "Testing 03 : [numvar1]."

    return

Now, the better (but slightly less easy to follow) version:

Python:
init python:
    def clamp2(parm_n, minn, maxn):
        return max(min(maxn,parm_ n), minn)

default numvar1 = 0

label start:

    scene black with fade

    $ numvar1 = 150
    $ numvar1 = clamp2(numvar1, -100, 100)
    "Testing 01b : [numvar1]."

    $ numvar1 = -999
    $ numvar1 = clamp2(numvar1, -100, 100)
    "Testing 02b : [numvar1]."

    $ numvar1 = 75
    $ numvar1 = clamp2(numvar1, -100, 100)
    "Testing 03b : [numvar1]."

    return

Exactly the same results, but making better use of python inbuilt functions.


Next, an example that doesn't require you to type -100, 100 everywhere.
This example assumes that different variables (like love, corruption, hate, etc) will have different high/low limits. So each clamp has it's own function.

Python:
define love_maximum = 80
define love_minimum = -80

init python:
    def clamp_love(parm_n):
        return max(min(store.love_maximum, parm_n), store.love_minimum)

default numvar1 = 0

label start:

    scene black with fade

    $ numvar1 = 150

    $ numvar1 += 10
    $ numvar1 = clamp_love(numvar1)
    "Testing 01c : [numvar1]."

    $ numvar1 = -999

    $ numvar1 -= 10
    $ numvar1 = clamp_love(numvar1)
    "Testing 02c : [numvar1]."

    $ numvar1 = 75

    $ numvar1 = clamp_love(numvar1)
    "Testing 03c : [numvar1]."

    return

Just a note to explain that I'm using default to create variables where the value will probably change while the game is running and define to create variables that won't. Variables created with default are included in the save files, whereas variables created with define aren't. Which means if I wanted to change the upper limit to 120 instead of 80, I could (without someone loading a save file which still overwrote the value with 80).

Both sets of variables are held with a standard storage space called store. It's just how RenPy does things, that you need to know when it comes to creating your own functions.

Honestly, the static values for the upper and lower limits could just be hardcoded into the function without using define ..., but I'd regard it as better practice to do it this way.


Obviously, what you code would really look like would be more like:

Python:
    menu:
        "Pick something"
        "Nice thing":
            $ love_points +=1
            $ love_points = clamp_love(love_points)
        "Bad thing":
            $ love_points -=1
            $ love_points = clamp_love(love_points)

    "Testing 04 : [love_points]."

Even then, if it's one function per variable - you don't need to really pass the value to the function and can merge the value change and the limit checks together into one function...
Something like:

Python:
init python:
    def update_love_points(parm_n):
        store.love_points += parm_n
        store.love_points = max(min(25, store.love_points), -25)

default love_points = 0

label start:

    scene black with face

    menu:
        "Pick something"
        "Nice thing":
            $ update_love_points( +100 )

        "Bad thing":
            $ update_love_points( -100 )

    "Testing 04 : [love_points]."

    return

Whilst I don't like hardcoding values like -25 and +25, I figure I can at least leave the choice to you.

Just mix and match the parts of the examples you are comfortable with.

The original python solution on google was using a simple parameter (n). But I was worried about the possible problems in a game that has a character definition which also uses n. It'd probably be fine, but to avoid all possible conflicts, I changed the parameter value to (parm_n) instead.
 
Last edited:

xrknz

Newbie
Game Developer
Apr 28, 2024
20
42
Thank you for the help! It works!
I like thera are so many options. Maybe a bit overwhelming o_O
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,639
2,306
Thank you for the help! It works!
I like thera are so many options. Maybe a bit overwhelming o_O

Honestly, you probably just want the final one. (or at least a mix of the last one and the next to last one).
The rest are there to show my thought process of actually getting to that point.
Fixing a problem is good. Knowing how the fix works is better.
 
Last edited:

ZorasSon

Newbie
Sep 18, 2020
15
12
Also what you wrote should work in Python.

Python:
kil = 40

#Something that changes kil here
kil = -101
#kil has changed, need to limit it

if kil > 100:
    kil = 100
elif kil < -100:
    kil = -100

print(kil)
So if you were used to Python this might be a Renpy thing (I never used it to be able to comment on it -- maybe some sort of interactive mode?). In that case putting everything on functions should work for any similar errors.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,639
2,306
So if you were used to Python this might be a Renpy thing [...]

RenPy is python, or at least built upon it.

You can invoke a single python command by prefixing it with $.

So...

Python:
    # in RenPy...

    $ x += 1

You can also invoke multiple python statements by prefixing them with a python: block.

Python:
    # in RenPy...

    python:
        x += 1
        y += 1
        z += 1

The only downside is that RenPy doesn't do RenPy things (like maintain the rollback data) when doing a python command. The RenPy infrastructure stuff is only done when control passes back to RenPy. Plus in practical terms, the vast majority of the things required for a VN game is done by RenPy statements, not python statements.
 
  • Like
Reactions: ZorasSon

dude.FF

New Member
Aug 2, 2023
3
2
There is a python callback feature in RenPy - a list of functions that will be called once a python block is ended.

Code:
init python:
    def clamp():
        global kil, love_points, suzi_affection, suzi_bounds
        
        negative_love_limit = -50
        positive_love_limit = 50
        
        if kil < -100:
            kil = -100
        if kil > 100:
            kil = 100
        
        if love_points < negative_love_limit:
            love_points = negative_love_limit
        if love_points > positive_love_limit:
            love_points = positive_love_limit
        
        if suzi_affection < suzi_bounds[0]:
            suzi_affection = suzi_bounds[0]
        if suzi_affection > suzi_bounds[1]:
            suzi_affection = suzi_bounds[1]

    config.python_callbacks.append(clamp)
There are 3 examples how you can configure it - with hardcode, with local variables, and with character-related range. Just required variables in it, and be happy.
During the game you can just write $ kil += 400 or $ suzi_affection = -200, and be sure that variables won't go out control. Notive that suzi's boundaries are also a variable, and thus could be changed during the game too. For example, if you can't progress further with her unless you complete some quest.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,639
2,306
There is a python callback feature in RenPy - a list of functions that will be called once a python block is ended.

def clamp():
[...]
config.python_callbacks.append(clamp)

A brief explanation for those not familiar with callbacks...

Callbacks are functions that are added to a list of functions that are invoked any time that "something" happens. In this case, after any python statement is executed.

The danger (if it is such a thing) is that it is ANY python function, not just those that change the variables you care about.
It also risks getting invoked before the variables are created. Lots of python statements can be executed while your game is starting - each one of those will invoke the functions in the callback list. It will definitely get invoked a lot more than you think it will.

I mention all that, because one thing that trips people up is their game crashing because a callback function references a variable that hasn't been created yet. It is therefore advised to do a if hasattr("store", "variablenamehere"): type check before using those variables in your callback function.

They are a good way to deal with this issue though, because it means you can just leave the code as:

Python:
    $ love_points += 1

And the callback will take care of if it goes out of range (if coded correctly), because $ is "do python command" and after the python commands finish, the callback is invoked.