diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 21e978f..4d08308 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ android:usesCleartextTraffic="true" android:hardwareAccelerated="true" android:networkSecurityConfig="@xml/network_security_config" + android:requestLegacyExternalStorage="true" android:theme="@style/AppTheme"> - - + + + + + + + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 9cd899b..4ac0f77 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "private": true, "version": "1.6.0", - "buildDate": "2026/1/6 07:55:12", + "buildDate": "2026/1/23 01:10:25", "main": "dist-electron/main.js", "type": "module", "scripts": { diff --git a/src/App.vue b/src/App.vue index c2b340f..e240bec 100644 --- a/src/App.vue +++ b/src/App.vue @@ -518,7 +518,7 @@ import HintToast from '@/components/notifications/HintToast.vue' import BackToTop from '@/components/common/BackToTop.vue' import Sonner from '@/components/ui/sonner/Sonner.vue' - import { MissionType, BuildingType, TechnologyType, DiplomaticEventType, ShipType } from '@/types/game' + import { MissionType, BuildingType, TechnologyType, DiplomaticEventType, ShipType, DefenseType } from '@/types/game' import type { FleetMission, NPC, MissileAttack } from '@/types/game' import { DIPLOMATIC_CONFIG } from '@/config/gameConfig' import type { VersionInfo } from '@/utils/versionCheck' @@ -853,6 +853,29 @@ generateNPCPlanets() } + // 数据迁移:为没有 bonusPoints 的玩家计算奖励积分 + if (gameStore.player.bonusPoints === undefined) { + // 计算基础积分(建筑、科技、舰船、防御) + let totalCost = 0 + gameStore.player.planets.forEach(planet => { + Object.entries(planet.buildings).forEach(([buildingType, level]) => { + totalCost += publicLogic.calculateBuildingTotalCost(buildingType as BuildingType, level) + }) + Object.entries(planet.fleet).forEach(([shipType, count]) => { + totalCost += publicLogic.calculateShipUnitCost(shipType as ShipType) * count + }) + Object.entries(planet.defense).forEach(([defenseType, count]) => { + totalCost += publicLogic.calculateDefenseUnitCost(defenseType as DefenseType) * count + }) + }) + Object.entries(gameStore.player.technologies).forEach(([techType, level]) => { + totalCost += publicLogic.calculateTechnologyTotalCost(techType as TechnologyType, level) + }) + const basePoints = Math.floor(totalCost / 1000) + // bonusPoints = 当前积分 - 基础积分 + gameStore.player.bonusPoints = Math.max(0, gameStore.player.points - basePoints) + } + // 初始化或更新玩家积分 gameStore.player.points = publicLogic.calculatePlayerPoints(gameStore.player) @@ -1512,6 +1535,17 @@ read: false }) } + + // 更新任务状态为返回中 + // Deploy任务不需要返回(舰队已经留在目标星球) + if (mission.missionType !== MissionType.Deploy) { + mission.status = 'returning' + // 确保returnTime已设置(如果还没设置的话) + if (!mission.returnTime) { + const flightTime = mission.arrivalTime - mission.departureTime + mission.returnTime = Date.now() + flightTime + } + } } const processMissionReturn = (mission: FleetMission) => { diff --git a/src/components/common/ItemDetailView.vue b/src/components/common/ItemDetailView.vue index f1184ad..da52e9f 100644 --- a/src/components/common/ItemDetailView.vue +++ b/src/components/common/ItemDetailView.vue @@ -498,11 +498,7 @@ // 如果有星际研究网络,计算有效实验室等级 if (intergalacticResearchNetworkLevel > 0) { - return researchLogic.calculateEffectiveLabLevel( - gameStore.player.planets, - currentPlanet.value.id, - intergalacticResearchNetworkLevel - ) + return researchLogic.calculateEffectiveLabLevel(gameStore.player.planets, currentPlanet.value.id, intergalacticResearchNetworkLevel) } return currentPlanet.value.buildings['researchLab'] || 0 @@ -599,11 +595,7 @@ const miningTechLevel = gameStore.player?.technologies?.[TechnologyType.MiningTechnology] || 0 // 使用增强版计算函数获取带加成的储量上限 - const enhancedDeposits = oreDepositLogic.calculateEnhancedDeposits( - deposits.position, - deepDrillingLevel, - miningTechLevel - ) + const enhancedDeposits = oreDepositLogic.calculateEnhancedDeposits(deposits.position, deepDrillingLevel, miningTechLevel) const initial = enhancedDeposits[resourceType] // 百分比基于增强后的上限计算 diff --git a/src/config/gameConfig.ts b/src/config/gameConfig.ts index d16d965..7c7ade4 100644 --- a/src/config/gameConfig.ts +++ b/src/config/gameConfig.ts @@ -538,12 +538,13 @@ export const TECHNOLOGIES: Record = { baseTime: 60, costMultiplier: 2, fleetStorageBonus: 500, // 每级全局增加500舰队仓储 - maxLevel: 10, // 最多10级(最多11个研究队列和11个舰队槽位) + maxLevel: 10, // 最多15级(最多16个研究队列和16个舰队槽位) requirements: { [BuildingType.ResearchLab]: 1 }, levelRequirements: { 3: { [BuildingType.ResearchLab]: 5 }, 5: { [BuildingType.ResearchLab]: 8, [BuildingType.RoboticsFactory]: 5 }, - 8: { [BuildingType.ResearchLab]: 10, [BuildingType.NaniteFactory]: 2 } + 8: { [BuildingType.ResearchLab]: 10, [BuildingType.NaniteFactory]: 2 }, + 12: { [BuildingType.ResearchLab]: 12, [BuildingType.NaniteFactory]: 5 } } }, [TechnologyType.EspionageTechnology]: { @@ -777,7 +778,7 @@ export const TECHNOLOGIES: Record = { }, 8: { [BuildingType.ResearchLab]: 14, - [TechnologyType.ComputerTechnology]: 12, + [TechnologyType.ComputerTechnology]: 10, [BuildingType.NaniteFactory]: 5 } } diff --git a/src/locales/de.ts b/src/locales/de.ts index 66cb61e..99f33a4 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -960,6 +960,7 @@ export default { exporting: 'Exportieren...', exportSuccess: 'Export erfolgreich', exportSuccessWithPath: 'Export erfolgreich, Datei gespeichert unter: {path}', + exportCopiedToClipboard: 'Export erfolgreich, Daten in die Zwischenablage kopiert', storagePermissionDenied: 'Speicherberechtigung verweigert, Datei kann nicht exportiert werden', exportFailed: 'Export fehlgeschlagen, bitte erneut versuchen', importData: 'Daten importieren', diff --git a/src/locales/en.ts b/src/locales/en.ts index fddf992..26b6422 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -978,6 +978,7 @@ export default { exporting: 'Exporting...', exportSuccess: 'Export successful', exportSuccessWithPath: 'Export successful, file saved to: {path}', + exportCopiedToClipboard: 'Export successful, data copied to clipboard', storagePermissionDenied: 'Storage permission denied, cannot export file', exportFailed: 'Export failed, please try again', importData: 'Import Data', diff --git a/src/locales/es-LA.ts b/src/locales/es-LA.ts index c637603..8f3614f 100644 --- a/src/locales/es-LA.ts +++ b/src/locales/es-LA.ts @@ -969,6 +969,7 @@ export default { exporting: 'Exportando...', exportSuccess: 'Exportación exitosa', exportSuccessWithPath: 'Exportación exitosa, archivo guardado en: {path}', + exportCopiedToClipboard: 'Exportación exitosa, datos copiados al portapapeles', storagePermissionDenied: 'Permiso de almacenamiento denegado, no se puede exportar el archivo', exportFailed: 'Exportación fallida, por favor intenta de nuevo', importData: 'Importar Datos', diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 37d33be..d149f87 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -983,6 +983,7 @@ export default { exporting: 'エクスポート中...', exportSuccess: 'エクスポート成功', exportSuccessWithPath: 'エクスポート成功、ファイルの保存先:{path}', + exportCopiedToClipboard: 'エクスポート成功、データがクリップボードにコピーされました', storagePermissionDenied: 'ストレージ権限が拒否されました。ファイルをエクスポートできません', exportFailed: 'エクスポートに失敗しました。もう一度お試しください', importData: 'データインポート', diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 64b930a..bd03c41 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -938,6 +938,7 @@ export default { exporting: '내보내는 중...', exportSuccess: '내보내기 성공', exportSuccessWithPath: '내보내기 성공, 파일 저장 위치: {path}', + exportCopiedToClipboard: '내보내기 성공, 데이터가 클립보드에 복사됨', storagePermissionDenied: '저장소 권한이 거부되어 파일을 내보낼 수 없습니다', exportFailed: '내보내기 실패, 다시 시도해주세요', importData: '데이터 가져오기', diff --git a/src/locales/ru.ts b/src/locales/ru.ts index 9fd7a2a..aa3b1fb 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -961,6 +961,7 @@ export default { exporting: 'Экспорт...', exportSuccess: 'Экспорт успешен', exportSuccessWithPath: 'Экспорт успешен, файл сохранен в: {path}', + exportCopiedToClipboard: 'Экспорт успешен, данные скопированы в буфер обмена', storagePermissionDenied: 'Разрешение на хранение отклонено, невозможно экспортировать файл', exportFailed: 'Экспорт не удался, попробуйте еще раз', importData: 'Импорт данных', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 7daf3c1..cccddf3 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -209,7 +209,7 @@ export default { darkMatterTank: '增加暗物质存储上限', missileSilo: '存储和发射导弹,每级可存储10枚导弹', terraformer: '改造行星地形,每级增加30个可用空间', - lunarBase: '增加月球可用空间,每级+30空间', + lunarBase: '增加月球可用空间,每级+5空间', sensorPhalanx: '侦测周围星系的舰队活动', jumpGate: '瞬间传送舰队到其他月球', planetDestroyerFactory: '建造能够摧毁行星的终极武器', @@ -948,6 +948,7 @@ export default { exporting: '导出中...', exportSuccess: '导出成功', exportSuccessWithPath: '导出成功,文件已保存到:{path}', + exportCopiedToClipboard: '导出成功,数据已复制到剪贴板', storagePermissionDenied: '存储权限被拒绝,无法导出文件', exportFailed: '导出失败,请重试', importData: '导入数据', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 5518fd3..f1d6fd1 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -952,6 +952,7 @@ export default { exporting: '匯出中...', exportSuccess: '匯出成功', exportSuccessWithPath: '匯出成功,檔案已儲存到:{path}', + exportCopiedToClipboard: '匯出成功,資料已複製到剪貼簿', storagePermissionDenied: '儲存權限被拒絕,無法匯出檔案', exportFailed: '匯出失敗,請重試', importData: '匯入資料', diff --git a/src/logic/achievementLogic.ts b/src/logic/achievementLogic.ts index d2bec30..2168d80 100644 --- a/src/logic/achievementLogic.ts +++ b/src/logic/achievementLogic.ts @@ -15,25 +15,37 @@ import { ACHIEVEMENTS, ACHIEVEMENT_MAP, getNextTier, getTierIndex } from '@/conf // 初始化空的成就统计数据 export const initializeAchievementStats = (): AchievementStats => { - const emptyShipRecord = Object.values(ShipType).reduce((acc, type) => { - acc[type] = 0 - return acc - }, {} as Record) + const emptyShipRecord = Object.values(ShipType).reduce( + (acc, type) => { + acc[type] = 0 + return acc + }, + {} as Record + ) - const emptyDefenseRecord = Object.values(DefenseType).reduce((acc, type) => { - acc[type] = 0 - return acc - }, {} as Record) + const emptyDefenseRecord = Object.values(DefenseType).reduce( + (acc, type) => { + acc[type] = 0 + return acc + }, + {} as Record + ) - const emptyBuildingRecord = Object.values(BuildingType).reduce((acc, type) => { - acc[type] = 0 - return acc - }, {} as Record) + const emptyBuildingRecord = Object.values(BuildingType).reduce( + (acc, type) => { + acc[type] = 0 + return acc + }, + {} as Record + ) - const emptyTechRecord = Object.values(TechnologyType).reduce((acc, type) => { - acc[type] = 0 - return acc - }, {} as Record) + const emptyTechRecord = Object.values(TechnologyType).reduce( + (acc, type) => { + acc[type] = 0 + return acc + }, + {} as Record + ) return { // 资源统计 @@ -221,7 +233,8 @@ export const applyAchievementReward = (player: Player, reward: AchievementReward } if (reward.points) { - player.points += reward.points + // 奖励积分存入 bonusPoints,不会被 calculatePlayerPoints 覆盖 + player.bonusPoints = (player.bonusPoints || 0) + reward.points } } diff --git a/src/logic/buildingLogic.ts b/src/logic/buildingLogic.ts index 3e547ab..fca2a1f 100644 --- a/src/logic/buildingLogic.ts +++ b/src/logic/buildingLogic.ts @@ -23,44 +23,30 @@ export const calculateBuildingCost = (buildingType: BuildingType, targetLevel: n /** * 计算建筑升级时间 - * 使用 2moons 公式(调整版): - * 1. 成本系数 = Σ (资源^0.3 / 0.003) - * 2. 时间(秒) = 成本系数 / ((1 + 机器人工厂) × 2^纳米工厂 × 游戏速度) * @param buildingType 建筑类型 * @param targetLevel 目标等级 * @param buildingSpeedBonus 指挥官等提供的速度加成百分比 * @param roboticsFactoryLevel 机器人工厂等级 * @param naniteFactoryLevel 纳米工厂等级 - * @param gameSpeed 游戏速度(默认1) */ export const calculateBuildingTime = ( buildingType: BuildingType, targetLevel: number, buildingSpeedBonus: number = 0, roboticsFactoryLevel: number = 0, - naniteFactoryLevel: number = 0, - gameSpeed: number = 1 + naniteFactoryLevel: number = 0 ): number => { - // 计算该等级的成本 - const cost = calculateBuildingCost(buildingType, targetLevel) + const config = BUILDINGS[buildingType] + const multiplier = Math.pow(config.costMultiplier, targetLevel - 1) + const baseTime = config.baseTime * multiplier - // 2moons 公式:成本系数 = Σ (资源^0.3 / 0.003) - let elementCost = 0 - if (cost.metal > 0) elementCost += Math.pow(cost.metal, 0.3) / 0.003 - if (cost.crystal > 0) elementCost += Math.pow(cost.crystal, 0.3) / 0.003 - if (cost.deuterium > 0) elementCost += Math.pow(cost.deuterium, 0.3) / 0.003 - - // 机器人工厂和纳米工厂的加速 - const factoryBonus = (1 + roboticsFactoryLevel) * Math.pow(2, naniteFactoryLevel) - - // 简化公式:时间(秒) = 成本系数 / (工厂加成 × 游戏速度) - const timeInSeconds = elementCost / (factoryBonus * gameSpeed) + // 机器人工厂和纳米工厂的加速:建造时间 / (1 + 机器人工厂等级 + 纳米工厂等级 × 2) + const factorySpeedDivisor = 1 + roboticsFactoryLevel + naniteFactoryLevel * 2 // 指挥官等的百分比加成 const speedMultiplier = 1 - buildingSpeedBonus / 100 - // 确保最小时间为5秒 - return Math.max(5, Math.floor(timeInSeconds * speedMultiplier)) + return Math.floor((baseTime / factorySpeedDivisor) * speedMultiplier) } /** @@ -78,38 +64,80 @@ export const calculateUsedSpace = (planet: Planet): number => { } /** - * 检查建筑升级条件 + * 检查建筑升级条件(包括基础要求和等级门槛要求) */ export const checkBuildingRequirements = ( buildingType: BuildingType, planet: Planet, - technologies: Partial> + technologies: Partial>, + targetLevel?: number ): boolean => { const config = BUILDINGS[buildingType] + const currentLevel = planet.buildings[buildingType] || 0 + const level = targetLevel ?? currentLevel + 1 + + // 检查基础 requirements const requirements = (config as any).requirements - if (!requirements) return true + if (requirements) { + for (const [key, reqLevel] of Object.entries(requirements)) { + const requiredLevel = reqLevel as number + if (Object.values(BuildingType).includes(key as BuildingType)) { + const requiredBuildingType = key as BuildingType + const requiredBuildingConfig = BUILDINGS[requiredBuildingType] - for (const [key, level] of Object.entries(requirements)) { - const requiredLevel = level as number - if (Object.values(BuildingType).includes(key as BuildingType)) { - const requiredBuildingType = key as BuildingType - const requiredBuildingConfig = BUILDINGS[requiredBuildingType] + // 如果当前是月球,且所需建筑是星球专属建筑(planetOnly),则跳过此前置条件 + // 这允许在月球上建造机器人工厂等建筑,即使它们的前置条件是无法在月球建造的矿场 + if (planet.isMoon && requiredBuildingConfig?.planetOnly) { + continue + } - // 如果当前是月球,且所需建筑是星球专属建筑(planetOnly),则跳过此前置条件 - // 这允许在月球上建造机器人工厂等建筑,即使它们的前置条件是无法在月球建造的矿场 - if (planet.isMoon && requiredBuildingConfig?.planetOnly) { - continue - } - - if ((planet.buildings[requiredBuildingType] || 0) < requiredLevel) { - return false - } - } else if (Object.values(TechnologyType).includes(key as TechnologyType)) { - if ((technologies[key as TechnologyType] || 0) < requiredLevel) { - return false + if ((planet.buildings[requiredBuildingType] || 0) < requiredLevel) { + return false + } + } else if (Object.values(TechnologyType).includes(key as TechnologyType)) { + if ((technologies[key as TechnologyType] || 0) < requiredLevel) { + return false + } } } } + + // 检查等级门槛 levelRequirements + const levelRequirements = (config as any).levelRequirements + if (levelRequirements) { + // 找出所有小于等于目标等级的门槛 + const applicableLevels = Object.keys(levelRequirements) + .map(Number) + .filter(l => l <= level) + .sort((a, b) => a - b) + + for (const threshold of applicableLevels) { + const reqs = levelRequirements[threshold] + if (!reqs) continue + + for (const [key, reqLevel] of Object.entries(reqs)) { + const requiredLevel = reqLevel as number + if (Object.values(BuildingType).includes(key as BuildingType)) { + const requiredBuildingType = key as BuildingType + const requiredBuildingConfig = BUILDINGS[requiredBuildingType] + + // 如果当前是月球,且所需建筑是星球专属建筑(planetOnly),则跳过此前置条件 + if (planet.isMoon && requiredBuildingConfig?.planetOnly) { + continue + } + + if ((planet.buildings[requiredBuildingType] || 0) < requiredLevel) { + return false + } + } else if (Object.values(TechnologyType).includes(key as TechnologyType)) { + if ((technologies[key as TechnologyType] || 0) < requiredLevel) { + return false + } + } + } + } + } + return true } diff --git a/src/logic/campaignLogic.ts b/src/logic/campaignLogic.ts index c2694aa..1f817e5 100644 --- a/src/logic/campaignLogic.ts +++ b/src/logic/campaignLogic.ts @@ -364,7 +364,8 @@ export const claimQuestRewards = ( } if (rewards.points) { - player.points += rewards.points + // 奖励积分存入 bonusPoints,不会被 calculatePlayerPoints 覆盖 + player.bonusPoints = (player.bonusPoints || 0) + rewards.points } if (rewards.ships && currentPlanet) { diff --git a/src/logic/gameLogic.ts b/src/logic/gameLogic.ts index fcaa805..ccee1b6 100644 --- a/src/logic/gameLogic.ts +++ b/src/logic/gameLogic.ts @@ -149,7 +149,33 @@ export const processGameUpdate = ( } } - // 更新所有星球资源(直接同步计算,避免 Worker 通信开销) + // 收集所有新解锁的内容 + const allUnlockedItems: UnlockedItem[] = [] + + // 先完成所有星球的建造队列(升级建筑),确保资源更新时使用新的建筑等级 + player.planets.forEach(planet => { + // 保存完成前的建筑状态 + const previousBuildings = { ...planet.buildings } + + // 检查建造队列 + buildingLogic.completeBuildQueue(planet, now, onPointsEarned, onCompleted) + + // 检查新解锁(只在主星球上检查,避免重复通知) + if (!planet.isMoon && onUnlock) { + const unlockedItems = unlockLogic.checkAllNewlyUnlocked(planet, player.technologies, previousBuildings, previousTechnologies) + allUnlockedItems.push(...unlockedItems) + } + + // 更新星球最大空间 + if (planet.isMoon) { + planet.maxSpace = planetLogic.calculateMoonMaxSpace(planet) + } else { + const terraformingTechLevel = player.technologies[TechnologyType.TerraformingTechnology] || 0 + planet.maxSpace = planetLogic.calculatePlanetMaxSpace(planet, terraformingTechLevel) + } + }) + + // 然后更新所有星球资源(使用新的建筑等级计算生产) // 获取采矿技术等级(用于矿脉恢复上限计算) const miningTechLevel = player.technologies[TechnologyType.MiningTechnology] || 0 // 获取资源研究科技等级 @@ -183,32 +209,6 @@ export const processGameUpdate = ( } }) - // 收集所有新解锁的内容 - const allUnlockedItems: UnlockedItem[] = [] - - // 更新所有星球其他状态 - player.planets.forEach(planet => { - // 保存完成前的建筑状态 - const previousBuildings = { ...planet.buildings } - - // 检查建造队列 - buildingLogic.completeBuildQueue(planet, now, onPointsEarned, onCompleted) - - // 检查新解锁(只在主星球上检查,避免重复通知) - if (!planet.isMoon && onUnlock) { - const unlockedItems = unlockLogic.checkAllNewlyUnlocked(planet, player.technologies, previousBuildings, previousTechnologies) - allUnlockedItems.push(...unlockedItems) - } - - // 更新星球最大空间 - if (planet.isMoon) { - planet.maxSpace = planetLogic.calculateMoonMaxSpace(planet) - } else { - const terraformingTechLevel = player.technologies[TechnologyType.TerraformingTechnology] || 0 - planet.maxSpace = planetLogic.calculatePlanetMaxSpace(planet, terraformingTechLevel) - } - }) - // 检查研究队列 const updatedResearchQueue = researchLogic.completeResearchQueue( player.researchQueue, diff --git a/src/logic/publicLogic.ts b/src/logic/publicLogic.ts index 1833d39..767b8a2 100644 --- a/src/logic/publicLogic.ts +++ b/src/logic/publicLogic.ts @@ -72,7 +72,16 @@ export const checkRequirements = ( for (const [key, requiredLevel] of Object.entries(requirements)) { // 检查是否为建筑类型 if (Object.values(BuildingType).includes(key as BuildingType)) { - const currentLevel = planet.buildings[key as BuildingType] || 0 + const requiredBuildingType = key as BuildingType + const requiredBuildingConfig = BUILDINGS[requiredBuildingType] + + // 如果当前是月球,且所需建筑是星球专属建筑(planetOnly),则跳过此前置条件 + // 这允许在月球上建造机器人工厂等建筑,即使它们的前置条件是无法在月球建造的矿场 + if (planet.isMoon && requiredBuildingConfig?.planetOnly) { + continue + } + + const currentLevel = planet.buildings[requiredBuildingType] || 0 if (currentLevel < requiredLevel) { return false } @@ -164,7 +173,7 @@ export const getMaxFleetMissions = (additionalFleetSlots: number = 0, computerTe * @param level 目标等级 * @returns 总资源成本(金属+水晶+重氢) */ -const calculateBuildingTotalCost = (buildingType: BuildingType, level: number): number => { +export const calculateBuildingTotalCost = (buildingType: BuildingType, level: number): number => { if (level <= 0) return 0 const config = BUILDINGS[buildingType] @@ -192,7 +201,7 @@ const calculateBuildingTotalCost = (buildingType: BuildingType, level: number): * @param level 目标等级 * @returns 总资源成本(金属+水晶+重氢) */ -const calculateTechnologyTotalCost = (techType: TechnologyType, level: number): number => { +export const calculateTechnologyTotalCost = (techType: TechnologyType, level: number): number => { if (level <= 0) return 0 const config = TECHNOLOGIES[techType] @@ -219,7 +228,7 @@ const calculateTechnologyTotalCost = (techType: TechnologyType, level: number): * @param shipType 舰船类型 * @returns 单个舰船的资源成本(金属+水晶+重氢) */ -const calculateShipUnitCost = (shipType: ShipType): number => { +export const calculateShipUnitCost = (shipType: ShipType): number => { const config = SHIPS[shipType] if (!config) return 0 @@ -231,7 +240,7 @@ const calculateShipUnitCost = (shipType: ShipType): number => { * @param defenseType 防御类型 * @returns 单个防御的资源成本(金属+水晶+重氢) */ -const calculateDefenseUnitCost = (defenseType: DefenseType): number => { +export const calculateDefenseUnitCost = (defenseType: DefenseType): number => { const config = DEFENSES[defenseType] if (!config) return 0 @@ -273,6 +282,11 @@ export const calculatePlayerPoints = (player: Player): number => { }) }) - // 每1000资源 = 1积分 - return Math.floor(totalCost / 1000) + // 每1000资源 = 1积分(基础积分) + const basePoints = Math.floor(totalCost / 1000) + + // 加上奖励积分(战役、成就等奖励的积分) + const bonusPoints = player.bonusPoints || 0 + + return basePoints + bonusPoints } diff --git a/src/stores/gameStore.ts b/src/stores/gameStore.ts index 819441a..2f00298 100644 --- a/src/stores/gameStore.ts +++ b/src/stores/gameStore.ts @@ -46,6 +46,7 @@ export const useGameStore = defineStore('game', { giftNotifications: [], giftRejectedNotifications: [], points: 0, + bonusPoints: 0, isGMEnabled: false, // 明确设置 GM 模式默认为 false lastVersionCheckTime: 0, // 最后一次检查版本的时间戳,默认为0 achievementStats: initializeAchievementStats() as AchievementStats, diff --git a/src/types/game.ts b/src/types/game.ts index 3c85510..e3e8454 100644 --- a/src/types/game.ts +++ b/src/types/game.ts @@ -799,6 +799,7 @@ export interface Player { allyDefenseNotifications?: AllyDefenseNotification[] // 友好NPC协防通知 attitudeChangeNotifications?: AttitudeChangeNotification[] // NPC态度变化通知 points: number // 总积分(每1000资源=1分) + bonusPoints?: number // 奖励积分(战役、成就等奖励的积分,不会被重新计算覆盖) isGMEnabled?: boolean // GM模式开关(默认false,通过秘籍激活) lastVersionCheckTime?: number // 最后一次自动检查版本的时间戳(被动检测) lastManualUpdateCheck?: number // 最后一次手动检查更新的时间戳(主动检测) diff --git a/src/views/SettingsView.vue b/src/views/SettingsView.vue index d6ab3a6..e216bf1 100644 --- a/src/views/SettingsView.vue +++ b/src/views/SettingsView.vue @@ -179,8 +179,8 @@ areMainSwitchesOff ? t('settings.notificationsDisabled') : isTypesExpanded - ? t('settings.collapseTypes') - : t('settings.expandTypes') + ? t('settings.collapseTypes') + : t('settings.expandTypes') }}

@@ -591,31 +591,92 @@ const fileName = `${pkg.name}-${new Date().toISOString().slice(0, 10)}-${Date.now()}.json` const jsonString = JSON.stringify(exportData, null, 2) - // Android 保存到公共 Downloads 目录 + // Android/原生平台 if (Capacitor.isNativePlatform()) { - // 检查并请求存储权限 - const permStatus = await Filesystem.checkPermissions() - if (permStatus.publicStorage !== 'granted') { + // 尝试保存到公共 Downloads 目录 + try { + const permStatus = await Filesystem.checkPermissions() + if (permStatus.publicStorage === 'granted') { + const result = await Filesystem.writeFile({ + path: `Download/${fileName}`, + data: jsonString, + directory: Directory.ExternalStorage, + encoding: Encoding.UTF8 + }) + toast.success(t('settings.exportSuccessWithPath', { path: result.uri })) + return + } + + // 尝试请求权限 const reqResult = await Filesystem.requestPermissions() - if (reqResult.publicStorage !== 'granted') { - toast.error(t('settings.storagePermissionDenied')) + if (reqResult.publicStorage === 'granted') { + const result = await Filesystem.writeFile({ + path: `Download/${fileName}`, + data: jsonString, + directory: Directory.ExternalStorage, + encoding: Encoding.UTF8 + }) + toast.success(t('settings.exportSuccessWithPath', { path: result.uri })) + return + } + } catch (extError) { + console.warn('ExternalStorage failed, trying Documents:', extError) + } + + // 备选方案:保存到应用文档目录 (TapPlay 等沙盒环境) + try { + const result = await Filesystem.writeFile({ + path: fileName, + data: jsonString, + directory: Directory.Documents, + encoding: Encoding.UTF8 + }) + toast.success(t('settings.exportSuccessWithPath', { path: result.uri })) + return + } catch (docError) { + console.warn('Documents failed, trying Data:', docError) + } + + // 最后备选:保存到应用数据目录 + try { + const result = await Filesystem.writeFile({ + path: fileName, + data: jsonString, + directory: Directory.Data, + encoding: Encoding.UTF8 + }) + toast.success(t('settings.exportSuccessWithPath', { path: result.uri })) + return + } catch (dataError) { + console.warn('Data directory failed:', dataError) + } + + // 所有文件系统方式都失败,尝试 Web Share API + if (navigator.share && navigator.canShare) { + const file = new File([jsonString], fileName, { type: 'application/json' }) + if (navigator.canShare({ files: [file] })) { + await navigator.share({ + files: [file], + title: t('settings.exportData') + }) + toast.success(t('settings.exportSuccess')) return } } - const result = await Filesystem.writeFile({ - path: `Download/${fileName}`, - data: jsonString, - directory: Directory.ExternalStorage, - encoding: Encoding.UTF8 - }) - toast.success(t('settings.exportSuccessWithPath', { path: result.uri })) - return + // 最后的备选:复制到剪贴板 + try { + await navigator.clipboard.writeText(jsonString) + toast.success(t('settings.exportCopiedToClipboard')) + return + } catch { + toast.error(t('settings.exportFailed')) + } } else { // Web 使用 file-saver saveAs(new Blob([jsonString], { type: 'application/json' }), fileName) + toast.success(t('settings.exportSuccess')) } - toast.success(t('settings.exportSuccess')) } catch (error) { console.error('Export failed:', error) toast.error(t('settings.exportFailed'))