Codes: Achievements, Take Two
Last time, we added achievements to our game, in a rather crappy and user un-friendly way, but that should theoretically integrate with Steam properly. Today, we improve that by defining a custom achievement command and showing a message to the user!
The first thing to do is to define a command:
python early:
basic_achievements = (
("First Steps", "Starting your college life", "Starting your college life"),
…More achievements…
)
def parse_achieve(lex):
what = lex.rest()
return what
def lint_achieve(what):
if not any(what == a for a,lkd,unk in basic_achievements):
renpy.error("Invalid achievement " + what)
def execute_achieve(what):
if achievement.has(what):
return
achievement.grant(what)
renpy.show_screen("gain_achieve", _layer="screens", achievetext=what)
renpy.register_statement("achieve", parse=parse_achieve, execute=execute_achieve, lint=lint_achieve)
Note the screen that we show to notify the user. That screen is defined as follows:
screen gain_achieve(achievetext):
tag achievementalert
fixed:
image "sp/achievementbg.png" at Position(xpos=0.85, ypos=0.1, xanchor=0.5, yanchor=0.5)
text "{size=22}You unlocked an achievement:\n" + achievetext + "{/size}" id "achievetext" at Position(xpos=0.85, ypos=0.1, xanchor=0.5, yanchor=0.5) xmaximum 290 ymaximum 90
timer 3.0 action Hide("gain_achieve")
Basically, we’ve added a new screen that that shows up in the upper right of the screen, tells the user they unlocked an achievement, and then dismisses itself after 3 seconds.
One important thing I want to point out here is the _layer=“screens”parameter when you show the screen with renpy.show_screen(). Assuming I understand RenPy properly, by default, there are a few different threads to render the GUI. In particular, when you show a screen, the main rendering thread is blocked on the screen, and will not do anything else until you dismiss it. In this case, because we’re showing a screen that dismisses itself, RenPy freaks out and throws an error on the next call that does anything other than hides the screen.
When we direct the screen to be displayed in the overlay layer, instead, it does not tie up the main thread. Indeed, gameplay continues normally and the user can keep playing.
The final part of this is to actually grant achievements within your game script. Because we’ve defined a custom command, this is now easy:
"You feel an immense sense of relief, as well."
achieve Nothing But Love
show adam grin
"Adam turns to you, grinning."
One caveat that I have not figured out yet: What to do if your achievement name contains quote characters. For example, I have an achievement called “Journey’s End” with an apostrophe in the name. Trying to grant it directly like this results in an error:
achieve Journey's End
Instead, I’ve special-cased that achievement, and grant it as JourneysEnd instead. I’m sure there’s a way to escape the apostrophe, but I also didn’t care to keep banging my head against it tonight.
One other important thing to note: These statements are notequivalent.
achieve Nothing But Love
achieve "Nothing But Love"
The version without quotes is almost certainly what you want. (I’m assuming the quotes are read literally, instead of denoting a string, but I also did not test to see exactly why it was failing to work properly.)