From 2ed15c4782614df2fb10d61a17fb339ce3adf26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A6=E5=90=9B?= <73606411+setube@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:47:38 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96UI=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E7=BB=93=E6=9E=84=E4=B8=8E=E7=A7=AF=E5=88=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构部分UI组件脚本结构,统一导入风格,提升可维护性。CardUnlockOverlay解锁条件弹窗改为列表展示,提升可读性。修复QueueNotifications滚动区域高度。ScrollableDialogContent增加最大高度。StarsBackground与ParticlesBg组件代码格式优化。App.vue引入玩家积分定时更新逻辑,NPC成长系统补充间谍探测器修复。 --- src/App.vue | 152 +++--- src/components/CardUnlockOverlay.vue | 30 +- src/components/QueueNotifications.vue | 2 +- .../ui/bg-stars/StarsBackground.vue | 185 ++++--- src/components/ui/bg-stars/index.ts | 2 +- .../ui/dialog/ScrollableDialogContent.vue | 2 +- .../ui/particles-bg/ParticlesBg.vue | 452 +++++++++--------- src/components/ui/particles-bg/index.ts | 2 +- src/components/ui/separator/Separator.vue | 34 +- src/components/ui/separator/index.ts | 2 +- src/components/ui/sheet/Sheet.vue | 26 +- src/components/ui/sheet/SheetClose.vue | 19 +- src/components/ui/sheet/SheetContent.vue | 85 ++-- src/components/ui/sheet/SheetDescription.vue | 30 +- src/components/ui/sheet/SheetFooter.vue | 20 +- src/components/ui/sheet/SheetHeader.vue | 19 +- src/components/ui/sheet/SheetOverlay.vue | 31 +- src/components/ui/sheet/SheetTitle.vue | 30 +- src/components/ui/sheet/SheetTrigger.vue | 19 +- src/components/ui/sheet/index.ts | 16 +- src/components/ui/skeleton/Skeleton.vue | 27 +- src/components/ui/skeleton/index.ts | 2 +- src/config/gameConfig.ts | 10 +- src/locales/de.ts | 11 +- src/locales/en.ts | 7 +- src/locales/ja.ts | 11 +- src/locales/ko.ts | 11 +- src/locales/ru.ts | 10 +- src/locales/zh-CN.ts | 7 +- src/locales/zh-TW.ts | 10 +- src/logic/npcBehaviorLogic.ts | 188 ++++++-- src/logic/npcGrowthLogic.ts | 138 ++++-- src/logic/publicLogic.ts | 131 ++++- src/logic/resourceLogic.ts | 2 +- src/logic/shipValidation.ts | 6 +- src/utils/migration.ts | 42 +- src/views/BuildingsView.vue | 78 ++- src/views/DiplomacyView.vue | 117 +++++ src/views/FleetView.vue | 27 +- src/views/GMView.vue | 15 +- src/views/MessagesView.vue | 7 +- src/views/ResearchView.vue | 76 ++- 42 files changed, 1342 insertions(+), 749 deletions(-) diff --git a/src/App.vue b/src/App.vue index bf59852..238823f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -200,49 +200,49 @@ - - -
-
-
- -
- - - - -
-
-
-
+ + +
+
+
+ +
+ + + + +
+
+
+
@@ -329,13 +329,7 @@
- +
@@ -346,16 +340,8 @@
- +
- @@ -477,12 +463,13 @@ import * as npcGrowthLogic from '@/logic/npcGrowthLogic' import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic' import * as diplomaticLogic from '@/logic/diplomaticLogic' + import * as publicLogic from '@/logic/publicLogic' import pkg from '../package.json' import { toast } from 'vue-sonner' import { migrateGameData } from '@/utils/migration' import { checkLatestVersion } from '@/utils/versionCheck' - import {StarsBackground} from "@/components/ui/bg-stars"; - import {ParticlesBg} from "@/components/ui/particles-bg"; + import { StarsBackground } from '@/components/ui/bg-stars' + import { ParticlesBg } from '@/components/ui/particles-bg' // 执行数据迁移(在 store 初始化之前) migrateGameData() @@ -539,6 +526,10 @@ if (Object.keys(universeStore.planets).length === 0) { generateNPCPlanets() } + + // 初始化或更新玩家积分 + gameStore.player.points = publicLogic.calculatePlayerPoints(gameStore.player) + return } gameStore.player = gameLogic.initializePlayer(gameStore.player.id, t('common.playerName')) @@ -547,6 +538,8 @@ gameStore.currentPlanetId = initialPlanet.id // 新玩家初始化时生成NPC星球 generateNPCPlanets() + // 初始化玩家积分 + gameStore.player.points = publicLogic.calculatePlayerPoints(gameStore.player) } const generateNPCPlanets = () => { @@ -562,8 +555,8 @@ const updateGame = async () => { const now = Date.now() - gameStore.gameTime = now if (gameStore.isPaused) return + gameStore.gameTime = now // 检查军官过期 gameLogic.checkOfficersExpiration(gameStore.player.officers, now) // 处理游戏更新(建造队列、研究队列等) @@ -1045,11 +1038,7 @@ if (!gameStore.player.diplomaticRelations) { gameStore.player.diplomaticRelations = {} } - const relation = diplomaticLogic.getOrCreateRelation( - gameStore.player.diplomaticRelations, - gameStore.player.id, - targetNpc.id - ) + const relation = diplomaticLogic.getOrCreateRelation(gameStore.player.diplomaticRelations, gameStore.player.id, targetNpc.id) gameStore.player.diplomaticRelations[targetNpc.id] = diplomaticLogic.updateReputation( relation, reputationLoss, @@ -1123,7 +1112,7 @@ // NPC成长系统更新函数 let npcUpdateCounter = 0 // 累计秒数 - const NPC_UPDATE_INTERVAL = 1 // 每1秒更新一次NPC,确保发育速度与玩家相当 + const NPC_UPDATE_INTERVAL = 5 // 每1秒更新一次NPC,确保发育速度与玩家相当 const updateNPCGrowth = (deltaSeconds: number) => { // 累积时间 @@ -1155,7 +1144,11 @@ difficulty: 'medium' as const, // 默认中等难度 relations: {}, // 外交关系 allies: [], // 盟友列表 - enemies: [] // 敌人列表 + enemies: [], // 敌人列表 + lastSpyTime: 0, // 上次侦查时间 + lastAttackTime: 0, // 上次攻击时间 + fleetMissions: [], // 舰队任务 + playerSpyReports: {} // 对玩家的侦查报告 }) } @@ -1184,6 +1177,11 @@ } } + // 确保所有NPC都有间谍探测器(修复旧版本保存的数据) + if (npcStore.npcs.length > 0) { + npcGrowthLogic.ensureNPCSpyProbes(npcStore.npcs) + } + // 如果没有NPC,直接返回 if (npcStore.npcs.length === 0) { npcUpdateCounter = 0 @@ -1238,6 +1236,7 @@ // 游戏循环定时器 let gameLoop: ReturnType | null = null + let pointsUpdateInterval: ReturnType | null = null let konamiCleanup: (() => void) | null = null let versionCheckInterval: ReturnType | null = null @@ -1255,6 +1254,18 @@ }, interval) } + // 启动积分更新定时器(每10秒更新一次) + const startPointsUpdate = () => { + if (pointsUpdateInterval) { + clearInterval(pointsUpdateInterval) + } + pointsUpdateInterval = setInterval(() => { + if (!gameStore.isPaused) { + gameStore.player.points = publicLogic.calculatePlayerPoints(gameStore.player) + } + }, 10000) // 10秒更新一次 + } + // 监听游戏速度变化,重新启动游戏循环 watch( () => gameStore.gameSpeed, @@ -1275,6 +1286,8 @@ await initGame() // 启动游戏循环 startGameLoop() + // 启动积分更新定时器 + startPointsUpdate() // 启动科乐美秘籍监听 konamiCleanup = setupKonamiCode() @@ -1326,6 +1339,7 @@ // 清理定时器 onUnmounted(() => { if (gameLoop) clearInterval(gameLoop) + if (pointsUpdateInterval) clearInterval(pointsUpdateInterval) if (konamiCleanup) konamiCleanup() if (versionCheckInterval) clearInterval(versionCheckInterval) // 移除队列取消事件监听 diff --git a/src/components/CardUnlockOverlay.vue b/src/components/CardUnlockOverlay.vue index 95e3eba..883dfe9 100644 --- a/src/components/CardUnlockOverlay.vue +++ b/src/components/CardUnlockOverlay.vue @@ -17,8 +17,14 @@ {{ requirementsDialogTitle }} - - {{ requirementsDialogMessage }} + +
+
+ + + {{ req.name }}: Lv {{ req.requiredLevel }} ({{ t('common.current') }}: Lv {{ req.currentLevel }}) +
+
@@ -35,7 +41,7 @@ import { useI18n } from '@/composables/useI18n' import { useGameConfig } from '@/composables/useGameConfig' import { BuildingType, TechnologyType } from '@/types/game' - import { Lock } from 'lucide-vue-next' + import { Lock, Check, X } from 'lucide-vue-next' import { Button } from '@/components/ui/button' import { AlertDialog, @@ -61,7 +67,7 @@ // AlertDialog 状态 const requirementsDialogOpen = ref(false) const requirementsDialogTitle = ref('') - const requirementsDialogMessage = ref('') + const requirementsDialogItems = ref>([]) const isUnlocked = computed(() => { // 如果已经建造过(level > 0),则认为已解锁,不显示遮罩 @@ -70,34 +76,32 @@ return publicLogic.checkRequirements(gameStore.currentPlanet, gameStore.player.technologies, props.requirements) }) - const getRequirementsList = (): string => { - if (!props.requirements || !gameStore.currentPlanet) return '' + const getRequirementsList = (): Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }> => { + if (!props.requirements || !gameStore.currentPlanet) return [] - const lines: string[] = [] + const items: Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }> = [] for (const [key, requiredLevel] of Object.entries(props.requirements)) { // 检查是否为建筑类型 if (Object.values(BuildingType).includes(key as BuildingType)) { const buildingType = key as BuildingType const currentLevel = gameStore.currentPlanet.buildings[buildingType] || 0 const name = BUILDINGS.value[buildingType]?.name || buildingType - const status = currentLevel >= requiredLevel ? '✓' : '✗' - lines.push(`${status} ${name}: Lv ${requiredLevel} (${t('common.current')}: Lv ${currentLevel})`) + items.push({ name, requiredLevel, currentLevel, met: currentLevel >= requiredLevel }) } // 检查是否为科技类型 else if (Object.values(TechnologyType).includes(key as TechnologyType)) { const techType = key as TechnologyType const currentLevel = gameStore.player.technologies[techType] || 0 const name = TECHNOLOGIES.value[techType]?.name || techType - const status = currentLevel >= requiredLevel ? '✓' : '✗' - lines.push(`${status} ${name}: Lv ${requiredLevel} (${t('common.current')}: Lv ${currentLevel})`) + items.push({ name, requiredLevel, currentLevel, met: currentLevel >= requiredLevel }) } } - return lines.join('\n') + return items } const showRequirements = () => { requirementsDialogTitle.value = t('common.requirementsNotMet') - requirementsDialogMessage.value = getRequirementsList() + requirementsDialogItems.value = getRequirementsList() requirementsDialogOpen.value = true } diff --git a/src/components/QueueNotifications.vue b/src/components/QueueNotifications.vue index bf6a88d..c4a1bfb 100644 --- a/src/components/QueueNotifications.vue +++ b/src/components/QueueNotifications.vue @@ -16,7 +16,7 @@

{{ t('queue.title') }}

- +
{{ t('queue.empty') }}
diff --git a/src/components/ui/bg-stars/StarsBackground.vue b/src/components/ui/bg-stars/StarsBackground.vue index ccec34b..a193a2a 100644 --- a/src/components/ui/bg-stars/StarsBackground.vue +++ b/src/components/ui/bg-stars/StarsBackground.vue @@ -1,26 +1,17 @@ diff --git a/src/components/ui/bg-stars/index.ts b/src/components/ui/bg-stars/index.ts index 205c143..f5eec56 100644 --- a/src/components/ui/bg-stars/index.ts +++ b/src/components/ui/bg-stars/index.ts @@ -1 +1 @@ -export { default as StarsBackground } from "./StarsBackground.vue"; +export { default as StarsBackground } from './StarsBackground.vue' diff --git a/src/components/ui/dialog/ScrollableDialogContent.vue b/src/components/ui/dialog/ScrollableDialogContent.vue index 02b8524..678eb47 100644 --- a/src/components/ui/dialog/ScrollableDialogContent.vue +++ b/src/components/ui/dialog/ScrollableDialogContent.vue @@ -17,7 +17,7 @@ -
+
diff --git a/src/components/ui/particles-bg/ParticlesBg.vue b/src/components/ui/particles-bg/ParticlesBg.vue index 13607db..3d9181a 100644 --- a/src/components/ui/particles-bg/ParticlesBg.vue +++ b/src/components/ui/particles-bg/ParticlesBg.vue @@ -1,250 +1,238 @@ diff --git a/src/components/ui/particles-bg/index.ts b/src/components/ui/particles-bg/index.ts index e544838..44a8db0 100644 --- a/src/components/ui/particles-bg/index.ts +++ b/src/components/ui/particles-bg/index.ts @@ -1 +1 @@ -export { default as ParticlesBg } from "./ParticlesBg.vue"; +export { default as ParticlesBg } from './ParticlesBg.vue' diff --git a/src/components/ui/separator/Separator.vue b/src/components/ui/separator/Separator.vue index 78d60ec..370c8f1 100644 --- a/src/components/ui/separator/Separator.vue +++ b/src/components/ui/separator/Separator.vue @@ -1,20 +1,3 @@ - - + + diff --git a/src/components/ui/separator/index.ts b/src/components/ui/separator/index.ts index 4407287..2287bcb 100644 --- a/src/components/ui/separator/index.ts +++ b/src/components/ui/separator/index.ts @@ -1 +1 @@ -export { default as Separator } from "./Separator.vue" +export { default as Separator } from './Separator.vue' diff --git a/src/components/ui/sheet/Sheet.vue b/src/components/ui/sheet/Sheet.vue index 8522f84..7134fa1 100644 --- a/src/components/ui/sheet/Sheet.vue +++ b/src/components/ui/sheet/Sheet.vue @@ -1,19 +1,15 @@ - - + + diff --git a/src/components/ui/sheet/SheetClose.vue b/src/components/ui/sheet/SheetClose.vue index 39a942c..ba38cd8 100644 --- a/src/components/ui/sheet/SheetClose.vue +++ b/src/components/ui/sheet/SheetClose.vue @@ -1,15 +1,12 @@ - - + + diff --git a/src/components/ui/sheet/SheetContent.vue b/src/components/ui/sheet/SheetContent.vue index e0c4b8f..4fdec0f 100644 --- a/src/components/ui/sheet/SheetContent.vue +++ b/src/components/ui/sheet/SheetContent.vue @@ -1,52 +1,21 @@ - - + + diff --git a/src/components/ui/sheet/SheetDescription.vue b/src/components/ui/sheet/SheetDescription.vue index 6c8ba0a..699fb36 100644 --- a/src/components/ui/sheet/SheetDescription.vue +++ b/src/components/ui/sheet/SheetDescription.vue @@ -1,21 +1,17 @@ - - + + diff --git a/src/components/ui/sheet/SheetFooter.vue b/src/components/ui/sheet/SheetFooter.vue index 5fcf751..7ad80fc 100644 --- a/src/components/ui/sheet/SheetFooter.vue +++ b/src/components/ui/sheet/SheetFooter.vue @@ -1,16 +1,12 @@ - - + + diff --git a/src/components/ui/sheet/SheetHeader.vue b/src/components/ui/sheet/SheetHeader.vue index b6305ab..3babc83 100644 --- a/src/components/ui/sheet/SheetHeader.vue +++ b/src/components/ui/sheet/SheetHeader.vue @@ -1,15 +1,12 @@ - - + + diff --git a/src/components/ui/sheet/SheetOverlay.vue b/src/components/ui/sheet/SheetOverlay.vue index 220452a..8805dcc 100644 --- a/src/components/ui/sheet/SheetOverlay.vue +++ b/src/components/ui/sheet/SheetOverlay.vue @@ -1,21 +1,26 @@ - - + + diff --git a/src/components/ui/sheet/SheetTitle.vue b/src/components/ui/sheet/SheetTitle.vue index 889ae54..3f4541e 100644 --- a/src/components/ui/sheet/SheetTitle.vue +++ b/src/components/ui/sheet/SheetTitle.vue @@ -1,21 +1,17 @@ - - + + diff --git a/src/components/ui/sheet/SheetTrigger.vue b/src/components/ui/sheet/SheetTrigger.vue index 41b121d..669d82b 100644 --- a/src/components/ui/sheet/SheetTrigger.vue +++ b/src/components/ui/sheet/SheetTrigger.vue @@ -1,15 +1,12 @@ - - + + diff --git a/src/components/ui/sheet/index.ts b/src/components/ui/sheet/index.ts index 7c70e5d..ee33431 100644 --- a/src/components/ui/sheet/index.ts +++ b/src/components/ui/sheet/index.ts @@ -1,8 +1,8 @@ -export { default as Sheet } from "./Sheet.vue" -export { default as SheetClose } from "./SheetClose.vue" -export { default as SheetContent } from "./SheetContent.vue" -export { default as SheetDescription } from "./SheetDescription.vue" -export { default as SheetFooter } from "./SheetFooter.vue" -export { default as SheetHeader } from "./SheetHeader.vue" -export { default as SheetTitle } from "./SheetTitle.vue" -export { default as SheetTrigger } from "./SheetTrigger.vue" +export { default as Sheet } from './Sheet.vue' +export { default as SheetClose } from './SheetClose.vue' +export { default as SheetContent } from './SheetContent.vue' +export { default as SheetDescription } from './SheetDescription.vue' +export { default as SheetFooter } from './SheetFooter.vue' +export { default as SheetHeader } from './SheetHeader.vue' +export { default as SheetTitle } from './SheetTitle.vue' +export { default as SheetTrigger } from './SheetTrigger.vue' diff --git a/src/components/ui/skeleton/Skeleton.vue b/src/components/ui/skeleton/Skeleton.vue index 0dadcef..3d9a848 100644 --- a/src/components/ui/skeleton/Skeleton.vue +++ b/src/components/ui/skeleton/Skeleton.vue @@ -1,17 +1,14 @@ - - + + diff --git a/src/components/ui/skeleton/index.ts b/src/components/ui/skeleton/index.ts index e5ce72c..be21fad 100644 --- a/src/components/ui/skeleton/index.ts +++ b/src/components/ui/skeleton/index.ts @@ -1 +1 @@ -export { default as Skeleton } from "./Skeleton.vue" +export { default as Skeleton } from './Skeleton.vue' diff --git a/src/config/gameConfig.ts b/src/config/gameConfig.ts index 460846c..0303bcf 100644 --- a/src/config/gameConfig.ts +++ b/src/config/gameConfig.ts @@ -444,12 +444,12 @@ export const TECHNOLOGIES: Record = { [TechnologyType.ComputerTechnology]: { id: TechnologyType.ComputerTechnology, name: '计算机技术', - description: '增加研究队列数量,每级+1队列', + description: '增加研究队列和舰队任务槽位,每级+1队列+1槽位', baseCost: { metal: 0, crystal: 400, deuterium: 600, darkMatter: 0, energy: 0 }, baseTime: 60, costMultiplier: 2, fleetStorageBonus: 500, // 每级全局增加500舰队仓储 - maxLevel: 10, // 最多10级(最多11个研究队列) + maxLevel: 10, // 最多10级(最多11个研究队列和11个舰队槽位) requirements: { [BuildingType.ResearchLab]: 1 }, levelRequirements: { 3: { [BuildingType.ResearchLab]: 5 }, @@ -460,7 +460,7 @@ export const TECHNOLOGIES: Record = { [TechnologyType.EspionageTechnology]: { id: TechnologyType.EspionageTechnology, name: '间谍技术', - description: '提高间谍探测效果,每级提高1级侦查深度', + description: '提高间谍探测效果,每级提高1级侦查深度。侦察等级=己方等级-对方等级+侦察船数/5。≥-1显示舰队,≥1显示防御,≥3显示建筑,≥5显示科技', baseCost: { metal: 200, crystal: 1000, deuterium: 200, darkMatter: 0, energy: 0 }, baseTime: 60, costMultiplier: 2, @@ -530,7 +530,7 @@ export const TECHNOLOGIES: Record = { id: TechnologyType.GravitonTechnology, name: '引力技术', description: '研究引力操纵,死星的必要技术', - baseCost: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 300000, energy: 0 }, + baseCost: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 100000, energy: 0 }, baseTime: 0, costMultiplier: 3, maxLevel: 1, // 只有1级 @@ -872,7 +872,7 @@ export const SHIPS: Record = { id: ShipType.Deathstar, name: '死星', description: '终极武器,能够摧毁整个行星', - cost: { metal: 5000000, crystal: 4000000, deuterium: 1000000, darkMatter: 50000, energy: 0 }, + cost: { metal: 5000000, crystal: 4000000, deuterium: 1000000, darkMatter: 20000, energy: 0 }, buildTime: 600, cargoCapacity: 1000000, attack: 200000, diff --git a/src/locales/de.ts b/src/locales/de.ts index 7249f89..c605ef9 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -32,6 +32,7 @@ export default { goToBuildings: 'Zu Gebäuden', locked: 'Gesperrt', viewRequirements: 'Anforderungen anzeigen', + requirements: 'Anforderungen', requirementsNotMet: 'Anforderungen nicht erfüllt', current: 'Aktuell', level: 'Stufe', @@ -140,6 +141,7 @@ export default { jumpGate: 'Sprungtor', planetDestroyerFactory: 'Planetenzerstörer-Fabrik', buildTime: 'Bauzeit', + build: '', production: 'Produktion', consumption: 'Verbrauch', totalCost: 'Gesamtkosten', @@ -281,8 +283,8 @@ export default { ionTechnology: 'Ionenwaffentechnologie', hyperspaceTechnology: 'Hyperraumsprung-Technologie', plasmaTechnology: 'Plasmawaffentechnologie', - computerTechnology: 'Erhöht Forschungsauftragskapazität, +1 pro Stufe (max 10 Stufen)', - espionageTechnology: 'Verbessert Sondenwirksamkeit, +1 Spionagestufe pro Stufe', + computerTechnology: 'Erhöht Forschungswarteschlange und Flottenmissionsslots, +1 Warteschlange +1 Slot pro Stufe (max 10 Stufen)', + espionageTechnology: 'Verbessert Sondenwirksamkeit, +1 Spionagestufe pro Stufe. Spionagestufe = eigene Stufe - Gegnerstufe + Sonden/5. ≥-1 zeigt Flotte, ≥1 zeigt Verteidigung, ≥3 zeigt Gebäude, ≥5 zeigt Technologien', weaponsTechnology: 'Erhöht Angriffskraft von Schiffen und Verteidigung um 10% pro Stufe', shieldingTechnology: 'Erhöht Schilde von Schiffen und Verteidigung um 10% pro Stufe', armourTechnology: 'Erhöht Panzerung von Schiffen und Verteidigung um 10% pro Stufe', @@ -352,6 +354,7 @@ export default { gmModeActivated: '', upgradeCost: 'Ausbaukosten', buildTime: 'Bauzeit', + build: 'Bauen', upgrade: 'Ausbauen', maxLevelReached: 'Maximale Stufe erreicht', requirementsNotMet: 'Anforderungen nicht erfüllt', @@ -383,6 +386,7 @@ export default { fuelConsumption: 'Treibstoffverbrauch', buildCost: 'Baukosten', buildTime: 'Bauzeit', + build: '', perUnit: 'Pro Einheit', batchCalculator: 'Batch-Rechner', quantity: 'Menge', @@ -413,6 +417,7 @@ export default { armor: 'Panzerung', buildCost: 'Baukosten', buildTime: 'Bauzeit', + build: '', perUnit: 'Pro Einheit', batchCalculator: 'Batch-Rechner', quantity: 'Menge', @@ -426,6 +431,7 @@ export default { shield: 'Schild', armor: 'Panzerung', buildTime: 'Bauzeit', + build: '', seconds: 's', unitCost: 'Stückkosten', buildQuantity: 'Baumenge', @@ -469,6 +475,7 @@ export default { colonize: 'Kolonisieren', spy: 'Spionage', deploy: 'Stationieren', + expedition: 'Expedition', recycle: 'Recyceln', transportResources: 'Ressourcen transportieren', totalCargoCapacity: 'Gesamtladekapazität', diff --git a/src/locales/en.ts b/src/locales/en.ts index 0b79843..e814d0b 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -31,6 +31,7 @@ export default { goToBuildings: 'Go to Buildings', locked: 'Locked', viewRequirements: 'View Requirements', + requirements: 'Requirements', requirementsNotMet: 'Requirements Not Met', current: 'Current', level: 'Level', @@ -280,8 +281,8 @@ export default { ionTechnology: 'Ion weapon technology', hyperspaceTechnology: 'Hyperspace jump technology', plasmaTechnology: 'Plasma weapon technology', - computerTechnology: 'Increases research queue capacity, +1 per level (max 10 levels)', - espionageTechnology: 'Improves spy probe effectiveness, +1 espionage level per level', + computerTechnology: 'Increases research queue and fleet mission slots, +1 queue +1 slot per level (max 10 levels)', + espionageTechnology: 'Improves spy probe effectiveness, +1 espionage level per level. Spy level = your level - enemy level + probes/5. ≥-1 shows fleet, ≥1 shows defense, ≥3 shows buildings, ≥5 shows technologies', weaponsTechnology: 'Increases ship and defense attack power by 10% per level', shieldingTechnology: 'Increases ship and defense shields by 10% per level', armourTechnology: 'Increases ship and defense armour by 10% per level', @@ -353,6 +354,7 @@ export default { level: 'Level', upgradeCost: 'Upgrade Cost', buildTime: 'Build Time', + build: 'Build', upgrade: 'Upgrade', maxLevelReached: 'Max Level Reached', requirementsNotMet: 'Requirements Not Met', @@ -467,6 +469,7 @@ export default { colonize: 'Colonize', spy: 'Spy', deploy: 'Deploy', + expedition: 'Expedition', recycle: 'Recycle', destroy: 'Planet Destruction', transportResources: 'Transport Resources', diff --git a/src/locales/ja.ts b/src/locales/ja.ts index 0b2f9b1..09cb640 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -32,6 +32,7 @@ export default { goToBuildings: '建物へ移動', locked: 'ロック済み', viewRequirements: '必要条件を表示', + requirements: '必要条件', requirementsNotMet: '必要条件が満たされていません', current: '現在', level: 'レベル', @@ -140,6 +141,7 @@ export default { jumpGate: 'ジャンプゲート', planetDestroyerFactory: '惑星破壊工場', buildTime: '建設時間', + build: '', production: '生産量', consumption: '消費', totalCost: '総コスト', @@ -281,8 +283,8 @@ export default { ionTechnology: 'イオン兵器技術', hyperspaceTechnology: 'ハイパースペースジャンプ技術', plasmaTechnology: 'プラズマ兵器技術', - computerTechnology: '研究キュー数を増加、レベル毎に+1(最大10レベル)', - espionageTechnology: 'スパイ探査機の効果を向上、レベル毎に偵察深度+1', + computerTechnology: '研究キューと艦隊任務スロットを増加、レベル毎に+1キュー+1スロット(最大10レベル)', + espionageTechnology: 'スパイ探査機の効果を向上、レベル毎に偵察深度+1。偵察レベル=自分のレベル-相手のレベル+探査機数/5。≥-1で艦隊表示、≥1で防御表示、≥3で建物表示、≥5で技術表示', weaponsTechnology: '艦船と防御の攻撃力をレベル毎に10%増加', shieldingTechnology: '艦船と防御のシールドをレベル毎に10%増加', armourTechnology: '艦船と防御の装甲をレベル毎に10%増加', @@ -341,6 +343,7 @@ export default { fuelConsumption: '燃料消費', buildCost: '建設コスト', buildTime: '建設時間', + build: '', perUnit: 'ユニットあたり', batchCalculator: '一括計算機', quantity: '数量', @@ -368,6 +371,7 @@ export default { gmModeActivated: '', upgradeCost: 'アップグレードコスト', buildTime: '建設時間', + build: '建設', upgrade: 'アップグレード', maxLevelReached: '最大レベルに達しました', requirementsNotMet: '要件が満たされていません', @@ -395,6 +399,7 @@ export default { armor: '装甲', buildCost: '建設コスト', buildTime: '建設時間', + build: '', perUnit: 'ユニットあたり', batchCalculator: '一括計算機', quantity: '数量', @@ -425,6 +430,7 @@ export default { shield: 'シールド', armor: '装甲', buildTime: '建設時間', + build: '', seconds: '秒', unitCost: 'ユニットコスト', buildQuantity: '建造数', @@ -467,6 +473,7 @@ export default { colonize: '植民', spy: '偵察', deploy: '配備', + expedition: '探検', recycle: '回収', transportResources: '資源輸送', totalCargoCapacity: '総積載量', diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 632f3d6..42874a6 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -32,6 +32,7 @@ export default { goToBuildings: '건물로 이동', locked: '잠김', viewRequirements: '요구사항 보기', + requirements: '요구사항', requirementsNotMet: '요구사항 미충족', current: '현재', level: '레벨', @@ -140,6 +141,7 @@ export default { jumpGate: '점프 게이트', planetDestroyerFactory: '행성 파괴 공장', buildTime: '건설 시간', + build: '', production: '생산량', consumption: '소비', totalCost: '총 비용', @@ -281,8 +283,8 @@ export default { ionTechnology: '이온 무기 기술', hyperspaceTechnology: '초공간 점프 기술', plasmaTechnology: '플라즈마 무기 기술', - computerTechnology: '연구 대기열 수 증가, 레벨당 +1 (최대 10레벨)', - espionageTechnology: '스파이 탐사기 효과 향상, 레벨당 정찰 깊이 +1', + computerTechnology: '연구 대기열 및 함대 임무 슬롯 증가, 레벨당 +1 대기열 +1 슬롯 (최대 10레벨)', + espionageTechnology: '스파이 탐사기 효과 향상, 레벨당 정찰 깊이 +1. 정찰 레벨 = 내 레벨 - 상대 레벨 + 탐사기 수/5. ≥-1 함대 표시, ≥1 방어 표시, ≥3 건물 표시, ≥5 기술 표시', weaponsTechnology: '함선과 방어의 공격력 레벨당 10% 증가', shieldingTechnology: '함선과 방어의 실드 레벨당 10% 증가', armourTechnology: '함선과 방어의 장갑 레벨당 10% 증가', @@ -352,6 +354,7 @@ export default { gmModeActivated: '', upgradeCost: '업그레이드 비용', buildTime: '건설 시간', + build: '건설', upgrade: '업그레이드', maxLevelReached: '최대 레벨 도달', requirementsNotMet: '요구 사항 미충족', @@ -382,6 +385,7 @@ export default { fuelConsumption: '연료 소비', buildCost: '건설 비용', buildTime: '건설 시간', + build: '', perUnit: '단위당', batchCalculator: '일괄 계산기', quantity: '수량', @@ -412,6 +416,7 @@ export default { armor: '장갑', buildCost: '건설 비용', buildTime: '건설 시간', + build: '', perUnit: '단위당', batchCalculator: '일괄 계산기', quantity: '수량', @@ -425,6 +430,7 @@ export default { shield: '실드', armor: '장갑', buildTime: '건설 시간', + build: '', seconds: '초', unitCost: '단위 비용', buildQuantity: '건조 수량', @@ -467,6 +473,7 @@ export default { colonize: '식민', spy: '정찰', deploy: '배치', + expedition: '탐험', recycle: '회수', transportResources: '자원 수송', totalCargoCapacity: '총 적재량', diff --git a/src/locales/ru.ts b/src/locales/ru.ts index 9a13bed..8c8bf5c 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -32,6 +32,7 @@ export default { goToBuildings: 'К зданиям', locked: 'Заблокировано', viewRequirements: 'Просмотр требований', + requirements: 'Требования', requirementsNotMet: 'Требования не выполнены', current: 'Текущий', level: 'Уровень', @@ -140,6 +141,7 @@ export default { jumpGate: 'Прыжковые ворота', planetDestroyerFactory: 'Фабрика разрушителей планет', buildTime: 'Время строительства', + build: '', production: 'Производство', consumption: 'Потребление', totalCost: 'Общая стоимость', @@ -281,8 +283,8 @@ export default { ionTechnology: 'Технология ионного оружия', hyperspaceTechnology: 'Технология гиперпространственных прыжков', plasmaTechnology: 'Технология плазменного оружия', - computerTechnology: 'Увеличивает вместимость очереди исследований, +1 за уровень (макс 10 уровней)', - espionageTechnology: 'Повышает эффективность зондов, +1 уровень шпионажа за уровень', + computerTechnology: 'Увеличивает очередь исследований и слоты флотских миссий, +1 очередь +1 слот за уровень (макс 10 уровней)', + espionageTechnology: 'Повышает эффективность зондов, +1 уровень шпионажа за уровень. Уровень разведки = ваш уровень - уровень врага + зонды/5. ≥-1 показывает флот, ≥1 показывает оборону, ≥3 показывает здания, ≥5 показывает технологии', weaponsTechnology: 'Увеличивает силу атаки кораблей и обороны на 10% за уровень', shieldingTechnology: 'Увеличивает щиты кораблей и обороны на 10% за уровень', armourTechnology: 'Увеличивает броню кораблей и обороны на 10% за уровень', @@ -353,6 +355,7 @@ export default { gmModeActivated: '', upgradeCost: 'Стоимость улучшения', buildTime: 'Время строительства', + build: 'Построить', upgrade: 'Улучшить', maxLevelReached: 'Достигнут максимальный уровень', requirementsNotMet: 'Требования не выполнены', @@ -384,6 +387,7 @@ export default { fuelConsumption: 'Расход топлива', buildCost: 'Стоимость постройки', buildTime: 'Время строительства', + build: '', perUnit: 'За единицу', batchCalculator: 'Калькулятор партий', quantity: 'Количество', @@ -414,6 +418,7 @@ export default { armor: 'Броня', buildCost: 'Стоимость постройки', buildTime: 'Время строительства', + build: '', perUnit: 'За единицу', batchCalculator: 'Калькулятор партий', quantity: 'Количество', @@ -470,6 +475,7 @@ export default { colonize: 'Колонизация', spy: 'Разведка', deploy: 'Размещение', + expedition: 'Экспедиция', recycle: 'Переработка', transportResources: 'Транспортировка ресурсов', totalCargoCapacity: 'Общая грузоподъёмность', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index c6447c8..16b4111 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -31,6 +31,7 @@ export default { goToBuildings: '前往建筑页面', locked: '已锁定', viewRequirements: '查看前置条件', + requirements: '前置条件', requirementsNotMet: '前置条件未满足', current: '当前', level: '等级', @@ -281,8 +282,8 @@ export default { ionTechnology: '离子武器技术', hyperspaceTechnology: '超空间跳跃技术', plasmaTechnology: '等离子武器技术', - computerTechnology: '增加研究队列数量,每级+1队列(最多10级)', - espionageTechnology: '提高间谍探测效果,每级提高1级侦查深度', + computerTechnology: '增加研究队列和舰队任务槽位,每级+1队列+1槽位(最多10级)', + espionageTechnology: '提高间谍探测效果,每级提高1级侦查深度。侦察等级=己方等级-对方等级+侦察船数/5。≥-1显示舰队,≥1显示防御,≥3显示建筑,≥5显示科技', weaponsTechnology: '提高舰船和防御的攻击力,每级+10%', shieldingTechnology: '提高舰船和防御的护盾值,每级+10%', armourTechnology: '提高舰船和防御的装甲值,每级+10%', @@ -352,6 +353,7 @@ export default { level: '等级', upgradeCost: '升级消耗', buildTime: '建造时间', + build: '建造', upgrade: '升级', maxLevelReached: '等级已满', requirementsNotMet: '条件不足', @@ -461,6 +463,7 @@ export default { colonize: '殖民', spy: '侦察', deploy: '部署', + expedition: '探险', recycle: '回收', destroy: '行星毁灭', transportResources: '运输资源', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 4d7920a..e16fb3f 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -32,6 +32,7 @@ export default { goToBuildings: '前往建築頁面', locked: '已鎖定', viewRequirements: '查看前置條件', + requirements: '前置條件', requirementsNotMet: '前置條件未滿足', current: '當前', level: '等級', @@ -140,6 +141,7 @@ export default { jumpGate: '跳躍門', planetDestroyerFactory: '行星毀滅者工廠', buildTime: '建造時間', + build: '', production: '產量', consumption: '消耗', totalCost: '累積成本', @@ -283,8 +285,8 @@ export default { ionTechnology: '離子武器技術', hyperspaceTechnology: '超空間跳躍技術', plasmaTechnology: '等離子武器技術', - computerTechnology: '增加研究佇列數量,每級+1佇列(最多10級)', - espionageTechnology: '提高間諜探測效果,每級提高1級偵查深度', + computerTechnology: '增加研究佇列和艦隊任務槽位,每級+1佇列+1槽位(最多10級)', + espionageTechnology: '提高間諜探測效果,每級提高1級偵查深度。偵察等級=己方等級-對方等級+偵察船數/5。≥-1顯示艦隊,≥1顯示防禦,≥3顯示建築,≥5顯示科技', weaponsTechnology: '提高艦船和防禦的攻擊力,每級+10%', shieldingTechnology: '提高艦船和防禦的護盾值,每級+10%', armourTechnology: '提高艦船和防禦的裝甲值,每級+10%', @@ -354,6 +356,7 @@ export default { gmModeActivated: '', upgradeCost: '升級消耗', buildTime: '建造時間', + build: '建造', upgrade: '升級', maxLevelReached: '等級已滿', requirementsNotMet: '條件不足', @@ -384,6 +387,7 @@ export default { fuelConsumption: '燃料消耗', buildCost: '建造成本', buildTime: '建造時間', + build: '', perUnit: '每個單位', batchCalculator: '批量建造計算器', quantity: '數量', @@ -414,6 +418,7 @@ export default { armor: '裝甲', buildCost: '建造成本', buildTime: '建造時間', + build: '', perUnit: '每個單位', batchCalculator: '批量建造計算器', quantity: '數量', @@ -469,6 +474,7 @@ export default { colonize: '殖民', spy: '偵察', deploy: '部署', + expedition: '探險', recycle: '回收', transportResources: '運輸資源', totalCargoCapacity: '總載貨量', diff --git a/src/logic/npcBehaviorLogic.ts b/src/logic/npcBehaviorLogic.ts index 8fb14f0..1efb822 100644 --- a/src/logic/npcBehaviorLogic.ts +++ b/src/logic/npcBehaviorLogic.ts @@ -2,7 +2,7 @@ import type { NPC, Planet, Player, FleetMission, SpyReport, SpiedNotification, I import { MissionType, ShipType, TechnologyType, RelationStatus } from '@/types/game' import * as fleetLogic from './fleetLogic' import * as diplomaticLogic from './diplomaticLogic' -import { DIPLOMATIC_CONFIG } from '@/config/gameConfig' +import { DIPLOMATIC_CONFIG, SHIPS } from '@/config/gameConfig' /** * NPC行为决策系统 @@ -31,47 +31,47 @@ export interface DynamicBehaviorConfig { */ export const calculateDynamicBehavior = (playerPoints: number): DynamicBehaviorConfig => { if (playerPoints < 1000) { - // 新手阶段:NPC很温和 + // 新手阶段:NPC温和但会主动侦查攻击 return { - spyInterval: 2400, // 40分钟侦查一次 - attackInterval: 4800, // 80分钟攻击一次 - attackProbability: 0.15, // 15%概率攻击 + spyInterval: 300, // 5分钟侦查一次(让新玩家快速体验游戏内容) + attackInterval: 600, // 10分钟攻击一次 + attackProbability: 0.4, // 40%概率攻击 minSpyProbes: 1, attackFleetSizeRatio: 0.3 // 只派30%舰队 } } else if (playerPoints < 5000) { - // 初级阶段:NPC稍微激进 + // 初级阶段:NPC比较激进 return { - spyInterval: 1800, // 30分钟侦查一次 - attackInterval: 3600, // 60分钟攻击一次 - attackProbability: 0.25, // 25%概率攻击 + spyInterval: 420, // 7分钟侦查一次 + attackInterval: 900, // 15分钟攻击一次 + attackProbability: 0.45, // 45%概率攻击 minSpyProbes: 2, attackFleetSizeRatio: 0.5 // 派50%舰队 } } else if (playerPoints < 20000) { - // 中级阶段:NPC比较激进 + // 中级阶段:NPC很激进 return { - spyInterval: 1200, // 20分钟侦查一次 - attackInterval: 2400, // 40分钟攻击一次 - attackProbability: 0.4, // 40%概率攻击 + spyInterval: 360, // 6分钟侦查一次 + attackInterval: 720, // 12分钟攻击一次 + attackProbability: 0.55, // 55%概率攻击 minSpyProbes: 3, attackFleetSizeRatio: 0.7 // 派70%舰队 } } else if (playerPoints < 50000) { - // 高级阶段:NPC很激进 + // 高级阶段:NPC非常激进 return { - spyInterval: 900, // 15分钟侦查一次 - attackInterval: 1800, // 30分钟攻击一次 - attackProbability: 0.55, // 55%概率攻击 + spyInterval: 300, // 5分钟侦查一次 + attackInterval: 600, // 10分钟攻击一次 + attackProbability: 0.65, // 65%概率攻击 minSpyProbes: 4, attackFleetSizeRatio: 0.85 // 派85%舰队 } } else { - // 专家阶段:NPC非常激进 + // 专家阶段:NPC极度激进 return { - spyInterval: 600, // 10分钟侦查一次 - attackInterval: 1200, // 20分钟攻击一次 - attackProbability: 0.7, // 70%概率攻击 + spyInterval: 240, // 4分钟侦查一次 + attackInterval: 480, // 8分钟攻击一次 + attackProbability: 0.8, // 80%概率攻击 minSpyProbes: 5, attackFleetSizeRatio: 0.95 // 派95%舰队 } @@ -89,12 +89,12 @@ export const shouldNPCSpyPlayer = (npc: NPC, player: Player, currentTime: number return false } - // 检查外交关系 - 根据关系状态调整侦查概率 + // 检查外交关系 - 只有中立和敌对NPC才会侦查 const relation = npc.relations?.[player.id] if (relation) { if (relation.status === RelationStatus.Friendly) { - // 友好NPC侦查频率降低到50% - return Math.random() < 0.5 + // 友好NPC不侦查玩家 + return false } if (relation.status === RelationStatus.Hostile) { // 敌对NPC必定侦查 @@ -102,6 +102,7 @@ export const shouldNPCSpyPlayer = (npc: NPC, player: Player, currentTime: number } } + // 中立或无关系:正常侦查 return true } @@ -116,7 +117,7 @@ export const shouldNPCAttackPlayer = (npc: NPC, player: Player, currentTime: num return false } - // 检查外交关系 + // 检查外交关系 - 只有中立和敌对NPC才会攻击 const relation = npc.relations?.[player.id] if (relation) { if (relation.status === RelationStatus.Friendly) { @@ -125,11 +126,11 @@ export const shouldNPCAttackPlayer = (npc: NPC, player: Player, currentTime: num } if (relation.status === RelationStatus.Hostile) { // 敌对NPC攻击概率翻倍 - return Math.random() < config.attackProbability * 2.0 + return Math.random() < Math.min(config.attackProbability * 2.0, 1.0) } } - // 中立或无关系:正常概率 + // 中立或无关系:正常概率攻击 return Math.random() < config.attackProbability } @@ -1059,3 +1060,136 @@ export const createNPCRevengeMission = (npc: NPC, allPlanets: Planet[], config: return mission } + +/** + * NPC状态诊断函数 - 用于调试和了解NPC当前状态 + */ +export interface NPCDiagnosticInfo { + npcId: string + npcName: string + difficulty: string + relationStatus: string + reputation: number + canSpy: boolean + canAttack: boolean + spyProbes: number + totalFleetPower: number + lastSpyTime: number + lastAttackTime: number + timeSinceLastSpy: number + timeSinceLastAttack: number + nextSpyIn: number + nextAttackIn: number + attackProbability: number + reasons: string[] +} + +export const diagnoseNPCBehavior = ( + npcs: NPC[], + player: Player, + currentTime: number +): NPCDiagnosticInfo[] => { + const playerPoints = player.points || 0 + const config = calculateDynamicBehavior(playerPoints) + + return npcs.map(npc => { + const planet = npc.planets[0] + const relation = npc.relations?.[player.id] + const reasons: string[] = [] + + // 检查外交关系 + let canSpy = true + let canAttack = true + let relationStatus = '无关系' + let reputation = 0 + + if (relation) { + relationStatus = relation.status === RelationStatus.Friendly ? '友好' : + relation.status === RelationStatus.Hostile ? '敌对' : '中立' + reputation = relation.reputation || 0 + + if (relation.status === RelationStatus.Friendly) { + canSpy = false + canAttack = false + reasons.push('友好NPC不会侦查或攻击玩家') + } else if (relation.status === RelationStatus.Hostile) { + reasons.push('敌对NPC攻击概率翻倍') + } + } + + // 检查侦查探测器数量 + const spyProbes = planet?.fleet?.[ShipType.EspionageProbe] || 0 + if (spyProbes < config.minSpyProbes) { + canSpy = false + reasons.push(`侦查探测器不足 (${spyProbes}/${config.minSpyProbes})`) + } + + // 计算舰队战力 + let totalFleetPower = 0 + if (planet?.fleet) { + Object.entries(planet.fleet).forEach(([shipType, count]) => { + const shipConfig = SHIPS[shipType as ShipType] + if (shipConfig) { + const power = shipConfig.attack + shipConfig.shield + shipConfig.armor / 10 + totalFleetPower += power * (count as number) + } + }) + } + + if (totalFleetPower === 0) { + canAttack = false + reasons.push('没有战斗舰队') + } + + // 时间检查 + const lastSpyTime = npc.lastSpyTime || 0 + const lastAttackTime = npc.lastAttackTime || 0 + const timeSinceLastSpy = Math.floor((currentTime - lastSpyTime) / 1000) + const timeSinceLastAttack = Math.floor((currentTime - lastAttackTime) / 1000) + + const nextSpyIn = Math.max(0, config.spyInterval - timeSinceLastSpy) + const nextAttackIn = Math.max(0, config.attackInterval - timeSinceLastAttack) + + if (timeSinceLastSpy < config.spyInterval) { + reasons.push(`侦查冷却中 (${Math.floor(nextSpyIn / 60)}分${nextSpyIn % 60}秒)`) + } + + if (timeSinceLastAttack < config.attackInterval) { + reasons.push(`攻击冷却中 (${Math.floor(nextAttackIn / 60)}分${nextAttackIn % 60}秒)`) + } + + // 检查是否已经侦查过玩家 + const hasSpiedPlayer = npc.playerSpyReports && Object.keys(npc.playerSpyReports).length > 0 + + if (!hasSpiedPlayer && canAttack) { + canAttack = false + reasons.push('尚未侦查过玩家,无法攻击') + } + + // 计算实际攻击概率 + let actualAttackProbability = config.attackProbability + if (relation?.status === RelationStatus.Hostile) { + actualAttackProbability = Math.min(config.attackProbability * 2.0, 1.0) + } + + return { + npcId: npc.id, + npcName: npc.name, + difficulty: npc.difficulty, + relationStatus, + reputation, + canSpy, + canAttack, + spyProbes, + totalFleetPower: Math.floor(totalFleetPower), + lastSpyTime, + lastAttackTime, + timeSinceLastSpy, + timeSinceLastAttack, + nextSpyIn, + nextAttackIn, + attackProbability: actualAttackProbability, + reasons + } + }) +} diff --git a/src/logic/npcGrowthLogic.ts b/src/logic/npcGrowthLogic.ts index 77eb4d7..0e60b49 100644 --- a/src/logic/npcGrowthLogic.ts +++ b/src/logic/npcGrowthLogic.ts @@ -23,25 +23,25 @@ export interface NPCGrowthGameState { // NPC成长配置(旧版,保留用于兼容) export const NPC_GROWTH_CONFIG = { easy: { - powerRatio: 0.6, // 实力比例(相对玩家) + powerRatio: 1.0, // 实力比例(相对玩家) - 提升到1.0,与玩家势均力敌 checkInterval: 300, // 检查间隔(秒) - 5分钟 - resourceGrowthRate: 0.5, // 资源增长速率系数 - buildingGrowthSpeed: 0.5, // 建筑升级速度系数 - techGrowthSpeed: 0.5 // 科技研究速度系数 + resourceGrowthRate: 1.3, // 资源增长速率系数 - 大幅提升 + buildingGrowthSpeed: 1.0, // 建筑升级速度系数 + techGrowthSpeed: 1.0 // 科技研究速度系数 }, medium: { - powerRatio: 0.8, + powerRatio: 1.5, // 提升到1.5,超越玩家 checkInterval: 180, // 3分钟 - resourceGrowthRate: 0.8, - buildingGrowthSpeed: 0.8, - techGrowthSpeed: 0.8 + resourceGrowthRate: 1.8, // 大幅提升资源增长 + buildingGrowthSpeed: 1.5, + techGrowthSpeed: 1.5 }, hard: { - powerRatio: 1.1, + powerRatio: 2.0, // 提升到2.0,远超玩家 checkInterval: 120, // 2分钟 - resourceGrowthRate: 1.2, - buildingGrowthSpeed: 1.0, - techGrowthSpeed: 1.0 + resourceGrowthRate: 2.5, // 极高资源增长 + buildingGrowthSpeed: 2.0, + techGrowthSpeed: 2.0 } } as const @@ -61,58 +61,58 @@ export const calculateDynamicDifficulty = (playerPoints: number): DynamicDifficu // 积分区间和对应的难度参数 if (playerPoints < 1000) { // 新手期:0-1,000分 - // NPC保持30-50%实力,给予充分发展空间,但资源增长速度加快 - const ratio = 0.3 + (playerPoints / 1000) * 0.2 + // NPC保持50-70%实力,给予发展空间但保持挑战 + const ratio = 0.5 + (playerPoints / 1000) * 0.2 return { powerRatio: ratio, checkInterval: 300, // 5分钟 - resourceGrowthRate: 0.8, // 从0.4提升到0.8,确保NPC有足够资源发育 - buildingGrowthSpeed: 0.6, // 从0.4提升到0.6 - techGrowthSpeed: 0.6 // 从0.4提升到0.6 - } - } else if (playerPoints < 5000) { - // 初级期:1,000-5,000分 - // NPC保持50-70%实力,逐渐增加挑战 - const ratio = 0.5 + ((playerPoints - 1000) / 4000) * 0.2 - return { - powerRatio: ratio, - checkInterval: 240, // 4分钟 - resourceGrowthRate: 1.0, // 从0.6提升到1.0,与玩家资源产出相当 - buildingGrowthSpeed: 0.8, // 从0.6提升到0.8 - techGrowthSpeed: 0.8 // 从0.6提升到0.8 - } - } else if (playerPoints < 20000) { - // 中级期:5,000-20,000分 - // NPC保持70-90%实力,持续挑战 - const ratio = 0.7 + ((playerPoints - 5000) / 15000) * 0.2 - return { - powerRatio: ratio, - checkInterval: 180, // 3分钟 - resourceGrowthRate: 0.8, + resourceGrowthRate: 1.2, // 提升资源增长,确保NPC快速发育 buildingGrowthSpeed: 0.8, techGrowthSpeed: 0.8 } + } else if (playerPoints < 5000) { + // 初级期:1,000-5,000分 + // NPC保持70-110%实力,快速追赶玩家 + const ratio = 0.7 + ((playerPoints - 1000) / 4000) * 0.4 + return { + powerRatio: ratio, + checkInterval: 240, // 4分钟 + resourceGrowthRate: 1.5, // 大幅提升资源增长速度 + buildingGrowthSpeed: 1.2, + techGrowthSpeed: 1.2 + } + } else if (playerPoints < 20000) { + // 中级期:5,000-20,000分 + // NPC保持110-150%实力,形成强大威胁 + const ratio = 1.1 + ((playerPoints - 5000) / 15000) * 0.4 + return { + powerRatio: ratio, + checkInterval: 180, // 3分钟 + resourceGrowthRate: 1.8, // 极大幅提升资源增长 + buildingGrowthSpeed: 1.5, + techGrowthSpeed: 1.5 + } } else if (playerPoints < 50000) { // 高级期:20,000-50,000分 - // NPC保持90-110%实力,与玩家势均力敌 - const ratio = 0.9 + ((playerPoints - 20000) / 30000) * 0.2 + // NPC保持150-200%实力,远超玩家 + const ratio = 1.5 + ((playerPoints - 20000) / 30000) * 0.5 return { powerRatio: ratio, checkInterval: 150, // 2.5分钟 - resourceGrowthRate: 1.0, - buildingGrowthSpeed: 1.0, - techGrowthSpeed: 1.0 + resourceGrowthRate: 2.2, // 极高资源增长 + buildingGrowthSpeed: 1.8, + techGrowthSpeed: 1.8 } } else { // 专家期:50,000+分 - // NPC保持110-130%实力,超越玩家 - const ratio = Math.min(1.3, 1.1 + ((playerPoints - 50000) / 50000) * 0.2) + // NPC保持200-250%实力,成为超强对手 + const ratio = Math.min(2.5, 2.0 + ((playerPoints - 50000) / 50000) * 0.5) return { powerRatio: ratio, checkInterval: 120, // 2分钟 - resourceGrowthRate: 1.2, - buildingGrowthSpeed: 1.2, - techGrowthSpeed: 1.2 + resourceGrowthRate: 2.5, // 极高的资源增长速度 + buildingGrowthSpeed: 2.0, + techGrowthSpeed: 2.0 } } } @@ -543,6 +543,50 @@ export const initializeNPCStartingPower = ( planet.resources.crystal = 50000 * config.powerRatio planet.resources.deuterium = 20000 * config.powerRatio planet.resources.darkMatter = 1000 * config.powerRatio + + // 给予起始舰队(确保NPC能够立即侦查和攻击) + // 使用平方根函数来平滑舰队数量增长,避免初期过于强大 + const fleetRatio = Math.sqrt(config.powerRatio) + + // 间谍探测器 - 必需,用于侦查玩家 + planet.fleet[ShipType.EspionageProbe] = Math.max(5, Math.floor(10 * fleetRatio)) + + // 基础战斗舰队 + planet.fleet[ShipType.LightFighter] = Math.floor(20 * fleetRatio) + planet.fleet[ShipType.HeavyFighter] = Math.floor(10 * fleetRatio) + planet.fleet[ShipType.Cruiser] = Math.floor(5 * fleetRatio) + + // 运输和回收舰船 + planet.fleet[ShipType.SmallCargo] = Math.floor(5 * fleetRatio) + planet.fleet[ShipType.Recycler] = Math.floor(3 * fleetRatio) +} + +/** + * 确保所有NPC都有最低数量的间谍探测器 + * 用于修复旧版本保存的NPC数据 + */ +export const ensureNPCSpyProbes = (npcs: NPC[]): void => { + npcs.forEach(npc => { + const planet = npc.planets[0] + if (!planet) return + + // 如果没有舰队数据,初始化 + if (!planet.fleet) { + planet.fleet = {} as any + } + + // 检查间谍探测器数量 + const currentProbes = planet.fleet[ShipType.EspionageProbe] || 0 + + // 如果没有探测器,根据NPC难度给予基础数量 + if (currentProbes === 0) { + const config = NPC_GROWTH_CONFIG[npc.difficulty] + const fleetRatio = Math.sqrt(config.powerRatio) + planet.fleet[ShipType.EspionageProbe] = Math.max(5, Math.floor(10 * fleetRatio)) + + console.log(`[NPC Migration] Added ${planet.fleet[ShipType.EspionageProbe]} spy probes to NPC ${npc.name}`) + } + }) } /** diff --git a/src/logic/publicLogic.ts b/src/logic/publicLogic.ts index dba4fa4..dfcaad9 100644 --- a/src/logic/publicLogic.ts +++ b/src/logic/publicLogic.ts @@ -3,12 +3,13 @@ * 提供跨模块共享的通用业务逻辑功能 */ -import { BuildingType, TechnologyType } from '@/types/game' -import type { Planet, Resources, Officer, BuildingConfig, TechnologyConfig } from '@/types/game' +import { BuildingType, TechnologyType, ShipType, DefenseType } from '@/types/game' +import type { Planet, Resources, Officer, BuildingConfig, TechnologyConfig, Player } from '@/types/game' import { OfficerType } from '@/types/game' import * as officerLogic from '@/logic/officerLogic' import * as resourceLogic from '@/logic/resourceLogic' import { scaleResources } from '@/utils/speed' +import { BUILDINGS, TECHNOLOGIES, SHIPS, DEFENSES } from '@/config/gameConfig' /** * 获取特定等级的升级条件 @@ -139,8 +140,128 @@ export const getMaxResearchQueue = (technologies: Partial { - return Math.min(1 + additionalFleetSlots, 10) +export const getMaxFleetMissions = (additionalFleetSlots: number = 0, computerTechnologyLevel: number = 0): number => { + return Math.min(1 + computerTechnologyLevel + additionalFleetSlots, 20) +} + +/** + * 计算建筑的总成本(从等级1到目标等级的累计成本) + * @param buildingType 建筑类型 + * @param level 目标等级 + * @returns 总资源成本(金属+水晶+重氢) + */ +const calculateBuildingTotalCost = (buildingType: BuildingType, level: number): number => { + if (level <= 0) return 0 + + const config = BUILDINGS[buildingType] + if (!config) return 0 + + let totalCost = 0 + const { baseCost, costMultiplier } = config + + // 累加从等级1到目标等级的所有成本 + for (let i = 1; i <= level; i++) { + const levelCost = { + metal: Math.floor(baseCost.metal * Math.pow(costMultiplier, i - 1)), + crystal: Math.floor(baseCost.crystal * Math.pow(costMultiplier, i - 1)), + deuterium: Math.floor(baseCost.deuterium * Math.pow(costMultiplier, i - 1)) + } + totalCost += levelCost.metal + levelCost.crystal + levelCost.deuterium + } + + return totalCost +} + +/** + * 计算科技的总成本(从等级1到目标等级的累计成本) + * @param techType 科技类型 + * @param level 目标等级 + * @returns 总资源成本(金属+水晶+重氢) + */ +const calculateTechnologyTotalCost = (techType: TechnologyType, level: number): number => { + if (level <= 0) return 0 + + const config = TECHNOLOGIES[techType] + if (!config) return 0 + + let totalCost = 0 + const { baseCost, costMultiplier } = config + + // 累加从等级1到目标等级的所有成本 + for (let i = 1; i <= level; i++) { + const levelCost = { + metal: Math.floor(baseCost.metal * Math.pow(costMultiplier, i - 1)), + crystal: Math.floor(baseCost.crystal * Math.pow(costMultiplier, i - 1)), + deuterium: Math.floor(baseCost.deuterium * Math.pow(costMultiplier, i - 1)) + } + totalCost += levelCost.metal + levelCost.crystal + levelCost.deuterium + } + + return totalCost +} + +/** + * 计算单个舰船的成本 + * @param shipType 舰船类型 + * @returns 单个舰船的资源成本(金属+水晶+重氢) + */ +const calculateShipUnitCost = (shipType: ShipType): number => { + const config = SHIPS[shipType] + if (!config) return 0 + + return config.cost.metal + config.cost.crystal + config.cost.deuterium +} + +/** + * 计算单个防御的成本 + * @param defenseType 防御类型 + * @returns 单个防御的资源成本(金属+水晶+重氢) + */ +const calculateDefenseUnitCost = (defenseType: DefenseType): number => { + const config = DEFENSES[defenseType] + if (!config) return 0 + + return config.cost.metal + config.cost.crystal + config.cost.deuterium +} + +/** + * 计算玩家的总积分 + * 积分规则:(建筑成本 + 科技成本 + 舰队成本 + 防御成本) / 1000 + * @param player 玩家对象 + * @returns 玩家总积分 + */ +export const calculatePlayerPoints = (player: Player): number => { + let totalCost = 0 + + // 1. 计算所有星球的建筑成本 + player.planets.forEach(planet => { + Object.entries(planet.buildings).forEach(([buildingType, level]) => { + totalCost += calculateBuildingTotalCost(buildingType as BuildingType, level) + }) + }) + + // 2. 计算科技成本 + Object.entries(player.technologies).forEach(([techType, level]) => { + totalCost += calculateTechnologyTotalCost(techType as TechnologyType, level) + }) + + // 3. 计算所有星球的舰队成本 + player.planets.forEach(planet => { + Object.entries(planet.fleet).forEach(([shipType, count]) => { + totalCost += calculateShipUnitCost(shipType as ShipType) * count + }) + }) + + // 4. 计算所有星球的防御成本 + player.planets.forEach(planet => { + Object.entries(planet.defense).forEach(([defenseType, count]) => { + totalCost += calculateDefenseUnitCost(defenseType as DefenseType) * count + }) + }) + + // 每1000资源 = 1积分 + return Math.floor(totalCost / 1000) } diff --git a/src/logic/resourceLogic.ts b/src/logic/resourceLogic.ts index 3d0d9f2..3af996a 100644 --- a/src/logic/resourceLogic.ts +++ b/src/logic/resourceLogic.ts @@ -76,7 +76,7 @@ export const calculateResourceProduction = ( metal: metalMineLevel * 1500 * Math.pow(1.5, metalMineLevel) * resourceBonus * productionEfficiency, crystal: crystalMineLevel * 1000 * Math.pow(1.5, crystalMineLevel) * resourceBonus * productionEfficiency, deuterium: deuteriumSynthesizerLevel * 500 * Math.pow(1.5, deuteriumSynthesizerLevel) * resourceBonus * productionEfficiency, - darkMatter: darkMatterCollectorLevel * 25 * Math.pow(1.5, darkMatterCollectorLevel) * darkMatterBonus, + darkMatter: darkMatterCollectorLevel * 100 * Math.pow(1.5, darkMatterCollectorLevel) * darkMatterBonus, energy: energyProduction } } diff --git a/src/logic/shipValidation.ts b/src/logic/shipValidation.ts index b1936b7..ba43154 100644 --- a/src/logic/shipValidation.ts +++ b/src/logic/shipValidation.ts @@ -149,7 +149,8 @@ export const validateFleetDispatch = ( fleet: Partial, cargo: Resources, officers: Record, - currentFleetMissions: number = 0 + currentFleetMissions: number = 0, + technologies: Partial> = {} ): { valid: boolean reason?: string @@ -159,7 +160,8 @@ export const validateFleetDispatch = ( const bonuses = officerLogic.calculateActiveBonuses(officers, Date.now()) // 检查舰队任务槽位是否已满 - const maxFleetMissions = publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots) + const computerTechLevel = technologies[TechnologyType.ComputerTechnology] || 0 + const maxFleetMissions = publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots, computerTechLevel) if (currentFleetMissions >= maxFleetMissions) { return { valid: false, reason: 'errors.fleetMissionsFull' } } diff --git a/src/utils/migration.ts b/src/utils/migration.ts index e62a349..79c478f 100644 --- a/src/utils/migration.ts +++ b/src/utils/migration.ts @@ -1,4 +1,4 @@ -import type { Planet, DebrisField } from '@/types/game' +import type { Planet, DebrisField, NPC } from '@/types/game' import { decryptData, encryptData } from './crypto' import pkg from '../../package.json' @@ -65,10 +65,48 @@ export const migrateGameData = (): void => { universeData.debrisFields = oldData.debrisFields delete oldData.debrisFields } + + // 修复NPC数据(确保所有必需字段都存在) + if (oldData.npcs && Array.isArray(oldData.npcs)) { + oldData.npcs.forEach((npc: NPC) => { + // 确保NPC有必需的时间字段 + if (npc.lastSpyTime === undefined) { + npc.lastSpyTime = 0 + } + if (npc.lastAttackTime === undefined) { + npc.lastAttackTime = 0 + } + // 确保NPC有必需的数组字段 + if (!npc.fleetMissions) { + npc.fleetMissions = [] + } + if (!npc.playerSpyReports) { + npc.playerSpyReports = {} + } + if (!npc.relations) { + npc.relations = {} + } + if (!npc.allies) { + npc.allies = [] + } + if (!npc.enemies) { + npc.enemies = [] + } + }) + } + + // 初始化玩家积分(如果不存在) + if (oldData.player && oldData.player.points === undefined) { + // 积分会在游戏启动时通过 initGame 计算,这里设置为0 + oldData.player.points = 0 + } + // 保存迁移后的数据 localStorage.setItem(universeStorageKey, encryptData(universeData)) localStorage.setItem(storageKey, encryptData(oldData)) + + console.log('[Migration] Game data migrated successfully') } catch (error) { - console.error(error) + console.error('[Migration] Failed to migrate game data:', error) } } diff --git a/src/views/BuildingsView.vue b/src/views/BuildingsView.vue index 0a8c443..b66c8dd 100644 --- a/src/views/BuildingsView.vue +++ b/src/views/BuildingsView.vue @@ -128,9 +128,18 @@ {{ alertDialogTitle }} - + {{ alertDialogMessage }} + +
+
+ + + {{ req.name }}: Lv {{ req.requiredLevel }} ({{ t('common.current') }}: Lv {{ req.currentLevel }}) +
+
+
{{ t('common.confirm') }} @@ -179,7 +188,7 @@ AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog' - import { Clock, Grid3x3 } from 'lucide-vue-next' + import { Clock, Grid3x3, Check, X } from 'lucide-vue-next' import { formatNumber, formatTime, getResourceCostColor } from '@/utils/format' import * as buildingLogic from '@/logic/buildingLogic' import * as buildingValidation from '@/logic/buildingValidation' @@ -196,6 +205,8 @@ const alertDialogOpen = ref(false) const alertDialogTitle = ref('') const alertDialogMessage = ref('') + const alertDialogRequirements = ref>([]) + const alertDialogShowRequirements = ref(false) // 拆除确认对话框状态 const demolishConfirmOpen = ref(false) @@ -248,7 +259,9 @@ // 检查前置条件 if (!checkUpgradeRequirements(buildingType)) { alertDialogTitle.value = t('common.requirementsNotMet') - alertDialogMessage.value = getRequirementsList(buildingType) + alertDialogRequirements.value = getRequirementsList(buildingType) + alertDialogShowRequirements.value = true + alertDialogMessage.value = '' alertDialogOpen.value = true return } @@ -257,6 +270,7 @@ if (!result.success) { alertDialogTitle.value = t('buildingsView.upgradeFailed') alertDialogMessage.value = result.reason ? t(result.reason) : t('buildingsView.upgradeFailedMessage') + alertDialogShowRequirements.value = false alertDialogOpen.value = true } } @@ -292,18 +306,23 @@ return t('buildingsView.maxLevelReached') // "等级已满" } - if (planet.value.buildQueue.length > 0) return t('buildingsView.upgrade') + // 0级为建造,1级及以上为升级 + const buttonTextKey = currentLevel === 0 ? 'buildingsView.build' : 'buildingsView.upgrade' + + if (planet.value.buildQueue.length > 0) return t(buttonTextKey) // 检查前置条件 if (!checkUpgradeRequirements(buildingType)) { return t('buildingsView.requirementsNotMet') } - return t('buildingsView.upgrade') + return t(buttonTextKey) } - // 获取前置条件列表文本 - const getRequirementsList = (buildingType: BuildingType): string => { + // 获取前置条件列表 + const getRequirementsList = ( + buildingType: BuildingType + ): Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }> => { const config = BUILDINGS.value[buildingType] const currentLevel = getBuildingLevel(buildingType) const targetLevel = currentLevel + 1 @@ -311,28 +330,59 @@ // 获取目标等级的所有前置条件(包括等级门槛) const requirements = publicLogic.getLevelRequirements(config, targetLevel) - if (!requirements || !planet.value) return '' + if (!requirements || !planet.value) return [] - const lines: string[] = [] + const items: Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }> = [] for (const [key, requiredLevel] of Object.entries(requirements)) { // 检查是否为建筑类型 if (Object.values(BuildingType).includes(key as BuildingType)) { const bt = key as BuildingType const currentLevel = planet.value.buildings[bt] || 0 const name = BUILDINGS.value[bt]?.name || bt - const status = currentLevel >= requiredLevel ? '✓' : '✗' - lines.push(`${status} ${name}: Lv ${requiredLevel} (${t('common.current')}: Lv ${currentLevel})`) + items.push({ name, requiredLevel, currentLevel, met: currentLevel >= requiredLevel }) } // 检查是否为科技类型 else if (Object.values(TechnologyType).includes(key as TechnologyType)) { const tt = key as TechnologyType const currentLevel = gameStore.player.technologies[tt] || 0 const name = TECHNOLOGIES.value[tt]?.name || tt - const status = currentLevel >= requiredLevel ? '✓' : '✗' - lines.push(`${status} ${name}: Lv ${requiredLevel} (${t('common.current')}: Lv ${currentLevel})`) + items.push({ name, requiredLevel, currentLevel, met: currentLevel >= requiredLevel }) } } - return lines.join('\n') + return items + } + + // 获取前置条件显示(简化版,用于卡片内显示) + const getRequirementsDisplay = (buildingType: BuildingType): Array<{ name: string; level: number; met: boolean }> => { + if (!planet.value) return [] + + const config = BUILDINGS.value[buildingType] + const currentLevel = getBuildingLevel(buildingType) + const targetLevel = currentLevel + 1 + + // 获取目标等级的所有前置条件(包括等级门槛) + const requirements = publicLogic.getLevelRequirements(config, targetLevel) + + if (!requirements || Object.keys(requirements).length === 0) return [] + + const items: Array<{ name: string; level: number; met: boolean }> = [] + for (const [key, requiredLevel] of Object.entries(requirements)) { + // 检查是否为建筑类型 + if (Object.values(BuildingType).includes(key as BuildingType)) { + const bt = key as BuildingType + const currentLevel = planet.value.buildings[bt] || 0 + const name = BUILDINGS.value[bt]?.name || bt + items.push({ name, level: requiredLevel, met: currentLevel >= requiredLevel }) + } + // 检查是否为科技类型 + else if (Object.values(TechnologyType).includes(key as TechnologyType)) { + const tt = key as TechnologyType + const currentLevel = gameStore.player.technologies[tt] || 0 + const name = TECHNOLOGIES.value[tt]?.name || tt + items.push({ name, level: requiredLevel, met: currentLevel >= requiredLevel }) + } + } + return items } // 检查是否可以升级 diff --git a/src/views/DiplomacyView.vue b/src/views/DiplomacyView.vue index 441f749..5eae437 100644 --- a/src/views/DiplomacyView.vue +++ b/src/views/DiplomacyView.vue @@ -5,8 +5,107 @@

{{ t('diplomacy.title') }}

{{ t('diplomacy.description') }}

+ + + + + + + +
暂无NPC数据
+
+
+
+

{{ diagnostic.npcName }}

+ + {{ diagnostic.relationStatus }} + +
+
+
+ 难度: + {{ diagnostic.difficulty }} +
+
+ 好感度: + {{ diagnostic.reputation }} +
+
+ 侦查探测器: + {{ diagnostic.spyProbes }} +
+
+ 舰队战力: + {{ diagnostic.totalFleetPower }} +
+
+ 可以侦查: + + {{ diagnostic.canSpy ? '是' : '否' }} + +
+
+ 可以攻击: + + {{ diagnostic.canAttack ? '是' : '否' }} + +
+
+ 攻击概率: + {{ (diagnostic.attackProbability * 100).toFixed(0) }}% +
+
+ 下次侦查: + + + + +
+
+ 下次攻击: + + + + +
+
+
+
状态说明:
+
    +
  • {{ reason }}
  • +
+
+
+
+
+
+ @@ -218,10 +317,15 @@ import { useI18n } from '@/composables/useI18n' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Badge } from '@/components/ui/badge' + import { Button } from '@/components/ui/button' + import { Dialog, DialogDescription, DialogTitle } from '@/components/ui/dialog' + import ScrollableDialogContent from '@/components/ui/dialog/ScrollableDialogContent.vue' import { Pagination, PaginationContent, PaginationItem, PaginationNext, PaginationPrevious } from '@/components/ui/pagination' import NpcRelationCard from '@/components/NpcRelationCard.vue' import { RelationStatus } from '@/types/game' import type { DiplomaticRelation } from '@/types/game' + import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic' + import { Search } from 'lucide-vue-next' const gameStore = useGameStore() const npcStore = useNPCStore() @@ -229,6 +333,19 @@ const activeTab = ref('all') + // NPC诊断功能 + const npcDiagnosticOpen = ref(false) + const npcDiagnostics = ref([]) + const behaviorConfig = computed(() => { + return npcBehaviorLogic.calculateDynamicBehavior(gameStore.player.points || 0) + }) + + const showNPCDiagnostic = () => { + const currentTime = Date.now() + npcDiagnostics.value = npcBehaviorLogic.diagnoseNPCBehavior(npcStore.npcs, gameStore.player, currentTime) + npcDiagnosticOpen.value = true + } + // 检测并生成NPC盟友 const initializeNPCAllies = () => { const npcs = npcStore.npcs diff --git a/src/views/FleetView.vue b/src/views/FleetView.vue index 0a6bb4d..8014737 100644 --- a/src/views/FleetView.vue +++ b/src/views/FleetView.vue @@ -156,7 +156,7 @@ -
+
+
+ + +

{{ t('fleetView.totalCargoCapacity') }}: {{ formatNumber(getTotalCargoCapacity()) }} | {{ t('fleetView.used') }}: @@ -344,7 +358,7 @@ import { useGameConfig } from '@/composables/useGameConfig' import { computed, ref, onMounted, onUnmounted, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' - import { ShipType, MissionType, BuildingType } from '@/types/game' + import { ShipType, MissionType, BuildingType, TechnologyType } from '@/types/game' import type { Fleet, Resources } from '@/types/game' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' @@ -366,7 +380,7 @@ AlertDialogTitle } from '@/components/ui/alert-dialog' import UnlockRequirement from '@/components/UnlockRequirement.vue' - import { Sword, Package, Rocket as RocketIcon, Eye, Users, Recycle, Skull, Gift } from 'lucide-vue-next' + import { Sword, Package, Rocket as RocketIcon, Eye, Users, Recycle, Skull, Gift, Compass } from 'lucide-vue-next' import { formatNumber, formatTime } from '@/utils/format' import * as shipValidation from '@/logic/shipValidation' import * as fleetLogic from '@/logic/fleetLogic' @@ -397,7 +411,8 @@ // 计算最大舰队任务槽位 const maxFleetMissions = computed(() => { const bonuses = officerLogic.calculateActiveBonuses(gameStore.player.officers, Date.now()) - return publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots) + const computerTechLevel = gameStore.player.technologies[TechnologyType.ComputerTechnology] || 0 + return publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots, computerTechLevel) }) const activeTab = ref<'fleet' | 'send' | 'missions'>('fleet') @@ -504,6 +519,7 @@ { type: MissionType.Colonize, name: t('fleetView.colonize'), icon: RocketIcon }, { type: MissionType.Spy, name: t('fleetView.spy'), icon: Eye }, { type: MissionType.Deploy, name: t('fleetView.deploy'), icon: Users }, + { type: MissionType.Expedition, name: t('fleetView.expedition'), icon: Compass }, { type: MissionType.Recycle, name: t('fleetView.recycle'), icon: Recycle }, { type: MissionType.Destroy, name: t('fleetView.destroy'), icon: Skull } ]) @@ -616,7 +632,8 @@ fleet, cargo, gameStore.player.officers, - currentMissions + currentMissions, + gameStore.player.technologies ) if (!validation.valid) return false const shouldDeductCargo = missionType === MissionType.Transport diff --git a/src/views/GMView.vue b/src/views/GMView.vue index 788c2c3..68c93e5 100644 --- a/src/views/GMView.vue +++ b/src/views/GMView.vue @@ -331,6 +331,7 @@ } from '@/components/ui/alert-dialog' import { BuildingType, TechnologyType, ShipType, DefenseType, OfficerType } from '@/types/game' import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic' + import * as publicLogic from '@/logic/publicLogic' import { Home } from 'lucide-vue-next' const router = useRouter() @@ -340,6 +341,11 @@ const { t } = useI18n() const { BUILDINGS, TECHNOLOGIES, SHIPS, DEFENSES, OFFICERS } = useGameConfig() + // 更新玩家积分的辅助函数 + const updatePlayerPoints = () => { + gameStore.player.points = publicLogic.calculatePlayerPoints(gameStore.player) + } + const goHome = () => { router.push('/') } @@ -407,22 +413,26 @@ const setBuildingLevel = (building: BuildingType, level: number) => { if (selectedPlanet.value) { selectedPlanet.value.buildings[building] = level + updatePlayerPoints() } } const setTechnologyLevel = (tech: TechnologyType, level: number) => { gameStore.player.technologies[tech] = level + updatePlayerPoints() } const setShipCount = (ship: ShipType, count: number) => { if (selectedPlanet.value) { selectedPlanet.value.fleet[ship] = (selectedPlanet.value.fleet[ship] || 0) + count + updatePlayerPoints() } } const setDefenseCount = (defense: DefenseType, count: number) => { if (selectedPlanet.value) { selectedPlanet.value.defense[defense] = (selectedPlanet.value.defense[defense] || 0) + count + updatePlayerPoints() } } @@ -614,7 +624,7 @@ const maxAllResources = () => { if (!selectedPlanet.value) return - const maxAmount = 1000000000 // 10亿 + const maxAmount = 1000000000000000000 selectedPlanet.value.resources.metal = maxAmount selectedPlanet.value.resources.crystal = maxAmount selectedPlanet.value.resources.deuterium = maxAmount @@ -708,6 +718,9 @@ } }) + // 更新玩家积分(因为建筑/科技/舰队/防御可能已改变) + updatePlayerPoints() + toast.success( t('gmView.completeQueuesSuccess', { buildingCount, diff --git a/src/views/MessagesView.vue b/src/views/MessagesView.vue index 0332997..b759e6b 100644 --- a/src/views/MessagesView.vue +++ b/src/views/MessagesView.vue @@ -4,7 +4,7 @@ - + {{ tab.label }} @@ -555,7 +555,7 @@ import BattleReportDialog from '@/components/BattleReportDialog.vue' import SpyReportDialog from '@/components/SpyReportDialog.vue' import { formatDate } from '@/utils/format' - import { X, Sword, Eye, AlertTriangle, Package, Recycle, Gift, Ban, Check, Users, Skull, Globe } from 'lucide-vue-next' + import { X, Sword, Eye, AlertTriangle, Package, Recycle, Gift, Ban, Check, Users, Skull, Globe, Compass } from 'lucide-vue-next' import type { BattleResult, SpyReport, @@ -837,6 +837,7 @@ [MissionType.Transport]: t('fleetView.transport'), [MissionType.Colonize]: t('fleetView.colonize'), [MissionType.Deploy]: t('fleetView.deploy'), + [MissionType.Expedition]: t('fleetView.expedition'), [MissionType.Recycle]: t('fleetView.recycle'), [MissionType.Destroy]: t('fleetView.destroy'), [MissionType.MissileAttack]: t('galaxyView.missileAttack') @@ -963,6 +964,8 @@ return Recycle case MissionType.Colonize: return Globe + case MissionType.Expedition: + return Compass case MissionType.Destroy: return Skull default: diff --git a/src/views/ResearchView.vue b/src/views/ResearchView.vue index ccc5e58..36c0942 100644 --- a/src/views/ResearchView.vue +++ b/src/views/ResearchView.vue @@ -65,9 +65,18 @@ {{ alertDialogTitle }} - + {{ alertDialogMessage }} + +

+
+ + + {{ req.name }}: Lv {{ req.requiredLevel }} ({{ t('common.current') }}: Lv {{ req.currentLevel }}) +
+
+ {{ t('common.confirm') }} @@ -99,6 +108,7 @@ AlertDialogTitle } from '@/components/ui/alert-dialog' import CardUnlockOverlay from '@/components/CardUnlockOverlay.vue' + import { Check, X } from 'lucide-vue-next' import { formatNumber, getResourceCostColor } from '@/utils/format' import * as publicLogic from '@/logic/publicLogic' import * as researchLogic from '@/logic/researchLogic' @@ -115,6 +125,8 @@ const alertDialogOpen = ref(false) const alertDialogTitle = ref('') const alertDialogMessage = ref('') + const alertDialogRequirements = ref>([]) + const alertDialogShowRequirements = ref(false) // 资源类型配置(用于成本显示) const costResourceTypes = [ @@ -185,8 +197,10 @@ return t('researchView.research') // "研究" } - // 获取前置条件列表文本 - const getRequirementsList = (techType: TechnologyType): string => { + // 获取前置条件列表 + const getRequirementsList = ( + techType: TechnologyType + ): Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }> => { const config = TECHNOLOGIES.value[techType] const currentLevel = getTechLevel(techType) const targetLevel = currentLevel + 1 @@ -194,28 +208,59 @@ // 获取目标等级的所有前置条件(包括等级门槛) const requirements = publicLogic.getLevelRequirements(config, targetLevel) - if (!requirements || !planet.value) return '' + if (!requirements || !planet.value) return [] - const lines: string[] = [] + const items: Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }> = [] for (const [key, requiredLevel] of Object.entries(requirements)) { // 检查是否为建筑类型 if (Object.values(BuildingType).includes(key as BuildingType)) { const bt = key as BuildingType const currentLevel = planet.value.buildings[bt] || 0 const name = BUILDINGS.value[bt]?.name || bt - const status = currentLevel >= requiredLevel ? '✓' : '✗' - lines.push(`${status} ${name}: Lv ${requiredLevel} (${t('common.current')}: Lv ${currentLevel})`) + items.push({ name, requiredLevel, currentLevel, met: currentLevel >= requiredLevel }) } // 检查是否为科技类型 else if (Object.values(TechnologyType).includes(key as TechnologyType)) { const tt = key as TechnologyType const currentLevel = gameStore.player.technologies[tt] || 0 const name = TECHNOLOGIES.value[tt]?.name || tt - const status = currentLevel >= requiredLevel ? '✓' : '✗' - lines.push(`${status} ${name}: Lv ${requiredLevel} (${t('common.current')}: Lv ${currentLevel})`) + items.push({ name, requiredLevel, currentLevel, met: currentLevel >= requiredLevel }) } } - return lines.join('\n') + return items + } + + // 获取前置条件显示(简化版,用于卡片内显示) + const getRequirementsDisplay = (techType: TechnologyType): Array<{ name: string; level: number; met: boolean }> => { + if (!planet.value) return [] + + const config = TECHNOLOGIES.value[techType] + const currentLevel = getTechLevel(techType) + const targetLevel = currentLevel + 1 + + // 获取目标等级的所有前置条件(包括等级门槛) + const requirements = publicLogic.getLevelRequirements(config, targetLevel) + + if (!requirements || Object.keys(requirements).length === 0) return [] + + const items: Array<{ name: string; level: number; met: boolean }> = [] + for (const [key, requiredLevel] of Object.entries(requirements)) { + // 检查是否为建筑类型 + if (Object.values(BuildingType).includes(key as BuildingType)) { + const bt = key as BuildingType + const currentLevel = planet.value.buildings[bt] || 0 + const name = BUILDINGS.value[bt]?.name || bt + items.push({ name, level: requiredLevel, met: currentLevel >= requiredLevel }) + } + // 检查是否为科技类型 + else if (Object.values(TechnologyType).includes(key as TechnologyType)) { + const tt = key as TechnologyType + const currentLevel = gameStore.player.technologies[tt] || 0 + const name = TECHNOLOGIES.value[tt]?.name || tt + items.push({ name, level: requiredLevel, met: currentLevel >= requiredLevel }) + } + } + return items } // 研究科技 @@ -223,7 +268,9 @@ // 检查前置条件 if (!checkUpgradeRequirements(techType)) { alertDialogTitle.value = t('common.requirementsNotMet') - alertDialogMessage.value = getRequirementsList(techType) + alertDialogRequirements.value = getRequirementsList(techType) + alertDialogShowRequirements.value = true + alertDialogMessage.value = '' alertDialogOpen.value = true return } @@ -232,6 +279,7 @@ if (!success) { alertDialogTitle.value = t('researchView.researchFailed') alertDialogMessage.value = t('researchView.researchFailedMessage') + alertDialogShowRequirements.value = false alertDialogOpen.value = true } } @@ -253,6 +301,12 @@ return false } + // 检查队列中是否已存在该科技的研究任务 + const existingQueueItem = player.value.researchQueue.find(item => item.type === 'technology' && item.itemType === techType) + if (existingQueueItem) { + return false + } + // 检查研究队列是否已满 const maxQueue = publicLogic.getMaxResearchQueue(gameStore.player.technologies) if (player.value.researchQueue.length >= maxQueue) {