rp_11009/assets/Game/Scripts/SlotGame.ts
2026-04-10 10:40:03 +08:00

623 lines
23 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, 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('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);
}
}