import { _decorator, Animation, Button, Component, Label, Layout, Node, ParticleSystem, Prefab, RichText, Size, sp, Sprite, SpriteFrame, tween, UITransform, v3, Vec3 } from 'cc'; import { RollerManager } from './game/RollerManager'; import { GameInfo, ICON_RATE, ROLLER_COMBINE_EVENT, SLOT_GAME_EVENT, WIN_TYPE } from './game/Define'; import { Icon, IconEventBus } from './game/Icon'; import { IconMsg } from './game/IconMsg'; import { gold2cash } from '../../Loading/scripts/comm'; import { NodePoolManager } from '../../Loading/scripts/manager/NodePoolManager'; import { I18nManager } from '../../Loading/scripts/manager/I18nManager'; import { AudioManager } from '../../Loading/scripts/manager/AudioManager'; let { ccclass, property } = _decorator; @ccclass('SlotGame') export class SlotGame extends Component { @property(RollerManager) rollerManager: RollerManager = null; @property(Prefab) iconMsgPre: Prefab = null; @property([SpriteFrame]) multiFrame = new Array; // 跑马灯信息 private moveSprite: Sprite[] = []; private startPos: Vec3 = new Vec3(540, 0, 0); private moveSpeed: number = 200; private moveIndex: number = 0; private grayNode: Node = null; private winLayer: Node = null; private readyHand: Node = null; private scatterLayer: Node = null; // 赢分信息相关 private winType: Node = null; private winTypeAni: Animation = null; private winTypeParticle: ParticleSystem = null; private normalWin: Node = null; private normalWinLayout: Layout = null; private normalWinSprite: Node = null; private normalTotalWinSprite: Node = null; private normalWinLabel: Label = null; private totalWin: Node = null; private totalWinLayout: Layout = null; private totalWinLabel: Label = null; private baseBg: Node = null; private freeBg: Node = null; private base: Node = null; private normalMultiAni: Animation = null; private multi: Node = null; private waysCount: Label = null; private waysLayout: Layout = null; private layCount: Label = null; private free: Node = null; private freeMultiAni: Animation = null; private freeMultiLabel: Label = null; private freeWaysCount: Label = null; private freeWaysLayout: Layout = null; private freeLayCount: Label = null; private featureBuyNode: Node = null; // 初始化盘面 private gameInfo: GameInfo = null; // 每次的spin结果 private spinData: any = null; private isEliminating: boolean = false; // 设置是否正在消除中 setIsEliminating(isEliminating: boolean) { this.isEliminating = isEliminating; } protected onLoad(): void { this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ONE_ROLLER_STOP, this.onRollerStop, this) this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ALL_ROLLER_STOP, this.allRollerStop, this); this.rollerManager.node.on(ROLLER_COMBINE_EVENT.ROLLER_BOUNCE, this.onRollerBounce, 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); this.grayNode = this.node.getChildByName('grayNode'); this.winLayer = this.node.getChildByName('winLayer'); this.readyHand = this.node.getChildByName('readyHand'); this.scatterLayer = this.node.getChildByName('scatterLayer'); this.winType = this.node.getChildByName('winType'); this.winTypeAni = this.winType.getComponent(Animation); this.winTypeParticle = this.winType.getChildByName('ParticleNode').getChildByName('Particle').getComponent(ParticleSystem); let frameNode = this.winType.getChildByName('FrameNode'); this.normalWin = frameNode.getChildByName('marqueeFrame_02'); this.totalWin = frameNode.getChildByName('marqueeFrame_03'); this.normalWinLayout = this.normalWin.getChildByName('layout').getComponent(Layout); this.normalWinSprite = this.normalWinLayout.node.getChildByName('winSprite'); this.normalTotalWinSprite = this.normalWinLayout.node.getChildByName('totalWinSprite'); this.normalWinLabel = this.normalWinLayout.node.getChildByName('winScore').getComponent(Label); this.totalWinLayout = this.totalWin.getChildByName('layout').getComponent(Layout); this.totalWinLabel = this.totalWinLayout.node.getChildByName('winScore').getComponent(Label); this.baseBg = this.node.getChildByName('base_bg'); this.freeBg = this.node.getChildByName('free_bg'); this.base = this.node.getChildByName('base'); this.normalMultiAni = this.base.getChildByName('NormalMultiAniNode').getComponent(Animation); this.multi = this.base.getChildByName('multi'); let ways = this.base.getChildByName('ways'); this.waysCount = ways.getChildByName('count').getComponent(Label); this.waysLayout = ways.getChildByName('lay').getComponent(Layout); this.layCount = this.waysLayout.node.getChildByName('count').getComponent(Label); this.free = this.node.getChildByName('free'); this.freeMultiAni = this.free.getChildByName('FreeSpinMultiAniNode').getComponent(Animation); this.freeMultiLabel = this.free.getChildByName('multi').getComponent(Label); let free_ways = this.free.getChildByName('ways'); this.freeWaysCount = free_ways.getChildByName('count').getComponent(Label); this.freeWaysLayout = free_ways.getChildByName('lay').getComponent(Layout); this.freeLayCount = this.freeWaysLayout.node.getChildByName('count').getComponent(Label); this.featureBuyNode = this.base.getChildByName('buy'); frameNode.getChildByName('marqueeFrame_01').getChildByName('Mask').children.forEach((child, i) => { if (child.name.includes('win_bp_text_')) { this.moveSprite.push(child.getComponent(Sprite)); } }) this.startMoveSprite(); IconEventBus.on('ICON_CLICKED', this.onIconClicked, this); } startMoveSprite() { this.playNextSprite(); } playNextSprite() { // 如果所有精灵都播放完了,重置索引 if (this.moveIndex >= this.moveSprite.length) { this.moveIndex = 0; } let sprite = this.moveSprite[this.moveIndex]; let spriteWidth = sprite.node.getComponent(UITransform).width; sprite.node.setPosition(this.startPos.x, 0, 0); let targetPos = -spriteWidth - 600; let duration = Math.abs(this.startPos.x - targetPos) / this.moveSpeed; this.createLoopingMovement(sprite.node, targetPos, duration); } createLoopingMovement(node: Node, targetX: number, duration: number) { tween(node) .to(duration, { position: new Vec3(targetX, 0, 0) }, { easing: 'linear' }) .call(() => { // 当前精灵播放完成,移动到下一个精灵 this.moveIndex++; this.playNextSprite(); }) .start(); } onDestroy(): void { this.rollerManager.node.off(ROLLER_COMBINE_EVENT.ONE_ROLLER_STOP, this.onRollerStop, this) this.rollerManager.node.off(ROLLER_COMBINE_EVENT.ALL_ROLLER_STOP, this.allRollerStop, this); this.rollerManager.node.off(ROLLER_COMBINE_EVENT.ROLLER_BOUNCE, this.onRollerBounce, this); this.rollerManager.node.off(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_DELETED, this.onAllRollerIconsDeleted, this); this.rollerManager.node.off(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_CREATED, this.onAllRollerIconsCreated, this); this.rollerManager.node.off(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_FALLEN, this.onAllRollerIconsFallen, this); IconEventBus.on('ICON_CLICKED', this.onIconClicked, this); } private currentBgState: boolean = true; // 记录当前背景状态,默认为普通模式(false) private isFirstBgChange: boolean = true; // 添加首次调用标记 // 切换背景 changeBg(isFree: boolean) { // 如果状态没有改变,直接返回 if (this.isFirstBgChange || this.currentBgState !== isFree) { this.currentBgState = isFree; this.isFirstBgChange = false; // 标记已经不是第一次调用 // 切换背景 this.baseBg.active = !isFree; this.freeBg.active = isFree; this.base.active = !isFree; this.free.active = isFree; } } // 初始化滚轮数据 initRollerWithIcon(gameInfo: any) { this.gameInfo = gameInfo; this.spinData = this.gameInfo.Data; this.rollerManager.initRollerWithIcon(this.spinData); } setRollerIconRule(rollerIconRule: any) { this.rollerManager.setRollerIconRule(rollerIconRule); } setFastSpin(isFastSpin: boolean) { this.rollerManager.setFastSpin(isFastSpin); } spin(isFree: boolean) { AudioManager.instance.playSFX('Spin_Button_Click'); this.setWaysCount('...'); if (!isFree) { this.setMultiLabel([2, 2, 2, 2]); } this.showWinScore(false, false, false, false, false); this.rollerManager.resetInfo(); this.rollerManager.startScroll(); } manualStop() { this.hideReadyHand(); this.rollerManager.manualStop(this.spinData); } stopScroll(spinData: any, isFree: boolean, callback?: Function, callback2?: Function) { this.spinData = spinData; this.rollerManager.setIsFreeSpin(isFree); // 停止滚轮 this.rollerManager.stopScroll(this.spinData); } setMultiLabel(multiArr: Array) { multiArr.push(multiArr[3] + 2) for (let i = 0; i < multiArr.length; i++) { this.multi.children[i].getChildByName('Label').getComponent(Label).string = 'x' + multiArr[i] switch (true) { case multiArr[i] <= 4: this.multi.children[i].getComponent(Sprite).spriteFrame = this.multiFrame[0] break case multiArr[i] <= 8 && multiArr[i] > 4: this.multi.children[i].getComponent(Sprite).spriteFrame = this.multiFrame[1] break case multiArr[i] <= 16 && multiArr[i] > 8: this.multi.children[i].getComponent(Sprite).spriteFrame = this.multiFrame[2] break case multiArr[i] > 16: this.multi.children[i].getComponent(Sprite).spriteFrame = this.multiFrame[3] break } } } setWaysCount(ways: any) { if (ways == '...') { this.waysCount.node.active = true; this.waysLayout.node.active = false; this.freeWaysCount.node.active = true; this.freeWaysLayout.node.active = false; } else { this.waysCount.node.active = false; this.waysLayout.node.active = true; this.freeWaysCount.node.active = false; this.freeWaysLayout.node.active = true; this.layCount.string = `${ways}`; this.freeLayCount.string = `${ways}`; this.waysLayout.updateLayout(); this.freeWaysLayout.updateLayout(); } } // 消除完整的逻辑 deleteIconNode() { //需要消除的位置 let deleteMsg = []; //金变百搭的位置 let specialIcons = []; let Desc = this.spinData.WinInfo.Desc for (let WinId in Desc) { deleteMsg.push(...Desc[WinId].Middle) if (Desc[WinId].Transform) { specialIcons.push(...Desc[WinId].Transform); } } deleteMsg = deleteMsg.filter(item => specialIcons.indexOf(item) === -1); this.rollerManager.handleWinIcons(this.winLayer, deleteMsg, specialIcons || []); } handleScatter(isRollerScrolling: boolean,) { if (isRollerScrolling) { } else { this.scatterLayer.active = false; } } changeIconAndFrameType(spinData: any) { this.spinData = spinData; // 先去找到PanChange当中是否有oldPos,如果没有代表当前icon没有动,就去CroSymbols当中找 // 初始化changeData数组 let changeData = []; // let colorChanges = this.spinData.PanChanges.CrossSymbolColorChange // 遍历CrossSymbolColorChange获取颜色变化信息 for (let key in this.spinData.Change.Details) { //判断组合块是否发生了金变百搭 let newIndex = -1; if (this.spinData.Change.Details[key].Symbol) { newIndex = this.spinData.Change.Details[key].Symbol.New } else { let newPos = this.spinData.Change.Details[key].Pos.New[0] newIndex = this.spinData.Symbol.Middle[newPos] } let oldStartPos; let frameType; let height; // 先在CrossSymbolPosChange中查找位置变化 if (this.spinData.Change.Details[key].Pos) { oldStartPos = this.spinData.Change.Details[key].Pos.Old[0]; } // 如果没找到则在CroSymbols中查找 else if (this.spinData.CroSymbols[key]) { oldStartPos = this.spinData.CroSymbols[key].PosFirst; } // 从CroSymbols获取frameType和height if (this.spinData.CroSymbols[key]) { let symbol = this.spinData.CroSymbols[key]; frameType = symbol.Type; height = symbol.PosLast - symbol.PosFirst + 1; } // 添加到changeData数组 changeData.push({ oldStartPos: oldStartPos, newIndex: newIndex, newFrameType: frameType, lheight: height }); } this.rollerManager.changeIconAndFrameType(changeData); } // 消除创建的逻辑 createNewIconTop(spinData: any) { this.spinData = spinData; let createDatas: number[][][] = [[[]]] let CroSymbols = null if (this.spinData.Change) { createDatas = this.spinData.Change.Middle; } if (this.spinData.CroSymbols) { CroSymbols = this.spinData.CroSymbols } this.rollerManager.createNewIconTop(createDatas, CroSymbols); } // icon掉落的逻辑 iconFallDown() { this.setWaysCount(this.spinData.Symbol.WaysNum); this.rollerManager.iconFallDown(this.spinData); } onRollerBounce() { } onRollerStop(rollerId: number) { // if (rollerId < 1) return; // 计算当前停止列之前的所有数字的乘积 let totalWays = 1; for (let i = 0; i < rollerId; i++) { totalWays *= this.spinData.Symbol.ReelNum[i]; } let isExpect = this.rollerManager.checkNextRollerExpect(rollerId); if (isExpect && !this.rollerManager.getIsFastSpin()) { let nextRollerId = this.rollerManager.getNextRollerIndex(rollerId); this.showReadyHand(nextRollerId); this.showScatterOnIsScroll(); } // 设置显示的分数 this.setWaysCount(totalWays); } allRollerStop() { // 设置显示的分数 this.hideReadyHand(); let isExpect = this.rollerManager.checkNextRollerExpect(6); if (isExpect && !this.rollerManager.getIsFastSpin()) { this.hideScatterOnIsScroll(); } this.setWaysCount(this.spinData.Symbol.WaysNum); this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_STOP); } // 新增:事件处理方法 onAllRollerIconsDeleted() { // 向上传递事件 this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_ICONS_DELETED); } onAllRollerIconsCreated() { // 向上传递事件 this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_ICONS_CREATED); } onAllRollerIconsFallen() { // 向上传递事件 this.node.emit(SLOT_GAME_EVENT.ALL_ROLLER_ICONS_FALLEN); } showWinScore(bol: boolean, isFirstWin: boolean, isBigWin: boolean, isReconnect: boolean, isNormalTotalWin: boolean, score?: number) { // 隐藏 if (!bol) { this.normalWin.active = false; this.totalWin.active = false; this.winTypeAni.node.getChildByName('AniNode').active = false; this.winTypeAni.stop(); this.winTypeAni.resume(); return; } this.winTypeAni.node.getChildByName('AniNode').active = true; // 设置活动显示窗口 this.normalWin.active = !isBigWin; this.totalWin.active = isBigWin; // 更新对应标签和布局 if (isBigWin) { if (!isReconnect) { AudioManager.instance.playSFX('Appear_Total_Win_Sound'); } this.totalWinLabel.string = gold2cash(score); this.totalWinLayout.updateLayout(); } else { if (isNormalTotalWin) { if (!isReconnect) { AudioManager.instance.playSFX('Appear_Small_Total_Win_Sound'); } this.normalWinSprite.active = false; this.normalTotalWinSprite.active = true; } else { if (!isReconnect) { // AudioManager.instance.playSFX('Appear_Normal_Win_Sound'); } this.normalWinSprite.active = true; this.normalTotalWinSprite.active = false; } this.normalWinLabel.string = gold2cash(score); this.normalWinLayout.updateLayout(); } // 播放动画效果 if (isFirstWin) { this.winTypeAni.play('Marquee_in_animation'); this.winTypeParticle.play(); this.winTypeAni.once(Animation.EventType.FINISHED, () => { this.winTypeAni.play('Marquee_loop_animation'); }); } else { this.winTypeAni.play('Marquee_win_animation'); this.winTypeParticle.play(); this.winTypeAni.once(Animation.EventType.FINISHED, () => { this.winTypeAni.play('Marquee_loop_animation'); }); } } checkWinType(score: number) { let winType = WIN_TYPE.NORAML_WIN; let bet = this.spinData.Bet; let multi = score / bet; if (multi == 0) { winType = WIN_TYPE.NONE; } else if (multi > 0 && multi < 6) { winType = WIN_TYPE.NORAML_WIN; } else if (multi >= 6 && multi < 20) { winType = WIN_TYPE.MIDDLE_WIN; } else if (multi >= 20 && multi < 35) { winType = WIN_TYPE.BIG_WIN; } else if (multi >= 35 && multi < 50) { winType = WIN_TYPE.MEGA_WIN; } else if (multi >= 50) { winType = WIN_TYPE.SUPER_MEGA_WIN; } return winType; } isScroll() { return this.rollerManager.isScroll(); } isShow: boolean = false; onIconClicked(iconComponent: Icon) { if (this.isEliminating) return; if (this.isShow) return; let isExpect = this.rollerManager.checkNextRollerExpect(6); if (isExpect) return; if (this.freeBg.active) return; this.node.parent.getChildByName('layer').active = false; if (this.rollerManager.isScroll()) return; this.isShow = true; let iconWorldPos = iconComponent.getWorldPosition(); let localPos = this.node.getComponent(UITransform).convertToNodeSpaceAR(iconWorldPos); let iconMsgNode = NodePoolManager.instance.getNodeFromPoolStatic('iconMsg', this.iconMsgPre); iconMsgNode.position = localPos; // 显示图标信息 this.node.parent.getChildByName('layer').getChildByName('iconLayer').addChild(iconMsgNode); let isLeft = false; if (iconComponent.rollerIndex < 4) { isLeft = true; } if (iconComponent.rollerIndex == 0 && iconComponent.startPos >= 2) { isLeft = false; } iconMsgNode.getComponent(IconMsg).show(isLeft, iconComponent); this.node.parent.getChildByName('layer').active = true; // 添加点击事件关闭图标信息 this.node.parent.getChildByName('layer').getChildByName('iconLayer').once(Node.EventType.TOUCH_END, () => { iconMsgNode.getComponent(IconMsg).hide(); this.hideIconMsg(); }); } // 加入非空判断 hideIconMsg() { if (this.node.parent.getChildByName('layer').active) { this.node.parent.getChildByName('layer').active = false; while (this.node.parent.getChildByName('layer').getChildByName('iconLayer').children.length > 0) { let child = this.node.parent.getChildByName('layer').getChildByName('iconLayer').children[0]; NodePoolManager.instance.putNodeToPool('iconMsg', child); this.isShow = false; } } } playNormalMultiAni(isSwitch: boolean) { // if (isSwitch) { // this.normalMultiAni.play('NM_multiVFX_switch_animation'); // } else { // this.normalMultiAni.play('NM_multiVFX_win_animation'); // } } playFreeMultiAni() { this.freeMultiAni.play('FS_multiVFX_win_animation'); } showReadyHand(rollerId?: number) { if (this.rollerManager.getIsManualStop() || this.rollerManager.getIsFastSpin()) return; this.readyHand.active = true; this.readyHand.getChildByName('readyHand').active = true; let spine = this.readyHand.getChildByName('readyHand').getComponent(sp.Skeleton); spine.setAnimation(0, 'readyHand_01', true); let posX = this.rollerManager.getRollerPosition(rollerId).x; this.readyHand.setPosition(posX, 270, 0); this.grayNode.active = true; this.grayNode.children.forEach((child, index) => { if (index <= rollerId) { child.active = true; } else { child.active = false; } }) if (rollerId == 0 || rollerId) { if (rollerId != -1) { AudioManager.instance.playSFX('Appear_Scatter_Sound_Final'); } } } hideReadyHand() { this.readyHand.active = false; let spine = this.readyHand.getChildByName('readyHand').getComponent(sp.Skeleton); spine.clearTracks(); this.grayNode.active = false; this.grayNode.children.forEach(child => { child.active = true; }) } showScatterOnIsScroll() { let scatterPos = this.rollerManager.getScatterPos(); this.scatterLayer.active = true; scatterPos.forEach(pos => { let iconNode = this.rollerManager.getIconNode(pos); if (this.scatterIconNodeMap.get(pos) == null) { this.scatterIconNodeMap.set(pos, { node: iconNode, originalParent: iconNode.parent, originalPosition: iconNode.position.clone() }); // 计算并设置正确位置 let worldPos = this.rollerManager.getIconWorldPosition(pos); let localPos = this.scatterLayer.getComponent(UITransform).convertToNodeSpaceAR(worldPos); iconNode.parent = this.scatterLayer; iconNode.setPosition(localPos); } }) } hideScatterOnIsScroll() { let scatterPos = this.rollerManager.getScatterPos(); scatterPos.forEach(pos => { let iconInfo = this.scatterIconNodeMap.get(pos); iconInfo.node.parent = iconInfo.originalParent; iconInfo.node.setPosition(iconInfo.originalPosition); this.scatterIconNodeMap.delete(pos); }) } playScatterAni(callback: Function) { AudioManager.instance.playSFX('Appear_Scatter_Win_Sound'); let scatterPos = this.rollerManager.getScatterPos(); let iconNodes = []; scatterPos.forEach(pos => { let iconNode = this.rollerManager.getIconNode(pos); iconNode.getComponent(Icon).playWinAni(true); iconNodes.push(iconNode); }) this.scheduleOnce(() => { iconNodes.forEach(iconNode => { iconNode.getComponent(Icon).playWinAni(false); }) callback(); }, 2); } showFeatureBuy(isShow: boolean) { this.featureBuyNode.active = !isShow; } setFeatureBuyInteractable(isInteractable: boolean) { this.featureBuyNode.getComponent(Sprite).grayscale = !isInteractable; this.featureBuyNode.getChildByName('FEATUREBUY').getComponent(Sprite).grayscale = !isInteractable; this.featureBuyNode.getComponent(Button).interactable = isInteractable; } // 修改winIconNodeMap的类型为存储更多信息 private scatterIconNodeMap: Map = new Map(); }