mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 16:05:12 +08:00
feat: 新增Android平台支持及构建流程
集成Android平台相关目录与配置文件,包含Gradle构建脚本、资源文件、启动图标、Java入口、Proguard规则等,完善.gitignore以排除Android构建产物。更新CI流程,支持自动构建并发布Android APK。移除README中项目结构说明,简化文档。
This commit is contained in:
@@ -7,12 +7,13 @@
|
||||
|
||||
<!-- 标签切换 -->
|
||||
<Tabs v-model="activeTab" class="w-full">
|
||||
<TabsList class="grid w-full grid-cols-3">
|
||||
<TabsTrigger v-for="tab in fleetTabs" :key="tab.value" :value="tab.value">
|
||||
<TabsList :class="['grid', 'w-full', showJumpGateTab ? 'grid-cols-4' : 'grid-cols-3']">
|
||||
<TabsTrigger v-for="tab in visibleTabs" :key="tab.value" :value="tab.value">
|
||||
{{ t(`fleetView.${tab.labelKey}`) }}
|
||||
<Badge v-if="tab.value === 'missions' && gameStore.player.fleetMissions.length > 0" variant="destructive" class="ml-1">
|
||||
{{ gameStore.player.fleetMissions.length }}
|
||||
</Badge>
|
||||
<Badge v-if="tab.value === 'jumpGate' && jumpGateReady" variant="default" class="ml-1">✓</Badge>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
@@ -59,6 +60,80 @@
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- 舰队预设 -->
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<CardTitle>{{ t('fleetView.fleetPresets') }}</CardTitle>
|
||||
<CardDescription>{{ t('fleetView.fleetPresetsDescription') }}</CardDescription>
|
||||
</div>
|
||||
<Button @click="saveAsPreset" variant="outline" size="sm" :disabled="fleetPresets.length >= MAX_PRESETS">
|
||||
<Save class="h-4 w-4 mr-1" />
|
||||
{{ t('fleetView.savePreset') }}
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div v-if="fleetPresets.length === 0" class="text-center py-4 text-muted-foreground text-sm">
|
||||
{{ t('fleetView.noPresets') }}
|
||||
</div>
|
||||
<div v-else class="space-y-2">
|
||||
<div
|
||||
v-for="preset in fleetPresets"
|
||||
:key="preset.id"
|
||||
class="flex items-center justify-between p-3 border rounded-lg hover:bg-muted/50 transition-colors"
|
||||
:class="{ 'ring-2 ring-primary': editingPresetId === preset.id }"
|
||||
>
|
||||
<div class="flex-1 cursor-pointer" @click="loadPreset(preset)">
|
||||
<div class="flex items-center gap-2">
|
||||
<Star class="h-4 w-4 text-yellow-500" />
|
||||
<span class="font-medium">{{ preset.name }}</span>
|
||||
</div>
|
||||
<div class="text-xs text-muted-foreground mt-1 flex flex-wrap gap-2">
|
||||
<span v-if="preset.targetPosition">
|
||||
[{{ preset.targetPosition.galaxy }}:{{ preset.targetPosition.system }}:{{ preset.targetPosition.position }}]
|
||||
</span>
|
||||
<span v-if="preset.missionType">
|
||||
{{ getMissionName(preset.missionType) }}
|
||||
</span>
|
||||
<span>{{ Object.entries(preset.fleet).filter(([_, count]) => count > 0).length }} {{ t('fleetView.shipTypes') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Button v-if="editingPresetId === preset.id" @click="updatePreset(preset.id)" variant="default" size="sm">
|
||||
{{ t('common.save') }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="editingPresetId !== preset.id"
|
||||
@click="editingPresetId = preset.id"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:title="t('fleetView.editPreset')"
|
||||
>
|
||||
<Pencil class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button @click.stop="startRenamePreset(preset)" variant="ghost" size="sm" :title="t('fleetView.renamePreset')">
|
||||
<Type class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@click.stop="deletePreset(preset.id)"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="text-destructive hover:text-destructive"
|
||||
:title="t('fleetView.deletePreset')"
|
||||
>
|
||||
<Trash2 class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="editingPresetId" class="text-xs text-muted-foreground mt-2">
|
||||
{{ t('fleetView.editingPresetHint') }}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- 选择舰队 -->
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -93,13 +168,27 @@
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t('fleetView.targetCoordinates') }}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="grid grid-cols-3 gap-2 sm:gap-4">
|
||||
<div v-for="coord in coordinateFields" :key="coord.key" class="space-y-2">
|
||||
<Label :for="coord.key" class="text-xs sm:text-sm">{{ t(`fleetView.${coord.key}`) }}</Label>
|
||||
<Input :id="coord.key" v-model.number="targetPosition[coord.key]" type="number" :min="1" :max="coord.max" placeholder="1" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 目标类型选择(行星/月球) -->
|
||||
<div v-if="hasMoonAtTargetPosition" class="flex items-center gap-4 p-3 bg-muted/50 rounded-lg">
|
||||
<span class="text-sm font-medium">{{ t('fleetView.targetType') }}:</span>
|
||||
<div class="flex gap-2">
|
||||
<Button @click="targetIsMoon = false" :variant="!targetIsMoon ? 'default' : 'outline'" size="sm">
|
||||
<Globe class="h-4 w-4 mr-1" />
|
||||
{{ t('fleetView.planet') }}
|
||||
</Button>
|
||||
<Button @click="targetIsMoon = true" :variant="targetIsMoon ? 'default' : 'outline'" size="sm">
|
||||
<Moon class="h-4 w-4 mr-1" />
|
||||
{{ t('fleetView.moon') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -278,6 +367,108 @@
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<!-- 跳跃门 -->
|
||||
<TabsContent v-if="showJumpGateTab" value="jumpGate" class="mt-4 space-y-4">
|
||||
<!-- 跳跃门状态 -->
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle class="flex items-center gap-2">
|
||||
<Zap class="h-5 w-5" />
|
||||
{{ t('fleetView.jumpGate') }}
|
||||
</CardTitle>
|
||||
<CardDescription>{{ t('fleetView.jumpGateDescription') }}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<!-- 冷却状态 -->
|
||||
<div v-if="!jumpGateReady" class="p-4 bg-muted/50 rounded-lg">
|
||||
<div class="flex items-center gap-2 text-amber-600 dark:text-amber-400">
|
||||
<Clock class="h-4 w-4" />
|
||||
<span class="font-medium">{{ t('fleetView.jumpGateCooldown') }}</span>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center gap-2">
|
||||
<span class="text-sm text-muted-foreground">{{ t('fleetView.jumpGateCooldownRemaining') }}:</span>
|
||||
<span class="font-bold">{{ formatTime(Math.floor(jumpGateCooldownRemaining / 1000)) }}</span>
|
||||
</div>
|
||||
<Progress :model-value="100 - (jumpGateCooldownRemaining / 3600000) * 100" class="mt-2" />
|
||||
</div>
|
||||
<!-- 就绪状态 -->
|
||||
<div v-else class="p-4 bg-green-500/10 rounded-lg">
|
||||
<div class="flex items-center gap-2 text-green-600 dark:text-green-400">
|
||||
<Check class="h-4 w-4" />
|
||||
<span class="font-medium">{{ t('fleetView.jumpGateReady') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- 选择目标月球 -->
|
||||
<Card v-if="jumpGateReady">
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t('fleetView.jumpGateSelectTarget') }}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div v-if="availableJumpGateMoons.length === 0" class="text-center py-4 text-muted-foreground">
|
||||
{{ t('fleetView.jumpGateNoTargetMoons') }}
|
||||
</div>
|
||||
<div v-else class="space-y-2">
|
||||
<div
|
||||
v-for="moon in availableJumpGateMoons"
|
||||
:key="moon.id"
|
||||
class="p-3 border rounded-lg cursor-pointer transition-colors"
|
||||
:class="selectedJumpGateTarget?.id === moon.id ? 'ring-2 ring-primary bg-primary/10' : 'hover:bg-muted/50'"
|
||||
@click="selectedJumpGateTarget = moon"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<span class="font-medium">{{ moon.name }}</span>
|
||||
<span class="text-sm text-muted-foreground ml-2">
|
||||
[{{ moon.position.galaxy }}:{{ moon.position.system }}:{{ moon.position.position }}]
|
||||
</span>
|
||||
</div>
|
||||
<Badge v-if="isJumpGateMoonReady(moon)" variant="default">{{ t('fleetView.jumpGateReady') }}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- 选择传送舰队 -->
|
||||
<Card v-if="jumpGateReady && selectedJumpGateTarget">
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t('fleetView.jumpGateSelectFleet') }}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
|
||||
<div v-for="(count, shipType) in planet.fleet" :key="shipType" class="space-y-2">
|
||||
<Label :for="`jump-ship-${shipType}`" class="text-xs sm:text-sm">
|
||||
{{ SHIPS[shipType].name }} ({{ t('fleetView.available') }}: {{ count }})
|
||||
</Label>
|
||||
<div class="flex gap-2">
|
||||
<Input
|
||||
:id="`jump-ship-${shipType}`"
|
||||
v-model.number="jumpGateFleet[shipType]"
|
||||
type="number"
|
||||
min="0"
|
||||
:max="count"
|
||||
placeholder="0"
|
||||
class="text-sm"
|
||||
/>
|
||||
<Button @click="jumpGateFleet[shipType] = count" variant="outline" size="sm">{{ t('fleetView.all') }}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 传送按钮 -->
|
||||
<div class="mt-6">
|
||||
<Button @click="executeJumpGateTransfer" :disabled="!canExecuteJumpGate" class="w-full">
|
||||
<Zap class="h-4 w-4 mr-2" />
|
||||
{{ t('fleetView.jumpGateTransfer') }}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<!-- 提示对话框 -->
|
||||
@@ -295,6 +486,40 @@
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
<!-- 预设名称对话框 -->
|
||||
<AlertDialog :open="showPresetNameDialog" @update:open="showPresetNameDialog = $event">
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{{ pendingPresetAction === 'save' ? t('fleetView.savePresetTitle') : t('fleetView.renamePresetTitle') }}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{{ pendingPresetAction === 'save' ? t('fleetView.savePresetDescription') : t('fleetView.renamePresetDescription') }}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<div class="py-4">
|
||||
<Label for="preset-name">{{ t('fleetView.presetName') }}</Label>
|
||||
<Input
|
||||
id="preset-name"
|
||||
v-model="editingPresetName"
|
||||
:placeholder="t('fleetView.presetNamePlaceholder')"
|
||||
class="mt-2"
|
||||
@keyup.enter="handlePresetNameConfirm"
|
||||
/>
|
||||
</div>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel
|
||||
@click="() => { showPresetNameDialog = false; pendingPresetAction = null }"
|
||||
>
|
||||
{{ t('common.cancel') }}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction @click="handlePresetNameConfirm" :disabled="!editingPresetName.trim()">
|
||||
{{ t('common.confirm') }}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -307,7 +532,7 @@
|
||||
import { computed, ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ShipType, MissionType, BuildingType, TechnologyType } from '@/types/game'
|
||||
import type { Fleet, Resources } from '@/types/game'
|
||||
import type { Fleet, Resources, FleetPreset } from '@/types/game'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -329,7 +554,27 @@
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import UnlockRequirement from '@/components/UnlockRequirement.vue'
|
||||
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
|
||||
import { Sword, Package, Rocket as RocketIcon, Eye, Users, Recycle, Skull, Gift, Compass } from 'lucide-vue-next'
|
||||
import {
|
||||
Sword,
|
||||
Package,
|
||||
Rocket as RocketIcon,
|
||||
Eye,
|
||||
Users,
|
||||
Recycle,
|
||||
Skull,
|
||||
Gift,
|
||||
Compass,
|
||||
Save,
|
||||
Trash2,
|
||||
Pencil,
|
||||
Star,
|
||||
Type,
|
||||
Zap,
|
||||
Clock,
|
||||
Check,
|
||||
Globe,
|
||||
Moon
|
||||
} from 'lucide-vue-next'
|
||||
import { formatNumber, formatTime } from '@/utils/format'
|
||||
import * as shipValidation from '@/logic/shipValidation'
|
||||
import * as fleetLogic from '@/logic/fleetLogic'
|
||||
@@ -337,6 +582,8 @@
|
||||
import * as officerLogic from '@/logic/officerLogic'
|
||||
import * as publicLogic from '@/logic/publicLogic'
|
||||
import * as diplomaticLogic from '@/logic/diplomaticLogic'
|
||||
import * as gameLogic from '@/logic/gameLogic'
|
||||
import * as moonLogic from '@/logic/moonLogic'
|
||||
|
||||
const route = useRoute()
|
||||
const gameStore = useGameStore()
|
||||
@@ -363,15 +610,127 @@
|
||||
return publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots, computerTechLevel)
|
||||
})
|
||||
|
||||
const activeTab = ref<'fleet' | 'send' | 'missions'>('fleet')
|
||||
const activeTab = ref<'fleet' | 'send' | 'missions' | 'jumpGate'>('fleet')
|
||||
|
||||
// Tab 配置
|
||||
const fleetTabs = [
|
||||
{ value: 'fleet', labelKey: 'fleetOverview' },
|
||||
{ value: 'send', labelKey: 'sendFleet' },
|
||||
{ value: 'missions', labelKey: 'flightMissions' }
|
||||
{ value: 'missions', labelKey: 'flightMissions' },
|
||||
{ value: 'jumpGate', labelKey: 'jumpGate' }
|
||||
] as const
|
||||
|
||||
// 跳跃门相关
|
||||
const selectedJumpGateTarget = ref<typeof planet.value | null>(null)
|
||||
const jumpGateFleet = ref<Partial<Fleet>>({
|
||||
[ShipType.LightFighter]: 0,
|
||||
[ShipType.HeavyFighter]: 0,
|
||||
[ShipType.Cruiser]: 0,
|
||||
[ShipType.Battleship]: 0,
|
||||
[ShipType.SmallCargo]: 0,
|
||||
[ShipType.LargeCargo]: 0,
|
||||
[ShipType.ColonyShip]: 0,
|
||||
[ShipType.Recycler]: 0,
|
||||
[ShipType.EspionageProbe]: 0,
|
||||
[ShipType.DarkMatterHarvester]: 0,
|
||||
[ShipType.Deathstar]: 0
|
||||
})
|
||||
|
||||
// 是否显示跳跃门标签页(当前在月球上且有跳跃门)
|
||||
const showJumpGateTab = computed(() => {
|
||||
if (!planet.value) return false
|
||||
if (!planet.value.isMoon) return false
|
||||
const jumpGateLevel = planet.value.buildings[BuildingType.JumpGate] || 0
|
||||
return jumpGateLevel > 0
|
||||
})
|
||||
|
||||
// 跳跃门是否就绪(冷却完成)
|
||||
const jumpGateReady = computed(() => {
|
||||
if (!planet.value) return false
|
||||
return moonLogic.isJumpGateReady(planet.value)
|
||||
})
|
||||
|
||||
// 跳跃门剩余冷却时间
|
||||
const jumpGateCooldownRemaining = computed(() => {
|
||||
if (!planet.value) return 0
|
||||
return moonLogic.getJumpGateCooldownRemaining(planet.value)
|
||||
})
|
||||
|
||||
// 可用的目标月球(有跳跃门且冷却完成的其他月球)
|
||||
const availableJumpGateMoons = computed(() => {
|
||||
if (!planet.value) return []
|
||||
return gameStore.player.planets.filter(p => {
|
||||
if (p.id === planet.value?.id) return false // 排除当前月球
|
||||
if (!p.isMoon) return false
|
||||
const jumpGateLevel = p.buildings[BuildingType.JumpGate] || 0
|
||||
if (jumpGateLevel <= 0) return false
|
||||
return moonLogic.isJumpGateReady(p)
|
||||
})
|
||||
})
|
||||
|
||||
// 检查目标月球的跳跃门是否就绪
|
||||
const isJumpGateMoonReady = (moon: typeof planet.value) => {
|
||||
if (!moon) return false
|
||||
return moonLogic.isJumpGateReady(moon)
|
||||
}
|
||||
|
||||
// 是否可以执行跳跃门传送
|
||||
const canExecuteJumpGate = computed(() => {
|
||||
if (!planet.value || !selectedJumpGateTarget.value) return false
|
||||
if (!jumpGateReady.value) return false
|
||||
// 检查是否选择了至少一艘舰船
|
||||
const totalShips = Object.values(jumpGateFleet.value).reduce((sum, count) => sum + (count || 0), 0)
|
||||
return totalShips > 0
|
||||
})
|
||||
|
||||
// 执行跳跃门传送
|
||||
const executeJumpGateTransfer = () => {
|
||||
if (!planet.value || !selectedJumpGateTarget.value) return
|
||||
if (!canExecuteJumpGate.value) return
|
||||
|
||||
const sourceMoon = planet.value
|
||||
const targetMoon = selectedJumpGateTarget.value
|
||||
|
||||
// 转移舰队
|
||||
Object.entries(jumpGateFleet.value).forEach(([shipType, count]) => {
|
||||
if (count && count > 0) {
|
||||
const ship = shipType as ShipType
|
||||
// 从源月球扣除
|
||||
if (sourceMoon.fleet[ship] >= count) {
|
||||
sourceMoon.fleet[ship] -= count
|
||||
// 添加到目标月球
|
||||
targetMoon.fleet[ship] = (targetMoon.fleet[ship] || 0) + count
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 设置两个跳跃门的冷却时间
|
||||
moonLogic.useJumpGate(sourceMoon)
|
||||
moonLogic.useJumpGate(targetMoon)
|
||||
|
||||
// 重置跳跃门舰队选择
|
||||
Object.keys(jumpGateFleet.value).forEach(key => {
|
||||
jumpGateFleet.value[key as ShipType] = 0
|
||||
})
|
||||
selectedJumpGateTarget.value = null
|
||||
|
||||
// 显示成功对话框
|
||||
alertDialogTitle.value = t('fleetView.jumpGateSuccess')
|
||||
alertDialogMessage.value = t('fleetView.jumpGateSuccessMessage', {
|
||||
target: `${targetMoon.name} [${targetMoon.position.galaxy}:${targetMoon.position.system}:${targetMoon.position.position}]`
|
||||
})
|
||||
alertDialogCallback.value = null
|
||||
alertDialogOpen.value = true
|
||||
}
|
||||
|
||||
// 可见的标签页(根据是否有跳跃门动态显示)
|
||||
const visibleTabs = computed(() => {
|
||||
if (showJumpGateTab.value) {
|
||||
return fleetTabs
|
||||
}
|
||||
return fleetTabs.filter(tab => tab.value !== 'jumpGate')
|
||||
})
|
||||
|
||||
// 选择的舰队
|
||||
const selectedFleet = ref<Partial<Fleet>>({
|
||||
[ShipType.LightFighter]: 0,
|
||||
@@ -390,6 +749,20 @@
|
||||
// 目标坐标
|
||||
const targetPosition = ref({ galaxy: 1, system: 1, position: 1 })
|
||||
|
||||
// 目标是否为月球(用于区分同坐标的行星和月球)
|
||||
const targetIsMoon = ref(false)
|
||||
|
||||
// 检查目标位置是否有月球(玩家自己的)
|
||||
const hasMoonAtTargetPosition = computed(() => {
|
||||
return gameStore.player.planets.some(
|
||||
p =>
|
||||
p.isMoon &&
|
||||
p.position.galaxy === targetPosition.value.galaxy &&
|
||||
p.position.system === targetPosition.value.system &&
|
||||
p.position.position === targetPosition.value.position
|
||||
)
|
||||
})
|
||||
|
||||
// 坐标字段配置
|
||||
const coordinateFields: { key: keyof typeof targetPosition.value; max: number }[] = [
|
||||
{ key: 'galaxy', max: 9 },
|
||||
@@ -463,6 +836,227 @@
|
||||
// 是否为赠送模式
|
||||
const isGiftMode = ref(false)
|
||||
|
||||
// 舰队预设相关状态
|
||||
const MAX_PRESETS = 3
|
||||
const editingPresetId = ref<string | null>(null)
|
||||
const editingPresetName = ref('')
|
||||
const showPresetNameDialog = ref(false)
|
||||
const pendingPresetAction = ref<'save' | 'rename' | null>(null)
|
||||
|
||||
// 获取预设列表
|
||||
const fleetPresets = computed(() => gameStore.player.fleetPresets || [])
|
||||
|
||||
// 生成唯一ID
|
||||
const generatePresetId = (): string => {
|
||||
return `preset_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
}
|
||||
|
||||
// 保存当前配置为预设
|
||||
const saveAsPreset = () => {
|
||||
if (fleetPresets.value.length >= MAX_PRESETS) {
|
||||
alertDialogTitle.value = t('fleetView.presetLimitReached')
|
||||
alertDialogMessage.value = t('fleetView.presetLimitReachedMessage', { max: MAX_PRESETS.toString() })
|
||||
alertDialogCallback.value = null
|
||||
alertDialogOpen.value = true
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有选择舰船
|
||||
const hasShips = Object.values(selectedFleet.value).some(count => count > 0)
|
||||
if (!hasShips) {
|
||||
alertDialogTitle.value = t('fleetView.presetError')
|
||||
alertDialogMessage.value = t('fleetView.presetNoShips')
|
||||
alertDialogCallback.value = null
|
||||
alertDialogOpen.value = true
|
||||
return
|
||||
}
|
||||
|
||||
pendingPresetAction.value = 'save'
|
||||
editingPresetName.value = t('fleetView.presetDefaultName', { number: (fleetPresets.value.length + 1).toString() })
|
||||
showPresetNameDialog.value = true
|
||||
}
|
||||
|
||||
// 确认保存预设
|
||||
const confirmSavePreset = () => {
|
||||
if (!editingPresetName.value.trim()) return
|
||||
|
||||
// 只保存数量大于0的舰船
|
||||
const fleetToSave: Partial<Fleet> = {}
|
||||
for (const [shipType, count] of Object.entries(selectedFleet.value)) {
|
||||
if (count && count > 0) {
|
||||
fleetToSave[shipType as ShipType] = count
|
||||
}
|
||||
}
|
||||
|
||||
// 只保存数量大于0的资源
|
||||
const cargoToSave: Partial<Resources> | undefined =
|
||||
selectedMission.value === MissionType.Transport
|
||||
? {
|
||||
metal: cargo.value.metal || 0,
|
||||
crystal: cargo.value.crystal || 0,
|
||||
deuterium: cargo.value.deuterium || 0,
|
||||
darkMatter: cargo.value.darkMatter || 0
|
||||
}
|
||||
: undefined
|
||||
|
||||
const newPreset: FleetPreset = {
|
||||
id: generatePresetId(),
|
||||
name: editingPresetName.value.trim(),
|
||||
fleet: fleetToSave,
|
||||
targetPosition: {
|
||||
galaxy: targetPosition.value.galaxy,
|
||||
system: targetPosition.value.system,
|
||||
position: targetPosition.value.position
|
||||
},
|
||||
missionType: selectedMission.value,
|
||||
cargo: cargoToSave
|
||||
}
|
||||
|
||||
if (!gameStore.player.fleetPresets) {
|
||||
gameStore.player.fleetPresets = []
|
||||
}
|
||||
gameStore.player.fleetPresets.push(newPreset)
|
||||
|
||||
showPresetNameDialog.value = false
|
||||
editingPresetName.value = ''
|
||||
pendingPresetAction.value = null
|
||||
}
|
||||
|
||||
// 加载预设
|
||||
const loadPreset = (preset: FleetPreset) => {
|
||||
// 加载舰队配置
|
||||
Object.keys(selectedFleet.value).forEach(key => {
|
||||
selectedFleet.value[key as ShipType] = preset.fleet[key as ShipType] || 0
|
||||
})
|
||||
|
||||
// 加载目标坐标
|
||||
if (preset.targetPosition) {
|
||||
targetPosition.value = { ...preset.targetPosition }
|
||||
}
|
||||
|
||||
// 加载任务类型
|
||||
if (preset.missionType) {
|
||||
selectedMission.value = preset.missionType
|
||||
}
|
||||
|
||||
// 加载运输资源
|
||||
if (preset.cargo && preset.missionType === MissionType.Transport) {
|
||||
cargo.value = {
|
||||
metal: preset.cargo.metal || 0,
|
||||
crystal: preset.cargo.crystal || 0,
|
||||
deuterium: preset.cargo.deuterium || 0,
|
||||
darkMatter: preset.cargo.darkMatter || 0,
|
||||
energy: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新预设(点击预设后修改内容)
|
||||
const updatePreset = (presetId: string) => {
|
||||
const presetIndex = gameStore.player.fleetPresets?.findIndex(p => p.id === presetId)
|
||||
if (presetIndex === undefined || presetIndex === -1) return
|
||||
|
||||
const hasShips = Object.values(selectedFleet.value).some(count => count > 0)
|
||||
if (!hasShips) {
|
||||
alertDialogTitle.value = t('fleetView.presetError')
|
||||
alertDialogMessage.value = t('fleetView.presetNoShips')
|
||||
alertDialogCallback.value = null
|
||||
alertDialogOpen.value = true
|
||||
return
|
||||
}
|
||||
|
||||
const existingPreset = gameStore.player.fleetPresets![presetIndex]
|
||||
if (!existingPreset) return
|
||||
|
||||
// 只保存数量大于0的舰船
|
||||
const fleetToSave: Partial<Fleet> = {}
|
||||
for (const [shipType, count] of Object.entries(selectedFleet.value)) {
|
||||
if (count && count > 0) {
|
||||
fleetToSave[shipType as ShipType] = count
|
||||
}
|
||||
}
|
||||
|
||||
// 只保存数量大于0的资源
|
||||
const cargoToSave: Partial<Resources> | undefined =
|
||||
selectedMission.value === MissionType.Transport
|
||||
? {
|
||||
metal: cargo.value.metal || 0,
|
||||
crystal: cargo.value.crystal || 0,
|
||||
deuterium: cargo.value.deuterium || 0,
|
||||
darkMatter: cargo.value.darkMatter || 0
|
||||
}
|
||||
: undefined
|
||||
|
||||
const updatedPreset: FleetPreset = {
|
||||
id: existingPreset.id,
|
||||
name: existingPreset.name,
|
||||
fleet: fleetToSave,
|
||||
targetPosition: {
|
||||
galaxy: targetPosition.value.galaxy,
|
||||
system: targetPosition.value.system,
|
||||
position: targetPosition.value.position
|
||||
},
|
||||
missionType: selectedMission.value,
|
||||
cargo: cargoToSave
|
||||
}
|
||||
|
||||
gameStore.player.fleetPresets![presetIndex] = updatedPreset
|
||||
editingPresetId.value = null
|
||||
}
|
||||
|
||||
// 开始编辑预设名称
|
||||
const startRenamePreset = (preset: FleetPreset) => {
|
||||
// 保存要重命名的预设ID,但不进入编辑内容模式
|
||||
editingPresetName.value = preset.name
|
||||
pendingPresetAction.value = 'rename'
|
||||
// 使用临时变量存储要重命名的预设ID
|
||||
renameTargetPresetId.value = preset.id
|
||||
showPresetNameDialog.value = true
|
||||
}
|
||||
|
||||
// 要重命名的预设ID(与编辑预设内容分开)
|
||||
const renameTargetPresetId = ref<string | null>(null)
|
||||
|
||||
// 确认重命名预设
|
||||
const confirmRenamePreset = () => {
|
||||
if (!editingPresetName.value.trim() || !renameTargetPresetId.value) return
|
||||
|
||||
const preset = gameStore.player.fleetPresets?.find(p => p.id === renameTargetPresetId.value)
|
||||
if (preset) {
|
||||
preset.name = editingPresetName.value.trim()
|
||||
}
|
||||
|
||||
showPresetNameDialog.value = false
|
||||
renameTargetPresetId.value = null
|
||||
editingPresetName.value = ''
|
||||
pendingPresetAction.value = null
|
||||
}
|
||||
|
||||
// 删除预设
|
||||
const deletePreset = (presetId: string) => {
|
||||
const preset = gameStore.player.fleetPresets?.find(p => p.id === presetId)
|
||||
if (!preset) return
|
||||
|
||||
alertDialogTitle.value = t('fleetView.deletePresetTitle')
|
||||
alertDialogMessage.value = t('fleetView.deletePresetMessage', { name: preset.name })
|
||||
alertDialogCallback.value = () => {
|
||||
const index = gameStore.player.fleetPresets?.findIndex(p => p.id === presetId)
|
||||
if (index !== undefined && index > -1) {
|
||||
gameStore.player.fleetPresets!.splice(index, 1)
|
||||
}
|
||||
}
|
||||
alertDialogOpen.value = true
|
||||
}
|
||||
|
||||
// 处理预设名称对话框确认
|
||||
const handlePresetNameConfirm = () => {
|
||||
if (pendingPresetAction.value === 'save') {
|
||||
confirmSavePreset()
|
||||
} else if (pendingPresetAction.value === 'rename') {
|
||||
confirmRenamePreset()
|
||||
}
|
||||
}
|
||||
|
||||
// 监听目标NPC变化,当目标不再是NPC时自动禁用赠送模式
|
||||
watch(targetNpc, newValue => {
|
||||
if (!newValue && isGiftMode.value) {
|
||||
@@ -543,8 +1137,16 @@
|
||||
if (!hasShips) return { valid: false, errorKey: 'fleetView.noShipsSelected' }
|
||||
|
||||
// 检查是否派遣到自己的星球
|
||||
// 回收任务和部署任务除外(回收残骸可能在同位置,部署可能到自己的月球)
|
||||
if (planet.value && selectedMission.value !== MissionType.Recycle && selectedMission.value !== MissionType.Deploy) {
|
||||
// 回收任务、部署任务和运输任务除外:
|
||||
// - 回收任务:可能回收同位置的残骸
|
||||
// - 部署任务:可能部署到自己的月球
|
||||
// - 运输任务:可能从行星向同位置的月球运输资源(OGame规则允许)
|
||||
if (
|
||||
planet.value &&
|
||||
selectedMission.value !== MissionType.Recycle &&
|
||||
selectedMission.value !== MissionType.Deploy &&
|
||||
selectedMission.value !== MissionType.Transport
|
||||
) {
|
||||
const isSamePlanet =
|
||||
targetPosition.value.galaxy === planet.value.position.galaxy &&
|
||||
targetPosition.value.system === planet.value.position.system &&
|
||||
@@ -591,7 +1193,8 @@
|
||||
targetPosition: { galaxy: number; system: number; position: number },
|
||||
missionType: MissionType,
|
||||
fleet: Partial<Fleet>,
|
||||
cargo: Resources = { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 }
|
||||
cargo: Resources = { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 },
|
||||
isMoonTarget: boolean = false
|
||||
): boolean => {
|
||||
if (!gameStore.currentPlanet) return false
|
||||
const currentMissions = gameStore.player.fleetMissions.length
|
||||
@@ -604,6 +1207,13 @@
|
||||
gameStore.player.technologies
|
||||
)
|
||||
if (!validation.valid) return false
|
||||
|
||||
// 追踪燃料消耗(同时计入资源消耗和燃料统计)
|
||||
if (validation.fuelNeeded && validation.fuelNeeded > 0) {
|
||||
gameLogic.trackResourceConsumption(gameStore.player, { deuterium: validation.fuelNeeded })
|
||||
gameLogic.trackFuelConsumption(gameStore.player, validation.fuelNeeded)
|
||||
}
|
||||
|
||||
const shouldDeductCargo = missionType === MissionType.Transport
|
||||
shipValidation.executeFleetDispatch(gameStore.currentPlanet, fleet, validation.fuelNeeded!, shouldDeductCargo, cargo)
|
||||
const distance = fleetLogic.calculateDistance(gameStore.currentPlanet.position, targetPosition)
|
||||
@@ -620,6 +1230,11 @@
|
||||
flightTime
|
||||
)
|
||||
|
||||
// 如果目标是月球,设置标记
|
||||
if (isMoonTarget) {
|
||||
mission.targetIsMoon = true
|
||||
}
|
||||
|
||||
// 如果是赠送模式,标记任务
|
||||
if (missionType === MissionType.Transport && isGiftMode.value && targetNpc.value) {
|
||||
mission.isGift = true
|
||||
@@ -655,7 +1270,8 @@
|
||||
targetPosition.value,
|
||||
selectedMission.value,
|
||||
fleet,
|
||||
selectedMission.value === MissionType.Transport ? cargo.value : undefined
|
||||
selectedMission.value === MissionType.Transport ? cargo.value : undefined,
|
||||
targetIsMoon.value
|
||||
)
|
||||
|
||||
if (success) {
|
||||
|
||||
Reference in New Issue
Block a user