559 lines
17 KiB
TypeScript
559 lines
17 KiB
TypeScript
import { _decorator, Component, Mask, Node, Sprite, SpriteFrame, UITransform, v3, Vec3 } from 'cc';
|
||
import { IconFactory } from './IconFactory';
|
||
import { Roller, ROLLER_STATE } from './Roller';
|
||
import { ICON_HEIGHT, ICON_WIDTH, IParsedGameData, ROLLER_COMBINE_EVENT, ROLLER_EVENT } from './Define';
|
||
import { AudioManager } from '../../Main/Scripts/managers/AudioManager';
|
||
let { ccclass, property, executeInEditMode } = _decorator;
|
||
|
||
// 帧到启动ID映射(按帧序)
|
||
let frameOrder = [
|
||
{ frame: 1, ids: [0] },
|
||
{ frame: 5, ids: [1] },
|
||
{ frame: 8, ids: [2] },
|
||
{ frame: 11, ids: [3] },
|
||
{ frame: 14, ids: [4] },
|
||
{ frame: 17, ids: [5] },
|
||
];
|
||
|
||
let fps = 30;
|
||
|
||
@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)
|
||
// maskSpriteFrame: SpriteFrame = null;
|
||
|
||
rollerMsg: any[] = [
|
||
// { row: 1, col: 4, isHorizontal: true },
|
||
{ id: 0, row: 5, col: 1, isHorizontal: false },
|
||
{ id: 1, row: 5, col: 1, isHorizontal: false },
|
||
{ id: 2, row: 5, col: 1, isHorizontal: false },
|
||
{ id: 3, row: 5, col: 1, isHorizontal: false },
|
||
{ id: 4, row: 5, col: 1, isHorizontal: false },
|
||
{ id: 5, row: 5, col: 1, isHorizontal: false },
|
||
];
|
||
|
||
upLayer: any = null;
|
||
// 滚轮数组
|
||
allRollers: Roller[] = [];
|
||
// 是否快速旋转
|
||
_isFastSpin: boolean = false;
|
||
// 是否手动停止
|
||
_isManualStop: boolean = false;
|
||
// spinData
|
||
_parsedData: IParsedGameData = null;
|
||
/** 分割过的数据,一位数组改为二维数组, 每个元素代表一个滚轮 */
|
||
_resultStopData: number[][] = [];
|
||
// 是否是免费游戏
|
||
_isFreeSpin: boolean = false;
|
||
|
||
private _createdRollerCount: number = 0;
|
||
private _fallenRollerCount: number = 0;
|
||
private _pendingDeleteCount: number = 0;
|
||
|
||
_scatterStartRollerId: number = 0;
|
||
|
||
@property
|
||
_format = false;
|
||
@property({ tooltip: '格式化' })
|
||
get format(): boolean {
|
||
return this._format;
|
||
}
|
||
set format(a: boolean) {
|
||
this._format = a;
|
||
let maskNode = this.node.getChildByName('mask');
|
||
maskNode.removeAllChildren();
|
||
|
||
|
||
// new Node('mask');
|
||
// maskNode.addComponent(UITransform);
|
||
// maskNode.addComponent(Mask);
|
||
// maskNode.getComponent(Mask).type = 3;
|
||
// maskNode.getComponent(Mask).alphaThreshold = 0.3;
|
||
// maskNode.getComponent(Sprite).spriteFrame = this.maskSpriteFrame;
|
||
// maskNode.getComponent(Mask).inverted = false;
|
||
|
||
maskNode.setPosition(0, 0);
|
||
// this.node.addChild(maskNode);
|
||
|
||
for (let i = 0; i <= 5; i++) {
|
||
let rollerId = i;
|
||
let rollerRow = this.rollerMsg[i].row;
|
||
let roller = Roller.create(rollerId, rollerRow, this.iconWidth, this.iconHeight, this.iconFactory);
|
||
maskNode.addChild(roller.node);
|
||
let rollerPosition = this.getRollerPosition(rollerId);
|
||
roller.node.setPosition(rollerPosition);
|
||
// 只在第一次设置format
|
||
if (!roller.format) {
|
||
roller.format = true;
|
||
}
|
||
this.allRollers.push(roller);
|
||
}
|
||
|
||
// 按照rollerMsg.id从小到大对allRollers排序
|
||
this.allRollers.sort((a, b) => {
|
||
return a.rollerId - b.rollerId;
|
||
});
|
||
}
|
||
|
||
setUpLayer(upLayer: any) {
|
||
this.upLayer = upLayer;
|
||
}
|
||
|
||
// 获取滚轮的坐标
|
||
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);
|
||
}
|
||
|
||
protected onLoad(): void {
|
||
// this.format = true;
|
||
this.registerEvent();
|
||
this._createdRollerCount = 0;
|
||
this._fallenRollerCount = 0;
|
||
}
|
||
|
||
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() {
|
||
// 掉落模式无需调速,保留接口兼容外部调用
|
||
}
|
||
|
||
|
||
// 随机创建ICON回调
|
||
onRollerRIconCreate() {
|
||
return;
|
||
}
|
||
|
||
// 最后一页创建的回调
|
||
onRollerLastPageCreate(rollerId: number) {
|
||
|
||
}
|
||
|
||
|
||
onRollerBounce(rollerId: number) {
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ROLLER_BOUNCE, rollerId);
|
||
|
||
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('Roller_Down');
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
onRollerSlowDown() {
|
||
return;
|
||
}
|
||
|
||
private stopChainRoller(id: number) {
|
||
let roller = this.allRollers[id];
|
||
let stopData = this._resultStopData[id];
|
||
AudioManager.instance.playSFX('Ready_Hand_SFX');
|
||
roller.setNoBounce(true);
|
||
roller.stopScroll(stopData, 0);
|
||
}
|
||
|
||
hasEmitScatterTween: boolean = false;
|
||
onRollerStop(rollerId: number) {
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ONE_ROLLER_STOP, rollerId);
|
||
if (this._parsedData.HasScatterExpect) {
|
||
this.upLayer.playScatterSpine(true);
|
||
let nextRollerId = rollerId + 1 >= this.allRollers.length ? null : rollerId + 1;
|
||
if (nextRollerId >= this._scatterStartRollerId && nextRollerId !== null) {
|
||
if (!this.hasEmitScatterTween) {
|
||
this.node.emit(ROLLER_COMBINE_EVENT.START_GAME_SCALE_TWEEN);
|
||
this.hasEmitScatterTween = true;
|
||
}
|
||
this.stopChainRoller(nextRollerId);
|
||
this.upLayer.playReadyHandAni(nextRollerId);
|
||
}
|
||
}
|
||
|
||
let allRollerStop = true;
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let roller = this.allRollers[i];
|
||
if (roller.isScroll()) allRollerStop = false;
|
||
}
|
||
|
||
if (allRollerStop) {
|
||
if (this.hasEmitScatterTween) {
|
||
this.node.emit(ROLLER_COMBINE_EVENT.START_GAME_END_SCALE_TWEEN);
|
||
}
|
||
this.node.emit(ROLLER_COMBINE_EVENT.ALL_ROLLER_STOP);
|
||
this.hasEmitScatterTween = false;
|
||
|
||
if (this._isFastSpin || this._isManualStop) {
|
||
let hasScatter = this._parsedData.PanData.indexOf(1) !== -1;
|
||
let hasWild = this._parsedData.PanData.indexOf(0) !== -1;
|
||
|
||
if (hasWild && !hasScatter) {
|
||
AudioManager.instance.playSFX('Appear_Wild_Sound');
|
||
}
|
||
|
||
if (hasScatter) {
|
||
AudioManager.instance.playSFX('Appear_Scatter_Sound');
|
||
}
|
||
|
||
if (!hasWild && !hasScatter) {
|
||
AudioManager.instance.playSFX('Roller_Down');
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
// 滚轮匀速时候的回调
|
||
onRollerUniform() {
|
||
return;
|
||
}
|
||
|
||
|
||
// 滚轮icon删除的回调
|
||
onRollerIconDeleted(rollerId: number) {
|
||
if (this._pendingDeleteCount > 0) {
|
||
this._pendingDeleteCount--;
|
||
if (this._pendingDeleteCount === 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: IParsedGameData) {
|
||
this._parsedData = data;
|
||
|
||
let panData = data.PanData;
|
||
|
||
// 分割数据
|
||
this._resultStopData = this.splitArray(panData, [5, 5, 5, 5, 5, 5]);
|
||
// 处理n*1符号
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let roller = this.allRollers[i];
|
||
roller.initRollerWithIcon(i, this._resultStopData[i]);
|
||
}
|
||
}
|
||
|
||
|
||
// 滚轮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._isManualStop = false;
|
||
this.unscheduleAllCallbacks();
|
||
if (this._isFastSpin) {
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
let roller = this.allRollers[i];
|
||
if (roller) {
|
||
if (this._isManualStop) return;
|
||
roller.startScroll();
|
||
}
|
||
}
|
||
} else {
|
||
frameOrder.forEach(({ frame, ids }) => {
|
||
let delay = Math.max(0, (frame - 1) / fps);
|
||
ids.forEach(id => {
|
||
let roller = this.allRollers[id];
|
||
if (roller) {
|
||
this.scheduleOnce(() => {
|
||
if (this._isManualStop) return;
|
||
roller.startScroll();
|
||
}, delay);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
}
|
||
|
||
// 滚轮停止滚动
|
||
stopScroll(data: IParsedGameData) {
|
||
this._parsedData = data;
|
||
let panData = data.PanData;
|
||
|
||
this._resultStopData = this.splitArray(panData, [5, 5, 5, 5, 5, 5]);
|
||
|
||
// 记录 scatter 起始列(onRollerStop 链式逻辑仍需此值)
|
||
if (data.HasScatterExpect) {
|
||
this._scatterStartRollerId = this.getScatterStartRollerId();
|
||
}
|
||
|
||
if (this._isFastSpin) {
|
||
// 快速模式:所有列同时掉落
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
const roller = this.allRollers[i];
|
||
roller.stopScroll(this._resultStopData[i], 0);
|
||
}
|
||
} else {
|
||
// 普通模式:从左到右依次掉落,每列间隔 0.12s
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
const colDelay = i * 0.12;
|
||
const roller = this.allRollers[i];
|
||
const stopData = this._resultStopData[i];
|
||
this.scheduleOnce(() => {
|
||
if (this._isManualStop) return;
|
||
roller.stopScroll(stopData, 0);
|
||
}, colDelay);
|
||
}
|
||
}
|
||
}
|
||
|
||
canManualStop(): boolean {
|
||
return this.allRollers.every(roller => roller.getState() < ROLLER_STATE.LAST_PAGE_CREATE || roller.getState() == ROLLER_STATE.STOP);
|
||
}
|
||
|
||
|
||
// 对服务器下发的数据进行操作
|
||
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;
|
||
}
|
||
|
||
getAllRemoveIconsPos(removeData: number[]): number[][] {
|
||
let group: number[][] = [];
|
||
for (let i = 0; i < this.allRollers.length; i++) {
|
||
group[i] = [];
|
||
}
|
||
for (let i = 0; i < removeData.length; i++) {
|
||
let pos = removeData[i];
|
||
let lx = this.getLx(pos);
|
||
group[lx].push(pos);
|
||
}
|
||
return group;
|
||
}
|
||
|
||
deleteIconNode(pos: number) {
|
||
let lx = this.getLx(pos);
|
||
let ly = this.getLy(pos);
|
||
this.allRollers[lx].deleteIconNode([ly]);
|
||
}
|
||
|
||
createNewIconTop(addtional: any) {
|
||
this.allRollers.forEach(roller => {
|
||
roller.createNewIconTop(addtional[roller.rollerId]);
|
||
})
|
||
}
|
||
|
||
iconFallDown() {
|
||
this.allRollers.forEach(roller => {
|
||
roller.iconFallDown(this._parsedData.HasScatterExpect);
|
||
})
|
||
}
|
||
|
||
// 获取icon坐标
|
||
getIconWorldPosition(pos: number) {
|
||
let lx = this.getLx(pos);
|
||
let roller = this.allRollers[lx];
|
||
return roller.getIconWorldPosition(this.getLy(pos));
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
// 开始一批删除(声明本轮将有多少次 deleteIconNode 调用)
|
||
beginDeleteBatch(expected: number) {
|
||
this._pendingDeleteCount = expected;
|
||
}
|
||
|
||
getScatterStartRollerId() {
|
||
let startId = 0;
|
||
let twoScattterId = this._parsedData.ScatterPos[1];
|
||
startId = this.getLx(twoScattterId) + 1;
|
||
return startId;
|
||
}
|
||
|
||
hasScatterOnRoller(rollerId: number): boolean {
|
||
let seg = this._resultStopData[rollerId] || [];
|
||
return seg.indexOf(1) !== -1;
|
||
}
|
||
|
||
}
|
||
|