LoafyLemon
98ba4d03a2
* 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
969 lines
40 KiB
Plaintext
969 lines
40 KiB
Plaintext
|
|
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 = {
|
|
"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,
|
|
"one-piece suits": 2, "import": 2,
|
|
"robes": 1, "export": 1,
|
|
"gloves": 0, "pubes": 0, "delete": 0,
|
|
"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")
|
|
|
|
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:
|
|
$ 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
|
|
add "gui_fade"
|
|
|
|
if renpy.mobile:
|
|
use close_button_background
|
|
use close_button
|
|
|
|
fixed:
|
|
# TODO: Wardrobe doesn't work well with the gui animation
|
|
# if settings.get("animations"):
|
|
# at gui_animation
|
|
|
|
use wardrobe_menu(xx, yy)
|
|
if current_category == "outfits":
|
|
use wardrobe_outfit_menuitem(20, 50)
|
|
elif current_subcategory != None:
|
|
use wardrobe_menuitem(20, 50)
|
|
|
|
label wardrobe_menu():
|
|
python:
|
|
|
|
char_active = get_character_object(active_girl)
|
|
char_outfit = get_character_outfit(active_girl, type="last")
|
|
char_outfit.save()
|
|
|
|
wardrobe_subcategories = char_active.wardrobe
|
|
|
|
if renpy.android:
|
|
wardrobe_subcategories.update( { "outfits": { k:char_active.outfits for k in {"load", "save", "delete", "schedule"} } } )
|
|
else:
|
|
wardrobe_subcategories.update( { "outfits": { k:char_active.outfits for k in {"load", "save", "delete", "schedule", "import", "export"} } } )
|
|
|
|
# Defaults
|
|
current_category = "head"
|
|
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
|
|
|
|
label .after_init:
|
|
|
|
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
|
|
|
|
$ _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 = 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 ""
|
|
|
|
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")
|
|
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])
|
|
|
|
elif _choice[0] == "subcategory":
|
|
if not current_subcategory == _choice[1]:
|
|
$ current_subcategory = _choice[1]
|
|
|
|
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":
|
|
### 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])
|
|
$ 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])
|
|
|
|
$ char_active.equip(_choice[1])
|
|
$ current_item = _choice[1]
|
|
|
|
if wardrobe_fallback_required(_choice[1]):
|
|
# Has to be called regardless of player preference.
|
|
$ renpy.call(get_character_response(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])
|
|
|
|
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]):
|
|
$ 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
|
|
renpy.notify("Overwritten.")
|
|
else:
|
|
renpy.notify("Save failed: Cancelled by user.")
|
|
|
|
else:
|
|
char_active.create_outfit()
|
|
renpy.notify("Outfit Saved.")
|
|
|
|
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":
|
|
python:
|
|
_confirmed = wardrobe_suppress_warnings or renpy.call_screen("confirm", "Delete this outfit?")
|
|
|
|
if _confirmed:
|
|
_choice[1].delete()
|
|
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":
|
|
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)
|
|
achievements.unlock("export")
|
|
|
|
elif _choice[0] == "import":
|
|
$ _outfit = char_active.import_outfit(_choice[1])
|
|
|
|
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)
|
|
get_character_sayer(active_girl)("", face="happy")
|
|
else:
|
|
wardrobe_music = True
|
|
renpy.music.play("music/Spring_In_My_Step.ogg", fadein=1)
|
|
get_character_sayer(active_girl)("", face="happy")
|
|
|
|
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(active_girl)
|
|
|
|
if wardrobe_randomise_color:
|
|
|
|
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(active_girl, "category lower undergarment"):
|
|
continue
|
|
|
|
if progress >= get_character_requirement(active_girl, "unequip panties"):
|
|
valid_choices += [None]
|
|
elif k == "bra":
|
|
if not progress >= get_character_requirement(active_girl, "category upper undergarment"):
|
|
continue
|
|
|
|
if progress >= get_character_requirement(active_girl, "unequip bra"):
|
|
valid_choices += [None]
|
|
elif k == "top":
|
|
if progress >= get_character_requirement(active_girl, "unequip top"):
|
|
valid_choices += [None]
|
|
elif k == "bottom":
|
|
if progress >= get_character_requirement(active_girl, "unequip bottom"):
|
|
valid_choices += [None]
|
|
elif any(k.startswith(type) for type in ("piercing", "tattoo")):
|
|
if not progress >= get_character_requirement(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 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()
|
|
|
|
char_active.equip(cloth)
|
|
else:
|
|
char_active.unequip(k)
|
|
|
|
|
|
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)
|
|
enable_game_menu()
|
|
renpy.return_statement()
|
|
|
|
jump .after_init
|
|
|
|
screen wardrobe_menu(xx, yy):
|
|
tag wardrobe
|
|
zorder 24
|
|
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")
|
|
|
|
window:
|
|
pos (xx, yy)
|
|
xysize (344, 507)
|
|
#background panel
|
|
|
|
use invisible_button()
|
|
|
|
# Main Categories
|
|
grid 2 4:
|
|
ypos 108
|
|
xoffset -36
|
|
xspacing 200 + 72
|
|
yspacing 18
|
|
|
|
for i, category in enumerate(wardrobe_categories):
|
|
if wardrobe_check_category(category):
|
|
$ icon = Fixed(icon_bg, Transform("interface/wardrobe/icons/categories/{}/{}.webp".format(active_girl, category), zoom=0.45, anchor=(0.5, 0.5), align=(0.5, 0.5)), icon_frame)
|
|
else:
|
|
$ icon = Fixed(icon_bg, Transform("interface/wardrobe/icons/categories/{}/{}.webp".format(active_girl, category), zoom=0.45, anchor=(0.5, 0.5), align=(0.5, 0.5), matrixcolor=SaturationMatrix(0.0)), icon_frame)
|
|
$ icon_xoffset = -18 if (i % 2) == 0 else 18
|
|
|
|
button:
|
|
focus_mask None
|
|
xysize (72, 72)
|
|
background icon
|
|
activate_sound "sounds/scroll.ogg"
|
|
tooltip category
|
|
action Return(["category", category])
|
|
if current_category == category:
|
|
xoffset icon_xoffset
|
|
|
|
# Outfits and Studio
|
|
hbox:
|
|
$ icon_yoffset = -18
|
|
|
|
pos (92, 18)
|
|
spacing 18
|
|
# Outfits Manager
|
|
button:
|
|
focus_mask None
|
|
xysize (72, 72)
|
|
background 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"
|
|
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", 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
|
|
anchor (0.5, 1.0)
|
|
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 Transform("interface/wardrobe/icons/random.webp", size=(72,72))
|
|
tooltip "Randomise Outfit"
|
|
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}[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}[active_girl]{/color} will automatically wear outfits\nbased on set schedule, time of day and weather."
|
|
action [ToggleVariable(active_girl+"_outfits_schedule", 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 Supression":
|
|
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:
|
|
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 = "interface/wardrobe/icons/{}.webp".format(subcategory)
|
|
|
|
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
|
|
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"
|
|
|
|
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)
|
|
$ is_blacklisted = char_active.is_blacklisted(item.type)
|
|
$ is_blacklister = any(char_active.is_equipped(x) for x in item.blacklist)
|
|
$ is_modded = bool(item.modpath)
|
|
$ is_multislot = item.is_multislot()
|
|
$ warnings = []
|
|
|
|
if is_blacklisted or is_blacklister:
|
|
$ blacklisted = [x for x in item.blacklist if char_active.is_equipped(x)] # Offender (List currently blacklisted clothing types by this item)
|
|
$ blacklister = char_active.get_blacklister(item.type) # Victim (List clothing types blacklisting this item )
|
|
$ warnings.append("Incompatible with:{size=-4}\n" + "\n".join(set(blacklisted + blacklister)) + "{/size}")
|
|
|
|
if is_inadequate:
|
|
$ warnings.append("Character level too low")
|
|
|
|
if is_modded:
|
|
$ warnings.append("Item belongs to a mod:\n{size=-4}{color=#35aae2}" + item.get_modname() + "{/color}{/size}")
|
|
|
|
# if is_multislot:
|
|
# $ slot = str(int(item.type[-1])+1)
|
|
# $ warnings.append("Occupies " + item.type[:-1] + " slot number " + slot)
|
|
|
|
button:
|
|
focus_mask None
|
|
xysize icon_size
|
|
background icon
|
|
action Return(["equip", item])
|
|
tooltip ("\n".join(warnings))
|
|
if is_inadequate:
|
|
foreground "#b2000040"
|
|
hover_foreground "#CD5C5C40"
|
|
if not is_seen:
|
|
unhovered Function(item.mark_as_seen)
|
|
|
|
add icon_frame
|
|
|
|
hbox:
|
|
offset (5, 5)
|
|
|
|
if is_modded:
|
|
text "M" color "#00b200"
|
|
|
|
if is_blacklisted or is_blacklister:
|
|
text "!" color "#b20000"
|
|
|
|
if config.developer:
|
|
text "\nReq. {}".format(item.level) size 10 color "#00ffff" outlines [(1, "#000000", 1, 1)]
|
|
|
|
# Bottom-Right
|
|
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
|
|
|
|
# Bottom-Right
|
|
if not is_seen:
|
|
text "NEW" style "wardrobe_item_caption" anchor (1.0, 1.0) align (1.0, 1.0) offset (-5, -5)
|
|
|
|
# # Bottom-Left
|
|
# if is_multislot:
|
|
# text "[slot]" style "wardrobe_item_caption" anchor (0.0, 1.0) align (0.0, 1.0) offset (5, -5)
|
|
|
|
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 = "interface/wardrobe/icons/{}.webp".format(subcategory)
|
|
$ action = Return(["subcategory", subcategory])
|
|
|
|
if subcategory == "schedule" and not getattr(renpy.store, active_girl+"_outfits_schedule"):
|
|
$ icon = gray_tint(icon)
|
|
$ action = Confirm("Outfit scheduling is currently disabled,\nwould you like to turn it on?", [SetVariable(active_girl+"_outfits_schedule", 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)
|
|
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"
|
|
|
|
# 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))
|
|
|
|
$ 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:
|
|
$ action = Return(["schedule", item])
|
|
$ alternate = Return(["schedule", item])
|
|
|
|
$ warnings = "\n".join(warnings)
|
|
|
|
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"
|
|
|
|
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
|
|
|
|
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 = "{}worn during the {}".format(boolean, i) if i in ("day", "night") else "{}worn in {} weather".format(boolean, i)
|
|
textbutton i:
|
|
style gui.theme("dropdown")
|
|
tooltip caption
|
|
action ToggleDict(item.schedule, i, True, False)
|
|
|
|
style wardrobe_window is empty
|
|
|
|
style wardrobe_button is empty:
|
|
foreground None
|
|
hover_foreground "#ffffff80"
|
|
activate_sound "sounds/click.ogg"
|
|
|
|
style wardrobe_button_text:
|
|
color "#fff"
|
|
size 20
|
|
outlines [ (1, "#000", 0, 0) ]
|
|
|
|
style wardrobe_item_caption:
|
|
color "#fff"
|
|
size 14
|
|
outlines [ (1, "#000", 0, 0) ]
|