849 lines
29 KiB
TypeScript
849 lines
29 KiB
TypeScript
import JungleAnimator from "./JungleAnimator";
|
||
import JungleProgress from "./JungleProgress";
|
||
import JungleRewardService from "./JungleRewardService";
|
||
import {
|
||
JungleActivityConfig,
|
||
JungleRenderState,
|
||
JungleTierConfig,
|
||
JungleTierState,
|
||
JungleViewAssets,
|
||
} from "./JungleTypes";
|
||
import JungleView from "./JungleView";
|
||
import Utils from "../../Script/module/Pay/Utils";
|
||
import { MiniGameSdk } from "../../Script/Sdk/MiniGameSdk";
|
||
|
||
const { ccclass, property } = cc._decorator;
|
||
|
||
/**
|
||
* Jungle Treasure 流程控制器。
|
||
* 只管理活动状态和模块协作;UI、动画、配置、存档与发奖均由独立模块负责。
|
||
*/
|
||
@ccclass
|
||
export default class JungleManager extends cc.Component {
|
||
@property(cc.SpriteFrame)
|
||
cardFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
trackFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
titleFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
buttonFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
freeFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
claimFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
yuanFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.BitmapFont)
|
||
priceFont: cc.BitmapFont = null;
|
||
|
||
@property(cc.BitmapFont)
|
||
countFont: cc.BitmapFont = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
lockFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
unlockFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
timerFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
closeFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
coinFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
freezeFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
hammerFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
magicWandFrame: cc.SpriteFrame = null;
|
||
|
||
@property(cc.SpriteFrame)
|
||
infiniteHealthFrame: cc.SpriteFrame = null;
|
||
|
||
@property({ tooltip: "服务器支付尚未接入时,点击付费档位自动模拟支付成功" })
|
||
useMockPayment: boolean = true;
|
||
|
||
@property({ tooltip: "只预览领取和轨道动画,不发道具、不支付、不保存进度" })
|
||
previewAnimationOnly: boolean = true;
|
||
|
||
private activityConfig: JungleActivityConfig = null;
|
||
private currentTierIndex: number = 0;
|
||
private cards: cc.Node[] = [];
|
||
private slotPositions: cc.Vec2[] = [];
|
||
private isMoving: boolean = false;
|
||
private isPurchasing: boolean = false;
|
||
private isClaiming: boolean = false;
|
||
private isUnlockAnimating: boolean = false;
|
||
private isAttentionAnimating: boolean = false;
|
||
private hasExpired: boolean = false;
|
||
private claimTimeout: any = null;
|
||
private paymentTimeout: any = null;
|
||
private onShowListener: () => void = null;
|
||
private pendingPaymentTierId: number = 0;
|
||
private pendingPaymentProductId: string = "";
|
||
private pendingPaymentPrice: number = 0;
|
||
private checkingIosOrder: boolean = false;
|
||
private entrySource: string = "jump_window";
|
||
|
||
private view: JungleView = null;
|
||
private animator: JungleAnimator = null;
|
||
private progress: JungleProgress = null;
|
||
private rewardService: JungleRewardService = null;
|
||
|
||
protected onLoad(): void {
|
||
this.progress = new JungleProgress();
|
||
this.rewardService = new JungleRewardService();
|
||
this.activityConfig = null;
|
||
this.currentTierIndex = 0;
|
||
|
||
this.view = new JungleView(
|
||
this.node,
|
||
this.getViewAssets(),
|
||
this,
|
||
this.closeActivity,
|
||
this.onCardClicked,
|
||
);
|
||
const refs = this.view.build();
|
||
this.cards = refs.cards;
|
||
this.slotPositions = refs.slotPositions;
|
||
this.loadClaimFrame();
|
||
this.animator = new JungleAnimator(
|
||
this,
|
||
refs.contentNode,
|
||
this.cardFrame,
|
||
this.unlockFrame,
|
||
);
|
||
|
||
this.cards.forEach((card) => card.active = false);
|
||
this.schedule(this.updateTimer, 1);
|
||
this.schedule(this.playFirstCardAttention, 2);
|
||
this.registerWechatOnShow();
|
||
}
|
||
|
||
/**
|
||
* 领取图是后加入的分包资源,运行时再按路径加载一次。
|
||
* 这样即使旧 prefab 缓存没有反序列化 claimFrame,也不会出现付费按钮空白。
|
||
*/
|
||
private loadClaimFrame(): void {
|
||
let bundle = cc.assetManager.getBundle("jungle_treasure");
|
||
if (!bundle) {
|
||
cc.warn("[JungleTreasure] 领取图片所在分包未加载");
|
||
return;
|
||
}
|
||
bundle.load("img/lingqu", cc.SpriteFrame, (error: Error, frame: cc.SpriteFrame) => {
|
||
if (error || !frame || !cc.isValid(this.node) || !this.view) {
|
||
if (error) {
|
||
cc.warn("[JungleTreasure] 领取图片加载失败", error);
|
||
}
|
||
return;
|
||
}
|
||
this.claimFrame = frame;
|
||
this.view.setClaimFrame(frame);
|
||
this.renderAllCards();
|
||
});
|
||
}
|
||
|
||
protected onDestroy(): void {
|
||
this.unschedule(this.updateTimer);
|
||
this.unschedule(this.playFirstCardAttention);
|
||
this.clearClaimTimeout();
|
||
this.clearPaymentTimeout();
|
||
this.unregisterWechatOnShow();
|
||
}
|
||
|
||
/** 使用后端配置和 0/1/2 状态数组初始化;状态 2 的档位不进入展示队列。 */
|
||
public setServerData(
|
||
config: JungleActivityConfig,
|
||
tierStates: JungleTierState[],
|
||
source?: string,
|
||
): boolean {
|
||
if (!config || !Array.isArray(config.tiers) || !Array.isArray(tierStates)
|
||
|| tierStates.length < config.tiers.length) {
|
||
cc.warn("[JungleTreasure] server config or tierStates is invalid");
|
||
return false;
|
||
}
|
||
if (source !== undefined) {
|
||
this.entrySource = source === "front_page" || source === "shop_page"
|
||
? source
|
||
: "jump_window";
|
||
}
|
||
|
||
const visibleTiers: JungleTierConfig[] = [];
|
||
for (let index = 0; index < config.tiers.length; index++) {
|
||
const sourceTier = config.tiers[index];
|
||
const rawState = Number(tierStates[index]);
|
||
if (rawState !== 0 && rawState !== 1 && rawState !== 2) {
|
||
cc.warn("[JungleTreasure] invalid tier state:", index + 1, tierStates[index]);
|
||
return false;
|
||
}
|
||
if (rawState === 2) {
|
||
continue;
|
||
}
|
||
visibleTiers.push(Object.assign({}, sourceTier, {
|
||
state: rawState as JungleTierState,
|
||
}));
|
||
}
|
||
|
||
this.activityConfig = Object.assign({}, config, {
|
||
tiers: visibleTiers,
|
||
});
|
||
this.currentTierIndex = 0;
|
||
this.hasExpired = false;
|
||
this.renderAllCards();
|
||
this.updateTimer();
|
||
return true;
|
||
}
|
||
|
||
/** 正式支付层完成后回调。支付失败或取消不会移动轨道。 */
|
||
public completePurchase(success: boolean): void {
|
||
if (!this.isPurchasing) {
|
||
return;
|
||
}
|
||
this.isPurchasing = false;
|
||
if (success) {
|
||
const tier = this.activityConfig && this.activityConfig.tiers[this.currentTierIndex];
|
||
if (tier) {
|
||
tier.state = 1;
|
||
}
|
||
this.renderAllCards();
|
||
if (tier) {
|
||
this.playTierUnlockAnimation(tier.id);
|
||
}
|
||
} else {
|
||
this.renderAllCards();
|
||
}
|
||
}
|
||
|
||
public openActivity(): void {
|
||
this.node.active = true;
|
||
this.registerWechatOnShow();
|
||
this.renderAllCards();
|
||
this.updateTimer();
|
||
}
|
||
|
||
public closeActivity(): void {
|
||
if (this.isMoving || this.isPurchasing || this.isClaiming || this.isUnlockAnimating) {
|
||
return;
|
||
}
|
||
this.stopFirstCardAttention();
|
||
this.unregisterWechatOnShow();
|
||
this.node.active = false;
|
||
this.node.emit("jungle-close");
|
||
}
|
||
|
||
/** 仅用于当前模拟配置的联调。 */
|
||
public resetMockProgress(): void {
|
||
if (!this.activityConfig) {
|
||
return;
|
||
}
|
||
this.progress.reset(this.activityConfig);
|
||
this.currentTierIndex = 0;
|
||
this.renderAllCards();
|
||
}
|
||
|
||
public getCurrentTierIndex(): number {
|
||
return this.currentTierIndex;
|
||
}
|
||
|
||
private onCardClicked(buttonComponent: cc.Button): void {
|
||
console.log("点击礼包档位按钮最外层", buttonComponent);
|
||
if (this.isMoving || this.isPurchasing || this.isClaiming
|
||
|| this.isUnlockAnimating || !this.activityConfig) {
|
||
return;
|
||
}
|
||
|
||
const button = buttonComponent && buttonComponent.node;
|
||
if (!button) {
|
||
return;
|
||
}
|
||
const card = button.parent;
|
||
if (card !== this.cards[0]) {
|
||
this.animator.playLockedFeedback(card);
|
||
return;
|
||
}
|
||
this.stopFirstCardAttention();
|
||
|
||
const tier = this.activityConfig.tiers[this.currentTierIndex];
|
||
if (!tier) {
|
||
return;
|
||
}
|
||
const isPurchaseBlocked = tier.price > 0
|
||
&& tier.state === 0
|
||
&& !!this.activityConfig.endAt
|
||
&& Date.now() >= this.activityConfig.endAt;
|
||
if (isPurchaseBlocked) {
|
||
return;
|
||
}
|
||
|
||
if (this.previewAnimationOnly) {
|
||
this.completeCurrentTier();
|
||
} else if (tier.state === 0) {
|
||
this.requestPurchase(tier);
|
||
} else {
|
||
console.log("点击礼包档位按钮准备请求", buttonComponent);
|
||
this.requestClaim(tier);
|
||
}
|
||
}
|
||
|
||
private trackJungleAction(eventName: string, tier: JungleTierConfig): void {
|
||
let currentBlock = Number(tier && tier.id);
|
||
if (!isFinite(currentBlock) || currentBlock < 1 || currentBlock > 51) {
|
||
cc.warn("[JungleTreasure] 埋点格子编号无效:", currentBlock);
|
||
return;
|
||
}
|
||
cc.fx.GameTool.shushu_Track(eventName, {
|
||
current_block: currentBlock,
|
||
source: this.entrySource,
|
||
});
|
||
}
|
||
|
||
private requestPurchase(tier: JungleTierConfig): void {
|
||
this.trackJungleAction("pay_jungle", tier);
|
||
if (!tier.productId) {
|
||
this.showToast("礼包商品配置错误");
|
||
return;
|
||
}
|
||
this.isPurchasing = true;
|
||
this.pendingPaymentTierId = tier.id;
|
||
this.pendingPaymentProductId = tier.productId;
|
||
this.pendingPaymentPrice = Math.round(Number(tier.price) * 100);
|
||
this.renderAllCards();
|
||
this.openLoad();
|
||
|
||
if (this.useMockPayment) {
|
||
this.scheduleOnce(() => {
|
||
this.closeLoad();
|
||
this.completePurchase(true);
|
||
}, 0.45);
|
||
return;
|
||
}
|
||
|
||
const systemType = this.getSystemType();
|
||
if (systemType === "ios" && !cc.fx.GameConfig.GM_INFO.iosCanPay) {
|
||
this.startOldIosPayment(tier);
|
||
return;
|
||
}
|
||
this.startDirectPayment(tier, systemType);
|
||
}
|
||
|
||
/** 状态 1 的礼包必须先由后端改成 2;成功后才发奖励并播放轨道动画。 */
|
||
private requestClaim(tier: JungleTierConfig): void {
|
||
this.trackJungleAction("collect_jungle", tier);
|
||
console.log("点击礼包档位按钮内里面");
|
||
this.isClaiming = true;
|
||
this.renderAllCards();
|
||
this.openLoad();
|
||
|
||
let completed = false;
|
||
this.clearClaimTimeout();
|
||
this.claimTimeout = setTimeout(() => {
|
||
if (completed) {
|
||
return;
|
||
}
|
||
completed = true;
|
||
this.claimTimeout = null;
|
||
this.finishClaimFailure("领取请求超时,请稍后重试");
|
||
}, 8000);
|
||
console.log("点击礼包档位按钮开始请求");
|
||
Utils.claimJungleTreasure(tier.id, (response) => {
|
||
console.log("点击礼包档位按钮请求结果", response);
|
||
if (completed || !cc.isValid(this.node)) {
|
||
return;
|
||
}
|
||
completed = true;
|
||
this.clearClaimTimeout();
|
||
const data = response && response.code === 1 ? response.data : null;
|
||
const states = data && data.tierStates;
|
||
const claimedTierId = Number(data && data.claimedTierId);
|
||
if (!Array.isArray(states)
|
||
|| claimedTierId !== tier.id
|
||
|| Number(states[tier.id - 1]) !== 2) {
|
||
this.finishClaimFailure((response && (response.msg || response.message)) || "礼包领取失败");
|
||
return;
|
||
}
|
||
|
||
this.closeLoad();
|
||
this.isClaiming = false;
|
||
tier.state = 2;
|
||
this.publishServerState(data);
|
||
// 后端已确认领取后,才执行客户端道具发放与礼包移动动画。
|
||
this.completeCurrentTier();
|
||
});
|
||
}
|
||
|
||
private finishClaimFailure(message: string): void {
|
||
this.closeLoad();
|
||
this.isClaiming = false;
|
||
this.renderAllCards();
|
||
this.showToast(message);
|
||
}
|
||
|
||
private startDirectPayment(tier: JungleTierConfig, systemType: string): void {
|
||
let callbackReceived = false;
|
||
this.clearPaymentTimeout();
|
||
this.paymentTimeout = setTimeout(() => {
|
||
if (callbackReceived) {
|
||
return;
|
||
}
|
||
callbackReceived = true;
|
||
this.paymentTimeout = null;
|
||
this.finishPaymentFailure("支付请求超时,请稍后重试");
|
||
// 包含玩家在微信支付面板中的操作时间,不能按普通接口的 8 秒计算。
|
||
}, 5 * 60 * 1000);
|
||
console.log("准备开始调用 购买礼包接口", this.pendingPaymentPrice);
|
||
Utils.buyProp(
|
||
tier.productId,
|
||
1,
|
||
this.pendingPaymentPrice,
|
||
systemType,
|
||
tier.productId,
|
||
(result) => {
|
||
console.log("点击礼包档位按钮支付结果", result);
|
||
if (!cc.isValid(this.node)) {
|
||
console.log("11111点击礼包档位按钮支付结果回调已处理或节点已销毁");
|
||
return;
|
||
}
|
||
callbackReceived = true;
|
||
this.clearPaymentTimeout();
|
||
if (!result) {
|
||
this.finishPaymentFailure("支付拉起失败");
|
||
console.log("22222点击礼包档位按钮支付结果回调已处理或节点已销毁");
|
||
return;
|
||
}
|
||
if (result.err) {
|
||
if (Number(result.errCode) === 16) {
|
||
this.startOldIosPayment(tier);
|
||
console.log("33333点击礼包档位按钮支付结果回调已处理或节点已销毁");
|
||
return;
|
||
}
|
||
this.finishPaymentFailure(Number(result.errCode) === -2 ? "已取消支付" : "支付拉起失败");
|
||
console.log("44444点击礼包档位按钮支付结果回调已处理或节点已销毁");
|
||
return;
|
||
}
|
||
console.log("55555点击礼包档位按钮支付结果", tier);
|
||
this.pollDirectPaymentResult(tier);
|
||
},
|
||
);
|
||
}
|
||
|
||
private pollDirectPaymentResult(tier: JungleTierConfig): void {
|
||
console.log("拿到礼包档位按钮轮询支付结果之后调用档位是否解锁");
|
||
Utils.getPayInfo((response) => {
|
||
console.log("点击礼包档位按钮轮询支付结果", response);
|
||
if (!cc.isValid(this.node)) {
|
||
return;
|
||
}
|
||
const payState = Number(response && response.data && response.data.pay_state);
|
||
console.log("payState点击礼包档位按钮轮询支付结果", payState);
|
||
if (response && response.code === 1 && payState === 2) {
|
||
console.log("准备改变礼包状态为可领取", response);
|
||
const name = "购买丛林宝藏礼包:" + tier.productId;
|
||
MiniGameSdk.API.yinli_Pay(this.pendingPaymentPrice, Utils.outTradeNo, name);
|
||
this.refreshAfterPayment(tier.id);
|
||
return;
|
||
}
|
||
this.finishPaymentFailure(payState === 1 ? "已取消支付" : "支付结果获取失败");
|
||
});
|
||
}
|
||
|
||
/** iOS 旧支付:跳转客服,回到小游戏时由 wx.onShow 查询订单结果。 */
|
||
private startOldIosPayment(tier: JungleTierConfig): void {
|
||
this.clearPaymentTimeout();
|
||
const payInfo = {
|
||
price: this.pendingPaymentPrice,
|
||
payment_name: tier.productId,
|
||
payment_count: 1,
|
||
};
|
||
Utils.GoKEFu(payInfo, (result) => {
|
||
if (!cc.isValid(this.node)) {
|
||
return;
|
||
}
|
||
if (result !== "success") {
|
||
this.finishPaymentFailure("客服支付入口打开失败");
|
||
}
|
||
});
|
||
}
|
||
|
||
private registerWechatOnShow(): void {
|
||
if (this.onShowListener) {
|
||
return;
|
||
}
|
||
// @ts-ignore
|
||
if (typeof wx === "undefined" || !wx.onShow) {
|
||
return;
|
||
}
|
||
this.onShowListener = () => this.handleWechatOnShow();
|
||
// @ts-ignore
|
||
wx.onShow(this.onShowListener);
|
||
}
|
||
|
||
private unregisterWechatOnShow(): void {
|
||
// @ts-ignore
|
||
if (!this.onShowListener || typeof wx === "undefined" || !wx.offShow) {
|
||
return;
|
||
}
|
||
// @ts-ignore
|
||
wx.offShow(this.onShowListener);
|
||
this.onShowListener = null;
|
||
}
|
||
|
||
private handleWechatOnShow(): void {
|
||
const gameInfo: any = cc.fx.GameConfig.GM_INFO;
|
||
const orderNo = gameInfo.iosJungleOrder;
|
||
if (!orderNo || this.checkingIosOrder) {
|
||
return;
|
||
}
|
||
this.checkingIosOrder = true;
|
||
this.isPurchasing = true;
|
||
this.openLoad();
|
||
Utils.getIosPayInfo(orderNo, (response) => {
|
||
if (!cc.isValid(this.node)) {
|
||
return;
|
||
}
|
||
this.checkingIosOrder = false;
|
||
const data = response && response.data;
|
||
const productId = (data && data.payment_name) || this.pendingPaymentProductId;
|
||
const price = Number(data && data.goodsPrice) || this.pendingPaymentPrice;
|
||
if (response && response.code === 1) {
|
||
gameInfo.iosJungleOrder = "";
|
||
const name = "购买丛林宝藏礼包:" + productId;
|
||
MiniGameSdk.API.yinli_Pay(price, orderNo, name);
|
||
this.refreshAfterPayment(this.resolvePendingTierId(productId));
|
||
return;
|
||
}
|
||
if (response && response.code === 2) {
|
||
this.closeLoad();
|
||
this.isPurchasing = false;
|
||
this.renderAllCards();
|
||
this.showToast("支付结果确认超时,稍后返回活动会继续查询");
|
||
return;
|
||
}
|
||
gameInfo.iosJungleOrder = "";
|
||
this.finishPaymentFailure("支付未成功");
|
||
});
|
||
}
|
||
|
||
private refreshAfterPayment(tierId: number): void {
|
||
let completed = false;
|
||
this.clearPaymentTimeout();
|
||
this.paymentTimeout = setTimeout(() => {
|
||
if (completed) {
|
||
return;
|
||
}
|
||
completed = true;
|
||
this.paymentTimeout = null;
|
||
this.finishPaymentFailure("支付成功,礼包状态刷新超时,请重新进入活动");
|
||
}, 8000);
|
||
|
||
const requestUnlockedState = () => {
|
||
if (completed || !cc.isValid(this.node)) {
|
||
return;
|
||
}
|
||
Utils.getJungleTreasure((response) => {
|
||
if (completed || !cc.isValid(this.node)) {
|
||
return;
|
||
}
|
||
const data = response && response.code === 1 ? response.data : null;
|
||
const states = data && data.tierStates;
|
||
const refreshedState = Array.isArray(states) && tierId > 0
|
||
? Number(states[tierId - 1])
|
||
: -1;
|
||
|
||
// 微信支付回调会先把订单改为成功,再写玩家的活动解锁状态。
|
||
// 订单成功后第一次读取可能仍为 0,因此在原有 8 秒超时内继续轮询。
|
||
if (!data || !data.config || !Array.isArray(states)
|
||
|| (tierId > 0 && refreshedState !== 1 && refreshedState !== 2)) {
|
||
this.scheduleOnce(requestUnlockedState, 0.4);
|
||
return;
|
||
}
|
||
|
||
completed = true;
|
||
this.clearPaymentTimeout();
|
||
this.isPurchasing = false;
|
||
this.publishServerState(data);
|
||
const refreshed = this.setServerData(data.config, states);
|
||
if (refreshed && refreshedState === 1) {
|
||
this.playTierUnlockAnimation(tierId);
|
||
}
|
||
if (!refreshed) {
|
||
this.closeLoad();
|
||
this.renderAllCards();
|
||
this.showToast("支付成功,礼包状态刷新失败,请重新进入活动");
|
||
return;
|
||
}
|
||
// setServerData 已将付费档位从“3元”重新渲染为“领取”,完成后再关闭 Loading。
|
||
this.closeLoad();
|
||
this.showToast("支付成功,礼包已解锁");
|
||
});
|
||
};
|
||
requestUnlockedState();
|
||
}
|
||
|
||
private finishPaymentFailure(message: string): void {
|
||
this.clearPaymentTimeout();
|
||
this.closeLoad();
|
||
this.isPurchasing = false;
|
||
this.checkingIosOrder = false;
|
||
this.renderAllCards();
|
||
this.showToast(message);
|
||
}
|
||
|
||
private resolvePendingTierId(productId: string): number {
|
||
if (this.pendingPaymentTierId > 0) {
|
||
return this.pendingPaymentTierId;
|
||
}
|
||
const match = String(productId || "").match(/^jungle_treasure_(\d+)$/);
|
||
if (!match) {
|
||
return 0;
|
||
}
|
||
const paidIndex = Number(match[1]);
|
||
return paidIndex === 1 ? 4 : 4 + (paidIndex - 1) * 6;
|
||
}
|
||
|
||
private publishServerState(data: any): void {
|
||
this.node.emit("jungle-state-changed", data);
|
||
}
|
||
|
||
private getSystemType(): string {
|
||
try {
|
||
// @ts-ignore
|
||
const info = typeof wx !== "undefined" && wx.getSystemInfoSync
|
||
// @ts-ignore
|
||
? wx.getSystemInfoSync()
|
||
: null;
|
||
return info && info.platform === "ios" ? "ios" : "Android";
|
||
} catch (error) {
|
||
cc.warn("[JungleTreasure] 获取系统类型失败", error);
|
||
return "Android";
|
||
}
|
||
}
|
||
|
||
private getSceneController(): any {
|
||
const canvas = cc.find("Canvas");
|
||
return canvas && (canvas.getComponent("JiaZai") || canvas.getComponent("SceneManager"));
|
||
}
|
||
|
||
private openLoad(): void {
|
||
const controller = this.getSceneController();
|
||
if (controller && typeof controller.openLoad === "function") {
|
||
controller.openLoad();
|
||
}
|
||
}
|
||
|
||
private closeLoad(): void {
|
||
const controller = this.getSceneController();
|
||
if (controller && typeof controller.closeLoad === "function") {
|
||
controller.closeLoad();
|
||
}
|
||
}
|
||
|
||
private showToast(message: string): void {
|
||
if (MiniGameSdk && MiniGameSdk.API && MiniGameSdk.API.showToast) {
|
||
MiniGameSdk.API.showToast(message);
|
||
}
|
||
}
|
||
|
||
private clearClaimTimeout(): void {
|
||
if (this.claimTimeout) {
|
||
clearTimeout(this.claimTimeout);
|
||
this.claimTimeout = null;
|
||
}
|
||
}
|
||
|
||
private clearPaymentTimeout(): void {
|
||
if (this.paymentTimeout) {
|
||
clearTimeout(this.paymentTimeout);
|
||
this.paymentTimeout = null;
|
||
}
|
||
}
|
||
|
||
private completeCurrentTier(): void {
|
||
console.log("点击礼包档位按钮完成当前档位");
|
||
const tier = this.activityConfig.tiers[this.currentTierIndex];
|
||
if (!tier || this.isMoving) {
|
||
return;
|
||
}
|
||
|
||
this.isMoving = true;
|
||
if (!this.previewAnimationOnly) {
|
||
this.rewardService.grant(this.node, tier.rewards);
|
||
}
|
||
|
||
const leavingCard = this.cards[0];
|
||
this.animator.playClaimEffect(
|
||
leavingCard,
|
||
() => this.animator.playCardExit(leavingCard, () => this.shiftCards()),
|
||
);
|
||
}
|
||
|
||
private shiftCards(): void {
|
||
const oldCards = this.cards.slice();
|
||
this.currentTierIndex++;
|
||
if (!this.previewAnimationOnly) {
|
||
this.progress.save(this.activityConfig, this.currentTierIndex);
|
||
}
|
||
|
||
const recycled = oldCards[0];
|
||
recycled.stopAllActions();
|
||
recycled.active = false;
|
||
recycled.scale = 1;
|
||
recycled.opacity = 0;
|
||
recycled.setAnchorPoint(0.5, 0.5);
|
||
|
||
this.animator.moveCardsWithOverlap(oldCards, this.slotPositions, 1, () => {
|
||
this.cards.splice(
|
||
0,
|
||
this.cards.length,
|
||
oldCards[1],
|
||
oldCards[2],
|
||
oldCards[3],
|
||
oldCards[4],
|
||
oldCards[5],
|
||
recycled,
|
||
);
|
||
this.showNewSixthCard(recycled);
|
||
});
|
||
}
|
||
|
||
private showNewSixthCard(card: cc.Node): void {
|
||
const sixthTierIndex = this.currentTierIndex + 5;
|
||
const targetCenter = this.slotPositions[5];
|
||
this.view.renderCard(card, this.activityConfig, this.getRenderState(), sixthTierIndex, 5);
|
||
|
||
if (sixthTierIndex >= this.activityConfig.tiers.length) {
|
||
card.setAnchorPoint(0.5, 0.5);
|
||
card.setPosition(targetCenter);
|
||
this.finishShift();
|
||
return;
|
||
}
|
||
|
||
this.animator.showNewCard(card, targetCenter, () => this.finishShift());
|
||
}
|
||
|
||
private finishShift(): void {
|
||
this.isMoving = false;
|
||
this.renderAllCards();
|
||
if (this.currentTierIndex >= this.activityConfig.tiers.length) {
|
||
this.node.emit("jungle-complete");
|
||
return;
|
||
}
|
||
|
||
let currentTier = this.activityConfig.tiers[this.currentTierIndex];
|
||
if (currentTier && currentTier.state !== 0) {
|
||
this.playTierUnlockAnimation(currentTier.id);
|
||
}
|
||
}
|
||
|
||
private renderAllCards(): void {
|
||
if (this.view) {
|
||
this.view.renderAllCards(this.activityConfig, this.getRenderState());
|
||
}
|
||
}
|
||
|
||
/** 无论当前礼包是否解锁,每两秒让最前方礼包整体 duang 一次。 */
|
||
private playFirstCardAttention(): void {
|
||
if (!this.node.activeInHierarchy || !this.activityConfig || !this.animator
|
||
|| this.isAttentionAnimating || this.isMoving || this.isPurchasing
|
||
|| this.isClaiming || this.isUnlockAnimating) {
|
||
return;
|
||
}
|
||
let firstCard = this.cards[0];
|
||
if (!firstCard || !firstCard.activeInHierarchy) {
|
||
return;
|
||
}
|
||
|
||
this.isAttentionAnimating = true;
|
||
this.animator.playCardAttention(firstCard, () => {
|
||
if (cc.isValid(this.node)) {
|
||
this.isAttentionAnimating = false;
|
||
}
|
||
});
|
||
}
|
||
|
||
private stopFirstCardAttention(): void {
|
||
if (!this.isAttentionAnimating || !this.animator) {
|
||
return;
|
||
}
|
||
this.animator.stopCardAttention(this.cards[0]);
|
||
this.isAttentionAnimating = false;
|
||
}
|
||
|
||
private playTierUnlockAnimation(tierId: number): void {
|
||
let targetCard: cc.Node = null;
|
||
for (let index = 0; index < this.cards.length; index++) {
|
||
let card = this.cards[index];
|
||
if (card && card.active && card.name === "reward_card_" + tierId) {
|
||
targetCard = card;
|
||
break;
|
||
}
|
||
}
|
||
if (!targetCard || !this.animator) {
|
||
return;
|
||
}
|
||
|
||
this.isUnlockAnimating = true;
|
||
this.animator.playUnlockEffect(targetCard, () => {
|
||
if (!cc.isValid(this.node)) {
|
||
return;
|
||
}
|
||
this.isUnlockAnimating = false;
|
||
this.renderAllCards();
|
||
});
|
||
}
|
||
|
||
private updateTimer(): void {
|
||
if (!this.view || !this.activityConfig) {
|
||
return;
|
||
}
|
||
if (this.view.updateTimer(this.activityConfig) === 0 && !this.hasExpired) {
|
||
this.hasExpired = true;
|
||
this.renderAllCards();
|
||
this.node.emit("jungle-expired");
|
||
}
|
||
}
|
||
|
||
private getRenderState(): JungleRenderState {
|
||
return {
|
||
currentTierIndex: this.currentTierIndex,
|
||
isMoving: this.isMoving,
|
||
isPurchasing: this.isPurchasing || this.isClaiming || this.isUnlockAnimating,
|
||
};
|
||
}
|
||
|
||
private getViewAssets(): JungleViewAssets {
|
||
return {
|
||
cardFrame: this.cardFrame,
|
||
trackFrame: this.trackFrame,
|
||
titleFrame: this.titleFrame,
|
||
buttonFrame: this.buttonFrame,
|
||
freeFrame: this.freeFrame,
|
||
claimFrame: this.claimFrame,
|
||
yuanFrame: this.yuanFrame,
|
||
priceFont: this.priceFont,
|
||
countFont: this.countFont,
|
||
lockFrame: this.lockFrame,
|
||
timerFrame: this.timerFrame,
|
||
closeFrame: this.closeFrame,
|
||
coinFrame: this.coinFrame,
|
||
freezeFrame: this.freezeFrame,
|
||
hammerFrame: this.hammerFrame,
|
||
magicWandFrame: this.magicWandFrame,
|
||
infiniteHealthFrame: this.infiniteHealthFrame,
|
||
};
|
||
}
|
||
}
|