rp_10012/assets/Game/scripts/SlotGame.ts
2025-09-17 14:09:54 +08:00

661 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { _decorator, Animation, Button, Component, Label, Layout, Node, ParticleSystem, Prefab, RichText, Size, sp, Sprite, 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;
// 跑马灯信息
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 multiLabel: Label = 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.multiLabel = this.base.getChildByName('multi').getComponent(Label);
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(1);
// }
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(multi: number) {
// this.multiLabel.string = `x${multi}`;
// this.freeMultiLabel.string = `x${multi}`;
// }
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 bottomData = this.spinData.WinPosition.BottomNormal.map(pos => pos + 4);
let topData = this.spinData.WinPosition.TopNormal || [];
deleteMsg = [...topData, ...bottomData];
let specialIcons = [...this.spinData.WinPosition.BottomGold];
let aniData = specialIcons.map(pos => pos + 4) || [];
this.rollerManager.handleWinIcons(this.winLayer, deleteMsg, aniData);
}
handleScatter(isRollerScrolling: boolean,) {
if (isRollerScrolling) {
} else {
this.scatterLayer.active = false;
}
}
changeIconAndFrameType(spinData: any) {
this.spinData = spinData;
// 先去找到PanChange当中是否有oldPos,如果没有代表当前icon没有动就去crossSymbols当中找
// 初始化changeData数组
let changeData = [];
let colorChanges = this.spinData.PanChanges.CrossSymbolColorChange
// 遍历CrossSymbolColorChange获取颜色变化信息
for (let key in this.spinData.PanChanges.CrossSymbolColorChange) {
let colorChange = this.spinData.PanChanges.CrossSymbolColorChange[key];
let newIndex = colorChange.NewColor;
let oldStartPos;
let frameType;
let height;
// 先在CrossSymbolPosChange中查找位置变化
if (this.spinData.PanChanges.CrossSymbolPosChange[key]) {
oldStartPos = this.spinData.PanChanges.CrossSymbolPosChange[key].OldPos[0];
}
// 如果没找到则在crossSymbols中查找
else if (this.spinData.CrossSymbols[key]) {
oldStartPos = this.spinData.CrossSymbols[key].PosFirst;
}
// 从crossSymbols获取frameType和height
if (this.spinData.CrossSymbols[key]) {
let symbol = this.spinData.CrossSymbols[key];
frameType = symbol.FrameType;
height = symbol.PosLast - symbol.PosFirst + 1;
}
// 添加到changeData数组
changeData.push({
oldStartPos: oldStartPos + 4,
newIndex: newIndex,
newFrameType: frameType,
lheight: height
});
}
this.rollerManager.changeIconAndFrameType(changeData);
}
// 消除创建的逻辑
createNewIconTop(spinData: any) {
this.spinData = spinData;
let createDatas = this.spinData.PanChanges.TopNewSymbols;
let createDatas2 = this.spinData.PanChanges.BottomNewSymbols;
this.rollerManager.createNewIconTop([createDatas, ...createDatas2], spinData);
}
// icon掉落的逻辑
iconFallDown() {
this.setWaysCount(this.spinData.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.SymbolNumOfReels[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.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);
}
showMultiMove(multiNum) {
this.rollerManager.showMultiMove(multiNum)
}
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.AllBet;
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 - 1).x;
this.readyHand.setPosition(posX, 180, 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<number, {
node: Node,
originalParent: Node,
originalPosition: Vec3
}> = new Map();
}