const { ccclass } = cc._decorator; type WinPanelAction = (event?: cc.Event, customEventData?: string) => void; interface WinPanelActions { [name: string]: WinPanelAction; } /** Win prefab 内部按钮入口;具体游戏流程由 Map 在实例化后注入。 */ @ccclass export default class WinPanel extends cc.Component { private actions: WinPanelActions = null; private rainbowStars: cc.Node[] = []; private starTime = 0; private avatarFrame: cc.SpriteFrame = null; bind(actions: WinPanelActions) { this.actions = actions || {}; } initRainbowWinStreak(count: number) { const streak = this.node.getChildByName("WinStreak2"); if (!streak) return; count = Math.max(0, Math.min(10, count)); const reward = streak.getChildByName("reward"); const avatar = streak.getChildByName("avatar"); const milestone = count > 0 ? streak.getChildByName("hammer").getChildByName(String(count)) : streak.getChildByName("progress"); if (milestone) { const position = milestone.convertToWorldSpaceAR(cc.v2()); [reward, avatar].forEach(node => { if (node) node.x = node.parent.convertToNodeSpaceAR(position).x; }); } this.rainbowStars = reward ? reward.children.filter(node => node.name === "star") : []; this.starTime = 0; this.update(0); const mask = avatar && avatar.getChildByName("mask"); if (!mask) return; const icon = mask.getChildByName("icon"); const spine = mask.getChildByName("sp"); const selected = cc.fx.GameConfig.GM_INFO.useravatarIcon || "icon_0"; const remote = selected.length > 10; icon.active = remote; spine.active = !remote; if (remote) { icon.getComponent(cc.Sprite).spriteFrame = null; cc.assetManager.loadRemote(selected, { ext: '.png' }, (err, texture: cc.Texture2D) => { if (err || !texture || !cc.isValid(this, true) || !cc.isValid(icon, true) || cc.fx.GameConfig.GM_INFO.useravatarIcon !== selected) return; if (!this.avatarFrame) this.avatarFrame = new cc.SpriteFrame(texture); else this.avatarFrame.setTexture(texture); icon.getComponent(cc.Sprite).spriteFrame = this.avatarFrame; }); } else { cc.fx.GameTool.loadSpineSimple(spine, cc.fx.GameTool.getActionName(selected)); } } update(dt: number) { if (!this.rainbowStars.length) return; this.starTime = (this.starTime + dt) % 1.8; this.rainbowStars.forEach((star, index) => { const phase = (this.starTime / 1.8 - index / this.rainbowStars.length) * Math.PI * 2; star.opacity = 40 + (Math.cos(phase) + 1) * 107.5; }); } setSpecialTipsVisible(visible: boolean) { const specialTips = this.node.getChildByName("specialTips"); if (specialTips) { specialTips.active = visible; } } openPosition(event?: cc.Event, customEventData?: string) { this.invoke("openPosition", event, customEventData); } winMaxLevelShowClose() { this.setSpecialTipsVisible(false); } winLevel(event?: cc.Event, customEventData?: string) { this.invoke("winLevel", event, customEventData); } winLevel2(event?: cc.Event, customEventData?: string) { this.invoke("winLevel2", event, customEventData); } returnHome(event?: cc.Event, customEventData?: string) { this.invoke("returnHome", event, customEventData); } returnHomeScene(event?: cc.Event, customEventData?: string) { this.invoke("returnHomeScene", event, customEventData); } showDoubleCoin(event?: cc.Event, customEventData?: string) { this.invoke("showDoubleCoin", event, customEventData); } private invoke(name: string, event?: cc.Event, customEventData?: string) { const action = this.actions && this.actions[name]; if (action) { action(event, customEventData); return; } cc.warn("WinPanel action is not bound:", name); } onDestroy() { if (this.avatarFrame) this.avatarFrame.destroy(); this.actions = null; } }