init python: class DollOutfit(DollMethods, SlottedNoRollback): default_schedule = {"day": False, "night": False, "cloudy": False, "rainy": False, "snowy": False} def __init__(self, group, unlocked=False, name="", desc="", price=0, temp=False, schedule={}, hidden=False, addons=[]): self.group = [x.clone() if not x.parent else x for x in group] self.name = name self.desc = desc self.price = price self.char = self.group[0].char self.unlocked = unlocked self.schedule = dict(list(self.default_schedule.items()) + list(schedule.items())) self.temp = temp self.hidden = hidden self.addons = addons self._hash = self.generate_hash() if not self.temp: if unlocked: self.unlock() if not self.hidden and not self in self.char.outfits: self.char.outfits.append(self) def __hash__(self): return self._hash def __eq__(self, obj): if not isinstance(obj, DollOutfit): return NotImplemented return self._hash == obj._hash def generate_hash(self): salt = str( [self.name, str([x._hash for x in self.group]) ] ) return hash(salt) def delete(self): if self in self.char.outfits: self.char.outfits.remove(self) @functools.cache def build_image(self, hash): from itertools import chain matrix = SaturationMatrix(0.0) sprites = list(chain.from_iterable( (self.char.body.build_image(self.char.body._hash, matrix=matrix), *(x.build_image(x._hash, matrix=matrix) for x in self.group)) )) masks = [sprites.pop(sprites.index(x)) for x in sprites if x[0] == "mask"] sprites.sort(key=itemgetter(2)) masks.sort(key=itemgetter(2)) back_sprites = [x[1] for x in sprites if x[2] < 0] #Apply alpha mask for m in masks: _, mask, mask_zorder = m for i, s in enumerate(sprites): _, sprite, sprite_zorder = s if i < 1 or mask_zorder > sprite_zorder: continue masked = AlphaMask(Fixed(*(x[1] for x in sprites[:i]), fit_first=True), mask) sprites = sprites[i:] sprites.insert(0, (None, masked, mask_zorder)) break sprites = back_sprites + [x[1] for x in sprites] return Fixed(*sprites, fit_first=True) @property def image(self): return self.build_image(self._hash) def exists(self): return (self in self.char.outfits) def export_data(self, filename, tofile=True): """Exports outfit to .png file or clipboard text.""" exported = [self.group[0].name] for i in self.group: if i.color: color = [j.hexcode for j in i.color] exported.append([i.id, color]) # Encode data if tofile: path = os.path.join(config.gamedir, "outfits") if not os.path.exists(path): os.makedirs(path) path = os.path.join(path, "_temp.png") d = Transform(self.image, crop=(210, 200, 700, 1000), anchor=(0.5, 1.0), align=(0.5, 1.0), xsize=310, ysize=470, fit="contain") d = Fixed( "interface/wardrobe/export_background.webp", d, "interface/wardrobe/export_frame.webp", Text(active_girl, align=(0.5, 0.995)), Text("Ver. {}".format(config.version), size=10, align=(0.99, 0.99)) ) displayable_to_file(d, path, size=(310, 470) ) ImagePayload().inject("_temp.png", filename, str(exported)) os.remove(path) else: set_clipboard(exported) renpy.notify("Export successful!") def unlock(self): """Unlocks outfit and respective clothing objects from which they were cloned.""" self.unlocked = True for i in self.group: i.unlock() for i in self.addons: i.unlock() def save(self): """Overwrites this outfit with clothes currently equipped by the character.""" self.group = [x[0].clone() for x in self.char.states.values() if x[0]] return def is_modded(self): """Returns True if one of the group items comes from a mod.""" for i in self.group: if i.is_modded(): return True return False def get_modname(self): """Returns a list of mods contained within the outfit group.""" return list(set([i.get_modname() for i in self.group if i.is_modded()])) def get_schedule(self): """Returns a dictionary with the current schedule.""" return self.schedule def set_schedule(self, **kwargs): for k, v in kwargs.items(): self.schedule[k] = v def has_type(self, *args): """Takes argument(s) containing string cloth type(s). Returns True if worn, False otherwise.""" types = set(x.type for x in self.group) for arg in args: if arg in self.multislots: if not any(x.startswith(arg) for x in types): return False else: if not arg in types: return False return True def has_any_type(self, *args): """Takes arguments containing string cloth types. Returns True if ANY of them is worn, False otherwise.""" if "clothes" in args: for k in self.char.states.keys(): if not k.startswith(self.blacklist_toggles): if self.has_type(k): return True else: for arg in args: if self.has_type(arg): return True return False