Outfit import/export
* Re-implemented image payload injection using Python3-compliant methods * Added sanity checks for image importing and exporting * Added compression for the payload * Added user input for exported image file names * Removed redundant layer for Tonks' ribbon bra * Updated MyMod clothing definition example. * Fixed regression in outfit clone functions
This commit is contained in:
parent
5fdf1e87fe
commit
fb8e3aab0f
BIN
game/characters/tonks/clothes/bra/ribbon/backupskin.webp
(Stored with Git LFS)
BIN
game/characters/tonks/clothes/bra/ribbon/backupskin.webp
(Stored with Git LFS)
Binary file not shown.
@ -1,13 +1,19 @@
|
|||||||
# Add new hairstyle for character as an instance of DollCloth,
|
# Add new hairstyle for character as an instance of DollCloth,
|
||||||
# make sure the variable name is unique, preferably starting with mod name.
|
# make sure the variable name is unique, preferably starting with mod name.
|
||||||
default MyMod_ponytail = DollCloth(
|
default MyMod_ponytail = DollCloth(
|
||||||
modpath="mods/mymod/", # shortened filepath to this file
|
modpath="MyMod", # File path; Usually a mod folder name. (case insensitive)
|
||||||
name="hermione", # Character name (case sensitive)
|
name="hermione", # Character name (case sensitive)
|
||||||
categories=("head","hair"), # Main category and subcategory of the item
|
categories=("head","hair"), # Main category and subcategory of the item (case sensitive)
|
||||||
type="hair", # Item type
|
type="hair", # Item type (case sensitive)
|
||||||
id="ponytail", # Item identificator
|
id="ponytail", # Item identifier (case sensitive)
|
||||||
unlocked=True, # True=Item is unlocked by default, False=Item is a part of the outfit and requries to be bought
|
unlocked=True, # True=Item is unlocked by default, False=Item is a part of the outfit and requries to be bought
|
||||||
level=0, # Character whoring/friendship level required to wear this cloth. (Optional)
|
level=0, # Character whoring/friendship level required to wear this cloth. (Optional)
|
||||||
armfix=False, # If cloth images intersect with arm layers, set to True. (Optional)
|
color=["#985930", "#c38959"], # Python list with default colours in one of the formats listed down below.
|
||||||
color=[[152, 89, 48, 255], [195, 137, 89, 255]] # Python list with default colours in RGBA format applicable for each colourable file layer
|
zorder=None # Item zorder number, or None, if None default zorder for the slot will be used.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Example valid colour formats:
|
||||||
|
# RGB: [(255, 255, 255), (255, 255, 255)]
|
||||||
|
# RGBA: [(255, 255, 255, 255), (255, 255, 255, 255)]
|
||||||
|
# HEX: [("#985930", "#c38959")]
|
||||||
|
# HEXA: [("#985930FF", "#c38959FF")]
|
||||||
|
@ -252,8 +252,6 @@ init python:
|
|||||||
|
|
||||||
def clone(self):
|
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."""
|
"""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 DollCloth(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)
|
return DollCloth(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)
|
||||||
|
|
||||||
def is_modded(self):
|
def is_modded(self):
|
||||||
|
@ -183,6 +183,4 @@ init python:
|
|||||||
|
|
||||||
def clone(self):
|
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."""
|
"""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)
|
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)
|
||||||
|
@ -505,11 +505,11 @@ init python:
|
|||||||
# Grab data
|
# Grab data
|
||||||
if fromfile:
|
if fromfile:
|
||||||
try:
|
try:
|
||||||
imported = image_payload.decode(path)
|
imported = ImagePayload().extract(path)
|
||||||
except:
|
except Exception as e:
|
||||||
if image_payload._file:
|
|
||||||
image_payload._file.close()
|
|
||||||
renpy.notify("Import failed: Corrupted file.")
|
renpy.notify("Import failed: Corrupted file.")
|
||||||
|
print(e)
|
||||||
|
renpy.block_rollback()
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
imported = get_clipboard()
|
imported = get_clipboard()
|
||||||
@ -518,8 +518,9 @@ init python:
|
|||||||
if imported:
|
if imported:
|
||||||
try:
|
try:
|
||||||
imported = make_revertable(evaluate(imported))
|
imported = make_revertable(evaluate(imported))
|
||||||
except:
|
except Exception as e:
|
||||||
renpy.notify("Import failed: Corrupted outfit data.")
|
renpy.notify("Import failed: Corrupted outfit data.")
|
||||||
|
print(e)
|
||||||
renpy.block_rollback()
|
renpy.block_rollback()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -86,16 +86,21 @@ init python:
|
|||||||
def export_data(self, filename, tofile=True):
|
def export_data(self, filename, tofile=True):
|
||||||
"""Exports outfit to .png file or clipboard text."""
|
"""Exports outfit to .png file or clipboard text."""
|
||||||
exported = [self.group[0].name]
|
exported = [self.group[0].name]
|
||||||
exported.extend([x.id, x.color] for x in self.group)
|
|
||||||
|
for i in self.group:
|
||||||
|
if i.color:
|
||||||
|
color = [j.hexcode for j in i.color]
|
||||||
|
exported.append([i.id, color])
|
||||||
|
|
||||||
# Encode data
|
# Encode data
|
||||||
if tofile:
|
if tofile:
|
||||||
path = "{}/game/outfits/".format(config.basedir)
|
path = os.path.join(config.gamedir, "outfits")
|
||||||
fn = "{}.png".format(filename)
|
|
||||||
|
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
|
|
||||||
|
path = os.path.join(path, "_temp.png")
|
||||||
|
|
||||||
d = Transform(self.image, crop=(210, 200, 700, 1000), anchor=(0.5, 1.0), align=(0.5, 1.0), xsize=310, ysize=470, fit="contain")
|
d = Transform(self.image, crop=(210, 200, 700, 1000), anchor=(0.5, 1.0), align=(0.5, 1.0), xsize=310, ysize=470, fit="contain")
|
||||||
d = Fixed(
|
d = Fixed(
|
||||||
"interface/wardrobe/export_background.webp",
|
"interface/wardrobe/export_background.webp",
|
||||||
@ -105,8 +110,9 @@ init python:
|
|||||||
Text("Ver. {}".format(config.version), size=10, align=(0.99, 0.99))
|
Text("Ver. {}".format(config.version), size=10, align=(0.99, 0.99))
|
||||||
)
|
)
|
||||||
|
|
||||||
displayable_to_file(d, path+fn, size=(310, 470) )
|
displayable_to_file(d, path, size=(310, 470) )
|
||||||
image_payload.encode(filename, str(exported))
|
ImagePayload().inject("_temp.png", filename, str(exported))
|
||||||
|
os.remove(path)
|
||||||
else:
|
else:
|
||||||
set_clipboard(exported)
|
set_clipboard(exported)
|
||||||
renpy.notify("Export successful!")
|
renpy.notify("Export successful!")
|
||||||
|
@ -97,7 +97,7 @@ init -1 python:
|
|||||||
try:
|
try:
|
||||||
return __import__('ast').literal_eval(txt)
|
return __import__('ast').literal_eval(txt)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error evaluating clipboard data:")
|
print("Error evaluating data:")
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def reset_variables(*args):
|
def reset_variables(*args):
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -266,7 +266,12 @@ label wardrobe_menu():
|
|||||||
|
|
||||||
elif _choice[0] == "export":
|
elif _choice[0] == "export":
|
||||||
python:
|
python:
|
||||||
_choice[1].export_data(datetime.datetime.now().strftime("%d %b %Y-%H%M%S"))
|
filename = renpy.input("Save as:", datetime.datetime.now().strftime("%d %b %Y-%H%M%S"))
|
||||||
|
|
||||||
|
if not filename.endswith(".png"):
|
||||||
|
filename += ".png"
|
||||||
|
|
||||||
|
_choice[1].export_data(filename)
|
||||||
achievements.unlock("export")
|
achievements.unlock("export")
|
||||||
|
|
||||||
elif _choice[0] == "import":
|
elif _choice[0] == "import":
|
||||||
|
Loading…
Reference in New Issue
Block a user