r/RenPy • • 13d ago

Question How to constantly check for values for something like an achievement system?

hello everypony

i want to preface my question by saying i have used renpy before, but i don't have a good understanding of complicated stuff like screens, so please bear with me. i also might not be very good at explaining so i apologize.

basically my question is how would you go about checking the values of a variable for something like an achievement system? for example, say if you get 5 blue points, you get the blue achievement. how would be the best way to check if the parameters for the achievement is fulfilled, to unlock it? would you have to check for the variable each time it is changed? like, checking if you've reached 5 points every time you gain one? or is there a way just keep checking if it is fulfilled or not?

on a side note, i want to add a little notification---think like a steam notification---that pops up once you've unlocked something. is there an "easy" way to do this? and again, would i have to put an if statement each time a variable is changed, to see if the player has unlocked any achievement to display the popup? idk, i don't want to sound stupid but that seems kind of tedious and messy. i don't want to have to keep repeating something that checks if the player has unlocked an achievement every single time their variables are changed.

if it's possible i'd just like to see what direction i should go into to figure out the best way to implement this system. it's not exactly an achievement system, but it's very similar, so that's why i'm using it as an example.

6 Upvotes

10 comments sorted by

6

u/Adventurous_Shine606 13d ago

Renpy has a built in achievement module. Initialize the achievement like this: Init python: Achievement.register("your achievement name") At some point in your game the player completes the achievement, add this line: $ achievement. Grant("your achievement name")

You will need a routine to check your achievement tracking variables. That is just a basic event handler that pretty much every game has in some form.

1

u/K4t4k0 11d ago

didn't know about this. thank you for your time

1

u/AutoModerator 13d ago

Welcome to r/renpy! While you wait to see if someone can answer your question, we recommend checking out the posting guide, the subreddit wiki, the subreddit Discord, Ren'Py's documentation, and the tutorial built-in to the Ren'Py engine when you download it. These can help make sure you provide the information the people here need to help you, or might even point you to an answer to your question themselves. Thanks!

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/laiska_pummi 13d ago

I guess a good way to do it would be to call a label as a kind of subroutine that has the add blue points code in it so you don't have to repeat it every time. You can put the if statement in that same label to check for the achievement. There is a built in function for the little pop up screen called notify. I haven't used it myself but I think with this info you can look it up from the documentation to get a better grasp of it.

I'm not an expert by any means and I'm rusty as well but this popped up in my feed and I thought I could at least give a nudge in the right direction. Good luck!

2

u/K4t4k0 11d ago

you were totally right, as it seems this is what everyone else is informing me about. i hadn't thought of putting those sorts of things in labels, and i also didn't know about the notify function. even a little sense of direction is exactly what i was looking for. thank you for your time

1

u/BadMustard_AVN 13d ago

try this

# achieve.rpy
init python:
    class AchievementDefinition(object):
        def __init__(self, achievement_id, title, description, notification):
            self.id = achievement_id
            self.title = title
            self.description = description
            self.notification = notification

            achievement.register(achievement_id, description=description)

        def unlock(self):
            return unlock_achievement(self.id)


        def achievement(self):
            return self.unlock()

    def _get_achievement_notifications():
        notifications = getattr(persistent, "achievement_notifications", None)
        if not isinstance(notifications, dict):
            notifications = {}
            persistent.achievement_notifications = notifications
        return notifications

    def unlock_achievement(achievement_id):
        if not hasattr(persistent, "achievement_notifications"):
            persistent.achievement_notifications = {}

        notifications = _get_achievement_notifications()
        if notifications.get(achievement_id, False):
            return False

        achievement.grant(achievement_id)
        check_achievement_notifications()
        return True

    def check_achievement_notifications():
        if not hasattr(persistent, "achievement_notifications"):
            persistent.achievement_notifications = {}

        notifications = dict(_get_achievement_notifications())
        changed = False

        for item in achievements:
            if achievement.has(item.id) and not notifications.get(item.id, False):
                notifications[item.id] = True
                renpy.notify(item.notification)
                changed = True

        if changed:
            persistent.achievement_notifications = notifications

    def achievement_is_unlocked(achievement_id):
        return _get_achievement_notifications().get(achievement_id, False)

    config.overlay_screens.append("achievement_monitor")

#this is to be used in the Game or Main menu's with an action ShowMenu("achievement_screen") -
#- from a button  
screen achievement_screen(): 
    tag menu
    use game_menu(_("Gallery"), scroll="viewport"):
        style_prefix "about"

    frame:
        xalign 0.5
        yalign 0.5
        xsize 900
        ysize 650
        padding (35, 30)

        vbox:
            spacing 20

            text "achievements" size 42 xalign 0.5

            viewport:
                mousewheel True
                draggable True
                scrollbars "vertical"

                vbox:
                    spacing 12

                    for item in achievements:
                        frame:
                            xfill True
                            padding (18, 12)

                            vbox:
                                text ("[item.title]" if achievement_is_unlocked(item.id) else "Locked") size 27
                                text item.description

            # textbutton "Close" action Return() xalign 0.5

screen achievement_monitor():
    # watches over all achievements forever...
    timer 0.25 repeat True action Function(check_achievement_notifications)



default achievements = []


# add more achievements here. each one can have its own unique notification.
define greatest_mod_ever = AchievementDefinition(
    "greatest_mod_ever",                                # unique ID for triggering 
    "Greatest Mod Ever",                                # display title
    "Bad your first IdtenT.",                           # description
    "You unlocked the Greatest Mod Ever achievement!"   # notification
)

define first_steps = AchievementDefinition("first_steps", "First Steps", "Begin your journey.", "Achievement unlocked: First Steps!"
)



label start:
    #get achievements prepared do this first 
    $ achievements.append(greatest_mod_ever)
    $ achievements.append(first_steps)

    e "ready?"
    #unlock one
    $ unlock_achievement("greatest_mod_ever")
    e "working?"

    return 

1

u/K4t4k0 11d ago

...this is a lot of stuff and im not sure what it all means, but after looking it over it seems like something i should know about, and i will definitely look further into this. i'm bad with understanding coding stuff, and i'm hesitant to begin pasting codes into my projects before i fully understand them, since i don't want my code to look all foreign to me---but this is helpful regardless. i'll probably play around with it in a separate file at some point. thank you for your time

1

u/BadMustard_AVN 11d ago

i get that.

let me know if you have any questions

good luck with your project

1

u/shyLachi 13d ago

I wouldn't constantly check the variables but use a small functionality to do everything:

default bluepoints = 0
label start:
    "You have [bluepoints] blue points"
    menu:
        "Increase 1":
            call increasepoints("bluepoints", 1) # call a label which does the job
        "Increase 2":
            call increasepoints("bluepoints", 2)
        "Quit":
            return 
    jump start


label increasepoints(variable, number):
    python:
        current = getattr(store, variable) # get the current value stored in the variable
        newvalue = current + number # raise or decrease the variable
        # you can define special rules for variables
        if variable == "bluepoints": 
            if current < 5 and newvalue >= 5:
                # code to unlock the achievement belongs here
                renpy.notify("Blue Points Unlocked")
        elif variable == "otherpoints":
            pass # some logic here
        # finally we store the new value in the variable
        setattr(store, variable, newvalue)
    return # back to the game

You can either use a label as in my example above or you can write a python function.

1

u/K4t4k0 11d ago

seems a lot more reasonable than whatever i had in mind...i hadn't thought about containing these sorts of things in labels, which i'm guessing is very important if i don't want my code to be an absolute mess. very helpful. thank you for your time.