From 98ba4d03a225feffa8143e044aa4fa0b1250207c Mon Sep 17 00:00:00 2001 From: LoafyLemon Date: Fri, 24 Mar 2023 18:04:49 +0000 Subject: [PATCH] Wardrobe improvements * Added thread-assisted assets preloading * Added loading displayable * Added rollback and skipping support to wardrobe reactions * Improved wardrobe performance * Improved wardrobe reactions compatibility with Renpy scope --- game/scripts/wardrobe/reactions.rpy | 8 + game/scripts/wardrobe/studio.rpy | 2 + game/scripts/wardrobe/wardrobe.rpy | 362 ++++++++++++++++------------ 3 files changed, 223 insertions(+), 149 deletions(-) diff --git a/game/scripts/wardrobe/reactions.rpy b/game/scripts/wardrobe/reactions.rpy index bd931acc..5199976a 100644 --- a/game/scripts/wardrobe/reactions.rpy +++ b/game/scripts/wardrobe/reactions.rpy @@ -64,6 +64,14 @@ init python: return not (flag >= req) def wardrobe_react(what, arg): + global _skipping if wardrobe_chitchats: + _skipping = True + renpy.suspend_rollback(False) + renpy.hide_screen("wardrobe") + renpy.hide_screen("wardrobe_menuitem") + renpy.hide_screen("wardrobe_outfit_menuitem") + renpy.show("gui_fade", zorder=10, behind=get_character_tag(active_girl)) + renpy.block_rollback() renpy.call(get_character_response(active_girl, what), arg) return diff --git a/game/scripts/wardrobe/studio.rpy b/game/scripts/wardrobe/studio.rpy index c7d695b0..e1f5ec7b 100644 --- a/game/scripts/wardrobe/studio.rpy +++ b/game/scripts/wardrobe/studio.rpy @@ -164,6 +164,8 @@ label studio(char): # TODO: Finish adding presets saving. # Add character drag offset based on zoom. + hide screen wardrobe + python: last_char = char_active last_girl = active_girl diff --git a/game/scripts/wardrobe/wardrobe.rpy b/game/scripts/wardrobe/wardrobe.rpy index 4d084427..442b40f1 100644 --- a/game/scripts/wardrobe/wardrobe.rpy +++ b/game/scripts/wardrobe/wardrobe.rpy @@ -2,7 +2,6 @@ default wardrobe_music = False default wardrobe_chitchats = True default wardrobe_autosave = False -default wardrobe_loaded = False default wardrobe_suppress_warnings = False default wardrobe_randomise_color = False default wardrobe_global_color = False @@ -21,16 +20,112 @@ define wardrobe_subcategories_sorted = { define wardrobe_categories = ("head", "piercings & tattoos", "upper body", "upper undergarment", "lower body", "lower undergarment", "legwear", "misc") define wardrobe_outfit_schedule = ("day", "night", "cloudy", "rainy", "snowy") +init python: + def preload_wardrobe_assets(): + global _lock, _predicted + _lock = True + renpy.start_predict_screen("wardrobe") + c = get_character_object(active_girl) + d = [v[0] for i in c.wardrobe_list for v in i.get_layers(i._hash).values()] + renpy.start_predict(*d, "interface/wardrobe/*.webp") + _predicted = d + _lock = False + + _lock = False + +style loading_text: + color "#ffffff" + size 64 + +style loading_trivia_text: + color "#ffffff" + size 24 + +layeredimage loading: + always "gui_fade" + always Transform(Text("Loading", style="loading_text"), align=(0.5, 0.4)) + always "loading_spinner" + +image loading_spinner: + align (0.5, 0.5) + + Text("{unicode}╞▰═════════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═▰════════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞══▰═══════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═══▰══════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞════▰═════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═════▰════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞══════▰═══╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═══════▰══╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞════════▰═╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═════════▰╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═════════▰╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞════════▰═╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═══════▰══╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞══════▰═══╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═════▰════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞════▰═════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═══▰══════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞══▰═══════╡{/unicode}", style="loading_text") + pause 0.1 + Text("{unicode}╞═▰════════╡{/unicode}", style="loading_text") + pause 0.1 + repeat + label wardrobe: - $ gui.in_context("wardrobe_menu") + $ renpy.config.skipping = None + $ _game_menu_screen = None + $ _skipping = False + $ renpy.suspend_rollback(True) + show loading zorder 1000 + + # Ensure there's no thread in use before assigning a new one. + while _lock: + $ renpy.pause(0.5, hard=True) + + $ renpy.invoke_in_thread(preload_wardrobe_assets) + + # Await thread return + # Note: renpy.pause must be called from within the main thread + while _lock: + $ renpy.pause(0.5, predict=True) + + hide loading + + # $ renpy.scene("screens") + # show expression screenshot.image + call wardrobe_menu + + show screen main_room + show screen ui_top_bar + $ _game_menu_screen = "save" + $ _skipping = True + $ renpy.stop_predict(_predicted) + $ del _predicted + $ renpy.suspend_rollback(False) + $ renpy.block_rollback() return screen wardrobe(xx, yy): tag wardrobe zorder 24 - modal True - predict False - add "gui_fade" if renpy.mobile: @@ -50,6 +145,7 @@ screen wardrobe(xx, yy): label wardrobe_menu(): python: + char_active = get_character_object(active_girl) char_outfit = get_character_outfit(active_girl, type="last") char_outfit.save() @@ -66,17 +162,25 @@ label wardrobe_menu(): category_items = OrderedDict(sorted(iter(list(wardrobe_subcategories.get(current_category, {}).items())), key=lambda x: wardrobe_subcategories_sorted.get(x[0], 0), reverse=True)) current_subcategory = list(category_items.keys())[0] if category_items else "" menu_items = [x for x in category_items.get(current_subcategory, []) if x.unlocked==True] + icon_items = [Transform(x.icon, size=(96, 96), fit="contain", anchor=(0.5, 0.5), align=(0.5, 0.5)) for x in menu_items] current_item = char_active.get_equipped_item(menu_items) last_track = renpy.music.get_playing() if wardrobe_music: play music "music/Spring_In_My_Step.ogg" fadein 1 if_changed - show screen wardrobe(662, 50) - label .after_init: - # Not to self: Do not use a python: block, because + hide gui_fade + show screen wardrobe(662, 50) + $ renpy.hide(get_character_tag(active_girl)) + $ renpy.config.skipping = None + $ _game_menu_screen = None + $ _skipping = False + $ renpy.suspend_rollback(True) + $ renpy.block_rollback() + + # Note to self: Do not use a python: block, because # renpy cannot return to the middle of a python block # while mixing python and renpy scope # https://github.com/renpy/renpy/issues/959 @@ -86,17 +190,19 @@ label wardrobe_menu(): if _choice[0] == "category": if not current_category == _choice[1]: if wardrobe_check_category(_choice[1]): - $ wardrobe_loaded = False $ current_category = _choice[1] $ category_items = OrderedDict(sorted(iter(wardrobe_subcategories.get(current_category, {}).items()), key=lambda x: wardrobe_subcategories_sorted.get(x[0], 0), reverse=True)) $ current_subcategory = list(category_items.keys())[0] if category_items else "" - $ menu_items = [x for x in category_items.get(current_subcategory, []) if x.unlocked==True] if current_category == "outfits": $ _outfit = char_active.create_outfit(temp=True) + $ menu_items = [x for x in reversed(category_items.get(current_subcategory, [])) if x.unlocked==True] + $ icon_items = [Transform(x.image, crop=(220, 0, 680, 1200), size=(96, 168), fit="contain", anchor=(0.5, 1.0), align=(0.5, 1.0), yoffset=-6) for x in menu_items] $ current_item = next( (x for x in char_active.outfits if _outfit == x), None) else: + $ menu_items = [x for x in category_items.get(current_subcategory, []) if x.unlocked==True] + $ icon_items = [Transform(x.icon, size=(96, 96), fit="contain", anchor=(0.5, 0.5), align=(0.5, 0.5)) for x in menu_items] $ current_item = char_active.get_equipped_item(menu_items) $ char_active.wear("all") @@ -109,18 +215,21 @@ label wardrobe_menu(): elif _choice[0] == "subcategory": if not current_subcategory == _choice[1]: - $ wardrobe_loaded = False $ current_subcategory = _choice[1] - if current_subcategory == "import": - $ menu_items = list_outfit_files() - else: - $ menu_items = [x for x in category_items.get(current_subcategory) if x.unlocked==True] - if current_category == "outfits": $ _outfit = char_active.create_outfit(temp=True) $ current_item = next( (x for x in char_active.outfits if _outfit == x), None) + + if current_subcategory == "import": + $ menu_items = list_outfit_files() + $ icon_items = [Transform(f"outfits/{x}", size=(96, 168), fit="contain", anchor=(0.5, 1.0), align=(0.5, 1.0), yoffset=-6) for x in menu_items] + else: + $ menu_items = [x for x in reversed(category_items.get(current_subcategory)) if x.unlocked==True] + $ icon_items = [Transform(x.image, crop=(220, 0, 680, 1200), size=(96, 168), fit="contain", anchor=(0.5, 1.0), align=(0.5, 1.0), yoffset=-6) for x in menu_items] else: + $ menu_items = [x for x in category_items.get(current_subcategory) if x.unlocked==True] + $ icon_items = [Transform(x.icon, size=(96, 96), fit="contain", anchor=(0.5, 0.5), align=(0.5, 0.5)) for x in menu_items] $ current_item = char_active.get_equipped_item(menu_items) elif _choice[0] == "equip": @@ -200,24 +309,8 @@ label wardrobe_menu(): if rebuild: outfit.is_stale() - elif _choice[0] == "resetcolor": - python: - current_item.reset_color() - - if wardrobe_global_color: - for outfit in char_active.outfits: - rebuild = False - - for i in outfit.group: - if not i.id == current_item.id: - continue - - i.set_color(current_item.color) - i.is_stale() - rebuild = True - - if rebuild: - outfit.is_stale() + menu_items = [x for x in category_items.get(current_subcategory) if x.unlocked==True] + icon_items = [Transform(x.icon, size=(96, 96), fit="contain", anchor=(0.5, 0.5), align=(0.5, 0.5)) for x in menu_items] elif _choice[0] == "touch": if wardrobe_check_touch(_choice[1]): @@ -253,7 +346,8 @@ label wardrobe_menu(): char_active.create_outfit() renpy.notify("Outfit Saved.") - menu_items = [x for x in category_items.get(current_subcategory) if x.unlocked==True] + menu_items = [x for x in reversed(category_items.get(current_subcategory)) if x.unlocked==True] + icon_items = [Transform(x.image, crop=(220, 0, 680, 1200), size=(96, 168), fit="contain", anchor=(0.5, 1.0), align=(0.5, 1.0), yoffset=-6) for x in menu_items] current_item = next( (x for x in char_active.outfits if _outfit == x), None) elif _choice[0] == "deloutfit": @@ -262,7 +356,8 @@ label wardrobe_menu(): if _confirmed: _choice[1].delete() - menu_items = [x for x in category_items.get(current_subcategory) if x.unlocked==True] + menu_items = [x for x in reversed(category_items.get(current_subcategory)) if x.unlocked==True] + icon_items = [Transform(x.image, crop=(220, 0, 680, 1200), size=(96, 168), fit="contain", anchor=(0.5, 1.0), align=(0.5, 1.0), yoffset=-6) for x in menu_items] renpy.notify("Outfit Deleted.") elif _choice[0] == "export": @@ -442,7 +537,6 @@ label wardrobe_menu(): if wardrobe_music: renpy.music.play(last_track) enable_game_menu() - wardrobe_loaded = False renpy.return_statement() jump .after_init @@ -451,8 +545,6 @@ screen wardrobe_menu(xx, yy): tag wardrobe zorder 24 style_prefix "wardrobe" - predict False - modal True default icon_bg = Frame(gui.format("interface/frames/{}/iconmed.webp"), 6, 6) default icon_frame = Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6) @@ -525,10 +617,6 @@ screen wardrobe_menu(xx, yy): anchor (0.5, 1.0) align (0.5, 1.0) - at transform: - mesh True - gl_pixel_perfect True - # Easter Egg (Headpats, boobs, pussy) button style "empty" xysize (120, 80) xalign 0.525 ypos 60 action Return(["touch", "head"]) button style "empty" xysize (120, 60) xalign 0.525 ypos 238 action Return(["touch", "breasts"]) @@ -578,8 +666,6 @@ screen wardrobe_menuitem(xx, yy): tag wardrobe_menuitem zorder 24 style_prefix "wardrobe" - predict False - modal True default icon_size = (96, 96) default icon_frame = Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6) @@ -639,12 +725,7 @@ screen wardrobe_menuitem(xx, yy): mousewheel True scrollbars "vertical" - at transform: - mesh True - gl_pixel_perfect True - - for item in menu_items: - $ icon = item.icon + for item, icon in zip(menu_items, icon_items): $ is_seen = item.seen $ is_equipped = char_active.is_equipped_item(item) $ is_inadequate = bool(get_character_progression(active_girl) < item.level) @@ -672,7 +753,7 @@ screen wardrobe_menuitem(xx, yy): button: focus_mask None xysize icon_size - background Transform(icon, xsize=icon_size[0], fit="contain", anchor=(0.5, 0.5), align=(0.5, 0.5)) + background icon action Return(["equip", item]) tooltip ("\n".join(warnings)) if is_inadequate: @@ -711,7 +792,6 @@ screen wardrobe_outfit_menuitem(xx, yy): tag wardrobe_menuitem zorder 24 style_prefix "wardrobe" - predict False default icon_size = (96, 168) default icon_frame = Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6) @@ -749,118 +829,102 @@ screen wardrobe_outfit_menuitem(xx, yy): action action # Outfit icons - if not wardrobe_loaded: - text "Loading..." size 24 align (0.5, 0.6) - $ wardrobe_loaded = True - $ renpy.restart_interaction() - #timer 0.001 action SetVariable("wardrobe_loaded", True) - else: - vpgrid: - cols 5 - spacing 5 - pos (28, 192) - xysize (507, 308) + vpgrid: + cols 5 + spacing 5 + pos (28, 192) + xysize (507, 308) - # if renpy.android: - # mousewheel "horizontal" - # scrollbars "horizontal" - # else: - mousewheel True - scrollbars "vertical" + # if renpy.android: + # mousewheel "horizontal" + # scrollbars "horizontal" + # else: + mousewheel True + scrollbars "vertical" - # at transform: - # mesh True - # gl_pixel_perfect True - # events False + # Add empty slot + if current_subcategory == "save": + textbutton "Save": + focus_mask None + xysize icon_size + idle_background "#00000033" + text_align (0.5, 0.5) + action Return(["addoutfit", None]) - # Add empty slot - if current_subcategory == "save": - textbutton "Save": - focus_mask None - xysize icon_size - idle_background "#00000033" - text_align (0.5, 0.5) - action Return(["addoutfit", None]) + for item, icon in zip(menu_items, icon_items): + if current_subcategory == "import": + $ is_modded = False + $ is_equipped = False + else: + $ is_modded = item.is_modded() + $ is_equipped = bool(current_item == item) + $ is_inadequate = (current_subcategory in {"save", "load", "schedule"} and not wardrobe_check_equip_outfit(item)) - for item in reversed(menu_items): - if current_subcategory == "import": - $ icon = "/outfits/{}".format(item) - $ is_modded = False - $ is_equipped = False + $ warnings = [] + + if is_modded: + $ warnings.append("Outfit contains items from these mods:\n{size=-4}{color=#35aae2}"+ "\n".join(item.get_modname()) + "{/color}{/size}") + + $ alternate = None + if current_subcategory == "delete": + $ action = Return(["deloutfit", item]) + elif current_subcategory == "load": + $ action = Return(["equip", item]) + elif current_subcategory == "save": + $ action = Return(["addoutfit", item]) + elif current_subcategory == "import": + $ action = Return(["import", item]) + elif current_subcategory == "export": + $ action = Return(["export", item]) + elif current_subcategory == "schedule": + + if is_inadequate: + $ action = NullAction() + $ alternate = None else: - $ icon = Transform(item.image, crop=(220, 0, 680, 1200)) - $ is_modded = item.is_modded() - $ is_equipped = bool(current_item == item) - $ is_inadequate = (current_subcategory in {"save", "load", "schedule"} and not wardrobe_check_equip_outfit(item)) - $ icon = Transform(icon, size=icon_size, fit="contain", anchor=(0.5, 1.0), align=(0.5, 1.0), yoffset=-6) + $ action = Return(["schedule", item]) + $ alternate = Return(["schedule", item]) - $ warnings = [] + $ warnings = "\n".join(warnings) - if is_modded: - $ warnings.append("Outfit contains items from these mods:\n{size=-4}{color=#35aae2}"+ "\n".join(item.get_modname()) + "{/color}{/size}") + button: + focus_mask None + xysize icon_size + background icon + tooltip warnings + action action + alternate alternate + if is_inadequate: + foreground "#b2000040" + hover_foreground "#CD5C5C40" + selected_foreground "#CD5C5C40" - $ alternate = None - if current_subcategory == "delete": - $ action = Return(["deloutfit", item]) - elif current_subcategory == "load": - $ action = Return(["equip", item]) - elif current_subcategory == "save": - $ action = Return(["addoutfit", item]) - elif current_subcategory == "import": - $ action = Return(["import", item]) - elif current_subcategory == "export": - $ action = Return(["export", item]) - elif current_subcategory == "schedule": + add icon_frame - if is_inadequate: - $ action = NullAction() - $ alternate = None - else: - $ action = Return(["schedule", item]) - $ alternate = Return(["schedule", item]) + hbox: + offset (5, 5) - $ warnings = "\n".join(warnings) + if is_modded: + text "M" color "#00b200" - button: - focus_mask None - xysize icon_size - background icon - tooltip warnings - action action - alternate alternate - if is_inadequate: - foreground "#b2000040" - hover_foreground "#CD5C5C40" - selected_foreground "#CD5C5C40" + if not current_subcategory in {"import", "export"} and getattr(renpy.store, active_girl+"_outfits_schedule"): + vbox: + pos (6, 6) + spacing 1 + for i in wardrobe_outfit_schedule: + if item.schedule[i]: + add Transform("interface/wardrobe/icons/outfits/{}.webp".format(i), size=(16, 16)) - add icon_frame - - hbox: - offset (5, 5) - - if is_modded: - text "M" color "#00b200" - - if not current_subcategory in {"import", "export"} and getattr(renpy.store, active_girl+"_outfits_schedule"): - vbox: - pos (6, 6) - spacing 1 - for i in wardrobe_outfit_schedule: - if item.schedule[i]: - add Transform("interface/wardrobe/icons/outfits/{}.webp".format(i), size=(16, 16)) - - if is_equipped: - add "interface/topbar/icon_check.webp": - anchor (1.0, 1.0) - align (1.0, 1.0) - offset (-5, -5) - zoom 0.5 + if is_equipped: + add "interface/topbar/icon_check.webp": + anchor (1.0, 1.0) + align (1.0, 1.0) + offset (-5, -5) + zoom 0.5 screen wardrobe_schedule_menuitem(item): tag dropdown zorder 24 - modal True - predict False default mpos = renpy.get_mouse_pos()