rp_11009/assets/Game/Scripts/SlotMsg.ts
2026-04-24 15:25:27 +08:00

401 lines
15 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, Component, director, instantiate, Label, Node, sp, Sprite, Tween, tween, UIOpacity, UITransform, v3, Vec3 } from 'cc';
import { I18nManager } from '../../Main/Scripts/managers/I18nManager';
import { getLanguage, gold2cash } from '../../Main/Scripts/main/comm';
import { UIManager } from '../../Main/Scripts/managers/UIManager';
import { AudioManager } from '../../Main/Scripts/managers/AudioManager';
import { Icon } from './Icon';
import { SlotGame } from './SlotGame';
import { SlotBar } from './SlotBar';
import { NET_MODE } from 'cc/env';
const { ccclass, property } = _decorator;
@ccclass('SlotMsg')
export class SlotMsg extends Component {
// 展示文字信息
// @property(sp.Skeleton)
// smallMsgSpine: sp.Skeleton = null;
// // 展示普通赢分信息
// @property(sp.Skeleton)
// middleMsgSpine: sp.Skeleton = null;
// // 展示总赢分信息
// @property(sp.Skeleton)
// largeMsgSpine: sp.Skeleton = null;
// 展示文字信息
@property(Node)
smallMsgNode: Node = null;
// 展示普通赢分信息
@property(Node)
middleMsgNode: Node = null;
// 展示总赢分信息
@property(Node)
largeMsgNode: Node = null;
@property(Sprite)
i18nSpriteMsg: Sprite = null;
@property(Node)
grayMsgNode: Node = null;
protected onLoad(): void {
this.grayMsgNode.active = false;
}
startPos: Vec3 = new Vec3(-259.5, 0, 0);
maxWidth: number = 540;
currTotaleMulti: number = 0
currRoundWinMsg: string = ''
showLabelMsgForTween() {
Tween.stopAllByTarget(this.i18nSpriteMsg.node);
this.smallMsgNode.active = true;
// this.smallMsgSpine.setAnimation(0, '2', false);
// this.smallMsgSpine.setCompleteListener(() => {
// this.smallMsgSpine.setAnimation(0, '1', true);
// this.smallMsgSpine.setCompleteListener(null);
// });
this.middleMsgNode.active = false;
this.largeMsgNode.active = false;
let add = 0;
let isFirst = true;
let startPos = new Vec3(0, 0, 0);
let endPos = new Vec3(0, 0, 0);
let speed = 1080 / 8;
let delayTime = 0;
let moveTime = 0;
// 通过数字获取name
let spriteName = ['Normal8', 'Normal9', 'Normal10', 'Normal11'];
let setSpriteFrame = () => {
startPos.x = this.startPos.x;
let spriteFrame = I18nManager.instance.getSpriteFrame(spriteName[add]);
this.i18nSpriteMsg.spriteFrame = spriteFrame;
let distane = spriteFrame.rect.width + 109;
let X = -430 - distane;
endPos.x = X;
delayTime = 1;
moveTime = distane / speed;
if (spriteFrame.rect.width < this.maxWidth) {
startPos.x = -spriteFrame.rect.width / 2;
endPos.x = -spriteFrame.rect.width / 2;
delayTime = 2;
moveTime = 1;
}
//带图的文本稍微上移几个像素
if (add == 0) {
startPos.y = 5;
endPos.y = 5;
}
}
setSpriteFrame();
let tweenFun = () => {
tween(this.i18nSpriteMsg.node)
.set({ position: startPos })
.delay(delayTime)
.to(moveTime, { position: endPos })
.call(() => {
isFirst = false;
add = add + 1 >= spriteName.length ? 0 : add + 1;
setSpriteFrame();
tweenFun();
})
.start();
}
tweenFun();
}
setTotaleMulti(mul: number) {
this.currTotaleMulti = mul
}
showTotalWinAnimaiton(startScore: number, endScore: number, cb) {
this.smallMsgNode.active = false;
this.middleMsgNode.active = false;
this.largeMsgNode.active = true;
// this.largeMsgSpine.setAnimation(0, '2', false);
// this.largeMsgSpine.setCompleteListener(() => {
// this.largeMsgSpine.setAnimation(0, '1', true);
// this.largeMsgSpine.setCompleteListener(null);
// });
let msg = this.largeMsgNode.getChildByName('msg');
let winCount = msg.getChildByName('winCount');
winCount.getComponent(Label).string = gold2cash(startScore);
AudioManager.instance.playSFX('Gold_Up');
UIManager.instance.tweenScorelinear(startScore, endScore, 2)
.onUpdate((v: number) => {
winCount.getComponent(Label).string = gold2cash(v);
})
.onComplete(() => {
winCount.getComponent(Label).string = gold2cash(endScore);
AudioManager.instance.stopSFX('Gold_Up');
tween(winCount)
.to(0.2, { scale: v3(1.1, 1.1, 1.1) })
.to(0.2, { scale: v3(1, 1, 1) })
.start()
this.scheduleOnce(() => {
director.getScene()?.getComponentInChildren(SlotGame).setFreeTotalMulti(1)
if (cb) cb();
}, 1)
})
.start();
}
showRoundWinMsg(endScore: number, currMulti: number = 0) {
if (endScore === 0) return;
this.smallMsgNode.active = false;
this.middleMsgNode.active = true;
this.largeMsgNode.active = false;
// this.middleMsgSpine.setAnimation(0, '2', false);
// this.middleMsgSpine.setCompleteListener(() => {
// this.middleMsgSpine.setAnimation(0, '1', true);
// this.middleMsgSpine.setCompleteListener(null);
// });
let msg = this.middleMsgNode.getChildByName('msg');
let winCount = msg.getChildByName('winCount');
if (currMulti == 0) {
winCount.getComponent(Label).string = gold2cash(endScore);
this.currRoundWinMsg = winCount.getComponent(Label).string
AudioManager.instance.playSFX('Slot_Msg_Normal_Win');
} else {
winCount.getComponent(Label).string = this.currRoundWinMsg + ' x ' + currMulti
}
}
showTotalWinMsg(endScore: number) {
if (endScore === 0) return;
this.smallMsgNode.active = false;
this.middleMsgNode.active = false;
this.largeMsgNode.active = true;
this.largeMsgNode.getChildByName('pmd3_spine').getComponent(sp.Skeleton).setAnimation(0, 'animation', false)
// this.largeMsgNode.setAnimation(0, '2', false);
// this.largeMsgNode.setCompleteListener(() => {
// this.largeMsgNode.setAnimation(0, '1', true);
// this.largeMsgNode.setCompleteListener(null);
// });
let msg = this.largeMsgNode.getChildByName('msg');
let winCount = msg.getChildByName('winCount');
winCount.getComponent(Label).string = gold2cash(endScore);
AudioManager.instance.playSFX('Slot_Msg_Total_Win');
}
hideWinSpine() {
this.smallMsgNode.active = true;
this.middleMsgNode.active = false;
this.largeMsgNode.active = false;
}
/**
* 展示多倍炸弹倍率动画
* @param spinData 当前spin数据
* @param callBack 动画结束回调
*
* 此函数内部的run是async从上到下用await/同步promise的方式严格保证顺序和队列效果一步一步地执行动画和界面更新。
* 下面会对run里每一行的逻辑做中文注释逐步解析
*/
showMultiWinAnimation(spinData, callBack: () => void) {
// 获取倍率描述,多模式下兼容字段名
// let mulDesc = spinData?.WinInfo?.MulDesc || spinData?.WinInfo?.muldesc;
let multiedScore = spinData?.RoundInfo.AllScoreMul; // 最终得到的分数
// let mulValue = mulDesc?.Mul; // 总倍率
let slotGame = director.getScene()?.getComponentInChildren(SlotGame);
let multiInfos = slotGame?.getCurrentMultiSettleInfos?.() || []; // 炸弹节点信息
if (!multiInfos.length) {
console.error('SlotGame not find multiInfos msg');
return;
}
// 提示分数的spine节点和最终显示位置
let msg = this.largeMsgNode.getChildByName('msg');
let winCount = msg.getChildByName('winCount');
let uiTf = this.node.getComponent(UITransform);
let centerLocalPos = v3(50, -500, 0); // 信息栏坐标(本地)
let targetLocalPos = uiTf.convertToNodeSpaceAR(winCount.getWorldPosition()); // 目标分数字显示位置
// 设置一个节点的多倍文本
let setMulText = (node: Node, mul: number) => {
// let labNode = node.getChildByName('lab');
if (!node) return;
let lab = node.getComponent(Label);
if (!lab) return;
lab.string = `x${mul}`;
};
// 节点tween到目标位置的promise动画完resolve
let tweenToAsync = (node: Node, toPos: Vec3, duration: number) => {
return new Promise<void>((resolve) => {
tween(node)
.to(duration, { position: toPos }, { easing: 'quadIn' })
.call(() => resolve())
.start();
});
};
// 放大动画脉冲的promise动画完resolve
let pulseAsync = (node: Node) => {
return new Promise<void>((resolve) => {
tween(node)
.to(0.2, { scale: v3(1.5, 1.5, 1.5) }, { easing: 'quadOut' })
.call(() => resolve())
.start();
});
};
// 延迟promise用于动画间隔
let delayAsync = (seconds: number) => {
return new Promise<void>((resolve) => {
this.scheduleOnce(() => resolve(), seconds);
});
};
// === 跑队列 ===
// 这部分run内部就是完整的队列模式所有动画/节点操作都是await同步执行
let run = async () => {
// 1. 确认总倍率节点,并首先飞入信息栏
let centerMulNode = slotGame.node.getChildByPath('FreeGameBg1/totalMulti');
// this.node.addChild(centerMulNode);
// centerMulNode.setPosition(centerLocalPos);
// centerMulNode.setScale(1, 1, 1);
// centerMulNode.active = true;
// setMulText(centerMulNode, 0);
//总倍率有值时要第一个参与倍率计算
if (this.currTotaleMulti > 0) {
let flyNode = instantiate(centerMulNode)
flyNode.setPosition(centerMulNode.getPosition())
this.node.addChild(flyNode)
await tweenToAsync(flyNode, centerLocalPos, 0.6);
flyNode.destroy()
this.showRoundWinMsg(1, this.currTotaleMulti)
}
let sumMul = this.currTotaleMulti;
// 2. 遍历每一个待结算的Multi多倍icon按顺序“飞入信息栏”, 逐步累加
for (let i = 0; i < multiInfos.length; i++) {
let info = multiInfos[i];
if (!info?.multiNode?.isValid) continue;
let iconComp = info.iconNode?.getComponent(Icon);
// 2.1 播放爆炸动画 加入队列,爆炸完之后再进行下一步
await new Promise<void>((resolve) => {
iconComp.playMultiSpine();
this.scheduleOnce(() => resolve(), 0.2);
});
// 2.2 当前multiNode放大脉冲动画await保证上一步结束
await pulseAsync(info.multiNode);
// 2.3 复制multiNode实际用于“飞入信息栏”
let sourceWorldPos = info.multiNode.getWorldPosition();
let sourceLocalPos = uiTf.convertToNodeSpaceAR(sourceWorldPos);
let flyNode = instantiate(info.multiNode);
info.multiNode.active = false;
this.node.addChild(flyNode);
flyNode.setPosition(sourceLocalPos);
// 2.4 飞到信息栏动画
await tweenToAsync(flyNode, centerLocalPos, 0.6);
flyNode.destroy();
// 2.5 累加倍率,信息栏文本根据每个倍率到达而改变
sumMul += info.mul;
this.showRoundWinMsg(1, sumMul)
// centerMulNode.active = true;
// setMulText(centerMulNode, sumMul);
// 2.6 中心放大动画(脉冲)
// await pulseAsync(centerMulNode);
// 2.7 小延迟准备下一个icon
await delayAsync(0.1);
}
// 4. 中心multi飞到最终目标位置
// await tweenToAsync(centerMulNode, targetLocalPos, 0.2);
// centerMulNode.destroy();
// 5. 展示最终总分获胜弹窗和新的总倍率
let showCurrMulti = () => {
return new Promise<void>((resolve) => {
this.setTotaleMulti(sumMul)
let totalMultiBgSpin = slotGame.node.getChildByPath('FreeGameBg1/qiu')
tween(totalMultiBgSpin)
.to(0.2, { scale: v3(1.2, 1.2, 1) })
.to(0.2, { scale: v3(1, 1, 1) })
.call(() => {
slotGame.setFreeTotalMulti(this.currTotaleMulti)
resolve()
})
.start()
});
}
if (spinData.IsFree) {
await showCurrMulti()
}
this.showTotalWinMsg(multiedScore);
// 直接调用回调(如果业务上需要动画结束后再回调可考虑调整)
if (callBack) callBack();
let slotBar = director.getScene()?.getComponentInChildren(SlotBar);
if (slotBar) slotBar.updateWinMsg(spinData.AllScore, false);
};
// 直接触发队列执行不等待run()结束动画流程内部用await限定顺序外部流程不依赖
run();
}
showTipSmall(tips: string, Type: string = "") {
Tween.stopAllByTarget(this.grayMsgNode);
let bgWidth, bgHeigt = 0;
if (Type != "") {
this.grayMsgNode.getChildByName('layout').getChildByName('fast').active = true;
bgWidth = 142;
this.grayMsgNode.getChildByPath('layout/fast/toast_Turboon').active = Type == "onFast";
this.grayMsgNode.getChildByPath('layout/fast/toast_Turbooff').active = Type == "offFast";
} else {
this.grayMsgNode.getChildByName('layout').getChildByName('fast').active = false;
bgWidth = 100;
}
let lab = this.grayMsgNode.getChildByPath('layout/lab').getComponent(Label);
lab.string = tips;
lab.updateRenderData(true);
let tipWidth = this.grayMsgNode.getChildByPath('layout/lab').getComponent(UITransform).width;
let frame = this.grayMsgNode.getChildByName('sp');
frame.getComponent(UITransform).setContentSize(tipWidth + bgWidth, 132);
this.grayMsgNode.setScale(0, 0, 0);
this.grayMsgNode.active = true;
tween(this.grayMsgNode)
.set({ scale: v3(0, 0, 0) })
.to(0.1, { scale: v3(1, 1, 1) })
.delay(1)
.call(() => {
this.grayMsgNode.active = false;
})
.start();
}
}