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):
layer_types = {
"mask": "-1",
"skin": 0,
"skin": 1,
"armfix": "+1",
"outline": None,
"extra": None,

View File

@ -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):

View File

@ -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,8 +50,33 @@ init python:
if name is None:
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)
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)
fn, ext = os.path.splitext(fn)
@ -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

View File

@ -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

View File

@ -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."""