488 lines
25 KiB
Python
488 lines
25 KiB
Python
"""Import the supplied 0917 shop slices and author the separate Creator 2.4 prefab.
|
|
|
|
Run with a Pillow-enabled Python. Existing asset UUIDs are deterministic. Artwork is
|
|
copied unchanged; missing shared slices are extracted losslessly from their atlas.
|
|
"""
|
|
import argparse
|
|
import copy
|
|
import json
|
|
import shutil
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
from PIL import Image
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
DEST = ROOT / "assets/shop/redesign0917"
|
|
NS = uuid.UUID("1c30e866-71dc-4b6d-9fb6-3c47a9b69817")
|
|
|
|
# Source artwork keeps its supplied names; imported assets use English names.
|
|
RESOURCE_NAMES = {
|
|
"打折数字": "discount_digits", "个数数量": "quantity_digits",
|
|
"钱数数字": "price_green_digits", "黄色钱数数字": "price_gold_digits",
|
|
"首购数字": "bonus_digits", "商城月卡存图0917": "monthly_card",
|
|
"剩余天数数字": "day_digits", "chuizi00.png": "hammer.png",
|
|
"lijilq.png": "claim_button.png", "mofab00.png": "magic_wand.png",
|
|
"shijian00.png": "freeze.png", "shop_1.png": "header_background.png",
|
|
"tu00.png": "gift_coin_icon.png", "wenzi00.png": "gift_title_small.png",
|
|
"xianshitub.png": "limited_badge.png", "元.png": "currency_yuan.png",
|
|
"加号.png": "bonus_plus.png", "商城底板.png": "shop_background.png",
|
|
"图标00.png": "infinite_health.png", "小圆点00.png": "dot_normal.png",
|
|
"小圆点01.png": "dot_selected.png", "小鱼干小兔.png": "starter_banner.png",
|
|
"幸运礼包小图.png": "lucky_banner.png", "底框.png": "gift_card.png",
|
|
"打折标签.png": "discount_tag.png", "文字01.png": "gift_title_medium.png",
|
|
"文字大.png": "gift_title_large.png", "标签00.png": "first_purchase_badge.png",
|
|
"礼包01.png": "gift_small.png", "礼包02.png": "gift_medium.png",
|
|
"礼包03.png": "gift_large.png", "礼包条.png": "gift_section_title.png",
|
|
"立即购买按.png": "buy_button.png", "超值tu.png": "value_badge.png",
|
|
"金币图底图.png": "coin_card.png", "金币条.png": "coin_section_title.png",
|
|
"钱数按钮.png": "coin_price_button.png", "钱数按钮00.png": "gift_price_button.png",
|
|
"首购小.png": "first_purchase_special_badge.png",
|
|
"30天特权字.png": "privilege_30_days.png", "剩余天数.png": "remaining_days.png",
|
|
"月卡小图.png": "month_banner.png", "立即开通.png": "activate_button.png",
|
|
"立即续费.png": "renew_button.png",
|
|
**{f"金币0{i}.png": f"coin_0{i}.png" for i in range(1, 7)},
|
|
}
|
|
|
|
|
|
def english_path(path):
|
|
result = Path(*[RESOURCE_NAMES.get(part.strip(), part.strip()) for part in Path(path).parts])
|
|
if not result.as_posix().isascii():
|
|
raise ValueError(f"Missing English asset name: {path}")
|
|
return result
|
|
|
|
|
|
def uid(key):
|
|
return str(uuid.uuid5(NS, key))
|
|
|
|
|
|
def compressed_uuid(value):
|
|
digits = value.replace("-", "")
|
|
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
|
result = digits[:5]
|
|
for i in range(5, 32, 3):
|
|
n = int(digits[i:i+3], 16)
|
|
result += alphabet[n >> 6] + alphabet[n & 63]
|
|
return result
|
|
|
|
|
|
def write_json(path, data):
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
|
|
|
|
def folder(path):
|
|
path.mkdir(parents=True, exist_ok=True)
|
|
meta_path = Path(str(path) + ".meta")
|
|
existing = json.loads(meta_path.read_text(encoding="utf-8")) if meta_path.exists() else {}
|
|
write_json(meta_path, dict(ver="1.1.3", uuid=existing.get("uuid", uid(str(path.relative_to(ROOT)))),
|
|
importer="folder", isBundle=False, bundleName="", priority=1,
|
|
compressionType={}, optimizeHotUpdate={}, inlineSpriteFrames={}, isRemoteBundle={}, subMetas={}))
|
|
|
|
|
|
def texture(path):
|
|
w, h = Image.open(path).size
|
|
key = path.relative_to(ROOT).as_posix()
|
|
tex, frame = uid(key), uid(key + "/frame")
|
|
meta_path = Path(str(path) + ".meta")
|
|
if meta_path.exists():
|
|
existing = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
tex = existing["uuid"]
|
|
frame = next(iter(existing["subMetas"].values()))["uuid"]
|
|
sub = dict(ver="1.0.6", uuid=frame, importer="sprite-frame", rawTextureUuid=tex,
|
|
trimType="none", trimThreshold=1, rotated=False, offsetX=0, offsetY=0,
|
|
trimX=0, trimY=0, width=w, height=h, rawWidth=w, rawHeight=h,
|
|
borderTop=0, borderBottom=0, borderLeft=0, borderRight=0, subMetas={})
|
|
write_json(Path(str(path) + ".meta"), dict(ver="2.3.7", uuid=tex, importer="texture",
|
|
type="sprite", wrapMode="clamp", filterMode="bilinear", premultiplyAlpha=False,
|
|
genMipmaps=False, packable=w <= 1024 and h <= 1024, width=w, height=h,
|
|
platformSettings={}, subMetas={path.stem: sub}))
|
|
return frame, w, h
|
|
|
|
|
|
def trimmed_sprites(data, frame_sizes):
|
|
"""Keep authored layout nodes; native-size TRIMMED images are scaled leaves.
|
|
|
|
Children and widgets stay on the layout node, so changing image size mode
|
|
cannot resize its buttons, displace text, or scale the entire child hierarchy.
|
|
"""
|
|
# Creator's built-in 2x2 splash frame is used by the payment overlay masks.
|
|
frame_sizes = {"a23235d1-15db-4b95-8439-a2e005bfff91": (2, 2), **frame_sizes}
|
|
for i, sprite in list(enumerate(data)):
|
|
if sprite["__type__"] != "cc.Sprite":
|
|
continue
|
|
owner_id = sprite["node"]["__id__"]
|
|
owner = data[owner_id]
|
|
if owner["_name"] == "SpriteVisual":
|
|
sprite["_sizeMode"] = 1
|
|
continue
|
|
frame = sprite.get("_spriteFrame")
|
|
w, h = frame_sizes[frame["__uuid__"]] if frame else (owner["_contentSize"]["width"], owner["_contentSize"]["height"])
|
|
visual = copy.deepcopy(owner)
|
|
visual.update(_name="SpriteVisual", _parent={"__id__": owner_id}, _children=[],
|
|
_components=[{"__id__": i}], _prefab=None, _opacity=255, _active=True, _id="",
|
|
_skewX=0, _skewY=0)
|
|
visual["_contentSize"].update(width=w, height=h)
|
|
visual["_anchorPoint"].update(x=.5, y=.5)
|
|
visual["_eulerAngles"].update(x=0, y=0, z=0)
|
|
size, anchor = owner["_contentSize"], owner["_anchorPoint"]
|
|
visual["_trs"]["array"] = [(0.5-anchor["x"])*size["width"], (0.5-anchor["y"])*size["height"],
|
|
0, 0, 0, 0, 1, size["width"]/w if w else 1, size["height"]/h if h else 1, 1]
|
|
visual_id = len(data)
|
|
data.append(visual)
|
|
owner["_components"].remove({"__id__": i})
|
|
owner["_children"].insert(0, {"__id__": visual_id})
|
|
sprite.update(node={"__id__": visual_id}, _sizeMode=1)
|
|
|
|
|
|
def asset_index():
|
|
frames, textures = {}, {}
|
|
for p in (ROOT / "assets").rglob("*.meta"):
|
|
if DEST in p.parents:
|
|
continue
|
|
try:
|
|
m = json.loads(p.read_text(encoding="utf-8-sig"))
|
|
except (ValueError, OSError):
|
|
continue
|
|
if m.get("importer") == "texture":
|
|
textures[m["uuid"]] = Path(str(p)[:-5])
|
|
for name, f in m.get("subMetas", {}).items():
|
|
if f.get("importer") == "sprite-frame":
|
|
frames[f["uuid"]] = (p, name, f)
|
|
return frames, textures
|
|
|
|
|
|
def extract(frame, textures):
|
|
im = Image.open(textures[frame["rawTextureUuid"]]).convert("RGBA")
|
|
x, y, w, h = [frame[k] for k in ("trimX", "trimY", "width", "height")]
|
|
im = im.crop((x, y, x + (h if frame["rotated"] else w), y + (w if frame["rotated"] else h)))
|
|
if frame["rotated"]:
|
|
im = im.transpose(Image.Transpose.ROTATE_90)
|
|
result = Image.new("RGBA", (frame["rawWidth"], frame["rawHeight"]))
|
|
result.paste(im, (round((result.width-w)/2+frame["offsetX"]), round((result.height-h)/2-frame["offsetY"])))
|
|
return result
|
|
|
|
|
|
def build(source):
|
|
folder(DEST)
|
|
folder(DEST / "texture")
|
|
folder(DEST / "texture/shared")
|
|
frames, textures = asset_index()
|
|
art, provenance = {}, {}
|
|
for p in sorted(source.rglob("*.png")):
|
|
rel = Path(*[part.strip() for part in p.relative_to(source).parts])
|
|
imported = english_path(rel)
|
|
dst = DEST / "texture" / imported
|
|
if not dst.parent.exists():
|
|
folder(dst.parent)
|
|
shutil.copyfile(p, dst)
|
|
art[rel.as_posix()] = texture(dst)
|
|
provenance[imported.as_posix()] = str(p)
|
|
|
|
old = json.loads((ROOT / "assets/shop/prefab/shop.prefab").read_text(encoding="utf-8"))
|
|
copied = {}
|
|
|
|
def shared(frame_uuid, name=None):
|
|
if frame_uuid in copied:
|
|
return copied[frame_uuid]
|
|
meta, original, f = frames[frame_uuid]
|
|
name = name or (Path(original).stem + "_" + frame_uuid[:8])
|
|
rel = "shared/" + name + ".png"
|
|
dst = DEST / "texture" / rel
|
|
extract(f, textures).save(dst)
|
|
art[rel] = texture(dst)
|
|
provenance[rel] = f"{meta.relative_to(ROOT).as_posix()} :: {original}"
|
|
copied[frame_uuid] = rel
|
|
return rel
|
|
|
|
def named(atlas, name, output):
|
|
f = json.loads((ROOT / atlas).read_text(encoding="utf-8"))["subMetas"][name]
|
|
return shared(f["uuid"], output)
|
|
|
|
hour = [named("assets/shop/img/texture_atlas-1.plist.meta", f"{n}h.png", f"hours_{n}") for n in (1, 2, 4)]
|
|
multiply = shared("50d5f850-fb89-461d-9f80-becccda30060", "multiply")
|
|
yellow_yuan = shared("1375b028-68b3-484a-972b-32553c1269f6", "yuan_yellow")
|
|
hp = [named("assets/common/font.plist.meta", f"hp_{n}.png", f"hp_{n}") for n in range(10)]
|
|
time = [named("assets/common/font.plist.meta", f"time_{n}.png", f"time_{n}") for n in range(10)]
|
|
colon = named("assets/common/font.plist.meta", "time_10.png", "time_colon")
|
|
|
|
data = [dict(__type__="cc.Prefab", _name="shop0917", _objFlags=0, _native="",
|
|
data={"__id__": 1}, optimizationPolicy=0, asyncLoadAssets=False, readonly=False)]
|
|
ref = lambda i: {"__id__": i}
|
|
asset = lambda key: {"__uuid__": art[key][0]}
|
|
templates = {t: next(x for x in old if x["__type__"] == t) for t in
|
|
("cc.Node", "cc.Sprite", "cc.Button", "cc.Widget", "cc.Mask", "cc.ScrollView")}
|
|
|
|
def component(node, kind, **props):
|
|
c = copy.deepcopy(templates.get(kind, dict(__type__=kind, _name="", _objFlags=0, _enabled=True, _id="")))
|
|
c.update(node=ref(node), **props)
|
|
i = len(data)
|
|
data.append(c)
|
|
data[node]["_components"].append(ref(i))
|
|
return i
|
|
|
|
def node(name, parent, x=0, y=0, w=0, h=0, key=None, active=True, anchor_y=.5):
|
|
n = copy.deepcopy(templates["cc.Node"])
|
|
n.update(_name=name, _parent=ref(parent) if parent else None, _children=[], _components=[],
|
|
_active=active, _prefab=None, _opacity=255, _id="")
|
|
n["_trs"]["array"] = [x, y, 0, 0, 0, 0, 1, 1, 1, 1]
|
|
n["_contentSize"].update(width=w, height=h)
|
|
n["_anchorPoint"].update(x=.5, y=anchor_y)
|
|
i = len(data)
|
|
data.append(n)
|
|
if parent:
|
|
data[parent]["_children"].append(ref(i))
|
|
if key:
|
|
component(i, "cc.Sprite", _spriteFrame=asset(key))
|
|
return i
|
|
|
|
def image(name, parent, x, y, key, w=None, h=None):
|
|
_, iw, ih = art[key]
|
|
return node(name, parent, x, y, w or iw, h or ih, key)
|
|
|
|
def widget(n, flags=45, top=0, bottom=0):
|
|
component(n, "cc.Widget", _alignFlags=flags, _top=top, _bottom=bottom, _left=0, _right=0)
|
|
|
|
def button(n, handler, custom="", script="shop"):
|
|
e = len(data)
|
|
data.append(dict(__type__="cc.ClickEvent", target=ref(1), component=script,
|
|
_componentId="", handler=handler, customEventData=custom))
|
|
component(n, "cc.Button", clickEvents=[ref(e)], zoomScale=.97, **{"_N$target": ref(n)})
|
|
|
|
def number(name, parent, x, y, value, group, height, gap=-2, suffix=None):
|
|
keys = [f"{group}/{c}.png" for c in str(value)]
|
|
if suffix:
|
|
keys.append(suffix)
|
|
widths = [art[k][1]*height/art[k][2] for k in keys]
|
|
total = sum(widths) + gap*(len(keys)-1)
|
|
n = node(name, parent, x, y, total, height)
|
|
cursor = -total/2
|
|
for i, (key, width) in enumerate(zip(keys, widths)):
|
|
image(f"glyph_{i}", n, cursor+width/2, 0, key, width, height)
|
|
cursor += width+gap
|
|
return n
|
|
|
|
def clone_tree(old_id, parent):
|
|
# Clone an existing shared avatar/loading/dialog subtree, preserving local
|
|
# references while copying all its visible image slices into the new folder.
|
|
ids = set()
|
|
def visit(i):
|
|
if i in ids:
|
|
return
|
|
ids.add(i)
|
|
obj = old[i]
|
|
if obj["__type__"] == "cc.Node":
|
|
for r in obj["_children"] + obj["_components"]:
|
|
visit(r["__id__"])
|
|
for r in obj.get("clickEvents", []):
|
|
visit(r["__id__"])
|
|
visit(old_id)
|
|
mapping = {i: len(data)+j for j, i in enumerate(sorted(ids))}
|
|
def remap(v):
|
|
if isinstance(v, dict):
|
|
if "__id__" in v:
|
|
return ref(mapping[v["__id__"]]) if v["__id__"] in mapping else (ref(1) if v["__id__"] == 1 else None)
|
|
if "__uuid__" in v and v["__uuid__"] in frames:
|
|
return asset(shared(v["__uuid__"]))
|
|
return {k: remap(a) for k, a in v.items()}
|
|
if isinstance(v, list):
|
|
return [remap(a) for a in v]
|
|
return v
|
|
for i in sorted(ids):
|
|
obj = remap(copy.deepcopy(old[i]))
|
|
if obj["__type__"] == "cc.Node":
|
|
obj["_prefab"] = None
|
|
data.append(obj)
|
|
result = mapping[old_id]
|
|
data[result]["_parent"] = ref(parent)
|
|
data[parent]["_children"].append(ref(result))
|
|
return result
|
|
|
|
# Existing delivery/month-card callbacks locate Canvas/shop by name.
|
|
root = node("shop", None, 540, 1170, 1080, 2340)
|
|
widget(root)
|
|
component(root, "cc.BlockInputEvents")
|
|
background = image("Background", root, 0, 0, "商城底板.png", 1080, 2340)
|
|
widget(background)
|
|
# Only the product area scrolls. The first 1005 reference pixels stay fixed.
|
|
scroll = node("ProductsScrollView", root, 0, -502.5, 1080, 1335)
|
|
widget(scroll, top=1005)
|
|
view = node("view", scroll, 0, 0, 1080, 1335)
|
|
widget(view)
|
|
component(view, "cc.Mask")
|
|
content = node("content", view, 0, 667.5, 1080, 2855, anchor_y=1)
|
|
scroll_comp = component(scroll, "cc.ScrollView", **{"_N$content": ref(content), "content": ref(content),
|
|
"_N$verticalScrollBar": None, "elastic": True, "brake": .75})
|
|
image("ProductBackground", content, 0, -1427.5, "商城底板.png", 1080, 2855)
|
|
image("GiftSectionTitle", content, 0, -69, "礼包条.png")
|
|
for i, (price, coins, discount, title, hours, count) in enumerate([
|
|
(10, 2500, "7", "wenzi00.png", 1, 0),
|
|
(20, 5000, "55", "文字01.png", 2, 2),
|
|
(30, 7500, "4", "文字大.png", 4, 5),
|
|
]):
|
|
# Reference card tops: 1143, 1635, 2135 (relative to full reference image).
|
|
cy = -(138 + [0, 492, 992][i] + 226)
|
|
card = image(f"Gift_{price}", content, 0, cy, "底框.png")
|
|
image("GiftTitle", card, -252, -149, title)
|
|
image("UnlimitedHealth", card, -329, 58 if i == 0 else 112, "图标00.png")
|
|
image("Duration", card, -329, -1 if i == 0 else 52, hour[i])
|
|
image("CoinsIcon", card, -146, 62 if i == 0 else 110, "tu00.png")
|
|
number("CoinAmount", card, -146, -4 if i == 0 else 49, coins, "个数数量", 46)
|
|
if count:
|
|
for j, key in enumerate(["chuizi00.png", "shijian00.png", "mofab00.png"]):
|
|
px = -365+j*120
|
|
image("PropIcon"+str(j), card, px, -25, key, art[key][1]*.85, art[key][2]*.85)
|
|
amount = node("PropAmount"+str(j), card, px, -87, 70, 40)
|
|
image("multiply", amount, -20, 0, multiply, 25, 25)
|
|
number("count", amount, 13, 0, count, "个数数量", 42)
|
|
tag = image("DiscountTag", card, 32, 154, "打折标签.png")
|
|
digits = number("Discount", tag, -9 if len(discount) == 1 else -22, 14, discount, "打折数字", 52)
|
|
# The tag art itself is slanted; its digits follow that printed baseline.
|
|
import math
|
|
data[digits]["_trs"]["array"][5:7] = [math.sin(math.radians(-13)/2), math.cos(math.radians(-13)/2)]
|
|
data[digits]["_eulerAngles"]["z"] = -13
|
|
gift_key = f"礼包0{i+1}.png"
|
|
image("GiftArtwork", card, 259, 48, gift_key)
|
|
buy = image("Buy", card, 265, -136, "钱数按钮00.png")
|
|
button(buy, "buyProduct", f"unlimited_health_bundle_{price}")
|
|
number("Price", buy, 0, 7, price, "黄色钱数数字", 65, 4, yellow_yuan)
|
|
image("CoinSectionTitle", content, 0, -1689, "金币条.png")
|
|
offers = []
|
|
for i, (coins, price) in enumerate(zip([1200, 8000, 16000, 32000, 100000, 240000], [6, 36, 68, 128, 328, 648])):
|
|
card = image(f"gold_{i+1}", content, -330+(i%3)*330, -(1988+(i//3)*494), "金币图底图.png")
|
|
offer = node("FirstPurchase", card)
|
|
image("FirstPurchaseBadge", offer, -116, 149, "标签00.png")
|
|
amount = number("BonusAmount", offer, 43, 153, coins, "首购数字", 41)
|
|
image("Plus", amount, -data[amount]["_contentSize"]["width"]/2-16, 0, "加号.png", 28, 28)
|
|
offers.append(offer)
|
|
key = f"金币0{i+1}.png"
|
|
image("CoinArtwork", card, 0, -10, key)
|
|
number("CoinAmount", card, 0, -87, coins, "个数数量", 46)
|
|
buy = image("Buy", card, 0, -181, "钱数按钮.png")
|
|
button(buy, "buyProduct", f"gold_{i+1}")
|
|
number("Price", buy, 0, 7, price, "钱数数字", 54, 1, "元.png")
|
|
|
|
top = node("Top", root, 0, 667.5, 1080, 1005)
|
|
widget(top, flags=41)
|
|
image("HeaderBackground", top, 0, 0, "shop_1.png")
|
|
for original, name in [(472, "kuang"), (477, "avatar")]:
|
|
n = clone_tree(original, top)
|
|
data[n]["_trs"]["array"][:2] = [-427, 185.5]
|
|
# Avatar frame is decorative here; the profile page remains outside shop.
|
|
data[n]["_components"] = [r for r in data[n]["_components"] if data[r["__id__"]]["__type__"] != "cc.Button"]
|
|
close = image("Close", top, 436, 139.5, shared("4eb76a13-bdd5-44cb-975d-32fd4900be79", "close"))
|
|
button(close, "closeShop", "exit")
|
|
stamina = node("Stamina", top, -169, 224.5, 285, 100)
|
|
image("bg", stamina, 26.654, 0, shared("27e4e784-2bb1-40b2-9726-a3d160165c43", "health_background"), 236, 62)
|
|
progress = image("progresss", stamina, 26.654, 0, shared("21ec0fc2-b090-4eb7-bb39-83b6de6b2f87", "health_fill"), 238, 64)
|
|
data[data[progress]["_components"][0]["__id__"]].update(_type=3, _fillRange=1)
|
|
full = node("man", stamina)
|
|
image("Full", full, 25, 0, shared("1eabaff0-07cb-4de7-a55d-75a068061ea2", "health_full"), 74, 38)
|
|
image("Heart", stamina, -98, 0, shared("b6b570b9-d928-4646-8a2a-70f18cc10991", "heart"), 92, 86)
|
|
plus = shared("f9ab818d-bcb7-465b-af54-0af12cf7d936", "plus_green")
|
|
image("Plus", stamina, -69, -25, plus, 38, 38)
|
|
health = node("health", stamina, -98, 0)
|
|
image("glyph_0", health, 0, 0, hp[5], 30, 39)
|
|
timer = node("time", stamina, 27, 0, active=False)
|
|
skyline = node("skyLine", stamina, 0, 0, active=False)
|
|
image("Infinity", skyline, -98, 0, shared("419e6dd8-8ab5-4910-bf94-b82afe18191b", "health_infinite"), 99, 87)
|
|
skytime = node("skyTime", skyline, 27, 0)
|
|
button(stamina, "openHealth", script="Shop0917View")
|
|
coinbar = node("Coin", top, 129, 224.5, 275, 100)
|
|
image("Background", coinbar, 5, 0, shared("e29788dd-4216-4e8b-9186-25040fc8e3ec", "coin_background"), 238, 64)
|
|
image("Icon", coinbar, -105, 0, shared("94a3bed8-306b-4f18-9806-2df14cc935f3", "coin_icon"), 84, 95)
|
|
image("Plus", coinbar, -69, -25, plus, 38, 38)
|
|
coin = number("Coin", coinbar, 30, 0, 563, "个数数量", 37)
|
|
button(coinbar, "scrollToCoins", script="Shop0917View")
|
|
|
|
carousel = node("Carousel", top, 0, -247.5, 1080, 381)
|
|
component(carousel, "cc.Mask")
|
|
banners = []
|
|
# The starter_pack purchase opens NewbieGift (lucky gift); Jungle is the fish-box activity.
|
|
for i, key in enumerate(["幸运礼包小图.png", "商城月卡存图0917/月卡小图.png", "小鱼干小兔.png"]):
|
|
card = image(["StarterBanner", "MonthBanner", "LuckyBanner"][i], carousel, (i-1)*900+5, 0, key, 862, 381)
|
|
banners.append(card)
|
|
if i == 1:
|
|
month_title = image("Privilege", card, 357, 121, "商城月卡存图0917/30天特权字.png")
|
|
month_days = node("RemainingDays", card, 357, 121, active=False)
|
|
image("Caption", month_days, 0, 0, "商城月卡存图0917/剩余天数.png")
|
|
day_number = number("Days", month_days, 0, 0, 30, "商城月卡存图0917/剩余天数数字", 32)
|
|
month_buy = image("Open", card, 254, -83, "商城月卡存图0917/立即开通.png")
|
|
button(month_buy, "openmonthCard")
|
|
else:
|
|
image("Badge", card, 337, 110, "xianshitub.png" if i == 0 else "超值tu.png")
|
|
buy = image("Open", card, 254, -83, "立即购买按.png" if i == 0 else "lijilq.png")
|
|
button(buy, "openStarter" if i == 0 else "openJungleTreasure", script="Shop0917View" if i == 0 else "shop")
|
|
dots = []
|
|
for i in range(3):
|
|
dot = image("Dot"+str(i), top, (i-1)*47, -464.5, "小圆点01.png" if i == 1 else "小圆点00.png")
|
|
button(dot, "selectBanner", str(i), "Shop0917View")
|
|
dots.append(dot)
|
|
# Shared payment overlays retain their existing behavior and are independent
|
|
# of the supplied storefront design. The loading animation needs no font text.
|
|
loading = clone_tree(560, root)
|
|
data[loading]["_active"] = False
|
|
data[loading]["_children"] = [r for r in data[loading]["_children"] if data[r["__id__"]]["_name"] != "New Label"]
|
|
confirm = clone_tree(597, root)
|
|
data[confirm]["_active"] = False
|
|
# Serialize the controller with the same payment/lifecycle API as the old shop.
|
|
controller = copy.deepcopy(old[624])
|
|
controller.update(node=ref(root), shop=ref(root), itemList=ref(content), coin=ref(coin),
|
|
Stamina=ref(stamina), monthCardTime=ref(month_days), coinAnim=None)
|
|
controller_id = len(data)
|
|
data.append(controller)
|
|
data[root]["_components"].append(ref(controller_id))
|
|
view_uuid = json.loads((ROOT / "assets/shop/script/Shop0917View.ts.meta").read_text())["uuid"]
|
|
v = component(root, compressed_uuid(view_uuid), scrollView=ref(scroll_comp), carousel=ref(carousel),
|
|
banners=list(map(ref, banners)), dots=list(map(ref, dots)), firstPurchase=list(map(ref, offers)),
|
|
coin=ref(coin), health=ref(health), monthPrivilege=ref(month_title), monthRemaining=ref(month_days),
|
|
monthDays=ref(day_number), monthButton=ref(data[month_buy]["_components"][0]["__id__"]),
|
|
countFrames=[asset(f"个数数量/{i}.png") for i in range(10)],
|
|
healthFrames=[asset(k) for k in hp], timeFrames=[asset(k) for k in time], colonFrame=asset(colon),
|
|
dayFrames=[asset(f"商城月卡存图0917/剩余天数数字/{i}.png") for i in range(10)],
|
|
monthOpen=asset("商城月卡存图0917/立即开通.png"), monthRenew=asset("商城月卡存图0917/立即续费.png"),
|
|
dotNormal=asset("小圆点00.png"), dotSelected=asset("小圆点01.png"))
|
|
controller["redesignView"] = ref(v)
|
|
|
|
# Remove detached legacy objects and assign stable prefab file IDs to all nodes.
|
|
live = set()
|
|
def collect(i):
|
|
if i in live:
|
|
return
|
|
live.add(i)
|
|
obj = data[i]
|
|
def scan(value):
|
|
if isinstance(value, dict):
|
|
if "__id__" in value:
|
|
collect(value["__id__"])
|
|
else:
|
|
for a in value.values(): scan(a)
|
|
elif isinstance(value, list):
|
|
for a in value: scan(a)
|
|
scan(obj)
|
|
collect(0)
|
|
mapping = {i: j for j, i in enumerate(sorted(live))}
|
|
def reindex(v):
|
|
if isinstance(v, dict):
|
|
return {"__id__": mapping[v["__id__"]]} if "__id__" in v else {k: reindex(a) for k,a in v.items()}
|
|
return [reindex(a) for a in v] if isinstance(v, list) else v
|
|
data = [reindex(data[i]) for i in sorted(live)]
|
|
trimmed_sprites(data, {frame: (w, h) for frame, w, h in art.values()})
|
|
for i, obj in list(enumerate(data)):
|
|
if obj["__type__"] == "cc.Node":
|
|
obj["_prefab"] = ref(len(data))
|
|
data.append(dict(__type__="cc.PrefabInfo", root=ref(1), asset=ref(0),
|
|
fileId=uid(f"node/{i}/{obj['_name']}").replace("-", ""), sync=False))
|
|
prefab = ROOT / "assets/shop/prefab/shop0917.prefab"
|
|
write_json(prefab, data)
|
|
write_json(Path(str(prefab)+".meta"), dict(ver="1.3.2", uuid=uid("shop0917.prefab"),
|
|
importer="prefab", optimizationPolicy="MULTI_INSTANCE", asyncLoadAssets=False, readonly=False, subMetas={}))
|
|
write_json(ROOT / "docs/shop0917-resource-manifest.json", provenance)
|
|
print(f"Authored {prefab.relative_to(ROOT)}: {len(data)} objects, {len(art)} independent image slices")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("source", type=Path)
|
|
build(parser.parse_args().source)
|