|
|
@@ -0,0 +1,878 @@
|
|
|
+import { _decorator, sys } from 'cc';
|
|
|
+const { ccclass, property } = _decorator;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 玩家数据结构
|
|
|
+ */
|
|
|
+export interface PlayerData {
|
|
|
+ // 基本信息
|
|
|
+ playerId: string;
|
|
|
+ playerName: string;
|
|
|
+ createTime: number;
|
|
|
+ lastPlayTime: number;
|
|
|
+ totalPlayTime: number;
|
|
|
+
|
|
|
+ // 等级和经验
|
|
|
+ playerLevel: number;
|
|
|
+ experience: number;
|
|
|
+ experienceToNext: number;
|
|
|
+
|
|
|
+ // 货币系统
|
|
|
+ coins: number; // 金币
|
|
|
+ diamonds: number; // 钻石
|
|
|
+ gems: number; // 宝石
|
|
|
+
|
|
|
+ // 关卡进度
|
|
|
+ currentLevel: number;
|
|
|
+ maxUnlockedLevel: number;
|
|
|
+ levelProgress: { [levelId: number]: LevelProgress };
|
|
|
+
|
|
|
+ // 道具和装备
|
|
|
+ inventory: InventoryData;
|
|
|
+
|
|
|
+ // 统计数据
|
|
|
+ statistics: GameStatistics;
|
|
|
+
|
|
|
+ // 设置
|
|
|
+ settings: PlayerSettings;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 关卡进度数据
|
|
|
+ */
|
|
|
+export interface LevelProgress {
|
|
|
+ levelId: number;
|
|
|
+ completed: boolean;
|
|
|
+ bestScore: number;
|
|
|
+ bestTime: number;
|
|
|
+ stars: number;
|
|
|
+ attempts: number;
|
|
|
+ firstClearTime: number;
|
|
|
+ lastPlayTime: number;
|
|
|
+ rewards: RewardData[];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 背包数据
|
|
|
+ */
|
|
|
+export interface InventoryData {
|
|
|
+ weapons: { [weaponId: string]: WeaponData };
|
|
|
+ items: { [itemId: string]: ItemData };
|
|
|
+ materials: { [materialId: string]: number };
|
|
|
+ capacity: number;
|
|
|
+ usedSlots: number;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 武器数据
|
|
|
+ */
|
|
|
+export interface WeaponData {
|
|
|
+ weaponId: string;
|
|
|
+ level: number;
|
|
|
+ experience: number;
|
|
|
+ rarity: string;
|
|
|
+ obtainTime: number;
|
|
|
+ upgradeCount: number;
|
|
|
+ isEquipped: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 道具数据
|
|
|
+ */
|
|
|
+export interface ItemData {
|
|
|
+ itemId: string;
|
|
|
+ count: number;
|
|
|
+ obtainTime: number;
|
|
|
+ lastUsedTime: number;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 奖励数据
|
|
|
+ */
|
|
|
+export interface RewardData {
|
|
|
+ type: 'coins' | 'diamonds' | 'gems' | 'weapon' | 'item' | 'experience';
|
|
|
+ id?: string;
|
|
|
+ amount: number;
|
|
|
+ obtainTime: number;
|
|
|
+ source: string; // 获得来源:level_complete, daily_reward, shop_purchase等
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 游戏统计数据
|
|
|
+ */
|
|
|
+export interface GameStatistics {
|
|
|
+ totalGamesPlayed: number;
|
|
|
+ totalWins: number;
|
|
|
+ totalLosses: number;
|
|
|
+ totalScore: number;
|
|
|
+ totalEnemiesDefeated: number;
|
|
|
+ totalShotsfired: number;
|
|
|
+ totalDamageDealt: number;
|
|
|
+ totalTimePlayed: number;
|
|
|
+ highestLevel: number;
|
|
|
+ consecutiveWins: number;
|
|
|
+ bestWinStreak: number;
|
|
|
+ favoriteWeapon: string;
|
|
|
+ weaponsUnlocked: number;
|
|
|
+ itemsCollected: number;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 玩家设置
|
|
|
+ */
|
|
|
+export interface PlayerSettings {
|
|
|
+ soundEnabled: boolean;
|
|
|
+ musicEnabled: boolean;
|
|
|
+ soundVolume: number;
|
|
|
+ musicVolume: number;
|
|
|
+ vibrationEnabled: boolean;
|
|
|
+ autoSaveEnabled: boolean;
|
|
|
+ language: string;
|
|
|
+ graphics: 'low' | 'medium' | 'high';
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 存档管理器
|
|
|
+ * 负责玩家数据的保存、加载和管理
|
|
|
+ */
|
|
|
+@ccclass('SaveDataManager')
|
|
|
+export class SaveDataManager {
|
|
|
+ private static instance: SaveDataManager = null;
|
|
|
+
|
|
|
+ private playerData: PlayerData = null;
|
|
|
+ private initialized: boolean = false;
|
|
|
+ private autoSaveInterval: number = 30; // 自动保存间隔(秒)
|
|
|
+ private lastSaveTime: number = 0;
|
|
|
+
|
|
|
+ // 存档文件键名
|
|
|
+ private readonly SAVE_KEY = 'pong_game_save_data';
|
|
|
+ private readonly BACKUP_KEY = 'pong_game_save_data_backup';
|
|
|
+ private readonly SETTINGS_KEY = 'pong_game_settings';
|
|
|
+
|
|
|
+ private constructor() {
|
|
|
+ // 私有构造函数,确保单例模式
|
|
|
+ }
|
|
|
+
|
|
|
+ public static getInstance(): SaveDataManager {
|
|
|
+ if (SaveDataManager.instance === null) {
|
|
|
+ SaveDataManager.instance = new SaveDataManager();
|
|
|
+ }
|
|
|
+ return SaveDataManager.instance;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化存档管理器
|
|
|
+ */
|
|
|
+ public initialize(): void {
|
|
|
+ if (this.initialized) return;
|
|
|
+
|
|
|
+ this.loadPlayerData();
|
|
|
+ this.initialized = true;
|
|
|
+
|
|
|
+ console.log('✅ 存档管理器初始化完成');
|
|
|
+ console.log(`玩家等级: ${this.playerData.playerLevel}`);
|
|
|
+ console.log(`当前关卡: ${this.playerData.currentLevel}`);
|
|
|
+ console.log(`金币: ${this.playerData.coins}`);
|
|
|
+ console.log(`钻石: ${this.playerData.diamonds}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载玩家数据
|
|
|
+ */
|
|
|
+ private loadPlayerData(): void {
|
|
|
+ try {
|
|
|
+ const savedData = sys.localStorage.getItem(this.SAVE_KEY);
|
|
|
+
|
|
|
+ if (savedData) {
|
|
|
+ const data = JSON.parse(savedData);
|
|
|
+ this.playerData = this.validateAndMigrateData(data);
|
|
|
+ this.playerData.lastPlayTime = Date.now();
|
|
|
+ console.log('✅ 存档数据加载成功');
|
|
|
+ } else {
|
|
|
+ this.createNewPlayerData();
|
|
|
+ console.log('🆕 创建新的玩家存档');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('❌ 存档数据加载失败,尝试加载备份:', error);
|
|
|
+ this.loadBackupData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载备份数据
|
|
|
+ */
|
|
|
+ private loadBackupData(): void {
|
|
|
+ try {
|
|
|
+ const backupData = sys.localStorage.getItem(this.BACKUP_KEY);
|
|
|
+
|
|
|
+ if (backupData) {
|
|
|
+ const data = JSON.parse(backupData);
|
|
|
+ this.playerData = this.validateAndMigrateData(data);
|
|
|
+ this.playerData.lastPlayTime = Date.now();
|
|
|
+ console.log('✅ 备份数据加载成功');
|
|
|
+ } else {
|
|
|
+ this.createNewPlayerData();
|
|
|
+ console.log('🆕 备份数据不存在,创建新存档');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('❌ 备份数据加载失败,创建新存档:', error);
|
|
|
+ this.createNewPlayerData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建新的玩家数据
|
|
|
+ */
|
|
|
+ private createNewPlayerData(): void {
|
|
|
+ this.playerData = {
|
|
|
+ playerId: this.generatePlayerId(),
|
|
|
+ playerName: 'Player',
|
|
|
+ createTime: Date.now(),
|
|
|
+ lastPlayTime: Date.now(),
|
|
|
+ totalPlayTime: 0,
|
|
|
+
|
|
|
+ playerLevel: 1,
|
|
|
+ experience: 0,
|
|
|
+ experienceToNext: 100,
|
|
|
+
|
|
|
+ coins: 1000, // 初始金币
|
|
|
+ diamonds: 50, // 初始钻石
|
|
|
+ gems: 0,
|
|
|
+
|
|
|
+ currentLevel: 1,
|
|
|
+ maxUnlockedLevel: 1,
|
|
|
+ levelProgress: {},
|
|
|
+
|
|
|
+ inventory: {
|
|
|
+ weapons: {},
|
|
|
+ items: {},
|
|
|
+ materials: {},
|
|
|
+ capacity: 50,
|
|
|
+ usedSlots: 0
|
|
|
+ },
|
|
|
+
|
|
|
+ statistics: {
|
|
|
+ totalGamesPlayed: 0,
|
|
|
+ totalWins: 0,
|
|
|
+ totalLosses: 0,
|
|
|
+ totalScore: 0,
|
|
|
+ totalEnemiesDefeated: 0,
|
|
|
+ totalShotsfired: 0,
|
|
|
+ totalDamageDealt: 0,
|
|
|
+ totalTimePlayed: 0,
|
|
|
+ highestLevel: 1,
|
|
|
+ consecutiveWins: 0,
|
|
|
+ bestWinStreak: 0,
|
|
|
+ favoriteWeapon: '',
|
|
|
+ weaponsUnlocked: 0,
|
|
|
+ itemsCollected: 0
|
|
|
+ },
|
|
|
+
|
|
|
+ settings: {
|
|
|
+ soundEnabled: true,
|
|
|
+ musicEnabled: true,
|
|
|
+ soundVolume: 0.8,
|
|
|
+ musicVolume: 0.6,
|
|
|
+ vibrationEnabled: true,
|
|
|
+ autoSaveEnabled: true,
|
|
|
+ language: 'zh-CN',
|
|
|
+ graphics: 'medium'
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ this.savePlayerData();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证和迁移数据(用于版本兼容)
|
|
|
+ */
|
|
|
+ private validateAndMigrateData(data: any): PlayerData {
|
|
|
+ // 确保所有必要字段存在
|
|
|
+ const defaultData = this.createDefaultPlayerData();
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...defaultData,
|
|
|
+ ...data,
|
|
|
+ // 确保嵌套对象也被正确合并
|
|
|
+ inventory: { ...defaultData.inventory, ...data.inventory },
|
|
|
+ statistics: { ...defaultData.statistics, ...data.statistics },
|
|
|
+ settings: { ...defaultData.settings, ...data.settings }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建默认玩家数据模板
|
|
|
+ */
|
|
|
+ private createDefaultPlayerData(): PlayerData {
|
|
|
+ return {
|
|
|
+ playerId: this.generatePlayerId(),
|
|
|
+ playerName: 'Player',
|
|
|
+ createTime: Date.now(),
|
|
|
+ lastPlayTime: Date.now(),
|
|
|
+ totalPlayTime: 0,
|
|
|
+ playerLevel: 1,
|
|
|
+ experience: 0,
|
|
|
+ experienceToNext: 100,
|
|
|
+ coins: 1000,
|
|
|
+ diamonds: 50,
|
|
|
+ gems: 0,
|
|
|
+ currentLevel: 1,
|
|
|
+ maxUnlockedLevel: 1,
|
|
|
+ levelProgress: {},
|
|
|
+ inventory: {
|
|
|
+ weapons: {},
|
|
|
+ items: {},
|
|
|
+ materials: {},
|
|
|
+ capacity: 50,
|
|
|
+ usedSlots: 0
|
|
|
+ },
|
|
|
+ statistics: {
|
|
|
+ totalGamesPlayed: 0,
|
|
|
+ totalWins: 0,
|
|
|
+ totalLosses: 0,
|
|
|
+ totalScore: 0,
|
|
|
+ totalEnemiesDefeated: 0,
|
|
|
+ totalShotsfired: 0,
|
|
|
+ totalDamageDealt: 0,
|
|
|
+ totalTimePlayed: 0,
|
|
|
+ highestLevel: 1,
|
|
|
+ consecutiveWins: 0,
|
|
|
+ bestWinStreak: 0,
|
|
|
+ favoriteWeapon: '',
|
|
|
+ weaponsUnlocked: 0,
|
|
|
+ itemsCollected: 0
|
|
|
+ },
|
|
|
+ settings: {
|
|
|
+ soundEnabled: true,
|
|
|
+ musicEnabled: true,
|
|
|
+ soundVolume: 0.8,
|
|
|
+ musicVolume: 0.6,
|
|
|
+ vibrationEnabled: true,
|
|
|
+ autoSaveEnabled: true,
|
|
|
+ language: 'zh-CN',
|
|
|
+ graphics: 'medium'
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存玩家数据
|
|
|
+ */
|
|
|
+ public savePlayerData(force: boolean = false): void {
|
|
|
+ if (!this.playerData) return;
|
|
|
+
|
|
|
+ const currentTime = Date.now();
|
|
|
+
|
|
|
+ // 检查是否需要保存(避免频繁保存)
|
|
|
+ if (!force && currentTime - this.lastSaveTime < 5000) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 更新最后保存时间
|
|
|
+ this.playerData.lastPlayTime = currentTime;
|
|
|
+
|
|
|
+ // 创建备份
|
|
|
+ const currentData = sys.localStorage.getItem(this.SAVE_KEY);
|
|
|
+ if (currentData) {
|
|
|
+ sys.localStorage.setItem(this.BACKUP_KEY, currentData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存当前数据
|
|
|
+ const dataToSave = JSON.stringify(this.playerData);
|
|
|
+ sys.localStorage.setItem(this.SAVE_KEY, dataToSave);
|
|
|
+
|
|
|
+ this.lastSaveTime = currentTime;
|
|
|
+ console.log('💾 玩家数据保存成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('❌ 玩家数据保存失败:', error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自动保存检查
|
|
|
+ */
|
|
|
+ public checkAutoSave(): void {
|
|
|
+ if (!this.playerData || !this.playerData.settings.autoSaveEnabled) return;
|
|
|
+
|
|
|
+ const currentTime = Date.now();
|
|
|
+ if (currentTime - this.lastSaveTime > this.autoSaveInterval * 1000) {
|
|
|
+ this.savePlayerData();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 玩家基本信息管理 ===
|
|
|
+
|
|
|
+ public getPlayerData(): PlayerData {
|
|
|
+ return this.playerData;
|
|
|
+ }
|
|
|
+
|
|
|
+ public getPlayerLevel(): number {
|
|
|
+ return this.playerData?.playerLevel || 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ public getCurrentLevel(): number {
|
|
|
+ return this.playerData?.currentLevel || 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ public getMaxUnlockedLevel(): number {
|
|
|
+ return this.playerData?.maxUnlockedLevel || 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ public getCoins(): number {
|
|
|
+ return this.playerData?.coins || 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ public getDiamonds(): number {
|
|
|
+ return this.playerData?.diamonds || 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ public getGems(): number {
|
|
|
+ return this.playerData?.gems || 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 关卡进度管理 ===
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 完成关卡
|
|
|
+ */
|
|
|
+ public completeLevel(levelId: number, score: number, time: number, stars: number): void {
|
|
|
+ if (!this.playerData) return;
|
|
|
+
|
|
|
+ // 更新关卡进度
|
|
|
+ if (!this.playerData.levelProgress[levelId]) {
|
|
|
+ this.playerData.levelProgress[levelId] = {
|
|
|
+ levelId: levelId,
|
|
|
+ completed: false,
|
|
|
+ bestScore: 0,
|
|
|
+ bestTime: 0,
|
|
|
+ stars: 0,
|
|
|
+ attempts: 0,
|
|
|
+ firstClearTime: 0,
|
|
|
+ lastPlayTime: 0,
|
|
|
+ rewards: []
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ const progress = this.playerData.levelProgress[levelId];
|
|
|
+ progress.attempts += 1;
|
|
|
+ progress.lastPlayTime = Date.now();
|
|
|
+
|
|
|
+ // 首次完成
|
|
|
+ if (!progress.completed) {
|
|
|
+ progress.completed = true;
|
|
|
+ progress.firstClearTime = Date.now();
|
|
|
+
|
|
|
+ // 解锁下一关
|
|
|
+ if (levelId === this.playerData.maxUnlockedLevel) {
|
|
|
+ this.playerData.maxUnlockedLevel = levelId + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新最佳记录
|
|
|
+ if (score > progress.bestScore) {
|
|
|
+ progress.bestScore = score;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (time > 0 && (progress.bestTime === 0 || time < progress.bestTime)) {
|
|
|
+ progress.bestTime = time;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (stars > progress.stars) {
|
|
|
+ progress.stars = stars;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新统计数据
|
|
|
+ this.playerData.statistics.totalGamesPlayed += 1;
|
|
|
+ this.playerData.statistics.totalWins += 1;
|
|
|
+ this.playerData.statistics.totalScore += score;
|
|
|
+ this.playerData.statistics.consecutiveWins += 1;
|
|
|
+
|
|
|
+ if (this.playerData.statistics.consecutiveWins > this.playerData.statistics.bestWinStreak) {
|
|
|
+ this.playerData.statistics.bestWinStreak = this.playerData.statistics.consecutiveWins;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (levelId > this.playerData.statistics.highestLevel) {
|
|
|
+ this.playerData.statistics.highestLevel = levelId;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 给予经验值
|
|
|
+ this.addExperience(score / 10 + stars * 20);
|
|
|
+
|
|
|
+ // 计算并给予奖励
|
|
|
+ this.giveCompletionRewards(levelId, stars);
|
|
|
+
|
|
|
+ this.savePlayerData();
|
|
|
+
|
|
|
+ console.log(`🎉 关卡 ${levelId} 完成!得分: ${score}, 星级: ${stars}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 关卡失败
|
|
|
+ */
|
|
|
+ public failLevel(levelId: number): void {
|
|
|
+ if (!this.playerData) return;
|
|
|
+
|
|
|
+ // 更新关卡进度
|
|
|
+ if (!this.playerData.levelProgress[levelId]) {
|
|
|
+ this.playerData.levelProgress[levelId] = {
|
|
|
+ levelId: levelId,
|
|
|
+ completed: false,
|
|
|
+ bestScore: 0,
|
|
|
+ bestTime: 0,
|
|
|
+ stars: 0,
|
|
|
+ attempts: 0,
|
|
|
+ firstClearTime: 0,
|
|
|
+ lastPlayTime: 0,
|
|
|
+ rewards: []
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ const progress = this.playerData.levelProgress[levelId];
|
|
|
+ progress.attempts += 1;
|
|
|
+ progress.lastPlayTime = Date.now();
|
|
|
+
|
|
|
+ // 更新统计数据
|
|
|
+ this.playerData.statistics.totalGamesPlayed += 1;
|
|
|
+ this.playerData.statistics.totalLosses += 1;
|
|
|
+ this.playerData.statistics.consecutiveWins = 0;
|
|
|
+
|
|
|
+ this.savePlayerData();
|
|
|
+
|
|
|
+ console.log(`💔 关卡 ${levelId} 失败`);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取关卡进度
|
|
|
+ */
|
|
|
+ public getLevelProgress(levelId: number): LevelProgress | null {
|
|
|
+ return this.playerData?.levelProgress[levelId] || null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查关卡是否已解锁
|
|
|
+ */
|
|
|
+ public isLevelUnlocked(levelId: number): boolean {
|
|
|
+ return levelId <= (this.playerData?.maxUnlockedLevel || 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查关卡是否已完成
|
|
|
+ */
|
|
|
+ public isLevelCompleted(levelId: number): boolean {
|
|
|
+ const progress = this.getLevelProgress(levelId);
|
|
|
+ return progress?.completed || false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 货币管理 ===
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加金币
|
|
|
+ */
|
|
|
+ public addCoins(amount: number, source: string = 'unknown'): boolean {
|
|
|
+ if (!this.playerData || amount <= 0) return false;
|
|
|
+
|
|
|
+ this.playerData.coins += amount;
|
|
|
+ this.addReward('coins', '', amount, source);
|
|
|
+
|
|
|
+ console.log(`💰 获得金币 +${amount} (来源: ${source})`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 消费金币
|
|
|
+ */
|
|
|
+ public spendCoins(amount: number): boolean {
|
|
|
+ if (!this.playerData || amount <= 0 || this.playerData.coins < amount) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.playerData.coins -= amount;
|
|
|
+ console.log(`💸 消费金币 -${amount}`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加钻石
|
|
|
+ */
|
|
|
+ public addDiamonds(amount: number, source: string = 'unknown'): boolean {
|
|
|
+ if (!this.playerData || amount <= 0) return false;
|
|
|
+
|
|
|
+ this.playerData.diamonds += amount;
|
|
|
+ this.addReward('diamonds', '', amount, source);
|
|
|
+
|
|
|
+ console.log(`💎 获得钻石 +${amount} (来源: ${source})`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 消费钻石
|
|
|
+ */
|
|
|
+ public spendDiamonds(amount: number): boolean {
|
|
|
+ if (!this.playerData || amount <= 0 || this.playerData.diamonds < amount) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.playerData.diamonds -= amount;
|
|
|
+ console.log(`💸 消费钻石 -${amount}`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加宝石
|
|
|
+ */
|
|
|
+ public addGems(amount: number, source: string = 'unknown'): boolean {
|
|
|
+ if (!this.playerData || amount <= 0) return false;
|
|
|
+
|
|
|
+ this.playerData.gems += amount;
|
|
|
+ this.addReward('gems', '', amount, source);
|
|
|
+
|
|
|
+ console.log(`💜 获得宝石 +${amount} (来源: ${source})`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 经验和等级管理 ===
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加经验值
|
|
|
+ */
|
|
|
+ public addExperience(amount: number): boolean {
|
|
|
+ if (!this.playerData || amount <= 0) return false;
|
|
|
+
|
|
|
+ this.playerData.experience += amount;
|
|
|
+
|
|
|
+ // 检查是否升级
|
|
|
+ while (this.playerData.experience >= this.playerData.experienceToNext) {
|
|
|
+ this.levelUp();
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`⭐ 获得经验值 +${amount}`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 玩家升级
|
|
|
+ */
|
|
|
+ private levelUp(): void {
|
|
|
+ this.playerData.experience -= this.playerData.experienceToNext;
|
|
|
+ this.playerData.playerLevel += 1;
|
|
|
+
|
|
|
+ // 计算下一级所需经验值
|
|
|
+ this.playerData.experienceToNext = this.calculateExperienceToNext(this.playerData.playerLevel);
|
|
|
+
|
|
|
+ // 升级奖励
|
|
|
+ const coinReward = this.playerData.playerLevel * 100;
|
|
|
+ const diamondReward = Math.floor(this.playerData.playerLevel / 5);
|
|
|
+
|
|
|
+ this.addCoins(coinReward, 'level_up');
|
|
|
+ if (diamondReward > 0) {
|
|
|
+ this.addDiamonds(diamondReward, 'level_up');
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`🎉 玩家升级到 ${this.playerData.playerLevel} 级!`);
|
|
|
+ console.log(`奖励:金币 +${coinReward}, 钻石 +${diamondReward}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算升级所需经验值
|
|
|
+ */
|
|
|
+ private calculateExperienceToNext(level: number): number {
|
|
|
+ return 100 + (level - 1) * 50;
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 道具和武器管理 ===
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加武器
|
|
|
+ */
|
|
|
+ public addWeapon(weaponId: string, rarity: string = 'common'): boolean {
|
|
|
+ if (!this.playerData) return false;
|
|
|
+
|
|
|
+ if (!this.playerData.inventory.weapons[weaponId]) {
|
|
|
+ this.playerData.inventory.weapons[weaponId] = {
|
|
|
+ weaponId: weaponId,
|
|
|
+ level: 1,
|
|
|
+ experience: 0,
|
|
|
+ rarity: rarity,
|
|
|
+ obtainTime: Date.now(),
|
|
|
+ upgradeCount: 0,
|
|
|
+ isEquipped: false
|
|
|
+ };
|
|
|
+
|
|
|
+ this.playerData.statistics.weaponsUnlocked += 1;
|
|
|
+ this.addReward('weapon', weaponId, 1, 'drop');
|
|
|
+
|
|
|
+ console.log(`⚔️ 获得武器: ${weaponId} (${rarity})`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加道具
|
|
|
+ */
|
|
|
+ public addItem(itemId: string, count: number = 1): boolean {
|
|
|
+ if (!this.playerData || count <= 0) return false;
|
|
|
+
|
|
|
+ if (!this.playerData.inventory.items[itemId]) {
|
|
|
+ this.playerData.inventory.items[itemId] = {
|
|
|
+ itemId: itemId,
|
|
|
+ count: 0,
|
|
|
+ obtainTime: Date.now(),
|
|
|
+ lastUsedTime: 0
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ this.playerData.inventory.items[itemId].count += count;
|
|
|
+ this.playerData.statistics.itemsCollected += count;
|
|
|
+ this.addReward('item', itemId, count, 'drop');
|
|
|
+
|
|
|
+ console.log(`📦 获得道具: ${itemId} x${count}`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用道具
|
|
|
+ */
|
|
|
+ public useItem(itemId: string, count: number = 1): boolean {
|
|
|
+ if (!this.playerData || count <= 0) return false;
|
|
|
+
|
|
|
+ const item = this.playerData.inventory.items[itemId];
|
|
|
+ if (!item || item.count < count) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ item.count -= count;
|
|
|
+ item.lastUsedTime = Date.now();
|
|
|
+
|
|
|
+ if (item.count === 0) {
|
|
|
+ delete this.playerData.inventory.items[itemId];
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`🔧 使用道具: ${itemId} x${count}`);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 奖励管理 ===
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加奖励记录
|
|
|
+ */
|
|
|
+ private addReward(type: RewardData['type'], id: string, amount: number, source: string): void {
|
|
|
+ if (!this.playerData) return;
|
|
|
+
|
|
|
+ const reward: RewardData = {
|
|
|
+ type: type,
|
|
|
+ id: id,
|
|
|
+ amount: amount,
|
|
|
+ obtainTime: Date.now(),
|
|
|
+ source: source
|
|
|
+ };
|
|
|
+
|
|
|
+ // 这里可以添加到全局奖励历史记录中
|
|
|
+ // 暂时不实现,避免数据过大
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 给予关卡完成奖励
|
|
|
+ */
|
|
|
+ private giveCompletionRewards(levelId: number, stars: number): void {
|
|
|
+ // 基础金币奖励
|
|
|
+ const baseCoins = levelId * 50;
|
|
|
+ const starBonus = stars * 25;
|
|
|
+ const totalCoins = baseCoins + starBonus;
|
|
|
+
|
|
|
+ this.addCoins(totalCoins, `level_${levelId}_complete`);
|
|
|
+
|
|
|
+ // 钻石奖励(3星才给)
|
|
|
+ if (stars >= 3) {
|
|
|
+ const diamonds = Math.max(1, Math.floor(levelId / 3));
|
|
|
+ this.addDiamonds(diamonds, `level_${levelId}_perfect`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 特殊关卡额外奖励
|
|
|
+ if (levelId % 10 === 0) {
|
|
|
+ this.addGems(1, `level_${levelId}_milestone`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 统计数据更新 ===
|
|
|
+
|
|
|
+ public updateStatistic(key: keyof GameStatistics, value: number): void {
|
|
|
+ if (!this.playerData) return;
|
|
|
+
|
|
|
+ if (typeof this.playerData.statistics[key] === 'number') {
|
|
|
+ (this.playerData.statistics[key] as number) += value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 设置管理 ===
|
|
|
+
|
|
|
+ public updateSetting<K extends keyof PlayerSettings>(key: K, value: PlayerSettings[K]): void {
|
|
|
+ if (!this.playerData) return;
|
|
|
+
|
|
|
+ this.playerData.settings[key] = value;
|
|
|
+ this.savePlayerData();
|
|
|
+ }
|
|
|
+
|
|
|
+ public getSetting<K extends keyof PlayerSettings>(key: K): PlayerSettings[K] {
|
|
|
+ return this.playerData?.settings[key];
|
|
|
+ }
|
|
|
+
|
|
|
+ // === 工具方法 ===
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成唯一玩家ID
|
|
|
+ */
|
|
|
+ private generatePlayerId(): string {
|
|
|
+ return `player_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重置所有数据
|
|
|
+ */
|
|
|
+ public resetAllData(): void {
|
|
|
+ sys.localStorage.removeItem(this.SAVE_KEY);
|
|
|
+ sys.localStorage.removeItem(this.BACKUP_KEY);
|
|
|
+ this.createNewPlayerData();
|
|
|
+ console.log('🔄 所有存档数据已重置');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出存档数据(用于调试)
|
|
|
+ */
|
|
|
+ public exportSaveData(): string {
|
|
|
+ return JSON.stringify(this.playerData, null, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导入存档数据(用于调试)
|
|
|
+ */
|
|
|
+ public importSaveData(dataString: string): boolean {
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(dataString);
|
|
|
+ this.playerData = this.validateAndMigrateData(data);
|
|
|
+ this.savePlayerData(true);
|
|
|
+ console.log('✅ 存档数据导入成功');
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('❌ 存档数据导入失败:', error);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取存档摘要信息
|
|
|
+ */
|
|
|
+ public getSaveSummary(): string {
|
|
|
+ if (!this.playerData) return '无存档数据';
|
|
|
+
|
|
|
+ return `玩家等级: ${this.playerData.playerLevel} | ` +
|
|
|
+ `当前关卡: ${this.playerData.currentLevel} | ` +
|
|
|
+ `最高关卡: ${this.playerData.maxUnlockedLevel} | ` +
|
|
|
+ `金币: ${this.playerData.coins} | ` +
|
|
|
+ `钻石: ${this.playerData.diamonds} | ` +
|
|
|
+ `游戏次数: ${this.playerData.statistics.totalGamesPlayed}`;
|
|
|
+ }
|
|
|
+}
|