feat: 初始化项目结构与核心功能

引入项目基础目录结构,包含多语言支持、主要页面与组件、核心游戏逻辑、UI 组件库、加密与本地持久化、自动化 Docker 构建流程、GitHub issue 模板(中英文)、README(中英文)、LICENSE 及开发配置文件。实现 OGame 单机版主要功能模块,为后续开发和扩展奠定基础。
This commit is contained in:
谦君
2025-12-11 14:49:25 +08:00
commit 705ee8c3db
178 changed files with 17258 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
import { computed } from 'vue'
import { useI18n } from './useI18n'
import {
BUILDINGS as ORIGINAL_BUILDINGS,
SHIPS as ORIGINAL_SHIPS,
DEFENSES as ORIGINAL_DEFENSES,
TECHNOLOGIES as ORIGINAL_TECHNOLOGIES,
OFFICERS as ORIGINAL_OFFICERS
} from '@/config/gameConfig'
import { BuildingType, ShipType, DefenseType, TechnologyType, OfficerType } from '@/types/game'
import type { BuildingConfig, ShipConfig, DefenseConfig, TechnologyConfig, OfficerConfig } from '@/types/game'
/**
* 提供翻译后的游戏配置对象的 Composable
* 将 i18n 系统的翻译覆盖到原始 gameConfig 上
*/
export const useGameConfig = () => {
const { t } = useI18n()
// 建筑类型枚举值到翻译键的映射
const buildingKeyMap: Record<BuildingType, string> = {
[BuildingType.MetalMine]: 'metalMine',
[BuildingType.CrystalMine]: 'crystalMine',
[BuildingType.DeuteriumSynthesizer]: 'deuteriumSynthesizer',
[BuildingType.SolarPlant]: 'solarPlant',
[BuildingType.RoboticsFactory]: 'roboticsFactory',
[BuildingType.NaniteFactory]: 'naniteFactory',
[BuildingType.Shipyard]: 'shipyard',
[BuildingType.ResearchLab]: 'researchLab',
[BuildingType.MetalStorage]: 'metalStorage',
[BuildingType.CrystalStorage]: 'crystalStorage',
[BuildingType.DeuteriumTank]: 'deuteriumTank',
[BuildingType.DarkMatterCollector]: 'darkMatterCollector',
[BuildingType.LunarBase]: 'lunarBase',
[BuildingType.SensorPhalanx]: 'sensorPhalanx',
[BuildingType.JumpGate]: 'jumpGate'
}
// 舰船类型枚举值到翻译键的映射
const shipKeyMap: Record<ShipType, string> = {
[ShipType.LightFighter]: 'lightFighter',
[ShipType.HeavyFighter]: 'heavyFighter',
[ShipType.Cruiser]: 'cruiser',
[ShipType.Battleship]: 'battleship',
[ShipType.SmallCargo]: 'smallCargo',
[ShipType.LargeCargo]: 'largeCargo',
[ShipType.ColonyShip]: 'colonyShip',
[ShipType.Recycler]: 'recycler',
[ShipType.EspionageProbe]: 'espionageProbe',
[ShipType.DarkMatterHarvester]: 'darkMatterHarvester'
}
// 防御设施类型枚举值到翻译键的映射
const defenseKeyMap: Record<DefenseType, string> = {
[DefenseType.RocketLauncher]: 'rocketLauncher',
[DefenseType.LightLaser]: 'lightLaser',
[DefenseType.HeavyLaser]: 'heavyLaser',
[DefenseType.GaussCannon]: 'gaussCannon',
[DefenseType.IonCannon]: 'ionCannon',
[DefenseType.PlasmaTurret]: 'plasmaTurret',
[DefenseType.SmallShieldDome]: 'smallShieldDome',
[DefenseType.LargeShieldDome]: 'largeShieldDome'
}
// 科技类型枚举值到翻译键的映射
const technologyKeyMap: Record<TechnologyType, string> = {
[TechnologyType.EnergyTechnology]: 'energyTechnology',
[TechnologyType.LaserTechnology]: 'laserTechnology',
[TechnologyType.IonTechnology]: 'ionTechnology',
[TechnologyType.HyperspaceTechnology]: 'hyperspaceTechnology',
[TechnologyType.PlasmaTechnology]: 'plasmaTechnology',
[TechnologyType.ComputerTechnology]: 'computerTechnology',
[TechnologyType.CombustionDrive]: 'combustionDrive',
[TechnologyType.ImpulseDrive]: 'impulseDrive',
[TechnologyType.HyperspaceDrive]: 'hyperspaceDrive',
[TechnologyType.DarkMatterTechnology]: 'darkMatterTechnology'
}
// 军官类型枚举值到翻译键的映射
const officerKeyMap: Record<OfficerType, string> = {
[OfficerType.Commander]: 'commander',
[OfficerType.Admiral]: 'admiral',
[OfficerType.Engineer]: 'engineer',
[OfficerType.Geologist]: 'geologist',
[OfficerType.Technocrat]: 'technocrat',
[OfficerType.DarkMatterSpecialist]: 'darkMatterSpecialist'
}
// 翻译后的建筑配置
const BUILDINGS = computed(() => {
const translated: Record<BuildingType, BuildingConfig> = {} as Record<BuildingType, BuildingConfig>
for (const [key, config] of Object.entries(ORIGINAL_BUILDINGS)) {
const buildingType = key as BuildingType
const translationKey = buildingKeyMap[buildingType]
translated[buildingType] = {
...config,
name: t(`buildings.${translationKey}`),
description: t(`buildingDescriptions.${translationKey}`)
}
}
return translated
})
// 翻译后的舰船配置
const SHIPS = computed(() => {
const translated: Record<ShipType, ShipConfig> = {} as Record<ShipType, ShipConfig>
for (const [key, config] of Object.entries(ORIGINAL_SHIPS)) {
const shipType = key as ShipType
const translationKey = shipKeyMap[shipType]
translated[shipType] = {
...config,
name: t(`ships.${translationKey}`),
description: t(`shipDescriptions.${translationKey}`)
}
}
return translated
})
// 翻译后的防御设施配置
const DEFENSES = computed(() => {
const translated: Record<DefenseType, DefenseConfig> = {} as Record<DefenseType, DefenseConfig>
for (const [key, config] of Object.entries(ORIGINAL_DEFENSES)) {
const defenseType = key as DefenseType
const translationKey = defenseKeyMap[defenseType]
translated[defenseType] = {
...config,
name: t(`defenses.${translationKey}`),
description: t(`defenseDescriptions.${translationKey}`)
}
}
return translated
})
// 翻译后的科技配置
const TECHNOLOGIES = computed(() => {
const translated: Record<TechnologyType, TechnologyConfig> = {} as Record<TechnologyType, TechnologyConfig>
for (const [key, config] of Object.entries(ORIGINAL_TECHNOLOGIES)) {
const technologyType = key as TechnologyType
const translationKey = technologyKeyMap[technologyType]
translated[technologyType] = {
...config,
name: t(`technologies.${translationKey}`),
description: t(`technologyDescriptions.${translationKey}`)
}
}
return translated
})
// 翻译后的军官配置
const OFFICERS = computed(() => {
const translated: Record<OfficerType, OfficerConfig> = {} as Record<OfficerType, OfficerConfig>
for (const [key, config] of Object.entries(ORIGINAL_OFFICERS)) {
const officerType = key as OfficerType
const translationKey = officerKeyMap[officerType]
translated[officerType] = {
...config,
name: t(`officers.${translationKey}`),
description: t(`officerDescriptions.${translationKey}`)
}
}
return translated
})
return {
BUILDINGS,
SHIPS,
DEFENSES,
TECHNOLOGIES,
OFFICERS
}
}

View File

@@ -0,0 +1,38 @@
import { computed } from 'vue'
import { useGameStore } from '@/stores/gameStore'
import { locales, type Locale } from '@/locales'
export const useI18n = () => {
const gameStore = useGameStore()
const currentLocale = computed(() => gameStore.locale)
const messages = computed(() => locales[currentLocale.value])
// 获取翻译文本的辅助函数
const t = (key: string): string => {
const keys = key.split('.')
let value: any = messages.value
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k]
} else {
return key // 如果找不到翻译,返回原始 key
}
}
return typeof value === 'string' ? value : key
}
const setLocale = (locale: Locale) => {
gameStore.locale = locale
}
return {
t,
locale: currentLocale,
setLocale,
messages
}
}

View File

@@ -0,0 +1,49 @@
import { ref, onMounted, watch } from 'vue'
import { useGameStore } from '@/stores/gameStore'
type Theme = 'light' | 'dark'
const isDark = ref<boolean>(false)
export const useTheme = () => {
const gameStore = useGameStore()
// 初始化主题
onMounted(() => {
if (!gameStore.isDark) {
// 首次访问,使用系统主题偏好
isDark.value = window.matchMedia('(prefers-color-scheme: dark)').matches
gameStore.isDark = isDark.value ? 'dark' : 'light'
} else {
// 从 gameStore 读取保存的主题Pinia会自动从localStorage恢复
const savedTheme = gameStore.isDark as Theme
isDark.value = savedTheme === 'dark'
}
applyTheme()
})
// 监听主题变化
watch(isDark, () => {
applyTheme()
gameStore.isDark = isDark.value ? 'dark' : 'light'
})
// 应用主题
const applyTheme = () => {
if (isDark.value) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}
// 切换主题
const toggleTheme = () => {
isDark.value = !isDark.value
}
return {
isDark,
toggleTheme
}
}