init python: class DollOutfit(DollMethods): 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.hash = self.generate_hash() self.temp = temp self.hidden = hidden self.addons = addons if not self.temp: if unlocked: self.unlock() if not self.hidden and not self in self.char.outfits: self.char.outfits.append(self) # if config.developer: # def __del__(self): # print("Outfit with hash: {} has been garbage collected.".format(self.hash)) 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( sorted( [sorted([x.name, x.type, x.id, str(x.color)] ) for x in self.group]) ) return hash(salt) def delete(self): if self in self.char.outfits: self.char.outfits.remove(self) def make_image(self): asyncio.run(self.build_image()) async def build_image(self): # Add body, face, cum, clothes, masks async def build_clothes(group): sprites = [] masks = [] for i in group: sprites.append([i.get_image(), i.zorder]) sprites.extend([ (i.get_image(), i.zorder), i.get_back(), i.get_front(), i.get_armfix(mannequin=True), ]) if i.mask: masks.append((i.mask, i.zorder-1)) return (sprites, masks) async def build_mannequin(group): return (self.char.body.get_mannequin(group), 0) mannequin, (clothes, masks) = await asyncio.gather( build_mannequin(self.group), build_clothes(self.group), ) sprites = [ mannequin, *clothes, ] # Filter out Nulls sprites = [x for x in sprites if not isinstance(x[0], Null)] sprites.sort(key=itemgetter(1)) masks.sort(key=itemgetter(1)) # Filter out sprites with zorder less than zero, there's no need to iterate over them. back_sprites = [x[0] for x in sprites if x[1] < 0] sprites = [x for x in sprites if x[1] > -1] # 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 c = tuple(x[0] for x in sprites[:i] if not isinstance(x[0], Null)) masked = AlphaMask(Fixed(*c, fit_first=True), mask) sprites = sprites[i:] sprites.insert(0, (masked, mask_zorder)) break sprites = back_sprites + [x[0] for x in sprites] self.sprite = DollDisplayable(Fixed(*sprites, fit_first=True)) return 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] exported.extend([x.id, x.color] for x in self.group) # Encode data if tofile: path = "{}/game/outfits/".format(config.basedir) fn = "{}.png".format(filename) if not os.path.exists(path): os.makedirs(path) d = Transform(self.get_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+fn, size=(310, 470) ) image_payload.encode(filename, str(exported)) 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 = [] for v in self.char.clothes.values(): if v[0]: self.group.append(v[0].clone()) self.rebuild_image() 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.clothes.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