import NumberToImage from "./NumberToImage"; const { ccclass } = cc._decorator; export type ThreeWinsProp = "hourglass" | "rocket"; export interface ThreeWinsSelection { hourglass: boolean; rocket: boolean; } export interface ThreeWinsPanelData { level: number; threeWinStreak: number; tenWinStreak: number; hourglassAmount: number; rocketAmount: number; infiniteHourglass?: boolean; infiniteRocket?: boolean; } export interface ThreeWinsPanelCallbacks { onProp: (prop: ThreeWinsProp, selected: boolean) => void; onStart: (selection: ThreeWinsSelection) => void; } /** 只管理预制体显示和选择;库存检查、购买及开局由 SceneManager 处理。 */ @ccclass export default class ThreeWinsPanel extends cc.Component { private atlas: cc.SpriteAtlas = null; private data: ThreeWinsPanelData = null; private callbacks: ThreeWinsPanelCallbacks = null; private selected: ThreeWinsSelection = { hourglass: false, rocket: false }; private bound = false; private entryScale: cc.Vec2 = null; init(atlas: cc.SpriteAtlas, data: ThreeWinsPanelData, callbacks: ThreeWinsPanelCallbacks) { this.atlas = atlas; this.callbacks = callbacks; this.selected = { hourglass: false, rocket: false }; if (!this.bound) { this.node.getChildByName("hourglass").on("click", this.onHourglass, this); this.node.getChildByName("rocket").on("click", this.onRocket, this); this.node.getChildByName("begin").on("click", this.onStart, this); this.bound = true; } this.refresh(data); this.entryScale = cc.v2(this.node.scaleX, this.node.scaleY); this.node.setScale(0); } playEntry() { if (!this.entryScale) return; const scale = this.entryScale; this.entryScale = null; cc.tween(this.node) .to(0.28, { scaleX: scale.x * 1.06, scaleY: scale.y * 1.06 }, { easing: "cubicOut" }) .to(0.10, { scaleX: scale.x * 0.97, scaleY: scale.y * 0.97 }, { easing: "sineInOut" }) .to(0.12, { scaleX: scale.x, scaleY: scale.y }, { easing: "sineOut" }) .start(); } refresh(data: ThreeWinsPanelData) { this.data = data; const tier = Math.min(3, Math.max(0, Math.floor(data.threeWinStreak || 0))); const tenWins = Math.min(10, Math.max(0, Math.floor(data.tenWinStreak || 0))); const box = this.node.getChildByName("box"); for (let i = 0; i <= 3; i++) box.getChildByName("box" + i).active = i === tier; const three = this.node.getChildByName("three"); three.getChildByName("progress").getComponent(cc.Sprite).spriteFrame = this.atlas.getSpriteFrame("progress_" + tier); for (let i = 1; i <= 3; i++) three.getChildByName("progress" + i).active = i <= tier; this.node.getChildByName("progress").getComponent(cc.Sprite).fillRange = tenWins / 10; this.renderNumber(this.node.getChildByName("custom"), data.level, "custom_", 44); this.renderNumber(this.node.getChildByName("rainbowNumber"), tenWins, "rainbow", 34, true); this.renderProp("hourglass"); this.renderProp("rocket"); } refreshInventory(data: ThreeWinsPanelData) { const previous = this.data; this.data = data; if (!previous || previous.hourglassAmount !== data.hourglassAmount || previous.infiniteHourglass !== data.infiniteHourglass) { this.renderProp("hourglass"); } if (!previous || previous.rocketAmount !== data.rocketAmount || previous.infiniteRocket !== data.infiniteRocket) { this.renderProp("rocket"); } } setSelected(prop: ThreeWinsProp, selected: boolean) { this.selected[prop] = selected; this.renderProp(prop); } private onHourglass() { if (this.callbacks) this.callbacks.onProp("hourglass", !this.selected.hourglass); } private onRocket() { if (this.callbacks) this.callbacks.onProp("rocket", !this.selected.rocket); } private onStart() { if (this.callbacks) this.callbacks.onStart({ ...this.selected }); } private renderProp(prop: ThreeWinsProp) { const node = this.node.getChildByName(prop); const count = prop === "hourglass" ? this.data.hourglassAmount : this.data.rocketAmount; const infinite = prop === "hourglass" ? this.data.infiniteHourglass : this.data.infiniteRocket; const available = count > 0 || !!infinite; if (!available) this.selected[prop] = false; const selected = this.selected[prop]; node.getChildByName("add").active = !available; node.getChildByName("select").active = selected; const remaining = node.getChildByName("remaining"); remaining.active = available && !selected; const amount = remaining.getChildByName("number"); if (infinite) { this.clearNumber(amount); this.addCountSymbol(amount, "∞", 0); return; } this.renderNumber(amount, count, "prop_", 30); } private renderNumber(target: cc.Node, value: number, prefix: string, spacing: number, right = false) { this.clearNumber(target); const number = Math.max(0, Math.floor(value || 0)); NumberToImage.numberToImageNodesPublic(this.atlas, number, spacing, 0, prefix, target, false); const offset = right ? (target.childrenCount - 1) * spacing : (target.childrenCount - 1) * spacing / 2; for (const digit of target.children) digit.x -= offset; } private clearNumber(target: cc.Node) { for (const child of target.children.slice()) child.destroy(); target.removeAllChildren(); } private addCountSymbol(target: cc.Node, text: string, x: number) { const node = new cc.Node("symbol"); node.parent = target; node.x = x; const label = node.addComponent(cc.Label); label.string = text; label.fontSize = 36; label.lineHeight = 44; label.enableBold = true; const outline = node.addComponent(cc.LabelOutline); outline.color = cc.color(126, 44, 20); outline.width = 2; } onDestroy() { this.callbacks = null; } }