Files
MatchMaster/assets/action_bundle/script/DailyQuests.ts
T

1172 lines
56 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.
// Learn TypeScript:
// - https://docs.cocos.com/creator/2.4/manual/en/scripting/typescript.html
// Learn Attribute:
// - https://docs.cocos.com/creator/2.4/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
// - https://docs.cocos.com/creator/2.4/manual/en/scripting/life-cycle-callbacks.html
import JiaZai from "../../Script/JiaZai";
import LoadingCatAnimation from "../../Script/LoadingCatAnimation";
import { MiniGameSdk } from "../../Script/Sdk/MiniGameSdk";
import DailyTaskProgressTracker from "../../Script/module/Task/DailyTaskProgressTracker";
// import JiaZai from "./JiaZai";
// import Utils from "./module/Pay/Utils";
// import { MiniGameSdk } from "./Sdk/MiniGameSdk";
const { ccclass, property } = cc._decorator;
@ccclass
export default class DailyQuests extends cc.Component {
@property(cc.Node)
content: cc.Node = null;
@property(cc.SpriteFrame)
taskNormal: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
taskClaimable: cc.SpriteFrame = null;
// coin, freeze, hammer, magic_wand, infinite_health, hourglass, rocket,
// infinite_hourglass, infinite_rocket;缺图时保留空图标占位。
@property([cc.SpriteFrame])
rewardIcons: cc.SpriteFrame[] = [];
@property([cc.SpriteFrame])
rewardDigits: cc.SpriteFrame[] = [];
@property(cc.SpriteFrame)
rewardMultiply: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
rewardMinutes: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
rewardHours: cc.SpriteFrame = null;
@property([cc.SpriteFrame])
weeklyDigits: cc.SpriteFrame[] = [];
@property([cc.SpriteFrame])
taskDigits: cc.SpriteFrame[] = [];
@property(cc.SpriteFrame)
taskSlash: cc.SpriteFrame = null;
@property(cc.SpriteFrame)
countPlus: cc.SpriteFrame = null;
public juwai = false;
btn_Touch: boolean = false;
private iosPrice: number = 0;
private iosProductId: string = "";
private iosCount: number = 1;
public home: number = 0;
public touchStart: boolean = false;
public reward: boolean = false;
sceneValue: any;
private dayEndsAt: number = 0;
private weekEndsAt: number = 0;
private cacheRefreshPending = false;
private selectedWeeklyRewardId: string = "";
private rewardEffects: { layer: cc.Node, complete: () => void }[] = [];
private pendingClaims = new Set<string>();
private animatingClaims = new Set<string>();
private displaySession = 0;
private dailyTaskOpeningPlayed = false;
private dailyTaskProgressTween: cc.Tween<{ ratio: number }> = null;
private dailyTaskStateAnimations: {
node: cc.Node, task: any, index: number, scaleX: number, scaleY: number,
widget: cc.Widget, widgetEnabled: boolean, opening: boolean, complete?: () => void
}[] = [];
private weeklyChestAnimations: { node: cc.Node, tween: cc.Tween<cc.Node>, angle: number }[] = [];
onLoad() {
this.btn_Touch = true;
// 检测微信小游戏切到后台
this.home = 0;
this.node.on(cc.Node.EventType.TOUCH_START, this.onWeeklyPreviewOutsideTouch, this, true);
}
start() {
// this.init();
}
init(data) {
// 当前这次打开跨期后保留原界面,关闭再打开才接收新周期数据。
if (this.dailyTaskOpeningPlayed && this.hasExpiredDisplayedTasks()) return;
if (this.animatingClaims.size) { this.refreshFromCache(); return; }
this.cacheRefreshPending = false;
this.clearDailyTaskProgressAnimation();
this.clearDailyTaskStateAnimations();
data = cc.fx.GameTool.getCachedDailyQuests() || data;
this.sceneValue = cc.fx.GameConfig.GM_INFO.sceneValue;
this.touchStart = true;
this.reward = false;
const finishNode = this.node.getChildByName("finishi");
if (finishNode) finishNode.active = false;
if (!DailyTaskProgressTracker.isValidTaskData(data)) return;
const animateOpening = !this.dailyTaskOpeningPlayed;
this.dailyTaskOpeningPlayed = true;
this.initCountdowns(data);
this.removeGeneratedWeeklyNodes();
while (this.content.children.length < data.dailyTasks.length) {
const template = this.content.children[this.content.children.length - 1];
if (!template) break;
this.content.addChild(cc.instantiate(template));
}
data.dailyTasks.forEach((task, index) => this.renderDailyTask(this.content.children[index], task, index));
for (let i = this.content.children.length - 1; i >= data.dailyTasks.length; i--) {
if (!this.content.children[i].name.startsWith("weekly_")) this.content.children[i].active = false;
}
this.renderWeeklyRewards(data);
if (finishNode) finishNode.active = data.dailyTasks.every(task => task.state === 2);
const layout = this.content.getComponent(cc.Layout);
if (layout) layout.updateLayout();
if (animateOpening) this.playDailyTaskProgressAnimation(data.dailyTasks, this.cacheOpenedTaskProgress(data));
}
refreshFromCache() {
if (this.hasExpiredDisplayedTasks()) return;
if (!this.touchStart || this.dailyTaskProgressTween || this.hasOpeningStateAnimation()) {
this.cacheRefreshPending = true;
return;
}
this.cacheRefreshPending = false;
const data = cc.fx.GameTool.getCachedDailyQuests() || cc.fx.GameConfig.GM_INFO.tasks;
if (!DailyTaskProgressTracker.isValidTaskData(data) || !cc.isValid(this.node) || !this.node.activeInHierarchy) return;
data.dailyTasks.forEach(task => {
if (this.animatingClaims.has(this.claimKey(task.taskId, "daily"))) return;
const row = this.content.getChildByName(task.taskId);
if (!row) return;
if (this.pendingClaims.has(this.claimKey(task.taskId, "daily"))) this.updateDailyClaimButton(row, task, task.state);
else this.renderDailyTask(row, task, this.content.children.indexOf(row));
});
this.renderWeeklyRewards(data);
const finish = this.node.getChildByName("finishi");
if (finish) finish.active = !this.animatingClaims.size && data.dailyTasks.every(task => task.state === 2);
}
private hasOpeningStateAnimation(): boolean {
return this.dailyTaskStateAnimations.some(animation => animation.opening);
}
private claimKey(id: string, scope: "daily" | "weekly"): string {
return `${scope}:${scope === "daily" ? this.dayEndsAt : this.weekEndsAt}:${id}`;
}
private initCountdowns(data: any): void {
this.unschedule(this.updateCountdowns);
this.dayEndsAt = Number(data.dayEndsAt) || 0;
this.weekEndsAt = Number(data.weekEndsAt) || 0;
// 校时在收到接口响应时完成,重复打开缓存不会把倒计时拨回去。
this.updateCountdowns();
if (this.dayEndsAt > 0 || this.weekEndsAt > 0) this.schedule(this.updateCountdowns, 1);
}
private updateCountdowns(): void {
if (this.cacheRefreshPending && this.touchStart
&& !this.dailyTaskProgressTween && !this.hasOpeningStateAnimation()) {
this.refreshFromCache();
}
const bg = this.node.getChildByName("bg");
if (!bg) return;
const dayNode = bg.getChildByName("dayCountDown");
const weekNode = bg.getChildByName("weekCountDown");
const correctedNow = cc.fx.GameTool.getDailyTaskServerTime();
const daySeconds = this.getRemainingSeconds(this.dayEndsAt, correctedNow);
const weekSeconds = this.getRemainingSeconds(this.weekEndsAt, correctedNow);
if (dayNode) this.setNodeText(dayNode, this.dayEndsAt ? `刷新倒计时 ${this.formatClock(daySeconds)}` : "");
if (weekNode) this.setNodeText(weekNode, this.weekEndsAt ? `本周剩余: ${this.formatClock(weekSeconds)}` : "");
// 倒计时归零后保持显示,重新打开时由统一缓存入口检查过期并刷新。
}
private hasExpiredDisplayedTasks(): boolean {
const now = cc.fx.GameTool.getDailyTaskServerTime();
return this.dayEndsAt > 0 && now >= this.dayEndsAt || this.weekEndsAt > 0 && now >= this.weekEndsAt;
}
private canUseDisplayedTasks(): boolean {
if (!this.hasExpiredDisplayedTasks()) return true;
MiniGameSdk.API.showToast("任务已过期,请关闭后重新打开");
return false;
}
private getRemainingSeconds(endsAt: number, now: number): number {
return endsAt > 0 ? Math.max(0, Math.ceil((endsAt - now) / 1000)) : 0;
}
private formatClock(totalSeconds: number): string {
const hours = Math.floor(totalSeconds / 3600);
if (totalSeconds >= 86400) return `${Math.floor(hours / 24)}天${hours % 24}时`;
const minutes = Math.floor(totalSeconds % 3600 / 60);
const seconds = totalSeconds % 60;
const pad = (value: number) => value < 10 ? `0${value}` : `${value}`;
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
}
private renderDailyTask(child: cc.Node, task: any, index: number, state = task.state): void {
if (!child) return;
child.active = true;
child.name = task.taskId;
child.opacity = 255;
child.setSiblingIndex(index);
child.setPosition(0, this.content.height / 2 - child.height / 2 - index * (child.height + 14));
const claimable = state === 1;
const claimed = state === 2;
child.getChildByName("background").getComponent(cc.Sprite).spriteFrame = claimable ? this.taskClaimable : this.taskNormal;
const descNode = child.getChildByName("desc");
this.setNodeText(descNode, this.getTaskTitle(task));
descNode.active = !claimable;
descNode.y = claimed ? 0 : 32;
const processNode = child.getChildByName("process");
this.renderDailyTaskProgress(child, task, Number(task.value) || 0);
const processLabel = child.getChildByName("processLabel");
processLabel.active = state === 0;
const rewardsNode = child.getChildByName("rewards");
rewardsNode.active = !claimed;
this.renderTaskRewards(rewardsNode, task);
const getNode = child.getChildByName("btnGet");
const completeNode = child.getChildByName("complete");
const lockNode = child.getChildByName("lock");
if (lockNode) lockNode.active = false;
if (processNode) processNode.active = state === 0;
if (getNode) {
getNode.active = claimable;
this.bindButtonData(getNode, task.taskId);
this.updateDailyClaimButton(child, task, state);
}
if (completeNode) completeNode.active = claimed;
}
private updateDailyClaimButton(row: cc.Node, task: any, state: number): void {
const node = row.getChildByName("btnGet");
if (!node) return;
const pending = this.pendingClaims.has(this.claimKey(task.taskId, "daily"));
node.opacity = pending ? 130 : 255;
const button = node.getComponent(cc.Button);
if (button) button.interactable = state === 1 && !pending && !this.animatingClaims.has(this.claimKey(task.taskId, "daily"))
&& !this.dailyTaskProgressTween && !this.hasOpeningStateAnimation();
}
private renderDailyTaskProgress(node: cc.Node, task: any, value: number): void {
const target = Math.max(0, Number(task.target) || 0);
value = Math.max(0, Math.min(value, target));
node.getChildByName("process").getComponent(cc.ProgressBar).progress = target > 0 ? value / target : 0;
// 在线任务仍按秒累计和填充进度条,只将显示单位转换为整分钟。
const unit = task.metric === "online_seconds" ? 60 : 1;
this.setRewardCount(node.getChildByName("processLabel"), `${Math.floor(value / unit)}/${Math.floor(target / unit)}`, this.taskDigits, 28);
}
private cacheOpenedTaskProgress(data: any): number[] {
const current = data.dailyTasks.map(task => Math.max(0, Math.min(Number(task.value) || 0, Number(task.target) || 0)));
const day = data.dayKey || data.dayEndsAt;
if (!day) return current;
// taskId 可能复用;将目标和随机参数一起匹配,避免调试重置后沿用另一任务的进度。
const keys = data.dailyTasks.map(task => JSON.stringify([task.taskId, task.metric, task.target, task.params || {}]));
try {
const uid = cc.fx.StorageMessage.getStorage("uid") || cc.fx.GameConfig.GM_INFO.uid;
if (!uid) return current;
const storageKey = `dailyTaskOpenedProgress:${uid}`;
const previous = cc.fx.StorageMessage.getStorage(storageKey);
const values = previous && previous.day === day && previous.values || {};
const starts = keys.map((key, index) => typeof values[key] === "number" && Number.isFinite(values[key])
? Math.max(0, Math.min(values[key], current[index])) : 0);
const snapshot = {};
keys.forEach((key, index) => snapshot[key] = current[index]);
// 保存打开时的最终进度;后续刷新、领取以及关闭不覆盖这份快照。
cc.fx.StorageMessage.setStorage(storageKey, { day, values: snapshot });
return starts;
} catch (error) {
// 存储不可用时正常展示任务,不影响领取。
return current;
}
}
private playDailyTaskProgressAnimation(tasks: any[], starts: number[]): void {
const rows = tasks.map((task, index) => ({
task, index, node: this.content.getChildByName(task.taskId),
start: starts[index], end: Math.max(0, Math.min(Number(task.value) || 0, Number(task.target) || 0))
}))
.filter(row => cc.isValid(row.node) && row.task.state !== 2);
if (!this.node.activeInHierarchy || !rows.some(row => row.end > row.start)) {
this.playDailyTaskStateAnimations(tasks);
return;
}
this.touchStart = false;
rows.forEach(({ node, task, index, start }) => {
this.renderDailyTask(node, task, index, 0);
this.renderDailyTaskProgress(node, task, start);
});
// 共用一个补间,让所有行同时开始和结束,且不改动 GM_INFO.tasks。
this.dailyTaskProgressTween = cc.tween({ ratio: 0 })
.to(0.6, { ratio: 1 }, {
easing: "quadOut", onUpdate: (value: { ratio: number }) => {
rows.forEach(row => this.renderDailyTaskProgress(row.node, row.task, row.start + (row.end - row.start) * value.ratio));
}
})
.call(() => {
this.dailyTaskProgressTween = null;
rows.forEach(row => this.renderDailyTaskProgress(row.node, row.task, row.end));
this.touchStart = true;
this.playDailyTaskStateAnimations(tasks);
}).start();
}
private clearDailyTaskProgressAnimation(): void {
if (!this.dailyTaskProgressTween) return;
this.dailyTaskProgressTween.stop();
this.dailyTaskProgressTween = null;
this.touchStart = true;
}
private playDailyTaskStateAnimations(tasks: any[], claimedTaskId = "", onComplete?: () => void): void {
if (!cc.isValid(this.node) || !this.node.activeInHierarchy) {
if (onComplete) onComplete();
return;
}
const animations: typeof this.dailyTaskStateAnimations = [];
tasks.forEach(task => {
if (claimedTaskId ? task.taskId !== claimedTaskId || task.state !== 2 : task.state !== 1) return;
const node = this.content.getChildByName(task.taskId);
if (!cc.isValid(node)) return;
const index = this.content.children.indexOf(node);
const widget = node.getComponent(cc.Widget);
const widgetEnabled = !!widget && widget.enabled;
// 先按原尺寸完成布局,动画期间避免 Widget 拉伸抵消缩放。
if (widgetEnabled) {
widget.updateAlignment();
widget.enabled = false;
}
animations.push({ node, task, index, scaleX: node.scaleX, scaleY: node.scaleY, widget, widgetEnabled, opening: !claimedTaskId });
});
let remaining = animations.length;
if (!remaining) {
if (onComplete) onComplete();
return;
}
animations[0].complete = onComplete;
this.dailyTaskStateAnimations.push(...animations);
// 同一帧启动所有任务行,不按任务顺序逐个播放。
animations.forEach(({ node, task, index, scaleX, scaleY }) => {
this.renderDailyTask(node, task, index, claimedTaskId ? 1 : 0);
cc.tween(node)
.to(0.14, { scaleX: 0, scaleY: 0 }, { easing: "quadIn" })
.call(() => this.renderDailyTask(node, task, index))
.to(0.22, { scaleX: scaleX * 1.1, scaleY: scaleY * 1.1 }, { easing: "quadOut" })
.to(0.12, { scaleX, scaleY }, { easing: "sineInOut" })
.call(() => {
if (--remaining === 0) this.clearDailyTaskStateAnimations(animations);
})
.start();
});
}
private clearDailyTaskStateAnimations(batch = this.dailyTaskStateAnimations.slice()): void {
const animations = batch.filter(animation => this.dailyTaskStateAnimations.indexOf(animation) >= 0);
this.dailyTaskStateAnimations = this.dailyTaskStateAnimations.filter(animation => animations.indexOf(animation) < 0);
animations.forEach(({ node, task, index, scaleX, scaleY, widget, widgetEnabled }) => {
cc.Tween.stopAllByTarget(node);
if (!cc.isValid(node)) return;
node.scaleX = scaleX;
node.scaleY = scaleY;
this.renderDailyTask(node, task, index);
if (cc.isValid(widget)) widget.enabled = widgetEnabled;
});
animations.forEach(animation => { if (animation.complete) animation.complete(); });
}
private renderTaskRewards(node: cc.Node, task: any): void {
// 仅每日任务使用动态克隆;周奖励 preview 不调用此方法。
const rewards = (task.rewards || []).concat([{ type: "stars", count: task.stars }]);
if (!cc.isValid(node)) return;
const template = node.getChildByName("rewardItem");
if (!cc.isValid(template)) {
cc.error("DailyQuests: rewards 下缺少 rewardItem 奖励模板,请检查 dailyQuests 预制体。");
return;
}
while (node.children.length < rewards.length) {
const slot = cc.instantiate(template);
if (!cc.isValid(slot)) return;
node.addChild(slot);
}
this.renderRewardItems(node, rewards);
}
private renderRewardItems(node: cc.Node, rewards: any[], arrangeDaily = true): void {
if (!cc.isValid(node)) return;
const types = ["coin", "freeze", "hammer", "magic_wand", "infinite_health",
"hourglass", "rocket", "infinite_hourglass", "infinite_rocket"];
const spacing = Math.min(146, 300 / rewards.length);
node.children.forEach((slot, index) => {
slot.active = index < rewards.length;
if (!slot.active) return;
if (arrangeDaily) {
slot.x = (index - (rewards.length - 1) / 2) * spacing;
slot.scale = Math.min(1, spacing / 125);
}
const reward = rewards[index];
slot.getChildByName("stars").active = reward.type === "stars";
const icon = slot.getChildByName("icon");
const frame = this.rewardIcons[types.indexOf(reward.type === "magic" ? "magic_wand" : reward.type)];
icon.active = reward.type !== "stars";
icon.getComponent(cc.Sprite).spriteFrame = frame || null;
if (arrangeDaily) {
// 以当前金币素材的裁剪高度为基准,保持金币和罐头的现有显示大小。
this.fitRewardIconHeight(icon, 95);
}
const fallback = slot.getChildByName("rewardName");
if (fallback) {
fallback.active = false;
this.setNodeText(fallback, "");
}
const timed = reward.type.indexOf("infinite_") === 0;
this.setRewardCount(slot.getChildByName("count"), timed
? `${Math.round(reward.count / 60 * 10) / 10}分钟`
: `${reward.type === "stars" || reward.type === "coin" ? "" : "×"}${reward.count}`);
slot.getChildByName("plus").active = arrangeDaily && index < rewards.length - 1;
});
const layout = node.getComponent(cc.Layout);
if (layout) layout.updateLayout();
}
private fitRewardIconHeight(node: cc.Node, height: number): void {
const sprite = node.getComponent(cc.Sprite);
node.scale = 1;
sprite.sizeMode = cc.Sprite.SizeMode.TRIMMED;
sprite.trim = true;
if (!sprite.spriteFrame) return; // 未配置素材的奖励保留空节点占位。
const rect = sprite.spriteFrame.getRect();
if (rect.height <= 0) return;
node.setContentSize(rect.width, rect.height);
const scaleFactor = sprite.spriteFrame === this.rewardIcons[0] || sprite.spriteFrame === this.rewardIcons[4] ? 0.75 : 1;
node.scale = height / rect.height * scaleFactor;
}
private setNodeText(node: cc.Node, text: string): void {
if (!node) return;
const label = node.getComponent(cc.Label);
if (label) label.string = text;
const richText = node.getComponent(cc.RichText);
if (richText) richText.string = text;
}
private setRewardCount(node: cc.Node, text: string, digits: cc.SpriteFrame[] = this.rewardDigits, maxHeight: number = 40): void {
if (!node) return;
const label = node.getComponent(cc.Label);
const outline = node.getComponent(cc.LabelOutline);
const tokens: string[] = text.match(/分钟|小时|./g) || [];
const frames = tokens.map(token => /^\d$/.test(token) ? digits[Number(token)]
: token === "×" || token === "x" ? this.rewardMultiply
: token === "分钟" ? this.rewardMinutes : token === "小时" ? this.rewardHours
: token === "+" ? this.countPlus : token === "/" ? this.taskSlash : null);
let content = node.getChildByName("RewardCountGlyphs");
// Keep the existing text as a fallback if an editor copy has missing frame bindings.
const ready = tokens.every((token, index) => !/^(?:[\d×x+\/]|分钟|小时)$/.test(token) || !!frames[index]);
if (label) { label.string = text; label.enabled = !ready; }
if (content) content.active = ready;
if (!ready) return;
if (!content) {
// Hide the prefab preview digits before rendering the current value.
node.children.forEach(child => child.active = false);
content = new cc.Node("RewardCountGlyphs"); node.addChild(content);
}
const height = Math.min(maxHeight, label ? label.fontSize : maxHeight, node.height);
const scales = tokens.map(token => token === "×" || token === "x" ? 0.6 : 1);
const widths = tokens.map((token, index) => {
const frame = frames[index];
if (frame) { const size = frame.getRect(); return height * size.width / size.height; }
// Punctuation retains the authored label style.
return Array.from(token).reduce((width, char) => width + height * (char === "." ? 0.3 : char === "+" ? 0.65 : 1), 0);
});
const total = widths.reduce((sum, width, index) => sum + width * scales[index], 0);
content.scale = Math.min(1, node.width / Math.max(1, total), node.height / height);
content.setPosition((0.5 - node.anchorX) * node.width, (0.5 - node.anchorY) * node.height);
let x = -total / 2;
tokens.forEach((token, index) => {
let glyph = content.children[index];
if (!glyph) { glyph = new cc.Node("glyph" + index); content.addChild(glyph); }
glyph.active = true;
glyph.scale = scales[index];
let sprite = glyph.getComponent(cc.Sprite), textLabel = glyph.getComponent(cc.Label);
if (frames[index]) {
if (!sprite) sprite = glyph.addComponent(cc.Sprite);
sprite.enabled = true; sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM; sprite.trim = true;
sprite.spriteFrame = frames[index];
if (textLabel) textLabel.enabled = false;
} else {
if (sprite) sprite.enabled = false;
if (!textLabel) textLabel = glyph.addComponent(cc.Label);
textLabel.enabled = true; textLabel.string = token;
textLabel.fontSize = height; textLabel.lineHeight = height;
textLabel.fontFamily = label ? label.fontFamily : "Arial";
textLabel.enableBold = !!label && label.enableBold;
textLabel.overflow = cc.Label.Overflow.SHRINK;
textLabel.enableWrapText = false;
textLabel.horizontalAlign = cc.Label.HorizontalAlign.CENTER;
textLabel.verticalAlign = cc.Label.VerticalAlign.CENTER;
if (outline) {
const stroke = glyph.getComponent(cc.LabelOutline) || glyph.addComponent(cc.LabelOutline);
stroke.color = outline.color; stroke.width = outline.width;
}
}
glyph.setContentSize(widths[index], height);
glyph.setPosition(x + widths[index] * scales[index] / 2, 0);
x += widths[index] * scales[index];
});
content.children.forEach((glyph, index) => { if (index >= tokens.length) glyph.active = false; });
}
private bindButtonData(node: cc.Node, taskId: string): void {
const button = node.getComponent(cc.Button);
if (!button) return;
button.clickEvents.forEach(event => event.customEventData = taskId);
}
private getTaskTitle(task: any): string {
const colorNames = { 1: "蓝色", 2: "紫色", 3: "浅绿色", 4: "米黄色", 5: "青色", 6: "橙色", 7: "深绿色", 8: "黄色", 9: "粉色", 10: "红色" };
const propNames = { freeze: "冻结", hammer: "锤子", magic_wand: "魔法棒", hourglass: "沙漏", rocket: "火箭" };
const params = task.params || {};
switch (task.metric) {
case "clear_blocks": return `清除${task.target}个方块`;
case "clear_size": return params.cellCount != null ? `清除${task.target}个占${params.cellCount}格的方块`
: `清除${task.target}个${params.width || 1}x${params.height || 1}方块`;
case "clear_color": return `清除${task.target}个${colorNames[params.colorId] || "指定颜色"}方块`;
case "pass_levels": return `通过${task.target}关`;
case "no_prop_pass": return `不使用道具通过${task.target}关`;
case "win_streak": return `连续通过${task.target}关`;
case "first_try_pass": return `首次尝试通过${task.target}关`;
case "collect_coin": return `收集${task.target}金币`;
case "spend_coin": return `消耗${task.target}金币`;
case "use_prop": return `使用${task.target}个道具`;
case "use_specific_prop": return `使用${task.target}次${propNames[params.propType] || "指定道具"}`;
case "online_seconds": return `累计在线${Math.floor(task.target / 60)}分钟`;
case "login": return `登录游戏${task.target}次`;
case "share": return `分享${task.target}次游戏`;
default: return task.taskId;
}
}
private removeGeneratedWeeklyNodes(): void {
this.content.children.filter(node => node.name.startsWith("weekly_")).forEach(node => {
node.removeFromParent();
node.destroy();
});
}
private renderWeeklyRewards(data: any): void {
const weekly = this.node.getChildByName("bg").getChildByName("weeklyRewards");
if (!weekly) {
this.stopWeeklyChestAnimations();
return;
}
const rewards = Array.isArray(data.weeklyRewards) ? data.weeklyRewards : [];
weekly.active = rewards.length > 0;
if (!weekly.active) {
this.stopWeeklyChestAnimations();
return;
}
const stars = Math.max(0, Number(data.weeklyStars) || 0);
const maximum = Math.max(1, ...rewards.map(reward => Number(reward.target) || 0));
const progress = weekly.getChildByName("progress").getComponent(cc.ProgressBar);
progress.progress = Math.min(1, stars / maximum);
this.setRewardCount(weekly.getChildByName("current").getChildByName("count"), `${stars}`);
const tiers = weekly.getChildByName("tiers");
tiers.children.forEach(tier => {
const reward = rewards.find(item => item.rewardId === tier.name);
tier.active = !!reward;
const chest = tier.getChildByName("chest");
const pending = this.pendingClaims.has(this.claimKey(tier.name, "weekly"));
const button = tier.getComponent(cc.Button);
if (button) button.interactable = !pending;
tier.opacity = pending ? 130 : 255;
this.updateWeeklyChestSwing(chest, !!reward && reward.state === 1);
if (!reward) return;
this.setRewardCount(tier.getChildByName("target"), `${reward.target}`, this.weeklyDigits, 34);
tier.getChildByName("claimable").active = reward.state === 1;
tier.getChildByName("claimed").active = reward.state === 2;
chest.opacity = reward.state === 2 ? 150 : 255;
this.bindButtonData(tier, reward.rewardId);
});
this.updateWeeklyPreview(data);
}
private updateWeeklyChestSwing(node: cc.Node, claimable: boolean): void {
if (!claimable || !cc.isValid(node) || !node.activeInHierarchy) {
this.stopWeeklyChestAnimations(node);
return;
}
if (this.weeklyChestAnimations.some(animation => animation.node === node)) return;
const angle = node.angle;
// chest 的锚点已在预制体中设为顶部中间,只旋转图标,数字和点击区域保持不动。
const tween = cc.tween(node).repeatForever(
cc.tween()
.to(0.12, { angle: angle + 6 }, { easing: "sineInOut" })
.to(0.24, { angle: angle - 6 }, { easing: "sineInOut" })
.to(0.12, { angle }, { easing: "sineInOut" })
.delay(2)
);
this.weeklyChestAnimations.push({ node, tween, angle });
tween.start();
}
private stopWeeklyChestAnimations(node?: cc.Node): void {
this.weeklyChestAnimations = this.weeklyChestAnimations.filter(animation => {
if (node && animation.node !== node) return true;
animation.tween.stop();
if (cc.isValid(animation.node)) animation.node.angle = animation.angle;
return false;
});
}
selectWeeklyReward(event: cc.Event, rewardId: string): void {
if (!this.touchStart || this.hasOpeningStateAnimation()) return;
if (!this.canUseDisplayedTasks()) return;
const data = cc.fx.GameConfig.GM_INFO.tasks;
const reward = data && data.weeklyRewards && data.weeklyRewards.find(item => item.rewardId === rewardId);
if (!reward) return;
if (reward.state === 1) {
this.closeWeeklyPreview();
this.getWeeklyReward(rewardId);
return;
}
this.selectedWeeklyRewardId = rewardId;
this.updateWeeklyPreview(data);
}
closeWeeklyPreview(): void {
this.selectedWeeklyRewardId = "";
this.updateWeeklyPreview(cc.fx.GameConfig.GM_INFO.tasks);
}
private onWeeklyPreviewOutsideTouch(event: cc.Event.EventTouch): void {
const bg = this.node.getChildByName("bg");
const weekly = bg && bg.getChildByName("weeklyRewards");
const preview = weekly && weekly.getChildByName("preview");
if (!preview || !preview.active) return;
const point = event.getLocation();
const camera = cc.Camera.findCamera(preview);
if (camera) camera.getScreenToWorldPoint(point, point);
const arrow = preview.getChildByName("arrow");
if (preview.getBoundingBoxToWorld().contains(point)
|| (arrow && arrow.getBoundingBoxToWorld().contains(point))) return;
this.closeWeeklyPreview();
event.stopPropagation();
}
private layoutWeeklyPreview(preview: cc.Node, rewardList: cc.Node): void {
// 只调整编辑器已有节点,兼容旧预制体中残留的两层缩放。
preview.scale = 1;
rewardList.scale = 1;
rewardList.y = 0;
rewardList.children.forEach(slot => {
slot.scale = 1;
slot.y = 0;
slot.setContentSize(148, 164);
const icon = slot.getChildByName("icon");
icon.scale = 1;
icon.setPosition(0, 12);
icon.setContentSize(136, 140);
this.fitRewardIconHeight(icon, 95);
const count = slot.getChildByName("count");
count.scale = 1;
count.setPosition(0, -51);
count.setContentSize(148, 48);
});
const layout = rewardList.getComponent(cc.Layout);
if (layout) layout.enabled = false;
const visible = rewardList.children.filter(slot => slot.active);
const columns = Math.min(3, visible.length);
const rows = Math.max(1, Math.ceil(visible.length / 3));
rewardList.setContentSize(columns * 148 + Math.max(0, columns - 1) * 16, rows * 180 - 16);
visible.forEach((slot, index) => {
const row = Math.floor(index / 3);
const rowCount = Math.min(3, visible.length - row * 3);
slot.setPosition((index % 3 - (rowCount - 1) / 2) * 164, ((rows - 1) / 2 - row) * 180);
});
const background = preview.getComponent(cc.Sprite);
background.type = cc.Sprite.Type.SLICED;
background.sizeMode = cc.Sprite.SizeMode.CUSTOM;
if (background.spriteFrame) {
background.spriteFrame.insetLeft = background.spriteFrame.insetRight = 28;
background.spriteFrame.insetTop = background.spriteFrame.insetBottom = 28;
}
preview.setContentSize(rewardList.width + 32, rows * 180);
preview.getChildByName("arrow").y = -preview.height / 2 - 9;
}
private updateWeeklyPreview(data: any): void {
const weekly = this.node.getChildByName("bg").getChildByName("weeklyRewards");
if (!weekly) return;
const preview = weekly.getChildByName("preview");
const reward = data && Array.isArray(data.weeklyRewards)
&& data.weeklyRewards.find(item => item.rewardId === this.selectedWeeklyRewardId);
preview.active = !!reward;
if (!reward) return;
const tier = weekly.getChildByName("tiers").getChildByName(reward.rewardId);
if (!tier) {
preview.active = false;
return;
}
const rewardList = preview.getChildByName("rewards");
// preview 的节点已显式保存在预制体中,运行时只更新图片、数量和状态。
// 不再执行原动态创建方式:
// rewardList.addChild(cc.instantiate(template));
this.renderRewardItems(rewardList, reward.rewards || [], false);
this.layoutWeeklyPreview(preview, rewardList);
const chest = tier.getChildByName("chest");
const chestTop = weekly.convertToNodeSpaceAR(chest.convertToWorldSpaceAR(cc.v2(
(0.5 - chest.anchorX) * chest.width, (1 - chest.anchorY) * chest.height
)));
const arrow = preview.getChildByName("arrow");
// 箭头底端与各自宝箱顶部保持 24 像素间距,兼容手动调整的位置和多行奖励。
preview.y = chestTop.y + 24 - arrow.y + arrow.height * arrow.anchorY * arrow.scaleY;
const limit = Math.max(0, weekly.width / 2 - preview.width / 2 - 10);
preview.x = Math.max(-limit, Math.min(limit, chestTop.x));
arrow.x = chestTop.x - preview.x;
}
//入场动画
setAction() {
this.node.getChildByName("gold_1").getComponent(sp.Skeleton).setAnimation(0, "animation", false);
setTimeout(() => {
this.node.getChildByName("qipao3").active = true;
this.node.getChildByName("qipao3").getComponent(sp.Skeleton).setAnimation(0, "animation", false);
cc.tween(this.node.getChildByName("propBg1"))
.delay(0.1)
.to(0.2, { opacity: 255 })
.start();
}, 400);
setTimeout(() => {
this.node.getChildByName("qipao2").active = true;
this.node.getChildByName("qipao2").getComponent(sp.Skeleton).setAnimation(0, "animation", false);
cc.tween(this.node.getChildByName("propBg2"))
.delay(0.1)
.to(0.2, { opacity: 255 })
.start();
}, 600);
setTimeout(() => {
this.node.getChildByName("gold_2").active = true;
this.node.getChildByName("qipao1").active = true;
this.node.getChildByName("qipao1").getComponent(sp.Skeleton).setAnimation(0, "animation", false);
cc.tween(this.node.getChildByName("propBg3"))
.delay(0.1)
.to(0.2, { opacity: 255 })
.start();
}, 800);
setTimeout(() => {
// this.node.getChildByName("gold_2").active = true;
this.node.getChildByName("baoxiang").active = true;
}, 1000);
}
//領取獎勵
openReward(name, res, showWindow = true) {
console.log("获取奖励名称", name);
const data = Array.isArray(res) ? res : [];
const tasks = cc.fx.GameConfig.GM_INFO.tasks;
const task = tasks && tasks.dailyTasks && tasks.dailyTasks.find(item => item.taskId === name);
DailyTaskProgressTracker.trackTask(task || { taskId: name }, "claim");
console.log("获得任务奖励", data);
let shuju = {
"coin": 0,
"freeze": 0,
"hammer": 0,
"magic": 0,
"magic_wand": 0,
"infinite_health": 0,
"hourglass": 0,
"rocket": 0,
"infinite_hourglass": 0,
"infinite_rocket": 0,
}
for (let i = 0; i < data.length; i++) {
let type = data[i].type;
let count = data[i].count;
shuju[type] += count;
}
let propData = {
"freeze": shuju.freeze || 0,
"hammer": shuju.hammer || 0,
"magic_wand": shuju.magic_wand || shuju.magic || 0,
"infinite_health": shuju.infinite_health || 0,
"price": 0
}
this.getShopProp(propData);
this.grantFutureProps(shuju);
if (shuju.coin > 0) {
cc.fx.GameTool.changeCoin(shuju.coin);
const data = {
change_reason: "quests",
id: 1001 + "",
num: shuju.coin,
compensate: false
};
cc.fx.GameTool.shushu_Track("resource_get", data);
}
if (shuju.infinite_health > 0) {
cc.fx.GameTool.setUserPowerTime(shuju.infinite_health, "DailyQuest");
}
// if (name == "share") {
// // console.log("_______获得无限体力时间:", shuju.infinite_health, name);
// cc.fx.GameTool.setUserPowerTime(900, "DailyQuest");
// }
const canvasTemp = cc.find("Canvas"); // 假设 Canvas 节点
if (canvasTemp) {
const JiaZai = canvasTemp.getComponent("JiaZai");
if (JiaZai) {
if (showWindow) JiaZai.openRewardWindow(data);
if (showWindow && shuju.coin > 0) {
JiaZai.updateCoin();
}
if (shuju.infinite_health > 0) {
JiaZai.updatePower();
}
}
}
// console.log("领取结果:", res.code, "关闭加载1");
if (cc.isValid(this.node)) this.closeLoad();
}
private playDailyRewardAnimation(row: cc.Node, task: any, rewards: any[], onComplete: () => void): void {
const rewardList = cc.isValid(row) && row.getChildByName("rewards");
const items = (rewards || []).concat([{ type: "stars", count: task.stars }]);
// 在任务行缩小前记录每个图标的位置,后续效果不跟随任务行的状态切换。
const origins = items.map((reward, index) => {
const slot = rewardList && rewardList.children[index];
const icon = slot && slot.getChildByName(reward.type === "stars" ? "stars" : "icon");
return cc.isValid(icon) ? icon.convertToWorldSpaceAR(cc.v2()) : null;
});
this.playRewardAnimation(items, onComplete, origins);
}
private playWeeklyRewardAnimation(rewardId: string, rewards: any[], onComplete: () => void): void {
const chest = cc.isValid(this.node) && cc.find(`bg/weeklyRewards/tiers/${rewardId}/chest`, this.node);
// 宝箱以顶部为锚点,奖励从图标的可见中心出现。
const origin = cc.isValid(chest) ? chest.convertToWorldSpaceAR(cc.v2(
(0.5 - chest.anchorX) * chest.width, (0.5 - chest.anchorY) * chest.height
)) : null;
this.playRewardAnimation(rewards, onComplete, (rewards || []).map(() => origin));
}
private playRewardAnimation(rewards: any[], onComplete: () => void, origins: cc.Vec2[] = []): void {
if (!cc.isValid(this.node) || !this.node.activeInHierarchy) {
onComplete();
return;
}
const items = (rewards || []).map((reward, index) => ({ reward, origin: origins[index] }))
.filter(item => Number(item.reward.count) > 0);
const previewRewards = cc.find("bg/weeklyRewards/preview/rewards", this.node);
const template = previewRewards && previewRewards.children[0];
if (!items.length || !cc.isValid(template)) {
onComplete();
return;
}
// 复用编辑器中的奖励模板,在独立效果层展示;不向 preview 动态添加节点。
const layer = new cc.Node("taskRewardEffects");
this.node.addChild(layer);
layer.zIndex = cc.macro.MAX_ZINDEX;
const bg = this.node.getChildByName("bg");
layer.setPosition(bg ? bg.position : cc.v3());
this.rewardEffects.push({ layer, complete: onComplete });
items.forEach(() => layer.addChild(cc.instantiate(template)));
this.renderRewardItems(layer, items.map(item => item.reward), false);
let remaining = items.length;
layer.children.forEach((node, index) => {
const { reward, origin } = items[index];
node.setPosition(origin ? layer.convertToNodeSpaceAR(origin) : cc.v2());
node.setContentSize(168, 190);
node.scale = 0;
node.opacity = 0;
["icon", "stars"].forEach(name => {
const icon = node.getChildByName(name);
icon.scale = 1;
icon.setPosition(0, origin ? 0 : 12);
icon.setContentSize(136, 140);
this.fitRewardIconHeight(icon, 95);
});
const countNode = node.getChildByName("count");
countNode.scale = 1;
countNode.setPosition(0, origin ? -63 : -51);
countNode.setContentSize(168, 48);
const count = reward.type.indexOf("infinite_") === 0
? `${Math.round(Number(reward.count) / 60 * 10) / 10}分钟` : `${reward.count}`;
this.setRewardCount(countNode, `+${count}`);
cc.tween(node)
.delay(index * 0.5)
.set({ opacity: 255 })
.to(0.18, { scale: 1.1 }, { easing: "quadOut" })
.to(0.12, { scale: 1 }, { easing: "sineInOut" })
// 每隔 0.5 秒启动下一项;保留展示时间,让相邻奖励同时可见。
.delay(0.2)
.to(0.5, { y: node.y + 140, opacity: 0 }, { easing: "sineIn" })
.call(() => {
node.destroy();
if (--remaining === 0) this.clearRewardAnimation(layer);
})
.start();
});
}
private clearRewardAnimation(layer?: cc.Node): void {
const effects = this.rewardEffects.filter(effect => !layer || effect.layer === layer);
this.rewardEffects = this.rewardEffects.filter(effect => effects.indexOf(effect) < 0);
effects.forEach(effect => {
if (cc.isValid(effect.layer)) {
effect.layer.children.forEach(node => cc.Tween.stopAllByTarget(node));
effect.layer.destroy();
}
effect.complete();
});
}
private grantFutureProps(rewards: any): void {
const now = Math.floor(Date.now() / 1000);
cc.fx.GameConfig.GM_INFO.hourglassAmount = (Number(cc.fx.GameConfig.GM_INFO.hourglassAmount) || 0) + (rewards.hourglass || 0);
cc.fx.GameConfig.GM_INFO.rocketAmount = (Number(cc.fx.GameConfig.GM_INFO.rocketAmount) || 0) + (rewards.rocket || 0);
if (rewards.infinite_hourglass > 0) {
cc.fx.GameConfig.GM_INFO.infiniteHourglassTime = Math.max(now, cc.fx.GameConfig.GM_INFO.infiniteHourglassTime || 0) + rewards.infinite_hourglass;
}
if (rewards.infinite_rocket > 0) {
cc.fx.GameConfig.GM_INFO.infiniteRocketTime = Math.max(now, cc.fx.GameConfig.GM_INFO.infiniteRocketTime || 0) + rewards.infinite_rocket;
}
cc.fx.StorageMessage.setStorage("futureProp", {
hourglassAmount: cc.fx.GameConfig.GM_INFO.hourglassAmount,
rocketAmount: cc.fx.GameConfig.GM_INFO.rocketAmount,
infiniteHourglassTime: cc.fx.GameConfig.GM_INFO.infiniteHourglassTime,
infiniteRocketTime: cc.fx.GameConfig.GM_INFO.infiniteRocketTime,
timestamp: Date.now(),
});
cc.fx.GameTool.setUserProp(0, 0, () => { });
}
setReward(id) {
}
closeDailyQuests(event?: cc.Event) {
if (event) cc.fx.AudioManager._instance.playEffect("anniu_little", null);
if (this.sceneValue)
cc.fx.GameConfig.GM_INFO.sceneValue = this.sceneValue;
if (this.node)
this.node.active = false;
const jiazaiNode = cc.find("Canvas"); // 假设 JiaZai 挂在 Canvas 节点
const jiazaiComp = jiazaiNode.getComponent(JiaZai);
if (jiazaiComp) {
jiazaiComp.shareIng = false;
jiazaiComp.refreshTaskRedDot();
}
}
openLoad() {
console.log("每日任务打开加载2");
LoadingCatAnimation.play(this.node.getChildByName("Loading"));
}
openConfirmBox() {
console.log("每日任务打开确认框,关闭加载");
this.closeLoad();
this.node.getChildByName("ConfirmBox").active = true;
}
closeConfirmBox() {
this.node.getChildByName("ConfirmBox").active = false;
}
closeLoad() {
LoadingCatAnimation.stop(this.node.getChildByName("Loading"));
}
//点击去完成
clickBtn(event, data) {
this.closeDailyQuests();
const taskData: any = cc.fx.GameConfig.GM_INFO.tasks;
const task = taskData && taskData.dailyTasks && taskData.dailyTasks.find(item => item.taskId === data);
if (!task) return;
if (["use_prop", "use_specific_prop", "pass_levels", "no_prop_pass", "win_streak", "first_try_pass", "clear_blocks", "clear_size", "clear_color"].indexOf(task.metric) >= 0) {
const jiazaiNode = cc.find("Canvas"); // 假设 JiaZai 挂在 Canvas 节点
const jiazaiComp = jiazaiNode.getComponent(JiaZai);
if (jiazaiComp) {
jiazaiComp.startGame();
}
}
else if (task.metric == "share") {
cc.fx.AudioManager._instance.playEffect("anniu_little", null);
let iphoneArr = [
"https://mmocgame.qpic.cn/wechatgame/Lf3SBqy9XpNkakoIZygRzXqww3HTibq6VyibazqmicwibjCS3YpgqbZtkdyABm4Y1wAr/0",
"https://mmocgame.qpic.cn/wechatgame/TWKuFxnCn7ksT3KXfhCC4yOfZeD4b0hrptDSJ2DFmwz02Yc8SppcwyPAOoS1MsMr/0",
"https://mmocgame.qpic.cn/wechatgame/dibaH2x79o1wSwBDymhyzXwfcyicaDb6R5icrFIO7251T4NgxIzXRXErHvAvn50vXFA/0",
"https://mmocgame.qpic.cn/wechatgame/Pgxad80d8ws3o69OicV3DTuTkcP81upQeJ0JBNS1xib3pzYLTF1ZqGY3niciaI7ICKlL/0",
"https://mmocgame.qpic.cn/wechatgame/SfB1vrRBIHKn9ffKFt5sib62yPLE31m2rCvk6DKlEicJNVZSoryEObD6ItwsQn4xibR/0",
"https://mmocgame.qpic.cn/wechatgame/OiaWk33I6QjgWiatrb5YVUq2p0QRmQgO6rLUWxEQDZ4ib9Ny4Pr8iaHnHI6WdxibY2nPL/0",
"https://mmocgame.qpic.cn/wechatgame/CG5xBibollws251aYD4msEPWCiafrcn4Fgtic4T2wME6sWmdfAUtfibgsWoxm59VadDD/0"
]
let randomIphone = iphoneArr[Math.floor(Math.random() * iphoneArr.length)];
let img = randomIphone;
const shareParams = {
title: '如果你突然打了个喷嚏,那一定是我在等你帮忙过关!', // 分享标题
imageUrl: img // 分享图片链接
};
// 调用微信分享 API
if (cc.sys.platform === cc.sys.WECHAT_GAME) {
//@ts-ignore
wx.shareAppMessage(shareParams);
if (cc.fx.GameTool && cc.fx.GameTool.recordDailyTaskShare) cc.fx.GameTool.recordDailyTaskShare();
}
}
}
getReward(event, data, share) {
if (!this.touchStart || this.hasOpeningStateAnimation() || !this.canUseDisplayedTasks()) return;
const tasks = cc.fx.GameConfig.GM_INFO.tasks;
const task = tasks && tasks.dailyTasks && tasks.dailyTasks.find(item => item.taskId === data);
if (!task || task.state !== 1) return;
if (event) cc.fx.AudioManager._instance.playEffect("anniu_little", null);
this.claimReward(data, "daily", task);
}
getWeeklyReward(rewardId: string) {
if (!this.touchStart || this.hasOpeningStateAnimation() || !this.canUseDisplayedTasks()) return;
const data = cc.fx.GameConfig.GM_INFO.tasks;
const reward = data && data.weeklyRewards && data.weeklyRewards.find(item => item.rewardId === rewardId);
if (!reward || reward.state !== 1) return;
this.claimReward(rewardId, "weekly", reward);
}
private claimReward(id: string, scope: "daily" | "weekly", task: any): void {
const key = this.claimKey(id, scope);
if (this.pendingClaims.has(key) || this.animatingClaims.has(key)) return;
this.pendingClaims.add(key);
this.refreshFromCache();
const session = this.displaySession;
const visible = () => session === this.displaySession && cc.isValid(this.node)
&& this.node.activeInHierarchy && !this.hasExpiredDisplayedTasks();
let responded = false;
// 排队或网络较慢时只提示,不解除同一奖励的请求锁,避免重复提交。
const onTimeout = () => {
if (!responded && visible()) MiniGameSdk.API.showToast("奖励仍在领取中,请稍候");
};
this.scheduleOnce(onTimeout, 8);
cc.fx.GameTool.claimDailyQuestReward(id, res => {
if (responded) return;
responded = true;
this.unschedule(onTimeout);
this.pendingClaims.delete(key);
if (res.code !== 1) {
if (visible()) MiniGameSdk.API.showToast(res.msg || "网络异常,领取奖励失败");
this.refreshFromCache();
return;
}
// 入账与动画分离;占用对应任务行,缓存通知只刷新其他行。
this.animatingClaims.add(key);
this.reward = true;
this.openReward(id, res.data.rewards, false);
let finished = false;
const finish = () => {
if (finished) return;
finished = true;
this.animatingClaims.delete(key);
this.reward = this.animatingClaims.size > 0;
this.refreshFromCache();
const canvas = cc.find("Canvas");
const home = canvas && canvas.getComponent("JiaZai");
if (home) home.updateCoin();
};
// 关闭后返回的结果仍入账,但不能在重新打开的面板上重播旧动画。
if (!visible()) { finish(); return; }
this.refreshFromCache();
if (scope === "weekly") {
this.playWeeklyRewardAnimation(id, res.data.rewards, finish);
return;
}
const row = this.content.getChildByName(id);
const claimNode = row && row.getChildByName("btnGet");
const button = claimNode && claimNode.getComponent(cc.Button);
if (button) button.interactable = false;
if (claimNode) claimNode.opacity = 255;
const rewardList = row && row.getChildByName("rewards");
if (rewardList) rewardList.active = false;
// 每一条任务独立保持“奖励动画 -> 已领取状态切换”的顺序。
this.playDailyRewardAnimation(row, task, res.data.rewards, () => {
if (!visible()) { finish(); return; }
const claimed = res.data.task.dailyTasks.find(item => item.taskId === id);
this.playDailyTaskStateAnimations(claimed ? [claimed] : [], id, finish);
if (cc.isValid(rewardList)) rewardList.active = false;
});
}, scope);
}
getShopProp(propData) {
//@ts-ignore
if ((typeof wx !== 'undefined' && wx !== null) || (typeof tt !== 'undefined' && tt !== null)) {
const num = 0;
const timestamp = Date.now();
console.log("_________道具增加的数量为:", propData);
cc.fx.GameConfig.GM_INFO.freezeAmount += propData.freeze;
cc.fx.GameConfig.GM_INFO.hammerAmount += propData.hammer;
cc.fx.GameConfig.GM_INFO.magicAmount += propData.magic_wand;
let propInfo = cc.fx.StorageMessage.getStorage("prop");
propInfo.hammerAmount = cc.fx.GameConfig.GM_INFO.hammerAmount;
propInfo.magicAmount = cc.fx.GameConfig.GM_INFO.magicAmount;
propInfo.freezeAmount = cc.fx.GameConfig.GM_INFO.freezeAmount;
propInfo.timestamp = timestamp;
cc.fx.StorageMessage.setStorage("prop", propInfo);
cc.fx.GameTool.setUserProp(0, num, (data) => {
})
if (propData.freeze > 0) {
const data1 = {
change_reason: "quests",
id: 2001,
num: propData.freeze,
compensate: false
}
cc.fx.GameTool.shushu_Track("resource_get", data1);
}
if (propData.hammer > 0) {
const data2 = {
change_reason: "quests",
id: 2002,
num: propData.hammer,
compensate: false
}
cc.fx.GameTool.shushu_Track("resource_get", data2);
}
if (propData.magic_wand > 0) {
const data3 = {
change_reason: "quests",
id: 2003,
num: propData.magic_wand,
compensate: false
}
cc.fx.GameTool.shushu_Track("resource_get", data3);
}
}
}
onDisable() {
this.displaySession++;
this.clearDailyTaskProgressAnimation();
this.clearDailyTaskStateAnimations();
this.dailyTaskOpeningPlayed = false;
this.clearRewardAnimation();
this.stopWeeklyChestAnimations();
}
onDestroy() {
this.displaySession++;
this.clearDailyTaskProgressAnimation();
this.clearDailyTaskStateAnimations();
this.clearRewardAnimation();
this.stopWeeklyChestAnimations();
this.node.off(cc.Node.EventType.TOUCH_START, this.onWeeklyPreviewOutsideTouch, this, true);
this.unschedule(this.updateCountdowns);
}
}