From 15bc242d2b5475c2bd236650ca41fcff878ce452 Mon Sep 17 00:00:00 2001 From: LoafyLemon Date: Sun, 13 Oct 2024 09:38:11 +0100 Subject: [PATCH] Wardrobe 2.0 Prototype --- .../creamy_pumpkin_pie/icons/accessories.png | 3 + game/gui/creamy_pumpkin_pie/icons/bottoms.png | 3 + game/gui/creamy_pumpkin_pie/icons/clothes.png | 3 + game/gui/creamy_pumpkin_pie/icons/dyes.png | 3 + game/gui/creamy_pumpkin_pie/icons/exit.png | 3 + .../gui/creamy_pumpkin_pie/icons/headgear.png | 3 + game/gui/creamy_pumpkin_pie/icons/noicon.png | 3 + game/gui/creamy_pumpkin_pie/icons/options.png | 3 + .../gui/creamy_pumpkin_pie/icons/schedule.png | 3 + game/gui/creamy_pumpkin_pie/icons/studio.png | 3 + game/gui/creamy_pumpkin_pie/icons/tops.png | 3 + .../creamy_pumpkin_pie/icons/underwear.png | 3 + game/gui/creamy_pumpkin_pie/side_frame.png | 3 + game/scripts/doll/clothes.rpy | 17 +- game/scripts/doll/outfits.rpy | 4 +- game/scripts/interface/gifts.rpy | 2 +- game/scripts/wardrobe/functions.rpy | 8 - game/scripts/wardrobe/wardrobe.rpy | 1005 ++--------------- 18 files changed, 153 insertions(+), 922 deletions(-) create mode 100644 game/gui/creamy_pumpkin_pie/icons/accessories.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/bottoms.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/clothes.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/dyes.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/exit.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/headgear.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/noicon.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/options.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/schedule.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/studio.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/tops.png create mode 100644 game/gui/creamy_pumpkin_pie/icons/underwear.png create mode 100644 game/gui/creamy_pumpkin_pie/side_frame.png diff --git a/game/gui/creamy_pumpkin_pie/icons/accessories.png b/game/gui/creamy_pumpkin_pie/icons/accessories.png new file mode 100644 index 00000000..5d7e1adb --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/accessories.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:455c9b0c6e5f95ee50347910d52ee795a3bf82135a7ad9d72adb0857de43a988 +size 34223 diff --git a/game/gui/creamy_pumpkin_pie/icons/bottoms.png b/game/gui/creamy_pumpkin_pie/icons/bottoms.png new file mode 100644 index 00000000..fc114fdc --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/bottoms.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb0831484eefa48f23942936882d96ae45439e16ed213313f52405be86d8d22f +size 38006 diff --git a/game/gui/creamy_pumpkin_pie/icons/clothes.png b/game/gui/creamy_pumpkin_pie/icons/clothes.png new file mode 100644 index 00000000..4f5a47cd --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/clothes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6784430d814b4a386cd917a395fa6f0c349aec512607e3ff896e37e94df67cfe +size 40731 diff --git a/game/gui/creamy_pumpkin_pie/icons/dyes.png b/game/gui/creamy_pumpkin_pie/icons/dyes.png new file mode 100644 index 00000000..e10fe559 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/dyes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bc1743c94ed46686e2a5136421742ab38a24a086e04ad651f11e67a7c999b04 +size 42201 diff --git a/game/gui/creamy_pumpkin_pie/icons/exit.png b/game/gui/creamy_pumpkin_pie/icons/exit.png new file mode 100644 index 00000000..dbeb66b4 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/exit.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1b7a5a8d0bd811b1c38b90c39863003a527e98104a399b4cc776ff5130ef36b +size 25080 diff --git a/game/gui/creamy_pumpkin_pie/icons/headgear.png b/game/gui/creamy_pumpkin_pie/icons/headgear.png new file mode 100644 index 00000000..26ce5ae7 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/headgear.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2a638f04812c3adfd491532cb3e702125fdec2005d23811863a84f184dedcac +size 76528 diff --git a/game/gui/creamy_pumpkin_pie/icons/noicon.png b/game/gui/creamy_pumpkin_pie/icons/noicon.png new file mode 100644 index 00000000..70ee43ed --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/noicon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f35b5fcf10976192c1814d104c8629b0a95fcff8c16f0b2f582d0f6f3bb25625 +size 18473 diff --git a/game/gui/creamy_pumpkin_pie/icons/options.png b/game/gui/creamy_pumpkin_pie/icons/options.png new file mode 100644 index 00000000..1943d739 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/options.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16d021612c07f9caca38270ffec70fda50aab974e32256e94773fd1895f714db +size 48666 diff --git a/game/gui/creamy_pumpkin_pie/icons/schedule.png b/game/gui/creamy_pumpkin_pie/icons/schedule.png new file mode 100644 index 00000000..23ffef11 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/schedule.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e5ed2843787c6658aeb1f30d65a4109acc2733cdd13cbac19f8028e6bcd7316 +size 33481 diff --git a/game/gui/creamy_pumpkin_pie/icons/studio.png b/game/gui/creamy_pumpkin_pie/icons/studio.png new file mode 100644 index 00000000..a4845133 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/studio.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42d0d6c1d0a01e18f33143ac79b26e69c277fc717a76ff6e22cff3dc58a631d6 +size 26933 diff --git a/game/gui/creamy_pumpkin_pie/icons/tops.png b/game/gui/creamy_pumpkin_pie/icons/tops.png new file mode 100644 index 00000000..415d3500 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/tops.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9888857a56315595f16d9fe111affd517f6a6cbeddbfa3227dec6dfbb3f1c47a +size 34630 diff --git a/game/gui/creamy_pumpkin_pie/icons/underwear.png b/game/gui/creamy_pumpkin_pie/icons/underwear.png new file mode 100644 index 00000000..5464dcaa --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/icons/underwear.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60b126ccc0a4d5735532f28b03ad7c233f51e20fb53dc0853ec1a6c8e34a9440 +size 55566 diff --git a/game/gui/creamy_pumpkin_pie/side_frame.png b/game/gui/creamy_pumpkin_pie/side_frame.png new file mode 100644 index 00000000..b22c4b55 --- /dev/null +++ b/game/gui/creamy_pumpkin_pie/side_frame.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10fcf99dfa9ede6076056183ab337884499325c7d6f3f461e0d96c803a918170 +size 45633 diff --git a/game/scripts/doll/clothes.rpy b/game/scripts/doll/clothes.rpy index 959bf938..92d9b399 100644 --- a/game/scripts/doll/clothes.rpy +++ b/game/scripts/doll/clothes.rpy @@ -191,7 +191,7 @@ init python: sprites.sort(key=itemgetter(2)) wmax, hmax = self.sizes - wmin = hmin = 96 + wmin = hmin = 256 x, y, w, h = crop_whitespace(bounds) xoffset, yoffset = w/2, h/2 @@ -216,7 +216,7 @@ init python: return self.build_icon(self._hash) def _build_button(self, _hash): - style = "wardrobe_button" + style = "wardrobe_item_button" is_seen = self.seen is_equipped = self.char.is_equipped_item(self) is_inadequate = bool(get_character_progression(self.char.name) < self.level) @@ -230,14 +230,12 @@ init python: blacklister = self.char.get_blacklister(self.type) # Victim (List clothing types blacklisting this item ) warnings.append("Incompatible with:{size=-4}\n" + "\n".join(set(blacklisted + blacklister)) + "{/size}") - child = Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6) + child = AlphaMask(Transform(self.icon, xysize=(96, 96)), Transform("wheelmenu_button_opaque", xysize=(96, 96))) hbox = [] overlay = [] action = [Return(["equip", self]), self.build_button] unhovered = None - foreground = None - hover_foreground = "#ffffff80" if is_inadequate: warnings.append("Character level too low") @@ -260,8 +258,8 @@ init python: unhovered = [Function(self.mark_as_seen), self.clear_button_cache, self.build_button] overlay.append(Text("NEW", align=(1.0, 1.0), offset=(-6, -6), style="wardrobe_button_text")) - if is_equipped: - overlay.append(Transform("interface/topbar/icon_check.webp", align=(1.0, 1.0), offset=(-6, -6), size=(24, 24))) + # if is_equipped: + # overlay.append(Transform("interface/topbar/icon_check.webp", align=(1.0, 1.0), offset=(-6, -6), size=(24, 24))) if hbox: overlay.append(HBox(*hbox, offset=(6, 6))) @@ -270,10 +268,9 @@ init python: child = Fixed(child, *overlay, fit_first=True) if is_inadequate: - foreground = "#b2000040" - hover_foreground = "#CD5C5C40" + style = wardrobe_item_button_inadequate - return Button(child=child, focus_mask=None, xysize=(96, 96), background=Transform(self.icon, xysize=(96, 96)), action=action, tooltip=("\n".join(warnings)), foreground=foreground, hover_foreground=hover_foreground, unhovered=unhovered, style=style) + return Button(child=child, focus_mask=None, xysize=(96, 96), action=action, tooltip=("\n".join(warnings)), unhovered=unhovered, style=style, selected=is_equipped) @functools.cache def build_button(self, _=None): diff --git a/game/scripts/doll/outfits.rpy b/game/scripts/doll/outfits.rpy index 14c45ace..384d9724 100644 --- a/game/scripts/doll/outfits.rpy +++ b/game/scripts/doll/outfits.rpy @@ -100,8 +100,6 @@ init python: return self.build_icon(self._hash) def _build_button(self, _hash, subcat): - global wardrobe_outfit_schedule - style = "wardrobe_button" # is_equipped = self.char.is_equipped_item(self) is_modded = self.is_modded() @@ -159,7 +157,7 @@ init python: # selected_foreground = "#CD5C5C40" if has_schedule: - for i in wardrobe_outfit_schedule: + for i in wardrobe.outfit_schedule: # Wardrobe store var if self.schedule[i]: vbox.append(Transform(f"interface/wardrobe/icons/outfits/{i}.webp", size=(16, 16), offset=(6, 6))) diff --git a/game/scripts/interface/gifts.rpy b/game/scripts/interface/gifts.rpy index 1f576c26..25780a7d 100644 --- a/game/scripts/interface/gifts.rpy +++ b/game/scripts/interface/gifts.rpy @@ -16,7 +16,7 @@ label gift_menu(inter_pause=True): hide screen blktone with d3 else: - $ renpy.call(get_character_gift_label(states.active_girl), _return) + $ renpy.call(f"give_{states.active_girl[:3]}_gift", _return) elif _return.type == "potion": if not states.active_girl in _return.usable_on: show screen blktone diff --git a/game/scripts/wardrobe/functions.rpy b/game/scripts/wardrobe/functions.rpy index 61a92c73..5285d730 100644 --- a/game/scripts/wardrobe/functions.rpy +++ b/game/scripts/wardrobe/functions.rpy @@ -54,14 +54,6 @@ init -1 python: __check_exists(key) return f"{key}_main" - def get_character_sayer(key): - __check_exists(key) - return getattr(store, key[:3]) - - def get_character_gift_label(key): - __check_exists(key) - return f"give_{key[:3]}_gift" - def get_character_unlock(key): __check_exists(key) return getattr(states, key[:3]).unlocked diff --git a/game/scripts/wardrobe/wardrobe.rpy b/game/scripts/wardrobe/wardrobe.rpy index 89fc8734..651867a0 100644 --- a/game/scripts/wardrobe/wardrobe.rpy +++ b/game/scripts/wardrobe/wardrobe.rpy @@ -1,12 +1,6 @@ -default wardrobe_music = False -default wardrobe_chitchats = True -default wardrobe_autosave = False -default wardrobe_suppress_warnings = False -default wardrobe_randomise_color = False -default wardrobe_global_color = False - -# Used as custom order for the sorting -define wardrobe_subcategories_sorted = { +# Defines +define wardrobe.outfit_schedule = ("day", "night", "cloudy", "rainy", "snowy") +define wardrobe.subcategories = { "hair": 5, "shirts": 5, "skirts": 5, "pantyhose": 5, "slot1": 5, "panties": 5, "save": 5, "earrings": 4, "sweaters": 4, "trousers": 4, "stockings": 4, "bikini panties": 4, "load": 4, "neckwear": 3, "dresses": 3, "shorts": 3, "socks": 3, "schedule": 3, @@ -16,919 +10,130 @@ define wardrobe_subcategories_sorted = { "other": -1, } -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") -default __lock = False -default __predicted = None +# Settings +default wardrobe.music = False +default wardrobe.chitchats = True +default wardrobe.autosave = False +default wardrobe.suppress_warnings = False +default wardrobe.randomize_color = False +default wardrobe.global_color = False -init python: - def preload_wardrobe_assets(c): - global __lock, __predicted +# Functions +init python in wardrobe: + from collections import OrderedDict - if renpy.version_tuple < (8, 2): - renpy.start_predict_screen("wardrobe") - else: - # use the new renpy.invoke_in_main_thread - renpy.invoke_in_main_thread(renpy.start_predict_screen, "wardrobe") - - # this is not thread-safe, but there's no better way : it's the slow part - d = [v[0] for i in c.wardrobe_list for v in i.get_layers(i._hash).values()] - - if renpy.version_tuple < (8, 2): - renpy.start_predict(*d, "interface/wardrobe/*.webp") - else: - renpy.invoke_in_main_thread(renpy.start_predict, *d, "interface/wardrobe/*.webp") - __predicted = d - __lock = False - - def rebuild_wardrobe_icons(items, subcat): - if not settings.get("multithreading"): - return - - if subcat == "import": - return - - for i in items.get(subcat, []): - i.build_button(subcat) - - def lock_wardrobe_icon(icon): - if not settings.get("multithreading"): - return icon - - lock = bool(DollThread._count) - - return gray_tint(icon) if lock else icon - - def randomise_wardrobe_color(cloth): - - def _func(cloth): - if not cloth is None: - if wardrobe_randomise_color and cloth.color: - col_len = len(cloth.color) - col = [] - - for i in range(col_len): - if col_len == 1: - col.append(tetriadic_colors[0]) - elif col_len == 2: - col.append(double_colors[i-1]) - elif col_len == 3: - col.append(triadic_colors[i-1]) - else: - try: - col.append(tetriadic_colors[i-1]) - except: - col.append(col[-1].rotate_hue(0.33)) - - cloth.set_color(col) - - if wardrobe_global_color: - for outfit in char_active.outfits: - rebuild = False - - for i in outfit.group: - if not (i.id, i.type) == (cloth.id, cloth.type): - continue - - if len(cloth.color) != len(i.color): - print(f"Mismatched color lens:\n{cloth}\n{i}") - renpy.notify("Error!") - continue - - i.set_color(cloth.color) - i.is_stale() - rebuild = True - - if rebuild: - outfit.is_stale() - - rebuild_wardrobe_icons(category_items, current_subcategory) - - if settings.get("multithreading"): - thread = DollThread(target=_func, args=(cloth,), interval=0.05) - thread.start() - else: - _func(cloth) - renpy.restart_interaction() - - # @functools.cache # Cache resets on wardrobe exit - def set_wardrobe_categories(current_category): - category_items = OrderedDict( + def get_subcategories(d): + return OrderedDict( sorted( [ (subcat, [item for item in items if item.unlocked]) - for subcat, items in wardrobe_subcategories.get(current_category, {}).items() + for subcat, items in subcategories.get(d, {}).items() ], - key=lambda x: wardrobe_subcategories_sorted.get(x[0], 0), + key=lambda x: subcategories.get(x[0], 0), reverse=True ) ) - return category_items -style loading_text: - color "#ffffff" - size 64 - -style loading_trivia_text: - color "#ffffff" - size 24 - -layeredimage loading: - # always "gui_fade" - always Text("Loading", style="loading_text"): - align (0.5, 0.35) - 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 - repeat - -label wardrobe: - $ renpy.call_in_new_context("_wardrobe") - return - -label _wardrobe: - $ 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) - - $ __lock = True - $ renpy.invoke_in_thread(preload_wardrobe_assets, get_character_object(states.active_girl)) - - # 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 - $ _game_menu_screen = "navigation" - $ _skipping = True - $ renpy.stop_predict(__predicted) - $ __predicted = None - $ renpy.suspend_rollback(False) - $ renpy.block_rollback() - return - -screen wardrobe(xx, yy): - tag wardrobe - zorder 24 - # add "gui_fade" - - if renpy.mobile: - use close_button_background - use close_button - - fixed: - use wardrobe_menu(xx, yy) - if current_category == "outfits": - use wardrobe_outfit_menuitem(20, 50) - elif current_subcategory is not None: - use wardrobe_menuitem(20, 50) - -label wardrobe_menu(): - $ renpy.dynamic( - char_active = get_character_object(states.active_girl), - char_outfit = get_character_outfit(states.active_girl, typ="last"), - ) - $ char_outfit.save() - - $ renpy.dynamic( - wardrobe_subcategories = char_active.wardrobe, - current_category = "head", - ) - python: - if renpy.android: - wardrobe_subcategories.update(outfits=dict.fromkeys(("load", "save", "delete", "schedule"), char_active.outfits)) - else: - wardrobe_subcategories.update(outfits=dict.fromkeys(("load", "save", "delete", "schedule", "import", "export"), char_active.outfits)) - - $ renpy.dynamic( - category_items = set_wardrobe_categories(current_category), - ) - $ renpy.dynamic( - current_subcategory = next(iter(category_items), ""), - ) - $ renpy.dynamic( - current_item = char_active.get_equipped_wardrobe_item(category_items, current_subcategory), - last_track = renpy.music.get_playing(), - ) - $ rebuild_wardrobe_icons(category_items, current_subcategory) - - if wardrobe_music: - play music "music/Spring_In_My_Step.ogg" fadein 1 if_changed - - show screen wardrobe(662, 50) - - label .after_init: - - $ renpy.hide(get_character_tag(states.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 - - $ renpy.dynamic(__choice = ui.interact()) - - if __choice[0] == "category": - if not current_category == __choice[1]: - if wardrobe_check_category(__choice[1]): - $ current_category = __choice[1] - - $ category_items = set_wardrobe_categories(current_category) - $ current_subcategory = list(category_items.keys())[0] if category_items else "" - - if current_category == "outfits": - $ char_active.clear_outfit_button_cache() - - $ current_item = char_active.get_equipped_wardrobe_item(category_items, current_subcategory) - - $ char_active.wear("all") - if current_category in ("lower undergarment", "upper undergarment"): - $ char_active.strip("top", "bottom", "robe", "accessory") - elif current_category == "piercings & tattoos": - $ char_active.strip("top", "bottom", "robe", "accessory", "bra", "panties", "stockings", "gloves") - else: - $ wardrobe_react("category_fail", __choice[1]) - - $ rebuild_wardrobe_icons(category_items, current_subcategory) - - elif __choice[0] == "subcategory": - if not current_subcategory == __choice[1]: - $ current_subcategory = __choice[1] - - if current_category == "outfits": - $ char_active.clear_outfit_button_cache() - - $ current_item = char_active.get_equipped_wardrobe_item(category_items, current_subcategory) - - $ rebuild_wardrobe_icons(category_items, current_subcategory) - - elif __choice[0] == "equip": - ### CLOTHING ### - if isinstance(__choice[1], DollCloth): - if __choice[1].type == "hair" and char_active.is_equipped_item(__choice[1]): - play sound "sounds/fail.ogg" - $ renpy.notify("Hair cannot be removed.") - else: - - if char_active.is_equipped_item(__choice[1]): - # UNEQUIP - if wardrobe_check_unequip(__choice[1]): - $ wardrobe_react("unequip", __choice[1]) - $ char_active.unequip(__choice[1]) - - if current_item: - $ current_item.clear_button_cache() - $ current_item.build_button() - - $ current_item = None - else: - $ wardrobe_react("unequip_fail", __choice[1]) - else: - # EQUIP - if wardrobe_check_equip(__choice[1]): - $ wardrobe_react("equip", __choice[1]) - - # Blacklist handling - if not wardrobe_check_blacklist(__choice[1]): - $ wardrobe_react("blacklist", __choice[1]) - - $ __choice[1].mark_as_seen() - $ char_active.equip(__choice[1]) - - if current_item: - $ current_item.clear_button_cache() - $ current_item.build_button() - - $ current_item = __choice[1] - $ current_item.clear_button_cache() - $ current_item.build_button() - - if wardrobe_fallback_required(__choice[1]): - # Has to be called regardless of player preference. - $ renpy.call(get_character_response(states.active_girl, "fallback"), __choice[1]) - else: - $ wardrobe_react("equip_fail", __choice[1]) - - ### OUTFIT ### - elif isinstance(__choice[1], DollOutfit): - $ _outfit = char_active.create_outfit(temp=True) - - if _outfit == __choice[1]: - $ renpy.notify("Load failed: Outfit already equipped.") - else: - if wardrobe_check_equip_outfit(__choice[1]): - - if not _outfit.exists(): - $ _confirmed = wardrobe_suppress_warnings or renpy.call_screen("confirm", "Discard unsaved changes and load this outfit?") - - if _confirmed: - $ wardrobe_react("equip_outfit", __choice[1]) - $ char_active.equip(__choice[1]) - $ current_item = __choice[1] - else: - $ renpy.notify("Load failed: Cancelled by user.") - else: - $ wardrobe_react("equip_outfit", __choice[1]) - $ char_active.equip(__choice[1]) - $ current_item = __choice[1] - else: - $ wardrobe_react("equip_outfit_fail", __choice[1]) - - elif __choice[0] == "setcolor": - python: - current_item.set_color(__choice[1]) - current_item.clear_button_cache() - current_item.build_button() - - 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() - - elif __choice[0] == "touch": - if wardrobe_check_touch(__choice[1]): - $ wardrobe_react("touch", __choice[1]) - else: - $ wardrobe_react("touch_fail", __choice[1]) - - elif __choice[0] == "addoutfit": - python: - _outfit = char_active.create_outfit(temp=True) - - if _outfit.exists(): - renpy.notify("Save failed: Outfit already exists.") - else: - if __choice[1]: - _index = char_active.outfits.index(__choice[1]) - _confirmed = wardrobe_suppress_warnings or renpy.call_screen("confirm", "Overwrite this outfit?") - - if _confirmed: - _old_outfit = char_active.outfits[_index] - _old_schedule = _old_outfit.schedule.copy() - - _outfit = char_active.create_outfit() - _outfit.delete() # Removes it from list only - _outfit.schedule = _old_schedule - - char_active.outfits[_index] = _outfit - _outfit.build_button(current_subcategory) - renpy.notify("Overwritten.") - else: - renpy.notify("Save failed: Cancelled by user.") - - else: - _outfit = char_active.create_outfit() - _outfit.build_button(current_subcategory) - renpy.notify("Outfit Saved.") - - current_item = char_active.get_equipped_wardrobe_item(category_items, current_subcategory) - - category_items = set_wardrobe_categories(current_category) - - elif __choice[0] == "deloutfit": - python: - _confirmed = wardrobe_suppress_warnings or renpy.call_screen("confirm", "Delete this outfit?") - - if _confirmed: - __choice[1].delete() - category_items = set_wardrobe_categories(current_category) - renpy.notify("Outfit Deleted.") - - elif __choice[0] == "export": - python: - filename = renpy.input("Save as:", datetime.datetime.now().strftime("%d %b %Y-%H%M%S")) - - if not filename.endswith(".png"): - filename += ".png" - - __choice[1].export_data(filename) - - elif __choice[0] == "import": - $ _outfit = char_active.import_outfit(__choice[1]) - $ category_items = set_wardrobe_categories(current_category) - - elif __choice[0] == "schedule": - $ renpy.call_screen("wardrobe_schedule_menuitem", __choice[1]) - - elif __choice == "music": - python: - if wardrobe_music: - wardrobe_music = False - renpy.music.play(last_track) - else: - wardrobe_music = True - renpy.music.play("music/Spring_In_My_Step.ogg", fadein=1) - - elif __choice == "randomise": - python: - _confirmed = False - - _outfit = char_active.create_outfit(temp=True) - - if not _outfit.exists(): - _confirmed = wardrobe_suppress_warnings or renpy.call_screen("confirm", "Randomise Outfit?\n{size=-6}Unsaved changes will be lost.{/size}") - - if not _confirmed: - renpy.notify("Advice: If you want to keep an outfit, save it.") - renpy.jump("wardrobe_menu.after_init") - - progress = get_character_progression(states.active_girl) - - if wardrobe_randomise_color: - # Set once per interaction - tetriadic_colors = [Color("%06x" % random.randint(0, 0xFFFFFF))] - triadic_colors = [tetriadic_colors[0].rotate_hue(0.25)] - double_colors = [tetriadic_colors[0], tetriadic_colors[0].rotate_hue(0.5)] - - for i in range(1, 3): - col = tetriadic_colors[0].rotate_hue((i * 90.0) / 360.0) - tetriadic_colors.append(col) - - col = triadic_colors[i-1].rotate_hue((i * 75.0) / 360.0) - triadic_colors.append(col) - - for k in dict(char_active.states).keys(): - valid_choices = [x for x in char_active.wardrobe_list if (istype(x, (DollCloth, DollClothDynamic, DollMakeup)) and x.type == k and x.unlocked and progress >= x.level)] - - if k == "panties": - if not progress >= get_character_requirement(states.active_girl, "category lower undergarment"): - continue - - if progress >= get_character_requirement(states.active_girl, "unequip panties"): - valid_choices += [None] - elif k == "bra": - if not progress >= get_character_requirement(states.active_girl, "category upper undergarment"): - continue - - if progress >= get_character_requirement(states.active_girl, "unequip bra"): - valid_choices += [None] - elif k == "top": - if progress >= get_character_requirement(states.active_girl, "unequip top"): - valid_choices += [None] - elif k == "bottom": - if progress >= get_character_requirement(states.active_girl, "unequip bottom"): - valid_choices += [None] - elif any(k.startswith(type) for type in ("piercing", "tattoo")): - if not progress >= get_character_requirement(states.active_girl, "category piercings & tattoos"): - continue - - valid_choices += [None] - elif k == "hair": - pass - elif k in char_active.body_layers: - pass - else: - valid_choices += [None] - - if valid_choices: - cloth = random.choice(valid_choices) - - if cloth: - randomise_wardrobe_color(cloth) - char_active.equip(cloth) - else: - char_active.unequip(k) - - if current_item: - current_item.clear_button_cache() - current_item.build_button(current_subcategory) - - current_item = char_active.get_equipped_wardrobe_item(category_items, current_subcategory) - - if current_item: - current_item.clear_button_cache() - current_item.build_button(current_subcategory) - - else: #__choice == "Close": - python: - _confirmed = False - - if wardrobe_autosave: - _outfit = char_active.create_outfit() - else: - _outfit = char_active.create_outfit(temp=True) - - if not _outfit.exists(): - renpy.notify("Advice: If you want to keep an outfit, save it.") - _confirmed = wardrobe_suppress_warnings or renpy.call_screen("confirm", "Exit without saving?\n{size=-6}Unsaved changes will be lost.{/size}") - - if not _confirmed: - renpy.jump("wardrobe_menu.after_init") - - char_active.equip(char_outfit) - - if wardrobe_global_color: - for cloth in char_outfit.group: - for outfit in char_active.outfits: - rebuild = False - - for i in outfit.group: - if not (i.id, i.type) == (cloth.id, cloth.type): - continue - - i.set_color(cloth.color) - i.is_stale() - rebuild = True - - if rebuild: - outfit.is_stale() - - renpy.hide_screen("wardrobe") - char_active.wear("all") - renpy.play('sounds/door2.ogg') - - if wardrobe_music: - renpy.music.play(last_track) - - DollThread.stop_all() - - enable_game_menu() - renpy.return_statement() - - jump .after_init - -screen wardrobe_menu(xx, yy): - tag wardrobe - zorder 24 + def get_icon(category): + fp = f"gui/creamy_pumpkin_pie/icons/{category}.png" + if renpy.loadable(fp): + return renpy.store.Image(fp, oversample=4) + return renpy.store.Image("gui/creamy_pumpkin_pie/icons/noicon.png", oversample=4) + +label wardrobe(inter_pause=True): + $ disable_game_menu() + if inter_pause: + # Ensures all irrelevant screens are hidden before capturing the surface tree + with Pause(0.2) + call screen wardrobe + $ enable_game_menu() + jump main_room_menu + +screen wardrobe(): + modal True + layer "interface" + zorder 0 style_prefix "wardrobe" - default icon_bg = Frame(gui.format("interface/frames/{}/iconmed.webp"), 6, 6) - default icon_frame = Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6) - default panel = gui.format("interface/frames/{}/wardrobe.webp") + 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 - window: - pos (xx, yy) - xysize (344, 507) - #background panel + default character = get_character_object(states.active_girl) + default selected_category = None + default selected_subcategory = None + # default menu_items = inventory.get_instances_of_type(category) + # default selected_item = None - use invisible_button() + add last_frame at navigation_last_frame_atl - # Main Categories - grid 2 4: - ypos 108 - xoffset -36 - xspacing 200 + 72 - yspacing 18 + add "gui_fade_both" at gui_fade - for i, category in enumerate(wardrobe_categories): - button: - focus_mask None - xysize (72, 72) - background lock_wardrobe_icon(Fixed(icon_bg, Transform(f"interface/wardrobe/icons/categories/{states.active_girl}/{category}.webp", zoom=0.45, anchor=(0.5, 0.5), align=(0.5, 0.5), matrixcolor=(IdentityMatrix() if wardrobe_check_category(category) else SaturationMatrix(0.0))), icon_frame)) - activate_sound "sounds/scroll.ogg" - tooltip category - sensitive (not bool(DollThread._count)) - action Return(["category", category]) - if current_category == category: - xoffset (-18 if (i % 2) == 0 else 18) + if navigation_exit: + timer 0.4 action Return() - # Outfits and Studio - hbox: - $ icon_yoffset = -18 + frame: + at navigation_atl - pos (92, 18) - spacing 18 - # Outfits Manager - button: - focus_mask None - xysize (72, 72) - background lock_wardrobe_icon(Fixed(icon_bg, Transform("interface/wardrobe/icons/categories/outfits.webp", zoom=0.45, anchor=(0.5, 0.5), align=(0.5, 0.5)), icon_frame)) - tooltip "Outfits Manager" - sensitive (not bool(DollThread._count)) - action Return(["category", "outfits"]) - if current_category == "outfits": - yoffset icon_yoffset - - # Studio - if not renpy.android: - button: - focus_mask None - xysize (72, 72) - background Fixed(icon_bg, Transform("interface/wardrobe/icons/categories/studio.webp", zoom=0.45, anchor=(0.5, 0.5), align=(0.5, 0.5)), icon_frame) - tooltip "Photo Studio" - action Function(renpy.call_in_new_context, "studio", states.active_girl) - - add panel - - # Character image cut to the size of the wardrobe - add char_active.image: - yoffset -6 - corner1 (184, 218) - corner2 (924, 1200) - zoom 0.45 - align (0.5, 1.0) - - # 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"]) - button style "empty" xysize (120, 60) xalign 0.525 ypos 360 action Return(["touch", "vagina"]) - - button: - focus_mask None - xysize (72, 72) - align (0.0, 1.0) - offset (10, -10) - background lock_wardrobe_icon(Transform("interface/wardrobe/icons/random.webp", size=(72,72))) - tooltip "Randomise Outfit" - sensitive (not DollThread._count) - action Return("randomise") - - use dropdown_menu(name="Options", pos=(12, 56)): - textbutton "Music": - style gui.theme("dropdown") - tooltip "My immortal." - selected wardrobe_music - action Return("music") - textbutton "Chit-chats": - style gui.theme("dropdown") - tooltip "{color=#35aae2}[states.active_girl]{/color} will make comments regarding your poor fashion tastes." - action ToggleVariable("wardrobe_chitchats", True, False) - textbutton "Outfits Scheduling": - style gui.theme("dropdown") - tooltip "{color=#35aae2}[states.active_girl]{/color} will automatically wear outfits\nbased on set schedule, time of day and weather." - action [ToggleVariable(f"states.{states.active_girl[:3]}.wardrobe_scheduling", True, False), If((current_category == "outfits" and current_subcategory == "schedule"), Return(["subcategory", "save"]))] - textbutton "Outfits Autosave": - style gui.theme("dropdown") - tooltip "Outfits will be automatically saved upon exit." - action ToggleVariable("wardrobe_autosave", True, False) - textbutton "Colour Synchronisation": - style gui.theme("dropdown") - tooltip "When changing colours of an item, apply it to all outfits with the same item." - action ToggleVariable("wardrobe_global_color", True, False) - textbutton "Colour Randomisation": - style gui.theme("dropdown") - tooltip "When randomising outfits, randomise colours as well." - action ToggleVariable("wardrobe_randomise_color", True, False) - textbutton "Prompts Suppression": - style gui.theme("dropdown") - tooltip "Disables warnings and prompts asking you to confirm certain actions. (Not recommended)" - action ToggleVariable("wardrobe_suppress_warnings", True, False) - -screen wardrobe_menuitem(xx, yy): - tag wardrobe_menuitem - zorder 24 - style_prefix "wardrobe" - - default icon_size = (96, 96) - default icon_frame = Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6) - default icon_transparent = Frame("interface/color_picker/checker.webp", tile=True) - default panel = gui.format("interface/frames/{}/panel.webp") - - window: - pos (xx, yy) - xysize (560, 454) - background panel - - use invisible_button() - - text "[current_category]" size 22 xalign 0.5 ypos 65 - - # Colours - if current_item and current_item.color: + vbox: hbox: - spacing 2 - xanchor 1.0 - pos (552, 61) - - # Colour picker - button: - xysize (32, 32) - tooltip "Dye clothing" - action Return(["setcolor", 0]) - add "interface/wardrobe/icons/brush.webp": - xysize (32, 32) - - # Subcategory icons - hbox: - spacing 5 - pos (12, 108) - - for subcategory in category_items.keys(): - $ icon = lock_wardrobe_icon(f"interface/wardrobe/icons/{subcategory}.webp") - - button: - focus_mask None - xysize (72, 72) - background Transform(icon, size=(72, 72), fit="contain", alpha=0.65) - selected_background Transform(icon, size=(72, 72), fit="contain", ) - selected (subcategory == current_subcategory) - tooltip subcategory - sensitive (not bool(DollThread._count)) - action Return(["subcategory", subcategory]) - - # # Item icons - # if not menu_items: - # text "Nothing here yet" size 24 align (0.5, 0.6) - # else: - vpgrid: - cols 5 - spacing 5 - pos (28, 192) - xysize (507, 308) - mousewheel True - scrollbars "vertical" - - style_prefix gui.theme("wardrobe") - - for item in category_items.get(current_subcategory, []): - add item.button - -screen wardrobe_outfit_menuitem(xx, yy): - tag wardrobe_menuitem - zorder 24 - style_prefix "wardrobe" - - default icon_size = (96, 168) - default icon_frame = Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6) - default panel = gui.format("interface/frames/{}/panel.webp") - - window: - pos (xx, yy) - xysize (560, 454) - background panel - - use invisible_button() - - text "[current_category]" size 22 xalign 0.5 ypos 65 - - # Subcategory icons - hbox: - spacing 5 - pos (8, 108) - - for subcategory in category_items.keys(): - $ icon = lock_wardrobe_icon(f"interface/wardrobe/icons/{subcategory}.webp") - $ action = Return(["subcategory", subcategory]) - - if subcategory == "schedule" and not get_character_scheduling(states.active_girl): - $ icon = gray_tint(icon) - $ action = Confirm("Outfit scheduling is currently disabled,\nwould you like to turn it on?", [SetVariable(f"states.{states.active_girl[:3]}.wardrobe_scheduling", True), Return(["subcategory", subcategory])]) - - button: - focus_mask None - xysize (72, 72) - background Transform(icon, alpha=0.65, xsize=72, fit="contain") - selected_background Transform(icon, xsize=72, fit="contain") - selected (subcategory == current_subcategory) - sensitive (not bool(DollThread._count)) - tooltip subcategory - action action - - # Outfit icons - vpgrid: - cols 5 - spacing 5 - pos (28, 192) - xysize (507, 308) - - # if renpy.android: - # mousewheel "horizontal" - # scrollbars "horizontal" - # else: - mousewheel True - scrollbars "vertical" - style_prefix gui.theme("wardrobe") - - # Add empty slot - if current_subcategory == "save": - textbutton "Save": - focus_mask None - xysize icon_size - insensitive_background "#0000001A" - idle_background "#00000033" - text_align (0.5, 0.5) - sensitive (not bool(DollThread._count)) - action Return(["addoutfit", None]) - - if current_subcategory == "import": - for item in list_outfit_files(): - add item - else: - - for item in reversed(category_items.get(current_subcategory, [])): - add item.button - -screen wardrobe_schedule_menuitem(item): - tag dropdown - zorder 24 - - default mpos = renpy.get_mouse_pos() - - use invisible_button(action=Return(), alternate=Show("wardrobe_schedule_menuitem", item=item)) - - window: - style "empty" - pos mpos - #use invisible_button(action=NullAction(), alternate=Return()) - - frame: - style "empty" - background "#00000080" - padding (5, 5, 5, 5) - - vbox: spacing 0 - for i in wardrobe_outfit_schedule: - $ boolean = "" if item.schedule[i] else "Not " - $ caption = f"{boolean}worn during the {i}" if i in ("day", "night") else f"{boolean}worn in {i} weather" - textbutton i: - style gui.theme("dropdown") - tooltip caption - action [ToggleDict(item.schedule, i, True, False), item.clear_button_cache, Function(item.build_button, current_subcategory)] + for category in character.wardrobe: + if category == "hidden": + continue -style wardrobe_window is empty + button: + add wardrobe.get_icon(category) xysize (64, 64) + tooltip "[category]" + action [SetScreenVariable("selected_category", category), SetScreenVariable("selected_subcategory", None)] -style wardrobe_button is empty: - foreground None - hover_foreground "#ffffff80" - activate_sound "sounds/qubodup-click1.ogg" + null height 5 + add "frame_spacer" xsize 500 xalign 0.5 + null height 5 + + if selected_category: + hbox: + for subcategory in character.wardrobe[selected_category]: + button: + add wardrobe.get_icon(subcategory) xysize (64, 64) + tooltip "[subcategory]" + action SetScreenVariable("selected_subcategory", subcategory) + + null height 5 + add "frame_spacer" xsize 500 xalign 0.5 + null height 5 + + if selected_subcategory: + vpgrid: + cols 5 + ysize 399 + xspacing 5 + yspacing 5 + mousewheel True + scrollbars "vertical" + for item in character.wardrobe[selected_category][selected_subcategory]: + add item.button + add character.image align (1.0, 1.0) zoom 0.6 + textbutton "Return" action [SetScreenVariable("navigation_last_frame_atl", navigation_last_frame_hide), SetScreenVariable("navigation_atl", navigation_hide), SetScreenVariable("navigation_exit", True)] keysym "game_menu" align (1.0, 0) + +style wardrobe_item_button is empty: + background Transform("wheelmenu_button", xysize=(96,96)) + hover_background At(Transform("wheelmenu_button_opaque", xysize=(96,96)), wheelmenu_hover_anim) + selected_foreground Transform("#ffffff", alpha=0.5) hover_sound "sounds/qubodup-hover1.ogg" + activate_sound "sounds/qubodup-click2.ogg" + # anchor (0.5, 0.5) -style wardrobe_button_text: - color "#fff" - insensitive_color "#808080" - size 20 - outlines [ (2, "#000", 0, 0) ] +style wardrobe_item_button_inadequate is empty: + background Transform("wheelmenu_button", xysize=(64,64)) + hover_background At(Transform("wheelmenu_button_opaque", xysize=(64,64), matrixcolor=TintMatrix("#ff0000")), wheelmenu_hover_anim) + hover_sound "sounds/qubodup-hover1.ogg" + activate_sound "sounds/qubodup-click2.ogg" + # anchor (0.5, 0.5) -style wardrobe_item_caption: - color "#fff" - size 14 - outlines [ (1, "#000", 0, 0) ] - -style dark_wardrobe_window is wardrobe_window -style dark_wardrobe_button is wardrobe_button -style darK_wardrobe_button_text is wardrobe_button_text -style dark_wardrobe_item_caption is wardrobe_item_caption -style dark_wardrobe_vscrollbar is dark_vscrollbar - -style light_wardrobe_window is wardrobe_window -style light_wardrobe_button is wardrobe_button -style light_wardrobe_button_text is wardrobe_button_text -style light_wardrobe_item_caption is wardrobe_item_caption -style light_wardrobe_vscrollbar is light_vscrollbar +style wardrobe_frame is empty: + xsize 540 + yfill True + padding (10, 10) + background Frame(Image("gui/creamy_pumpkin_pie/side_frame.png", oversample=4), 0, 100, 9, 75, tile=False)