@@ -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 } from '@/types/game'
+ import { MissionType, BuildingType, TechnologyType, DiplomaticEventType, ShipType } from '@/types/game'
import type { FleetMission, NPC, MissileAttack } from '@/types/game'
import { DIPLOMATIC_CONFIG } from '@/config/gameConfig'
import type { VersionInfo } from '@/utils/versionCheck'
@@ -838,8 +838,14 @@
// 计算离线收益(直接同步计算,应用游戏速度)
const bonuses = officerLogic.calculateActiveBonuses(gameStore.player.officers, now)
+ const miningTechLevel = gameStore.player.technologies[TechnologyType.MiningTechnology] || 0
+ const techBonuses = {
+ mineralResearchLevel: gameStore.player.technologies[TechnologyType.MineralResearch] || 0,
+ crystalResearchLevel: gameStore.player.technologies[TechnologyType.CrystalResearch] || 0,
+ fuelResearchLevel: gameStore.player.technologies[TechnologyType.FuelResearch] || 0
+ }
gameStore.player.planets.forEach(planet => {
- resourceLogic.updatePlanetResources(planet, now, bonuses, gameStore.gameSpeed)
+ resourceLogic.updatePlanetResources(planet, now, bonuses, gameStore.gameSpeed, miningTechLevel, techBonuses)
})
// 只在没有NPC星球时才生成(首次加载已有玩家数据时)
@@ -1065,6 +1071,19 @@
const targetNpc = npcStore.npcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
if (targetNpc) {
diplomaticLogic.handleAttackReputation(gameStore.player, targetNpc, attackResult.battleResult, npcStore.npcs, gameStore.locale)
+
+ // 同步战斗损失到NPC的实际星球数据
+ const npcPlanet = targetNpc.planets.find(p => p.id === targetPlanet.id)
+ if (npcPlanet) {
+ // 同步舰队损失
+ Object.entries(attackResult.battleResult.defenderLosses.fleet).forEach(([shipType, lost]) => {
+ npcPlanet.fleet[shipType as ShipType] = Math.max(0, (npcPlanet.fleet[shipType as ShipType] || 0) - lost)
+ })
+ // 同步防御损失(修复后的数据已在targetPlanet中)
+ npcPlanet.defense = { ...targetPlanet.defense }
+ // 同步资源(被掠夺后的)
+ npcPlanet.resources = { ...targetPlanet.resources }
+ }
}
}
@@ -1262,8 +1281,41 @@
}
}
} else if (mission.missionType === MissionType.Destroy) {
- // 处理行星毁灭任务
- const destroyResult = fleetLogic.processDestroyArrival(mission, targetPlanet, gameStore.player)
+ // 处理行星毁灭任务(需要先战斗,再计算毁灭概率)
+ const destroyResult = await fleetLogic.processDestroyArrival(mission, targetPlanet, gameStore.player, null, gameStore.player.planets)
+
+ // 处理战斗报告(如果发生了战斗)
+ if (destroyResult.battleResult) {
+ gameStore.player.battleReports.push(destroyResult.battleResult)
+
+ // 处理战斗对NPC的影响
+ if (targetPlanet) {
+ const targetNpc = npcStore.npcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
+ if (targetNpc) {
+ diplomaticLogic.handleAttackReputation(gameStore.player, targetNpc, destroyResult.battleResult, npcStore.npcs, gameStore.locale)
+
+ // 同步战斗损失到NPC的实际星球数据
+ const npcPlanet = targetNpc.planets.find(p => p.id === targetPlanet.id)
+ if (npcPlanet) {
+ Object.entries(destroyResult.battleResult.defenderLosses.fleet).forEach(([shipType, lost]) => {
+ npcPlanet.fleet[shipType as ShipType] = Math.max(0, (npcPlanet.fleet[shipType as ShipType] || 0) - lost)
+ })
+ npcPlanet.defense = { ...targetPlanet.defense }
+ npcPlanet.resources = { ...targetPlanet.resources }
+ }
+ }
+ }
+ }
+
+ // 处理新生成的月球
+ if (destroyResult.moon) {
+ gameStore.player.planets.push(destroyResult.moon)
+ }
+
+ // 处理残骸场
+ if (destroyResult.debrisField) {
+ universeStore.debrisFields[destroyResult.debrisField.id] = destroyResult.debrisField
+ }
// 更新成就统计 - 行星毁灭
if (destroyResult.success) {
@@ -1303,12 +1355,14 @@
? {
destroyedPlanetName:
targetPlanet?.name ||
- `[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`
+ `[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`,
+ hadBattle: !!destroyResult.battleResult
}
: {
failReason: destroyResult.failReason,
destructionChance: destroyResult.destructionChance,
- deathstarsLost: destroyResult.deathstarsLost
+ deathstarsLost: destroyResult.deathstarsLost,
+ hadBattle: !!destroyResult.battleResult
},
read: false
})
@@ -1387,7 +1441,7 @@
delete universeStore.debrisFields[destroyedDebrisId]
}
} else if (mission.missionType === MissionType.Expedition) {
- // 处理远征任务
+ // 处理探险任务
const expeditionResult = fleetLogic.processExpeditionArrival(mission)
// 确保返回时间正确设置(兼容旧版本任务数据)
@@ -1399,12 +1453,12 @@
mission.returnTime = now + flightDuration
}
- // 更新成就统计 - 远征
+ // 更新成就统计 - 探险
const isSuccessful =
expeditionResult.eventType === 'resources' || expeditionResult.eventType === 'darkMatter' || expeditionResult.eventType === 'fleet'
gameLogic.trackMissionStats(gameStore.player, 'expedition', { successful: isSuccessful })
- // 生成远征任务报告
+ // 生成探险任务报告
if (!gameStore.player.missionReports) {
gameStore.player.missionReports = []
}
@@ -1676,10 +1730,16 @@
// 应用损失到目标星球
missileLogic.applyMissileAttackResult(targetPlanet, impactResult.defenseLosses)
- // 如果目标是NPC的星球,扣除外交好感度
+ // 如果目标是NPC的星球,同步损失到NPC实际数据并扣除外交好感度
if (targetPlanet.ownerId && targetPlanet.ownerId !== gameStore.player.id) {
const targetNpc = npcStore.npcs.find(npc => npc.id === targetPlanet.ownerId)
if (targetNpc) {
+ // 同步防御损失到NPC的实际星球数据
+ const npcPlanet = targetNpc.planets.find(p => p.id === targetPlanet.id)
+ if (npcPlanet) {
+ missileLogic.applyMissileAttackResult(npcPlanet, impactResult.defenseLosses)
+ }
+
// 导弹攻击扣除好感度
const { REPUTATION_CHANGES } = DIPLOMATIC_CONFIG
const reputationLoss = REPUTATION_CHANGES.ATTACK / 2 // 导弹攻击的好感度惩罚是普通攻击的一半
@@ -2220,6 +2280,7 @@
const switchToMoon = () => {
if (moon.value) {
gameStore.currentPlanetId = moon.value.id
+ router.push('/')
}
}
@@ -2227,12 +2288,14 @@
const switchToParentPlanet = () => {
if (planet.value?.parentPlanetId) {
gameStore.currentPlanetId = planet.value.parentPlanetId
+ router.push('/')
}
}
// 切换到指定星球
const switchToPlanet = (planetId: string) => {
gameStore.currentPlanetId = planetId
+ router.push('/')
}
// 切换侧边栏
diff --git a/src/assets/main.css b/src/assets/main.css
index 48c14f6..d9c4528 100644
--- a/src/assets/main.css
+++ b/src/assets/main.css
@@ -72,15 +72,15 @@ html.light {
/* 队列添加动画 - 脉冲效果 */
@keyframes queue-pulse-animation {
0% {
- transform: scale(1);
+ transform: scale3d(1, 1, 1);
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
}
50% {
- transform: scale(1.1);
+ transform: scale3d(1.1, 1.1, 1);
box-shadow: 0 0 0 8px rgba(34, 197, 94, 0);
}
100% {
- transform: scale(1);
+ transform: scale3d(1, 1, 1);
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
}
}
diff --git a/src/components/campaign/QuestMap.vue b/src/components/campaign/QuestMap.vue
index 0de0d1d..05a5ab2 100644
--- a/src/components/campaign/QuestMap.vue
+++ b/src/components/campaign/QuestMap.vue
@@ -9,7 +9,7 @@
diff --git a/src/views/FleetView.vue b/src/views/FleetView.vue
index bed1b85..b9c5dd5 100644
--- a/src/views/FleetView.vue
+++ b/src/views/FleetView.vue
@@ -10,8 +10,8 @@
{{ t(`fleetView.${tab.labelKey}`) }}
-
- {{ gameStore.player.fleetMissions.length }}
+
+ {{ totalMissionsCount }}
✓
@@ -294,13 +294,14 @@
-
+
{{ t('fleetView.noFlightMissions') }}
+
@@ -378,6 +379,44 @@
+
+
+
+
+
+
+
+
+ {{ t('galaxyView.missileAttackTitle') }}
+
+
+ {{ getPlanetName(missileAttack.originPlanetId) }} → [{{ missileAttack.targetPosition.galaxy }}:{{ missileAttack.targetPosition.system }}:{{
+ missileAttack.targetPosition.position
+ }}]
+
+
+
+ {{ t('fleetView.outbound') }}
+
+
+
+
+
+
+
{{ t('galaxyView.missileCount') }}:
+
{{ missileAttack.missileCount }}
+
+
+
+
+
+ {{ t('fleetView.arrivalTime') }}:
+ {{ formatTime(getMissileRemainingTime(missileAttack)) }}
+
+
+
+
+
@@ -591,7 +630,8 @@
Clock,
Check,
Globe,
- Moon
+ Moon,
+ Crosshair
} from 'lucide-vue-next'
import { formatNumber, formatTime } from '@/utils/format'
import * as shipValidation from '@/logic/shipValidation'
@@ -628,6 +668,16 @@
return publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots, computerTechLevel)
})
+ // 飞行中的导弹攻击
+ const flyingMissileAttacks = computed(() => {
+ return gameStore.player.missileAttacks?.filter(m => m.status === 'flying') || []
+ })
+
+ // 总任务数量(舰队任务 + 导弹攻击)
+ const totalMissionsCount = computed(() => {
+ return gameStore.player.fleetMissions.length + flyingMissileAttacks.value.length
+ })
+
const activeTab = ref<'send' | 'missions' | 'jumpGate'>('send')
// Tab 配置
@@ -837,6 +887,8 @@
selectedMission.value = MissionType.Attack
} else if (mission === 'colonize') {
selectedMission.value = MissionType.Colonize
+ } else if (mission === 'recycle') {
+ selectedMission.value = MissionType.Recycle
} else if (gift === '1') {
// 如果有gift参数,设置为运输任务并启用赠送模式
selectedMission.value = MissionType.Transport
@@ -1413,4 +1465,18 @@
return Math.max(0, Math.min(100, (elapsed / total) * 100))
}
}
+
+ // 获取导弹任务剩余时间
+ const getMissileRemainingTime = (missileAttack: any): number => {
+ const now = currentTime.value
+ return Math.max(0, (missileAttack.arrivalTime - now) / 1000)
+ }
+
+ // 获取导弹任务进度
+ const getMissileProgress = (missileAttack: any): number => {
+ const now = currentTime.value
+ const total = missileAttack.arrivalTime - missileAttack.launchTime
+ const elapsed = now - missileAttack.launchTime
+ return Math.max(0, Math.min(100, (elapsed / total) * 100))
+ }
diff --git a/src/views/GalaxyView.vue b/src/views/GalaxyView.vue
index 5c9e85a..9c5b8b9 100644
--- a/src/views/GalaxyView.vue
+++ b/src/views/GalaxyView.vue
@@ -66,7 +66,7 @@
{{ t('galaxyView.myPlanets') }}
-
+
{{ t('galaxyView.selectPlanetToView') }}
@@ -114,7 +114,7 @@
{{ highlightedNpc.name }} ({{ highlightedNpc.planets.length }})
-
+
{{ t('galaxyView.selectPlanetToView') }}
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index 6b3a452..03c3980 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -149,11 +149,11 @@
@keyframes fadeIn {
from {
opacity: 0;
- transform: translateY(-10px);
+ transform: translate3d(0, -10px, 0);
}
to {
opacity: 1;
- transform: translateY(0);
+ transform: translate3d(0, 0, 0);
}
}
diff --git a/src/views/OverviewView.vue b/src/views/OverviewView.vue
index 1cca6d2..eaa773f 100644
--- a/src/views/OverviewView.vue
+++ b/src/views/OverviewView.vue
@@ -22,8 +22,47 @@
+
+
+
+
+
+
+
+
+ {{ t('planet.confirmAbandon') }}
+
+ {{ t('planet.abandonWarning', { name: planet.name }) }}
+
+
+
+ {{ t('common.cancel') }}
+
+ {{ t('planet.confirmAbandonButton') }}
+
+
+
+
+
+
+
+
+
+ {{ t('planet.abandonFailed') }}
+
+ {{ errorMessage }}
+
+
+
+ {{ t('common.confirm') }}
+
+
+
+
@@ -195,25 +234,45 @@
import { useGameStore } from '@/stores/gameStore'
import { useI18n } from '@/composables/useI18n'
import { useGameConfig } from '@/composables/useGameConfig'
- import { computed } from 'vue'
+ import { computed, ref } from 'vue'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
+ import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle
+ } from '@/components/ui/alert-dialog'
import ResourceIcon from '@/components/common/ResourceIcon.vue'
import { formatNumber, getResourceColor } from '@/utils/format'
import { scaleNumber } from '@/utils/speed'
import type { Planet } from '@/types/game'
+ import { TechnologyType } from '@/types/game'
import * as publicLogic from '@/logic/publicLogic'
import * as resourceLogic from '@/logic/resourceLogic'
+ import * as planetLogic from '@/logic/planetLogic'
const gameStore = useGameStore()
const { t } = useI18n()
const { SHIPS } = useGameConfig()
const planet = computed(() => gameStore.currentPlanet)
+
+ // 获取科技加成
+ const techBonuses = computed(() => ({
+ mineralResearchLevel: gameStore.player.technologies[TechnologyType.MineralResearch] || 0,
+ crystalResearchLevel: gameStore.player.technologies[TechnologyType.CrystalResearch] || 0,
+ fuelResearchLevel: gameStore.player.technologies[TechnologyType.FuelResearch] || 0
+ }))
+
const production = computed(() =>
- planet.value ? publicLogic.getResourceProduction(planet.value, gameStore.player.officers, gameStore.gameSpeed) : null
+ planet.value ? publicLogic.getResourceProduction(planet.value, gameStore.player.officers, gameStore.gameSpeed, techBonuses.value) : null
)
const capacity = computed(() => (planet.value ? publicLogic.getResourceCapacity(planet.value, gameStore.player.officers) : null))
@@ -226,7 +285,7 @@
// 资源产量详细breakdown
const productionBreakdown = computed(() => {
if (!planet.value) return null
- return resourceLogic.calculateProductionBreakdown(planet.value, gameStore.player.officers, Date.now(), gameStore.gameSpeed)
+ return resourceLogic.calculateProductionBreakdown(planet.value, gameStore.player.officers, Date.now(), gameStore.gameSpeed, techBonuses.value)
})
// 资源消耗详细breakdown
@@ -286,4 +345,45 @@
gameStore.currentPlanetId = planet.value.parentPlanetId
}
}
+
+ // 放弃殖民地相关
+ const showAbandonDialog = ref(false)
+ const showErrorDialog = ref(false)
+ const errorMessage = ref('')
+
+ // 是否显示放弃按钮(非主星才显示)
+ const canShowAbandonButton = computed(() => {
+ if (!planet.value) return false
+ // 找到主星(第一个非月球星球)
+ const mainPlanet = gameStore.player.planets.find(p => !p.isMoon)
+ // 当前星球不是主星时才显示放弃按钮
+ return mainPlanet && mainPlanet.id !== planet.value.id
+ })
+
+ // 处理放弃殖民地
+ const handleAbandonColony = () => {
+ if (!planet.value) return
+
+ const check = planetLogic.canAbandonColony(gameStore.player.planets, planet.value.id)
+ if (!check.canAbandon) {
+ showAbandonDialog.value = false
+ errorMessage.value = check.reason ? t(check.reason) : t('planet.abandonFailed')
+ showErrorDialog.value = true
+ return
+ }
+
+ // 记录当前星球ID用于后续切换
+ const abandonedPlanetId = planet.value.id
+
+ // 执行放弃
+ gameStore.player.planets = planetLogic.abandonColony(gameStore.player.planets, abandonedPlanetId)
+
+ // 切换到主星
+ const mainPlanet = gameStore.player.planets.find(p => !p.isMoon)
+ if (mainPlanet) {
+ gameStore.currentPlanetId = mainPlanet.id
+ }
+
+ showAbandonDialog.value = false
+ }
diff --git a/src/views/ResearchView.vue b/src/views/ResearchView.vue
index 8eb954a..b4b7749 100644
--- a/src/views/ResearchView.vue
+++ b/src/views/ResearchView.vue
@@ -175,7 +175,8 @@
techType,
currentLevel,
gameStore.player.officers,
- gameStore.player.technologies
+ gameStore.player.technologies,
+ gameStore.player.planets
)
gameStore.player.researchQueue.push(queueItem)
return true
@@ -333,11 +334,33 @@
const getResearchTime = (techType: TechnologyType): number => {
if (!planet.value) return 0
const currentLevel = getTechLevel(techType)
- const researchLabLevel = planet.value.buildings['researchLab'] || 0
+ const intergalacticResearchNetworkLevel = player.value.technologies[TechnologyType.IntergalacticResearchNetwork] || 0
+
+ // 计算有效研究实验室等级(考虑星际研究网络)
+ let researchLabLevel: number
+ if (intergalacticResearchNetworkLevel > 0) {
+ researchLabLevel = researchLogic.calculateEffectiveLabLevel(
+ gameStore.player.planets,
+ planet.value.id,
+ intergalacticResearchNetworkLevel
+ )
+ } else {
+ researchLabLevel = planet.value.buildings['researchLab'] || 0
+ }
+
const energyTechLevel = player.value.technologies['energyTechnology'] || 0
+ const universityLevel = planet.value.buildings['university'] || 0
const bonuses = officerLogic.calculateActiveBonuses(player.value.officers, gameStore.gameTime)
- return researchLogic.calculateTechnologyTime(techType, currentLevel, bonuses.researchSpeedBonus, researchLabLevel, energyTechLevel)
+ return researchLogic.calculateTechnologyTime(
+ techType,
+ currentLevel,
+ bonuses.researchSpeedBonus,
+ researchLabLevel,
+ energyTechLevel,
+ 1,
+ universityLevel
+ )
}
// 检查是否可以添加到等待队列
diff --git a/src/views/SettingsView.vue b/src/views/SettingsView.vue
index e966a90..3e6e798 100644
--- a/src/views/SettingsView.vue
+++ b/src/views/SettingsView.vue
@@ -114,6 +114,15 @@
{{ gameStore.isPaused ? t('settings.resume') : t('settings.pause') }}
+
+
+
+
+
{{ t('settings.battleMode') }}
+
{{ t('settings.battleModeDesc') }}
+
+
(gameStore.battleToFinish = val)" />
+
@@ -581,12 +590,22 @@
const fileName = `${pkg.name}-${new Date().toISOString().slice(0, 10)}-${Date.now()}.json`
const jsonString = JSON.stringify(exportData, null, 2)
- // Android 保存到 Documents 目录
+ // Android 保存到公共 Downloads 目录
if (Capacitor.isNativePlatform()) {
+ // 检查并请求存储权限
+ const permStatus = await Filesystem.checkPermissions()
+ if (permStatus.publicStorage !== 'granted') {
+ const reqResult = await Filesystem.requestPermissions()
+ if (reqResult.publicStorage !== 'granted') {
+ toast.error(t('settings.storagePermissionDenied'))
+ return
+ }
+ }
+
const result = await Filesystem.writeFile({
- path: fileName,
+ path: `Download/${fileName}`,
data: jsonString,
- directory: Directory.Documents,
+ directory: Directory.ExternalStorage,
encoding: Encoding.UTF8
})
toast.success(t('settings.exportSuccessWithPath', { path: result.uri }))
diff --git a/src/views/ShipyardView.vue b/src/views/ShipyardView.vue
index b70b141..72bab0a 100644
--- a/src/views/ShipyardView.vue
+++ b/src/views/ShipyardView.vue
@@ -101,6 +101,20 @@
/>
+
+
+
+
+
+
{{ t('shipyardView.totalCost') }}:
@@ -133,6 +147,36 @@
>
{{ t('queue.addToWaiting') }}
+
+
+
+
{{ t('shipyardView.scrapRefund') }}:
+
+
+
+ {{ t(`resources.${resourceType.key}`) }}:
+
+ +{{ formatNumber(getScrapRefund(shipType)[resourceType.key]) }}
+
+
+
+
+
+
+
@@ -243,6 +287,25 @@
[ShipType.Deathstar]: 0
})
+ // 每种舰船的拆除数量
+ const scrapQuantities = ref
>({
+ [ShipType.LightFighter]: 0,
+ [ShipType.HeavyFighter]: 0,
+ [ShipType.Cruiser]: 0,
+ [ShipType.Battleship]: 0,
+ [ShipType.Battlecruiser]: 0,
+ [ShipType.Bomber]: 0,
+ [ShipType.Destroyer]: 0,
+ [ShipType.SmallCargo]: 0,
+ [ShipType.LargeCargo]: 0,
+ [ShipType.ColonyShip]: 0,
+ [ShipType.Recycler]: 0,
+ [ShipType.EspionageProbe]: 0,
+ [ShipType.SolarSatellite]: 0,
+ [ShipType.DarkMatterHarvester]: 0,
+ [ShipType.Deathstar]: 0
+ })
+
const buildShip = (shipType: ShipType, quantity: number): { success: boolean; reason?: string } => {
if (!gameStore.currentPlanet) return { success: false }
const validation = shipValidation.validateShipBuild(gameStore.currentPlanet, shipType, quantity, gameStore.player.technologies)
@@ -380,4 +443,54 @@
waitingQueueLogic.addToBuildWaitingQueue(planet.value, item)
quantities.value[shipType] = 0
}
+
+ // 计算拆除返还资源
+ const getScrapRefund = (shipType: ShipType) => {
+ const quantity = scrapQuantities.value[shipType]
+ return shipLogic.calculateShipScrapRefund(shipType, quantity)
+ }
+
+ // 检查是否可以拆除
+ const canScrap = (shipType: ShipType): boolean => {
+ if (!planet.value) return false
+
+ const quantity = scrapQuantities.value[shipType]
+ if (quantity <= 0) return false
+
+ const available = planet.value.fleet[shipType] || 0
+ return available >= quantity
+ }
+
+ // 拆除舰船
+ const handleScrap = (shipType: ShipType, _event: MouseEvent) => {
+ // 防抖:防止快速点击
+ if (isProcessing.value) return
+ isProcessing.value = true
+ setTimeout(() => {
+ isProcessing.value = false
+ }, DEBOUNCE_DELAY)
+
+ const quantity = scrapQuantities.value[shipType]
+ if (quantity <= 0) {
+ alertDialogTitle.value = t('shipyardView.inputError')
+ alertDialogMessage.value = t('shipyardView.inputErrorMessage')
+ alertDialogOpen.value = true
+ return
+ }
+
+ if (!gameStore.currentPlanet) return
+
+ const validation = shipValidation.validateShipScrap(gameStore.currentPlanet, shipType, quantity)
+ if (!validation.valid) {
+ alertDialogTitle.value = t('shipyardView.scrapFailed')
+ alertDialogMessage.value = validation.reason ? t(validation.reason) : t('shipyardView.scrapFailedMessage')
+ alertDialogOpen.value = true
+ return
+ }
+
+ // 执行拆除
+ const queueItem = shipValidation.executeShipScrap(gameStore.currentPlanet, shipType, quantity, gameStore.player.officers)
+ gameStore.currentPlanet.buildQueue.push(queueItem)
+ scrapQuantities.value[shipType] = 0
+ }
diff --git a/src/workers/battle.worker.ts b/src/workers/battle.worker.ts
index 05e3591..278a69d 100644
--- a/src/workers/battle.worker.ts
+++ b/src/workers/battle.worker.ts
@@ -304,8 +304,24 @@ const simulateBattle = (attacker: BattleSideData, defender: BattleSideData, maxR
} else if (defenderUnits.length === 0) {
winner = 'attacker'
} else {
- // OGame原版规则:6回合后双方都有剩余单位时判定为平局
- winner = 'draw'
+ // 达到最大回合数后双方都有剩余单位
+ // 如果是战斗到底模式(maxRounds > 6),根据剩余战力判定胜负
+ if (maxRounds > 6) {
+ // 计算剩余战力
+ const attackerPower = attackerUnits.reduce((sum, u) => sum + u.count * u.armor, 0)
+ const defenderPower = defenderUnits.reduce((sum, u) => sum + u.count * u.armor, 0)
+ // 战力差距超过20%判定胜负,否则平局
+ if (attackerPower > defenderPower * 1.2) {
+ winner = 'attacker'
+ } else if (defenderPower > attackerPower * 1.2) {
+ winner = 'defender'
+ } else {
+ winner = 'draw'
+ }
+ } else {
+ // OGame原版规则:6回合后双方都有剩余单位时判定为平局
+ winner = 'draw'
+ }
}
return {
diff --git a/vite.config.ts b/vite.config.ts
index 2be8e01..c4fed7f 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -23,14 +23,7 @@ export default defineConfig(async () => {
background_color: '#000000',
display: 'fullscreen',
orientation: 'any',
- icons: [
- {
- src: 'logo.svg',
- sizes: 'any',
- type: 'image/svg+xml',
- purpose: 'any'
- }
- ]
+ icons: [{ src: 'logo.svg', sizes: 'any', type: 'image/svg+xml', purpose: 'any' }]
},
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,mp3,wav,json}'],
@@ -39,13 +32,7 @@ export default defineConfig(async () => {
{
urlPattern: ({ request }) => request.destination === 'image' || request.destination === 'audio',
handler: 'CacheFirst', // 优先使用缓存
- options: {
- cacheName: 'game-assets',
- expiration: {
- maxEntries: 100,
- maxAgeSeconds: 30 * 24 * 60 * 60 // 缓存 30 天
- }
- }
+ options: { cacheName: 'game-assets', expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 } }
}
]
}
@@ -113,16 +100,14 @@ export default defineConfig(async () => {
// 使用 lightningcss 处理 CSS,自动转换 oklch 等新语法为兼容格式
transformer: 'lightningcss',
lightningcss: {
- // 目标浏览器:Android 5+, iOS 10+, Chrome 60+
+ // 目标浏览器:降低到更保守的版本以支持华为等国产手机 WebView
targets: {
- android: 5 << 16, // Android 5.0
- chrome: 60 << 16, // Chrome 60
- ios_saf: 10 << 16 // iOS Safari 10
+ android: (4 << 16) | (4 << 8), // Android 4.4
+ chrome: 49 << 16, // Chrome 49 (Android 4.4 WebView)
+ ios_saf: (9 << 16) | (3 << 8) // iOS Safari 9.3
},
// 禁用现代 CSS 特性,确保兼容旧版浏览器
- drafts: {
- customMedia: false
- }
+ drafts: { customMedia: false }
}
},
// 优化依赖预构建
diff --git a/调整基础矿脉恢复速度.ini b/调整基础矿脉恢复速度.ini
new file mode 100644
index 0000000..73801a0
--- /dev/null
+++ b/调整基础矿脉恢复速度.ini
@@ -0,0 +1,28 @@
+OGame-Vue-Ts v1.6.0 更新说明
+
+调整基础矿脉恢复速度
+调整地质研究站每级恢复速率
+调整建造死星所需资源
+修复研究等待队列资源扣除但等级不增加的问题
+修复攻击NPC后NPC拥有资源不减少的问题
+修复导弹攻击后反弹道导弹不减少的问题
+修复矿脉上限加成计算错误问题
+为战斗模拟器新增加从侦查报告一键填入的功能
+调整建筑与研究升级时间
+修复文本丢失问题
+调整月球基地初始建造成本
+修复无法派遣舰队到自己星球位置的问题
+修复敌对NPC会无脑派遣舰队回收残骸导致产生更多残骸的恶性循环问题
+修复敌对NPC反复派回收船到玩家星球的问题
+修复NPC无脑进攻问题
+修复贸易邮件大量生成导致淹没其他邮件问题
+添加舰船拆除功能
+添加放弃殖民地功能
+修复从月球切换回母星会触发白屏问题
+修复死星毁灭任务跳过战斗的问题
+修复对战动画偶现卡顿导致弹窗无响应问题
+增加战斗到底模式, 开启后支持最多战斗100回合
+
+添加建筑: 大学
+添加科技: 星际研究网络, 矿物研究, 晶体研究, 燃料研究
+