Ren'Py Negative figures

mr.moonmi

Newbie
Game Developer
Jun 21, 2020
52
1,347
Hello everyone.
Does anybody know how to write a function for checking all variables in the game and prevent them from going below the 0?

Here are a couple of variables from my game:
Python:
default s_alpha = 0
default s_beta = 0
default s_chloe = 0
And after some choices players can decrease some of them even below zero. And I don't want that.

I've found one solution that works, but I have to use it every time I want to decrease the value.
Python:
$ s_alpha = max(0, points-1)
I'm pretty sure there's a better and more elegant way to check ALL variables and prevent them from going below the 0.
Hope someone knows how :) Thanks.
 

appelflappel

New Member
Mar 10, 2019
4
3
One way to do this is by creating a class which inherits the standard integer type and doesn't allow the value to go below zero. This would look something like
Python:
class NonNegative(int):
    def __new__(cls, value, *args, **kwargs):
        return  super(cls, cls).__new__(cls, max(value, 0))

    def __add__(self, other):
        res = super(NonNegative, self).__add__(other)
        return self.__class__(max(res, 0))

    def __sub__(self, other):
        res = super(NonNegative, self).__sub__(other)
        return self.__class__(max(res, 0))

    def __mul__(self, other):
        res = super(NonNegative, self).__mul__(other)
        return self.__class__(max(res, 0))

    def __div__(self, other):
        res = super(NonNegative, self).__div__(other)
        return self.__class__(max(res, 0))

    def __str__(self):
        return "%d" % int(self)

    def __repr__(self):
        return "NonNegative(%d)" % int(self)
At this point you can create an instance of this class by saying default s_alpha = NonNegative(0). You can now do anything you want with this variable like s_alpha += 3 or s_alpha -= 1 but the value should never go below zero. If you want to do something like s_alpha = points-1, you have to make sure that this points variable is also an instance of NonNegative, since otherwise you would overwrite s_alpha to become a regular integer again. (If points is not an instance of NonNegative, you could for example use s_alpha = NonNegative(points-1))

To keep your code clean I would put this class in some separate python file misc.py and import the class with from misc import NonNegative. Hope this helps!
 
  • Like
Reactions: anne O'nymous

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,490
7,035
Another, perhaps slightly simpler, way to do this is to update your variables using a function, rather than directly.
Code:
default love = 0
...
init python:
    def change_love(amount):
        global love

        love += amount
        if love < 0:
            love = 0
Given that, in your script you can then do
Code:
    me "I love you more"
    $ change_love(1)

    me "I love you less"
    $ change_love(-1)
By using a function, you can check boundaries, relationships with other variables, etc.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,363
15,280
Python:
class NonNegative(int):
    def __new__(cls, value, *args, **kwargs):
        return  super(cls, cls).__new__(cls, max(value, 0))

    def __add__(self, other):
        res = super(NonNegative, self).__add__(other)
        return self.__class__(max(res, 0))
Since you already max the value at creation time, you don't need to do it elsewhere.
Code:
    def __add__(self, other):
        return self.__class__(super(NonNegative, self).__add__(other))
Would be enough.

You also don't need to define __str__ and __repr__, the one coming from the ancestor are enough, and you forgot __floordiv__.


This being said, and since we are talking about this, here's a class to have truly capped integers, with either/both a minimal and maximal value.
Python:
init python:

    class CappedInt(int):
        def __new__(cls, value, maxValue=None, minValue=None ):
            if not maxValue is None: value = min( value, maxValue )
            if not minValue is None: value = max( minValue, value )
            self = super(cls, cls).__new__(cls, value )
            self.maxValue = maxValue
            self.minValue = minValue
            return self

        def __add__(self, value):
            return self.__class__( super(CappedInt, self).__add__(value), self.maxValue, self.minValue )

        def __sub__(self, value):
            return self.__class__( super(CappedInt, self).__sub__(value), self.maxValue, self.minValue )

        def __mul__(self, value):
            return self.__class__( super(CappedInt, self).__mul__(value), self.maxValue, self.minValue )

        def __div__(self, value):
            return self.__class__( super(CappedInt, self).__div__(value), self.maxValue, self.minValue )

        def __floordiv__(self, value):
            return self.__class__( super(CappedInt, self).__floordiv__(value), self.maxValue, self.minValue )

        def __rshift__(self, value):
            return self.__class__( super(CappedInt, self).__rshift__(value), self.maxValue, self.minValue )

        def __lshift__(self, value):
            return self.__class__( super(CappedInt, self).__lshift__(value), self.maxValue, self.minValue )

        def __pow__(self, value):
            return self.__class__( super(CappedInt, self).__pow__(value), self.maxValue, self.minValue )

default lovePercent = CappedInt( 0, maxValue=100, minValue=0 )
default money = CappedInt( 0, minValue=0 )
default hateFactor = CappedInt( 0, maxValue=10, minValue=-10 )
default answer = CappedInt( 0, maxValue=41 )
 
  • Like
Reactions: drKlauz