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.
1478 lines
61 KiB
1478 lines
61 KiB
import { Game, game, sys } from 'cc';
|
|
import { ConfigTableData, TableNames } from './ConfigTableData';
|
|
import { Singleton } from '../../mx/tools/Singleton';
|
|
import { ConfigProjectData as Project } from './ConfigProjectData';
|
|
import { DEBUG, EDITOR } from 'cc/env';
|
|
import { GEvent } from '../../mx/module/event/GEvent';
|
|
import MTools from '../../mx/tools/MTools';
|
|
import { sdkConfig } from '../sdk/sdkConfig';
|
|
import type { IItemSaveData } from './GameDataEnums';
|
|
import { ItemNumType, OtherDataType, StatisticsType, StatusType, TaskType } from './GameDataEnums';
|
|
import { DailyShopDataManager } from '../manager/DailyShopDataManager';
|
|
import { EmolumentDataManager } from '../manager/EmolumentDataManager';
|
|
import { DailyWinStats } from '../manager/DailyWinStats';
|
|
import { MatchWinStats } from '../manager/MatchWinStats';
|
|
|
|
export type { IItemSaveData } from './GameDataEnums';
|
|
export { ConstNumType, ItemNumType, OtherDataType, StatisticsType, StatusType, TaskType, HERO_DEFAULT_EQUIP_ID } from './GameDataEnums';
|
|
|
|
/**
|
|
* 游戏数据
|
|
*/
|
|
export class GameData extends Singleton {
|
|
|
|
/**是否首次登录(新玩家注册) */
|
|
get IsFirstLogin() { return this._IsFirstLogin; }
|
|
/**今天是否首次登录 */
|
|
get IsFirstLoginToday() { return this._IsFirstLoginToday; }
|
|
set IsFirstLoginToday(isFirst) { this._IsFirstLoginToday = isFirst }
|
|
/**登录天数 */
|
|
get LoginDays() { return this._LoginDays; }
|
|
/**离线时长(单位:秒) */
|
|
get OfflineTime() { return this._OfflineTime; }
|
|
/**本次登录在线时长(单位:秒) */
|
|
get OnlineTime() { return this._OnlineTime; }
|
|
/**当日累计在线时长(单位:秒) */
|
|
get OnlineTimeForToDay() { return this._OnlineTimeForToDay; }
|
|
/**所有累计在线时长(单位:秒) */
|
|
get OnlineTimeForAll() { return this._OnlineTimeForAll; }
|
|
|
|
/**当前登录时的日期 */
|
|
get CurrentLoginDate() { return this._CurrentLoginDate; }
|
|
|
|
private _IsFirstLogin = false;
|
|
private _IsFirstLoginToday = false;
|
|
private _LoginDays = 0;
|
|
private _OfflineTime = 0;
|
|
private _OnlineTime = 0;
|
|
private _OnlineTimeForToDay = 0;
|
|
private _OnlineTimeForAll = 0;
|
|
private _docName = "doc";
|
|
/**当前登录时的日期 */
|
|
private _CurrentLoginDate = -1;
|
|
|
|
|
|
/**项目配表数据 */
|
|
table: ConfigTableData = ConfigTableData.getInstance();
|
|
/**项目动态逻辑数据 */
|
|
project: Project = Project.getInstance();
|
|
|
|
/**初始化 */
|
|
init(docName, userData) {
|
|
// userData = '{"key":"","uid":"","playerTitleLevel":0,"dataVersion":14,"chapter":2,"level":1,"itemCount":["4_24","2_50","1_200","3_10","21_6","27_3","25_1","26_1","29_1"],"status":["2_2"],"statistics":["3_1766805850400","5_300","6_300","8_1766834348327","21_200","25_3","22_10","26_0","303_6","304_6","305_0","308_2","309_8","310_0","331_1","318_1766834643292"],"guide":[],"other":["37_","29_1&1&10&2&0|2&1&10&2&1|3&1&0&0&0|4&1&10&2&2|5&1&10&2&3|6&1&10&2&4|7&1&10&2&5|8&1&0&0&0|9&1&10&2&6|10&1&0&0&0|11&1&10&2&7|12&1&0&0&0|13&1&0&0&0|14&1&0&0&0|15&1&0&0&0|16&1&0&0&0|17&1&0&0&0|18&1&0&0&0|19&1&0&0&0","33_0&0&1","34_0&10&0&0&0&0&0","43_0&0&0&0&0","44_0&0&0&0&0","45_0&1&1&0&0&0&0&0&0&0&6&0&0&0","46_0&0&0&0&0&0&0&0&0&0&0&0&0&0","30_1|1#2|1","35_100&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0"],"onlyLocal":[]}'
|
|
|
|
if (userData && typeof userData === "object" && !Array.isArray(userData) && userData["key"] && userData["key"] != "") {
|
|
this._docName = userData["key"];
|
|
}
|
|
|
|
if (docName != "") {
|
|
this._docName = docName;
|
|
}
|
|
|
|
// SDK 可能在 init 前写入 uid,重建 Doc 时保留,避免被 initDoc 清掉
|
|
const prevUid = String((this.doc as Doc | undefined)?.uid ?? '').trim();
|
|
this.doc = new Doc();
|
|
this.initDoc();
|
|
if (prevUid) this.doc.uid = prevUid;
|
|
|
|
let LocalStr = sys.localStorage.getItem(this._docName);
|
|
|
|
////本地测试时,调取的存档字符串,记得发布时注释
|
|
//LocalStr = `{"key":"","uid":"","playerTitleLevel":0,"dataVersion":17996,"chapter":40,"level":1,"itemCount":["4_30","2_50","1_4944","3_3386","21_1387","25_1299","27_271","26_463","29_413","31_327","24_429","33_47","22_474","28_131","32_77","35_97","36_72","34_35","106_155","30_19","99_2","23_20","37_118","38_66","39_11","97_1"],"status":["2_2","5_0","4_1"],"statistics":["3_1768306060819","5_118","6_250284","8_1767995850105","21_1094834","25_106","22_27075","26_68","303_6","304_6","305_0","308_0","309_3","330_26","310_17","331_0","318_1767959448067","312_10","23_1089890","27_106","316_0","311_0","336_4","4_25","7_1768334223735","314_0","315_0","317_0","319_0","324_0","332_0","322_1","323_1","327_1","334_0","326_0","325_0","24_23689","28_67","306_21300","320_2","333_5","329_0","328_1","337_5","335_0"],"guide":[],"other":["37_","29_1&18&10&2&0|2&16&10&2&3|3&11&0&2&6|4&16&10&1&-1|5&18&10&1&-1|6&16&10&1&-1|7&28&10&1&-1|8&11&0&1&-1|9&16&10&1&-1|10&11&0&1&-1|11&16&10&1&-1|12&11&0&2&5|13&24&0&1&-1|14&11&0&2&1|15&11&0&2&7|16&11&0&2&4|17&10&0&1&-1|18&10&0&1&-1|19&12&0&2&2","33_2&0","34_280&530&930&4500&7&3&0","43_0&0&0&0&0","44_0&0&0&0&0","45_0&0&0&0&0&0&0&0&0&0&0&0&0&0","46_0&0&0&0&0&0&0&0&0&0&0&0&0&0","30_1|1#2|1#3|3#4|2#5|6#6|6#7|6#8|4#9|8#10|6#11|3#12|11#13|8#14|12#15|4#16|23#17|6#18|10#19|10#10021|6#10022|1#10023|1#10024|1#10025|22#10001|4#10011|270#10012|2#10013|10#20|13#21|10#22|33#10002|1#1001|1#2001|1#1002|1#2002|1#1003|1#2003|1#1004|1#2004|1#23|13#1005|1#24|22#25|24#26|16#27|24#64|1#59|1#65|1#66|1#69|1#41|2#33|9#45|1#30|31#32|83#28|5#29|4#31|37#34|37#35|14#36|14#37|8#38|13#39|36#40|67","35_100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#98&1&1&0#32&1&0&0#0&0&0&0#0&0&0&0#0&0&0&0#34&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#40&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#31&0&0&0#58&0&0&0#45&0&0&0#0&0&0&0#0&0&0&0#32&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0","40_100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#73&1&1&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0","41_100&1&1&1#100&1&1&1#100&1&1&1#100&1&1&1#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0#0&0&0&0","32_1|10|0&24|7|1&9|10|1&8|10|1&69|5|1&55|9|1","31_3|2#5|3#6|2#7|3#8|2#9|7#10|5#11|2#12|9#13|6#14|8#15|2#16|18#17|3#18|8#19|9#10025|9#10001|3#10011|231#10013|5#20|12#21|7#22|29#23|9#1005|1#24|19#25|19#26|13#27|23#64|1#59|1#65|1#66|1#69|1#41|2#45|1#30|29#32|71#28|4#29|3#31|33#33|7#34|36#35|13#36|13#37|7#38|12#39|34#40|66","36_5&2|16&2","38_,1001,1002,1003,1004","39_,2001,2002,2003,2004","50_wuqi|2#daoju|2#wujin|2#baoxiang|2"],"onlyLocal":[],"failCount":0,"lastFailCount":0}`
|
|
|
|
let local: Record<string, unknown> | null = null;
|
|
if (LocalStr) {
|
|
try {
|
|
const parsed = JSON.parse(LocalStr);
|
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
local = parsed as Record<string, unknown>;
|
|
}
|
|
} catch (parseErr) {
|
|
console.error('[GameData.init] 本地存档 JSON 解析失败', parseErr);
|
|
local = null;
|
|
}
|
|
}
|
|
|
|
let myRankId = local ? Math.floor(Number(local["rankId"]) || 0) : 0;
|
|
let myDataVersion = local ? Math.floor(Number(local["dataVersion"]) || 0) : 0;
|
|
let sd = null
|
|
if (userData != null && userData !== "") {
|
|
if (typeof userData === "string") {
|
|
try {
|
|
sd = JSON.parse(userData);
|
|
} catch (parseErr) {
|
|
console.error("[GameData.init] 存档 JSON 解析失败", parseErr);
|
|
sd = null;
|
|
}
|
|
} else if (typeof userData === "object") {
|
|
sd = userData as any;
|
|
}
|
|
if (sd) {
|
|
const serverRankId = Math.floor(Number(sd["rankId"]) || 0);
|
|
const serverDataVersion = Math.floor(Number(sd["dataVersion"]) || 0);
|
|
// 云端段位更低,或段位相同但云端更旧 → 用本地,避免回档
|
|
if (serverRankId < myRankId
|
|
|| (serverRankId === myRankId && serverDataVersion < myDataVersion)) {
|
|
sd = null;
|
|
gg.sdk.reportDY("inLevel", `存档异常-使用本地数据${myRankId}|${serverRankId}`);
|
|
let str = typeof userData === "string" ? userData : JSON.stringify(userData);
|
|
if (str.length > 350) {
|
|
str = str.substring(0, 350);
|
|
}
|
|
gg.sdk.reportDY("inLevel", `存档异常-远端数据|${str}`);
|
|
|
|
let str2 = LocalStr;
|
|
if (LocalStr && LocalStr.length > 350) {
|
|
str2 = LocalStr.substring(0, 350);
|
|
}
|
|
gg.sdk.reportDY("inLevel", `存档异常-本地数据|${str2}`);
|
|
}
|
|
}
|
|
}
|
|
if (sd) {
|
|
for (let key in this.doc) {
|
|
if (Object.prototype.hasOwnProperty.call(sd, key) && sd[key] !== undefined) {
|
|
this.doc[key] = sd[key];
|
|
} else if (local && Object.prototype.hasOwnProperty.call(local, key) && local[key] !== undefined) {
|
|
// 云端缺字段(旧版上传)时保留本地,避免 userProvince 等被清空
|
|
this.doc[key] = local[key];
|
|
}
|
|
}
|
|
|
|
//上报数据
|
|
//恢复仅存本地的数据
|
|
this.doc.onlyLocal = Array.isArray(local?.onlyLocal)
|
|
? (local.onlyLocal as string[]).slice()
|
|
: [];
|
|
let _IsFirstLogin = this.getStatus(StatusType.IsFirstLogin) == 0
|
|
gg.sdk.reportDY("inLevel", `
|
|
-IsFirstLogin=${_IsFirstLogin}`);
|
|
|
|
} else if (local) {
|
|
for (let key in this.doc) {
|
|
if (Object.prototype.hasOwnProperty.call(local, key) && local[key] !== undefined)
|
|
this.doc[key] = local[key];
|
|
}
|
|
//上报数据
|
|
let _IsFirstLogin = this.getStatus(StatusType.IsFirstLogin) == 0
|
|
gg.sdk.reportDY("inLevel", `首次登录本地-IsFirstLogin=${_IsFirstLogin}`);
|
|
}
|
|
|
|
this._sanitizeDocAfterLoad();
|
|
if (!this.doc.uid && prevUid) this.doc.uid = prevUid;
|
|
|
|
this.project.repairRankOnDocLoad();
|
|
//首次登录
|
|
this._IsFirstLogin = this.getStatus(StatusType.IsFirstLogin) == 0
|
|
|
|
this.setStatus(StatusType.IsFirstLogin, 2);
|
|
|
|
//今天首次登录
|
|
this.checkIsFirstLoginToday();
|
|
|
|
// 每日商店:登录时若已过凌晨则重置(对局中过零点不重置)
|
|
DailyShopDataManager.applyDayResetIfNeeded();
|
|
|
|
//每天首次登录初始化逻辑
|
|
if (this._IsFirstLoginToday) {
|
|
this.onFirstLoginToday();
|
|
}
|
|
// this.saveToServer();
|
|
//开始计时
|
|
this.startTimer();
|
|
|
|
this.setStatistics(StatisticsType.LastLoginTime, this.getCurentTime2());
|
|
|
|
game.on(Game.EVENT_HIDE, this.onHide, this);
|
|
sdkConfig.isInitGameData = true;
|
|
}
|
|
|
|
onFirstLoginToday() {
|
|
this._LoginDays++;
|
|
this.setStatistics(StatisticsType.LoginDays, this._LoginDays);
|
|
this._OnlineTimeForToDay = 0;
|
|
|
|
if (!this.IsFirstLogin)
|
|
gg.data.setStatistics(StatisticsType.FailContinuousCountReset, 5)
|
|
gg.data.setStatistics(StatisticsType.SuccessContinuousCount, 0)
|
|
this.setStatistics(StatisticsType.ShopBuyGoldAdCount, 6)
|
|
this.setStatistics(StatisticsType.ShopBuyItemAdCount, 6)
|
|
this.setStatistics(StatisticsType.FreeBoxCount, 0)
|
|
this.setStatistics(StatisticsType.FirstLoginWeight, 0)
|
|
this.setStatistics(StatisticsType.TodayAdCount, 0)
|
|
this.setStatus(StatusType.DayGiftGetFinish, 0);
|
|
this.setStatus(StatusType.DeskGiftGetFinish, 0);
|
|
|
|
if (this.getStatistics(StatisticsType.GreatGiftStatus) == 1) {
|
|
gg.data.setStatistics(StatisticsType.GreatGiftStatus, 0)
|
|
let day = this.getStatistics(StatisticsType.GreatGiftGetDay)
|
|
gg.data.setStatistics(StatisticsType.GreatGiftGetDay, day + 1)
|
|
}
|
|
|
|
// let greatGiftGetDay = this.getStatistics(StatisticsType.GreatGiftGetDay)
|
|
// gg.data.setStatistics(StatisticsType.GreatGiftGetDay, greatGiftGetDay + 1)
|
|
//签到数据更新//
|
|
let signData = this.getStringData(OtherDataType.SignData)
|
|
if (signData) {
|
|
let [day, status] = signData.split('&');
|
|
let curDay = Number(day);
|
|
let curStatus = Number(status);
|
|
if (curStatus > 0) {
|
|
curDay++;
|
|
if (curDay >= 14) {
|
|
curDay = 0
|
|
}
|
|
curStatus = 0;
|
|
}
|
|
this.setStringData(OtherDataType.SignData, curDay.toString() + '&' + curStatus.toString())
|
|
}
|
|
|
|
this.setStatistics(StatisticsType.buyPowerAdCount, 0)
|
|
this.setStatistics(StatisticsType.buyPowerMoneyCount, 0)
|
|
|
|
this.setStatistics(StatisticsType.saodangNormalCount, 0)
|
|
this.setStatistics(StatisticsType.saodangAdCount, 0)
|
|
|
|
this.setStatistics(StatisticsType.ShopDiamondShareCount, 1)
|
|
this.setStatistics(StatisticsType.SuccessShareGetRewardCount, 1)
|
|
this.setStatistics(StatisticsType.BuyGoldShareCount, 1)
|
|
|
|
//每日任务状态和进度重置
|
|
this.setStatistics(StatisticsType.DayActiveCount, 0)
|
|
|
|
//重置副本进入次数,扫荡次数
|
|
this.resetReplicaEnterAndSweepCountsToday();
|
|
|
|
gg.data.setStringData(OtherDataType.DayTaskRewardGetData, '0&0&0&0&0')
|
|
gg.data.setStringData(OtherDataType.DayTaskProgressData, '0&0&0&0&0&0&0&0&0&0&0&0&0&0')
|
|
gg.data.setStringData(OtherDataType.DayActiveGetRewardData, '0&0&0&0&0&0&0&0&0&0&0&0&0&0')
|
|
gg.data.setkeyKeyValueDayClear(OtherDataType.FreeUpgradeZombieFactory) //重置免费升级僵尸工厂次数重置
|
|
//重置副本扫荡次数
|
|
|
|
gg.data.getShopItemList()
|
|
DailyShopDataManager.onCrossDay();
|
|
DailyWinStats.onCrossDay();
|
|
EmolumentDataManager.onCrossDay();
|
|
MatchWinStats.onCrossDay();
|
|
// gg.data.setStatistics(StatisticsType.PropModelLevel, 0);
|
|
// gg.data.setStatistics(StatisticsType.BoxModelLevel, 0);
|
|
// //抽卡挑战模式挑战等级
|
|
// gg.data.setStatistics(StatisticsType.GachaChallengeModeLevel, 0);
|
|
|
|
GEvent.Ins.emit(GEvent.CrossDay);
|
|
}
|
|
|
|
/** Replica 表已移除 */
|
|
resetReplicaEnterAndSweepCountsToday(): void {
|
|
}
|
|
|
|
onHide() {
|
|
if (gg.game.isInitAllData) {
|
|
this.saveLocal();
|
|
this.saveToServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 重置游戏数据
|
|
*/
|
|
reset(callback: Function, tagStr: string = '') {
|
|
|
|
this.clearServerData((res) => {
|
|
if (res) {
|
|
//白名单不能清除
|
|
let whiteUseruuid = this.getStringData(OtherDataType.BaiMingTianUUIid);
|
|
this._IsFirstLogin = true;
|
|
this._IsFirstLoginToday = true;
|
|
this._LoginDays = 1;
|
|
this._OfflineTime = 0;
|
|
this._OnlineTime = 0;
|
|
this._OnlineTimeForToDay = 0;
|
|
this._OnlineTimeForAll = 0;
|
|
sys.localStorage.clear();
|
|
|
|
// 必须新建 Doc:initDoc 不会清 userName/userProvince/rank* 等字段
|
|
this.doc = new Doc();
|
|
this.initDoc();
|
|
DailyShopDataManager.clearForReset();
|
|
// this.saveLocal();
|
|
this.setStringData(OtherDataType.BaiMingTianUUIid, whiteUseruuid);
|
|
this.saveLocal();
|
|
callback && callback(true);
|
|
} else {
|
|
|
|
callback && callback(false);
|
|
}
|
|
}, tagStr);
|
|
|
|
|
|
}
|
|
|
|
private getSaveData() {
|
|
let result = "";
|
|
let temp = this.deepClone(this.doc);
|
|
temp.onlyLocal = []
|
|
// 昵称统一 Base64 后再上传,避免特殊字符破坏服务器存档;已是 Base64 不再处理(仅处理上传包,内存 doc 不动)
|
|
if (typeof temp.userName === 'string' && temp.userName && !this._isBase64Text(temp.userName)) {
|
|
temp.userName = this._utf8ToBase64(temp.userName);
|
|
}
|
|
result = JSON.stringify(temp);
|
|
temp = null;
|
|
return result;
|
|
}
|
|
|
|
/** 昵称是否已是 Base64(字符集+长度 4 的倍数;解出控制字符/替换符按明文处理) */
|
|
private _isBase64Text(s: string): boolean {
|
|
if (!s || s.length % 4 !== 0) return false;
|
|
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(s)) return false;
|
|
try {
|
|
const bin = this._base64Decode(s);
|
|
if (!bin) return false;
|
|
for (let i = 0; i < bin.length; i++) {
|
|
const c = bin.charCodeAt(i);
|
|
if (c < 32 && c !== 9 && c !== 10 && c !== 13) return false;
|
|
if (c === 127) return false;
|
|
}
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** Base64 → latin1 字符串(不依赖 atob,小游戏环境兜底);非法输入返回空串 */
|
|
private _base64Decode(b64: string): string {
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
const lookup: Record<string, number> = {};
|
|
for (let i = 0; i < chars.length; i++) lookup[chars.charAt(i)] = i;
|
|
const clean = b64.replace(/=+$/, '');
|
|
let out = '';
|
|
let buffer = 0;
|
|
let bits = 0;
|
|
for (let i = 0; i < clean.length; i++) {
|
|
const v = lookup[clean.charAt(i)];
|
|
if (v === undefined) return '';
|
|
buffer = (buffer << 6) | v;
|
|
bits += 6;
|
|
if (bits >= 8) {
|
|
bits -= 8;
|
|
out += String.fromCharCode((buffer >> bits) & 255);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** UTF-8 → Base64(无 btoa 环境走手写实现;编码失败返回原文避免丢昵称) */
|
|
private _utf8ToBase64(s: string): string {
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
const bytes: number[] = [];
|
|
for (let i = 0; i < s.length; i++) {
|
|
let code = s.codePointAt(i) ?? 0;
|
|
if (code > 0xffff) i++; // 跳过代理对低位
|
|
if (code < 0x80) {
|
|
bytes.push(code);
|
|
} else if (code < 0x800) {
|
|
bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
|
|
} else if (code < 0x10000) {
|
|
bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
|
|
} else {
|
|
bytes.push(
|
|
0xf0 | (code >> 18),
|
|
0x80 | ((code >> 12) & 0x3f),
|
|
0x80 | ((code >> 6) & 0x3f),
|
|
0x80 | (code & 0x3f),
|
|
);
|
|
}
|
|
}
|
|
let out = '';
|
|
for (let i = 0; i < bytes.length; i += 3) {
|
|
const b1 = bytes[i];
|
|
const b2 = i + 1 < bytes.length ? bytes[i + 1] : 0;
|
|
const b3 = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
out += chars[b1 >> 2];
|
|
out += chars[((b1 & 3) << 4) | (b2 >> 4)];
|
|
out += i + 1 < bytes.length ? chars[((b2 & 15) << 2) | (b3 >> 6)] : '=';
|
|
out += i + 2 < bytes.length ? chars[b3 & 63] : '=';
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** 读档后把数组/关键字段规范成安全类型,避免脏档导致运行时崩溃 */
|
|
private _sanitizeDocAfterLoad(): void {
|
|
const d = this.doc as unknown as Record<string, unknown>;
|
|
const arrKeys = ['itemCount', 'status', 'statistics', 'guide', 'other', 'onlyLocal'];
|
|
for (const k of arrKeys) {
|
|
const v = d[k];
|
|
if (!Array.isArray(v)) {
|
|
d[k] = [];
|
|
} else {
|
|
d[k] = (v as unknown[]).filter((x) => typeof x === 'string');
|
|
}
|
|
}
|
|
const strKeys = [
|
|
'key', 'uid', 'userName', 'userAvatar', 'userProvince',
|
|
'matchWinStatsDate', 'dailyWinsDate', 'rankLastSyncDailyWinsDate',
|
|
'rankLastSyncProvince', 'rankLastSyncNick', 'rankLastSyncAvatar', 'rankLastSyncRegion',
|
|
'emolumentSnapDate',
|
|
];
|
|
for (const k of strKeys) {
|
|
if (d[k] == null) d[k] = '';
|
|
else if (typeof d[k] !== 'string') d[k] = String(d[k]);
|
|
}
|
|
const numKeys = [
|
|
'roleId', 'playerTitleLevel', 'dataVersion', 'rankId', 'rankStars',
|
|
'matchWinCount', 'matchLoseCount', 'totalBattleCount',
|
|
'matchWinStreak', 'matchLoseStreak', 'dailyWins', 'yesterdayWins',
|
|
'rankLastSyncDailyWins', 'rankUserAuthPrompted', 'rankSyncedOnce',
|
|
'rankLastSyncStars', 'emolumentSnapStars', 'emolumentSnapWins',
|
|
'gmAwaitEmolumentPop', 'headIconId', 'battleIllustratedGuideClicked',
|
|
'level', 'failCount', 'lastFailCount',
|
|
];
|
|
for (const k of numKeys) {
|
|
const n = Number(d[k]);
|
|
d[k] = Number.isFinite(n) ? n : 0;
|
|
}
|
|
if (!(this.doc.rankLastSyncDailyWins >= 0)) this.doc.rankLastSyncDailyWins = -1;
|
|
if (!(this.doc.rankLastSyncStars >= 0)) this.doc.rankLastSyncStars = -1;
|
|
if (!(this.doc.rankId > 0)) this.doc.rankId = 1;
|
|
if (!(this.doc.level > 0)) this.doc.level = 1;
|
|
}
|
|
|
|
/**保存本地 */
|
|
saveLocal() {
|
|
this.setStatistics(StatisticsType.TodayOnLineTime, this._OnlineTimeForToDay);
|
|
this.setStatistics(StatisticsType.AllOnLineTime, this._OnlineTimeForAll);
|
|
this.doc.dataVersion++;
|
|
sys.localStorage.setItem(this._docName, JSON.stringify(this.doc));
|
|
}
|
|
|
|
/**保存到服务器(部分必要数据备份)
|
|
* @param callback 保存成功回调
|
|
* @param bForceSave 是否强制保存,默认false
|
|
*/
|
|
saveToServer(callback?: Function, bForceSave: boolean = false) {
|
|
this.saveLocal();
|
|
if (DEBUG || EDITOR || sys.isBrowser) {
|
|
if (callback) callback(false);
|
|
return;
|
|
}
|
|
//1秒内不重复保存(强制保存除外);被节流时延迟补传并保留 callback
|
|
if (MTools.beforeTimes(1000, "saveToServer") && !bForceSave) {
|
|
if (callback) this._pendingServerCallbacks.push(callback);
|
|
if (this._pendingServerTimer == null) {
|
|
this._pendingServerTimer = setTimeout(() => {
|
|
this._pendingServerTimer = null;
|
|
const cbs = this._pendingServerCallbacks.splice(0, this._pendingServerCallbacks.length);
|
|
this.saveToServer((ok: boolean) => {
|
|
for (let i = 0; i < cbs.length; i++) cbs[i]?.(ok);
|
|
}, false);
|
|
}, 1100);
|
|
}
|
|
return;
|
|
}
|
|
let d = this.getSaveData();
|
|
|
|
if (bForceSave) {
|
|
GEvent.Ins.emit(GEvent.SHOW_NET_UI);
|
|
}
|
|
ZYSDK.ZYSDK.setGameData(d, this.doc.rankId + "", this.OnlineTimeForAll, "", "", "", () => {
|
|
GEvent.Ins.emit(GEvent.HIDE_NET_UI);
|
|
this._forceSaveRetry = 0;
|
|
if (this.doc.failCount != this.doc.lastFailCount) {
|
|
gg.sdk.reportDY("inLevel", `存档失败次数-${this.doc.lastFailCount}`);
|
|
}
|
|
this.doc.failCount = this.doc.lastFailCount;
|
|
if (callback) callback(true);
|
|
}, () => {
|
|
this.doc.lastFailCount++;
|
|
if (bForceSave) {
|
|
gg.ui.showToast("通关数据存档中");
|
|
this._forceSaveRetry++;
|
|
// 有限次重试;callback 只在最终结果回调一次
|
|
if (this._forceSaveRetry <= 3) {
|
|
const retry = this._forceSaveRetry;
|
|
setTimeout(() => {
|
|
this.saveToServer(callback, true);
|
|
}, 800 * retry);
|
|
return;
|
|
}
|
|
this._forceSaveRetry = 0;
|
|
}
|
|
GEvent.Ins.emit(GEvent.HIDE_NET_UI);
|
|
if (callback) callback(false);
|
|
});
|
|
}
|
|
|
|
private _pendingServerCallbacks: Function[] = [];
|
|
private _pendingServerTimer: ReturnType<typeof setTimeout> | null = null;
|
|
private _forceSaveRetry = 0;
|
|
|
|
clearServerData(callback?: Function, tagStr: string = '') {
|
|
if (EDITOR || DEBUG || sys.isBrowser) {
|
|
if (callback) callback(true);
|
|
return;
|
|
}
|
|
gg.sdk.reportDY("inLevel", `存档重置-${tagStr}`);
|
|
ZYSDK.ZYSDK.setGameData("", "", 0, "", "", "", () => {
|
|
if (callback) callback(true);
|
|
}, () => {
|
|
if (callback) callback(false);
|
|
});
|
|
}
|
|
|
|
/**获取当前时间 */
|
|
getCurentTime() {
|
|
return new Date().getTime() + 8 * 60 * 60 * 1000;//转换本地时间+8小时
|
|
}
|
|
getCurentTime2() {
|
|
return new Date().getTime()//转换本地时间+8小时
|
|
}
|
|
|
|
/**获取当天剩余时间 */
|
|
getTodayResidualTime() {
|
|
let dayTime = 24 * 60 * 60 * 1000;
|
|
let curentTime = this.getCurentTime();
|
|
return dayTime - (curentTime % dayTime);
|
|
}
|
|
|
|
isSameWeek(date1, date2) {
|
|
// 设置周起始日为周一(1表示周一,0表示周日)
|
|
const weekStartDay = 1; // 可以根据需要调整为0(周日)
|
|
|
|
// 计算两个日期是星期几(0-6,0表示周日)
|
|
const dayOfWeek1 = date1.getDay();
|
|
const dayOfWeek2 = date2.getDay();
|
|
|
|
// 计算距离周起始日的天数差
|
|
const diff1 = dayOfWeek1 >= weekStartDay ? dayOfWeek1 - weekStartDay : 7 - (weekStartDay - dayOfWeek1);
|
|
const diff2 = dayOfWeek2 >= weekStartDay ? dayOfWeek2 - weekStartDay : 7 - (weekStartDay - dayOfWeek2);
|
|
|
|
// 设置为周起始日的日期
|
|
date1.setDate(date1.getDate() - diff1);
|
|
date2.setDate(date2.getDate() - diff2);
|
|
|
|
// 重置时间部分,只比较日期
|
|
date1.setHours(0, 0, 0, 0);
|
|
date2.setHours(0, 0, 0, 0);
|
|
|
|
// 比较两个周起始日是否相同
|
|
return date1.getTime() === date2.getTime();
|
|
}
|
|
/**
|
|
* 验证今天是否首次登录
|
|
*/
|
|
private checkIsFirstLoginToday() {
|
|
let lastLoginTime = this.getStatistics(StatisticsType.LastLoginTime);
|
|
let currentTime = this.getCurentTime2();
|
|
//判断是不是同一天
|
|
const lastDate = new Date(lastLoginTime);
|
|
const currentDate = new Date(currentTime); //已经转换为北京本地时间+8小时
|
|
|
|
// 检查是否是同一天(年、月、日相同)
|
|
const isSameDay =
|
|
lastDate.getFullYear() === currentDate.getFullYear() &&
|
|
lastDate.getMonth() === currentDate.getMonth() &&
|
|
lastDate.getDate() === currentDate.getDate();
|
|
|
|
this._IsFirstLoginToday = !isSameDay;
|
|
|
|
//获取是不是同一周
|
|
let isWeek = this.isSameWeek(lastDate, currentDate)
|
|
|
|
if (!isWeek) {
|
|
gg.data.setStringData(OtherDataType.TaskRewardGetData, '0&0&0&0&0')
|
|
//WeekActiveCount
|
|
this.setStatistics(StatisticsType.WeekActiveCount, 0);
|
|
}
|
|
|
|
|
|
this._LoginDays = this.getStatistics(StatisticsType.LoginDays);
|
|
this._OnlineTimeForAll = this.getStatistics(StatisticsType.AllOnLineTime);
|
|
this._OnlineTimeForToDay = this.getStatistics(StatisticsType.TodayOnLineTime);
|
|
|
|
//检测是不是第二周
|
|
//TaskRewardGetData是不是同一周
|
|
|
|
this._CurrentLoginDate = new Date().getDay();
|
|
//gg.data.setStringData(OtherDataType.TaskRewardGetData,'0&0&0&0&0')
|
|
}
|
|
|
|
/**验证跨天 */
|
|
public checkIsCrossDay() {
|
|
let currentDay = new Date().getDay();
|
|
if (currentDay != this._CurrentLoginDate) {
|
|
//跨天了
|
|
this._CurrentLoginDate = currentDay;
|
|
this._IsFirstLoginToday = true;
|
|
this._OnlineTimeForToDay = 0;
|
|
this._LoginDays++;
|
|
this.setStatistics(StatisticsType.LoginDays, this._LoginDays);
|
|
this.setStatistics(StatisticsType.TodayFirstLoginTime, this.getCurentTime());
|
|
this.onFirstLoginToday();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始计时
|
|
*/
|
|
private startTimer() {
|
|
this._OnlineTime = 1;
|
|
// 登录立刻结算离线期间应得的体力
|
|
this.project.autoAddVigour();
|
|
if (!this._timer_flag) {
|
|
this._timer_flag = setInterval(() => {
|
|
this._OnlineTime++;
|
|
this._OnlineTimeForToDay++;
|
|
this._OnlineTimeForAll++;
|
|
this.project.autoAddVigour();
|
|
|
|
if (this._OnlineTime % 30 == 0) {
|
|
this.saveLocal();
|
|
this.saveToServer();
|
|
}
|
|
}, 1000)
|
|
}
|
|
}
|
|
private _timer_flag = null;
|
|
|
|
//#region 存档数据
|
|
/**存档数据对象 */
|
|
doc: Doc = new Doc();
|
|
|
|
/**获取道具数量 */
|
|
getItemNum(itmeId) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.itemCount, itmeId)
|
|
return v == "" ? 0 : Number(v);
|
|
}
|
|
/**设置道具数量(警告:设置会覆盖道具的数量,请谨慎使用) */
|
|
setItemNum(itmeId, num: number) {
|
|
if (isNaN(num)) {
|
|
console.warn('[GameData] setItemNum NaN', itmeId, num);
|
|
return
|
|
}
|
|
this.reportItemNumChange(itmeId, num);
|
|
|
|
this.setKeyValueArray(this.doc.itemCount, itmeId, num);
|
|
GEvent.Ins.emit(GEvent.UpdateItemNum, itmeId);
|
|
}
|
|
/**数量变动打点 */
|
|
reportItemNumChange(itmeId, num: number) {
|
|
let v = this.getItemNum(itmeId);
|
|
if (itmeId == ItemNumType.money) {
|
|
let pArr = [50]
|
|
for (let i = 100; i < 500; i += 50) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 500; i < 2000; i += 100) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 2000; i < 5000; i += 200) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 5000; i < 10000; i += 500) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 10000; i < 100000; i += 5000) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 100000; i < 500000; i += 10000) {
|
|
pArr.push(i);
|
|
}
|
|
if (num > v) {
|
|
//增加
|
|
this.addStatistics(StatisticsType.AccumulateGetCoin, num - v);
|
|
let localIndex = this.getStatistics(StatisticsType.ReportIndexAccGetCoin);
|
|
let money = this.getStatistics(StatisticsType.AccumulateGetCoin);
|
|
for (let i = 0; i < pArr.length; i++) {
|
|
if (money >= pArr[i] && localIndex < i) {
|
|
localIndex = i;
|
|
gg.sdk.reportDY("inLevel", `金币获取-累积${pArr[i]}`)
|
|
}
|
|
}
|
|
this.setStatistics(StatisticsType.ReportIndexAccGetCoin, localIndex);
|
|
} else if (num < v) {
|
|
//减少
|
|
this.addStatistics(StatisticsType.AccumulateWasteCoin, v - num);
|
|
let localIndex = this.getStatistics(StatisticsType.ReportIndexAccWasteCoin);
|
|
let money = this.getStatistics(StatisticsType.AccumulateWasteCoin);
|
|
for (let i = 0; i < pArr.length; i++) {
|
|
if (money >= pArr[i] && localIndex < i) {
|
|
localIndex = i;
|
|
gg.sdk.reportDY("inLevel", `金币消耗-累积${pArr[i]}`)
|
|
}
|
|
}
|
|
this.setStatistics(StatisticsType.ReportIndexAccWasteCoin, localIndex);
|
|
}
|
|
} else if (itmeId == ItemNumType.diamond) {
|
|
let pArr = [10]
|
|
for (let i = 20; i < 500; i += 20) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 500; i < 2000; i += 100) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 2000; i < 5000; i += 200) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 5000; i < 10000; i += 500) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 10000; i < 100000; i += 5000) {
|
|
pArr.push(i);
|
|
}
|
|
for (let i = 100000; i < 500000; i += 10000) {
|
|
pArr.push(i);
|
|
}
|
|
if (num > v) {
|
|
//增加
|
|
this.addStatistics(StatisticsType.AccumulateGetDiamond, num - v);
|
|
let localIndex = this.getStatistics(StatisticsType.ReportIndexAccGetDiamond);
|
|
let diamond = this.getStatistics(StatisticsType.AccumulateGetDiamond);
|
|
for (let i = 0; i < pArr.length; i++) {
|
|
if (diamond >= pArr[i] && localIndex < i) {
|
|
localIndex = i;
|
|
gg.sdk.reportDY("inLevel", `钻石获取-累积${pArr[i]}`)
|
|
}
|
|
}
|
|
this.setStatistics(StatisticsType.ReportIndexAccGetDiamond, localIndex);
|
|
} else if (num < v) {
|
|
//减少
|
|
this.addStatistics(StatisticsType.AccumulateWasteDiamond, v - num);
|
|
let localIndex = this.getStatistics(StatisticsType.ReportIndexAccWasteDiamond);
|
|
let diamond = this.getStatistics(StatisticsType.AccumulateWasteDiamond);
|
|
for (let i = 0; i < pArr.length; i++) {
|
|
if (diamond >= pArr[i] && localIndex < i) {
|
|
localIndex = i;
|
|
gg.sdk.reportDY("inLevel", `钻石消耗-累积${pArr[i]}`)
|
|
}
|
|
}
|
|
this.setStatistics(StatisticsType.ReportIndexAccWasteDiamond, localIndex);
|
|
}
|
|
}
|
|
}
|
|
/**添加道具数量 */
|
|
addItemNum(itmeId, num: number) {
|
|
let v = this.getItemNum(itmeId);
|
|
this.setItemNum(itmeId, v + num);
|
|
}
|
|
/**减少道具数量 */
|
|
subItemNum(itmeId, num: number) {
|
|
|
|
let v = this.getItemNum(itmeId);
|
|
let count = v - num;
|
|
if (count < 0) {
|
|
count = 0;
|
|
}
|
|
this.setItemNum(itmeId, count);
|
|
}
|
|
/**获取所有道具 */
|
|
getAllItemNum(): IItemSaveData[] {
|
|
let arr: IItemSaveData[] = [];
|
|
for (let i = 0; i < this.doc.itemCount.length; i++) {
|
|
let data = this.doc.itemCount[i].split("_");
|
|
arr.push({ id: Number(data[0]), count: Number(data[1]) });
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
/**获取当前金钱 */
|
|
getMoney() {
|
|
return this.getItemNum(ItemNumType.money);
|
|
}
|
|
/**设置金钱数量 */
|
|
setMoney(num: number) {
|
|
this.setItemNum(ItemNumType.money, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateMoney);
|
|
}
|
|
/**添加金钱数量 */
|
|
addMoney(num: number) {
|
|
this.addItemNum(ItemNumType.money, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateMoney);
|
|
}
|
|
/**减少金钱数量 */
|
|
subMoney(num: number) {
|
|
this.subItemNum(ItemNumType.money, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateMoney);
|
|
}
|
|
/**获取当前战力 */
|
|
getCombatPower() {
|
|
return this.getItemNum(ItemNumType.combatPower);
|
|
}
|
|
/**设置战力数量 */
|
|
setCombatPower(num: number) {
|
|
this.setItemNum(ItemNumType.combatPower, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateCombatPower);
|
|
}
|
|
/**添加战力数量 */
|
|
addCombatPower(num: number) {
|
|
this.addItemNum(ItemNumType.combatPower, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateCombatPower);
|
|
}
|
|
/**减少战力数量 */
|
|
subCombatPower(num: number) {
|
|
this.subItemNum(ItemNumType.combatPower, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateCombatPower);
|
|
}
|
|
/**获取当前钻石 */
|
|
getDiamond() {
|
|
return this.getItemNum(ItemNumType.diamond);
|
|
}
|
|
/**设置钻石数量 */
|
|
setDiamond(num: number) {
|
|
this.setItemNum(ItemNumType.diamond, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateDiamond);
|
|
}
|
|
/**添加钻石数量 */
|
|
addDiamond(num: number) {
|
|
this.addItemNum(ItemNumType.diamond, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateDiamond);
|
|
}
|
|
/**减少钻石数量 */
|
|
subDiamond(num: number) {
|
|
this.subItemNum(ItemNumType.diamond, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateDiamond);
|
|
}
|
|
/**获取当前体力 */
|
|
getVigour() {
|
|
return this.getItemNum(ItemNumType.vigour);
|
|
}
|
|
/**设置体力数量 */
|
|
setVigour(num: number) {
|
|
this.setItemNum(ItemNumType.vigour, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateVigour);
|
|
}
|
|
/**添加体力数量 */
|
|
addVigour(num: number) {
|
|
this.addItemNum(ItemNumType.vigour, Math.floor(num));
|
|
GEvent.Ins.emit(GEvent.UpdateVigour);
|
|
}
|
|
/**减少体力数量 */
|
|
subVigour(num: number) {
|
|
const cost = Math.floor(num);
|
|
this.addDayTaskProgressData(TaskType.powerConsumption, cost);
|
|
this.subItemNum(ItemNumType.vigour, cost);
|
|
// 从满额首次降到未满时记下恢复锚点;进战斗等路径只走本方法,必须在此同步
|
|
this.project.syncAutoAddVigourTimeAfterConsume(cost);
|
|
GEvent.Ins.emit(GEvent.UpdateVigour);
|
|
// 推荐流复访托管:扣体力后刷新「体力回满」就绪时刻
|
|
try {
|
|
gg.sdk?.storeFeedData?.();
|
|
} catch { /* ignore */ }
|
|
}
|
|
|
|
/**获取状态信息 */
|
|
getStatus(type: StatusType) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.status, type)
|
|
return v == "" ? 0 : Number(v);
|
|
}
|
|
/**设置状态信息 */
|
|
setStatus(type: StatusType, status: number) {
|
|
this.setKeyValueArray(this.doc.status, type, status);
|
|
}
|
|
|
|
|
|
/**获取统计信息 */
|
|
getStatistics(type: StatisticsType) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.statistics, type)
|
|
return v == "" ? 0 : Number(v);
|
|
}
|
|
/**设置统计信息 */
|
|
setStatistics(type: StatisticsType, count: number) {
|
|
this.setKeyValueArray(this.doc.statistics, type, count);
|
|
}
|
|
/**添加统计信息 */
|
|
addStatistics(type: StatisticsType, count: number) {
|
|
let v = this.getStatistics(type);
|
|
this.setStatistics(type, v + count);
|
|
}
|
|
|
|
/** 进战斗时累积挑战次数 +1,并写入本地存档 */
|
|
addTotalBattleCount(): void {
|
|
const prev = Math.max(0, Math.floor(Number(this.doc.totalBattleCount) || 0));
|
|
this.doc.totalBattleCount = prev + 1;
|
|
}
|
|
|
|
/**保存数组数据 */
|
|
setArrayData(key: OtherDataType, arr: any[]) {
|
|
this.setKeyValueArray(this.doc.other, key, arr.join("#"));
|
|
}
|
|
/**获取数组数据 */
|
|
getArrayData(key: OtherDataType) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.other, key);
|
|
return v ? v.split("#") : [];
|
|
}
|
|
|
|
/**转化为int数组 */
|
|
getArrayDataInt(key: OtherDataType) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.other, key);
|
|
let array = v ? v.split("#") : [];
|
|
return array.map(Number);
|
|
}
|
|
|
|
/**
|
|
* 保存键值对数据(额外接口)
|
|
* @param key 您自己维护的key请保持唯一性
|
|
* @param value 存储的值字符串(“_”下划线是该接口保留的字符,请不要在value中包含下划线)
|
|
*/
|
|
setStringData(key: OtherDataType, value: string) {
|
|
this.setKeyValueArray(this.doc.other, key, value);
|
|
}
|
|
/**获取键值对数据 */
|
|
getStringData(key: OtherDataType) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.other, key);
|
|
return v ? v : "";
|
|
}
|
|
|
|
/** 保存同一个key下,多个key2的值 每日清空状态 */
|
|
setkeyKeyValueDayClear(key: OtherDataType, onlyLocal = false) {
|
|
let saveArr = onlyLocal ? this.doc.onlyLocal : this.doc.other;
|
|
this.setKeyValueArray(saveArr, key, '');
|
|
}
|
|
/**保存同一个key下,多个key2的值(例如:XX角色多少级,key为角色等级的key,key2为角色id,value为等级) */
|
|
setkeyKeyValue(key: OtherDataType, key2: number | string, value: number | string, onlyLocal = false) {
|
|
let saveArr = onlyLocal ? this.doc.onlyLocal : this.doc.other;
|
|
let v = this.getDataFromArrayKeyValueArray(saveArr, key);
|
|
let arr = v ? v.split("#") : [];
|
|
let prefix = key2 + "|";
|
|
let foundIndex = -1;
|
|
for (let i = 0; i < arr.length; i++) {
|
|
if (arr[i].startsWith(prefix)) {
|
|
foundIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (foundIndex !== -1) {
|
|
arr[foundIndex] = prefix + value;
|
|
} else {
|
|
arr.push(prefix + value);
|
|
}
|
|
this.setKeyValueArray(saveArr, key, arr.join("#"));
|
|
}
|
|
/**获取同一个key下,多个key2的值(例如:XX角色多少级,key为角色等级的key,key2为角色id,value为等级) */
|
|
getkeyKeyValue(key: OtherDataType, key2: number | string, onlyLocal = false) {
|
|
const saveArr = onlyLocal ? this.doc.onlyLocal : this.doc.other;
|
|
const v = this.getDataFromArrayKeyValueArray(saveArr, key);
|
|
if (!v) return "";
|
|
|
|
const prefix = key2.toString() + '|';
|
|
const arr = v.split("#");
|
|
const found = arr.find(item => item.startsWith(prefix));
|
|
return found ? found.substring(prefix.length) : "";
|
|
}
|
|
|
|
/**
|
|
* 保存键值对数据(仅存本地存储,不会备份到服务器)
|
|
* @param key 您自己维护的key请保持唯一性
|
|
* @param value 存储的值字符串(“_”下划线是该接口保留的字符,请不要在value中包含下划线)
|
|
*/
|
|
setStringDataOnlyLocal(key: OtherDataType, value: string) {
|
|
this.setKeyValueArray(this.doc.onlyLocal, key, value);
|
|
}
|
|
/**获取键值对数据(仅存本地存储,不会备份到服务器) */
|
|
getStringDataOnlyLocal(key: OtherDataType) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.onlyLocal, key);
|
|
return v ? v : "";
|
|
}
|
|
|
|
|
|
|
|
/**根据key获取键值对数组中的数据 */
|
|
private getDataFromArrayKeyValueArray(arr: string[], key: any): string {
|
|
if (!Array.isArray(arr)) return '';
|
|
const keyStr = key.toString();
|
|
const prefix = keyStr + '_';
|
|
const found = arr.find(item => typeof item === 'string' && item.startsWith(prefix));
|
|
return found ? found.substring(prefix.length) : '';
|
|
}
|
|
|
|
/**设置键值对数据中的数据 */
|
|
private setKeyValueArray(arr: string[], key: any, value: any): void {
|
|
if (!Array.isArray(arr)) {
|
|
console.warn('[GameData] setKeyValueArray 目标不是数组', key);
|
|
return;
|
|
}
|
|
const keyStr = key.toString();
|
|
const prefix = keyStr + '_';
|
|
const newEntry = prefix + value;
|
|
|
|
// 使用 findIndex 一次遍历找到匹配项的索引
|
|
const index = arr.findIndex(item => typeof item === 'string' && item.startsWith(prefix));
|
|
|
|
if (index !== -1) {
|
|
arr[index] = newEntry; // 替换
|
|
} else {
|
|
arr.push(newEntry); // 新增
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取引导状态
|
|
* @param key 您自己维护的key请保持唯一性
|
|
*/
|
|
public getLocalGuideValue(key: string) {
|
|
let v = this.getDataFromArrayKeyValueArray(this.doc.onlyLocal, key);
|
|
return v ? v : 0;
|
|
}
|
|
/**
|
|
* 设置引导状态
|
|
* @param key 您自己维护的key请保持唯一性
|
|
* @param value 存储的值字符串(“_”下划线是该接口保留的字符,请不要在value中包含下划线)
|
|
*/
|
|
public setLocalGuideValue(key: string, value: string) {
|
|
this.setKeyValueArray(this.doc.onlyLocal, key, value);
|
|
}
|
|
|
|
/**
|
|
* 深拷贝
|
|
* @param target 目标对象
|
|
*/
|
|
private deepClone<T>(target: T, visited = new WeakMap()): T {
|
|
if (typeof target !== 'object' || target === null) {
|
|
return target;
|
|
}
|
|
|
|
if (visited.has(target)) {
|
|
return visited.get(target);
|
|
}
|
|
|
|
if (Array.isArray(target)) {
|
|
let cloneArray: any[] = [];
|
|
visited.set(target, cloneArray);
|
|
for (let i = 0; i < target.length; i++) {
|
|
cloneArray[i] = this.deepClone(target[i], visited);
|
|
}
|
|
return cloneArray as T;
|
|
}
|
|
|
|
let cloneObject: { [key: string]: any } = {};
|
|
visited.set(target, cloneObject);
|
|
for (let key in target) {
|
|
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
cloneObject[key] = this.deepClone((target as { [key: string]: any })[key], visited);
|
|
}
|
|
}
|
|
return cloneObject as T;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------------
|
|
|
|
//获取商城列表(Shop 表已移除)
|
|
public getShopItemList() {
|
|
return [];
|
|
}
|
|
//获取打开宝箱的奖励数组(Box 表已移除)
|
|
public getBoxRewardArray(_boxLevelData: unknown) {
|
|
return [];
|
|
}
|
|
|
|
//获取特定碎片的数量(如通关奖励 104,5 → 展开为具体武器碎片)
|
|
public getFragmentNumArray(fragmentStr) {
|
|
return this._expandFragmentRewardStr(fragmentStr, true);
|
|
}
|
|
|
|
/**
|
|
* 将「品质碎片箱」串展开为具体武器碎片 id 列表。
|
|
* @param fragmentStr 如 "104,5" 或 "104,52:105,12"
|
|
* @param downgradeQuality 无对应品质时是否降级(通关奖励要降;开箱旧逻辑不降)
|
|
*/
|
|
private _expandFragmentRewardStr(fragmentStr: string, downgradeQuality: boolean): number[] {
|
|
if (!fragmentStr) return [];
|
|
const haveArr = gg.data.project.getHaveWeaponScrapItemIds();
|
|
const fragmentArrayAll: number[] = [];
|
|
for (const data1 of fragmentStr.split(':')) {
|
|
if (!data1) continue;
|
|
const parts = data1.split(',');
|
|
const boxItem = this.table.getItemData(Number(parts[0]));
|
|
if (!boxItem) {
|
|
console.log('_expandFragmentRewardStr: Item 缺失', parts[0]);
|
|
continue;
|
|
}
|
|
let quality1 = boxItem.quality;
|
|
const num1 = Number(parts[1]);
|
|
if (!(num1 > 0)) continue;
|
|
|
|
if (downgradeQuality) {
|
|
while (quality1 > 0) {
|
|
const hit = haveArr.some(id => this.table.getItemData(id)?.quality === quality1);
|
|
if (hit) break;
|
|
quality1--;
|
|
}
|
|
}
|
|
|
|
const qualityArray1: number[] = [];
|
|
const weightArray1: number[] = [];
|
|
for (const goodID of haveArr) {
|
|
const itemData = this.table.getItemData(goodID);
|
|
if (!itemData || itemData.quality !== quality1) continue;
|
|
qualityArray1.push(goodID);
|
|
// Item 表无 weight:同品质碎片池等权
|
|
weightArray1.push(1);
|
|
}
|
|
fragmentArrayAll.push(
|
|
...MTools.getRandomByWeightArray(num1, qualityArray1, weightArray1),
|
|
);
|
|
}
|
|
return fragmentArrayAll;
|
|
}
|
|
|
|
|
|
//获取当前是不是可以武器升级
|
|
public getIsCanUpgradeWeapon() {
|
|
const haveArr = gg.data.project.getHaveWeaponDataList();
|
|
for (let i = 0; i < haveArr.length; i++) {
|
|
const t = haveArr[i];
|
|
const equipWeaponId = gg.equip?.getHeroDisplayWeaponId?.(t.id) ?? 0;
|
|
const weaponRow = equipWeaponId > 0
|
|
? gg.data.table.getTableData<ITableWeapon>(TableNames.Weapon, equipWeaponId)
|
|
: null;
|
|
const scrapId = weaponRow?.itemid;
|
|
if (!scrapId) continue;
|
|
const weaponSPNum = gg.data.getItemNum(scrapId);
|
|
const needNum = gg.data.project.getWeaponFlagNumByLevel(t.id);
|
|
if (weaponSPNum >= needNum) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//获取玩家的钱够不够升级(Role 表已移除,按存档阶段粗判)
|
|
public getIsCanUpgradeWeaponByMoney() {
|
|
const myMoney = this.getItemNum(ItemNumType.money);
|
|
return myMoney >= 100;
|
|
}
|
|
|
|
//商店是不是有免费的领取
|
|
public getIsCanGetFreeItem() {
|
|
let freeSpendNum = this.getStatistics(StatisticsType.ShopBuyGoldAdCount)
|
|
if (freeSpendNum >= 6) {
|
|
return true
|
|
}
|
|
let freeSpendNum2 = this.getStatistics(StatisticsType.ShopBuyItemAdCount)
|
|
if (freeSpendNum2 >= 6) {
|
|
return true
|
|
}
|
|
if (gg.data.getDiamond() >= 200) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
|
|
//获取玩家等级
|
|
public getPlayerLevel() {
|
|
let curData = this.getStringData(OtherDataType.UserLevelupData)
|
|
if (curData == '') {
|
|
this.setStringData(OtherDataType.UserLevelupData, '0&10&0&0&0&0&0')
|
|
curData = '0&10&0&0&0&0&0'
|
|
}
|
|
let curDataArr = curData.split('&')
|
|
return Number(curDataArr[0])
|
|
}
|
|
/**获取玩家攻击力系数*/
|
|
public getPlayerAtk() {
|
|
let curData = this.getStringData(OtherDataType.UserLevelupData)
|
|
if (curData == '') {
|
|
this.setStringData(OtherDataType.UserLevelupData, '0&10&0&0&0&0&0')
|
|
curData = '0&10&0&0&0&0&0'
|
|
}
|
|
let curDataArr = curData.split('&')
|
|
let num = Number(curDataArr[1]);
|
|
return num
|
|
}
|
|
|
|
/**获取玩家全局攻击力系数*/
|
|
public getPlayerAtkCoefficient() {
|
|
let curData = this.getStringData(OtherDataType.UserLevelupData)
|
|
if (curData == '') {
|
|
this.setStringData(OtherDataType.UserLevelupData, '0&10&0&0&0&0&0')
|
|
curData = '0&10&0&0&0&0&0'
|
|
}
|
|
let curDataArr = curData.split('&')
|
|
let num = Number(curDataArr[1]);
|
|
return num
|
|
}
|
|
|
|
/**获取玩家暴击系数*/
|
|
public getPlayerCritCoefficient() {
|
|
let curData = this.getStringData(OtherDataType.UserLevelupData)
|
|
if (curData == '') {
|
|
this.setStringData(OtherDataType.UserLevelupData, '0&10&0&0&0&0&0')
|
|
curData = '0&10&0&0&0&0&0'
|
|
}
|
|
let curDataArr = curData.split('&')
|
|
return Number(curDataArr[2])
|
|
}
|
|
|
|
/**获取玩家冷却系数*/
|
|
public getPlayerCoolCoefficient() {
|
|
let curData = this.getStringData(OtherDataType.UserLevelupData)
|
|
if (curData == '') {
|
|
this.setStringData(OtherDataType.UserLevelupData, '0&10&0&0&0&0&0')
|
|
curData = '0&10&0&0&0&0&0'
|
|
}
|
|
let curDataArr = curData.split('&')
|
|
|
|
return Number(curDataArr[3])
|
|
}
|
|
|
|
|
|
//获取当前宝箱等级配置
|
|
public getCurBoxLevelData(_boxType: number) {
|
|
return null;
|
|
}
|
|
|
|
public getCurBoxLevel() {
|
|
return 1;
|
|
}
|
|
|
|
public getCurBoxExp() {
|
|
return [0, 0];
|
|
}
|
|
|
|
//获取战力
|
|
//获取当前战力
|
|
public getCurPower() {
|
|
let power = 100
|
|
//UserLevelupData
|
|
let curData = this.getStringData(OtherDataType.UserLevelupData)
|
|
if (curData == '') {
|
|
return power
|
|
}
|
|
let curDataArr = curData.split('&')
|
|
let attack = Number(curDataArr[1])
|
|
let dAttack = Number(curDataArr[2])
|
|
let lengque = Number(curDataArr[3])
|
|
power = 100 * (attack / 10) * (1 + dAttack / 10000) * (1 + lengque / 10000)
|
|
//四舍五入不保留小数
|
|
|
|
power = Math.round(power)
|
|
return power;
|
|
}
|
|
|
|
|
|
/** 获取战斗奖励加成比率(道具 id=98,para 为万分比) */
|
|
public getFightRewardRatio(): number {
|
|
const id = 98;
|
|
const row = this.table.getItemData(id);
|
|
if (!row) {
|
|
console.log('[GameData] getFightRewardRatio: Item 表缺少 id=', id);
|
|
return 0;
|
|
}
|
|
const rateWan = Number(row.para);
|
|
if (!Number.isFinite(rateWan)) {
|
|
console.log('[GameData] getFightRewardRatio: Item id=', id, 'para 无效', row.para);
|
|
return 0;
|
|
}
|
|
const num = this.getItemNum(id);
|
|
return num * (rateWan / 10000);
|
|
}
|
|
|
|
|
|
|
|
// //每日任务进度 任务进度
|
|
// DayTaskProgressData = 45,
|
|
// //每日活跃奖励领取情况
|
|
// DayActiveGetRewardData = 46,
|
|
//周任务奖励领取状态设置
|
|
public setWeekTaskRewardGetData(index, rewardGet) {
|
|
let rewardGetDataList = gg.data.getStringData(OtherDataType.TaskRewardGetData).split('&')
|
|
rewardGetDataList[index] = rewardGet
|
|
this.setStringData(OtherDataType.TaskRewardGetData, rewardGetDataList.join('&'))
|
|
}
|
|
|
|
public setDayTaskRewardGetData(index, rewardGet) {
|
|
let rewardGetDataList = gg.data.getStringData(OtherDataType.DayTaskRewardGetData).split('&')
|
|
rewardGetDataList[index] = rewardGet
|
|
this.setStringData(OtherDataType.DayTaskRewardGetData, rewardGetDataList.join('&'))
|
|
}
|
|
|
|
public addDayTaskProgressData(_taskType: TaskType, _addPro = 1) {
|
|
}
|
|
|
|
public addDayTaskScore(_score = 1) {
|
|
}
|
|
|
|
public addWeekTaskScore(_score = 1) {
|
|
}
|
|
|
|
//活跃奖励进度领取状态设置
|
|
public setDayTaskProgressData(index, rewardGet) {
|
|
let progressDataList = gg.data.getStringData(OtherDataType.DayActiveGetRewardData).split('&')
|
|
if (isNaN(rewardGet)) {
|
|
rewardGet = 1
|
|
}
|
|
progressDataList[index] = rewardGet
|
|
this.setStringData(OtherDataType.DayActiveGetRewardData, progressDataList.join('&'))
|
|
}
|
|
|
|
//判断任务是不是有奖励可以领取
|
|
public checkTaskRewardGetData() {
|
|
|
|
|
|
let progressDataList = gg.data.getStringData(OtherDataType.TaskRewardGetData).split('&')
|
|
let rewardGetDataList = gg.data.getStringData(OtherDataType.DayActiveGetRewardData).split('&')
|
|
let dayTaskRewardGetData = gg.data.getStringData(OtherDataType.DayTaskRewardGetData).split('&')
|
|
|
|
//y有1就返回true
|
|
for (let i = 0; i < progressDataList.length; i++) {
|
|
if (Number(progressDataList[i]) == 1) {
|
|
return true
|
|
}
|
|
}
|
|
//每日任务奖励领取情况
|
|
for (let i = 0; i < dayTaskRewardGetData.length; i++) {
|
|
if (Number(dayTaskRewardGetData[i]) == 1) {
|
|
return true
|
|
}
|
|
}
|
|
//每日活跃奖励领取情况
|
|
for (let i = 0; i < rewardGetDataList.length; i++) {
|
|
if (Number(rewardGetDataList[i]) == 1) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
/**初始化存档 */
|
|
private initDoc() {
|
|
this.doc.key = "";
|
|
this.doc.uid = "";
|
|
this.doc.roleId = 1;
|
|
this.doc.playerTitleLevel = 0;
|
|
this.doc.dataVersion = 0;
|
|
this.doc.rankId = 1;
|
|
this.doc.rankStars = 1;
|
|
this.doc.matchWinCount = 0;
|
|
this.doc.matchLoseCount = 0;
|
|
this.doc.totalBattleCount = 0;
|
|
this.doc.matchWinStatsDate = '';
|
|
this.doc.matchWinStreak = 0;
|
|
this.doc.matchLoseStreak = 0;
|
|
this.doc.dailyWins = 0;
|
|
this.doc.yesterdayWins = 0;
|
|
this.doc.dailyWinsDate = '';
|
|
this.doc.rankLastSyncDailyWins = -1;
|
|
this.doc.rankLastSyncDailyWinsDate = '';
|
|
this.doc.userName = "";
|
|
this.doc.userAvatar = "";
|
|
this.doc.userProvince = "";
|
|
this.doc.rankUserAuthPrompted = 0;
|
|
this.doc.rankSyncedOnce = 0;
|
|
this.doc.rankLastSyncStars = -1;
|
|
this.doc.rankLastSyncProvince = "";
|
|
this.doc.rankLastSyncNick = "";
|
|
this.doc.rankLastSyncAvatar = "";
|
|
this.doc.rankLastSyncRegion = "";
|
|
this.doc.emolumentSnapStars = 0;
|
|
this.doc.emolumentSnapWins = 0;
|
|
this.doc.emolumentSnapDate = '';
|
|
this.doc.gmAwaitEmolumentPop = 0;
|
|
this.doc.headIconId = 0;
|
|
this.doc.battleIllustratedGuideClicked = 0;
|
|
this.doc.level = 1;
|
|
this.doc.itemCount = [];
|
|
this.doc.status = [];
|
|
this.doc.statistics = [];
|
|
this.doc.guide = [];
|
|
this.doc.other = [];
|
|
this.doc.onlyLocal = [];
|
|
this.doc.failCount = 0;
|
|
this.doc.lastFailCount = 0;
|
|
|
|
}
|
|
//#endregion
|
|
}
|
|
|
|
/**存档数据结构 */
|
|
export class Doc {
|
|
/**存档key */
|
|
key: string = "";
|
|
/**用户id */
|
|
uid: string = "";
|
|
/**角色id 男1 女 0 */
|
|
roleId: number = 1;
|
|
/**玩家称号等级 */
|
|
playerTitleLevel: number = 0;
|
|
/**版本号 */
|
|
dataVersion: number = 0;
|
|
/** 当前段位(关卡解锁等进度;段位展示由 rankStars 推导) */
|
|
rankId: number = 1;
|
|
/** 总星数(段位星数存档;段位 id 由 Rank.level 推导) */
|
|
rankStars: number = 1;
|
|
/** 匹配页玩家胜场 */
|
|
matchWinCount: number = 0;
|
|
/** 匹配页玩家负场 */
|
|
matchLoseCount: number = 0;
|
|
/** 玩家累积挑战次数(每次进战斗 +1,跨天不清) */
|
|
totalBattleCount: number = 0;
|
|
/** 匹配胜率统计日期 yyyyMMdd(跨天清零) */
|
|
matchWinStatsDate: string = '';
|
|
/** 匹配连续胜场(跨天不清;胜负互斥) */
|
|
matchWinStreak: number = 0;
|
|
/** 匹配连续负场(跨天不清;胜负互斥) */
|
|
matchLoseStreak: number = 0;
|
|
/** 今日胜场(每日胜场榜,跨天清零) */
|
|
dailyWins: number = 0;
|
|
/** 昨日胜场 */
|
|
yesterdayWins: number = 0;
|
|
/** 今日胜场所属日期 YYYYMMDD */
|
|
dailyWinsDate: string = '';
|
|
/** 上次同步的每日胜场榜分数 */
|
|
rankLastSyncDailyWins: number = -1;
|
|
/** 上次同步每日胜场榜时的日期 YYYYMMDD */
|
|
rankLastSyncDailyWinsDate: string = '';
|
|
/** 排行榜昵称(授权写入为 UTF-8 Base64,读档/展示时解码) */
|
|
userName: string = "";
|
|
/** 排行榜头像 */
|
|
userAvatar: string = "";
|
|
/** 排行榜省份 */
|
|
userProvince: string = "";
|
|
/** 排行榜是否已弹过首次用户信息授权(1=已弹过,不再在打开时判断) */
|
|
rankUserAuthPrompted: number = 0;
|
|
/** 是否已同步过排行榜分数 */
|
|
rankSyncedOnce: number = 0;
|
|
/** 上次同步的省榜/总榜星数 */
|
|
rankLastSyncStars: number = -1;
|
|
/** 上次同步的省份 */
|
|
rankLastSyncProvince: string = "";
|
|
/** 上次同步的排行榜昵称 */
|
|
rankLastSyncNick: string = "";
|
|
/** 上次同步的排行榜头像 */
|
|
rankLastSyncAvatar: string = "";
|
|
/** 上次同步的排行榜地区 */
|
|
rankLastSyncRegion: string = "";
|
|
/** 待领俸禄星级(总星数;0=无,领取后清零) */
|
|
emolumentSnapStars: number = 0;
|
|
/** 待领俸禄胜场(领取后清零) */
|
|
emolumentSnapWins: number = 0;
|
|
/** 待领俸禄快照日期 YYYYMMDD(等于今日=当日累计未到弹窗日;早于今日=可弹;隔日未上线仍保留) */
|
|
emolumentSnapDate: string = '';
|
|
/** GM:下次进 Home 弹俸禄(1=待弹,弹后清零) */
|
|
gmAwaitEmolumentPop: number = 0;
|
|
/** 设置页选中头像 Headicon.id */
|
|
headIconId: number = 0;
|
|
/** 是否已点击过战斗图鉴(1=已点击,不再显示 ui_illustrated 引导) */
|
|
battleIllustratedGuideClicked: number = 0;
|
|
/**当前关卡 */
|
|
level: number = 1;
|
|
/**所有道具数量 */
|
|
itemCount: string[] = [];
|
|
/**存储所有状态 */
|
|
status: string[] = [];
|
|
/**统计信息 */
|
|
statistics: string[] = [];
|
|
/**引导状态 */
|
|
guide: string[] = [];
|
|
/**其他数据 */
|
|
other: string[] = [];
|
|
/**仅存本地缓存的数据,无需备份到服务器,数据丢失也没关系的数据 */
|
|
onlyLocal: string[] = [];
|
|
/**上报失败次数 */
|
|
failCount: number = 0;
|
|
/**上一次上报失败的次数 */
|
|
lastFailCount: number = 0;
|
|
|
|
|
|
}
|
|
|