# Functions (Initialised after wardrobe) init 5 python in clothing_store: import functools get_icon = renpy.store.wardrobe.get_icon # Simple Proxy since both interfaces share the same code parcel_callbacks = [] def checkout(): scope = renpy.get_screen("clothing_store_interface").scope cart = scope["cart"] renpy.call_screen_in_new_context("clothing_store_checkout", cart) def checkout_confirm(): scope = renpy.get_screen("clothing_store_checkout").scope transit_time = scope["transit_time"] cart = scope["cart"] renpy.play("sounds/money.ogg") scope["navigation_atl"] = renpy.store.gui_modal_hide scope["fade_atl"] = renpy.store.wardrobe_fade_hide renpy.call_in_new_context("clothing_store_checkout", transit_time, cart) curry = renpy.curry(renpy.store.execute_callbacks)(parcel_callbacks) if parcel_callbacks else None renpy.store.Parcel(contents=[(i, 1) for i in cart], wait=transit_time, func=curry).send() scope["navigation_exit"] = True exit() def checkout_cancel(): scope = renpy.get_screen("clothing_store_checkout").scope scope["navigation_atl"] = renpy.store.gui_modal_hide scope["fade_atl"] = renpy.store.wardrobe_fade_hide scope["navigation_exit"] = True renpy.restart_interaction() def change_section(section): scope = renpy.get_screen("clothing_store_interface").scope scope["character"] = renpy.store.get_character_object(section) scope["selected_section"] = section scope["selected_item"] = next(outfits(section), None) def add_remove_outfit(outfit): scope = renpy.get_screen("clothing_store_interface").scope cart = scope["cart"] if outfit in cart: # Remove the outfit from the cart if it's already there and there are more than one item cart ^= {outfit} elif len(cart) < 5: # Add the outfit to the cart if it's not already there and there are less than five items cart |= {outfit} else: renpy.sound.play("sounds/fail.ogg") renpy.call_in_new_context("clothing_store_cart_full") def select_outfit(outfit): scope = renpy.get_screen("clothing_store_interface").scope scope["selected_item"] = outfit renpy.restart_interaction() def exit(): scope = renpy.get_screen("clothing_store_interface").scope renpy.play("sounds/curtain_close.ogg") # Handle exit animation scope["navigation_last_frame_atl"] = renpy.store.navigation_last_frame_hide scope["navigation_atl"] = renpy.store.wardrobe_hide scope["information_atl"] = renpy.store.wardrobe_fade_hide scope["character_atl"] = renpy.store.wardrobe_character_hide scope["navigation_exit"] = True # Reset states renpy.restart_interaction() @functools.cache def characters(): # Filter out side characters from the dolls list return [doll for doll in renpy.store.states.dolls if doll != "hooch"] def outfits(section): scope = renpy.get_screen("clothing_store_interface").scope character = scope["character"] # Sort the outfits by price in ascending order sorted_outfits = sorted((x for x in reversed(character.outfits) if not x.unlocked and x.price > 0), key=lambda outfit: outfit.price) return iter(sorted_outfits) def discuss(outfit): renpy.store._skipping = True renpy.suspend_rollback(False) renpy.block_rollback() renpy.call_in_new_context("clothing_store_discuss", outfit) renpy.store._skipping = False def wheelmenu(outfit): scope = renpy.get_screen("clothing_store_interface").scope character = scope["character"] cart = scope["cart"] exit_action = renpy.store.Function(renpy.hide_screen, "wheelmenu") select_action = renpy.store.Function(select_outfit, outfit) add_remove_action = renpy.store.Function(add_remove_outfit, outfit) d = { _("select"): (renpy.store.Text("✅", align=(0.5, 0.5)), [exit_action, select_action]) } if outfit in cart: d[_("Remove from cart")] = (renpy.store.Text("🛒", align=(0.5, 0.5)), [exit_action, add_remove_action]) else: d[_("Add to cart")] = (renpy.store.Text("🛒", align=(0.5, 0.5)), [exit_action, add_remove_action]) btns = renpy.store.create_wheelmenu(d) renpy.play("sounds/qubodup-click1.ogg") renpy.show_screen("wheelmenu", btns, pos=None, close_action=exit_action) def character_in_cart(character): scope = renpy.get_screen("clothing_store_interface").scope cart = scope["cart"] for outfit in cart: if outfit.char.name == character: return True return False @functools.cache def mannequin(name): # Name used for cache ref only scope = renpy.get_screen("clothing_store_interface").scope character = scope["character"] return renpy.store.Transform(character.body.image, matrixcolor=renpy.store.SaturationMatrix(0.0)) # Context label clothing_store_interface(inter_pause=True): $ disable_game_menu() play sound "sounds/curtain_open.ogg" if inter_pause: # Ensures all irrelevant screens are hidden before capturing the surface tree with Pause(0.2) $ clothing_store.parcel_callbacks = [] # Clears the parcel callbacks upon the next call to screen clothing_store_interface call screen clothing_store_interface $ enable_game_menu() # jump expression f"{states.active_girl}_requests" jump clothing_store.exit # Interface screen clothing_store_interface(): layer "interface" zorder 0 style_prefix "store" default navigation_atl = wardrobe_show default information_atl = wardrobe_fade_show default character_atl = wardrobe_character_show default last_frame = (screenshot.capture() or screenshot.image) default navigation_last_frame_atl = navigation_last_frame_show default navigation_exit = False default character = get_character_object("tonks") default selected_section = "tonks" default selected_item = next(clothing_store.outfits(selected_section), None) default cart = set() $ total_price = sum([item.price for item in cart]) $ total_gold = states.env.gold-total_price $ can_afford = states.env.gold >= total_price add last_frame at navigation_last_frame_atl add "gui_fade_both" at gui_fade if navigation_exit: timer 0.4 action Return() if selected_item: add selected_item.image align (1.0, 1.0) zoom 0.6 at character_atl frame: style "empty" at information_atl add "gui_fade_bottom" vbox: xpos 550 yoffset -10 yalign 1.0 xsize 350 label selected_item.name align (0.0, 1.0) add "frame_spacer" xsize 350 text selected_item.desc align (0.0, 1.0) vbox: pos (972, 468) vbox: xoffset -36 text _("[states.env.gold]G") xalign 1.0 text _("-[total_price]G") xalign 1.0 add "frame_spacer" xsize 100 if total_gold >= 0: text _("[total_gold]G") color "#0ead00" xalign 1.0 else: text _("[total_gold]G") color "#ad0000" xalign 1.0 label _("G") + str(selected_item.price) xoffset -50 style "store_pricetag_label" else: add clothing_store.mannequin(selected_section) align (1.0, 1.0) zoom 0.6 at character_atl frame: style "empty" at navigation_atl vbox: style_prefix "navigation_tabs" pos (540, 300) at navigation_tabs_show textbutton _("Discuss") action Function(clothing_store.discuss, selected_item) sensitive (selected_item != None) style "navigation_tabs_button_special" at navigation_tabs textbutton (_("Remove from cart") if selected_item in cart else _("Add to cart")) action Function(clothing_store.add_remove_outfit, selected_item) selected (selected_item in cart) sensitive (selected_item != None) style "store_tabs_button_add_remove" at navigation_tabs textbutton _("Checkout") action clothing_store.checkout sensitive (can_afford and cart and (selected_item != None)) at navigation_tabs null height 35 textbutton _("Exit") action clothing_store.exit keysym "K_ESCAPE" at navigation_tabs frame: at navigation_atl vbox: # Sections hbox: spacing 0 for i in clothing_store.characters(): button: xysize (64, 64) add clothing_store.get_icon(i) xysize (64, 64) if clothing_store.character_in_cart(i): add Transform(Text("🛒"), align=(1.0, 1.0), size=(24, 24)) tooltip i action Function(clothing_store.change_section, i) selected (selected_section == i) null height 3 add "frame_spacer" xsize 500 xalign 0.5 null height 3 if not selected_item: text _("SOLD OUT!") size 96 at transform: align (0.5, 0.5) rotate -45 # Outfit List vpgrid: cols 5 spacing 4 mousewheel True scrollbars "vertical" for outfit in clothing_store.outfits(selected_section): button: style "store_rectangular_button" add outfit.button.child label "G[outfit.price]" style "store_pricetag_label_small" if outfit in cart: add Transform(Text("🛒"), align=(1.0, 1.0), size=(24, 24)) if renpy.android: action Function(clothing_store.wheelmenu, outfit) else: action Function(clothing_store.select_outfit, outfit) alternate Function(clothing_store.wheelmenu, outfit) selected (selected_item == outfit) screen clothing_store_checkout(cart): modal True layer "interface" zorder 0 style_prefix "frame" default navigation_atl = gui_modal_show default fade_atl = wardrobe_fade_show default navigation_exit = False add "gui_fade_both" at fade_atl default fast_delivery = False default fast_delivery_fee = 45 + ( (len(cart)-1) * 20 ) $ total_price = sum([item.price for item in cart]) + (fast_delivery_fee if fast_delivery else 0) $ total_gold = states.env.gold-total_price $ can_afford = states.env.gold >= total_price $ transit_time = (len(cart)+1)//2 if fast_delivery else len(cart)+1 if navigation_exit: timer 0.4 action Return() frame: at navigation_atl label _("Checkout") vbox: xminimum 496 hbox: style "store_checkout_hbox" for item in cart: button: xysize (96, 168) style "store_rectangular_button" add item.button.child label "G[item.price]" style "store_pricetag_label_small" action NullAction() textbutton _("Fast Delivery") action ToggleScreenVariable("fast_delivery", True, False) style "store_checkbox_button" text _("Delivery Time: [transit_time] day(s)") color "#ffffff" xalign 0.0 style "store_checkbox_button_text" vbox: style_prefix "store_checkout" text _("[states.env.gold]G") xalign 1.0 text _("-[total_price]G") xalign 1.0 add "frame_spacer" xsize 200 xalign 1.0 text _("[total_gold]G") color ("#0ead00" if can_afford else "#ad0000") xalign 1.0 null height 20 hbox: xalign 0.5 yalign 1.0 textbutton _("Confirm") action clothing_store.checkout_confirm sensitive (can_afford) textbutton _("Cancel") action clothing_store.checkout_cancel style store_rectangular_button is wardrobe_item_button: xysize (96, 168) background Transform("wheelmenu_frame_vertical", xysize=(96,168)) hover_background At(Transform("wheelmenu_frame_opaque_vertical", xysize=(96,168)), wheelmenu_hover_anim) selected_background Fixed(Transform("wheelmenu_frame_vertical", xysize=(96,168)), At(Transform("interface/achievements/glow.webp", align=(0.5, 0.5), size=(96, 96), alpha=0.5), rotate_circular)) selected_hover_background Fixed(At(Transform("wheelmenu_frame_vertical", xysize=(96,168)), wheelmenu_hover_anim), At(Transform("interface/achievements/glow.webp", align=(0.5, 0.5), size=(96, 96), alpha=0.5), rotate_circular)) selected_foreground None style store_frame is wardrobe_frame style store_button is wardrobe_button style store_text is wardrobe_text: color "#FFFFFF" style store_label_text is wardrobe_text: color "#EA8D60" style store_pricetag_label: background Image("gui/creamy_pumpkin_pie/store/price_tag.png", oversample=3) style store_pricetag_label_text: anchor (1.0, 0.5) text_align 1.0 pos (110, 30) color "#000000" outlines [(2, "#8BA5D9", 1, 1)] hinting "bytecode" size 24 style store_pricetag_label_small: align (0.0, 1.0) xysize (68, 28) background Frame(Image("gui/creamy_pumpkin_pie/store/price_tag.png", oversample=6), 20, 10, tile=False, fit="contain") style store_pricetag_label_small_text: anchor (1.0, 0.5) text_align 1.0 pos (50, 16) color "#000000" outlines [(1, "#8BA5D9", 1, 1)] hinting "bytecode" size 14 style store_tabs_button_add_remove is navigation_tabs_button: ysize 35 left_padding 15 right_padding 30 selected_right_padding 30 background Frame(Transform(Image("gui/creamy_pumpkin_pie/book/book_tab.png", oversample=4), matrixcolor=HueMatrix(180.0)), 0, 0, 80, 0, tile=False) selected_background Frame(Transform(Image("gui/creamy_pumpkin_pie/book/book_tab.png", oversample=4), matrixcolor=HueMatrix(275.0)), 0, 0, 80, 0, tile=False) insensitive_background Frame(Transform(Image("gui/creamy_pumpkin_pie/book/book_tab.png", oversample=4), matrixcolor=SaturationMatrix(0.0)), 0, 0, 80, 0, tile=False) activate_sound "sounds/woosh2.ogg" selected_activate_sound "sounds/woosh.ogg" style store_tabs_button_add_remove_text is navigation_tabs_button_text: selected_xoffset 0 style store_checkout_hbox: fit_first True spacing 4 xalign 0.5 style store_checkout_vbox: xalign 0.5 margin (10, 10) style store_checkout_text is store_text: size 36 style store_checkbox_button is wardrobe_checkbox_button style store_checkbox_button_text is wardrobe_checkbox_button_text