WTS/game/scripts/interface/achievements.rpy
LoafyLemon 38d2494bc3 Fix conflicting properties (by Gouvernathor)
big work
what was reliable before is now not, in recent versions of renpy, which is why we added that new config (which I put in say.rpy, but it can be moved somewhere else)
Also fixes the game_menu's title position, incidentally

some changes are incompatible with my diary and achievement branches, respectively, they will need to be merged with a bit of manual care
2024-03-25 14:57:36 +00:00

454 lines
20 KiB
Plaintext

init python:
from collections import namedtuple
AchievementKind = namedtuple("AchievementKind", ("category", "name", "description", "icon", "secret"))
AchievementKind.title = AchievementKind.name
define achievements_db = {
# id : (categoryname, title, description, icon, secret)
"unlockher": AchievementKind("Characters", "Granger Danger", "Awarded for unlocking Hermione Granger.", "interface/icons/head/hermione.webp", False),
"unlockcho": AchievementKind("Characters", "Chang Dynasty", "Awarded for unlocking Cho Chang.", "interface/icons/head/cho.webp", False),
"unlocklun": AchievementKind("Characters", "Looney Tunes", "Awarded for unlocking Luna Lovegood.", "interface/icons/head/luna.webp", False),
"unlockast": AchievementKind("Characters", "Green Peas", "Awarded for unlocking Astoria Greengrass.", "interface/icons/head/astoria.webp", False),
"unlockton": AchievementKind("Characters", "Nymphadoreador", "Awarded for unlocking Nymphadora Tonks.", "interface/icons/head/tonks.webp", False),
"overwhored": AchievementKind("Characters", "Overwhored", "Awarded for fully corrupting Hermione.", "interface/icons/head/hermione.webp", False),
"unlocksus": AchievementKind("Characters", "Boner", "Awarded for unlocking Susan Bones.", "interface/icons/head/susan.webp", False),
"unlocksna": AchievementKind("Characters", "Strictly colleagues", "Awarded for unlocking Severus Snape.", "interface/icons/head/snape.webp", False),
"mirror": AchievementKind("Mirror", "Mirror, mirror on the wall..", "Awarded for unlocking the Room of Requirement.", "images/rooms/room_of_requirement/mirror_hover.webp", False),
"gold": AchievementKind("General", "Gold Digger", "Awarded for having 10,000 gold in total.", "interface/icons/gold.webp", False),
"drunkard": AchievementKind("General", "Drunken Master", "Awarded for collecting 25 bottles of wine.", "interface/icons/wine.webp", True),
"workaholic": AchievementKind("General", "Workaholic", "Awarded for completing five full reports.", "interface/icons/generic_scroll.webp", False),
"fireplace": AchievementKind("General", "Feel the Heat", "Awarded for lighting the fireplace 5 times or more.", "images/rooms/main_room/fireplace/fireplace_idle.webp", True),
"peta": AchievementKind("General", "I think I forgot something...", "Awarded for not feeding the bird for 50 days.... \nYou monster.\n{size=-4}Disclaimer: No real nor fictional animals were harmed in the process.{/size}", "images/rooms/main_room/phoenix/phoenix_01.webp", True),
"petpal": AchievementKind("General", "Regular stroking", "Awarded for petting the bird 25 times.", "images/rooms/main_room/phoenix/phoenix_01.webp", False),
"postman": AchievementKind("Cardgame", "Poster Boy", "Awarded for buying all posters from the token shop.", "interface/icons/agrabah_poster.webp", False),
"hats": AchievementKind("Cardgame", "Mad Hatter", "Awarded for buying all hat decorations from the token shop.", "interface/icons/icon_gambler_hat.webp", False),
"daddy": AchievementKind("Characters", "Who's your daddy?", "Awarded for letting Hermione call you a {size=-5}(sugar){/size} daddy.", "interface/icons/head/hermione.webp", True),
"pantiesfap": AchievementKind("Characters", "I sneezed on them...", "Awarded for rubbing one out on Hermione's panties.", "characters/genie/chibis/jerk_off/02.webp", False),
"pantiesfapcho": AchievementKind("Characters", "Exercise is important", "Awarded for rubbing one out on Cho's panties.", "characters/genie/chibis/jerk_off/02.webp", False),
"bros": AchievementKind("Characters", "Bros before hoes", "Awarded for becoming best pals with Snape.", "interface/icons/head/snape.webp", False),
"knock": AchievementKind("Characters", "*Knock* *knock*", "Awarded for telling Hermione to go away during her introductory events.", "images/rooms/main_room/door/door_idle.webp", True),
"decorator": AchievementKind("Cardgame", "Decorator", "Awarded for decorating the office for the first time.", "interface/icons/stag_trophy.webp", False),
"flashback": AchievementKind("Cardgame", "Flashback", "Awarded for retelling what actually happened...", "interface/icons/cards.webp", True),
"start": AchievementKind("General", "Welcome to Hogwarts!", "Awarded for finishing the intro.", "interface/icon.webp", False),
"export": AchievementKind("General", "Sharing is caring", "Awarded for exporting an outfit through the wardrobe menu.", "interface/wardrobe/icons/load.webp", False),
"Credits": AchievementKind("General", "New game, who this?", "Awarded for checking out the Credits Menu.", "interface/icons/silver_scroll.webp", False),
"Cardwin": AchievementKind("Cardgame", "Time to duel", "Awarded for winning your first Card game duel.", "interface/icons/cards.webp", False),
"puzzle": AchievementKind("General", "Down the hatch!", "Awarded for wasting a bottle of unbelievably rare phoenix tears by drinking it.", "interface/icons/item_potion.webp", True),
"ending": AchievementKind("General", "Bittersweet Farewell", "Awarded for reaching the original ending.", "interface/icons/silver.webp", True),
#1.37 HG achievements
"busted": AchievementKind("Characters", "BUSTED!", "Awarded for getting busted by Hermione when busting a nut.", "interface/icons/head/hermione.webp", False),
"herstrip": AchievementKind("Characters", "Dance lessons", "Awarded for having Hermione dance naked in front of you... and Snape.", "interface/icons/head/hermione.webp", False),
"herkiss": AchievementKind("Characters", "First Kiss", "Awarded for having Hermione make out with you-- r... cock...", "interface/icons/head/hermione.webp", False),
"hertits": AchievementKind("Characters", "Boobs Lover", "Awarded for sticking it between Hermione's fun bags.", "interface/icons/head/hermione.webp", False),
"headlib": AchievementKind("Characters", "Head Librarian", "Awarded for releasing your seed in Hermione's mouth.", "interface/icons/head/hermione.webp", False),
"nerdgasm": AchievementKind("Characters", "Nerdgasm", "Awarded for doing the deed with Hermione.", "interface/icons/head/hermione.webp", False)
}
init python hide:
for name in achievements_db:
achievement.register(name)
# keep the achievements from earlier versions
if isinstance(persistent.achievements, dict):
# id : [categoryname, title, description, unlocked, icon, secret]
for k, v in persistent.achievements.items():
if v[3]:
achievement.grant(k)
del persistent.achievements
init python:
class Achievements(object):
"""
Useless class, can't be rolled out because of pickle and save compatibility.
"""
@staticmethod
def validate():
"""Check if icons are loadable at init"""
for i in achievements_db.values():
if not renpy.loadable(i.icon):
raise IOError(repr(i.icon))
@staticmethod
def status(id):
return achievement.has(id)
@staticmethod
def unlock(id, silent=False):
if _in_replay:
return
if not achievement.has(id):
achievement.grant(id)
if not silent:
renpy.play('sounds/achievement.ogg')
__popup_stack.append(id)
@staticmethod
def lock(self, id):
achievement.clear(id)
def achievement_sortfilter(lst, sortby="A-z", filtering=None):
"""
Takes a list/iterable of achievement ids, returns a list of achievement ids
"""
if filtering == "Locked":
lst = (x for x in lst if not achievement.has(x))
elif filtering == "Unlocked":
lst = filter(achievement.has, lst)
elif filtering == "Secret":
lst = (x for x in lst if achievements_db[x].secret is True)
# Always sort alphabetically first.
lst = sorted(lst, key=lambda x: natsort_key(achievements_db[x].title))
if sortby == "z-A":
lst.sort(key=lambda x: natsort_key(achievements_db[x].title), reverse=True)
elif sortby == "Unlocked":
lst.sort(key=lambda x: not achievement.has(x))
elif sortby == "Locked":
lst.sort(key=achievement.has)
return lst
default achievements = Achievements()
###
init python:
# intentionaly not a define nor a default
__popup_stack = []
config.after_default_callbacks.append(__popup_stack.clear)
config.always_shown_screens.append("achievement_main")
screen achievement_main():
layer "interface"
vbox:
for id index id in __popup_stack[:3]:
use achievement_window(msg=achievements_db[id].title, title="Achievement unlocked!", icon=achievements_db[id].icon)
timer 6 action Function(__popup_stack.remove, id)
label popup(msg="", title="", icon=None, xpos=0, ypos=60, sound=True, soundfile='sounds/achievement.ogg'):
if sound:
play sound soundfile
hide screen achievement_window
show screen achievement_window(msg=msg, title=title, icon=icon, xpos=xpos, ypos=ypos)
return
screen achievement_window(msg="", title="", icon=None, xpos=0, ypos=60):
tag popup_window
layer "interface"
frame:
style "empty"
at popup_animation(time=5.0, xx=-410)
pos (xpos, ypos)
xsize 410
ysize 96
add gui.format("interface/achievements/{}/box.webp")
if icon:
frame:
style "empty"
pos (6, 6)
xsize 84
ysize 84
$ image_zoom = crop_image_zoom(icon, 84, 84)
if 'head' in icon:
add image_zoom align (0.5, 1.0) yoffset -1
else:
add image_zoom align (0.5, 0.5)
add "interface/achievements/glass.webp"
frame:
style "empty"
xpos 96
xsize 314
vbox:
ypos 12
spacing 10
xalign 0.5
text title size 18 xalign 0.5
text msg size 14 xalign 0.5
timer 6.0 action Hide("achievement_window")
transform rotate_circular(t=7):
on show, appear, start:
subpixel True
rotate 0
linear t rotate 360
repeat
####################################
############# Menu #################
####################################
define achievement_categories_sorted = ("All", "General", "Characters", "Cardgame", "Mirror")
define items_shown = 36
init python:
class __SetCategory(Action):
def __init__(self, category):
self.category = category
def __call__(self):
global current_category
global number_unlocked
current_category = self.category
if current_category == "All":
category_items = achievements_db
else:
category_items = filter((lambda x: current_category==achievements_db[x].category), achievements_db)
__regen(category_items)
number_unlocked = len(tuple(filter(achievement.has, menu_items)))
def __regen(category_items=achievements_db):
global menu_items
global menu_items_length
global current_page
global current_item
menu_items = achievement_sortfilter(category_items, current_sorting, current_filter)
menu_items_length = len(menu_items)
current_page = 0
current_item = next(iter(menu_items), None)
renpy.restart_interaction()
label achievement:
$ gui.in_context("achievement_menu")
jump main_room_menu
label achievement_menu(xx=150, yy=90):
$ renpy.dynamic(
current_page = 0,
current_category = achievement_categories_sorted[0],
current_filter = None,
current_sorting = "Unlocked",
menu_items = achievement_sortfilter(achievements_db, "Unlocked", None),
number_unlocked = len(tuple(filter(achievement.has, achievements_db)))
)
$ renpy.dynamic(
menu_items_length = len(menu_items),
current_item = next(iter(menu_items), None),
)
call screen achievements(xx, yy)
return
screen achievements(xx, yy):
tag achievements
zorder 15
modal True
add "gui_fade"
if renpy.mobile:
use close_button_background
use close_button
fixed:
pos (xx, yy)
if settings.get("animations"):
at gui_animation
use achievement_menu()
use achievement_menuitem()
screen achievement_menu():
window:
style "empty"
style_prefix gui.theme('achievements')
xysize (207, 454)
use invisible_button()
add gui.format("interface/achievements/{}/panel_left.webp")
vbox:
style_prefix gui.theme('achievements_categories')
pos (6, 41)
for category in achievement_categories_sorted:
vbox:
textbutton category:
selected (current_category == category)
action __SetCategory(category)
add gui.format("interface/achievements/{}/spacer_left.webp")
vbox:
style_prefix gui.theme('achievements_filters')
pos (6, 384)
if current_filter is None:
textbutton "Show: All" action [CycleVariable("current_filter", (None, "Locked", "Unlocked", "Secret")), __regen]
else:
textbutton "Show: [current_filter]" action [CycleVariable("current_filter", (None, "Locked", "Unlocked", "Secret")), __regen]
textbutton "Sort by: [current_sorting]" action [CycleVariable("current_sorting", ("A-z", "z-A", "Unlocked", "Locked")), __regen]
screen achievement_menuitem():
window:
style "empty"
pos (217, -53)
xysize (560, 507)
use invisible_button()
add "interface/achievements/star.webp"
add gui.format("interface/achievements/{}/panel.webp")
text "Achievements" size 22 xalign 0.5 ypos 65
text "Unlocked: [number_unlocked]/[menu_items_length]" size 12 pos (24, 70)
# Page counter
if menu_items_length > items_shown:
hbox:
xanchor 1.0
pos (540, 24)
spacing 5
add "interface/page.webp" yanchor 0.5 ypos 53
text str(current_page+1)+"/"+str(int(math.ceil(menu_items_length/items_shown))) ypos 44 size 16
vbox:
pos (570, 186)
spacing 10
imagebutton:
idle gui.format("interface/frames/{}/arrow_up.webp")
if not current_page <= 0:
hover image_hover(gui.format("interface/frames/{}/arrow_up.webp"))
action SetVariable("current_page", current_page-1)
# TODO: in 8.2, replace with IncrementVariable("current_page", -1)
imagebutton:
idle Transform(gui.format("interface/frames/{}/arrow_up.webp"), yzoom = -1.0)
if current_page < math.ceil((menu_items_length-1)/items_shown)-1:
hover Transform(image_hover(gui.format("interface/frames/{}/arrow_up.webp")), yzoom = -1.0)
action SetVariable("current_page", current_page+1)
# TODO: in 8.2, replace with IncrementVariable("current_page")
# Add items
grid 9 4:
style "empty"
pos (24, 113)
spacing 10
for it_item in menu_items[current_page*items_shown:(current_page+1)*items_shown]:
$ it_item_data = achievements_db[it_item]
$ it_item_unlocked = achievement.has(it_item)
frame:
style "empty"
xsize 48
ysize 48
add gui.format("interface/achievements/{}/iconbox.webp")
if current_item and current_item == it_item:
add "interface/achievements/glow.webp" align (0.5, 0.5) zoom 0.105 alpha 0.7 at rotate_circular
if it_item_unlocked or not it_item_data.secret:
$ image_zoom = crop_image_zoom(it_item_data.icon, 42, 42, not it_item_unlocked)
else:
$ image_zoom = crop_image_zoom("interface/achievements/secret.webp", 35, 35, True)
add image_zoom:
xalign .5
if it_item_data.category == "Characters" and (it_item_unlocked or not it_item_data.secret):
yalign 1.
yoffset -3
else:
yalign .5
button:
style gui.theme("overlay_button")
background "interface/achievements/glass_iconbox.webp"
xsize 48 ysize 48
action SetVariable("current_item", it_item)
if it_item_data.secret and not it_item_unlocked:
tooltip "???"
else:
tooltip it_item_data.name
if current_item:
$ current_item_data = achievements_db[current_item]
$ current_item_unlocked = achievement.has(current_item)
frame:
style "empty"
xsize 96
ysize 96
pos (24, 375)
add gui.format("interface/achievements/{}/icon_selected.webp")
if current_item_unlocked or not current_item_data.secret:
$ image_zoom = crop_image_zoom(current_item_data.icon, 84, 84, not current_item_unlocked)
else:
$ image_zoom = crop_image_zoom("interface/achievements/secret.webp", 70, 70, True)
add image_zoom:
xalign .5
if current_item_data.category == "Characters" and (current_item_unlocked or not current_item_data.secret):
yalign 1.
yoffset -7
else:
yalign .5
add "interface/achievements/glass_selected.webp" pos (6, 6)
add gui.format("interface/achievements/{}/highlight.webp") pos (112, 375)
add gui.format("interface/achievements/{}/spacer.webp") pos (120, 398)
hbox:
spacing 5
xalign 0.5
text current_item_data.name ypos 380 size 16 xoffset 45
if current_item_unlocked:
add "interface/unlocked_True.webp" xoffset 45 ypos 377
else:
add "interface/unlocked_False.webp" xoffset 45 ypos 377
hbox:
pos (132, 407)
xsize 410
if current_item_unlocked or not current_item_data.secret:
text current_item_data.description size 12
else:
text "???" size 12
# Category styles
style achievements_categories_button is empty:
xysize (195, 16)
style dark_achievements_categories_button:
hover_background "interface/achievements/gray/highlight_left.webp"
selected_background "interface/achievements/gray/highlight_left.webp"
style light_achievements_categories_button:
hover_background "interface/achievements/gold/highlight_left.webp"
selected_background "interface/achievements/gold/highlight_left.webp"
style achievements_categories_button_text is text:
xalign 0.5
outlines []
# style dark_achievements_categories_button_text:
# take dark_button_text
# style light_achievements_categories_button_text:
# take light_button_text
# Filter styles
style achievements_filters_button is empty:
xysize (195, 32)
style dark_achievements_filters_button:
hover_background "#7d75aa40"
style light_achievements_filters_button:
hover_background "#e3ba7140"
style achievements_filters_button_text is default:
align (0.5, 0.5)
size 12
outlines []