LoafyLemon
22a84479dd
* 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
144 lines
5.1 KiB
Plaintext
144 lines
5.1 KiB
Plaintext
init python:
|
|
class DollCum(DollMethods):
|
|
layer_types = {
|
|
"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 (self.char.clothing_layers | self.char.face_layers).keys()}
|
|
self._hash = None
|
|
|
|
def generate_hash(self):
|
|
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):
|
|
if args:
|
|
self._cum = {k: args[0] for k in self._cum}
|
|
|
|
self._cum.update(kwargs)
|
|
self.is_stale()
|
|
|
|
@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():
|
|
|
|
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)
|
|
|
|
if not fp == path or not ext in extensions:
|
|
continue
|
|
|
|
ltype, *tails = fn.rsplit("_")
|
|
|
|
if not ltype in types:
|
|
print("Invalid layer type for file: {}".format(f))
|
|
continue
|
|
|
|
zorder = zorder or types.get(ltype)
|
|
|
|
if tails:
|
|
lmodifier, *tails = tails
|
|
|
|
if not lmodifier in modifiers:
|
|
print("Invalid modifier for file: {}".format(f))
|
|
continue
|
|
|
|
zorder_mod = modifiers.get(lmodifier)
|
|
zorder = (zorder + int(zorder_mod))
|
|
layers.setdefault(" ".join([part, name, ltype, lmodifier]), [f, zorder])
|
|
else:
|
|
layers.setdefault(" ".join([part, name, ltype]), [f, zorder])
|
|
|
|
return layers
|
|
|
|
@functools.cache
|
|
def build_image(self, hash, matrix=None):
|
|
if matrix is None:
|
|
matrix = self.char.body.hue
|
|
|
|
processors = {
|
|
"skin": lambda file: Transform(file, matrixcolor=matrix),
|
|
"default": lambda file: Image(file),
|
|
}
|
|
|
|
layers = self.get_layers(hash)
|
|
|
|
sprites = []
|
|
for identifier, (file, zorder) in layers.items():
|
|
|
|
cum_type, name, ltype = identifier.rsplit(" ")
|
|
processor = processors.get(identifier, processors["default"])
|
|
|
|
processed_file = processor(file)
|
|
sprites.append((identifier, processed_file, zorder))
|
|
|
|
return sprites
|
|
|
|
@property
|
|
def image(self):
|
|
if not renpy.is_skipping() and self.is_stale():
|
|
hash = self._hash
|
|
|
|
sprites = self.build_image(hash)
|
|
sprites.sort(key=itemgetter(2))
|
|
sprites = [x[1] for x in sprites]
|
|
|
|
self._image = Fixed(*sprites, fit_first=True)
|
|
return self._image
|