264 lines
12 KiB
TypeScript
264 lines
12 KiB
TypeScript
import PopupAnimator, { ActivityPopupAnimationType as PopupType } from "./ActivityPopupAnimator";
|
|
import NumberToImage from "./NumberToImage";
|
|
|
|
|
|
const { ccclass, property, requireComponent } = cc._decorator;
|
|
|
|
@ccclass
|
|
export default class Reward extends cc.Component {
|
|
onDisable() { PopupAnimator.cancel(this.popupBody); }
|
|
|
|
|
|
|
|
@property(cc.Prefab)
|
|
rewardNode: cc.Prefab = null;
|
|
private popupBody: cc.Node = null;
|
|
actionOver: boolean;
|
|
speedTime: number = 50;
|
|
private originalTimeScale: number = 1; // 记录原始时间缩放比例
|
|
callBack: Function;
|
|
|
|
|
|
onLoad() {
|
|
// 为节点添加点击事件监听器
|
|
this.speedTime = 50;
|
|
this.popupBody = new cc.Node("RewardContent");
|
|
this.node.children.slice().filter(node => node.name !== "New Sprite(Splash)").forEach(node => node.parent = this.popupBody);
|
|
this.popupBody.parent = this.node;
|
|
}
|
|
|
|
start() {
|
|
this.node.on(cc.Node.EventType.TOUCH_END, this.onNodeClick, this);
|
|
}
|
|
|
|
/**
|
|
* 节点点击事件处理函数
|
|
*/
|
|
onNodeClick() {
|
|
if (this.actionOver == true) {
|
|
cc.fx.AudioManager._instance.playEffect("anniu_little", null);
|
|
this.actionOver = false;
|
|
PopupAnimator.close(this.popupBody, PopupType.Bounce, () => {
|
|
if (this.callBack != null) {
|
|
this.callBack();
|
|
}
|
|
this.node.destroy();
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
speedUpGame() {
|
|
this.speedTime = 0;
|
|
this.originalTimeScale = cc.director.getScheduler().getTimeScale(); // 记录原始时间缩放比例
|
|
cc.director.getScheduler().setTimeScale(10); // 加速到 2 倍速
|
|
// 在奖励节点创建完成后恢复正常速度
|
|
const checkCompletion = () => {
|
|
if (this.actionOver) {
|
|
cc.director.getScheduler().setTimeScale(this.originalTimeScale); // 恢复原始时间缩放比例
|
|
} else {
|
|
setTimeout(checkCompletion, 50); // 每隔 100 毫秒检查一次
|
|
}
|
|
};
|
|
checkCompletion();
|
|
}
|
|
|
|
/**
|
|
* 初始化
|
|
* @param data 数据
|
|
*/
|
|
init(data, callBack?: Function, durationUnits = false) {
|
|
this.actionOver = false;
|
|
PopupAnimator.open(this.popupBody, PopupType.Bounce, () => this.presentRewards(data, callBack, durationUnits));
|
|
}
|
|
private presentRewards(data, callBack?: Function, durationUnits = false) {
|
|
this.actionOver = false;
|
|
if (callBack) this.callBack = callBack;
|
|
else this.callBack = null;
|
|
if (!data) {
|
|
this.actionOver = true;
|
|
this.node.destroy();
|
|
return;
|
|
}
|
|
const num = Math.min(data.length, 9); // 确保 num 不超过 9
|
|
const spacing = 10; // 间隔,默认 10
|
|
if (this.rewardNode && num > 0) {
|
|
// 获取单个 rewardNode 的宽度和高度
|
|
const tempNode = cc.instantiate(this.rewardNode);
|
|
const nodeWidth = tempNode.width;
|
|
const nodeHeight = tempNode.height;
|
|
tempNode.destroy();
|
|
|
|
// 定义每一行的节点数量
|
|
const rowCounts: number[] = [];
|
|
if (num <= 3) {
|
|
rowCounts.push(num);
|
|
} else if (num === 4) {
|
|
rowCounts.push(2, 2);
|
|
} else if (num === 5) {
|
|
rowCounts.push(3, 2);
|
|
} else if (num === 6) {
|
|
rowCounts.push(3, 3);
|
|
} else if (num === 7) {
|
|
rowCounts.push(3, 3, 1);
|
|
} else if (num === 8) {
|
|
rowCounts.push(3, 3, 2);
|
|
} else if (num === 9) {
|
|
rowCounts.push(3, 3, 3);
|
|
}
|
|
|
|
// 计算总高度
|
|
const rows = rowCounts.length;
|
|
const totalHeight = (nodeHeight * rows) + (spacing * (rows - 1));
|
|
|
|
// 计算起始位置的 y 坐标
|
|
const startY = totalHeight / 2 - nodeHeight / 2;
|
|
|
|
let index = 0;
|
|
const createNodeWithDelay = (row: number, col: number) => {
|
|
setTimeout(() => {
|
|
const rewardNode = cc.instantiate(this.rewardNode);
|
|
if (this.node) rewardNode.parent = this.popupBody;
|
|
// 计算当前行的总宽度
|
|
// let startX = 0;
|
|
// if (row === 0) {
|
|
// const totalWidth = (nodeWidth * rowCounts[row]) + (spacing * (rowCounts[row] - 1));
|
|
// startX = -totalWidth / 2 + nodeWidth / 2;
|
|
// } else {
|
|
// startX = -((nodeWidth * 3) + (spacing * 2)) / 2 + nodeWidth / 2;
|
|
// }
|
|
// 修复:统一使用当前行的节点数量计算起始位置
|
|
const totalWidth = (nodeWidth * rowCounts[row]) + (spacing * (rowCounts[row] - 1));
|
|
const startX = -totalWidth / 2 + nodeWidth / 2;
|
|
|
|
// 计算每个节点的 x 和 y 位置
|
|
const xPos = startX + (col * (nodeWidth + spacing));
|
|
const yPos = startY - (row * (nodeHeight + spacing));
|
|
rewardNode.setPosition(xPos, yPos);
|
|
// 查找 rewardNode 的子节点 icon
|
|
const iconNode = rewardNode.getChildByName('icon');
|
|
const rewardData = data[index];
|
|
const rewardIcon = this.getOrCreateRewardIcon(iconNode, rewardData);
|
|
if (rewardIcon) {
|
|
rewardIcon.active = true;
|
|
}
|
|
let label = rewardNode.getChildByName('label')
|
|
if (rewardData.type == 'coin')
|
|
NumberToImage.numberToImageNodesNumber(rewardData.count, 45, 5, "big_", label, true);
|
|
else if (rewardData.type == 'infinite_health') {
|
|
this.renderDuration(label, rewardData.count);
|
|
rewardNode.getChildByName('xnode').active = false;
|
|
} else {
|
|
NumberToImage.numberToImageNodesNumber(rewardData.count, 50, 15, "big_", label, true);
|
|
const xnode = rewardNode.getChildByName('xnode');
|
|
xnode.opacity = 0;
|
|
label.x += 20;
|
|
if (rewardData.count >= 10 && rewardData.type != 'pass_key') {
|
|
const firstDigit = label.children[0];
|
|
const digitLeft = label.x + (firstDigit.x - firstDigit.width * firstDigit.anchorX) * label.scaleX;
|
|
const plusRight = xnode.x + xnode.width * (1 - xnode.anchorX) * xnode.scaleX;
|
|
label.x += Math.max(0, plusRight + 6 - digitLeft);
|
|
}
|
|
}
|
|
if (data[index].type == 'pass_key') {
|
|
const firstDigit = label.children[0];
|
|
const xnode = rewardNode.getChildByName('xnode');
|
|
xnode.x = label.x + (firstDigit.x - firstDigit.width * firstDigit.anchorX) * label.scaleX
|
|
- 6 - xnode.width * (1 - xnode.anchorX) * xnode.scaleX;
|
|
}
|
|
this.alignAmount(rewardNode, rewardData.type !== 'coin' && rewardData.type !== 'infinite_health');
|
|
rewardNode.getChildByName('label').active = false;
|
|
if (iconNode) {
|
|
cc.tween(iconNode)
|
|
.to(0.2, { scale: 1.1 })
|
|
.to(0.1, { scale: 0.9 })
|
|
.to(0.05, { scale: 1 })
|
|
.delay(0.1)
|
|
.call(() => {
|
|
rewardNode.getChildByName('label').active = true;
|
|
if (rewardNode.getChildByName('xnode').opacity == 1) {
|
|
rewardNode.getChildByName('xnode').opacity = 0;
|
|
} else if (rewardNode.getChildByName('xnode').opacity != 255) {
|
|
rewardNode.getChildByName('xnode').active = true;
|
|
rewardNode.getChildByName('xnode').opacity = 255;
|
|
}
|
|
})
|
|
.start();
|
|
}
|
|
|
|
index++;
|
|
if (index == data.length) {
|
|
setTimeout(() => {
|
|
this.actionOver = true;
|
|
}, 500);
|
|
|
|
}
|
|
if (index < data.length) {
|
|
const nextCol = col + 1;
|
|
if (nextCol < rowCounts[row]) {
|
|
createNodeWithDelay(row, nextCol);
|
|
} else {
|
|
createNodeWithDelay(row + 1, 0);
|
|
}
|
|
}
|
|
}, index * this.speedTime); // 每个节点间隔 0.1 秒
|
|
};
|
|
createNodeWithDelay(0, 0);
|
|
}
|
|
}
|
|
|
|
private renderDuration(label: cc.Node, seconds: number): void {
|
|
const hours = seconds >= 3600;
|
|
const value = Math.ceil(seconds / (hours ? 3600 : 60));
|
|
const glyphs = String(value).split("").map(digit => "big_" + digit);
|
|
glyphs.push(hours ? "big_xiaoshi" : "big_fenzhong");
|
|
let x = 0;
|
|
glyphs.forEach(name => {
|
|
const node = new cc.Node(name); label.addChild(node);
|
|
const sprite = node.addComponent(cc.Sprite); sprite.spriteFrame = NumberToImage.publicFont._spriteFrames[name];
|
|
node.x = x + node.width * node.anchorX; x += node.width + 4;
|
|
});
|
|
label.setScale(Math.min(1, 300 / Math.max(1, x - 4)));
|
|
}
|
|
private alignAmount(reward: cc.Node, withPlus: boolean): void {
|
|
const label = reward.getChildByName("label"), plus = reward.getChildByName("xnode");
|
|
const left = Math.min(...label.children.map(n => n.x - n.width * n.anchorX * n.scaleX));
|
|
const right = Math.max(...label.children.map(n => n.x + n.width * (1 - n.anchorX) * n.scaleX));
|
|
const prefix = withPlus ? plus.width * plus.scaleX + 8 : 0;
|
|
const width = (right - left) * label.scaleX + prefix;
|
|
label.x = -width / 2 + prefix - left * label.scaleX;
|
|
if (withPlus) plus.x = -width / 2 + plus.width * plus.anchorX * plus.scaleX;
|
|
}
|
|
private getOrCreateRewardIcon(iconNode: cc.Node, rewardData: any): cc.Node {
|
|
if (!iconNode || !rewardData) return null;
|
|
const spriteFrame = rewardData.spriteFrame as cc.SpriteFrame;
|
|
let rewardIcon = iconNode.getChildByName(rewardData.type);
|
|
if (rewardIcon) {
|
|
const sprite = rewardIcon.getComponent(cc.Sprite);
|
|
if (spriteFrame && sprite) {
|
|
const targetWidth = rewardIcon.width * Math.abs(rewardIcon.scaleX);
|
|
const targetHeight = rewardIcon.height * Math.abs(rewardIcon.scaleY);
|
|
sprite.sizeMode = cc.Sprite.SizeMode.TRIMMED; sprite.spriteFrame = spriteFrame;
|
|
const rect = spriteFrame.getRect();
|
|
rewardIcon.setScale(Math.min(targetWidth / rect.width, targetHeight / rect.height));
|
|
}
|
|
return rewardIcon;
|
|
}
|
|
|
|
if (!spriteFrame) {
|
|
cc.warn("Reward icon is missing:", rewardData.type);
|
|
return null;
|
|
}
|
|
|
|
rewardIcon = new cc.Node(rewardData.type);
|
|
rewardIcon.parent = iconNode;
|
|
rewardIcon.active = false;
|
|
rewardIcon.color = cc.Color.WHITE;
|
|
const sprite = rewardIcon.addComponent(cc.Sprite);
|
|
sprite.sizeMode = cc.Sprite.SizeMode.TRIMMED;
|
|
sprite.spriteFrame = spriteFrame;
|
|
rewardIcon.setScale(rewardData.type === "cat_skin" ? 1.1 : 1);
|
|
return rewardIcon;
|
|
}
|
|
}
|