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
This commit is contained in:
LoafyLemon 2023-01-19 21:55:19 +00:00
parent 2946efbd2b
commit 22a84479dd
15 changed files with 86 additions and 34 deletions

View File

@ -2,7 +2,7 @@ init python:
class DollCloth(DollMethods): class DollCloth(DollMethods):
layer_types = { layer_types = {
"mask": "-1", "mask": "-1",
"skin": 0, "skin": 1,
"armfix": "+1", "armfix": "+1",
"outline": None, "outline": None,
"extra": None, "extra": None,

View File

@ -8,7 +8,7 @@ init python:
super().__init__(name, categories, type, id, color, zorder, unlocked, level, blacklist, modpath, parent) super().__init__(name, categories, type, id, color, zorder, unlocked, level, blacklist, modpath, parent)
def __repr__(self): 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 @property
def tracking(self): def tracking(self):

View File

@ -1,21 +1,24 @@
init python: init python:
class DollCum(DollMethods): class DollCum(DollMethods):
layer_types = { layer_types = {
"skin": 0, "mask": "-1",
"skin": 1,
"cum": 100, "cum": 100,
} }
layer_modifiers = { layer_modifiers = {
"back": "-300",
"front": "+300",
"zorder": None, "zorder": None,
} }
def __init__(self, obj): def __init__(self, obj):
self.char = 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 self._hash = None
def generate_hash(self): 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) return hash(salt)
def set_cum(self, *args, **kwargs): def set_cum(self, *args, **kwargs):
@ -27,11 +30,19 @@ init python:
@functools.cache @functools.cache
def get_layers(self, hash): def get_layers(self, hash):
def _lookahead(path):
return any(fp.startswith(path) for fp in renpy.list_files())
cum = self._cum cum = self._cum
extensions = self.extensions extensions = self.extensions
types = self.layer_types types = self.layer_types
modifiers = self.layer_modifiers modifiers = self.layer_modifiers
face_layers = self.char.face_layers
active_faces = self.char.face._face
active_clothes = self.char.clothes
layers = {} layers = {}
for part, name in cum.items(): for part, name in cum.items():
@ -39,8 +50,33 @@ init python:
if name is None: if name is None:
continue continue
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) 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(): for f in renpy.list_files():
fp, fn = os.path.split(f) fp, fn = os.path.split(f)
fn, ext = os.path.splitext(fn) fn, ext = os.path.splitext(fn)
@ -54,7 +90,7 @@ init python:
print("Invalid layer type for file: {}".format(f)) print("Invalid layer type for file: {}".format(f))
continue continue
zorder = types.get(ltype) zorder = zorder or types.get(ltype)
if tails: if tails:
lmodifier, *tails = tails lmodifier, *tails = tails

View File

@ -2,9 +2,8 @@ init python:
class DollFace(DollMethods): class DollFace(DollMethods):
layer_types = { layer_types = {
"eyemask": -1, "eyemask": -1,
"skin": 0, "skin": 1,
"expression": 1, "expression": None,
"overlay": 2,
} }
layer_modifiers = { layer_modifiers = {
@ -13,7 +12,7 @@ init python:
def __init__(self, obj): def __init__(self, obj):
self.char = 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 self._hash = None
def set_face(self, *args, **kwargs): def set_face(self, *args, **kwargs):
@ -34,6 +33,7 @@ init python:
extensions = self.extensions extensions = self.extensions
types = self.layer_types types = self.layer_types
modifiers = self.layer_modifiers modifiers = self.layer_modifiers
face_layers = self.char.face_layers
layers = {} layers = {}
for part, name in face.items(): for part, name in face.items():
@ -56,7 +56,7 @@ init python:
print("Invalid layer type for file: {}".format(f)) print("Invalid layer type for file: {}".format(f))
continue continue
zorder = types.get(ltype) zorder = types.get(ltype) or face_layers.get(part)
if tails: if tails:
lmodifier, *tails = tails lmodifier, *tails = tails

View File

@ -2,27 +2,40 @@ init python:
import asyncio import asyncio
class Doll(DollMethods): class Doll(DollMethods):
layers = [ # 0 - 50 = Skin Layers
("buttplug", -11), # 51 - 100 = Face Layers
("makeup", 111), # multislot # 101 - 300+ = Clothes Layers
("accessory", 121), # multislot
("piercing", 131), # multislot clothing_layers = {
("tattoo", 141), # multislot "buttplug": -11,
("pubes", 151), "makeup": 111, # multislot
("stockings", 161), "accessory": 121, # multislot
("panties", 171), "piercing": 131, # multislot
("garterbelt", 181), "tattoo": 141, # multislot
("bottom", 191), "pubes": 151,
("bra", 201), "stockings": 161,
("top", 211), "panties": 171,
("gloves", 221), "garterbelt": 181,
("robe", 231), "bottom": 191,
("neckwear", 241), "bra": 201,
("hair", 251), "top": 211,
("earrings", 261), "gloves": 221,
("glasses", 271), "robe": 231,
("headgear", 281) "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): def __init__(self, name):
self.wardrobe = {} self.wardrobe = {}
@ -30,7 +43,7 @@ init python:
self.blacklist = [] self.blacklist = []
self.outfits = [] self.outfits = []
self.name = name 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.face = DollFace(self)
self.body = DollBody(self) self.body = DollBody(self)
self.cum = DollCum(self) self.cum = DollCum(self)
@ -131,6 +144,7 @@ init python:
self.rebuild_blacklist() self.rebuild_blacklist()
update_chibi(self.name) update_chibi(self.name)
self.cum.is_stale()
self.is_stale() self.is_stale()
if renpy.showing(get_character_tag(self.name), layer=self.layer): if renpy.showing(get_character_tag(self.name), layer=self.layer):
@ -191,6 +205,7 @@ init python:
self.rebuild_blacklist() self.rebuild_blacklist()
update_chibi(self.name) update_chibi(self.name)
self.cum.is_stale()
self.is_stale() self.is_stale()
if renpy.showing(get_character_tag(self.name), layer=self.layer): if renpy.showing(get_character_tag(self.name), layer=self.layer):
@ -313,6 +328,7 @@ init python:
self.face.set_face(*args, **kwargs) self.face.set_face(*args, **kwargs)
[x[0].is_stale() for x in self.clothes.values() if isinstance(x[0], DollMakeup) and x[2]] [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): def get_face(self):
"""Returns a dictionary containing currently set facial expressions. Used in character studio.""" """Returns a dictionary containing currently set facial expressions. Used in character studio."""