All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 1m25s
872 lines
29 KiB
TypeScript
872 lines
29 KiB
TypeScript
import { _decorator, Component, Mask, Node, Sprite, SpriteFrame, UITransform, v3, Vec3 } from 'cc';
|
||
import { IconFactory } from './IconFactory';
|
||
import { Roller } from './Roller';
|
||
import { GameData, ICON_HEIGHT, ICON_WIDTH, ROLLER_COMBINE_EVENT, ROLLER_EVENT } from './Define';
|
||
import { AudioManager } from '../../../Loading/scripts/manager/AudioManager';
|
||
import { BaseRoller } from './BaseRoller';
|
||
import { HRoller } from './HRoller';
|
||
let { ccclass, property, executeInEditMode } = _decorator;
|
||
|
||
@ccclass('RollerManager')
|
||
@executeInEditMode
|
||
export class RollerManager extends Component {
|
||
|
||
@property({ tooltip: '图标的宽度' })
|
||
iconWidth: number = ICON_WIDTH;
|
||
|
||
@property({ tooltip: '图标的高度' })
|
||
iconHeight: number = ICON_HEIGHT;
|
||
|
||
@property({ tooltip: '图标之间的水平距离' })
|
||
iconHMerge: number = 0;
|
||
|
||
@property({ type: IconFactory, tooltip: '图标工厂' })
|
||
iconFactory: IconFactory = null;
|
||
|
||
@property(SpriteFrame)
|
||
hMaskSpriteFrame: SpriteFrame = null;
|
||
|
||
@property(SpriteFrame)
|
||
vMaskSpriteFrame: SpriteFrame = null;
|
||
|
||
rollerMsg: any[] = [
|
||
{ row: 1, col: 4, isHorizontal: true },
|
||
{ row: 5, col: 1, isHorizontal: false },
|
||
{ row: 5, col: 1, isHorizontal: false },
|
||
{ row: 5, col: 1, isHorizontal: false },
|
||
{ row: 5, col: 1, isHorizontal: false },
|
||
{ row: 5, col: 1, isHorizontal: false },
|
||
{ row: 5, col: 1, isHorizontal: false },
|
||
];
|
||
// 横向滚轮数组
|
||
hRollers: HRoller[] = [];
|
||
// 竖向滚轮数组
|
||
vRollers: Roller[] = [];
|
||
// 滚轮数组
|
||
allRollers: BaseRoller[] = [];
|
||
// 是否快速旋转
|
||
_isFastSpin: boolean = false;
|
||
// 是否手动停止
|
||
_isManualStop: boolean = false;
|
||
// spinData
|
||
_spinData: GameData = null;
|
||
/** 分割过的数据,一位数组改为二维数组, 每个元素代表一个滚轮 */
|
||
_resultStopData: number[][] = [];
|
||
// 是否是免费游戏
|
||
_isFreeSpin: boolean = false;
|
||
// 不规则图标信息
|
||
_crossSymbols: any = null;
|
||
// 处理过的不规则icon信息
|
||
_processedCrossSymbols: any = null;
|
||
|
||
private hMaskNode: Node = null;
|
||
private vMaskNode: Node = null;
|
||
|
||
|
||
// 添加计数器
|
||
private _deletedRollerCount: number = 0;
|
||
private _createdRollerCount: number = 0;
|
||
private _fallenRollerCount: number = 0;
|
||
|
||
|
||
@property
|
||
_format = false;
|
||
@property({ tooltip: '格式化' })
|
||
get format(): boolean {
|
||
return this._format;
|
||
}
|
||
set format(a: boolean) {
|
||
this._format = a;
|
||
this.node.removeAllChildren();
|
||
this.allRollers = [];
|
||
this.hRollers = [];
|
||
this.vRollers = [];
|
||
|
||
this.hMaskNode = this.createMaskNode('HMask', this.hMaskSpriteFrame);
|
||
this.vMaskNode = this.createMaskNode('VMask', this.vMaskSpriteFrame);
|
||
|
||
this.node.addChild(this.hMaskNode);
|
||
this.node.addChild(this.vMaskNode);
|
||
|
||
let rollerLength = this.rollerMsg.length;
|
||
for (let i = 0; i < rollerLength; i++) {
|
||
let rollerMsg = this.rollerMsg[i];
|
||
|
||
if (rollerMsg.isHorizontal) {
|
||
let hRoller = HRoller.create(i, rollerMsg.col, this.iconWidth, this.iconHeight, this.iconFactory);
|
||
// hRoller.format = true;
|
||
|
||
this.hMaskNode.addChild(hRoller.node);
|
||
|
||
let rollerPosition = new Vec3(0, 3 * this.iconHeight + 35, 0);
|
||
this.hMaskNode.setPosition(rollerPosition);
|
||
// hRoller.node.setPosition(rollerPosition);
|
||
// 保存引用
|
||
this.hRollers.push(hRoller);
|
||
this.allRollers.push(hRoller);
|
||
} else {
|
||
let roller = Roller.create(i, rollerMsg.row, this.iconWidth, this.iconHeight, this.iconFactory);
|
||
// roller.format = true;
|
||
|
||
this.vMaskNode.addChild(roller.node);
|
||
|
||
let rollerPosition = this.getRollerPosition(i - 1);
|
||
roller.node.setPosition(rollerPosition);
|
||
this.vRollers.push(roller);
|
||
this.allRollers.push(roller);
|
||
}
|
||
}
|
||
}
|
||
|
||
private createMaskNode(name: string, spriteFrame: SpriteFrame): Node {
|
||
let maskNode = new Node();
|
||
|
||
maskNode.name = name;
|
||
|
||
let comp = maskNode.addComponent(Mask);
|
||
comp.type = Mask.Type.SPRITE_STENCIL;
|
||
|
||
let spriteCom = maskNode.getComponent(Sprite);
|
||
spriteCom.spriteFrame = spriteFrame;
|
||
spriteCom.sizeMode = Sprite.SizeMode.CUSTOM;
|
||
spriteCom.trim = true;
|
||
return maskNode;
|
||
}
|
||
|
||
protected onLoad(): void {
|
||
this.format = true;
|
||
this.scatterPos = [];
|
||
this.registerEvent();
|
||
|
||
// 重置计数器
|
||
this._deletedRollerCount = 0;
|
||
this._createdRollerCount = 0;
|
||
this._fallenRollerCount = 0;
|
||
}
|
||
|
||
protected update(dt: number): void {
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let roller = this.allRollers[i];
|
||
roller.localUpdate(dt);
|
||
}
|
||
}
|
||
|
||
registerEvent() {
|
||
for (let lx = 0; lx < this.allRollers.length; lx++) {
|
||
let roller = this.allRollers[lx];
|
||
roller.node.on(ROLLER_EVENT.ON_R_ICON_CREATE, this.onRollerRIconCreate, this);
|
||
roller.node.on(ROLLER_EVENT.LAST_PAGE_CREATE, this.onRollerLastPageCreate, this);
|
||
roller.node.on(ROLLER_EVENT.ROLLER_BOUNCE, this.onRollerBounce, this);
|
||
roller.node.on(ROLLER_EVENT.ROLLER_DECELERATE, this.onRollerSlowDown, this);
|
||
roller.node.on(ROLLER_EVENT.ROLLER_STOP, this.onRollerStop, this);
|
||
roller.node.on(ROLLER_EVENT.ROLLER_UNIFORM, this.onRollerUniform, this);
|
||
roller.node.on(ROLLER_EVENT.ICON_DELETED, this.onRollerIconDeleted, this);
|
||
roller.node.on(ROLLER_EVENT.ICON_CREATE, this.onRollerIconCreate, this);
|
||
roller.node.on(ROLLER_EVENT.ICON_FALLEN, this.onRollerIconFallen, this);
|
||
}
|
||
}
|
||
|
||
setFastSpin(isFastSpin: boolean) {
|
||
this._isFastSpin = isFastSpin;
|
||
|
||
this.allRollers.forEach(roller => {
|
||
roller.setFastSpin(isFastSpin);
|
||
})
|
||
}
|
||
|
||
getIsFastSpin(): boolean {
|
||
return this._isFastSpin;
|
||
}
|
||
|
||
|
||
setIsFreeSpin(isFreeSpin: boolean) {
|
||
this._isFreeSpin = isFreeSpin;
|
||
}
|
||
|
||
changeSpeedFast() {
|
||
this.allRollers.forEach(roller => {
|
||
if (roller.isScroll()) {
|
||
roller.changeSpeed();
|
||
}
|
||
})
|
||
}
|
||
|
||
|
||
// 随机创建ICON回调
|
||
onRollerRIconCreate() {
|
||
return;
|
||
}
|
||
|
||
// 最后一页创建的回调
|
||
onRollerLastPageCreate(rollerId: number) {
|
||
if (this._isFastSpin) return;
|
||
|
||
let isExpect = this.checkNextRollerExpect(rollerId);
|
||
|
||
let nextStopRollerId = this.getNextRollerIndex(rollerId);
|
||
|
||
let stopSpeedData = this._isFastSpin ? [[0, 6000]] : [[0.1, 3500]];
|
||
if (!this._isFastSpin) {
|
||
stopSpeedData = isExpect ? [[1, 4000], [0.5], [0.1, 2500], [0.5], [0.5, 725]] : [[0.1, 3500]];
|
||
}
|
||
|
||
if (nextStopRollerId != -1) {
|
||
let nextStopRollerCrossSymbols = this._processedCrossSymbols[nextStopRollerId];
|
||
this.allRollers[nextStopRollerId].setCrossSymbols(nextStopRollerCrossSymbols);
|
||
|
||
this.allRollers[nextStopRollerId].stopScroll(this._resultStopData[nextStopRollerId], stopSpeedData, this._spinData.XnInfo);
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
scatterPos: number[] = [];
|
||
checkNextRollerExpect(rollerId: number, needScatterCount: number = 3) {
|
||
let curScatterCount = 0;
|
||
this.scatterPos = [];
|
||
let topData = this._resultStopData[0];
|
||
topData.forEach((iconIndex, index) => {
|
||
if (iconIndex == 1) {
|
||
curScatterCount++;
|
||
this.scatterPos.push(index);
|
||
}
|
||
})
|
||
|
||
// 检查所有已经停止的垂直滚轮(包括当前滚轮)
|
||
for (let i = 1; i <= rollerId; i++) {
|
||
const rollerData = this._resultStopData[i];
|
||
if (!rollerData) continue;
|
||
|
||
// 检查每个位置
|
||
for (let j = 0; j < rollerData.length; j++) {
|
||
// 检查是否有交叉符号覆盖该位置
|
||
if (this._processedCrossSymbols &&
|
||
this._processedCrossSymbols[i] &&
|
||
this._processedCrossSymbols[i][j]) {
|
||
|
||
const symbolInfo = this._processedCrossSymbols[i][j];
|
||
|
||
// 只计算开始位置,避免重复计数
|
||
if (symbolInfo.isStart && symbolInfo.iconIndex === 1) {
|
||
curScatterCount++;
|
||
this.scatterPos.push(4 + (i - 1) * 5 + symbolInfo.startPos);
|
||
}
|
||
}
|
||
// 如果位置没有被交叉符号覆盖,且是scatter
|
||
else if (rollerData[j] === 1) {
|
||
curScatterCount++;
|
||
this.scatterPos.push(4 + (i - 1) * 5 + j);
|
||
}
|
||
}
|
||
}
|
||
return curScatterCount >= needScatterCount;
|
||
}
|
||
|
||
getScatterPos(): number[] {
|
||
return this.scatterPos;
|
||
}
|
||
|
||
// 滚轮回弹的回调
|
||
onRollerBounce(rollerId: number) {
|
||
|
||
return;
|
||
}
|
||
|
||
// 滚轮减速的回调
|
||
onRollerSlowDown() {
|
||
return;
|
||
}
|
||
|
||
// 滚轮停止的回调
|
||
onRollerStop(rollerId: number) {
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ONE_ROLLER_STOP, rollerId);
|
||
// 检测当前滚轮的icon下标是否是8
|
||
let stopData = this._resultStopData[rollerId];
|
||
let isWild = stopData.indexOf(0) !== -1;
|
||
let isScatter = stopData.indexOf(1) !== -1;
|
||
if (!this._isFreeSpin && !this._isManualStop && !this._isFastSpin) {
|
||
if (isWild && !isScatter) {
|
||
AudioManager.instance.playSFX('Appear_Wild_Sound');
|
||
}
|
||
|
||
if (isScatter) {
|
||
AudioManager.instance.playSFX('Appear_Scatter_Sound');
|
||
}
|
||
|
||
if (!isWild && !isScatter) {
|
||
AudioManager.instance.playSFX('Scroll_Stop_Sound');
|
||
}
|
||
}
|
||
|
||
let allRollerStop = true;
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let roller = this.allRollers[i];
|
||
if (roller.isScroll()) allRollerStop = false;
|
||
}
|
||
|
||
if (allRollerStop) {
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ALL_ROLLER_STOP);
|
||
|
||
if (this._isFastSpin || this._isManualStop) {
|
||
let hasScatter = this.scatterPos.length > 0;
|
||
let hasWild = false;
|
||
this._resultStopData.forEach(stopData => {
|
||
if (stopData.indexOf(0) !== -1) {
|
||
hasWild = true;
|
||
}
|
||
})
|
||
|
||
if (hasWild && !hasScatter) {
|
||
AudioManager.instance.playSFX('Appear_Wild_Sound');
|
||
}
|
||
|
||
if (hasScatter) {
|
||
AudioManager.instance.playSFX('Appear_Scatter_Sound');
|
||
}
|
||
|
||
if (!hasWild && !hasScatter) {
|
||
AudioManager.instance.playSFX('Scroll_Stop_Sound');
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 滚轮匀速时候的回调
|
||
onRollerUniform() {
|
||
return;
|
||
}
|
||
|
||
|
||
// 滚轮icon删除的回调
|
||
onRollerIconDeleted(rollerId: number) {
|
||
this._deletedRollerCount++;
|
||
if (this._deletedRollerCount >= this.allRollers.length) {
|
||
this._deletedRollerCount = 0; // 重置计数器
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_DELETED);
|
||
}
|
||
}
|
||
|
||
// 滚轮icon创建的回调
|
||
onRollerIconCreate(rollerId: number) {
|
||
this._createdRollerCount++;
|
||
if (this._createdRollerCount >= this.allRollers.length) {
|
||
this._createdRollerCount = 0; // 重置计数器
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_CREATED);
|
||
}
|
||
}
|
||
|
||
// 滚轮icon掉落的回调
|
||
onRollerIconFallen(rollerId: number) {
|
||
this._fallenRollerCount++;
|
||
|
||
if (this._fallenRollerCount >= this.allRollers.length) {
|
||
|
||
this._fallenRollerCount = 0; // 重置计数器
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ALL_ROLLER_ICONS_FALLEN);
|
||
|
||
}
|
||
}
|
||
|
||
// 滚轮是否在滚动
|
||
isScroll(): boolean {
|
||
for (let lx = 0; lx < this.allRollers.length; lx++) {
|
||
let roller = this.allRollers[lx];
|
||
if (roller.isScroll()) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 初始化滚轮数据
|
||
initRollerWithIcon(data: GameData) {
|
||
this._spinData = data;
|
||
|
||
let topData = data.PanColor.Top;
|
||
let bottomData = data.PanColor.Bottom;
|
||
this._crossSymbols = data.CrossSymbols;
|
||
|
||
// 分割数据
|
||
this._resultStopData = [topData, ...this.splitArray(bottomData, [5, 5, 5, 5, 5, 5])];
|
||
// 处理n*1符号
|
||
let processedCrossSymbols = this.processCrossSymbolsForRollers();
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let roller = this.allRollers[i];
|
||
let rollerCrossSymbols = processedCrossSymbols[i];
|
||
|
||
roller.initRollerWithIcon(i, this._resultStopData[i], rollerCrossSymbols, this._spinData.XnInfo);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 只处理竖向滚轮
|
||
* 处理n*1符号数据,为每个滚轮准备对应的n*1符号信息
|
||
* @returns 处理后的n*1符号数据数组,每个元素对应一个滚轮
|
||
*/
|
||
private processCrossSymbolsForRollers(): any[] {
|
||
this._processedCrossSymbols = [];
|
||
let row = 5;
|
||
let col = 6;
|
||
// 如果没有交叉符号数据,返回空对象数组
|
||
if (!this._crossSymbols) {
|
||
return new Array(col).fill({});
|
||
}
|
||
|
||
// 为每个滚轮创建一个空对象
|
||
let rollerSymbols: any[] = [];
|
||
for (let i = 0; i < col; i++) {
|
||
rollerSymbols.push({});
|
||
}
|
||
|
||
// 遍历所有交叉符号
|
||
for (let symbolId in this._crossSymbols) {
|
||
let symbol = this._crossSymbols[symbolId];
|
||
|
||
// 计算符号所在的滚轮索引
|
||
let rollerIndex = Math.floor(symbol.PosFirst / 5);
|
||
|
||
// 计算在滚轮中的起始和结束位置
|
||
let startPos = symbol.PosFirst % row;
|
||
let endPos = symbol.PosLast % row;
|
||
// 在滚轮的每个位置上设置符号信息
|
||
for (let pos = startPos; pos <= endPos; pos++) {
|
||
rollerSymbols[rollerIndex][pos] = {
|
||
id: symbolId,
|
||
isStart: pos === startPos,
|
||
isEnd: pos === endPos,
|
||
startPos: startPos,
|
||
endPos: endPos,
|
||
lHeight: endPos - startPos + 1,
|
||
frameType: symbol.FrameType,
|
||
iconIndex: symbol.Color
|
||
};
|
||
}
|
||
}
|
||
this._processedCrossSymbols = [{}, ...rollerSymbols];
|
||
return this._processedCrossSymbols;
|
||
}
|
||
|
||
// 滚轮Icon生成规则
|
||
setRollerIconRule(rollerIconRule: number[][]) {
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
this.allRollers[i].setRollerIconRule(rollerIconRule[i]);
|
||
}
|
||
}
|
||
|
||
getIconNode(pos: number): Node {
|
||
let lx = this.getLx(pos);
|
||
let ly = this.getLy(pos);
|
||
let roller = this.allRollers[lx];
|
||
return roller.getIconNode(ly);
|
||
}
|
||
|
||
getLx(pos: number): number {
|
||
let currentPos = pos;
|
||
|
||
for (let i = 0; i < this.rollerMsg.length; i++) {
|
||
let roller = this.rollerMsg[i];
|
||
let rollerSize = roller.row * roller.col;
|
||
|
||
if (currentPos < rollerSize) {
|
||
return i;
|
||
}
|
||
currentPos -= rollerSize;
|
||
}
|
||
|
||
return -1; // 如果位置超出范围,返回-1
|
||
}
|
||
|
||
getLy(pos: number): number {
|
||
let currentPos = pos;
|
||
|
||
// 先找到对应的滚轮
|
||
let rollerId = this.getLx(pos);
|
||
if (rollerId === -1) return -1;
|
||
|
||
// 计算在当前滚轮之前的所有位置数
|
||
for (let i = 0; i < rollerId; i++) {
|
||
let roller = this.rollerMsg[i];
|
||
currentPos -= (roller.row * roller.col);
|
||
}
|
||
|
||
let currentRoller = this.rollerMsg[rollerId];
|
||
|
||
if (currentRoller.isHorizontal) {
|
||
// 横向滚轮直接返回列位置
|
||
return currentPos;
|
||
} else {
|
||
// 纵向滚轮返回行位置
|
||
return currentPos % currentRoller.row;
|
||
}
|
||
}
|
||
|
||
|
||
// 获取第一个可以停止的滚轮id
|
||
getFirstRollerIndex(): number {
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
// 获取下一个可以停止的滚轮id
|
||
getNextRollerIndex(id: number): number {
|
||
for (let lx = id + 1; lx < this.allRollers.length; lx++) {
|
||
return lx;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
resetInfo() {
|
||
this.allRollers.forEach(roller => {
|
||
roller.resetInfo();
|
||
})
|
||
}
|
||
|
||
getIsManualStop(): boolean {
|
||
return this._isManualStop;
|
||
}
|
||
|
||
// 滚轮开始滚动
|
||
startScroll() {
|
||
this.scatterPos = [];
|
||
this._isManualStop = false;
|
||
this.allRollers.forEach(roller => {
|
||
roller.startScroll();
|
||
})
|
||
}
|
||
|
||
// 滚轮停止滚动
|
||
stopScroll(data: GameData) {
|
||
this._spinData = data;
|
||
|
||
this._resultStopData = [];
|
||
// 分割底盘数据
|
||
this._resultStopData = [data.PanColor.Top, ...this.splitArray(data.PanColor.Bottom, [5, 5, 5, 5, 5, 5])];
|
||
// 处理不对则icon
|
||
this._crossSymbols = data.CrossSymbols;
|
||
let processedCrossSymbols = this.processCrossSymbolsForRollers();
|
||
let stopSpeedData = this._isFastSpin ? [[0, 6000]] : [[0.1, 3500]];
|
||
if (this._isFastSpin) {
|
||
// this.stopAllRollersImmediately(processedCrossSymbols);
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let stopData = this._resultStopData[i];
|
||
let roller = this.allRollers[i];
|
||
|
||
let rollerCrossSymbols = processedCrossSymbols[i];
|
||
roller.setCrossSymbols(rollerCrossSymbols);
|
||
roller.stopScroll(stopData, stopSpeedData, data.XnInfo)
|
||
}
|
||
} else {
|
||
// this.stopRollersInSequence(processedCrossSymbols);
|
||
let firstRollerCrossSymbols = processedCrossSymbols[0];
|
||
this.allRollers[0].setCrossSymbols(firstRollerCrossSymbols);
|
||
this.allRollers[0].stopScroll(this._resultStopData[0], stopSpeedData, data.XnInfo);
|
||
}
|
||
}
|
||
|
||
stopRollersInSequence(processedCrossSymbols: any[]) {
|
||
let standardStopDuration = [[0.1, 3500]];
|
||
}
|
||
|
||
manualStop(data: GameData) {
|
||
this._isManualStop = true;
|
||
this._resultStopData = [];
|
||
let topData = data.PanColor.Top;
|
||
this._resultStopData = [topData, ...this.splitArray(data.PanColor.Bottom, [5, 5, 5, 5, 5, 5])];
|
||
this._crossSymbols = data.CrossSymbols;
|
||
let processedCrossSymbols = this.processCrossSymbolsForRollers();
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let stopData = this._resultStopData[i];
|
||
let roller = this.allRollers[i];
|
||
let rollerCrossSymbols = processedCrossSymbols[i];
|
||
roller.setCrossSymbols(rollerCrossSymbols);
|
||
roller.manualStopScroll(stopData, data.XnInfo)
|
||
}
|
||
}
|
||
|
||
// 对服务器下发的数据进行操作
|
||
splitArray<T>(arr: T[], sizes: number[]): T[][] {
|
||
let result: T[][] = [];
|
||
let currentIndex = 0;
|
||
|
||
for (let size of sizes) {
|
||
let subArray = arr.slice(currentIndex, currentIndex + size);
|
||
result.push(subArray);
|
||
currentIndex += size;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// 获取滚轮的坐标
|
||
getRollerPosition(id: number): Vec3 {
|
||
let col = 6;
|
||
let hMiddle = Math.floor(col / 2);
|
||
let x = -(col % 2 == 0 ? hMiddle - 0.5 : hMiddle) * (this.iconWidth + this.iconHMerge);
|
||
let xdis = id * this.iconWidth + id * this.iconHMerge;
|
||
return v3(x + xdis, 0, 0);
|
||
}
|
||
|
||
// 获取icon坐标
|
||
getIconWorldPosition(pos: number) {
|
||
let lx = this.getLx(pos);
|
||
let roller = this.allRollers[lx];
|
||
return roller.getIconWorldPosition(this.getLy(pos));
|
||
}
|
||
|
||
getContentNode(pos: number): Node {
|
||
let lx = this.getLx(pos);
|
||
let roller = this.allRollers[lx];
|
||
return roller.getContentNode();
|
||
}
|
||
|
||
showMultiMove(XnInfo) {
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let roller = this.allRollers[i];
|
||
roller.showMultiMove(XnInfo)
|
||
}
|
||
}
|
||
|
||
|
||
// 将图标移动到win层并处理删除和动画
|
||
handleWinIcons(winLayer: Node, deleteMsg: number[], aniData: number[]) {
|
||
if (deleteMsg.length === 0 && aniData.length === 0) {
|
||
return; // 没有需要处理的图标,直接返回
|
||
}
|
||
|
||
// 激活win层
|
||
winLayer.active = true;
|
||
|
||
// 1. 找出所有需要处理的唯一位置
|
||
let processedNodes = new Set();
|
||
let uniquePositions = [];
|
||
|
||
// 合并并去重处理位置
|
||
[...deleteMsg, ...aniData].forEach(pos => {
|
||
let iconNode = this.getIconNode(pos);
|
||
if (iconNode && !processedNodes.has(iconNode)) {
|
||
uniquePositions.push(pos);
|
||
processedNodes.add(iconNode);
|
||
}
|
||
});
|
||
|
||
// 2. 缓存winLayer的UITransform以提高性能
|
||
let winLayerTransform = winLayer.getComponent(UITransform);
|
||
|
||
// 3. 将所有需处理的图标移动到win层
|
||
uniquePositions.forEach(pos => {
|
||
let iconNode = this.getIconNode(pos);
|
||
if (iconNode) {
|
||
// 保存原始父节点和位置信息到Map中
|
||
this.winIconNodeMap.set(pos, {
|
||
node: iconNode,
|
||
originalParent: iconNode.parent,
|
||
originalPosition: iconNode.position.clone()
|
||
});
|
||
// 计算并设置正确位置
|
||
let worldPos = this.getIconWorldPosition(pos);
|
||
if (worldPos) {
|
||
let localPos = winLayerTransform.convertToNodeSpaceAR(worldPos);
|
||
iconNode.parent = winLayer;
|
||
iconNode.setPosition(localPos);
|
||
}
|
||
}
|
||
});
|
||
|
||
// 4. 准备删除操作的数据结构
|
||
let rollerDeletePositions = new Map<number, number[]>();
|
||
let rollerAniPos = new Map<number, number[]>();
|
||
|
||
// 5. 分类处理需要删除的位置
|
||
deleteMsg.forEach(pos => {
|
||
let lx = this.getLx(pos);
|
||
let ly = this.getLy(pos);
|
||
|
||
if (!rollerDeletePositions.has(lx)) {
|
||
rollerDeletePositions.set(lx, []);
|
||
}
|
||
rollerDeletePositions.get(lx).push(ly);
|
||
});
|
||
|
||
// 6. 分类处理需要动画的位置
|
||
aniData.forEach(pos => {
|
||
let lx = this.getLx(pos);
|
||
let ly = this.getLy(pos);
|
||
|
||
if (!rollerAniPos.has(lx)) {
|
||
rollerAniPos.set(lx, []);
|
||
}
|
||
rollerAniPos.get(lx).push(ly);
|
||
});
|
||
|
||
// 7. 执行删除操作
|
||
rollerDeletePositions.forEach((positions, lx) => {
|
||
if (positions.length > 0) {
|
||
this.allRollers[lx].deleteIconNode(positions);
|
||
}
|
||
});
|
||
AudioManager.instance.playSFX('Win_Frame_Sound');
|
||
|
||
// 8. 执行动画操作
|
||
rollerAniPos.forEach((positions, lx) => {
|
||
if (positions.length > 0) {
|
||
this.allRollers[lx].playFrameTypeChangeAni(positions);
|
||
}
|
||
});
|
||
|
||
|
||
// 保存一个引用以避免闭包中的引用问题
|
||
let positionsCopy = [...uniquePositions];
|
||
let winLayerRef = winLayer;
|
||
|
||
|
||
// 9. 计算动画播放时间并处理没有删除图标的roller
|
||
let animationTime = aniData.length > 0 ? 2.4 : 1.2;
|
||
let rollersWithoutDeleteOps = [];
|
||
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
if (!rollerDeletePositions.has(i) || rollerDeletePositions.get(i).length === 0) {
|
||
rollersWithoutDeleteOps.push(i);
|
||
}
|
||
}
|
||
|
||
// 10. 使用单个计时器处理所有没有删除操作的roller
|
||
if (rollersWithoutDeleteOps.length > 0) {
|
||
this.scheduleOnce(() => {
|
||
rollersWithoutDeleteOps.forEach(i => {
|
||
this.allRollers[i].node.emit(ROLLER_EVENT.ICON_DELETED, this.allRollers[i]);
|
||
});
|
||
}, animationTime);
|
||
}
|
||
|
||
|
||
|
||
// 11. 添加清理函数,根据需要在适当时机调用
|
||
this.scheduleOnce(() => {
|
||
AudioManager.instance.playSFX('Win_Eliminate_Sound');
|
||
if (rollerAniPos.size > 0) {
|
||
AudioManager.instance.playSFX('Symbol_Change_Sound');
|
||
}
|
||
this.returnIconsFromWinLayer(winLayerRef, positionsCopy);
|
||
}, 1.1); // 在动画结束前稍早将图标移回
|
||
}
|
||
|
||
// 将图标从win层返回到原始层
|
||
private returnIconsFromWinLayer(winLayer: Node, positions: number[]) {
|
||
|
||
if (!winLayer || !positions || positions.length === 0) {
|
||
console.warn("Invalid arguments in returnIconsFromWinLayer");
|
||
if (winLayer) winLayer.active = false;
|
||
return;
|
||
}
|
||
|
||
|
||
positions.forEach(pos => {
|
||
let iconInfo = this.winIconNodeMap.get(pos);
|
||
|
||
// 检查图标信息是否存在
|
||
if (!iconInfo) {
|
||
console.warn(`No info found for icon at position ${pos}`);
|
||
return; // 跳过当前图标
|
||
}
|
||
|
||
let { node: iconNode, originalParent, originalPosition } = iconInfo;
|
||
|
||
// 检查图标是否还存在(可能已被删除)
|
||
if (iconNode && iconNode.isValid && originalParent && originalParent.isValid) {
|
||
try {
|
||
// 直接使用保存的原始父节点和位置信息
|
||
iconNode.parent = originalParent;
|
||
iconNode.position = originalPosition;
|
||
} catch (error) {
|
||
console.error(`Error moving icon back at position ${pos}:`, error);
|
||
}
|
||
} else {
|
||
console.warn(`Icon or original parent is invalid for position ${pos}`);
|
||
}
|
||
|
||
// 无论处理成功与否,都清理引用
|
||
this.winIconNodeMap.delete(pos);
|
||
});
|
||
|
||
// 隐藏win层
|
||
winLayer.active = false;
|
||
}
|
||
|
||
// changeIconAndFrameType(panChanges: any[]) {
|
||
// let rollerChangePositions: Map<number, any[]> = new Map();
|
||
// panChanges.forEach(change => {
|
||
// let lx = this.getLx(change.oldStartPos);
|
||
// let ly = this.getLy(change.oldStartPos);
|
||
// // 获取或创建该列的变化位置数组
|
||
// if (!rollerChangePositions.has(lx)) {
|
||
// rollerChangePositions.set(lx, []);
|
||
// }
|
||
// // 将oldStartPos改为ly并添加到对应列的数组中
|
||
// change.oldStartPos = ly;
|
||
// rollerChangePositions.get(lx).push(change);
|
||
// })
|
||
|
||
// rollerChangePositions.forEach((changes, lx) => {
|
||
// this.allRollers[lx].chanegeIconAndFrameType(changes);
|
||
// })
|
||
// }
|
||
|
||
|
||
createNewIconTop(createDatas: number[][], spinData: any) {
|
||
this.allRollers.forEach((roller, index) => {
|
||
let createData = createDatas[index];
|
||
|
||
roller.createNewIconTop(createData, spinData.XnInfo);
|
||
})
|
||
}
|
||
|
||
iconFallDown(data: GameData) {
|
||
this._spinData = data;
|
||
this._resultStopData = [];
|
||
this._fallenRollerCount = 0; // 重置计数器
|
||
this._deletedRollerCount = 0;
|
||
// 分割底盘数据
|
||
this._resultStopData = [data.PanColor.Top, ...this.splitArray(data.PanColor.Bottom, [5, 5, 5, 5, 5, 5])];
|
||
// 处理不对则icon
|
||
this._crossSymbols = data.CrossSymbols;
|
||
let processedCrossSymbols = this.processCrossSymbolsForRollers();
|
||
|
||
this.allRollers.forEach((roller, index) => {
|
||
let stopData = this._resultStopData[index];
|
||
let rollerCrossSymbols = processedCrossSymbols[index];
|
||
roller.setCrossSymbols(rollerCrossSymbols);
|
||
this.scheduleOnce(() => {
|
||
roller.iconFallDown(stopData, rollerCrossSymbols)
|
||
}, 0.03 * index)
|
||
})
|
||
|
||
// 在第一个图标开始掉落时播放音效
|
||
this.scheduleOnce(() => {
|
||
AudioManager.instance.playSFX('Win_Symbol_Fall');
|
||
}, 0.3);
|
||
}
|
||
|
||
onDestroy(): void {
|
||
// 清理事件监听
|
||
for (let roller of this.allRollers) {
|
||
roller.node.off(ROLLER_EVENT.ON_R_ICON_CREATE, this.onRollerRIconCreate, this);
|
||
roller.node.off(ROLLER_EVENT.LAST_PAGE_CREATE, this.onRollerLastPageCreate, this);
|
||
roller.node.off(ROLLER_EVENT.ROLLER_BOUNCE, this.onRollerBounce, this);
|
||
roller.node.off(ROLLER_EVENT.ROLLER_DECELERATE, this.onRollerSlowDown, this);
|
||
roller.node.off(ROLLER_EVENT.ROLLER_STOP, this.onRollerStop, this);
|
||
roller.node.off(ROLLER_EVENT.ROLLER_UNIFORM, this.onRollerUniform, this);
|
||
roller.node.off(ROLLER_EVENT.ICON_DELETED, this.onRollerIconDeleted, this);
|
||
roller.node.off(ROLLER_EVENT.ICON_CREATE, this.onRollerIconCreate, this);
|
||
roller.node.off(ROLLER_EVENT.ICON_FALLEN, this.onRollerIconFallen, this);
|
||
}
|
||
}
|
||
|
||
// 修改winIconNodeMap的类型为存储更多信息
|
||
private winIconNodeMap: Map<number, {
|
||
node: Node,
|
||
originalParent: Node,
|
||
originalPosition: Vec3
|
||
}> = new Map();
|
||
|
||
|
||
}
|
||
|