206 lines
6.6 KiB
TypeScript
206 lines
6.6 KiB
TypeScript
import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc';
|
||
import { HttpUtil } from './HttpUtil';
|
||
import { WxSDK } from './WxSDK';
|
||
import { API_ENDPOINTS, getShareJoinUrl, API_TIMEOUT } from '../config/ApiConfig';
|
||
import { ApiEnvelope, CreateShareData, JoinShareData, ReportProgressData, ShareLevelData } from '../types/ApiTypes';
|
||
import { RuntimeLevelConfig } from '../types/LevelTypes';
|
||
|
||
/**
|
||
* 分享管理器
|
||
* 负责创建分享、加入分享、缓存分享关卡数据
|
||
*/
|
||
export class ShareManager {
|
||
private static _instance: ShareManager | null = null;
|
||
|
||
/** 分享模式的关卡数据(null 表示正常模式) */
|
||
private _shareLevels: RuntimeLevelConfig[] | null = null;
|
||
|
||
/** API 返回的原始关卡数据(保留 imageUrl 用于懒加载) */
|
||
private _shareApiLevels: ShareLevelData[] = [];
|
||
|
||
private _shareTitle: string = '';
|
||
private _shareCode: string | null = null;
|
||
|
||
/** 图片缓存:URL -> SpriteFrame */
|
||
private _imageCache: Map<string, SpriteFrame> = new Map();
|
||
|
||
static get instance(): ShareManager {
|
||
if (!this._instance) {
|
||
this._instance = new ShareManager();
|
||
}
|
||
return this._instance;
|
||
}
|
||
|
||
private constructor() {}
|
||
|
||
get isShareMode(): boolean {
|
||
return this._shareLevels !== null && this._shareLevels.length > 0;
|
||
}
|
||
|
||
async createShare(title: string, levelIds: string[]): Promise<string | null> {
|
||
try {
|
||
const response = await HttpUtil.post<ApiEnvelope<CreateShareData>>(
|
||
API_ENDPOINTS.SHARE_CREATE,
|
||
{ title, levelIds },
|
||
API_TIMEOUT.DEFAULT,
|
||
);
|
||
|
||
if (!response.success || !response.data) {
|
||
console.error('[ShareManager] 创建分享失败:', response.message);
|
||
return null;
|
||
}
|
||
|
||
return response.data.shareCode;
|
||
} catch (err) {
|
||
console.error('[ShareManager] 创建分享异常:', err);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
async joinShare(code: string): Promise<boolean> {
|
||
try {
|
||
const response = await HttpUtil.post<ApiEnvelope<JoinShareData>>(
|
||
getShareJoinUrl(code),
|
||
{},
|
||
API_TIMEOUT.DEFAULT,
|
||
);
|
||
|
||
if (!response.success || !response.data) {
|
||
console.error('[ShareManager] 加入分享失败:', response.message);
|
||
return false;
|
||
}
|
||
|
||
const { shareCode, title, levels } = response.data;
|
||
this._shareCode = shareCode;
|
||
this._shareTitle = title;
|
||
this._shareApiLevels = levels;
|
||
|
||
const runtimeLevels: RuntimeLevelConfig[] = levels.map((level) => ({
|
||
id: level.id,
|
||
name: `第${level.level}关`,
|
||
spriteFrame: null,
|
||
clue1: level.hint1,
|
||
clue2: level.hint2,
|
||
clue3: level.hint3,
|
||
answer: level.answer,
|
||
}));
|
||
|
||
// 预加载首关图片
|
||
if (levels.length > 0) {
|
||
const sf = await this._loadImage(levels[0].imageUrl);
|
||
if (sf) {
|
||
runtimeLevels[0].spriteFrame = sf;
|
||
}
|
||
}
|
||
|
||
this._shareLevels = runtimeLevels;
|
||
console.log(`[ShareManager] 加入分享成功: ${title}, ${levels.length} 关`);
|
||
return true;
|
||
} catch (err) {
|
||
console.error('[ShareManager] 加入分享异常:', err);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async ensureShareLevelReady(index: number): Promise<RuntimeLevelConfig | null> {
|
||
if (!this._shareLevels || index < 0 || index >= this._shareLevels.length) {
|
||
return null;
|
||
}
|
||
|
||
const config = this._shareLevels[index];
|
||
if (config.spriteFrame) {
|
||
return config;
|
||
}
|
||
|
||
const apiLevel = this._shareApiLevels[index];
|
||
if (apiLevel?.imageUrl) {
|
||
const sf = await this._loadImage(apiLevel.imageUrl);
|
||
if (sf) {
|
||
config.spriteFrame = sf;
|
||
}
|
||
}
|
||
|
||
return config;
|
||
}
|
||
|
||
getShareLevelCount(): number {
|
||
return this._shareLevels?.length ?? 0;
|
||
}
|
||
|
||
/**
|
||
* 上报单关通关进度
|
||
* @param levelId 关卡 ID
|
||
* @param passed 是否通过
|
||
* @param timeSpent 用时(秒)
|
||
*/
|
||
async reportLevelProgress(
|
||
levelId: string,
|
||
passed: boolean,
|
||
timeSpent: number,
|
||
): Promise<ReportProgressData | null> {
|
||
if (!this._shareCode) {
|
||
console.warn('[ShareManager] reportLevelProgress: 无分享码,跳过上报');
|
||
return null;
|
||
}
|
||
|
||
try {
|
||
const response = await HttpUtil.post<ApiEnvelope<ReportProgressData>>(
|
||
API_ENDPOINTS.SHARE_PROGRESS,
|
||
{ shareCode: this._shareCode, levelId, passed, timeSpent },
|
||
API_TIMEOUT.DEFAULT,
|
||
);
|
||
|
||
if (!response.success || !response.data) {
|
||
console.error('[ShareManager] 上报进度失败:', response.message);
|
||
return null;
|
||
}
|
||
|
||
console.log(
|
||
`[ShareManager] 上报成功: passed=${response.data.passed}, withinTimeLimit=${response.data.withinTimeLimit}`,
|
||
);
|
||
return response.data;
|
||
} catch (err) {
|
||
console.error('[ShareManager] 上报进度异常:', err);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
triggerWxShare(title: string, shareCode: string): void {
|
||
WxSDK.shareAppMessage({
|
||
title: title || '来挑战我出的谐音梗吧!',
|
||
query: `shareCode=${shareCode}`,
|
||
});
|
||
}
|
||
|
||
clearShareMode(): void {
|
||
this._shareLevels = null;
|
||
this._shareApiLevels = [];
|
||
this._shareTitle = '';
|
||
this._shareCode = null;
|
||
this._imageCache.clear();
|
||
}
|
||
|
||
private _loadImage(url: string): Promise<SpriteFrame | null> {
|
||
const cached = this._imageCache.get(url);
|
||
if (cached) {
|
||
return Promise.resolve(cached);
|
||
}
|
||
|
||
return new Promise((resolve) => {
|
||
assetManager.loadRemote<ImageAsset>(url, (err, imageAsset) => {
|
||
if (err) {
|
||
console.error('[ShareManager] 加载图片失败:', url, err);
|
||
resolve(null);
|
||
return;
|
||
}
|
||
const texture = new Texture2D();
|
||
texture.image = imageAsset;
|
||
const spriteFrame = new SpriteFrame();
|
||
spriteFrame.texture = texture;
|
||
this._imageCache.set(url, spriteFrame);
|
||
resolve(spriteFrame);
|
||
});
|
||
});
|
||
}
|
||
}
|