Jam13Yuuka/game/achievements.rpy

454 lines
21 KiB
Plaintext

################################################################################
##
## Achievements for Ren'Py by Feniks (feniksdev.itch.io / feniksdev.com)
##
################################################################################
## This file contains code for an achievement system in Ren'Py. It is designed
## as a wrapper to the built-in achievement system, so it hooks into the
## Steam backend as well if you set up your achievement IDs the same as in
## the Steam backend.
##
## You don't have to register the achievements or anything with the backend,
## or worry about syncing - that's all automatically done for you when the
## achievements are declared and granted.
##
## To get started, declare a few achievements below. Some samples are included.
## You may also replace the `image locked_achievement` image with something
## appropriate - this image is used as the default "Locked" image for all your
## achievements unless you specify something else.
##
## Then you can make a button to go to your achievement gallery screen, e.g.
# textbutton _("Achievements") action ShowMenu("achievement_gallery")
## This will show the achievement gallery screen, declared below. You can
## further customize it however you like.
## If you click on an achievement in the gallery during development (this will
## not happen in a release build), it will toggle the achievement on/off.
## This will also let you see the achievement popup screen, similarly declared
## below. It can be customized however you like.
##
## If you use this code in your projects, credit me as Feniks @ feniksdev.com
##
## Leave a comment on the tool page on itch.io or an issue on the GitHub
## if you run into any issues.
################################################################################
################################################################################
## CONFIGURATION
################################################################################
## This is a configuration value which determines whether the in-game
## achievement popup should appear when Steam is detected. Since Steam
## already has its own built-in popup, you may want to set this to False
## if you don't want to show the in-game popup alongside it.
## The in-game popup will still work on non-Steam builds, such as builds
## released DRM-free on itch.io.
define myconfig.INGAME_POPUP_WITH_STEAM = True
## The length of time the in-game popup spends hiding itself (see
## transform achievement_popout below).
define myconfig.ACHIEVEMENT_HIDE_TIME = 1.0
## True if the game should show in-game achievement popups when an
## achievement is earned. You can set this to False if you just want an
## achievement gallery screen and don't want any popups.
define myconfig.SHOW_ACHIEVEMENT_POPUPS = True
## This can be set to a sound that plays when the achievement popup appears.
## None does not play a sound.
define myconfig.ACHIEVEMENT_SOUND = None # "audio/sfx/achievement.ogg"
## If the sound plays, this sets the channel it will play on. The audio
## channel plays on the sfx mixer, and can play overlapping sounds if multiple
## achievements are earned at once.
define myconfig.ACHIEVEMENT_CHANNEL = "audio"
## A callback, or list of callbacks, which are called when an achievement
## is granted. It is called with one argument, the achievement which
## was granted. It is only called if the achievement has not previously
## been earned. See the README for more information.
define myconfig.ACHIEVEMENT_CALLBACK = [
## This first example is an achievement which unlocks after two other
## achievements have been granted ("hidden_achievement" and
## "hidden_description").
#LinkedAchievement(hidden3=['hidden_achievement', 'hidden_description']),
## The second example is an achievement which unlocks after all achievements
## have been granted. This is a special case.
# LinkedAchievement(platinum_achievement='all'),
]
init python:
## This is a built-in configuration value. It will set the position of
## the Steam popup. You can change this to any of the following:
## "top_left", "top_right", "bottom_left", "bottom_right"
## You may want to use this to ensure any Steam notifications don't conflict
## with the position of the built-in notification, if you're using both.
achievement.steam_position = None
################################################################################
## DEFINING ACHIEVEMENTS
################################################################################
define kicked_out = Achievement(
name=_("Kicked Out"),
id="kicked_out",
description=_("You left willingly and easily."),
locked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
hidden=True,
)
define scare_tactics = Achievement(
name=_("Scare Tactics"),
id="scare_tactics",
description=_("You frightened your students off."),
locked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
hidden=True,
)
define very_loud_quitting = Achievement(
name=_("Very Loud Quitting"),
id="very_loud_quitting",
description=_("You incited all the faculty into quitting."),
locked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
hidden=True,
)
define she_has_your_back = Achievement(
name=_("She Has Your Back"),
id="she_has_your_back",
description=_("Either Reimu, Marisa, or Alice had your back."),
locked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
hidden=True,
)
define they_have_your_back = Achievement(
name=_("They Have Your Back"),
id="they_have_your_back",
description=_("Your class had your back."),
locked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
hidden=True,
)
define hedgehog_dilemma_solved = Achievement(
name=_("Hedgehog's Dilemma Solved"),
id="hedgehog_dilemma_solved",
description=_("You were proud and stubborn until the end."),
locked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
hidden=True,
)
define retroactive = Achievement(
name=_("Retroactive"),
id="retroactive",
description=_("You embraced change."),
locked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
hidden=True,
)
# ## Replace this with whatever locked image you want to use as the default
# ## for a locked achievement.
# image locked_achievement = Text("?")
# ## Example 1 ###################################################################
# ## This is how you declare achievements. You will use `define` and NOT
# ## `default`, so you can update the achievements later (you wouldn't want the
# ## description to be tied to a specific save file, for example).
# ## The order you declare achievements in is the order they will appear in the
# ## achievement gallery, by default.
# define sample_achievement = Achievement(
# ## The human-readable name, as it'll appear in the popup and in the gallery.
# name=_("Sample Achievement"),
# ## The id is used for Steam integration, and should match whatever ID
# ## you have set up in the Steam backend (if using).
# id="sample_achievement",
# ## Description.
# description=_("This is a sample achievement."),
# ## The image used in the popup and in the gallery once this achievement
# ## is unlocked.
# unlocked_image="gui/window_icon.png",
# ## By default all achievements use the "locked_achievement" image (declared
# ## above), but if you wanted to provide a different image, this is how
# ## you would specify it. It's used in the achievement gallery when the
# ## achievement is locked.
# locked_image="locked_achievement",
# ## All achievements are hidden=False by default, but you can change it to
# ## hidden=True if you'd like the title/description to show as ??? in the
# ## achievement gallery. See Examples 3 and 4 for examples of this.
# hidden=False,
# )
# ## You can grant an achievement in-game with `$ sample_achievement.grant()`
# ## Example 2 ###################################################################
# define progress_achievement = Achievement(
# name=_("Progress Achievement"),
# id="progress_achievement",
# description=_("This is an achievement with a progress bar."),
# unlocked_image=Transform("gui/window_icon.png", matrixcolor=InvertMatrix()),
# ## To record progress, you need to specify a stat_max. This means you can
# ## show a progress bar with % completion towards the achievement. It is
# ## useful if, for example, you have an achievement counting how many
# ## chapters the player has completed which unlocks when they have seen all
# ## the chapters.
# stat_max=12,
# ## You can also provide a stat_modulo, which means the achievement is only
# ## updated in the Steam backend every time the stat reaches a multiple of
# ## the modulo.
# ## Alternatively, this system also lets you set stat_update_percent instead,
# ## so if you want it to update every 10% it progresses, you can set
# # stat_update_percent=10
# ## This is most useful for achievements with a large number of steps,
# ## like a general % completion achievement. Maybe there are 600 things to
# ## complete for the achievement, but obviously 0.1% increments are pretty
# ## meaningless so you can either set stat_modulo=6 or stat_update_percent=1
# ## and it will update Steam every 6 steps or every 1%.
# )
# ## To update progress towards completion of this achievement, you can use
# # $ progress_achievement.add_progress(1)
# ## where 1 is how much progress is added to the stat (so, the first time it
# ## is called for the above example it'd be 1/12, the second it'd be 2/12, etc).
# ##
# ## Alternatively, you can directly set the progress like:
# # $ progress_achievement.progress(5)
# ## This will directly set progress to 5, making the above example 5/12 for
# ## example. This can be useful if you're doing something like using a set to
# ## track unique progress towards the achievement e.g.
# # $ persistent.seen_endings.add("end1")
# # $ ending_achievement.progress(len(persistent.seen_endings))
# ## This will prevent the achievement from being added to multiple times if the
# ## player sees the same ending multiple times.
# ## Example 3 ###################################################################
# ## This achievement is "hidden", that is, its name and description appear as
# ## ??? in the achievement gallery until it is unlocked.
# define hidden_achievement = Achievement(
# name=_("Hidden Achievement"),
# id="hidden_achievement",
# description=_("This hidden achievement hides both the name and description."),
# unlocked_image=Transform("gui/window_icon.png", matrixcolor=BrightnessMatrix(-1.0)),
# hidden=True, ## The important bit that hides the name and description
# )
# ## Example 4 ###################################################################
# define hidden_description = Achievement(
# name=_("Hidden Description"),
# id="hidden_description",
# description=_("This hidden achievement hides only the description."),
# unlocked_image=Transform("gui/window_icon.png", matrixcolor=SepiaMatrix()),
# hide_description=True, ## The important bit that hides only the description
# )
# ## Example 5 ###################################################################
# ## This achievement unlocks automatically when the other two hidden achievements
# ## are unlocked. This is set up via myconfig.ACHIEVEMENT_CALLBACK earlier in
# ## the file.
# define hidden_double_unlock = Achievement(
# name=_("You found it"),
# id="hidden3",
# description=_("This achievement unlocks automatically when the other two hidden achievements are unlocked."),
# unlocked_image=Transform("gui/window_icon.png", matrixcolor=ContrastMatrix(0.0)),
# hidden=True,
# ## Besides just setting hide_description=True to set it to "???", you can
# ## optionally provide your own custom description here, which is only
# ## shown until the achievement is unlocked.
# hide_description=_("Try unlocking the other two hidden achievements before this one."),
# )
# ## Example 6 ###################################################################
# ## This -2 makes sure it's declared before the other achievements. This is
# ## so it shows up first in the list even though it's defined all the way down
# ## here.
# define -2 all_achievements = Achievement(
# name=_("Platinum Achievement"),
# id="platinum_achievement",
# description=_("Congrats! You unlocked every achievement!"),
# unlocked_image=Transform("gui/window_icon.png", matrixcolor=BrightnessMatrix(1.0)),
# hide_description=_("Get all other achievements."),
# )
################################################################################
## SCREENS
################################################################################
## POPUP SCREEN
################################################################################
## A screen which shows a popup for an achievement the first time
## it is obtained. You may modify this however you like.
## The relevant information is:
## a.name = the human-readable name of the achievement
## a.description = the description
## a.unlocked_image = the image of the achievement, now that it's unlocked
## a.timestamp = the time the achievement was unlocked at
screen achievement_popup(a, tag, num):
zorder 190
## Allows multiple achievements to be slightly offset from each other.
## This number should be at least as tall as one achievement.
default achievement_yoffset = num*170
frame:
style_prefix 'achieve_popup'
## The transform that makes it pop out
at achievement_popout()
## Offsets the achievement down if there are multiple
yoffset achievement_yoffset
has hbox
add a.unlocked_image:
## Make sure the image is within a certain size. Useful because
## often popups are smaller than the full gallery image.
## In this case it will not exceed 95 pixels tall but will retain
## its dimensions.
fit "contain" ysize 95 align (0.5, 0.5)
vbox:
text a.name
text a.description size 25
## Hide the screen after 5 seconds. You can change the time but shouldn't
## change the action.
timer 5.0 action [Hide("achievement_popup"),
Show('finish_animating_achievement', num=num, _tag=tag+"1")]
style achieve_popup_frame:
is confirm_frame
align (0.0, 0.0)
style achieve_popup_hbox:
spacing 10
style achieve_popup_vbox:
spacing 2
## A transform that pops the achievement out from the left side of
## the screen and bounces it slightly into place, then does the
## reverse when the achievement is hidden.
transform achievement_popout():
## The `on show` event occurs when the screen is first shown.
on show:
## Align it off-screen at the left. Note that no y information is
## given, as that is handled on the popup screen.
xpos 0.0 xanchor 1.0
## Ease it on-screen
easein_back 1.0 xpos 0.0 xanchor 0.0
## The `on hide, replaced` event occurs when the screen is hidden.
on hide, replaced:
## Ease it off-screen again.
## This uses the hide time above so it supports displaying multiple
## achievements at once.
easeout_back myconfig.ACHIEVEMENT_HIDE_TIME xpos 0.0 xanchor 1.0
################################################################################
## ACHIEVEMENT GALLERY SCREEN
################################################################################
## The screen displaying a list of the achievements the player has earned.
## Feel free to update the styling for this however you like; this is just one
## way to display the various information.
screen ending_gallery():
tag menu
add VBox(Transform("#292835", ysize=110), "#21212db2") # Background
############################################################################
## Version 1 ###############################################################
## If you're using a default template/typical Ren'Py layout, uncomment
## the following:
# use game_menu(_("Achievement Gallery"), scroll='viewport'):
############################################################################
## Version 2 ###############################################################
## Otherwise, if you'd like this to be independent of the game menu,
## use the following:
if main_menu:
textbutton _("Return") action Return() align (1.0, 1.0)
viewport:
mousewheel True draggable True pagekeys True
scrollbars "vertical"
xalign 0.5 yalign 0.5
xsize int(config.screen_width*0.6) ysize int(config.screen_height*0.7)
xfill False yfill False
has vbox
spacing 20
############################################################################
## Version 3 ###############################################################
## You might also consider a vpgrid layout like so:
# textbutton _("Return") action Return() align (1.0, 1.0)
# vpgrid:
# cols 2
# mousewheel True draggable True pagekeys True
# scrollbars "vertical"
# xalign 0.5 yalign 0.5
# xsize 1500 ysize int(config.screen_height*0.7)
# yspacing 70 xspacing 50
############################################################################
## This list contains every achievement you declared. You can also
## create your own lists to iterate over, if desired. That would be
## useful if you wanted to group achievements by category, for example.
for a in Achievement.all_achievements:
button:
style_prefix 'achievement'
## During development, you can click on achievements in the
## gallery and they will toggle on/off.
if config.developer:
action a.Toggle()
has hbox
if a.idle_img:
fixed:
align (0.5, 0.5)
xysize (155, 155)
add a.idle_img fit "scale-down" ysize 155 align (0.5, 0.5)
else:
null width -10
vbox:
label a.name
text a.description
if a.has():
## There are two ways to display the timestamp. The
## first is automatically formatted like
## Unlocked Sep 14, 2023 @ 6:45 PM
text a.timestamp size 22
## If you want to format it yourself, you can use
## the get_timestamp method:
# text __("Achieved at ") + a.get_timestamp(__("%H:%M on %Y/%m/%d"))
## The above example would display the timestamp like:
## Achieved at 18:45 on 2023/09/14
## See https://strftime.org/ for formatting
## Note also the double underscores for translation.
elif a.stat_max:
# Has a bar to show stat progress.
## NOTE: If you don't want to show the progress *bar*,
## you can remove this entire block (or potentially just
## keep the text and not the bar if you like).
fixed:
fit_first True
bar value a.stat_progress range a.stat_max:
style 'achievement_bar'
text "[a.stat_progress]/[a.stat_max]":
style_suffix "progress_text"
## So there's a bit of space at the bottom after scrolling all the way.
null height 100
## A header that shows how many achievements you've earned, out of
## the total number of achievements in the game. Feel free to remove
## or relocate this.
label __("Endings: ") + "{earned}/{total}".format(
earned=Achievement.num_earned(), total=Achievement.num_total()):
text_size 52 xalign 0.5 text_color "#f93c3e" top_padding 15
## This is an example of a button you might have during development which
## will reset all achievement progress at once. It can also be provided
## to players if you'd like them to be able to reset their achievement
## progress.
if main_menu:
textbutton "Reset Progress" action Achievement.Reset() align (1.0, 0.0)
style achievement_button:
size_group 'achievement'
xmaximum 750
style achievement_label:
padding (2, 2)
style achievement_label_text:
size 40 color "#ff8335"
style achievement_hbox:
spacing 10
style achievement_vbox:
spacing 2
style achievement_bar:
xmaximum 600