消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

210 lines
7.1 KiB

import { MiniGamePlatform } from '../platform/MiniGamePlatform';
/**
* 激励视频广告(微信 / 抖音 / 快手 / B 站 / OPPO / QQ)。
*
* 官方要点:
* - 先 onLoad/onError/onClose,再 load/show(抖音)
* - 微信/快手:全局单例,show 失败时 load→show(微信文档)
* - B 站:show 前 reportScene(1007)
* - onClose 仅以 isEnded 判定奖励(快手);微信兼容 res===undefined
*/
export class RewardedVideoAd {
static keys: string[] = [];
private static _keyIndex = 0;
private static _errCount = 0;
private static _callback: Function | null = null;
/** 非单例平台当前实例 */
private static _ad: any = null;
private static _currentUnitId = '';
/** 微信/快手:adUnitId → 单例实例 */
private static _singletonAds = new Map<string, any>();
private static _eventsBound = new WeakSet<object>();
/** 防止 onClose 重复触发导致业务回调执行两次 */
private static _sessionFinished = false;
static init(keys: string[]) {
this.keys = keys ?? [];
MiniGamePlatform.init();
if (this.keys.length > 0 && MiniGamePlatform.pf?.createRewardedVideoAd) {
const ad = this._ensureAd(this.keys[0]);
ad?.load?.();
}
}
static show(callback?: Function) {
this._sessionFinished = false;
this._callback = callback ?? null;
this._errCount = 0;
const pf = MiniGamePlatform.pf;
if (!pf?.createRewardedVideoAd) {
this._logFail('AD:REWARD_NO_API');
this._finish(false);
return;
}
const adUnitId = this.keys[this._keyIndex];
if (!adUnitId) {
this._logFail('AD:REWARD_NO_UNIT');
this._finish(false);
return;
}
const ad = this._ensureAd(adUnitId);
if (!ad) {
this._logFail('AD:REWARD_CREATE_FAIL', adUnitId);
this._finish(false);
return;
}
this._ad = ad;
MiniGamePlatform.reportRewardedVideoScene();
this._showWithReload(ad);
}
/** 激励视频会话是否仍在等待 onClose(用于广告挂起态判断) */
static hasPendingCallback(): boolean {
return this._callback != null && !this._sessionFinished;
}
private static _ensureAd(adUnitId: string): any {
const pf = MiniGamePlatform.pf;
if (!pf?.createRewardedVideoAd) return null;
if (MiniGamePlatform.isRewardedVideoAdSingleton()) {
let ad = this._singletonAds.get(adUnitId);
if (ad) return ad;
ad = pf.createRewardedVideoAd({ adUnitId });
if (!ad) return null;
this._bindEvents(ad);
this._singletonAds.set(adUnitId, ad);
return ad;
}
if (this._ad && this._currentUnitId === adUnitId) {
return this._ad;
}
if (this._ad) {
this._destroyAd(this._ad);
this._ad = null;
}
const ad = pf.createRewardedVideoAd({ adUnitId });
if (!ad) return null;
this._currentUnitId = adUnitId;
this._bindEvents(ad);
ad.load?.();
return ad;
}
/** 官方推荐:先注册监听,再 load/show */
private static _bindEvents(ad: any): void {
if (this._eventsBound.has(ad)) return;
this._eventsBound.add(ad);
ad.onLoad?.(() => {
console.log('[RewardedVideoAd] onLoad');
});
ad.onError?.((err: unknown) => {
console.log('[RewardedVideoAd] onError', err);
this._logFail('AD:REWARD_ON_ERROR', err);
this._onLoadError();
});
ad.onClose?.((res?: { isEnded?: boolean }) => {
const ok = MiniGamePlatform.isRewardedVideoCompleted(res);
console.log('[RewardedVideoAd] onClose', res, 'completed=', ok);
this._finish(ok);
ad.load?.();
});
}
/** show 失败时 load→show(微信/快手/B站官方示例) */
private static _showWithReload(ad: any): void {
let showResult: { then?: Function; catch?: Function } | undefined;
try {
showResult = ad.show?.();
} catch (err) {
console.log('[RewardedVideoAd] show throw', err);
this._logFail('AD:REWARD_SHOW_THROW', err);
this._onLoadError();
return;
}
if (!showResult?.then) {
return;
}
showResult.catch((err: unknown) => {
console.log('[RewardedVideoAd] show fail, load then show', err);
this._logFail('AD:REWARD_SHOW_FAIL', err);
const loadResult = ad.load?.();
if (!loadResult?.then) {
ad.load?.();
return;
}
loadResult
.then(() => {
const retry = ad.show?.();
if (retry?.then) {
retry.catch((err2: unknown) => {
console.log('[RewardedVideoAd] load+show fail', err2);
this._logFail('AD:REWARD_LOAD_SHOW_FAIL', err2);
this._onLoadError();
});
}
})
.catch((err2: unknown) => {
console.log('[RewardedVideoAd] load+show fail', err2);
this._logFail('AD:REWARD_LOAD_FAIL', err2);
this._onLoadError();
});
});
}
private static _onLoadError(): void {
this._errCount++;
if (this._errCount >= 5 || this.keys.length === 0) {
this._finish(false);
return;
}
const oldKey = this.keys[this._keyIndex];
this._keyIndex = (this._keyIndex + 1) % this.keys.length;
const newKey = this.keys[this._keyIndex];
if (MiniGamePlatform.isRewardedVideoAdSingleton()) {
this._singletonAds.delete(oldKey);
} else if (this._ad) {
this._destroyAd(this._ad);
this._ad = null;
this._currentUnitId = '';
}
const ad = this._ensureAd(newKey);
if (!ad) {
this._logFail('AD:REWARD_SWITCH_FAIL', newKey);
this._finish(false);
return;
}
this._ad = ad;
this._showWithReload(ad);
}
private static _destroyAd(ad: any): void {
if (!ad?.destroy || MiniGamePlatform.isRewardedVideoAdSingleton()) return;
try {
ad.destroy();
} catch (e) {
console.warn('[RewardedVideoAd] destroy failed', e);
}
this._eventsBound.delete(ad);
}
private static _finish(success: boolean): void {
if (this._sessionFinished) return;
this._sessionFinished = true;
if (!success) {
this._logFail('AD:REWARD_FINISH_FAIL');
}
const cb = this._callback;
this._callback = null;
if (cb) cb(success);
}
private static _logFail(tag: string, detail?: unknown): void {
try {
gg.sdk?.pfLogKey?.(tag, detail);
} catch { /* ignore */ }
}
}