MatchMaster/tools/create-battle-pass-prefabs.cjs
2026-10-09 10:03:29 +08:00

112 lines
7.4 KiB
JavaScript

// One-time conversion to editable Cocos 2.4 assets. Existing prefabs are never overwritten.
const fs = require('node:fs'), path = require('node:path'), crypto = require('node:crypto');
const root = path.resolve(__dirname, '..');
const read = p => JSON.parse(fs.readFileSync(path.join(root, p), 'utf8'));
const clone = v => JSON.parse(JSON.stringify(v));
const uuid = key => { const h = crypto.createHash('sha256').update('pass-prefab:' + key).digest('hex'); return `${h.slice(0,8)}-${h.slice(8,12)}-4${h.slice(13,16)}-a${h.slice(17,20)}-${h.slice(20,32)}`; };
const write = (p, v) => fs.writeFileSync(path.join(root, p), JSON.stringify(v, null, 2) + '\n', {flag:'wx'});
const buy = read('assets/shop/prefab/buyActivate.prefab'), rewards = read('assets/publicUI/prefab/rewardNode.prefab');
const layout = read('assets/action_bundle/prefab/dailyQuests.prefab').find(v => v.__type__ === 'cc.Layout');
function copyTexture(from, name) {
const to = 'assets/passCheck/img/settlement/' + name + '.png';
if (fs.existsSync(path.join(root, to))) return;
const meta = read(from + '.meta'), frame = clone(Object.values(meta.subMetas)[0]);
meta.uuid = uuid(to); frame.uuid = uuid(to + ':frame'); frame.rawTextureUuid = meta.uuid;
meta.subMetas = {[name]:frame};
fs.copyFileSync(path.join(root, from), path.join(root, to), fs.constants.COPYFILE_EXCL);
write(to + '.meta', meta);
}
copyTexture('assets/mini_program_benefits/texture/solid-white.png', 'modal_mask');
copyTexture('assets/coin_madness/texture/ui/ended_reward_bg.png', 'reward_card');
const frame = p => Object.values(read(p + '.meta').subMetas)[0];
const local = n => frame('assets/passCheck/img/settlement/' + n + '.png');
const lose = n => frame('assets/lose/texture/' + n + '.png');
const atlas = read('assets/shop/img2/shopUI.plist.meta').subMetas;
function build(name, contents) {
const file = 'assets/passCheck/prefab/' + name + '.prefab';
if (fs.existsSync(path.join(root, file))) { console.log('Preserved editor asset:', file); return; }
const objects = [clone(buy[0])]; objects[0].data = {__id__:1};
const add = value => { objects.push(value); return objects.length - 1; };
function component(node, source) { const c = clone(source); c.node = {__id__:node}; c._id = ''; const id = add(c); objects[node]._components.push({__id__:id}); return c; }
function node(parent, name, x=0, y=0, w=0, h=0, active=true) {
const n = clone(buy[1]);
n._name=name; n._parent=parent?{__id__:parent}:null; n._children=[]; n._components=[]; n._active=active;
n._contentSize.width=w; n._contentSize.height=h; n._trs.array=[x,y,0,0,0,0,1,1,1,1];
const id=add(n); n._prefab={__id__:add({__type__:'cc.PrefabInfo',root:{__id__:1},asset:{__id__:0},fileId:uuid(file+':'+id),sync:false})};
if(parent)objects[parent]._children.push({__id__:id}); return id;
}
function image(parent, name, f, x=0, y=0, w=f.rawWidth, h=f.rawHeight, sliced=false) {
const id=node(parent,name,x,y,w,h), sprite=component(id,buy[3]);
sprite._spriteFrame={__uuid__:f.uuid}; sprite._type=sliced?1:0; return id;
}
const button = (parent,name,f,x,y,w,h) => {const id=image(parent,name,f,x,y,w,h);component(id,buy[16]);return id;};
function shade(parent,opacity) {
const id=image(parent,'shade',local('modal_mask'),0,0,4000,4000);
objects[id]._opacity=opacity; objects[id]._color={__type__:'cc.Color',r:0,g:0,b:0,a:255}; component(id,buy[4]);
}
function rewardRows(parent) {
const holder=node(parent,'rewards');
for(let row=0;row<2;row++) {
const count=row?2:3, rowNode=node(holder,row?'bottomRow':'topRow',0,-75-row*245,count*206+(count-1)*54,206);
const l=component(rowNode,layout);l._layoutSize={__type__:'cc.Size',width:objects[rowNode]._contentSize.width,height:206};
l['_N$layoutType']=1;l['_N$paddingBottom']=0;l['_N$spacingX']=54;l['_N$spacingY']=0;
for(let i=0;i<count;i++) {
const card=node(rowNode,'slot'+(row*3+i+1),(i-(count-1)/2)*260,0,206,206);
image(card,'background',local('reward_card'),0,0,206,206);
const content=node(card,'item'); objects[content]._trs.array[7]=objects[content]._trs.array[8]=0.7;
const icons=node(content,'icon');
for(const index of [6,9,12,15,18]) {
const src=rewards[index], sprite=rewards[src._components[0].__id__];
const n=image(icons,src._name,{uuid:sprite._spriteFrame.__uuid__,rawWidth:src._contentSize.width,rawHeight:src._contentSize.height});
objects[n]._trs=clone(src._trs);objects[n]._trs.array[9]=1;
objects[n]._active=src._name===['coin','freeze','magic','hammer','infinite_health'][row*3+i];
}
node(content,'amount',23.272,-121.815,88.98,50.4);
// The anchor is editable; only its digit children are filled from actual rewards.
}
}
}
const id=node(null,name,0,0,1080,1920);
contents({id,node,image,button,shade,rewardRows,objects});
write(file,objects);
write(file+'.meta',{ver:'1.3.2',uuid:uuid(file),importer:'prefab',optimizationPolicy:'AUTO',asyncLoadAssets:false,readonly:false,subMetas:{}});
console.log('Created:',file);
}
build('PassSettlement',({id,node,image,button,shade,rewardRows})=>{
shade(id,185); const panel=node(id,'panel',0,0,1080,1920);
for(const mode of ['offer','empty','claim']) {
const p=node(panel,mode,0,0,1080,1920,mode==='offer'),height=mode==='empty'?1080:1546,top=height/2;
image(p,'background',frame('assets/passCheck/img/passCheckWindow.png'),0,0,938,height,true);
image(p,'title',frame('assets/passCheck/img/tongpiaozi.png'),0,top-10,730,189);
button(p,'closeButton',lose('close'),408,top-105,103,112);
image(p,'copy',local(mode==='empty'?'copy_season_end_empty':mode==='offer'?'copy_buy_now':'copy_season_end_rewards'),0,mode==='empty'?-125:mode==='offer'?180:145);
if(mode==='empty')continue;
rewardRows(p);
if(mode==='claim')button(p,'claimButton',local('btn_claim'),0,-530,400,137);
else {
const b=button(p,'buyButton',atlas['giftBtnBg.png'],0,-530,520,160);
image(b,'price1',atlas['coin_1.png'],-67,5,38.4,60);
image(b,'price8',atlas['coin_8.png'],-15,5,55.2,62.4);
image(b,'priceYuan',atlas['coin_yuan.png'],49,5,63.6,58.8);
image(p,'bestValue',local('badge_best_value'),-245,-470);
}
}
const c=node(panel,'confirmation',0,0,2400,3000,false);shade(c,150);
image(c,'background',lose('kuang_bottom'),0,-75,902,970,true);
image(c,'header',lose('kuang_top'),0,450);image(c,'title',lose('fangqi'),25,460);
image(c,'copy',local('copy_abandon_confirm'),0,160);
button(c,'thinkAgainButton',lose('xiangxiang2'),0,-145,642,170);
button(c,'abandonButton',lose('giveup'),0,-340,438,138);
});
build('PassUnlock',({id,node,image,shade})=>{
shade(id,210);const panel=node(id,'content',0,0,1080,1920);
for(const tier of ['vip','svip']) {
const p=node(panel,tier,0,0,1080,1920,tier==='vip');
image(p,'badge',local(tier+'_badge'),0,470);
// Tween the wrapper, not the artwork's editable position or scale.
const animation=node(p,'cardAnimation',0,80);
image(animation,'card',local(tier+'_card_art'));
image(p,'copy',local('unlock_'+tier),0,-240);
}
});