From 352091267c41f9588f640bc4a10925168b4275a6 Mon Sep 17 00:00:00 2001 From: LoafyLemon Date: Thu, 12 Sep 2024 16:16:41 +0100 Subject: [PATCH] Reimplement Achievements interface --- game/scripts/interface/achievements.rpy | 404 ++++++------------------ game/scripts/rooms/main_room/init.rpy | 3 +- game/scripts/utility/updater.rpy | 9 + 3 files changed, 108 insertions(+), 308 deletions(-) diff --git a/game/scripts/interface/achievements.rpy b/game/scripts/interface/achievements.rpy index 350d5538..31b4cfd6 100644 --- a/game/scripts/interface/achievements.rpy +++ b/game/scripts/interface/achievements.rpy @@ -48,71 +48,6 @@ 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)) - - status = staticmethod(achievement.has) - - @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) - - lock = staticmethod(achievement.clear) - - 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 = [] @@ -179,271 +114,126 @@ transform rotate_circular(t=7): linear t rotate 360 repeat -#################################### -############# Menu ################# -#################################### +init python in achievements: + def validate(): + """Check if icons are loadable at init""" + for i in renpy.store.achievements_db.values(): + if not renpy.loadable(i.icon): + raise IOError(repr(i.icon)) -define achievement_categories_sorted = ("All", "General", "Characters", "Cardgame", "Mirror") -define items_shown = 36 + validate() -init python: - class __SetCategory(Action): - def __init__(self, category): - self.category = category + def unlock(id, silent=False): + if renpy.store._in_replay: + return - def __call__(self): - global current_category - global number_unlocked + if not renpy.store.achievement.has(id): + renpy.store.achievement.grant(id) - 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))) + if not silent: + renpy.play('sounds/achievement.ogg') + renpy.store.__popup_stack.append(id) - def __regen(category_items=achievements_db): - global menu_items - global menu_items_length - global current_page - global current_item + status = renpy.store.achievement.has + lock = renpy.store.achievement.clear - 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() + def get_list(category=None): + if category: + items = filter((lambda x: category==renpy.store.achievements_db[x].category.lower()), renpy.store.achievements_db) + else: + items = renpy.store.achievements_db + items = sorted(items, key=lambda x: renpy.store.natsort_key(renpy.store.achievements_db[x].title)) + return items -label achievement: - $ gui.in_context("achievement_menu") +label achievements: + $ disable_game_menu() + call screen achievements + $ enable_game_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 +screen achievements(): modal True + layer "interface" + zorder 0 + style_prefix "achievements" - # add "gui_fade" + default navigation_atl = navigation_show + default last_frame = (screenshot.capture() or screenshot.image) + default navigation_last_frame_atl = navigation_last_frame_show + default navigation_exit = False - if renpy.mobile: - use close_button_background + default category = None + default menu_items = achievements.get_list(category) - use close_button + add last_frame at navigation_last_frame_atl - fixed: - pos (xx, yy) - if settings.get("animations"): - at gui_animation - use achievement_menu() - use achievement_menuitem() + if navigation_exit: + timer 0.4 action Return() -screen achievement_menu(): - window: + $ print(menu_items) + + frame: style "empty" - style_prefix gui.theme('achievements') - xysize (207, 454) - - use invisible_button() - - add gui.format("interface/achievements/{}/panel_left.webp") + align (0.5, 0.5) + at navigation_atl 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) + style_prefix "navigation_subtabs" + pos (286, 170) - 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] + at navigation_subtabs_show -screen achievement_menuitem(): - window: - style "empty" - pos (217, -53) - xysize (560, 507) + textbutton _("All") action [SetScreenVariable("category", None), SetScreenVariable("menu_items", achievements.get_list())] selected (category==None) at navigation_tabs + textbutton _("General") action [SetScreenVariable("category", "general"), SetScreenVariable("menu_items", achievements.get_list("general"))] selected (category=="general") at navigation_tabs + textbutton _("Characters") action [SetScreenVariable("category", "characters"), SetScreenVariable("menu_items", achievements.get_list("characters"))] selected (category=="characters") at navigation_tabs + textbutton _("Cards") action [SetScreenVariable("category", "cardgame"), SetScreenVariable("menu_items", achievements.get_list("cardgame"))] selected (category=="cardgame") at navigation_tabs + textbutton _("Mirror of Erised") action [SetScreenVariable("category", "mirror"), SetScreenVariable("menu_items", achievements.get_list("mirror"))] selected (category=="mirror") at navigation_tabs + null height 35 + textbutton _("Return") action [SetScreenVariable("navigation_last_frame_atl", navigation_last_frame_hide), SetScreenVariable("navigation_atl", navigation_hide), SetScreenVariable("navigation_exit", True)] keysym ["game_menu"] at navigation_tabs - use invisible_button() + frame: + label _("Achievements") - 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 + # Add items + viewport: + ypos 32 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 + ymaximum 392 + scrollbars "vertical" + mousewheel True + vbox: + for item in menu_items: + $ item_unlocked = achievement.has(item) + $ item_data = achievements_db[item] + $ item_color = "#ffffff" if item_unlocked else "#8d8d8d" + fixed: + fit_first True + button: + hbox: + add crop_image_zoom(item_data.icon, 48, 48, not item_unlocked) xysize (48, 48) + null width 5 + vbox: + text "[item_data.title]" color item_color + text "[item_data.description]" color item_color style "achievements_text_small" + action NullAction() + if item_unlocked: + add "interface/topbar/icon_check.webp" xysize (24, 24) yalign 0.5 + add "frame_spacer" xalign 0.5 xsize 500 -# Category styles -style achievements_categories_button is empty: - xysize (195, 16) +style achievements_vbox is frame_vbox: + ypos 0 + yspacing 0 + xalign 0 -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) +style achievements_frame is frame +style achievements_label is frame_label +style achievements_label_text is frame_label_text +style achievements_text is frame_text: + xalign 0 +style achievements_text_small is achievements_text: + xalign 0 size 12 - outlines [] +style achievements_button is frame_button: + xfill True + yminimum 48 +style achievements_button_text is frame_button_text diff --git a/game/scripts/rooms/main_room/init.rpy b/game/scripts/rooms/main_room/init.rpy index 8bd832ef..36c94bf4 100644 --- a/game/scripts/rooms/main_room/init.rpy +++ b/game/scripts/rooms/main_room/init.rpy @@ -59,6 +59,7 @@ default desk_OBJ = RoomObject( "Do Paperwork": (Text("📝", align=(0.5, 0.5)), Jump("paperwork"), "states.paperwork_unlocked"), "Open Deck Builder": (Text("🃏", align=(0.5, 0.5)), Jump("deck_builder"), "states.cardgame.unlocked"), "Open Cheats Menu": (Text("🕹ī¸", align=(0.5, 0.5)), Jump("cheats"), "states.env.cheats"), + "Open Achievements Menu": (Text("⭐", align=(0.5, 0.5)), Jump("achievements"), "True"), }, hovered=Show( "gui_tooltip", @@ -136,4 +137,4 @@ label main_room_menu: if states.env.daytime: jump day_resume else: - jump night_resume \ No newline at end of file + jump night_resume diff --git a/game/scripts/utility/updater.rpy b/game/scripts/utility/updater.rpy index 0b79c59d..ecb00e90 100644 --- a/game/scripts/utility/updater.rpy +++ b/game/scripts/utility/updater.rpy @@ -1,3 +1,12 @@ +init python hide: + # 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: import requests