import { _decorator, Button, Component, Label, Layout, Node, Prefab, sp, Sprite, Tween, tween, UIOpacity, v3, Vec3 } from 'cc'; import { RollerManager } from './RollerManager'; import { DELETE_TIME, ICON_SERVER_MAP, IParsedGameData, ISpecialIcon, ROLLER_COMBINE_EVENT, SLOT_GAME_EVENT } from './Define'; import { UpLayer } from './UpLayer'; import { Icon } from './Icon'; import { UIManager } from '../../Main/Scripts/managers/UIManager'; import { GameDataManager } from '../../Main/Scripts/managers/GameDataManager'; import { gold2cash } from '../../Main/Scripts/main/comm'; import { AudioManager } from '../../Main/Scripts/managers/AudioManager'; import { FeatureBuyPopup } from './FeatureBuyPopup'; const { ccclass, property } = _decorator; @ccclass('SlotGame') export class SlotGame extends Component { @property(RollerManager) rollerManager: RollerManager = null; @property(UpLayer) upLayer: UpLayer = null; @property(Node) normalGameBg: Node = null; @property(Node) freeGameBg: Node = null; @property(Node) featureBuyNode: Node = null; @property(Node) doubleWinBtnNode: Node = null; parsedData: IParsedGameData = null; spinData: any = null; _lastRemoveData: number[] = []; // ← 清除 featureBuyActive: boolean = false; doubleWinIsOn: boolean = false; rollersMsg: number[] = [3, 4, 5, 5, 4, 3]; featureBuyBtnEnabled: boolean = true; waysNum = 3 * 4 * 5 * 5 * 4 * 3; protected onLoad(): void { this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ONE_ROLLER_STOP, this.onOneRollerStopped, this); this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ROLLER_BOUNCE, this.onRollerBounce, this); this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ALL_ROLLER_STOP, this.onAllRollerStopped, this); this.rollerManager.node.on(ROLLER_COMBINE_EVENT.START_GAME_SCALE_TWEEN, this.onStartGameScaleTween, this); this.rollerManager.node.on(ROLLER_COMBINE_EVENT.START_GAME_END_SCALE_TWEEN, this.onStartGameEndScaleTween, this) this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_DELETED, this.onAllRollerIconsDeleted, this); this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_CREATED, this.onAllRollerIconsCreated, this); this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_FALLEN, this.onAllRollerIconsFallen, this); } onClickDoubleWin() { this.doubleWinIsOn = !this.doubleWinIsOn; // this.doubleWinBtnNode.getChildByName('bg').active = this.doubleWinIsOn // this.doubleWinBtnNode.getChildByName('zxksctte1').active = this.doubleWinIsOn // this.doubleWinBtnNode.getChildByName('bet_bg').active = this.doubleWinIsOn // this.doubleWinBtnNode.getChildByName('count').active = this.doubleWinIsOn // this.doubleWinBtnNode.getChildByName('deng_on').active = this.doubleWinIsOn this.doubleWinBtnNode.getChildByName('on').active = this.doubleWinIsOn this.doubleWinBtnNode.getChildByName('off').active = !this.doubleWinIsOn // this.doubleWinBtnNode.getChildByName('deng_off').active = !this.doubleWinIsOn // this.doubleWinBtnNode.getChildByName('bet_label').active = !this.doubleWinIsOn let pt_11 = this.doubleWinBtnNode.getChildByName('pt_11'); let on = pt_11.getChildByName('on'); let off = pt_11.getChildByName('off'); let pt_12 = pt_11.getChildByName('pt_12'); let pt_13 = pt_11.getChildByName('pt_13'); pt_12.active = !this.doubleWinIsOn off.active = !this.doubleWinIsOn; pt_13.active = this.doubleWinIsOn on.active = this.doubleWinIsOn; this.refreshDoubleWinCount(); this.refreshBuyBtnState(false); this.node.emit(SLOT_GAME_EVENT.ON_DOUBLE_WIN_CLICK, this.doubleWinIsOn); // if (this.doubleWinIsOn) { // tween(pt_12) // .to(0.05, { position: v3(35, 0, 0) }) // .call(() => { // pt_12.active = false; // pt_12.setPosition(v3(-35, 0, 0)); // pt_13.active = true; // on.active = true; // off.active = false; // this.refreshDoubleWinCount(); // this.refreshBuyBtnState(false); // this.node.emit(SLOT_GAME_EVENT.ON_DOUBLE_WIN_CLICK, this.doubleWinIsOn); // }) // .start(); // } else { // tween(pt_13) // .to(0.05, { position: v3(-35, 0, 0) }) // .call(() => { // pt_13.active = false; // pt_13.setPosition(v3(35, 0, 0)); // pt_12.active = true; // on.active = false; // off.active = true; // this.refreshDoubleWinCount(); // this.refreshBuyBtnState(false); // this.node.emit(SLOT_GAME_EVENT.ON_DOUBLE_WIN_CLICK, this.doubleWinIsOn); // }) // .start(); // } } refreshDoubleWinVisual() { let pt_11 = this.doubleWinBtnNode.getChildByName('pt_11'); let on = pt_11.getChildByName('on'); let off = pt_11.getChildByName('off'); let pt_12 = pt_11.getChildByName('pt_12'); let pt_13 = pt_11.getChildByName('pt_13'); Tween.stopAllByTarget(pt_12); Tween.stopAllByTarget(pt_13); // if (this.doubleWinIsOn) { // pt_12.active = false; // pt_12.setPosition(v3(-35, 0, 0)); // pt_13.active = true; // pt_13.setPosition(v3(35, 0, 0)); // on.active = true; // off.active = false; // } else { // pt_13.active = false; // pt_13.setPosition(v3(35, 0, 0)); // pt_12.active = true; // pt_12.setPosition(v3(-35, 0, 0)); // on.active = false; // off.active = true; // } } refreshDoubleWinCount() { let pt_11 = this.doubleWinBtnNode.getChildByName('pt_11'); let countLabel = this.doubleWinBtnNode.getChildByName('count')?.getComponent(Label); if (!countLabel) return; let bet = GameDataManager.instance.curBet; let displayBet = this.doubleWinIsOn ? bet * 1.25 : bet; countLabel.string = gold2cash(displayBet); } refreshBuyBtnState(isFreeGame: boolean = false) { if (isFreeGame) { this.featureBuyNode.active = false; this.doubleWinBtnNode.active = false; return; } this.featureBuyNode.active = this.featureBuyActive; this.doubleWinBtnNode.active = this.featureBuyActive; if (!this.featureBuyActive) { return; } if (this.doubleWinIsOn) { this.setBtnEnable(this.doubleWinBtnNode.getChildByName('pt_11'), true); this.setBtnEnable(this.featureBuyNode, false); } else { this.setBtnEnable(this.doubleWinBtnNode.getChildByName('pt_11'), this.featureBuyBtnEnabled); this.setBtnEnable(this.featureBuyNode, this.featureBuyBtnEnabled); } } initRollerWithIcon(spinData: any) { this.spinData = spinData; this.upLayer.hideAllLayer(); this.rollerManager.setUpLayer(this.upLayer); this.updateGameBg(spinData.Mode == 1); this.parsedData = this.parseGameData(spinData, []); this._lastRemoveData = this.parsedData.RemoveData.slice(); // 保存本轮消除位置 this.rollerManager.initRollerWithIcon(this.parsedData); // this.setWaysCount(this.waysNum); } onStartGameScaleTween() { Tween.stopAllByTarget(this.node.parent); tween(this.node.parent) .to(5, { scale: v3(1.05, 1.05, 1.05) }) .start(); } onStartGameEndScaleTween() { Tween.stopAllByTarget(this.node.parent); tween(this.node.parent) .to(0.1, { scale: v3(1, 1, 1) }) .start(); } updateGameBg(isFreeGame: boolean) { this.normalGameBg.active = !isFreeGame; this.freeGameBg.active = isFreeGame; this.node.getChildByName('NormalGameBg1').active = !isFreeGame; this.node.getChildByName('FreeGameBg1').active = isFreeGame; this.refreshBuyBtnState(isFreeGame); if (!isFreeGame) { this.refreshDoubleWinCount(); // let tudou = this.node.getChildByName('NormalGameBg1').getChildByName('5'); // let leftPos = new Vec3(-360, -993, 0); // let rightPos = new Vec3(-210, -993, 0); // Tween.stopAllByTarget(tudou); // tudou.setPosition(leftPos); // tudou.setScale(1, 1, 0); // tween(tudou) // .to(1, { position: rightPos }) // .call(() => tudou.setScale(-1, 1, 0)) // .to(1, { position: leftPos }) // .call(() => tudou.setScale(1, 1, 0)) // .union() // .repeatForever() // .start(); // let flower = this.node.getChildByName('NormalGameBg1').getChildByName('6').getComponent(sp.Skeleton); // // ruchang , chixu ,xiaoshi 三个动画循环播放 0 -> 1 -> 2 -> 0 // let animationNames = ['ruchang', 'chixu', 'xiaoshi']; // let index = 0; // flower.setCompleteListener(() => { // index = (index + 1) % animationNames.length; // flower.setAnimation(0, animationNames[index], false); // }); // flower.setAnimation(0, animationNames[index], false); } } startScroll(isFreeSpin: boolean) { this.upLayer.giveBackAllSpecials(this.rollerManager); if (!isFreeSpin) { this.setFeatureBuyBtnEnable(false); this.setBtnEnable(this.doubleWinBtnNode.getChildByName('pt_11'), false); } // this.setWaysCount('...'); this.rollerManager.resetInfo(); this.rollerManager.startScroll(); this._lastRemoveData = []; // ← 清除 } stopScroll(spinData: any) { this.spinData = spinData; this.parsedData = this.parseGameData(spinData, []); this._lastRemoveData = this.parsedData.RemoveData.slice(); // 保存本轮消除位置 this.rollerManager.stopScroll(this.parsedData); } onOneRollerStopped(rollerId: number) { let roller = this.rollerManager.allRollers[rollerId]; this.upLayer.syncSpecialFromRoller(roller); } onRollerBounce(rollerId: number) { } onAllRollerStopped() { this.upLayer.setReadHandSkeletonActive(false); let delay = this.parsedData.hasSmallGame ? 2 : 0; if (this.parsedData.hasSmallGame) { this.upLayer.playScatterSpine(true); AudioManager.instance.playSFX('Before_Small_Game'); } this.scheduleOnce(() => { if (this.parsedData.hasSmallGame) { this.upLayer.playScatterSpine(false); } }, delay); this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_STOP); } playIconWinAni(spinData: any) { this.spinData = spinData; AudioManager.instance.playSFX('Win_Icon_Up'); let deleteIconNodesPos = this.rollerManager.getAllRemoveIconsPos(this.parsedData.RemoveData); this.upLayer.setWinLayerActive(true); // 统计“实际调用次数” let expected = 0; for (let i = deleteIconNodesPos.length - 1; i >= 0; i--) { let poses = deleteIconNodesPos[i]; expected += poses.length; // 每个格子会调一次 deleteIconNode(pos) } this.rollerManager.beginDeleteBatch(expected); if (this.rollerManager._isFastSpin) { // 快速模式一起展示播放动画 this.scheduleOnce(() => { for (let i = 0; i < deleteIconNodesPos.length; i++) { let poses = deleteIconNodesPos[i]; for (let j = 0; j < poses.length; j++) { let pos = poses[j]; let iconNode = this.rollerManager.getIconNode(pos); if (iconNode) { this.upLayer.removeSpecialByNode(iconNode); this.upLayer.playIconWinAni(iconNode); } } } }, 0) this.scheduleOnce(() => { for (let i = 0; i < deleteIconNodesPos.length; i++) { let poses = deleteIconNodesPos[i]; for (let j = 0; j < poses.length; j++) { let pos = poses[j]; let iconNode = this.rollerManager.getIconNode(pos); if (iconNode) { AudioManager.instance.playSFX('Eliminate_Icon'); this.rollerManager.deleteIconNode(pos); } } } }, 0.6) // 提前返回,不执行原本的for循环和删除逻辑 return; } for (let i = 0; i < deleteIconNodesPos.length; i++) { this.scheduleOnce(() => { let poses = deleteIconNodesPos[i]; for (let j = 0; j < poses.length; j++) { let pos = poses[j]; let iconNode = this.rollerManager.getIconNode(pos); if (iconNode) { this.upLayer.removeSpecialByNode(iconNode); this.upLayer.playIconWinAni(iconNode); } } }, 0.1 * i) } this.scheduleOnce(() => { for (let i = 0; i < deleteIconNodesPos.length; i++) { let poses = deleteIconNodesPos[i]; for (let j = 0; j < poses.length; j++) { let pos = poses[j]; let iconNode = this.rollerManager.getIconNode(pos); if (iconNode) { AudioManager.instance.playSFX('Eliminate_Icon'); this.rollerManager.deleteIconNode(pos); } } } }, 1.2) this.scheduleOnce(() => { this.upLayer.setWinGrayNode(false); }, DELETE_TIME) } onAllRollerIconsDeleted() { this.upLayer.giveBackAllSpecials(this.rollerManager); this.upLayer.setWinGrayNode(false); // 要在播放碎裂动画完成之后再展示 this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_ICONS_DELETED); } createNewIconTop(spinData: any) { this.spinData = spinData; this.parsedData = this.parseGameData(spinData, this._lastRemoveData); this._lastRemoveData = this.parsedData.RemoveData.slice(); // ← 更新为本轮 this.rollerManager.distributeFreeMulMap(this.parsedData.FreeMulMap); this.rollerManager.createNewIconTop(this.parsedData.NewIconTop); } onAllRollerIconsCreated() { this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_ICONS_CREATED); } IconFallDown() { // this.setWaysCount(this.waysNum); this.rollerManager.iconFallDown(); } getCurrentBombSettleInfos(): { pos: number, mul: number, iconNode: Node, multiNode: Node }[] { let result: { pos: number, mul: number, iconNode: Node, multiNode: Node }[] = []; if (!this.parsedData || !this.parsedData.BombPos || !this.parsedData.FreeMulMap) return result; for (let i = 0; i < this.parsedData.BombPos.length; i++) { let absPos = this.parsedData.BombPos[i]; let mul = this.parsedData.FreeMulMap[absPos]; if (mul == null) continue; let iconNode = this.rollerManager.getIconNode(absPos); if (!iconNode || !iconNode.isValid) continue; let iconComp = iconNode.getComponent(Icon); if (!iconComp || !iconComp.multiNode || !iconComp.multiNode.isValid) continue; result.push({ pos: absPos, mul: mul, iconNode, multiNode: iconComp.multiNode, }); } return result; } onAllRollerIconsFallen() { this.upLayer.resetAndSyncAllSpecials(this.rollerManager); let delay = this.parsedData.hasSmallGame ? 2 : 0; if (this.parsedData.hasSmallGame) { this.upLayer.playScatterSpine(true); AudioManager.instance.playSFX('Before_Small_Game'); } this.scheduleOnce(() => { if (this.parsedData.hasSmallGame) { this.upLayer.playScatterSpine(false); } }, delay); this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_ICONS_FALLEN); } // 设置按钮可以点击并且置灰的函数 setBtnEnable(btn: Button | Node, enable: boolean) { if (btn instanceof Button) { btn.interactable = enable; this.setNodeOpacity(btn.node, enable ? 255 : 180); } else { btn.getComponent(Button).interactable = enable; this.setNodeOpacity(btn, enable ? 255 : 180); } } setBtnEnableForBol(btn: Button | Node, bol: boolean) { if (btn instanceof Button) { btn.interactable = bol; } else { btn.getComponent(Button).interactable = bol; } } setNodeOpacity(node: Node, opacity: number) { node.getComponent(UIOpacity).opacity = opacity; } setFastSpin(isFastSpin: boolean) { this.rollerManager.setFastSpin(isFastSpin); } isScroll() { return this.rollerManager.isScroll(); } parseGameData(spinData: any, lastRemoveData: number[]): IParsedGameData { let { Symbol, WinInfo, Free } = spinData || {}; let serverPanData: number[] = Symbol.Middle; let freeMulMiddlesArr: any[] = Symbol?.FreeMulMiddles || []; let mulDesc = WinInfo?.MulDesc || null; let freeMulMap: { [pos: number]: number } = {}; for (let item of freeMulMiddlesArr) { freeMulMap[item.NumPos] = item.Mul; } let removeData: number[] = []; if (WinInfo != null && WinInfo.Desc != null) { for (let symbolId in WinInfo.Desc) { let descItem = WinInfo.Desc[symbolId]; if (descItem?.Middle) { descItem.Middle.forEach((pos: number) => removeData.push(pos)); } } } let NUM_COLS = 6; let ROWS_PER_COL = 5; let newIconTop: number[][] = []; for (let col = 0; col < NUM_COLS; col++) { let colStart = col * ROWS_PER_COL; // 上一轮该列被消除了多少个 let removedInCol = lastRemoveData.filter( p => p >= colStart && p < colStart + ROWS_PER_COL ).length; // 本轮 middle 该列顶部 removedInCol 个 = 从顶部新落入的图标 let newColIcons: number[] = []; for (let row = 0; row < removedInCol; row++) { let serverIconId = serverPanData[colStart + row]; let clientIconId = ICON_SERVER_MAP[serverIconId]; newColIcons.push(clientIconId); } newIconTop.push(newColIcons); } let scatterPos: number[] = []; let bombPos: number[] = []; let panData: number[] = []; for (let i = 0; i < serverPanData.length; i++) { let iconId = ICON_SERVER_MAP[serverPanData[i]]; panData.push(iconId); } for (let i = 0; i < panData.length; i++) { let iconId = panData[i]; if (iconId == 0) scatterPos.push(i); if (iconId == 10) bombPos.push(i); } let freeIsSame = Free == null ? false : Free.LeftCount !== Free.MaxCount; return { Multi: 0, PanData: panData, RemoveData: removeData, NewIconTop: newIconTop, ScatterPos: scatterPos, BombPos: bombPos, hasSmallGame: scatterPos.length >= 4, hasFreeSpinAgain: scatterPos.length >= 3 && freeIsSame, FreeMulMap: freeMulMap, RoundScore: WinInfo?.Score, MulValue: mulDesc?.Mul, UnmultiedScore: mulDesc?.UnMultied, MultiedScore: mulDesc?.Multied, }; } // -----------------------------购买相关----------------------------- onClickFeatureBuy() { this.setBtnEnableForBol(this.featureBuyNode, false); this.setBtnEnableForBol(this.doubleWinBtnNode.getChildByName('pt_11'), false); // let maliao = this.featureBuyNode.getChildByName('3_4').getComponent(sp.Skeleton); // maliao.setAnimation(0, 'animation', false); this.scheduleOnce(() => { // let zhuankuai = this.featureBuyNode.getChildByName('3_3').getComponent(sp.Skeleton); // zhuankuai.setAnimation(0, '2', false); // let goldSpine = this.featureBuyNode.getChildByName('3_1').getComponent(sp.Skeleton); // goldSpine.setAnimation(0, '2', false); // goldSpine.setCompleteListener(() => { // goldSpine.setAnimation(0, '1', true); // goldSpine.setCompleteListener(null); let scp = null; UIManager.instance.showPopupDynamic('FeatureBuyPopUp', 'Prefab/FeatureBuyPopUp', 'Game', (prefab) => { let main = prefab.getChildByName('main'); scp = prefab.getComponent(FeatureBuyPopup); scp.show(); let freeNeedCount = main.getChildByName('needCount'); freeNeedCount.getComponent(Label).string = `${gold2cash(GameDataManager.instance.curBet * 100)}`; }, (prefab) => { }, () => { let buyType = scp.BuyType; this.node.emit(SLOT_GAME_EVENT.FEATURE_BUY, buyType); this.setBtnEnable(this.featureBuyNode, false); this.setBtnEnable(this.doubleWinBtnNode.getChildByName('pt_11'), false); AudioManager.instance.playSFX('Click_Small_Game_Start'); }, () => { this.setBtnEnable(this.featureBuyNode, true); this.setBtnEnable(this.doubleWinBtnNode.getChildByName('pt_11'), true); AudioManager.instance.playSFX('Click_Small_Game_Cancel'); }); // }); let flower = this.featureBuyNode.getChildByName('3_2').getComponent(sp.Skeleton); flower.setAnimation(0, 'animation', false); }, 0.1) } setFeatureBuyActive(bol: boolean) { this.featureBuyActive = bol; this.refreshBuyBtnState(false); } showPos = new Vec3(-294, -329, 0); hidePos = new Vec3(-1080, -329, 0); hideFeatureBuy() { Tween.stopAllByTarget(this.featureBuyNode); this.setBtnEnable(this.featureBuyNode, false); tween(this.featureBuyNode) .to(0.1, { position: this.hidePos }) .call(() => { this.featureBuyNode.setPosition(this.hidePos); }) .start(); } showFeatureBuy(bol: boolean) { if (bol) { this.hideFeatureBuy(); return; } Tween.stopAllByTarget(this.featureBuyNode); this.setBtnEnable(this.featureBuyNode, false); tween(this.featureBuyNode) .to(0.1, { position: this.showPos }) .call(() => { this.setBtnEnable(this.featureBuyNode, true); this.featureBuyNode.setPosition(this.showPos); }) .start(); } setFeatureBuyBtnEnable(bol: boolean) { if (GameDataManager.instance.gameInfo.CloseBuyGame) return; this.featureBuyBtnEnabled = bol; this.refreshBuyBtnState(false); } }