From 22a84479dd184bb0677ad3c22cd44b2ac4cd94ef Mon Sep 17 00:00:00 2001 From: LoafyLemon Date: Thu, 19 Jan 2023 21:55:19 +0000 Subject: [PATCH] Dynamic Cum Layering * Added dynamic cum layering based on clothing and facial states w/ backwards compatibility * Fixed layers zordering * Refactored default layer dicts * Fixed repr class name in DollClothDynamic * Added back and front zorder modifiers to cum layers * Fixed cum layers not updating on call * Fixed skin layers fighting for zorder with the body --- .../cum/hair/heavy/{ => default}/cum.webp | 0 .../cum/hair/light/{ => default}/cum.webp | 0 .../L/{overlay.webp => expression.webp} | 0 .../R/{overlay.webp => expression.webp} | 0 .../ahegao/{overlay.webp => expression.webp} | 0 .../down/{overlay.webp => expression.webp} | 0 .../downR/{overlay.webp => expression.webp} | 0 .../mid/{overlay.webp => expression.webp} | 0 .../stare/{overlay.webp => expression.webp} | 0 .../up/{overlay.webp => expression.webp} | 0 game/scripts/doll/clothes.rpy | 2 +- game/scripts/doll/clothes_dynamic.rpy | 2 +- game/scripts/doll/cum.rpy | 46 ++++++++++++-- game/scripts/doll/face.rpy | 10 ++-- game/scripts/doll/main.rpy | 60 ++++++++++++------- 15 files changed, 86 insertions(+), 34 deletions(-) rename game/characters/tonks/cum/hair/heavy/{ => default}/cum.webp (100%) rename game/characters/tonks/cum/hair/light/{ => default}/cum.webp (100%) rename game/characters/tonks/face/pupils/L/{overlay.webp => expression.webp} (100%) rename game/characters/tonks/face/pupils/R/{overlay.webp => expression.webp} (100%) rename game/characters/tonks/face/pupils/ahegao/{overlay.webp => expression.webp} (100%) rename game/characters/tonks/face/pupils/down/{overlay.webp => expression.webp} (100%) rename game/characters/tonks/face/pupils/downR/{overlay.webp => expression.webp} (100%) rename game/characters/tonks/face/pupils/mid/{overlay.webp => expression.webp} (100%) rename game/characters/tonks/face/pupils/stare/{overlay.webp => expression.webp} (100%) rename game/characters/tonks/face/pupils/up/{overlay.webp => expression.webp} (100%) diff --git a/game/characters/tonks/cum/hair/heavy/cum.webp b/game/characters/tonks/cum/hair/heavy/default/cum.webp similarity index 100% rename from game/characters/tonks/cum/hair/heavy/cum.webp rename to game/characters/tonks/cum/hair/heavy/default/cum.webp diff --git a/game/characters/tonks/cum/hair/light/cum.webp b/game/characters/tonks/cum/hair/light/default/cum.webp similarity index 100% rename from game/characters/tonks/cum/hair/light/cum.webp rename to game/characters/tonks/cum/hair/light/default/cum.webp diff --git a/game/characters/tonks/face/pupils/L/overlay.webp b/game/characters/tonks/face/pupils/L/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/L/overlay.webp rename to game/characters/tonks/face/pupils/L/expression.webp diff --git a/game/characters/tonks/face/pupils/R/overlay.webp b/game/characters/tonks/face/pupils/R/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/R/overlay.webp rename to game/characters/tonks/face/pupils/R/expression.webp diff --git a/game/characters/tonks/face/pupils/ahegao/overlay.webp b/game/characters/tonks/face/pupils/ahegao/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/ahegao/overlay.webp rename to game/characters/tonks/face/pupils/ahegao/expression.webp diff --git a/game/characters/tonks/face/pupils/down/overlay.webp b/game/characters/tonks/face/pupils/down/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/down/overlay.webp rename to game/characters/tonks/face/pupils/down/expression.webp diff --git a/game/characters/tonks/face/pupils/downR/overlay.webp b/game/characters/tonks/face/pupils/downR/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/downR/overlay.webp rename to game/characters/tonks/face/pupils/downR/expression.webp diff --git a/game/characters/tonks/face/pupils/mid/overlay.webp b/game/characters/tonks/face/pupils/mid/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/mid/overlay.webp rename to game/characters/tonks/face/pupils/mid/expression.webp diff --git a/game/characters/tonks/face/pupils/stare/overlay.webp b/game/characters/tonks/face/pupils/stare/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/stare/overlay.webp rename to game/characters/tonks/face/pupils/stare/expression.webp diff --git a/game/characters/tonks/face/pupils/up/overlay.webp b/game/characters/tonks/face/pupils/up/expression.webp similarity index 100% rename from game/characters/tonks/face/pupils/up/overlay.webp rename to game/characters/tonks/face/pupils/up/expression.webp diff --git a/game/scripts/doll/clothes.rpy b/game/scripts/doll/clothes.rpy index 2b42c1df..738bd98b 100644 --- a/game/scripts/doll/clothes.rpy +++ b/game/scripts/doll/clothes.rpy @@ -2,7 +2,7 @@ init python: class DollCloth(DollMethods): layer_types = { "mask": "-1", - "skin": 0, + "skin": 1, "armfix": "+1", "outline": None, "extra": None, diff --git a/game/scripts/doll/clothes_dynamic.rpy b/game/scripts/doll/clothes_dynamic.rpy index a9929654..17c7390c 100644 --- a/game/scripts/doll/clothes_dynamic.rpy +++ b/game/scripts/doll/clothes_dynamic.rpy @@ -8,7 +8,7 @@ init python: super().__init__(name, categories, type, id, color, zorder, unlocked, level, blacklist, modpath, parent) def __repr__(self): - return f"DollMakeup(name={self.name}, categories={self.categories}, type={self.type}, id={self.id}, color={self.color}, zorder={self.zorder}, unlocked={self.unlocked}, level={self.level}, blacklist={self.blacklist}, modpath={self.modpath or None}, tracking={self._tracking}, parent={self.parent})" + return f"DollClothDynamic(name={self.name}, categories={self.categories}, type={self.type}, id={self.id}, color={self.color}, zorder={self.zorder}, unlocked={self.unlocked}, level={self.level}, blacklist={self.blacklist}, modpath={self.modpath or None}, tracking={self._tracking}, parent={self.parent})" @property def tracking(self): diff --git a/game/scripts/doll/cum.rpy b/game/scripts/doll/cum.rpy index adacd1d5..3b8f0d79 100644 --- a/game/scripts/doll/cum.rpy +++ b/game/scripts/doll/cum.rpy @@ -1,21 +1,24 @@ init python: class DollCum(DollMethods): layer_types = { - "skin": 0, + "mask": "-1", + "skin": 1, "cum": 100, } layer_modifiers = { + "back": "-300", + "front": "+300", "zorder": None, } def __init__(self, obj): self.char = obj - self._cum = {k: None for k in {"hair", "face", "breasts", "body", "crotch", "pussy", "legs"}} + self._cum = {k: None for k in (self.char.clothing_layers | self.char.face_layers).keys()} self._hash = None def generate_hash(self): - salt = str( [self.char.name, self.char.pose, sorted(list(self._cum.items()))] ) + salt = str( [self.char.name, self.char.pose, str(self.char.face._hash), str(self.char.face._hash), str([x[0]._hash for x in self.char.clothes.values() if x[0] and x[2]]), sorted(list(self._cum.items()))] ) return hash(salt) def set_cum(self, *args, **kwargs): @@ -27,11 +30,19 @@ init python: @functools.cache def get_layers(self, hash): + + def _lookahead(path): + return any(fp.startswith(path) for fp in renpy.list_files()) + cum = self._cum extensions = self.extensions types = self.layer_types modifiers = self.layer_modifiers + face_layers = self.char.face_layers + + active_faces = self.char.face._face + active_clothes = self.char.clothes layers = {} for part, name in cum.items(): @@ -39,7 +50,32 @@ init python: if name is None: continue - path = os.path.join("characters", self.char.name, self.char.pose, "cum", part, name) + if part in face_layers: + zorder = face_layers.get(part) + identifier = active_faces.get(part, "default") + path = os.path.join("characters", self.char.name, self.char.pose, "cum", part, name, identifier) + else: + + cloth, zorder, is_worn = active_clothes.get(part, [None, None, None]) + + if is_worn is None: + # Backwards compatibility for old layering system + path = os.path.join("characters", self.char.name, self.char.pose, "cum", part, name) + + if config.developer: + renpy.notify(f"Using old-type cum layer, consider updating the code; Layer: \"{part}\"") + else: + if cloth is None or is_worn is False: + identifier = "default" + modpath = "" + zorder = None + else: + identifier = cloth.id + modpath = cloth.modpath + zorder = cloth.zorder + 1 + + path = os.path.join(modpath, "characters", self.char.name, self.char.pose, "cum", part, name, identifier) + path = path if _lookahead(path) else os.path.join(os.path.split(path)[0], (identifier := "default")) for f in renpy.list_files(): fp, fn = os.path.split(f) @@ -54,7 +90,7 @@ init python: print("Invalid layer type for file: {}".format(f)) continue - zorder = types.get(ltype) + zorder = zorder or types.get(ltype) if tails: lmodifier, *tails = tails diff --git a/game/scripts/doll/face.rpy b/game/scripts/doll/face.rpy index 7903f043..2603127e 100644 --- a/game/scripts/doll/face.rpy +++ b/game/scripts/doll/face.rpy @@ -2,9 +2,8 @@ init python: class DollFace(DollMethods): layer_types = { "eyemask": -1, - "skin": 0, - "expression": 1, - "overlay": 2, + "skin": 1, + "expression": None, } layer_modifiers = { @@ -13,7 +12,7 @@ init python: def __init__(self, obj): self.char = obj - self._face = {k: None for k in {"cheeks", "eyebrows", "eyes", "mouth", "pupils", "tears"}} + self._face = {k: None for k in self.char.face_layers.keys()} self._hash = None def set_face(self, *args, **kwargs): @@ -34,6 +33,7 @@ init python: extensions = self.extensions types = self.layer_types modifiers = self.layer_modifiers + face_layers = self.char.face_layers layers = {} for part, name in face.items(): @@ -56,7 +56,7 @@ init python: print("Invalid layer type for file: {}".format(f)) continue - zorder = types.get(ltype) + zorder = types.get(ltype) or face_layers.get(part) if tails: lmodifier, *tails = tails diff --git a/game/scripts/doll/main.rpy b/game/scripts/doll/main.rpy index 9714c38f..7ea35b27 100644 --- a/game/scripts/doll/main.rpy +++ b/game/scripts/doll/main.rpy @@ -2,27 +2,40 @@ init python: import asyncio class Doll(DollMethods): - layers = [ - ("buttplug", -11), - ("makeup", 111), # multislot - ("accessory", 121), # multislot - ("piercing", 131), # multislot - ("tattoo", 141), # multislot - ("pubes", 151), - ("stockings", 161), - ("panties", 171), - ("garterbelt", 181), - ("bottom", 191), - ("bra", 201), - ("top", 211), - ("gloves", 221), - ("robe", 231), - ("neckwear", 241), - ("hair", 251), - ("earrings", 261), - ("glasses", 271), - ("headgear", 281) - ] + # 0 - 50 = Skin Layers + # 51 - 100 = Face Layers + # 101 - 300+ = Clothes Layers + + clothing_layers = { + "buttplug": -11, + "makeup": 111, # multislot + "accessory": 121, # multislot + "piercing": 131, # multislot + "tattoo": 141, # multislot + "pubes": 151, + "stockings": 161, + "panties": 171, + "garterbelt": 181, + "bottom": 191, + "bra": 201, + "top": 211, + "gloves": 221, + "robe": 231, + "neckwear": 241, + "hair": 251, + "earrings": 261, + "glasses": 271, + "headgear": 281 + } + + face_layers = { + "tears": 75, + "eyebrows": 70, + "pupils": 65, + "eyes": 60, + "mouth": 55, + "cheeks": 51 + } def __init__(self, name): self.wardrobe = {} @@ -30,7 +43,7 @@ init python: self.blacklist = [] self.outfits = [] self.name = name - self.clothes = {layer[0]: [None, layer[1], True] for layer in self.layers} + self.clothes = {k: [None, v, True] for k, v in self.clothing_layers.items()} self.face = DollFace(self) self.body = DollBody(self) self.cum = DollCum(self) @@ -131,6 +144,7 @@ init python: self.rebuild_blacklist() update_chibi(self.name) + self.cum.is_stale() self.is_stale() if renpy.showing(get_character_tag(self.name), layer=self.layer): @@ -191,6 +205,7 @@ init python: self.rebuild_blacklist() update_chibi(self.name) + self.cum.is_stale() self.is_stale() if renpy.showing(get_character_tag(self.name), layer=self.layer): @@ -313,6 +328,7 @@ init python: self.face.set_face(*args, **kwargs) [x[0].is_stale() for x in self.clothes.values() if isinstance(x[0], DollMakeup) and x[2]] + self.cum.is_stale() def get_face(self): """Returns a dictionary containing currently set facial expressions. Used in character studio."""