MatchMaster/assets/Script/CoinMadnessHost.ts

185 lines
9.9 KiB
TypeScript

import Utils from './module/Pay/Utils';
import { MiniGameSdk } from './Sdk/MiniGameSdk';
import HomePopupQueue from '../home_popup_queue/HomePopupQueue';
const { ccclass } = cc._decorator;
const BUNDLE = 'coin_madness';
/** Resident bridge only: never import the bundle's UI class or preload its assets. */
@ccclass
export default class CoinMadnessHost extends cc.Component {
private static occupied = false;
private bundle: cc.AssetManager.Bundle = null;
private panel: cc.Node = null;
private entry: cc.Node = null;
private loading = false;
private releasing = false;
private requestId = 0;
private data: any = null;
private finishQueue: () => void = null;
private alive = true;
private refreshing = false;
private busy = false;
private revision = 0;
private shown = () => this.refresh();
private static receiptKey(): string { return 'coinMadnessReceipt_' + cc.fx.GameConfig.GM_INFO.uid; }
static restorePending(user: any): void {
let pending = user && user.coinMadnessPending;
if (typeof pending === 'string') { try { pending = JSON.parse(pending); } catch { return; } }
if (!pending || !Number.isSafeInteger(pending.balance)) return;
// Login already includes the server grant. Override only coins, not other progression.
cc.fx.GameConfig.GM_INFO.coin = pending.balance;
cc.fx.StorageMessage.setStorage('coin', { coin: pending.balance, timestamp: Date.now(), coinMadnessReceipt: pending.id });
cc.fx.StorageMessage.setStorage(this.receiptKey(), pending.id);
}
start() { cc.game.on(cc.game.EVENT_SHOW, this.shown, this); this.refresh(); }
private request(action: string, body: any, done: (result: any) => void) {
Utils.POST('coinMadness', { uid: cc.fx.GameConfig.GM_INFO.uid, action, ...body }, done);
}
private refresh() {
if (!this.alive || this.refreshing || this.busy || !cc.fx.GameConfig.GM_INFO.uid
|| cc.fx.GameConfig.GM_INFO.canIos === false) return;
this.refreshing = true;
const revision = this.revision;
this.request('read', {}, result => {
this.refreshing = false;
if (!this.alive || revision !== this.revision || result?.code !== 1) return;
this.data = result.data; this.updateEntry();
this.unschedule(this.refresh);
if (this.data.state && !this.data.state.settled) {
const remaining = (this.data.state.config.endsAt - this.data.serverNow) / 1000;
if (remaining > 0) this.scheduleOnce(this.refresh, Math.min(remaining + 0.1, 60));
}
if (this.panel) this.panel.getComponent('CoinMadnessPanel').render(this.data);
if (this.data.pending || this.data.needsSettlement) this.queueSettlement();
});
}
private updateEntry() {
const visible = this.data?.available && (!this.data.state?.settled || this.data.pending);
if (!visible) { if (this.entry) this.entry.destroy(); this.entry = null; return; }
if (!this.entry) {
this.entry = new cc.Node('CoinMadnessEntry');
(this.node.getChildByName('Load') || this.node).addChild(this.entry);
this.entry.setContentSize(146, 112);
const widget = this.entry.addComponent(cc.Widget);
widget.isAlignLeft = true; widget.left = 24; widget.isAlignBottom = true; widget.bottom = 425;
const g = this.entry.addComponent(cc.Graphics);
g.fillColor = new cc.Color(231, 121, 24); g.roundRect(-73, -56, 146, 112, 22); g.fill();
const title = new cc.Node('Title'); this.entry.addChild(title);
const label = title.addComponent(cc.Label); label.string = '金币狂热'; label.fontSize = 27;
label.useSystemFont = true;
label.lineHeight = 31;
title.color = cc.Color.WHITE;
this.entry.on(cc.Node.EventType.TOUCH_END, this.open, this);
}
const state = this.data.state;
const target = state.config.stages[state.stage];
const ready = state.paidAt && target && state.progress >= target.wins;
this.entry.getChildByName('Title').getComponent(cc.Label).string = '金币狂热\n'
+ (ready ? '可领取' : state.paidAt ? '闯关领奖' : '限时活动');
this.entry.active = true;
}
open() {
if (this.loading || this.releasing || this.panel || this.busy || !this.data) return;
HomePopupQueue.get(this.node).enqueue('coinMadness', 55, done => {
if (!this.alive) { done(); return; }
this.finishQueue = done; this.load();
});
}
private load() {
if (CoinMadnessHost.occupied) { this.close(); this.toast('活动资源释放中,请稍后重试'); return; }
CoinMadnessHost.occupied = true;
this.loading = true;
const requestId = ++this.requestId;
cc.assetManager.loadBundle(BUNDLE, (error, bundle) => {
if (error) { CoinMadnessHost.occupied = false; this.loading = false; this.close(); this.toast('活动加载失败,请重试'); return; }
if (!this.alive || requestId !== this.requestId) { this.release(bundle); return; }
this.bundle = bundle;
bundle.load('CoinMadness', cc.Prefab, (loadError, prefab: cc.Prefab) => {
this.loading = false;
if (!this.alive || requestId !== this.requestId) { this.release(bundle); return; }
if (loadError) { this.close(); this.toast('活动资源加载失败,请重试'); return; }
this.panel = cc.instantiate(prefab); this.node.addChild(this.panel);
this.panel.zIndex = cc.macro.MAX_ZINDEX - 1;
this.panel.getComponent('CoinMadnessPanel').initialize(this.data, {
close: () => this.close(), refresh: () => this.refresh(),
request: (action, body, done) => this.action(action, body, done),
order: (endpoint, body, done) => Utils.POST(endpoint, { uid: cc.fx.GameConfig.GM_INFO.uid,
coinMadnessLevel: cc.fx.GameConfig.GM_INFO.level, ...body }, done),
});
this.refresh();
});
});
}
private action(action: string, body: any, done: (result: any) => void) {
if (this.busy) return;
this.busy = true;
this.revision++;
const uid = cc.fx.GameConfig.GM_INFO.uid;
this.request(action, { ...body, coinAmount: Math.floor(cc.fx.GameConfig.GM_INFO.coin) }, result => {
if (uid !== cc.fx.GameConfig.GM_INFO.uid) { this.busy = false; return; }
if (result?.code !== 1) { this.busy = false; if (this.alive) done(result); return; }
this.acceptReceipt(result.data, acknowledged => {
this.busy = false;
if (!this.alive) return;
this.data = acknowledged.data || result.data; this.updateEntry(); done(acknowledged);
});
});
}
private acceptReceipt(data: any, done: (result: any) => void) {
const receipt = data.pending;
if (!receipt) { done({ code: 1, data }); return; }
const info = cc.fx.GameConfig.GM_INFO;
const coinCache = cc.fx.StorageMessage.getStorage('coin');
if (cc.fx.StorageMessage.getStorage(CoinMadnessHost.receiptKey()) !== receipt.id && coinCache?.coinMadnessReceipt !== receipt.id) {
info.coin += receipt.coins;
cc.fx.StorageMessage.setStorage('coin', { coin: info.coin, timestamp: Date.now(), coinMadnessReceipt: receipt.id });
cc.fx.StorageMessage.setStorage(CoinMadnessHost.receiptKey(), receipt.id);
}
const home: any = cc.isValid(this.node) && this.node.getComponent('JiaZai');
if (home) home.updateCoin();
this.request('ack', { receiptId: receipt.id, coinAmount: Math.floor(info.coin) }, result => {
if (result?.code === 1 && this.alive) this.toast((receipt.expired ? '到期补发 ' : '获得 ') + receipt.coins + ' 金币');
done(result);
});
}
private queueSettlement() {
if (this.panel) return;
HomePopupQueue.get(this.node).enqueue('coinMadnessSettlement', 54, done => {
if (!this.alive || this.busy) { done(); return; }
const mask = new cc.Node('CoinMadnessSettlement'); this.node.addChild(mask);
mask.setContentSize(this.node.getContentSize()); mask.zIndex = cc.macro.MAX_ZINDEX - 1;
mask.addComponent(cc.BlockInputEvents);
this.action(this.data.pending ? 'read' : 'settle', { periodId: this.data.state?.config.periodId }, result => {
mask.destroy(); done();
if (result?.code !== 1) { this.toast('活动奖励待同步,请稍后打开活动重试'); return; }
this.refresh();
});
});
}
close() {
if (this.busy && this.alive) return;
++this.requestId;
const wasLoading = this.loading; this.loading = false;
if (this.panel) { this.panel.destroy(); this.panel = null; }
const bundle = this.bundle; this.bundle = null;
if (bundle && !wasLoading) this.release(bundle);
const done = this.finishQueue; this.finishQueue = null; if (done) done();
}
private release(bundle: cc.AssetManager.Bundle) {
this.releasing = true;
cc.director.once(cc.Director.EVENT_AFTER_DRAW, () => {
bundle.releaseAll();
if (cc.assetManager.getBundle(BUNDLE) === bundle) cc.assetManager.removeBundle(bundle);
this.releasing = false;
CoinMadnessHost.occupied = false;
});
}
private toast(message: string) { if (this.alive) MiniGameSdk.API.showToast(message); }
onDestroy() {
this.alive = false; this.unscheduleAllCallbacks();
cc.game.off(cc.game.EVENT_SHOW, this.shown, this); this.close(); this.data = null;
if (this.entry) this.entry.destroy(); this.entry = null;
}
}