forked from SilverStudioGames/WTS
157 lines
6.2 KiB
Plaintext
157 lines
6.2 KiB
Plaintext
|
init python:
|
||
|
class DollClothDynamic(DollCloth):
|
||
|
prefixes = ("!", "?")
|
||
|
|
||
|
def __init__(self, name, categories, type, id, color, zorder=None, unlocked=False, level=0, blacklist=[], modpath=None, tracking=None, parent=None):
|
||
|
self._tracking = tracking
|
||
|
|
||
|
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})"
|
||
|
|
||
|
@property
|
||
|
def tracking(self):
|
||
|
prefixes = "".join(self.prefixes)
|
||
|
return self._tracking.strip(prefixes)
|
||
|
|
||
|
@property
|
||
|
def tracking_object(self):
|
||
|
tracking = self.tracking
|
||
|
return self.char.clothes.get(tracking)[0]
|
||
|
|
||
|
def generate_hash(self):
|
||
|
tracking_object = self.tracking_object
|
||
|
tracking_hash = str(tracking_object._hash) if tracking_object else "default"
|
||
|
salt = str( sorted( [self.name, self.type, self.id, str(self.color)] ) ) + tracking_hash
|
||
|
return hash(salt)
|
||
|
|
||
|
def get_layers(self, _ignore_equipped=False):
|
||
|
path = os.path.join(self.modpath, "characters", self.name, self.pose, "clothes", self.type, self.id)
|
||
|
_tracking = self._tracking
|
||
|
|
||
|
def _negative_lookahead(tracking):
|
||
|
return None if self.tracking_object else "default"
|
||
|
|
||
|
def _lookahead(tracking, path):
|
||
|
tracking_object = self.tracking_object
|
||
|
tracking_id = tracking_object.id if tracking_object else None
|
||
|
path = os.path.join(path, tracking_id)
|
||
|
|
||
|
if not any(fp.startswith(path) for fp in renpy.list_files()):
|
||
|
return "default"
|
||
|
return tracking_id
|
||
|
|
||
|
def _default(tracking):
|
||
|
tracking_object = self.tracking_object
|
||
|
return tracking_object.id if tracking_object else None
|
||
|
|
||
|
if _ignore_equipped:
|
||
|
tracking_id = "default"
|
||
|
else:
|
||
|
processors = {
|
||
|
"!": lambda tracking, _: _negative_lookahead(_tracking),
|
||
|
"?": lambda tracking, path: _lookahead(_tracking, path),
|
||
|
None: lambda tracking, _: _default(_tracking)
|
||
|
}
|
||
|
|
||
|
prefix = next((p for p in self.prefixes if _tracking.startswith(p)), None)
|
||
|
processor = processors[prefix]
|
||
|
tracking_id = processor(_tracking, path)
|
||
|
|
||
|
if tracking_id is None:
|
||
|
print(f"Invalid tracker for object: {self}")
|
||
|
return {}
|
||
|
|
||
|
path = os.path.join(path, tracking_id)
|
||
|
|
||
|
extensions = self.extensions
|
||
|
types = self.layer_types
|
||
|
modifiers = self.layer_modifiers
|
||
|
|
||
|
layers = {}
|
||
|
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.isdigit() and not ltype in types:
|
||
|
print(f"Invalid layer type for file: {f}")
|
||
|
continue
|
||
|
|
||
|
zorder = z if (z := types.get(ltype)) is not None else self.zorder
|
||
|
|
||
|
if isinstance(zorder, str):
|
||
|
zorder = self.zorder + int(zorder)
|
||
|
|
||
|
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)) if lmodifier != "zorder" else int(tails[-1])
|
||
|
layers.setdefault("_".join([ltype, lmodifier]), [f, zorder])
|
||
|
else:
|
||
|
layers.setdefault(ltype, [f, zorder])
|
||
|
|
||
|
return layers
|
||
|
|
||
|
def rebuild_image(self):
|
||
|
self.layers = self.get_layers()
|
||
|
self._hash = self.generate_hash()
|
||
|
self._image_cached = False
|
||
|
|
||
|
@functools.cache
|
||
|
def build_icon(self, hash):
|
||
|
_tracking = self._tracking
|
||
|
|
||
|
if _tracking.startswith("!"):
|
||
|
self.layers = self.get_layers(_ignore_equipped=True)
|
||
|
hash = self.generate_hash()
|
||
|
tracking_object = None
|
||
|
else:
|
||
|
tracking_object = self.tracking_object
|
||
|
|
||
|
matrix = SaturationMatrix(0.0)
|
||
|
sprites = [i for i in self.build_image(hash, matrix=matrix) if not i[0] == "mask"]
|
||
|
|
||
|
if not tracking_object is None:
|
||
|
sprites.extend([i for i in tracking_object.build_image(tracking_object._hash, matrix=matrix) if not i[0] == "mask"])
|
||
|
|
||
|
sprites.extend(self.char.body.build_image(matrix=matrix))
|
||
|
sprites.sort(key=itemgetter(2))
|
||
|
self.bounds = bounds = self.bounds or self.layers.get("outline", [sprites[0][1]])[0]
|
||
|
|
||
|
wmax, hmax = self.sizes
|
||
|
wmin = hmin = 96
|
||
|
|
||
|
x, y, w, h = crop_whitespace(bounds)
|
||
|
xoffset, yoffset = w/2, h/2
|
||
|
|
||
|
w = h = max(w, h, wmin, hmin)
|
||
|
|
||
|
w = max(wmin, w + w/2)
|
||
|
h = max(hmin, h + h/2)
|
||
|
|
||
|
x = clamp( (x - w/2) + xoffset, 0, wmax)
|
||
|
y = clamp( (y - h/2) + yoffset, 0, hmax)
|
||
|
|
||
|
# Forbid exceeding the image height.
|
||
|
if y+h > hmax:
|
||
|
y = hmax-h
|
||
|
|
||
|
return Transform(Fixed(*[i[1] for i in sprites], fit_first=True), crop=(x, y, w, h))
|
||
|
|
||
|
def clone(self):
|
||
|
"""Creates a clone of this cloth object. Since it requires a parent object it should be used internally only to avoid object depth issue."""
|
||
|
if self.parent:
|
||
|
return self
|
||
|
return DollClothDynamic(self.name, self.categories, self.type, self.id, [x[:] for x in self.color] if self.color else None, self.zorder, self.unlocked, self.level, self.blacklist, self.modpath, self._tracking, self)
|