init python: class Inventory(object): def __init__(self): self.items = set() def add(self, item): self.items.add(item) def remove(self, item): self.items.remove(item) def get_instances(self): return self.items def get_instances_of_type(self, type): return sorted([x for x in self.get_instances() if x.type == type], key=lambda y: natsort_key(y.name)) def _get_givables(self, type): return any(x.givable for x in self.get_instances_of_type(type)) def _get_usables(self, type, char): return any(char in x.usable_on for x in self.get_instances_of_type(type)) def is_givable_type(self, type, char=None): if not char: char = states.active_girl return self._get_givables(type) and self._get_usables(type, char) 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=[]): self.id = id self.type = type self.name = name self.price = price self.desc = desc self.unlocked = unlocked self.use_func = use_func self.use_label = use_label self.use_caption = use_caption self.give_func = give_func self.give_label = give_label self.give_caption = give_caption self.limit = limit self.image = f"interface/icons/{self.id}.webp" if image == "default" else image self.currency = currency self.used = False self.infinite = infinite self.usable_on = usable_on self.givable = bool(self.give_func or self.give_label or self.usable_on) self.usable = bool(self.use_func or self.use_label) self._owned = owned inventory.add(self) def _check_usable(self, func_name): if not getattr(self, func_name): raise Exception(f"Item {self.name!r} is not marked as {func_name}able.") if self.owned == 0: raise Exception(f"Item {self.name!r} owned count is equal to zero.") def _use_or_give(self, func_name): self._check_usable(func_name) if not self.type == "quest": # Quest items require manual triggers, it's more convenient. self.used = True func = getattr(self, f"{func_name}_func") label = getattr(self, f"{func_name}_label") if func: func() if label: enable_game_menu() renpy.jump_out_of_context(label) if renpy.context_nesting_level() > 0 else renpy.jump(label) def use(self): self._use_or_give("use") def give(self): self._use_or_give("give") def get_image(self): return self.image() if callable(self.image) else self.image @property def caption(self): return self.use_caption if inventory_mode==0 else self.give_caption # 'inventory_mode' is a global @property def owned(self): return self._owned @owned.setter def owned(self, value): self.unlocked = True self._owned = max(min(value, self.limit), 0) class Decoration(Item): room_scale = 0.5 def __init__(self, id, type, name, placement, price=0, desc="", unlocked=True, image="default", room_image="default", room_image_hover=None, owned=0, replaces=False, use_action=None, replace_action=None, replace_anchor=None, replace_pos=None): super(Decoration, self).__init__(id=id, type=type, name=name, price=price, desc=desc, unlocked=unlocked, limit=1, image=image, givable=False, currency="tokens", use_caption="Apply", owned=owned) if room_image == "default": room_image = f"images/rooms/main_room/decorations/{self.id}.webp" self.room_image = Transform(room_image, zoom=self.room_scale) self.room_image_hover = Transform(room_image_hover or room_image, zoom=self.room_scale) self.usable = True self.placement = placement self.in_use = False self.replaces = replaces self.use_action = use_action self.replace_action = replace_action self.replace_anchor = replace_anchor self.replace_pos = replace_pos if not isinstance(self.placement, RoomObject): raise TypeError("Placement must be a RoomObject instance reference.") def use(self): if self.owned == 0: raise Exception(f"Decoration {self.name!r} owned count is equal to zero.") achievements.unlock("decorator") target = self.placement if not target.decoration is None: target.decoration.in_use = False if self.use_action: self.use_action() # Toggle if not target.decoration == self: target.set_decoration(self) self.in_use = True else: target.set_decoration(None) self.in_use = False class Potion(Item): def __init__(self, id, type, name, price=0, desc="", unlocked=True, label=None, limit=100, image="default", currency="gold", use_caption=_("Use"), give_caption=_("Give"), owned=0, recipe=None, usable_on=[], levels={}): super(Potion, self).__init__(id, type=type, name=name, price=price, desc=desc, unlocked=unlocked, give_label=label, limit=limit, image=image, givable=True, currency=currency, use_caption=use_caption, give_caption=give_caption, owned=owned) # self.givable = bool(self.give_label) self.label = label self.recipe = recipe self.usable_on = usable_on self.levels = levels self.make_intro = False self.in_progress = {i: False for i in usable_on} self.usable = bool(renpy.has_label(f"{self.label}_use")) if self.recipe is None: raise Exception(f"Potion {self.name!r} recipe is empty!") def has_ingredients(self): return all(x.owned > 0 for x in self.recipe) def set_active(self, who): """Marks the event as 'in progress' and will trigger a return event in the morning/evening.""" if not who in list(self.in_progress.keys()): raise Exception(f"Potion {self.name!r} is not marked as usable on {who!r}.") self.in_progress[who] = True def make(self): if not self.has_ingredients(): return for i in self.recipe: if not i.infinite: i.owned -= 1 self.owned += 1 label = f"{self.label}_make" if renpy.has_label(label) and not self.make_intro: self.make_intro = True self.jump(label) def check_progression(self, who): """Check if progression is eligible to play this event""" progression = get_character_progression(who) return (progression >= self.levels.get(who)) def give(self, who): """Use potion on """ give_label = f"{who[:3]}_{self.label}_give" check_label = f"{who[:3]}_potion_check" if not renpy.has_label(give_label): raise Exception(f"Potion {self.name!r} give label doesn't exist.") if not renpy.has_label(check_label): raise Exception(f"Potion {self.name!r} check label doesn't exist for {who!r}.") if self.owned == 0: raise Exception(f"Potion {self.name!r} owned count is equal to zero.") if not self.check_progression(who): self.jump(check_label) self.owned -= 1 self.jump(give_label) def use(self): """Use potion on Genie""" label = f"{self.label}_use" if not renpy.has_label(label): raise Exception(f"Potion {self.name!r} has no use label.") if self.owned == 0: raise Exception(f"Potion {self.name!r} owned count is equal to zero.") self.owned -= 1 self.jump(label) def ret(self, who): """Play the return event for """ if not self.in_progress[who]: raise Exception(f"Potion {self.name!r} is not marked as in progress.") label = f"{who[:3]}_{self.label}_return" if not renpy.has_label(label): raise Exception(f"Potion {self.name!r} has no return label.") self.in_progress[who] = False self.jump(label) def jump(self, label): if renpy.context_nesting_level() > 0: renpy.jump_out_of_context(label) else: renpy.jump(label) init offset = -5 default inventory = Inventory()