[Solved]Trying to make "config.python_callbacks" work for me

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
User avatar
8bitRainbow
Newbie
Posts: 19
Joined: Tue Mar 29, 2016 9:21 pm
Contact:

[Solved]Trying to make "config.python_callbacks" work for me

#1 Post by 8bitRainbow »

Okay, I got my self-balancing code to work. Now I want to be able to have the balance check updated continuously behind the scenes so I don't need to put $check_toughness() in front of checkpoints everywhere in my script. I could do that, but I prefer to have things automated, so I've challenged myself with this process (and so far, failed).

Here is my test code:

Code: Select all

    $toughness = 3.
    $sensitivity = 1.
    $tough_win=False
    $tough_pass=False
    $sensi_pass=False
    $sensi_win=False
    
    
init python:
    def check_toughness():
        
        tough_score = round((toughness / (toughness + sensitivity)) * 100, 2)
        if tough_score < 25:                         #from 1% upto 24% toughness = mostly sensitive, sensitivity wins!
            tough_win=False
            tough_pass=False
            sensi_pass=False
            sensi_win=True
        elif tough_score >= 25 and tough_score < 50: #from 25% upto 49% toughness = more sensitive, sensitivity passes.
            tough_win=False
            tough_pass=False
            sensi_pass=True
            sensi_win=False
        elif tough_score >= 50 and tough_score < 75: #from 50% upto 74% toughness = more tough, toughness passes.
            tough_win=False
            tough_pass=True
            sensi_pass=False
            sensi_win=False
        elif tough_score >= 75:                      #from 75% upto 100% toughness = mostly tough, toughness wins!
            tough_win=True
            tough_pass=False
            sensi_pass=False
            sensi_win=False
        else:
            pass
    
    
    config.python_callbacks.append(check_toughness)
So far the most common error I get no matter how I move things around is that "tough_win" (and I'm assuming the other 3 of them) is undefined. I'm not 100% clear on global variables but have tried declaring it as a global under init, out of init, any which way I could with no success.

Can someone help me figure out how to make this work?


*** Originally, I wanted to do a continuous check for my Stat class, where $tough = Stat() and contains "up" and "down" variables rather than "toughness" and "sensitivity" as they're presented here outside of a class. That way, I could have all Stat() stats checked and updated at once. That seemed to fail due to the check method being check(self) and "self" needing to be defined. Dunno if I explained that properly; I'll post that Class code below if anyone asks for it.
Last edited by 8bitRainbow on Fri Nov 17, 2017 9:26 pm, edited 1 time in total.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Trying to make "config.python_callbacks" work for me

#2 Post by Remix »

Within a function (or class method) you can mostly only read rather than write a value to a variable held within the global namespace.

You will have to globalize the variable inside the function:

Code: Select all

    
init python:

    def check_toughness():
        global tough_win, tough_pass, sensi_pass, sensi_win
        
        # this line should work fine as we are only reading the value
        tough_score = round((toughness / (toughness + sensitivity)) * 100, 2)
        if tough_score < 25:                         #from 1% upto 24% toughness = mostly sensitive, sensitivity wins!

            # these are the ones we are setting, so they need globalizing
            tough_win=False
            tough_pass=False
            sensi_pass=False
            sensi_win=True
Frameworks & Scriptlets:

User avatar
PyTom
Ren'Py Creator
Posts: 16093
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:

Re: Trying to make "config.python_callbacks" work for me

#3 Post by PyTom »

There's a kind of chicken-and-egg thing with python callbacks, since the callback will run while Ren'Py is starting up. I suggest using hasattr to fix this:

Code: Select all

    def check_toughness():
        if not hasattr(store, "sensi_win"):
             return
        
        ...
That's in addition to the global line that Remix mentioned.
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
8bitRainbow
Newbie
Posts: 19
Joined: Tue Mar 29, 2016 9:21 pm
Contact:

Re: Trying to make "config.python_callbacks" work for me

#4 Post by 8bitRainbow »

Oh wow thank you so much! This is a particularly confusing area of code for me. :D

More out of curiosity than necessity, would it be possible to give a similar solution to my Stat class methods? I'll paste in the code, would love to hear what you guys think!

Code: Select all

####Define stat system
    class Stat(renpy.store.object):
        def __init__(self, up=1., down=1., score=1., upwin=False, uppass=False, downpass=False, downwin=False):
            self.up=up
            self.down=down
            self.score=score
            self.upwin=upwin
            self.uppass=uppass
            self.downpass=downpass
            self.downwin=downwin
            

####stat adding and subtracting system
        def rise(self, amount=1):
            self.up += 1
            
        def drop(self, amount=1):
            self.down += 1
        
####checking system I'd like to apply the callbacks for to be refreshing automatically
        def check(self):
            self.score = round((self.up / (self.up + self.down)) * 100, 2)
            if self.score < 25:                         #from 1% upto 24% toughness = mostly sensitive, down wins!
                self.upwin=False
                self.uppass=False
                self.downpass=False
                self.downwin=True
            elif self.score >= 25 and self.score < 50: #from 25% upto 49% toughness = more sensitive, down passes.
                self.upwin=False
                self.uppass=False
                self.downpass=True
                self.downwin=False
            elif self.score >= 50 and self.score < 75: #from 50% upto 74% toughness = more tough, up passes.
                self.upwin=False
                self.uppass=True
                self.downpass=False
                self.downwin=False
            elif self.score >= 75:                      #from 75% upto 100% toughness = mostly tough, up wins!
                self.upwin=True
                self.uppass=False
                self.downpass=False
                self.downwin=False
            else:
                pass
This is the code I was originally using and when I couldn't get the callbacks to work with it, I began defining each stats' variables individually. In this class, tough.up would be "toughness" and tough.down would be "sensitivity" from my original example. I'd much rather use this system if I could have the callbacks automation working somehow.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Trying to make "config.python_callbacks" work for me

#5 Post by Remix »

It would only be feasible if the Stat class is either a Singleton (a Python (or any OOP language) pattern that instantiates only one instance of the class and has all calls reference that) or if there is a way of referencing each Stat instance ... basically you need some way in the code to add the "Class reference dot method name" to the callbacks ( e.g. Lucy.Stat.check, Player.Stat.check etc - if you are using Stat for multiple people).

This would either yield
config.python_callbacks.append(stat_singleton_reference.check) # add the method of a reference to a single instance
or
config.python_callbacks.append(pc.check) # add multiple methods for each character
config.python_callbacks.append(e.check)

There is a recent quite useful thread about incorporating stats into a Ren'py character class which might be worth a read. The post from Trooper6 is worth exploring as it details PyTom's advice on gluing stats and characters together using the character namespace.
If you were using something like that, you could likely write a wrapper function that did the gluing and callback append within the character define.

Sub-note: I do not have a clue from your code what anything means and why the character's Sensitivity<->Toughness is not just measured as a single value with all testing done within the rise or drop methods... If the values can only change there, surely there is no need for the check method if you move the sanity tests there or call check within both of them?
Frameworks & Scriptlets:

User avatar
8bitRainbow
Newbie
Posts: 19
Joined: Tue Mar 29, 2016 9:21 pm
Contact:

Re: Trying to make "config.python_callbacks" work for me

#6 Post by 8bitRainbow »

In the time it took to get a reply to my question, I accidentally figured it out! XD As a self-taught non-programmer I'm really proud of this system, even if it doesn't end up actually making much of a difference or if there would be a simpler way to implement the same functionality.

I really appreciate your help, Remix. To answer your question (which I'm almost sure I understand, my brain is kinda doing backflips), I'm trying to emulate a balancing system I've seen in text adventure games like the Choice Of Games series and Sorcery and whatnot. They have stats that never decrease, only opposing stat pairings that both increase. This is useful in character development because you can hold it against someone for acting like a jerk repeatedly even if they've acted kindly a little bit more (allowing for NPCs to hold grudges even if the overall situation is responding to the higher level of kindness, for example). It's also useful for comparing the stats against each other vs the number of decisions affecting them so far... so in the beginning it's very easy to switch from being tough to being wimpy but the more choices you make toward one or the other, the more "ground in" those choices become, decreasing the amount of Dramatic Personality Shifts in a complex stat raising simulator. Is that what you were asking about? I hope that's what you were asking about. :3

Anyway, I think this works. It does in my little test section. So if anyone wants to use it for their projects, feel free. Also all suggestions for improvements are welcome!

Code: Select all

    $tough.up = 3.
    $tough.down = 15.

init -2 python:
####Define stat system
    class Stat(renpy.store.object):
        def __init__(self, up=1., down=1., score=1., upwin=False, uppass=False, downpass=False, downwin=False):
            self.up=up
            self.down=down
            self.score=score
            self.upwin=upwin
            self.uppass=uppass
            self.downpass=downpass
            self.downwin=downwin
            

        #stat adding and subtracting system (for best results, leave at "1")
        def rise(self, amount=1):
            self.up += amount
            
        def drop(self, amount=1):
            self.down += amount
            
        #stat checking system
        def check(self):
            self.score = round((self.up / (self.up + self.down)) * 100, 2)
            if self.score < 25:                         #from 1% upto 24% toughness = mostly sensitive, down wins!
                self.upwin=False
                self.uppass=False
                self.downpass=False
                self.downwin=True
            elif self.score >= 25 and self.score < 50: #from 25% upto 49% toughness = more sensitive, down passes.
                self.upwin=False
                self.uppass=False
                self.downpass=True
                self.downwin=False
            elif self.score >= 50 and self.score < 75: #from 50% upto 74% toughness = more tough, up passes.
                self.upwin=False
                self.uppass=True
                self.downpass=False
                self.downwin=False
            elif self.score >= 75:                      #from 75% upto 100% toughness = mostly tough, up wins!
                self.upwin=True
                self.uppass=False
                self.downpass=False
                self.downwin=False
            else:
                pass
                
        
        
init python:
    def runcheck():
        try:
            tough.check()
        except:
            pass


    config.python_callbacks.append(runcheck)
        
        
label tryit:
    
    "My toughness is [tough.up] and my sensitivity is [tough.down]."
    "My toughness is [tough.score] percent."
    "I'm going to test my toughness!"
    
    "I attempt to not cry while watching Old Yeller."
    if tough.upwin:
        "I'm so burly, the movie cries just because I'm watching it."
    elif tough.uppass:
        "It's a bit of a struggle, but I don't shed a tear."
    elif tough.downpass:
        "I almost hold it in, but a tear runs down my cheek."
    elif tough.downwin:
        "I begin sobbing hysterically as soon as the title appears."
    else:
        "Something went wrong."

Post Reply

Who is online

Users browsing this forum: Google [Bot]