diff --git a/game/scripts/characters/hermione/requests.rpy b/game/scripts/characters/hermione/requests.rpy index a6d1f873..0e1bdd37 100644 --- a/game/scripts/characters/hermione/requests.rpy +++ b/game/scripts/characters/hermione/requests.rpy @@ -173,15 +173,14 @@ default her_ev_cumslut_public_t6_e2_hub = Event(id="her_ev_cumslut_public_t6_e2_ default her_ev_cumslut_public_t6_e3_hub = Event(id="her_ev_cumslut_public_t6_e3_hub", label="hg_pr_cumslut", req="states.her.tier >= 6", autoenqueue=True, autodequeue=False, queue="her_eventqueue_cumslut_public", subevents=["her_ev_cumslut_public_t6_e3"]) default her_ev_cumslut_public_t6_e4_hub = Event(id="her_ev_cumslut_public_t6_e3_hub", label="hg_pr_cumslut", req="states.her.tier >= 6", autoenqueue=True, autodequeue=False, queue="her_eventqueue_cumslut_public", subevents=["her_ev_cumslut_public_t6_e4"]) - define hermione_requests = [ - ("her_eventqueue_flirt_students", "Flirt with students!"), - ("her_eventqueue_flirt_teachers", "Flirt with teachers!"), - ("her_eventqueue_grope_public", "Let them grope you!"), - ("her_eventqueue_flash_public", "Flash your tits!"), - ("her_eventqueue_kiss_public", "Kiss a girl!"), - ("her_eventqueue_handjob_public", "Give a handjob!"), - ("her_eventqueue_blowjob_public", "Give a blowjob!"), - ("her_eventqueue_cumslut_public", "Act like a cumslut!"), - ("her_eventqueue_sex_public", "Have sex!") + ("her_eventqueue_flirt_students", _("Flirt with students!")), + ("her_eventqueue_flirt_teachers", _("Flirt with teachers!")), + ("her_eventqueue_grope_public", _("Let them grope you!")), + ("her_eventqueue_flash_public", _("Flash your tits!")), + ("her_eventqueue_kiss_public", _("Kiss a girl!")), + ("her_eventqueue_handjob_public", _("Give a handjob!")), + ("her_eventqueue_blowjob_public", _("Give a blowjob!")), + ("her_eventqueue_cumslut_public", _("Act like a cumslut!")), + ("her_eventqueue_sex_public", _("Have sex!")) ] diff --git a/game/scripts/interface/inventory.rpy b/game/scripts/interface/inventory.rpy index c1d31fff..736f6700 100644 --- a/game/scripts/interface/inventory.rpy +++ b/game/scripts/interface/inventory.rpy @@ -139,7 +139,7 @@ style inventory_item_button is empty: selected_hover_background Fixed(At(Transform("wheelmenu_button_opaque", xysize=(48,48)), wheelmenu_hover_anim), At(Transform("interface/achievements/glow.webp", align=(0.5, 0.5), size=(48, 48), alpha=0.5), rotate_circular)) style inventory_item_big_button is empty: - background Transform("wheelmenu_button", xysize=(48,48)) - hover_background At(Transform("wheelmenu_button_opaque", xysize=(48,48)), wheelmenu_hover_anim) - selected_background Fixed(Transform("wheelmenu_button", xysize=(48,48)), At(Transform("interface/achievements/glow.webp", align=(0.5, 0.5), size=(48, 48), alpha=0.5), rotate_circular)) - selected_hover_background Fixed(At(Transform("wheelmenu_button_opaque", xysize=(48,48)), wheelmenu_hover_anim), At(Transform("interface/achievements/glow.webp", align=(0.5, 0.5), size=(48, 48), alpha=0.5), rotate_circular)) + background Transform("wheelmenu_button", xysize=(96,96)) + hover_background At(Transform("wheelmenu_button_opaque", xysize=(96,96)), wheelmenu_hover_anim) + selected_background Fixed(Transform("wheelmenu_button", xysize=(96,96)), 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_button_opaque", xysize=(96,96)), wheelmenu_hover_anim), At(Transform("interface/achievements/glow.webp", align=(0.5, 0.5), size=(96, 96), alpha=0.5), rotate_circular)) diff --git a/game/scripts/inventory/classes.rpy b/game/scripts/inventory/classes.rpy index 6059fc76..c1b41760 100644 --- a/game/scripts/inventory/classes.rpy +++ b/game/scripts/inventory/classes.rpy @@ -26,6 +26,12 @@ init python: char = states.active_girl return self._get_givables(type) and self._get_usables(type, char) + def get_types(self): + item_types = set() + for item in self.get_instances(): + item_types.add(item.type) + return item_types + class Item(object): def __init__(self, id, type, name, price=0, desc="", unlocked=True, use_func=None, use_label=None, give_func=None, give_label=None, limit=100, image="default", givable=False, currency="gold", use_caption=_("Use"), give_caption=_("Give"), owned=0, infinite=False, usable_on=[], use_progression={}, give_progression={}): self.id = id diff --git a/game/scripts/shops/item/menu.rpy b/game/scripts/shops/item/menu.rpy index 5d5f9831..31fbcc25 100644 --- a/game/scripts/shops/item/menu.rpy +++ b/game/scripts/shops/item/menu.rpy @@ -1,289 +1,192 @@ -init python: - def shop_item_sortfilter(item, sortby="Price (Asc)", filtering=None): - # Always sort alphabetically first. - item = sorted(item, key=lambda x: natsort_key(x.name)) +# Functions (Initialised after item_store) +init 5 python in item_store: + import functools - if sortby == "Price (Asc)": - item.sort(key=lambda x: x.price, reverse=False) - elif current_sorting == "Price (Desc)": - item.sort(key=lambda x: x.price, reverse=True) - return item + get_icon = renpy.store.wardrobe.get_icon # Simple Proxy since both interfaces share the same code -label shop_item: - $ gui.in_context("shop_item_menu") - return + def buy(): + scope = renpy.get_screen("item_store_interface").scope + selected_item = scope["selected_item"] -label shop_item_menu(xx=150, yy=90): + renpy.call_in_new_context("purchase_item", selected_item) - python: - inventory_dict = { - "Gifts": inventory.get_instances_of_type("gift"), - "Books": inventory.get_instances_of_type("book"), - "Scrolls": inventory.get_instances_of_type("scroll"), - "Ingredients": inventory.get_instances_of_type("ingredient"), - "Decorations": inventory.get_instances_of_type("decoration"), - "Quest Items": inventory.get_instances_of_type("quest"), - } + section = scope["selected_section"] + _items = items(section) + # Avoid switching selection when not necessary + if selected_item.owned >= selected_item.limit or selected_item not in _items: + scope["selected_item"] = next(_items, None) + renpy.restart_interaction() - items_shown = 36 - current_page = 0 - current_category = next(iter(inventory_dict.keys())) - current_sorting = "Price (Asc)" + def select_item(item): + scope = renpy.get_screen("item_store_interface").scope + scope["selected_item"] = item - if current_category in {"Gifts", "Ingredients"}: - category_items = [x for x in inventory_dict[current_category] if (x.price > 0 and x.unlocked)] - elif current_category in {"Books", "Scrolls", "Decorations", "Quest Items"}: - category_items = [x for x in inventory_dict[current_category] if (x.price > 0 and x.owned < x.limit and x.unlocked)] + renpy.restart_interaction() - menu_items = shop_item_sortfilter(category_items, current_sorting) - menu_items_length = len(menu_items) + def change_section(section): + scope = renpy.get_screen("item_store_interface").scope + scope["selected_section"] = section + scope["selected_item"] = next(items(section), None) - current_item = next(iter(menu_items), None) + def exit(): + scope = renpy.get_screen("item_store_interface").scope - show screen shop_item(xx, yy) + renpy.play("sounds/curtain_close.ogg") - label .after_init: - $ renpy.dynamic(__choice = ui.interact()) + # 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 - if __choice[0] == "select": - $ current_item = __choice[1] - elif __choice[0] == "category": - $ current_category = __choice[1] - if current_category in {"Gifts", "Ingredients"}: - $ category_items = [x for x in inventory_dict[current_category] if (x.price > 0 and x.unlocked)] - elif current_category in {"Books", "Scrolls", "Decorations", "Quest Items"}: - $ category_items = [x for x in inventory_dict[current_category] if (x.price > 0 and x.owned < x.limit and x.unlocked)] - $ menu_items = shop_item_sortfilter(category_items, current_sorting) - $ menu_items_length = len(menu_items) - $ current_page = 0 - $ current_item = next(iter(menu_items), None) - pass - elif __choice == "inc": - $ current_page += 1 - elif __choice == "dec": - $ current_page += -1 - elif __choice == "sort": - if current_sorting == "Price (Asc)": - $ current_sorting = "Price (Desc)" - elif current_sorting == "Price (Desc)": - $ current_sorting = "Price (Asc)" + # Reset states + renpy.restart_interaction() - $ menu_items = shop_item_sortfilter(category_items, current_sorting) - elif __choice == "buy": - $ renpy.call("purchase_item", current_item) + def items(section): + # Sort the items by price in ascending order, then alphabetically by name if prices are the same + sorted_items = sorted( + (x for x in reversed(renpy.store.inventory.get_instances_of_type(section)) + if x.unlocked and x.price > 0 and x.owned < x.limit), + key=lambda item: (item.price, renpy.store.natsort_key(item.name)) + ) + return iter(sorted_items) - if current_category in {"Gifts", "Ingredients"}: - $ category_items = [x for x in inventory_dict[current_category] if (x.price > 0 and x.unlocked)] - elif current_category in {"Books", "Scrolls", "Decorations", "Quest Items"}: - $ category_items = [x for x in inventory_dict[current_category] if (x.price > 0 and x.owned < x.limit and x.unlocked)] - $ menu_items = shop_item_sortfilter(category_items, current_sorting) - $ menu_items_length = len(menu_items) +# Context +label item_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) + call screen item_store_interface + $ enable_game_menu() - if not current_item in menu_items: - # We should avoid changing currently selected item when it's still possible to obtain it. - $ current_item = next(iter(menu_items), None) - else: - show screen blktone - with d3 - menu: - ger "Are you done shopping, sir?" + jump item_store.exit - "-Yes, I'm done-": - hide screen shop_item - hide screen blktone - return - "-Not yet-": - hide screen blktone - with d3 +# Interface +screen item_store_interface(): + layer "interface" + zorder 0 + style_prefix "store" - jump .after_init + default navigation_atl = wardrobe_show + default information_atl = wardrobe_fade_show + default last_frame = (screenshot.capture() or screenshot.image) + default navigation_last_frame_atl = navigation_last_frame_show + default navigation_exit = False -screen shop_item(xx, yy): - tag shop_item - zorder 15 - modal True + default selected_section = "gift" + default selected_item = next(item_store.items(selected_section), None) + default cart = set() - add "gui_fade" + add last_frame at navigation_last_frame_atl - if renpy.mobile: - use close_button_background - use close_button + add "gui_fade_both" at gui_fade - fixed: - if settings.get("animations"): - at gui_animation - use shop_item_menu(xx, yy) - use shop_item_menuitem(xx, yy) + if navigation_exit: + timer 0.4 action Return() -screen shop_item_menu(xx, yy): - window: - style "empty" - style_prefix gui.theme('achievements') - pos (xx, yy) - xysize (207, 454) - - use invisible_button() - - add gui.format("interface/achievements/{}/panel_left.webp") - - vbox: - pos (6, 41) - for category in inventory_dict.keys(): - vbox: - textbutton category: - style "empty" - xsize 195 ysize 16 - text_xalign 0.5 - if current_category == category: - background gui.format("interface/achievements/{}/highlight_left.webp") - else: - hover_background gui.format("interface/achievements/{}/highlight_left.webp") - action Return(["category", category]) - add gui.format("interface/achievements/{}/spacer_left.webp") - vbox: - style_prefix gui.theme('achievements_filters') - pos (6, 384) - button action NullAction() style "empty" xsize 195 ysize 32 - textbutton "Sort by: [current_sorting]" action Return("sort") - -screen shop_item_menuitem(xx, yy): - window: - style "empty" - style_prefix gui.theme() - pos (xx+217, yy-53) - xysize (560, 507) - - use invisible_button() - - add "interface/achievements/inventory.webp" - add gui.format("interface/achievements/{}/panel.webp") - - #Western Egg - button xsize 90 ysize 60 action Play("sound", "sounds/plushie.ogg") xalign 0.5 style "empty" - - text "Store" size 22 xalign 0.5 ypos 65 - - if current_item is None or current_item.currency == "gold": - text "{color=#daa520}G{/color} {outlinecolor=#ffffff00}[states.env.gold]{/outlinecolor}" size 16 pos (24, 70) outlines [ (2, "#000", 0, 0) ] + if selected_item: + $ total_price = selected_item.price if selected_item else 0 + if selected_item.currency == "tokens": + $ currency_prefix = _("T") + $ currency = states.env.tokens + $ total_gold = states.env.tokens-total_price + $ can_afford = states.env.tokens >= total_price else: - text "{color=#2055da}T{/color} {outlinecolor=#ffffff00}[states.env.tokens]{/outlinecolor}" size 16 pos (24, 70) outlines [ (2, "#000", 0, 0) ] + $ currency_prefix = _("G") + $ currency = states.env.gold + $ total_gold = states.env.gold-total_price + $ can_afford = states.env.gold >= total_price + + add crop_image_zoom(selected_item.get_image(), 256, 256, False) align (0.5, 0.5) xoffset 270 + + frame: + style "empty" + at information_atl + + add "gui_fade_bottom" - # Page counter - if menu_items_length > items_shown: - hbox: - xanchor 1.0 - pos (540, 24) - spacing 5 - add "interface/page.webp" yanchor 0.5 ypos 53 - text str(current_page+1)+"/"+str(int(math.ceil(menu_items_length/items_shown))) ypos 44 size 16 vbox: - pos (570, 186) - spacing 10 + xpos 550 + yoffset -10 + yalign 1.0 + xsize 350 - imagebutton: - idle gui.format("interface/frames/{}/arrow_up.webp") - if not current_page <= 0: - hover image_hover(gui.format("interface/frames/{}/arrow_up.webp")) - action Return("dec") + hbox: + align (0.0, 1.0) + label selected_item.name + if selected_item.owned > 0: + text "{size=-4}x{/size}[selected_item.owned]" + add "frame_spacer" xsize 350 + text selected_item.desc align (0.0, 1.0) - imagebutton: - idle Transform(gui.format("interface/frames/{}/arrow_up.webp"), yzoom=-1) - if current_page < math.ceil((menu_items_length-1)/items_shown)-1: - hover Transform(image_hover(gui.format("interface/frames/{}/arrow_up.webp")), yzoom=-1) - action Return("inc") + vbox: + pos (972, 468) + vbox: + xoffset -36 + text _("[currency][currency_prefix]") xalign 1.0 + text _("-[total_price][currency_prefix]") xalign 1.0 + add "frame_spacer" xsize 100 + if total_gold >= 0: + text "[total_gold][currency_prefix]" color "#0ead00" xalign 1.0 + else: + text "[total_gold][currency_prefix]" color "#ad0000" xalign 1.0 - # Add items - for i in range(current_page*items_shown, (current_page*items_shown)+items_shown): + label "[currency_prefix]" + str(selected_item.price) xoffset -50 style "store_pricetag_label" - if i < menu_items_length: - $ price = menu_items[i].price - $ row = (i // 9) % 4 - $ col = i % 9 - frame: - style "empty" - xsize 48 - ysize 48 - pos (24+58*(col), 113+58*(row)) - add gui.format("interface/achievements/{}/iconbox.webp") + frame: + style "empty" + at navigation_atl + vbox: + style_prefix "navigation_tabs" + pos (540, 300) - if not current_item is None and current_item.id == menu_items[i].id: - add "interface/achievements/glow.webp" align (0.5, 0.5) zoom 0.105 alpha 0.7 at rotate_circular + at navigation_tabs_show - add crop_image_zoom( - menu_items[i].get_image(), 42, 42, - (menu_items[i].currency != "tokens" or states.env.tokens< menu_items[i].price) and (menu_items[i].owned >= 99 or states.env.gold < menu_items[i].price) - ) align (0.5, 0.5) + textbutton _("Buy") action item_store.buy sensitive (can_afford and (selected_item != None)) at navigation_tabs + null height 35 + textbutton _("Exit") action item_store.exit keysym "K_ESCAPE" at navigation_tabs + + frame: + at navigation_atl + + vbox: + # Sections + hbox: + spacing 0 + + for i in inventory.get_types(): button: - style gui.theme("overlay_button") - background "interface/achievements/glass_iconbox.webp" - xsize 46 ysize 46 - action Return(["select", menu_items[i]]) - tooltip menu_items[i].name + xysize (64, 64) + add item_store.get_icon(i) xysize (64, 64) + tooltip i + action Function(item_store.change_section, i) selected (selected_section == i) - if menu_items[i].owned > 0: - text str(menu_items[i].owned) size 10 align (0.1, 0.1) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] + null height 3 + add "frame_spacer" xsize 500 xalign 0.5 + null height 3 - if menu_items[i].currency == "tokens": - if states.env.tokens>= menu_items[i].price: - text "{color=#2055da}T{/color} [price]" size 10 pos (0.95, 0.95) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - else: - text "{color=#2055da}T{/color} {color=#ff0000}[price]{/color}" size 10 pos (0.95, 0.95) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - else: - if states.env.gold >= menu_items[i].price: - text "{color=#daa520}G{/color} [price]" size 10 pos (0.95, 0.95) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - else: - text "{color=#daa520}G{/color} {color=#ff0000}[price]{/color}" size 10 pos (0.95, 0.95) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] + if not selected_item: + text _("SOLD OUT!") size 96 at transform: + align (0.5, 0.5) + rotate -45 + # Item List + vpgrid: + cols 5 + spacing 4 + mousewheel True + scrollbars "vertical" - if menu_items_length <= 0: - text "Nothing here yet" align (0.5, 0.5) size 24 + for item in item_store.items(selected_section): + button: + xysize (96, 96) + style "inventory_item_big_button" + add crop_image_zoom(item.get_image(), 96, 96, False) align (0.5, 0.5) + label "[currency_prefix][item.price]" style "store_pricetag_label_small" - if current_item: - frame: - style "empty" - xsize 96 - ysize 96 - pos (24, 375) - add gui.format("interface/achievements/{}/icon_selected.webp") - - add crop_image_zoom( - current_item.get_image(), 84, 84, - (current_item.currency != "tokens" or states.env.tokens< current_item.price) and (current_item.owned >= 99 or states.env.gold < current_item.price) - ) align (0.5, 0.5) - add "interface/achievements/glass_selected.webp" pos (6, 6) - - if current_item.owned > 0: - text "[current_item.owned]" size 14 align (0.1, 0.1) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - - if current_item.currency == "tokens": - if states.env.tokens>= current_item.price: - text "{color=#2055da}T{/color} [current_item.price]" size 14 pos (0.9, 0.9) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - else: - text "{color=#2055da}T{/color} {color=#ff0000}[current_item.price]{/color}" size 14 pos (0.90, 0.90) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - else: - if states.env.gold >= current_item.price: - text "{color=#daa520}G{/color} [current_item.price]" size 14 pos (0.9, 0.9) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - else: - text "{color=#daa520}G{/color} {color=#ff0000}[current_item.price]{/color}" size 14 pos (0.90, 0.90) anchor (1.0, 1.0) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ] - - add gui.format("interface/achievements/{}/highlight.webp") pos (112, 375) - add gui.format("interface/achievements/{}/spacer.webp") pos (120, 398) - hbox: - spacing 5 - xalign 0.5 - text current_item.name ypos 380 size 16 xoffset 45 - - textbutton "Buy": - xysize (90, 26) - xalign 0.89 - xoffset 45 - ypos 374 - text_size 16 - sensitive ((current_item.currency == "tokens" and states.env.tokens>= current_item.price) or (current_item.owned < 99 and states.env.gold >= current_item.price)) - action Return("buy") - - hbox: - pos (132, 407) - xsize 410 - text current_item.desc size 12 + action Function(item_store.select_item, item) + selected (selected_item == item) diff --git a/game/scripts/shops/item/room.rpy b/game/scripts/shops/item/room.rpy index 395df8f9..0afc5837 100644 --- a/game/scripts/shops/item/room.rpy +++ b/game/scripts/shops/item/room.rpy @@ -216,7 +216,9 @@ label item_store: else: jump twins_random_duel - call shop_item + jump item_store_interface + + label .exit: twi "Come again!"