docs: 新增西班牙语和日语README并优化多语言文档

新增README-ES.md(西班牙语)和README-JA.md(日语)文档,完善多语言README互链。优化各语言README徽章、技术栈、外链格式及语言切换区,提升文档一致性与可读性。
This commit is contained in:
谦君
2025-12-25 18:25:08 +08:00
parent b24a262ca7
commit 724a70bebb
72 changed files with 13300 additions and 2133 deletions

View File

@@ -35,6 +35,7 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
- name: Build Frontend & Server
run: |
@@ -71,6 +72,17 @@ jobs:
- name: Setup Android SDK
uses: android-actions/setup-android@v3
# 缓存 Gradle 依赖 (可节省 3-5 分钟)
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('android/**/*.gradle*', 'android/**/gradle-wrapper.properties') }}
restore-keys: |
gradle-${{ runner.os }}-
- name: Build Frontend
run: |
pnpm install
@@ -78,9 +90,7 @@ jobs:
- name: Generate Android Icons
run: |
# 安装 ImageMagick
sudo apt-get update && sudo apt-get install -y imagemagick
# 使用 ImageMagick 生成各尺寸图标
# 使用 ImageMagick 生成各尺寸图标 (Ubuntu runner 已预装)
convert logo.png -resize 48x48 android/app/src/main/res/mipmap-mdpi/ic_launcher.png
convert logo.png -resize 48x48 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
convert logo.png -resize 72x72 android/app/src/main/res/mipmap-hdpi/ic_launcher.png
@@ -105,7 +115,7 @@ jobs:
working-directory: android
run: |
chmod +x ./gradlew
./gradlew assembleRelease
./gradlew assembleRelease --no-daemon
- name: Upload APK Artifacts
uses: actions/upload-artifact@v4
@@ -138,6 +148,16 @@ jobs:
node-version: 20
cache: 'pnpm'
- name: Cache Electron Builder
uses: actions/cache@v4
with:
path: |
~/.cache/electron
~/.cache/electron-builder
key: electron-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
electron-${{ runner.os }}-
- name: Build Electron
run: |
pnpm install
@@ -180,7 +200,6 @@ jobs:
mkdir -p ./final-release
# 移动 Server 文件并确保名字唯一
# 注意根据你之前的附件Artifact 名字是 server-windows-amd64
cp ./raw-assets/server-windows-amd64/OGame-Vue-Ts-server-win.exe ./final-release/OGame-Vue-Ts-server-win.exe || cp ./raw-assets/server-windows-amd64/server-windows-amd64.exe ./final-release/OGame-Vue-Ts-server-win.exe || true
cp ./raw-assets/server-linux-amd64/OGame-Vue-Ts-server-linux ./final-release/OGame-Vue-Ts-server-linux || true
cp ./raw-assets/server-linux-arm64/OGame-Vue-Ts-server-linux-arm64 ./final-release/OGame-Vue-Ts-server-linux-arm64 || true

View File

@@ -19,15 +19,17 @@ jobs:
- name: 检出代码
uses: actions/checkout@v6
- name: 安装 pnpm
uses: pnpm/action-setup@v4
- name: 安装 Nodejs
uses: actions/setup-node@v6
with:
node-version: 20 # 建议使用 LTS 版本
node-version: 20
cache: 'pnpm'
- name: 安装 pnpm 并构建前端
uses: pnpm/action-setup@v4
with:
run_install: true
- name: 安装依赖
run: pnpm install
- name: 构建前端项目
run: pnpm run build

View File

@@ -6,13 +6,10 @@
Ein modernes Weltraum-Strategiespiel basierend auf dem klassischen OGame, entwickelt mit Vue 3 und TypeScript.
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
[![Vue 3](https://img.shields.io/badge/Vue-3.5-brightgreen.svg)](https://vuejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![Vite](https://img.shields.io/badge/Vite-7.2-646CFF.svg)](https://vitejs.dev/)
[![Go](https://img.shields.io/badge/Go-1.23-79D4FD.svg)](https://golang.org/)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
[简体中文](README.md) | [English](README-EN.md) | [繁體中文](README-zh-TW.md) | Deutsch | [Русский](README-RU.md) | [한국어](README-KO.md)
[简体中文](README.md)| [繁體中文](README-zh-TW.md) | [English](README-EN.md) | Deutsch | [Русский](README-RU.md) | [Español](README-ES.md) | [한국어](README-KO.md) | [日本語](README-JA.md)
</div>
@@ -22,7 +19,6 @@ OGame Vue TS ist ein Einzelspieler-Weltraum-Strategiespiel im Browser, inspirier
## Hauptfunktionen
- **Mehrsprachige Unterstützung** - Verfügbar in 6 Sprachen: Englisch, Chinesisch (vereinfacht & traditionell), Deutsch, Russisch und Koreanisch
- **Gebäudeverwaltung** - Baue und verbessere verschiedene Gebäude auf Planeten und Monden
- **Technologieforschung** - Schalte fortschrittliche Technologien frei, um dein Imperium zu stärken
- **Flottenverwaltung** - Baue Schiffe, sende Missionen und nimm an taktischen Weltraumkämpfen teil
@@ -37,14 +33,14 @@ OGame Vue TS ist ein Einzelspieler-Weltraum-Strategiespiel im Browser, inspirier
## Technologie-Stack
- **Frontend-Framework:** [Vue 3](https://vuejs.org/) + Composition API (`<script setup>` Syntax)
- **Programmiersprache:** [TypeScript](https://www.typescriptlang.org/) (mit strikter Typprüfung)
- **Build-Tool:** [Vite](https://vitejs.dev/) (Custom Rolldown-Vite 7.2.5), [Golang](https://golang.org/) (für plattformübergreifenden Webserver), [Electron](https://www.electronjs.org/) (für plattformübergreifende Desktop-Anwendung)
- **Zustandsverwaltung:** [Pinia](https://pinia.vuejs.org/) + Persistenz-Plugin
- **Routing:** [Vue Router 4](https://router.vuejs.org/)
- **UI-Komponenten:** [shadcn-vue](https://www.shadcn-vue.com/) (New York Stil)
- **Styling:** [Tailwind CSS v4](https://tailwindcss.com/) + CSS-Variablen
- **Icons:** [Lucide Vue Next](https://lucide.dev/)
- **Frontend-Framework:** [Vue 3](https://vuejs.org) + Composition API (`<script setup>` Syntax)
- **Programmiersprache:** [TypeScript](https://www.typescriptlang.org) (mit strikter Typprüfung)
- **Build-Tool:** [Vite](https://vitejs.dev) (Custom Rolldown-Vite 7.2.5), [Golang](https://golang.org) (für plattformübergreifenden Webserver), [Electron](https://www.electronjs.org) (für plattformübergreifende Desktop-Anwendung)
- **Zustandsverwaltung:** [Pinia](https://pinia.vuejs.org) + Persistenz-Plugin
- **Routing:** [Vue Router 4](https://router.vuejs.org)
- **UI-Komponenten:** [shadcn-vue](https://www.shadcn-vue.com) (New York Stil)
- **Styling:** [Tailwind CSS v4](https://tailwindcss.com) + CSS-Variablen
- **Icons:** [Lucide Vue Next](https://lucide.dev)
- **Animationen:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **Internationalisierung:** Eigene i18n-Implementierung
@@ -52,9 +48,9 @@ OGame Vue TS ist ein Einzelspieler-Weltraum-Strategiespiel im Browser, inspirier
### Voraussetzungen
- [Node.js](https://nodejs.org/) (Version 18 oder höher empfohlen)
- [pnpm](https://pnpm.io/) (Version 10.13.1 oder höher)
- [Go](https://golang.org/) (Version 1.21 oder höher) (optional)
- [Node.js](https://nodejs.org) (Version 18 oder höher empfohlen)
- [pnpm](https://pnpm.io) (Version 10.13.1 oder höher)
- [Go](https://golang.org) (Version 1.21 oder höher) (optional)
### Installation
@@ -88,47 +84,6 @@ pnpm build
pnpm preview
```
## Verfügbare Sprachen
- 简体中文 (Vereinfachtes Chinesisch)
- 繁體中文 (Traditionelles Chinesisch)
- English (Englisch)
- Deutsch
- Русский (Russisch)
- 한국어 (Koreanisch)
## Spielfunktionen
### Ressourcenverwaltung
- **Metall** - Primäres Baumaterial
- **Kristall** - Fortschrittliche Technologiekomponente
- **Deuterium** - Treibstoff und Forschungsressource
- **Dunkle Materie** - Premium-Ressource
- **Energie** - Versorgt deine Anlagen
### Gebäudetypen
- **Ressourcengebäude** - Metallmine, Kristallmine, Deuterium-Synthesizer, Solarkraftwerk
- **Anlagen** - Roboterfabrik, Raumschiffwerft, Forschungslabor, Lagereinrichtungen
- **Spezialgebäude** - Nanitenfabrik, Terraformer und mehr
### Technologien
- **Energietechnik** - Verbessert die Energieeffizienz
- **Lasertechnik** - Verbessert Waffensysteme
- **Ionentechnik** - Fortschrittlicher Antrieb und Waffen
- **Hyperraumtechnik** - Ermöglicht schnelleres Reisen
- **Plasmatechnik** - Ultimative Waffensysteme
- Und vieles mehr...
### Schiffsklassen
- **Zivilschiffe** - Kleiner/Großer Transporter, Kolonieschiff, Recycler
- **Kampfschiffe** - Leichter/Schwerer Jäger, Kreuzer, Schlachtschiff, Bomber
- **Spezialschiffe** - Todesstern, Schlachtkreuzer, Zerstörer
### Verteidigungssysteme
- Raketenwerfer, Leichtes/Schweres Lasergeschütz, Gaußkanone
- Ionengeschütz, Plasmawerfer
- Kleine/Große Schildkuppel
## Datensicherheit
Alle Spieldaten werden automatisch mit AES-Verschlüsselung verschlüsselt, bevor sie im lokalen Speicher des Browsers gespeichert werden. Dein Spielfortschritt ist sicher und privat.
@@ -143,7 +98,7 @@ Beiträge sind willkommen! Bitte zögere nicht, Issues oder Pull Requests einzur
## Lizenz
Dieses Werk ist lizenziert unter der [Creative Commons Namensnennung-Nicht kommerziell 4.0 International Lizenz](https://creativecommons.org/licenses/by-nc/4.0/).
Dieses Werk ist lizenziert unter der [Creative Commons Namensnennung-Nicht kommerziell 4.0 International Lizenz](https://creativecommons.org/licenses/by-nc/4.0).
### Du darfst:
- **Teilen** — das Material in jedwedem Format oder Medium vervielfältigen und weiterverbreiten
@@ -155,7 +110,7 @@ Dieses Werk ist lizenziert unter der [Creative Commons Namensnennung-Nicht komme
## Danksagung
Dieses Projekt wurde vom originalen [OGame](https://ogame.org/) Browserspiel inspiriert. Alle Spielmechaniken und Designelemente wurden zu Bildungs- und Unterhaltungszwecken neu implementiert.
Dieses Projekt wurde vom originalen [OGame](https://ogame.org) Browserspiel inspiriert. Alle Spielmechaniken und Designelemente wurden zu Bildungs- und Unterhaltungszwecken neu implementiert.
## Haftungsausschluss

View File

@@ -6,13 +6,10 @@
A modern of the classic OGame space strategy game, built with Vue 3 and TypeScript.
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
[![Vue 3](https://img.shields.io/badge/Vue-3.5-brightgreen.svg)](https://vuejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![Vite](https://img.shields.io/badge/Vite-7.2-646CFF.svg)](https://vitejs.dev/)
[![Go](https://img.shields.io/badge/Go-1.23-79D4FD.svg)](https://golang.org/)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
[简体中文](README.md) | English | [繁體中文](README-zh-TW.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [한국어](README-KO.md)
[简体中文](README.md)| [繁體中文](README-zh-TW.md) | English | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [Español](README-ES.md) | [한국어](README-KO.md) | [日本語](README-JA.md)
</div>
@@ -22,7 +19,6 @@ OGame Vue TS is a single-player, browser-based space strategy game inspired by t
## Features
- **Multi-language Support** - Available in 6 languages: English, Chinese (Simplified & Traditional), German, Russian, and Korean
- **Building Management** - Construct and upgrade various buildings on planets and moons
- **Research Technologies** - Unlock advanced technologies to enhance your empire
- **Fleet Management** - Build ships, send missions, and engage in tactical space battles
@@ -37,14 +33,14 @@ OGame Vue TS is a single-player, browser-based space strategy game inspired by t
## Tech Stack
- **Frontend Framework:** [Vue 3](https://vuejs.org/) with Composition API (`<script setup>`)
- **Language:** [TypeScript](https://www.typescriptlang.org/) with strict type checking
- **Build Tool:** [Vite](https://vitejs.dev/) (Custom Rolldown-Vite 7.2.5)、[Golang](https://golang.org/)(Building cross-platform Web server.)、[Electron](https://www.electronjs.org/)(Building cross-platform visual interfaces)
- **State Management:** [Pinia](https://pinia.vuejs.org/) with persisted state plugin
- **Routing:** [Vue Router 4](https://router.vuejs.org/)
- **UI Components:** [shadcn-vue](https://www.shadcn-vue.com/) (New York style)
- **Styling:** [Tailwind CSS v4](https://tailwindcss.com/) with CSS variables
- **Icons:** [Lucide Vue Next](https://lucide.dev/)
- **Frontend Framework:** [Vue 3](https://vuejs.org) with Composition API (`<script setup>`)
- **Language:** [TypeScript](https://www.typescriptlang.org) with strict type checking
- **Build Tool:** [Vite](https://vitejs.dev) (Custom Rolldown-Vite 7.2.5)、[Golang](https://golang.org)(Building cross-platform Web server.)、[Electron](https://www.electronjs.org)(Building cross-platform visual interfaces)
- **State Management:** [Pinia](https://pinia.vuejs.org) with persisted state plugin
- **Routing:** [Vue Router 4](https://router.vuejs.org)
- **UI Components:** [shadcn-vue](https://www.shadcn-vue.com) (New York style)
- **Styling:** [Tailwind CSS v4](https://tailwindcss.com) with CSS variables
- **Icons:** [Lucide Vue Next](https://lucide.dev)
- **Animations:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **Internationalization:** Custom i18n implementation
@@ -52,9 +48,9 @@ OGame Vue TS is a single-player, browser-based space strategy game inspired by t
### Prerequisites
- [Node.js](https://nodejs.org/) (version 18 or higher recommended)
- [pnpm](https://pnpm.io/) (version 10.13.1 or higher)
- [Go](https://golang.org/) (version 1.21 or higher recommended) (optional for binary builds)
- [Node.js](https://nodejs.org) (version 18 or higher recommended)
- [pnpm](https://pnpm.io) (version 10.13.1 or higher)
- [Go](https://golang.org) (version 1.21 or higher recommended) (optional for binary builds)
### Installation
@@ -88,47 +84,6 @@ pnpm build
pnpm preview
```
## Available Languages
- 简体中文 (Simplified Chinese)
- 繁體中文 (Traditional Chinese)
- English
- Deutsch (German)
- Русский (Russian)
- 한국어 (Korean)
## Game Features
### Resource Management
- **Metal** - Primary construction material
- **Crystal** - Advanced technology component
- **Deuterium** - Fuel and research resource
- **Dark Matter** - Premium resource
- **Energy** - Powers your facilities
### Building Types
- **Resource Buildings** - Metal Mine, Crystal Mine, Deuterium Synthesizer, Solar Plant
- **Facilities** - Robotics Factory, Shipyard, Research Lab, Storage facilities
- **Special Buildings** - Nanite Factory, Terraformer, and more
### Technologies
- **Energy Technology** - Improves energy efficiency
- **Laser Technology** - Enhances weapon systems
- **Ion Technology** - Advanced propulsion and weapons
- **Hyperspace Technology** - Enables faster travel
- **Plasma Technology** - Ultimate weapon systems
- And many more...
### Ship Classes
- **Civil Ships** - Small/Large Cargo, Colony Ship, Recycler
- **Combat Ships** - Light/Heavy Fighter, Cruiser, Battleship, Bomber
- **Special Ships** - Deathstar, Battlecruiser, Destroyer
### Defense Systems
- Rocket Launcher, Light/Heavy Laser, Gauss Cannon
- Ion Cannon, Plasma Turret
- Small/Large Shield Dome
## Data Security
All game data is automatically encrypted using AES encryption before being stored in your browser's local storage. Your game progress is secure and private.
@@ -143,7 +98,7 @@ Contributions are welcome! Please feel free to submit issues or pull requests.
## License
This work is licensed under the [Creative Commons Attribution-NonCommercial 4.0 International License](https://creativecommons.org/licenses/by-nc/4.0/).
This work is licensed under the [Creative Commons Attribution-NonCommercial 4.0 International License](https://creativecommons.org/licenses/by-nc/4.0).
### You are free to:
- **Share** — copy and redistribute the material in any medium or format
@@ -157,7 +112,7 @@ This work is licensed under the [Creative Commons Attribution-NonCommercial 4.0
## Acknowledgments
This project is inspired by the original [OGame](https://ogame.org/) browser game. All game mechanics and design elements are reimplemented for educational and entertainment purposes.
This project is inspired by the original [OGame](https://ogame.org) browser game. All game mechanics and design elements are reimplemented for educational and entertainment purposes.
## Disclaimer

125
README-ES.md Normal file
View File

@@ -0,0 +1,125 @@
<div align="center">
<img src="public/logo.svg" alt="OGame Vue TS Logo" width="128" height="128">
# OGame Vue TS
Un juego de estrategia espacial moderno basado en Vue 3 y TypeScript.
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
[简体中文](README.md) | [繁體中文](README-zh-TW.md) | [English](README-EN.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | Español | [한국어](README-KO.md) | [日本語](README-JA.md)
</div>
## Acerca del Proyecto
OGame Vue TS es un juego de estrategia espacial basado en navegador, versión offline, inspirado en el clásico OGame. Construye tu imperio en la galaxia, investiga tecnologías, construye naves y participa en épicas batallas espaciales. Este proyecto está construido con tecnologías web modernas, se ejecuta completamente en el navegador, ofrece una experiencia de juego fluida y responsiva, y todos los datos se almacenan localmente.
## Características Principales
- **Gestión de Edificios** - Construye y mejora varios edificios en planetas y lunas
- **Investigación Tecnológica** - Desbloquea tecnologías avanzadas para fortalecer tu imperio
- **Gestión de Flotas** - Construye naves, envía misiones, participa en batallas espaciales tácticas
- **Sistema de Defensa** - Despliega instalaciones defensivas para proteger tus colonias
- **Sistema de Oficiales** - Recluta oficiales para obtener ventajas estratégicas
- **Simulador de Batallas** - Prueba escenarios de combate antes de invertir recursos
- **Vista Galáctica** - Explora el universo e interactúa con otros planetas
- **Persistencia de Datos Local** - Todos los datos del juego están encriptados y almacenados localmente en el navegador
- **Tema Oscuro/Claro** - Elige tu tema visual preferido
- **Gestión de Colas** - Administra múltiples colas de construcción e investigación
- **Generación de Lunas** - Mecanismo de generación de lunas basado en probabilidad desde campos de escombros
## Stack Tecnológico
- **Framework Frontend:** [Vue 3](https://vuejs.org) + Composition API (sintaxis `<script setup>`)
- **Lenguaje de Desarrollo:** [TypeScript](https://www.typescriptlang.org) (verificación de tipos estricta habilitada)
- **Herramientas de Construcción:** [Vite](https://vitejs.dev) (Rolldown-Vite 7.2.5 personalizado), [Golang](https://golang.org) (servidor web multiplataforma), [Electron](https://www.electronjs.org) (interfaz visual multiplataforma)
- **Gestión de Estado:** [Pinia](https://pinia.vuejs.org) + plugin de persistencia
- **Enrutamiento:** [Vue Router 4](https://router.vuejs.org)
- **Componentes UI:** [shadcn-vue](https://www.shadcn-vue.com) (estilo New York)
- **Estilos:** [Tailwind CSS v4](https://tailwindcss.com) + Variables CSS
- **Iconos:** [Lucide Vue Next](https://lucide.dev)
- **Animaciones:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **Internacionalización:** Implementación i18n personalizada
## Inicio Rápido
### Requisitos
- [Node.js](https://nodejs.org) (versión 18 o superior recomendada)
- [pnpm](https://pnpm.io) (versión 10.13.1 o superior)
- [Go](https://golang.org) (versión 1.21 o superior) (opcional)
### Instalación
```bash
# Clonar el repositorio
git clone https://github.com/setube/ogame-vue-ts.git
# Entrar al directorio del proyecto
cd ogame-vue-ts
# Instalar dependencias
pnpm install
```
### Desarrollo
```bash
# Iniciar servidor de desarrollo (puerto 25121)
pnpm dev
```
Visita `http://localhost:25121` en tu navegador
### Construcción para Producción
```bash
# Construir la aplicación
pnpm build
# Vista previa de la construcción
pnpm preview
```
## Seguridad de Datos
Todos los datos del juego se encriptan automáticamente usando AES antes de almacenarse en el almacenamiento local del navegador. Tu progreso de juego es seguro y privado.
## Personalización
La aplicación soporta personalización completa de temas a través de variables CSS de Tailwind definidas en `src/style.css`. Puedes cambiar fácilmente entre modo claro y oscuro.
## Contribuciones
¡Las contribuciones son bienvenidas! No dudes en enviar issues o pull requests.
## Licencia
Este trabajo está licenciado bajo la [Licencia Creative Commons Atribución-NoComercial 4.0 Internacional](https://creativecommons.org/licenses/by-nc/4.0).
### Eres libre de:
- **Compartir** — Copiar y redistribuir el material en cualquier medio o formato
- **Adaptar** — Remezclar, transformar y construir a partir del material
### Bajo los siguientes términos:
- **Atribución** — Debes dar crédito apropiado, proporcionar un enlace a la licencia e indicar si se realizaron cambios
- **NoComercial** — No puedes usar el material para fines comerciales
## Agradecimientos
Este proyecto está inspirado en el juego de navegador original [OGame](https://ogame.org). Todas las mecánicas de juego y elementos de diseño han sido reimplementados con fines educativos y de entretenimiento.
## Aviso Legal
Este proyecto no está afiliado, respaldado ni conectado con Gameforge AG o el juego oficial OGame. Este es un proyecto independiente de fans creado únicamente con fines educativos y de entretenimiento personal.
---
<div align="center">
Hecho con ❤️ por <a href="https://github.com/setube">setube</a>
<br>
© 2025 - Todos los derechos reservados (excepto los otorgados por la licencia CC BY-NC 4.0)
</div>

125
README-JA.md Normal file
View File

@@ -0,0 +1,125 @@
<div align="center">
<img src="public/logo.svg" alt="OGame Vue TS Logo" width="128" height="128">
# OGame Vue TS
Vue 3とTypeScriptで構築されたモダンな宇宙戦略ゲーム。
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
[简体中文](README.md) | [繁體中文](README-zh-TW.md) | [English](README-EN.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [Español](README-ES.md) | [한국어](README-KO.md) | 日本語
</div>
## プロジェクトについて
OGame Vue TSは、クラシックなOGameにインスパイアされたシングルプレイヤー向けブラウザベースの宇宙戦略ゲームです。銀河に帝国を築き、テクロジーを研究し、宇宙船を建造し、壮大な宇宙戦闘に参加しましょう。このプロジェクトはモダンなWeb技術で構築されており、ブラウザ内で完全に動作し、すべてのデータはローカルに保存されます。
## 主な機能
- **建物管理** - 惑星と月で様々な建物を建設・アップグレード
- **技術研究** - 先進技術をアンロックして帝国を強化
- **艦隊管理** - 宇宙船を建造し、ミッションを派遣し、戦術的な宇宙戦闘に参加
- **防衛システム** - 防衛施設を配置してコロニーを守る
- **士官システム** - 士官を雇用して戦略的優位性を獲得
- **戦闘シミュレーター** - 資源を投入する前に戦闘シナリオをテスト
- **銀河ビュー** - 宇宙を探索し、他の惑星と交流
- **ローカルデータ永続化** - すべてのゲームデータは暗号化されブラウザにローカル保存
- **ダーク/ライトテーマ** - お好みのビジュアルテーマを選択
- **キュー管理** - 複数の建設・研究キューを管理
- **月の生成** - デブリフィールドからの確率ベースの月生成メカニズム
## 技術スタック
- **フロントエンドフレームワーク:** [Vue 3](https://vuejs.org) + Composition API (`<script setup>` 構文)
- **開発言語:** [TypeScript](https://www.typescriptlang.org) (厳密な型チェック有効)
- **ビルドツール:** [Vite](https://vitejs.dev) (カスタムRolldown-Vite 7.2.5)、[Golang](https://golang.org) (クロスプラットフォームWebサーバー)、[Electron](https://www.electronjs.org) (クロスプラットフォームビジュアルインターフェース)
- **状態管理:** [Pinia](https://pinia.vuejs.org) + 永続化プラグイン
- **ルーティング:** [Vue Router 4](https://router.vuejs.org)
- **UIコンポーネント:** [shadcn-vue](https://www.shadcn-vue.com) (New Yorkスタイル)
- **スタイリング:** [Tailwind CSS v4](https://tailwindcss.com) + CSS変数
- **アイコン:** [Lucide Vue Next](https://lucide.dev)
- **アニメーション:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **国際化:** カスタムi18n実装
## クイックスタート
### 必要条件
- [Node.js](https://nodejs.org) (バージョン18以上推奨)
- [pnpm](https://pnpm.io) (バージョン10.13.1以上)
- [Go](https://golang.org) (バージョン1.21以上) (オプション)
### インストール
```bash
# リポジトリをクローン
git clone https://github.com/setube/ogame-vue-ts.git
# プロジェクトディレクトリに移動
cd ogame-vue-ts
# 依存関係をインストール
pnpm install
```
### 開発
```bash
# 開発サーバーを起動 (ポート25121)
pnpm dev
```
ブラウザで `http://localhost:25121` にアクセス
### 本番ビルド
```bash
# アプリケーションをビルド
pnpm build
# 本番ビルドをプレビュー
pnpm preview
```
## データセキュリティ
すべてのゲームデータは、ブラウザのローカルストレージに保存される前にAESで自動的に暗号化されます。ゲームの進行状況は安全でプライベートです。
## カスタマイズ
アプリケーションは`src/style.css`で定義されたTailwind CSS変数による完全なテーマカスタマイズをサポートしています。ライトモードとダークモードを簡単に切り替えることができます。
## 貢献
貢献は大歓迎ですお気軽にissueやpull requestを提出してください。
## ライセンス
この作品は[クリエイティブ・コモンズ 表示-非営利 4.0 国際ライセンス](https://creativecommons.org/licenses/by-nc/4.0)の下でライセンスされています。
### あなたは自由に:
- **共有** — どのようなメディアやフォーマットでも資料をコピー・再配布できます
- **翻案** — 資料をリミックス、変形、加工できます
### 以下の条件に従う必要があります:
- **表示** — 適切なクレジットを表示し、ライセンスへのリンクを提供し、変更があったかどうかを示す必要があります
- **非営利** — 資料を営利目的で使用することはできません
## 謝辞
このプロジェクトはオリジナルの[OGame](https://ogame.org)ブラウザゲームにインスパイアされています。すべてのゲームメカニクスとデザイン要素は、教育およびエンターテイメント目的で再実装されています。
## 免責事項
このプロジェクトはGameforge AGや公式OGameゲームとは一切関係がなく、承認や接続もありません。これは教育と個人的なエンターテイメントのみを目的として作成された独立したファンプロジェクトです。
---
<div align="center">
❤️を込めて作成 by <a href="https://github.com/setube">setube</a>
<br>
© 2025 - All rights reserved (CC BY-NC 4.0ライセンスで付与された権利を除く)
</div>

View File

@@ -6,13 +6,10 @@
Vue 3와 TypeScript로 제작된 클래식 OGame을 기반으로 한 현대적인 우주 전략 게임입니다.
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
[![Vue 3](https://img.shields.io/badge/Vue-3.5-brightgreen.svg)](https://vuejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![Vite](https://img.shields.io/badge/Vite-7.2-646CFF.svg)](https://vitejs.dev/)
[![Go](https://img.shields.io/badge/Go-1.23-79D4FD.svg)](https://golang.org/)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
[简体中文](README.md) | [English](README-EN.md) | [繁體中文](README-zh-TW.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | 한국어
[简体中文](README.md) | [繁體中文](README-zh-TW.md) | [English](README-EN.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [Español](README-ES.md) | 한국어 | [日本語](README-JA.md)
</div>
@@ -22,7 +19,6 @@ OGame Vue TS는 클래식 OGame에서 영감을 받은 싱글플레이어 브라
## 주요 기능
- **다국어 지원** - 6개 언어 지원: 영어, 중국어 (간체 및 번체), 독일어, 러시아어, 한국어
- **건물 관리** - 행성과 달에서 다양한 건물을 건설하고 업그레이드
- **기술 연구** - 제국을 강화하기 위한 첨단 기술 해금
- **함대 관리** - 우주선 건조, 미션 파견, 전술적 우주 전투 참여
@@ -37,14 +33,14 @@ OGame Vue TS는 클래식 OGame에서 영감을 받은 싱글플레이어 브라
## 기술 스택
- **프론트엔드 프레임워크:** [Vue 3](https://vuejs.org/) + Composition API (`<script setup>` 문법)
- **프로그래밍 언어:** [TypeScript](https://www.typescriptlang.org/) (엄격한 타입 검사 활성화)
- **빌드 도구:** [Vite](https://vitejs.dev/) (Custom Rolldown-Vite 7.2.5), [Golang](https://golang.org/) (크로스 플랫폼 웹 서버 구축), [Electron](https://www.electronjs.org/) (크로스 플랫폼 데스크톱 애플리케이션 구축)
- **상태 관리:** [Pinia](https://pinia.vuejs.org/) + 지속성 플러그인
- **라우팅:** [Vue Router 4](https://router.vuejs.org/)
- **UI 컴포넌트:** [shadcn-vue](https://www.shadcn-vue.com/) (New York 스타일)
- **스타일링:** [Tailwind CSS v4](https://tailwindcss.com/) + CSS 변수
- **아이콘:** [Lucide Vue Next](https://lucide.dev/)
- **프론트엔드 프레임워크:** [Vue 3](https://vuejs.org) + Composition API (`<script setup>` 문법)
- **프로그래밍 언어:** [TypeScript](https://www.typescriptlang.org) (엄격한 타입 검사 활성화)
- **빌드 도구:** [Vite](https://vitejs.dev) (Custom Rolldown-Vite 7.2.5), [Golang](https://golang.org) (크로스 플랫폼 웹 서버 구축), [Electron](https://www.electronjs.org) (크로스 플랫폼 데스크톱 애플리케이션 구축)
- **상태 관리:** [Pinia](https://pinia.vuejs.org) + 지속성 플러그인
- **라우팅:** [Vue Router 4](https://router.vuejs.org)
- **UI 컴포넌트:** [shadcn-vue](https://www.shadcn-vue.com) (New York 스타일)
- **스타일링:** [Tailwind CSS v4](https://tailwindcss.com) + CSS 변수
- **아이콘:** [Lucide Vue Next](https://lucide.dev)
- **애니메이션:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **국제화:** 커스텀 i18n 구현
@@ -52,9 +48,9 @@ OGame Vue TS는 클래식 OGame에서 영감을 받은 싱글플레이어 브라
### 요구 사항
- [Node.js](https://nodejs.org/) (버전 18 이상 권장)
- [pnpm](https://pnpm.io/) (버전 10.13.1 이상)
- [Go](https://golang.org/) (버전 1.21 이상) (선택 사항)
- [Node.js](https://nodejs.org) (버전 18 이상 권장)
- [pnpm](https://pnpm.io) (버전 10.13.1 이상)
- [Go](https://golang.org) (버전 1.21 이상) (선택 사항)
### 설치
@@ -88,47 +84,6 @@ pnpm build
pnpm preview
```
## 지원 언어
- 简体中文 (중국어 간체)
- 繁體中文 (중국어 번체)
- English (영어)
- Deutsch (독일어)
- Русский (러시아어)
- 한국어
## 게임 기능
### 자원 관리
- **금속** - 기본 건설 재료
- **크리스탈** - 첨단 기술 부품
- **중수소** - 연료 및 연구 자원
- **암흑 물질** - 프리미엄 자원
- **에너지** - 시설에 전력 공급
### 건물 유형
- **자원 건물** - 금속 광산, 크리스탈 광산, 중수소 합성기, 태양열 발전소
- **시설** - 로봇 공장, 조선소, 연구소, 저장 시설
- **특수 건물** - 나노 로봇 공장, 테라포머 등
### 기술
- **에너지 기술** - 에너지 효율 향상
- **레이저 기술** - 무기 시스템 강화
- **이온 기술** - 고급 추진 및 무기
- **초공간 기술** - 더 빠른 이동 가능
- **플라즈마 기술** - 궁극의 무기 시스템
- 그 외 다수...
### 함선 종류
- **민간 함선** - 소형/대형 수송선, 식민선, 재활용선
- **전투 함선** - 경/중 전투기, 순양함, 전함, 폭격기
- **특수 함선** - 데스스타, 전투순양함, 파괴자
### 방어 시스템
- 로켓 발사대, 경/중 레이저, 가우스 포
- 이온 포, 플라즈마 포탑
- 소형/대형 실드 돔
## 데이터 보안
모든 게임 데이터는 브라우저의 로컬 스토리지에 저장되기 전에 AES 암호화로 자동 암호화됩니다. 게임 진행 상황은 안전하고 비공개로 유지됩니다.
@@ -143,7 +98,7 @@ pnpm preview
## 라이선스
이 작품은 [크리에이티브 커먼즈 저작자표시-비영리 4.0 국제 라이선스](https://creativecommons.org/licenses/by-nc/4.0/)에 따라 라이선스가 부여됩니다.
이 작품은 [크리에이티브 커먼즈 저작자표시-비영리 4.0 국제 라이선스](https://creativecommons.org/licenses/by-nc/4.0)에 따라 라이선스가 부여됩니다.
### 자유롭게:
- **공유** — 어떤 매체나 포맷으로든 자료를 복사하고 재배포할 수 있습니다
@@ -155,7 +110,7 @@ pnpm preview
## 감사의 말
이 프로젝트는 원작 [OGame](https://ogame.org/) 브라우저 게임에서 영감을 받았습니다. 모든 게임 메커니즘과 디자인 요소는 교육 및 오락 목적으로 재구현되었습니다.
이 프로젝트는 원작 [OGame](https://ogame.org) 브라우저 게임에서 영감을 받았습니다. 모든 게임 메커니즘과 디자인 요소는 교육 및 오락 목적으로 재구현되었습니다.
## 면책 조항

View File

@@ -6,13 +6,10 @@
Современная космическая стратегическая игра, вдохновлённая классической OGame, созданная на Vue 3 и TypeScript.
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
[![Vue 3](https://img.shields.io/badge/Vue-3.5-brightgreen.svg)](https://vuejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![Vite](https://img.shields.io/badge/Vite-7.2-646CFF.svg)](https://vitejs.dev/)
[![Go](https://img.shields.io/badge/Go-1.23-79D4FD.svg)](https://golang.org/)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
[简体中文](README.md) | [English](README-EN.md) | [繁體中文](README-zh-TW.md) | [Deutsch](README-DE.md) | Русский | [한국어](README-KO.md)
[简体中文](README.md) | [繁體中文](README-zh-TW.md) | [English](README-EN.md) | [Deutsch](README-DE.md) | Русский | [Español](README-ES.md) | [한국어](README-KO.md) | [日本語](README-JA.md)
</div>
@@ -22,7 +19,6 @@ OGame Vue TS — это однопользовательская браузер
## Основные возможности
- **Многоязычная поддержка** - Доступно на 6 языках: английский, китайский (упрощённый и традиционный), немецкий, русский и корейский
- **Управление зданиями** - Стройте и улучшайте различные здания на планетах и лунах
- **Исследование технологий** - Открывайте передовые технологии для усиления вашей империи
- **Управление флотом** - Стройте корабли, отправляйте миссии, участвуйте в тактических космических боях
@@ -37,14 +33,14 @@ OGame Vue TS — это однопользовательская браузер
## Технологический стек
- **Frontend-фреймворк:** [Vue 3](https://vuejs.org/) + Composition API (синтаксис `<script setup>`)
- **Язык программирования:** [TypeScript](https://www.typescriptlang.org/) (со строгой проверкой типов)
- **Инструмент сборки:** [Vite](https://vitejs.dev/) (Custom Rolldown-Vite 7.2.5), [Golang](https://golang.org/) (для кроссплатформенного веб-сервера), [Electron](https://www.electronjs.org/) (для кроссплатформенного десктоп-приложения)
- **Управление состоянием:** [Pinia](https://pinia.vuejs.org/) + плагин персистентности
- **Маршрутизация:** [Vue Router 4](https://router.vuejs.org/)
- **UI-компоненты:** [shadcn-vue](https://www.shadcn-vue.com/) (стиль New York)
- **Стилизация:** [Tailwind CSS v4](https://tailwindcss.com/) + CSS-переменные
- **Иконки:** [Lucide Vue Next](https://lucide.dev/)
- **Frontend-фреймворк:** [Vue 3](https://vuejs.org) + Composition API (синтаксис `<script setup>`)
- **Язык программирования:** [TypeScript](https://www.typescriptlang.org) (со строгой проверкой типов)
- **Инструмент сборки:** [Vite](https://vitejs.dev) (Custom Rolldown-Vite 7.2.5), [Golang](https://golang.org) (для кроссплатформенного веб-сервера), [Electron](https://www.electronjs.org) (для кроссплатформенного десктоп-приложения)
- **Управление состоянием:** [Pinia](https://pinia.vuejs.org) + плагин персистентности
- **Маршрутизация:** [Vue Router 4](https://router.vuejs.org)
- **UI-компоненты:** [shadcn-vue](https://www.shadcn-vue.com) (стиль New York)
- **Стилизация:** [Tailwind CSS v4](https://tailwindcss.com) + CSS-переменные
- **Иконки:** [Lucide Vue Next](https://lucide.dev)
- **Анимации:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **Интернационализация:** Собственная реализация i18n
@@ -52,9 +48,9 @@ OGame Vue TS — это однопользовательская браузер
### Требования
- [Node.js](https://nodejs.org/) (рекомендуется версия 18 или выше)
- [pnpm](https://pnpm.io/) (версия 10.13.1 или выше)
- [Go](https://golang.org/) (версия 1.21 или выше) (опционально)
- [Node.js](https://nodejs.org) (рекомендуется версия 18 или выше)
- [pnpm](https://pnpm.io) (версия 10.13.1 или выше)
- [Go](https://golang.org) (версия 1.21 или выше) (опционально)
### Установка
@@ -88,47 +84,6 @@ pnpm build
pnpm preview
```
## Доступные языки
- 简体中文 (Упрощённый китайский)
- 繁體中文 (Традиционный китайский)
- English (Английский)
- Deutsch (Немецкий)
- Русский
- 한국어 (Корейский)
## Игровые возможности
### Управление ресурсами
- **Металл** - Основной строительный материал
- **Кристалл** - Компонент передовых технологий
- **Дейтерий** - Топливо и ресурс для исследований
- **Тёмная материя** - Премиум-ресурс
- **Энергия** - Питает ваши сооружения
### Типы зданий
- **Ресурсные здания** - Металлоплавильня, Кристальный завод, Синтезатор дейтерия, Солнечная электростанция
- **Сооружения** - Завод роботов, Верфь, Исследовательская лаборатория, Хранилища
- **Специальные здания** - Фабрика нанитов, Терраформер и другие
### Технологии
- **Энергетическая технология** - Повышает энергоэффективность
- **Лазерная технология** - Улучшает системы вооружения
- **Ионная технология** - Продвинутая двигательная установка и оружие
- **Гиперпространственная технология** - Обеспечивает более быстрые перелёты
- **Плазменная технология** - Максимальные системы вооружения
- И многое другое...
### Классы кораблей
- **Гражданские корабли** - Малый/Большой транспорт, Колонизатор, Переработчик
- **Боевые корабли** - Лёгкий/Тяжёлый истребитель, Крейсер, Линкор, Бомбардировщик
- **Специальные корабли** - Звезда Смерти, Линейный крейсер, Разрушитель
### Системы обороны
- Ракетная установка, Лёгкий/Тяжёлый лазер, Пушка Гаусса
- Ионное орудие, Плазменная турель
- Малый/Большой защитный купол
## Безопасность данных
Все игровые данные автоматически шифруются с помощью AES перед сохранением в локальном хранилище браузера. Ваш игровой прогресс защищён и приватен.
@@ -143,7 +98,7 @@ pnpm preview
## Лицензия
Эта работа лицензирована под [Creative Commons Attribution-NonCommercial 4.0 International License](https://creativecommons.org/licenses/by-nc/4.0/).
Эта работа лицензирована под [Creative Commons Attribution-NonCommercial 4.0 International License](https://creativecommons.org/licenses/by-nc/4.0).
### Вы можете:
- **Делиться** — копировать и распространять материал в любом формате или на любом носителе
@@ -155,7 +110,7 @@ pnpm preview
## Благодарности
Этот проект вдохновлён оригинальной браузерной игрой [OGame](https://ogame.org/). Все игровые механики и элементы дизайна были переосмыслены в образовательных и развлекательных целях.
Этот проект вдохновлён оригинальной браузерной игрой [OGame](https://ogame.org). Все игровые механики и элементы дизайна были переосмыслены в образовательных и развлекательных целях.
## Отказ от ответственности

View File

@@ -6,13 +6,10 @@
一個基於 Vue 3 和 TypeScript 構建的現代化 OGame 太空策略遊戲。
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
[![Vue 3](https://img.shields.io/badge/Vue-3.5-brightgreen.svg)](https://vuejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![Vite](https://img.shields.io/badge/Vite-7.2-646CFF.svg)](https://vitejs.dev/)
[![Go](https://img.shields.io/badge/Go-1.23-79D4FD.svg)](https://golang.org/)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
[简体中文](README.md) | [English](README-EN.md) | 繁體中文 | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [한국어](README-KO.md)
[简体中文](README.md) | 繁體中文 | [English](README-EN.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [Español](README-ES.md) | [한국어](README-KO.md) | [日本語](README-JA.md)
</div>
@@ -22,7 +19,6 @@ OGame Vue TS 是一款受經典 OGame 遊戲啟發的單機版、基於瀏覽器
## 核心特性
- **多語言支援** - 支援 6 種語言:英語、簡體中文、繁體中文、德語、俄語和韓語
- **建築管理** - 在行星和月球上建造和升級各種建築
- **科技研究** - 解鎖先進科技來增強你的帝國
- **艦隊管理** - 建造艦船、派遣任務、參與戰術太空戰鬥
@@ -37,14 +33,14 @@ OGame Vue TS 是一款受經典 OGame 遊戲啟發的單機版、基於瀏覽器
## 技術棧
- **前端框架:** [Vue 3](https://vuejs.org/) + Composition API (`<script setup>` 語法)
- **開發語言:** [TypeScript](https://www.typescriptlang.org/) (啟用嚴格類型檢查)
- **構建工具:** [Vite](https://vitejs.dev/) (自定義 Rolldown-Vite 7.2.5)、[Golang](https://golang.org/)(構建跨平台的Web服務端)、[Electron](https://www.electronjs.org/)(構建跨平台可視化介面)
- **狀態管理:** [Pinia](https://pinia.vuejs.org/) + 持久化插件
- **路由管理:** [Vue Router 4](https://router.vuejs.org/)
- **UI 組件:** [shadcn-vue](https://www.shadcn-vue.com/) (New York 風格)
- **樣式方案:** [Tailwind CSS v4](https://tailwindcss.com/) + CSS 變數
- **圖標庫:** [Lucide Vue Next](https://lucide.dev/)
- **前端框架:** [Vue 3](https://vuejs.org) + Composition API (`<script setup>` 語法)
- **開發語言:** [TypeScript](https://www.typescriptlang.org) (啟用嚴格類型檢查)
- **構建工具:** [Vite](https://vitejs.dev) (自定義 Rolldown-Vite 7.2.5)、[Golang](https://golang.org)(構建跨平台的Web服務端)、[Electron](https://www.electronjs.org)(構建跨平台可視化介面)
- **狀態管理:** [Pinia](https://pinia.vuejs.org) + 持久化插件
- **路由管理:** [Vue Router 4](https://router.vuejs.org)
- **UI 組件:** [shadcn-vue](https://www.shadcn-vue.com) (New York 風格)
- **樣式方案:** [Tailwind CSS v4](https://tailwindcss.com) + CSS 變數
- **圖標庫:** [Lucide Vue Next](https://lucide.dev)
- **動畫效果:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **國際化:** 自定義 i18n 實現
@@ -52,9 +48,9 @@ OGame Vue TS 是一款受經典 OGame 遊戲啟發的單機版、基於瀏覽器
### 環境要求
- [Node.js](https://nodejs.org/) (推薦 18 或更高版本)
- [pnpm](https://pnpm.io/) (版本 10.13.1 或更高)
- [Go](https://golang.org/) (版本 1.21 或更高版本)(可選)
- [Node.js](https://nodejs.org) (推薦 18 或更高版本)
- [pnpm](https://pnpm.io) (版本 10.13.1 或更高)
- [Go](https://golang.org) (版本 1.21 或更高版本)(可選)
### 安裝
@@ -88,47 +84,6 @@ pnpm build
pnpm preview
```
## 支援的語言
- 简体中文
- 繁體中文
- English (英語)
- Deutsch (德語)
- Русский (俄語)
- 한국어 (韓語)
## 遊戲特性
### 資源管理
- **金屬** - 主要建築材料
- **晶體** - 高級科技組件
- **重氫** - 燃料和研究資源
- **暗物質** - 高級資源
- **能量** - 為設施供電
### 建築類型
- **資源建築** - 金屬礦、晶體礦、重氫合成器、太陽能發電廠
- **設施建築** - 機器人工廠、船塢、研究實驗室、倉儲設施
- **特殊建築** - 納米機器人工廠、行星改造器等
### 科技系統
- **能量技術** - 提高能量效率
- **激光技術** - 增強武器系統
- **離子技術** - 高級推進和武器
- **超空間技術** - 實現更快的旅行
- **等離子技術** - 終極武器系統
- 還有更多...
### 艦船類別
- **民用艦船** - 小型/大型貨船、殖民船、回收船
- **戰鬥艦船** - 輕型/重型戰鬥機、巡洋艦、戰列艦、轟炸機
- **特殊艦船** - 死星、戰列巡洋艦、毀滅者
### 防禦系統
- 火箭發射器、輕型/重型激光炮、高斯炮
- 離子炮、等離子炮塔
- 小型/大型防護罩
## 數據安全
所有遊戲數據在儲存到瀏覽器的本地存儲之前都會使用 AES 加密自動加密。您的遊戲進度是安全且私密的。
@@ -143,7 +98,7 @@ pnpm preview
## 許可證
本作品採用 [創用CC 姓名標示-非商業性 4.0 國際 授權條款](https://creativecommons.org/licenses/by-nc/4.0/) 授權。
本作品採用 [創用CC 姓名標示-非商業性 4.0 國際 授權條款](https://creativecommons.org/licenses/by-nc/4.0) 授權。
### 您可以自由地:
- **分享** — 以任何媒介或格式重製及散布本素材
@@ -155,7 +110,7 @@ pnpm preview
## 致謝
本專案受原版 [OGame](https://ogame.org/) 瀏覽器遊戲啟發。所有遊戲機制和設計元素都是為了教育和娛樂目的而重新實現的。
本專案受原版 [OGame](https://ogame.org) 瀏覽器遊戲啟發。所有遊戲機制和設計元素都是為了教育和娛樂目的而重新實現的。
## 免責聲明

View File

@@ -6,13 +6,10 @@
一个基于 Vue 3 和 TypeScript 构建的现代化 OGame 太空策略游戏。
[![GitHub Release](https://img.shields.io/github/v/release/setube/ogame-vue-ts?style=flat&logo=github&label=Release)](https://github.com/setube/ogame-vue-ts/releases/latest)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0/)
[![Vue 3](https://img.shields.io/badge/Vue-3.5-brightgreen.svg)](https://vuejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![Vite](https://img.shields.io/badge/Vite-7.2-646CFF.svg)](https://vitejs.dev/)
[![Go](https://img.shields.io/badge/Go-1.23-79D4FD.svg)](https://golang.org/)
[![License: CC BY-NC 4.0](https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc/4.0)
[![Tap Tap](https://img.shields.io/badge/TapTap-OGame%20Vue%20Ts-18d6e0)](https://www.taptap.cn/app/801190)
简体中文 | [English](README-EN.md) | [繁體中文](README-zh-TW.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [한국어](README-KO.md)
简体中文 | [繁體中文](README-zh-TW.md) | [English](README-EN.md) | [Deutsch](README-DE.md) | [Русский](README-RU.md) | [Español](README-ES.md) | [한국어](README-KO.md) | [日本語](README-JA.md)
</div>
@@ -22,7 +19,6 @@ OGame Vue TS 是一款受经典 OGame 游戏启发的单机版、基于浏览器
## 核心特性
- **多语言支持** - 支持 6 种语言:英语、简体中文、繁体中文、德语、俄语和韩语
- **建筑管理** - 在行星和月球上建造和升级各种建筑
- **科技研究** - 解锁先进科技来增强你的帝国
- **舰队管理** - 建造舰船、派遣任务、参与战术太空战斗
@@ -37,14 +33,14 @@ OGame Vue TS 是一款受经典 OGame 游戏启发的单机版、基于浏览器
## 技术栈
- **前端框架:** [Vue 3](https://vuejs.org/) + Composition API (`<script setup>` 语法)
- **开发语言:** [TypeScript](https://www.typescriptlang.org/) (启用严格类型检查)
- **构建工具:** [Vite](https://vitejs.dev/) (自定义 Rolldown-Vite 7.2.5)、[Golang](https://golang.org/)(构建跨平台的Web服务端)、[Electron](https://www.electronjs.org/)(构建跨平台可视化界面)
- **状态管理:** [Pinia](https://pinia.vuejs.org/) + 持久化插件
- **路由管理:** [Vue Router 4](https://router.vuejs.org/)
- **UI 组件:** [shadcn-vue](https://www.shadcn-vue.com/) (New York 风格)
- **样式方案:** [Tailwind CSS v4](https://tailwindcss.com/) + CSS 变量
- **图标库:** [Lucide Vue Next](https://lucide.dev/)
- **前端框架:** [Vue 3](https://vuejs.org) + Composition API (`<script setup>` 语法)
- **开发语言:** [TypeScript](https://www.typescriptlang.org) (启用严格类型检查)
- **构建工具:** [Vite](https://vitejs.dev) (自定义 Rolldown-Vite 7.2.5)、[Golang](https://golang.org)(构建跨平台的Web服务端)、[Electron](https://www.electronjs.org)(构建跨平台可视化界面)
- **状态管理:** [Pinia](https://pinia.vuejs.org) + 持久化插件
- **路由管理:** [Vue Router 4](https://router.vuejs.org)
- **UI 组件:** [shadcn-vue](https://www.shadcn-vue.com) (New York 风格)
- **样式方案:** [Tailwind CSS v4](https://tailwindcss.com) + CSS 变量
- **图标库:** [Lucide Vue Next](https://lucide.dev)
- **动画效果:** [tw-animate-css](https://www.npmjs.com/package/tw-animate-css)
- **国际化:** 自定义 i18n 实现
@@ -52,9 +48,9 @@ OGame Vue TS 是一款受经典 OGame 游戏启发的单机版、基于浏览器
### 环境要求
- [Node.js](https://nodejs.org/) (推荐 18 或更高版本)
- [pnpm](https://pnpm.io/) (版本 10.13.1 或更高)
- [Go](https://golang.org/) (版本 1.21 或更高版本)(可选)
- [Node.js](https://nodejs.org) (推荐 18 或更高版本)
- [pnpm](https://pnpm.io) (版本 10.13.1 或更高)
- [Go](https://golang.org) (版本 1.21 或更高版本)(可选)
### 安装
@@ -88,47 +84,6 @@ pnpm build
pnpm preview
```
## 支持的语言
- 简体中文
- 繁體中文
- English (英语)
- Deutsch (德语)
- Русский (俄语)
- 한국어 (韩语)
## 游戏特性
### 资源管理
- **金属** - 主要建筑材料
- **晶体** - 高级科技组件
- **重氢** - 燃料和研究资源
- **暗物质** - 高级资源
- **能量** - 为设施供电
### 建筑类型
- **资源建筑** - 金属矿、晶体矿、重氢合成器、太阳能发电厂
- **设施建筑** - 机器人工厂、船坞、研究实验室、仓储设施
- **特殊建筑** - 纳米机器人工厂、行星改造器等
### 科技系统
- **能量技术** - 提高能量效率
- **激光技术** - 增强武器系统
- **离子技术** - 高级推进和武器
- **超空间技术** - 实现更快的旅行
- **等离子技术** - 终极武器系统
- 还有更多...
### 舰船类别
- **民用舰船** - 小型/大型货船、殖民船、回收船
- **战斗舰船** - 轻型/重型战斗机、巡洋舰、战列舰、轰炸机
- **特殊舰船** - 死星、战列巡洋舰、毁灭者
### 防御系统
- 火箭发射器、轻型/重型激光炮、高斯炮
- 离子炮、等离子炮塔
- 小型/大型防护罩
## 数据安全
所有游戏数据在存储到浏览器的本地存储之前都会使用 AES 加密自动加密。您的游戏进度是安全且私密的。
@@ -143,7 +98,7 @@ pnpm preview
## 许可证
本作品采用 [知识共享署名-非商业性使用 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc/4.0/) 进行许可。
本作品采用 [知识共享署名-非商业性使用 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc/4.0) 进行许可。
### 您可以自由地:
- **共享** — 在任何媒介以任何形式复制、发行本作品
@@ -155,7 +110,7 @@ pnpm preview
## 致谢
本项目受原版 [OGame](https://ogame.org/) 浏览器游戏启发。所有游戏机制和设计元素都是为了教育和娱乐目的而重新实现的。
本项目受原版 [OGame](https://ogame.org) 浏览器游戏启发。所有游戏机制和设计元素都是为了教育和娱乐目的而重新实现的。
## 免责声明

View File

@@ -75,7 +75,7 @@ func main() {
// --- 4. 控制台信息展示 ---
fmt.Println("=======================================")
fmt.Printf("OGame 服务启动成功!\n")
fmt.Printf("OGame Vue Ts 服务启动成功!\n")
fmt.Printf("启动时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
fmt.Printf("本地访问: %s\n", localUrl)
fmt.Printf("局域网访问: %s\n", lanUrl)

View File

@@ -8,14 +8,15 @@
"email": "1962257451@qq.com"
},
"private": true,
"version": "1.5.0",
"buildDate": "2025/12/24 01:51:29",
"version": "1.5.5",
"buildDate": "2025/12/25 18:23:43",
"main": "dist-electron/main.js",
"type": "module",
"scripts": {
"dev": "vite --port 25121",
"build": "vue-tsc -b && vite build && node update-build-date.js",
"preview": "vite preview",
"build:check": "pnpm run build",
"build:server": "pnpm run build && go build",
"build:electron": "cross-env ELECTRON_BUILD=1 pnpm run build && electron-builder",
"build:android": "pnpm run build && npx cap sync android",
@@ -51,6 +52,7 @@
"@types/node": "^24.10.2",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.8.1",
"autoprefixer": "^10.4.23",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cross-env": "^7.0.3",
@@ -92,6 +94,16 @@
"verifyUpdateCodeSignature": false,
"artifactName": "${productName}-Setup.${ext}"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"allowElevation": true,
"installerIcon": "public/favicon.ico",
"uninstallerIcon": "public/favicon.ico",
"installerHeaderIcon": "public/favicon.ico",
"createDesktopShortcut": true,
"createStartMenuShortcut": true
},
"mac": {
"target": [
"dmg",

29
pnpm-lock.yaml generated
View File

@@ -93,6 +93,9 @@ importers:
'@vue/tsconfig':
specifier: ^0.8.1
version: 0.8.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
autoprefixer:
specifier: ^10.4.23
version: 10.4.23(postcss@8.5.6)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -1669,6 +1672,13 @@ packages:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
autoprefixer@10.4.23:
resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==}
engines: {node: ^10 || ^12 || >=14}
hasBin: true
peerDependencies:
postcss: ^8.1.0
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
@@ -2205,6 +2215,9 @@ packages:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'}
fraction.js@5.3.4:
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
framer-motion@12.23.12:
resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==}
peerDependencies:
@@ -3101,6 +3114,9 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
postcss@8.5.6:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
@@ -5664,6 +5680,15 @@ snapshots:
at-least-node@1.0.0: {}
autoprefixer@10.4.23(postcss@8.5.6):
dependencies:
browserslist: 4.28.1
caniuse-lite: 1.0.30001760
fraction.js: 5.3.4
picocolors: 1.1.1
postcss: 8.5.6
postcss-value-parser: 4.2.0
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.1.0
@@ -6370,6 +6395,8 @@ snapshots:
hasown: 2.0.2
mime-types: 2.1.35
fraction.js@5.3.4: {}
framer-motion@12.23.12:
dependencies:
motion-dom: 12.23.12
@@ -7240,6 +7267,8 @@ snapshots:
possible-typed-array-names@1.1.0: {}
postcss-value-parser@4.2.0: {}
postcss@8.5.6:
dependencies:
nanoid: 3.3.11

View File

@@ -604,7 +604,7 @@
const showUpdateDialog = ref(false)
const updateInfo = ref<VersionInfo | null>(null)
// 所有可用的语言选项
const locales: Locale[] = ['zh-CN', 'zh-TW', 'en', 'de', 'ru', 'ko', 'ja']
const locales: Locale[] = ['zh-CN', 'zh-TW', 'en', 'de', 'ru', 'es-LA', 'ko', 'ja']
// 侧边栏状态(不持久化,根据屏幕尺寸初始化)
// PC端≥1024px默认打开移动端默认关闭
const sidebarOpen = ref(window.innerWidth >= 1024)
@@ -714,7 +714,21 @@
const unreadNPCActivity = gameStore.player.npcActivityNotifications?.filter(n => !n.read).length || 0
const unreadGifts = gameStore.player.giftNotifications?.filter(n => !n.read).length || 0
const unreadGiftRejected = gameStore.player.giftRejectedNotifications?.filter(n => !n.read).length || 0
return unreadBattles + unreadSpies + unreadSpied + unreadMissions + unreadNPCActivity + unreadGifts + unreadGiftRejected
const unreadTradeOffers = gameStore.player.tradeOffers?.filter(o => !o.read).length || 0
const unreadIntelReports = gameStore.player.intelReports?.filter(r => !r.read).length || 0
const unreadJointAttacks = gameStore.player.jointAttackInvites?.filter(i => !i.read).length || 0
return (
unreadBattles +
unreadSpies +
unreadSpied +
unreadMissions +
unreadNPCActivity +
unreadGifts +
unreadGiftRejected +
unreadTradeOffers +
unreadIntelReports +
unreadJointAttacks
)
})
// 正在执行的舰队任务数量(包括飞行中的导弹)
@@ -1972,6 +1986,87 @@
})
}
})
// 处理增强NPC行为中立和友好NPC的特殊行为
const relation = npc.relations?.[gameStore.player.id]
if (relation?.status === 'neutral') {
const neutralResult = npcBehaviorLogic.updateNeutralNPCBehavior(npc, npcStore.npcs, gameStore.player, now)
// 处理贸易提议
if (neutralResult.tradeOffer) {
if (!gameStore.player.tradeOffers) {
gameStore.player.tradeOffers = []
}
gameStore.player.tradeOffers.push(neutralResult.tradeOffer)
toast.info(t('npcBehavior.tradeOfferReceived'), {
description: t('npcBehavior.tradeOfferDesc', { npcName: neutralResult.tradeOffer.npcName })
})
}
// 处理态度摇摆
if (neutralResult.swingDirection) {
if (!gameStore.player.attitudeChangeNotifications) {
gameStore.player.attitudeChangeNotifications = []
}
gameStore.player.attitudeChangeNotifications.push({
id: `attitude_${Date.now()}_${npc.id}`,
timestamp: now,
npcId: npc.id,
npcName: npc.name,
previousStatus: 'neutral',
newStatus: neutralResult.swingDirection,
reason: 'attitude_swing',
read: false
})
const statusKey = neutralResult.swingDirection === 'friendly' ? 'npcBehavior.becameFriendly' : 'npcBehavior.becameHostile'
toast.info(t('npcBehavior.attitudeChanged'), {
description: t(statusKey, { npcName: npc.name })
})
}
} else if (relation?.status === 'friendly') {
const friendlyResult = npcBehaviorLogic.updateFriendlyNPCBehavior(npc, npcStore.npcs, gameStore.player, now)
// 处理情报报告
if (friendlyResult.intelReport) {
if (!gameStore.player.intelReports) {
gameStore.player.intelReports = []
}
gameStore.player.intelReports.push(friendlyResult.intelReport)
toast.info(t('npcBehavior.intelReceived'), {
description: t('npcBehavior.intelReceivedDesc', { npcName: friendlyResult.intelReport.fromNpcName })
})
}
// 处理联合攻击邀请
if (friendlyResult.jointAttackInvite) {
if (!gameStore.player.jointAttackInvites) {
gameStore.player.jointAttackInvites = []
}
gameStore.player.jointAttackInvites.push(friendlyResult.jointAttackInvite)
toast.info(t('npcBehavior.jointAttackInvite'), {
description: t('npcBehavior.jointAttackInviteDesc', { npcName: friendlyResult.jointAttackInvite.fromNpcName })
})
}
// 处理资源援助
if (friendlyResult.aidProvided) {
if (!gameStore.player.aidNotifications) {
gameStore.player.aidNotifications = []
}
gameStore.player.aidNotifications.push({
id: `aid_${Date.now()}_${npc.id}`,
timestamp: now,
npcId: npc.id,
npcName: npc.name,
aidResources: friendlyResult.aidProvided,
read: false
})
const totalAid = friendlyResult.aidProvided.metal + friendlyResult.aidProvided.crystal + friendlyResult.aidProvided.deuterium
toast.success(t('npcBehavior.aidReceived'), {
description: t('npcBehavior.aidReceivedDesc', { npcName: npc.name, amount: totalAid.toLocaleString() })
})
}
}
})
npcBehaviorCounter.value = 0

View File

@@ -55,8 +55,8 @@
class="transition-all duration-300"
/>
<!-- 流动动画点激活状态 -->
<circle v-if="connection.isActive" r="3" :fill="'hsl(var(--primary))'" class="animate-flow">
<animateMotion :dur="'2s'" repeatCount="indefinite" :path="connection.path" />
<circle v-if="connection.isActive" r="3" fill="#CDD1D7" class="animate-flow">
<animateMotion dur="2s" repeatCount="indefinite" :path="connection.path" />
</circle>
</template>
</g>
@@ -87,7 +87,7 @@
<!-- 图例 -->
<div class="absolute top-4 left-4 flex flex-wrap gap-3 text-xs">
<div class="flex items-center gap-1">
<div class="w-3 h-3 rounded-full bg-green-500" />
<div class="w-3 h-3 rounded-full bg-green-500 dark:bg-green-400" />
<span class="text-muted-foreground">{{ t('campaign.completed') }}</span>
</div>
<div class="flex items-center gap-1">
@@ -95,7 +95,7 @@
<span class="text-muted-foreground">{{ t('campaign.inProgress') }}</span>
</div>
<div class="flex items-center gap-1">
<div class="w-3 h-3 rounded-full bg-blue-400 animate-pulse" />
<div class="w-3 h-3 rounded-full bg-blue-400 dark:bg-blue-300 animate-pulse" />
<span class="text-muted-foreground">{{ t('campaign.available') }}</span>
</div>
<div class="flex items-center gap-1">

View File

@@ -18,7 +18,7 @@
</div>
<!-- 分支标记 -->
<div v-if="quest.isBranch" class="absolute -top-1 -left-1 w-5 h-5 bg-blue-500 rounded-full flex items-center justify-center">
<div v-if="quest.isBranch" class="absolute -top-1 -left-1 w-5 h-5 bg-blue-500 dark:bg-blue-400 rounded-full flex items-center justify-center">
<GitBranch class="w-3 h-3 text-white" />
</div>
@@ -50,7 +50,7 @@
</div>
<!-- 脉冲动画可用状态 -->
<div v-if="status === QuestStatus.Available" class="absolute inset-0 w-14 h-14 rounded-full animate-ping bg-blue-400/30" />
<div v-if="status === QuestStatus.Available" class="absolute inset-0 w-14 h-14 rounded-full animate-ping bg-blue-400/30 dark:bg-blue-300/30" />
</div>
</template>

View File

@@ -2,7 +2,7 @@
<!-- 遮罩从标题下方开始不遮挡名称 -->
<div
v-if="!isUnlocked"
class="absolute inset-x-0 top-30 sm:top-25 bottom-0 z-10 bg-background/70 backdrop-blur-[2px] rounded-b-lg flex items-center justify-center"
class="absolute inset-x-0 top-20 sm:top-13 bottom-0 z-10 bg-background/70 backdrop-blur-[2px] rounded-b-lg flex items-center justify-center"
>
<div class="text-center p-4 space-y-2">
<div class="flex justify-center">

View File

@@ -437,7 +437,7 @@
import { ref, computed } from 'vue'
import { useI18n } from '@/composables/useI18n'
import { useGameStore } from '@/stores/gameStore'
import type { BuildingType, TechnologyType, ShipType, DefenseType } from '@/types/game'
import { BuildingType, TechnologyType, type ShipType, type DefenseType } from '@/types/game'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
@@ -452,7 +452,7 @@
import * as shipLogic from '@/logic/shipLogic'
import * as oreDepositLogic from '@/logic/oreDepositLogic'
import * as resourceLogic from '@/logic/resourceLogic'
import { SHIPS, DEFENSES } from '@/config/gameConfig'
import { SHIPS, DEFENSES, ORE_DEPOSIT_CONFIG } from '@/config/gameConfig'
import { formatTime } from '@/utils/format'
import { Progress } from '@/components/ui/progress'
import ResourceIcon from '@/components/common/ResourceIcon.vue'
@@ -576,11 +576,23 @@
const deposits = currentPlanet.value.oreDeposits
const resourceType = miningResourceType.value
const remaining = deposits[resourceType]
const initial =
resourceType === 'metal' ? deposits.initialMetal : resourceType === 'crystal' ? deposits.initialCrystal : deposits.initialDeuterium
const percentage = oreDepositLogic.getDepositPercentage(deposits, resourceType)
const isWarning = oreDepositLogic.isDepositWarning(deposits, resourceType)
const isDepleted = oreDepositLogic.isDepositDepleted(deposits, resourceType)
// 获取深层钻探设施等级(星球级)和采矿技术等级(全局)
const deepDrillingLevel = currentPlanet.value.buildings[BuildingType.DeepDrillingFacility] || 0
const miningTechLevel = gameStore.player?.technologies?.[TechnologyType.MiningTechnology] || 0
// 使用增强版计算函数获取带加成的储量上限
const enhancedDeposits = oreDepositLogic.calculateEnhancedDeposits(
deposits.position,
deepDrillingLevel,
miningTechLevel
)
const initial = enhancedDeposits[resourceType]
// 百分比基于增强后的上限计算
const percentage = initial > 0 ? (remaining / initial) * 100 : 0
const isWarning = percentage < ORE_DEPOSIT_CONFIG.WARNING_THRESHOLD * 100 && percentage > 0
const isDepleted = remaining <= 0
// 计算当前产量(每小时)
const production = resourceLogic.calculateResourceProduction(currentPlanet.value, {
@@ -591,7 +603,7 @@
const productionPerHour = production[resourceType]
// 计算耗尽时间
const depletionTimeHours = oreDepositLogic.calculateDepletionTime(deposits, resourceType, productionPerHour)
const depletionTimeHours = productionPerHour > 0 ? remaining / productionPerHour : Infinity
const depletionTimeFormatted = oreDepositLogic.formatDepletionTime(depletionTimeHours)
return {
@@ -601,7 +613,10 @@
isWarning,
isDepleted,
productionPerHour,
depletionTimeFormatted
depletionTimeFormatted,
bonusMultiplier: enhancedDeposits.bonusMultiplier,
deepDrillingLevel,
miningTechLevel
}
})

View File

@@ -1,7 +1,9 @@
<template>
<Popover>
<PopoverTrigger as-child>
<span class="cursor-pointer underline decoration-dotted underline-offset-4 touch-manipulation">{{ formatNumber(value, 1) }}</span>
<span class="cursor-pointer touch-manipulation" :class="value >= 1000 ? 'underline decoration-dotted underline-offset-4 ' : ''">
{{ formatNumber(value, 1) }}
</span>
</PopoverTrigger>
<PopoverContent class="w-auto p-2 z-100" side="top" align="center">
<p class="font-mono text-sm">{{ props.value.toLocaleString() }}</p>

View File

@@ -20,7 +20,7 @@
</Button>
</div>
<ScrollArea class="h-96">
<Empty v-if="reports.length === 0" class="border-0">
<Empty v-if="allNotifications.length === 0" class="border-0">
<EmptyContent>
<ScrollText class="h-10 w-10 text-muted-foreground" />
<EmptyDescription>{{ t('diplomacy.noReports') }}</EmptyDescription>
@@ -28,48 +28,49 @@
</Empty>
<div v-else class="divide-y">
<div
v-for="report in reports"
:key="report.id"
v-for="notification in allNotifications"
:key="notification.id"
class="p-3 hover:bg-muted/50 cursor-pointer transition-colors"
:class="{ 'bg-primary/5': !report.read }"
@click="handleReportClick(report)"
:class="{ 'bg-primary/5': !notification.read }"
@click="handleNotificationClick(notification)"
>
<div class="flex items-center gap-3">
<!-- 左侧事件图标 -->
<div class="shrink-0">
<component :is="getEventIcon(report.eventType)" class="h-5 w-5" :class="getEventIconColor(report.eventType)" />
<component :is="getNotificationIcon(notification)" class="h-5 w-5" :class="getNotificationIconColor(notification)" />
</div>
<!-- 中间主要信息 -->
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<span class="font-medium text-sm truncate">{{ getNpcName(report) }}</span>
<Badge :variant="getStatusBadgeVariant(report.newStatus)" class="text-xs shrink-0">
{{ getStatusText(report.newStatus) }}
<span class="font-medium text-sm truncate">{{ getNotificationNpcName(notification) }}</span>
<Badge :variant="getNotificationBadgeVariant(notification)" class="text-xs shrink-0">
{{ getNotificationBadgeText(notification) }}
</Badge>
</div>
<p class="text-xs text-muted-foreground mt-0.5">
{{ getEventTypeText(report.eventType) }}
{{ getNotificationTitle(notification) }}
</p>
</div>
<!-- 右侧好感度变化和时间 -->
<!-- 右侧额外信息和时间 -->
<div class="shrink-0 text-right">
<span
v-if="getNotificationExtra(notification)"
class="text-sm font-bold block"
:class="report.reputationChange >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'"
:class="getNotificationExtra(notification)?.colorClass"
>
{{ report.reputationChange >= 0 ? '+' : '' }}{{ report.reputationChange }}
{{ getNotificationExtra(notification)?.text }}
</span>
<span class="text-[10px] text-muted-foreground">
{{ formatRelativeTime((Date.now() - report.timestamp) / 1000, t) }}{{ t('diplomacy.ago') }}
{{ formatRelativeTime((Date.now() - notification.timestamp) / 1000, t) }}{{ t('diplomacy.ago') }}
</span>
</div>
<!-- 未读标记 -->
<span v-if="!report.read" class="h-2 w-2 rounded-full bg-destructive shrink-0" />
<span v-if="!notification.read" class="h-2 w-2 rounded-full bg-destructive shrink-0" />
</div>
</div>
</div>
</ScrollArea>
<div v-if="reports.length > 0" class="p-2 border-t">
<div v-if="allNotifications.length > 0" class="p-2 border-t">
<Button variant="ghost" size="sm" class="w-full" @click="goToDiplomacy">
{{ t('diplomacy.viewAll') }}
</Button>
@@ -78,7 +79,7 @@
</Popover>
<!-- 外交报告详情对话框 -->
<Dialog :open="detailDialogOpen" @update:open="detailDialogOpen = $event">
<Dialog :open="detailDialogOpen && selectedNotification?.type === 'diplomatic'" @update:open="detailDialogOpen = $event">
<DialogContent class="max-w-2xl">
<DialogHeader>
<DialogTitle class="flex items-center gap-2">
@@ -101,7 +102,7 @@
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<h3 class="font-semibold text-lg">{{ getNpcName(selectedReport) }}</h3>
<Badge :variant="getStatusBadgeVariant(selectedReport.newStatus)">
<Badge v-if="selectedReport.newStatus" :variant="getStatusBadgeVariant(selectedReport.newStatus)">
{{ getStatusText(selectedReport.newStatus) }}
</Badge>
</div>
@@ -123,36 +124,41 @@
</p>
</div>
<!-- 关系变化 -->
<div class="grid grid-cols-2 gap-4">
<!-- 关系变化 (仅当有 reputationChange 时显示) -->
<div v-if="selectedReport.reputationChange !== undefined" class="grid grid-cols-2 gap-4">
<!-- 好感度变化 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm">{{ t('diplomacy.reputationChange') }}</h4>
<div class="p-3 bg-muted/30 rounded-md">
<div class="flex items-center justify-between text-sm mb-2">
<span class="text-muted-foreground">{{ t('diplomacy.before') }}</span>
<span class="font-semibold" :class="getReputationColor(selectedReport.newReputation - selectedReport.reputationChange)">
{{ selectedReport.newReputation - selectedReport.reputationChange > 0 ? '+' : ''
}}{{ selectedReport.newReputation - selectedReport.reputationChange }}
<span
class="font-semibold"
:class="getReputationColor((selectedReport.newReputation || 0) - (selectedReport.reputationChange || 0))"
>
{{ (selectedReport.newReputation || 0) - (selectedReport.reputationChange || 0) > 0 ? '+' : ''
}}{{ (selectedReport.newReputation || 0) - (selectedReport.reputationChange || 0) }}
</span>
</div>
<div
class="flex items-center justify-center text-lg font-bold my-1"
:class="selectedReport.reputationChange >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'"
:class="
(selectedReport.reputationChange || 0) >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'
"
>
{{ selectedReport.reputationChange >= 0 ? '+' : '' }}{{ selectedReport.reputationChange }}
{{ (selectedReport.reputationChange || 0) >= 0 ? '+' : '' }}{{ selectedReport.reputationChange || 0 }}
</div>
<div class="flex items-center justify-between text-sm mt-2">
<span class="text-muted-foreground">{{ t('diplomacy.after') }}</span>
<span class="font-semibold" :class="getReputationColor(selectedReport.newReputation)">
{{ selectedReport.newReputation > 0 ? '+' : '' }}{{ selectedReport.newReputation }}
<span class="font-semibold" :class="getReputationColor(selectedReport.newReputation ?? null)">
{{ (selectedReport.newReputation || 0) > 0 ? '+' : '' }}{{ selectedReport.newReputation || 0 }}
</span>
</div>
</div>
</div>
<!-- 关系状态变化 -->
<div class="space-y-2">
<div v-if="selectedReport.oldStatus && selectedReport.newStatus" class="space-y-2">
<h4 class="font-semibold text-sm">{{ t('diplomacy.statusChange') }}</h4>
<div class="p-3 bg-muted/30 rounded-md">
<div class="flex items-center justify-between text-sm mb-2">
@@ -189,10 +195,294 @@
</DialogFooter>
</DialogContent>
</Dialog>
<!-- 贸易提议详情对话框 -->
<Dialog :open="detailDialogOpen && selectedNotification?.type === 'trade'" @update:open="detailDialogOpen = $event">
<DialogContent class="max-w-lg">
<DialogHeader>
<DialogTitle class="flex items-center gap-2">
<ArrowLeftRight class="h-5 w-5 text-blue-500" />
{{ t('npcBehavior.trade.title') }}
</DialogTitle>
<DialogDescription class="sr-only">
{{ t('npcBehavior.trade.title') }}
</DialogDescription>
</DialogHeader>
<div v-if="selectedTradeOffer" class="space-y-4">
<!-- NPC信息卡片 -->
<div class="flex items-center gap-3 p-4 bg-muted/50 rounded-lg">
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<h3 class="font-semibold text-lg">{{ getTradeNpcName(selectedTradeOffer) }}</h3>
<Badge variant="outline" class="bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/30">
{{ t('diplomacy.notificationBadge.trade') }}
</Badge>
</div>
<p class="text-sm text-muted-foreground">
{{ formatRelativeTime((Date.now() - selectedTradeOffer.timestamp) / 1000, t) }}{{ t('diplomacy.ago') }}
</p>
</div>
</div>
<!-- 交易内容 -->
<div class="grid grid-cols-2 gap-4">
<!-- 对方提供 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm flex items-center gap-1.5">
<span class="h-2 w-2 rounded-full bg-green-500 dark:bg-green-400"></span>
{{ t('npcBehavior.trade.offers') }}
</h4>
<div class="p-3 bg-green-500/10 rounded-md">
<div class="flex items-center gap-2">
<ResourceIcon :type="selectedTradeOffer.offeredResources.type" size="md" />
<div>
<div class="text-lg font-bold text-green-600 dark:text-green-400">
<NumberWithTooltip :value="selectedTradeOffer.offeredResources.amount" />
</div>
<div class="text-xs text-muted-foreground">{{ t(`resources.${selectedTradeOffer.offeredResources.type}`) }}</div>
</div>
</div>
</div>
</div>
<!-- 对方要求 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm flex items-center gap-1.5">
<span class="h-2 w-2 rounded-full bg-orange-500 dark:bg-orange-400"></span>
{{ t('npcBehavior.trade.requests') }}
</h4>
<div class="p-3 bg-orange-500/10 rounded-md">
<div class="flex items-center gap-2">
<ResourceIcon :type="selectedTradeOffer.requestedResources.type" size="md" />
<div>
<div class="text-lg font-bold text-orange-600 dark:text-orange-400">
<NumberWithTooltip :value="selectedTradeOffer.requestedResources.amount" />
</div>
<div class="text-xs text-muted-foreground">{{ t(`resources.${selectedTradeOffer.requestedResources.type}`) }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 交易比率和过期时间 -->
<div class="p-3 bg-muted/30 rounded-md space-y-2">
<div class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t('npcBehavior.trade.ratio') }}</span>
<span class="font-medium">{{ getTradeRatioText(selectedTradeOffer) }}</span>
</div>
<div class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t('npcBehavior.trade.expiresIn') }}</span>
<span :class="getTradeExpiresClass(selectedTradeOffer)">{{ getTradeExpiresText(selectedTradeOffer) }}</span>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="detailDialogOpen = false">{{ t('common.close') }}</Button>
<Button @click="goToMessagesFromDialog">{{ t('diplomacy.viewAll') }}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<!-- 情报报告详情对话框 -->
<Dialog :open="detailDialogOpen && selectedNotification?.type === 'intel'" @update:open="detailDialogOpen = $event">
<DialogContent class="max-w-lg">
<DialogHeader>
<DialogTitle class="flex items-center gap-2">
<FileSearch class="h-5 w-5 text-cyan-500" />
{{ t('npcBehavior.intel.title') }}
</DialogTitle>
<DialogDescription class="sr-only">
{{ t('npcBehavior.intel.title') }}
</DialogDescription>
</DialogHeader>
<div v-if="selectedIntelReport" class="space-y-4">
<!-- 来源NPC信息 -->
<div class="flex items-center gap-3 p-4 bg-muted/50 rounded-lg">
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<h3 class="font-semibold text-lg">{{ getIntelFromNpcName(selectedIntelReport) }}</h3>
<Badge variant="secondary" class="bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 border-cyan-500/30">
{{ t('diplomacy.notificationBadge.intel') }}
</Badge>
</div>
<p class="text-sm text-muted-foreground">
{{ formatRelativeTime((Date.now() - selectedIntelReport.timestamp) / 1000, t) }}{{ t('diplomacy.ago') }}
</p>
</div>
</div>
<!-- 情报详情 -->
<div class="grid grid-cols-2 gap-4">
<!-- 目标 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm">{{ t('npcBehavior.intel.target') }}</h4>
<div class="p-3 bg-muted/30 rounded-md">
<div class="font-medium">{{ getIntelTargetNpcName(selectedIntelReport) }}</div>
</div>
</div>
<!-- 情报类型 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm">{{ t('npcBehavior.intel.type') }}</h4>
<div class="p-3 bg-muted/30 rounded-md">
<div class="flex items-center gap-2">
<component
:is="getIntelTypeIcon(selectedIntelReport)"
class="h-4 w-4"
:class="getIntelTypeIconColor(selectedIntelReport)"
/>
<span class="font-medium">{{ getIntelTypeText(selectedIntelReport) }}</span>
</div>
</div>
</div>
</div>
<!-- 情报内容 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm">{{ t('npcBehavior.intel.content') }}</h4>
<div class="p-4 bg-cyan-500/5 border border-cyan-500/20 rounded-md">
<!-- 舰队情报 -->
<div v-if="selectedIntelReport.intelType === 'enemyFleet' && selectedIntelReport.data.fleet" class="space-y-2">
<div v-for="(count, ship) in selectedIntelReport.data.fleet" :key="ship" class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t(`ships.${ship}`) }}</span>
<span class="font-medium">{{ (count as number).toLocaleString() }}</span>
</div>
<div v-if="Object.keys(selectedIntelReport.data.fleet).length === 0" class="text-sm text-muted-foreground text-center">
{{ t('npcBehavior.intel.noFleet') }}
</div>
</div>
<!-- 资源情报 -->
<div v-else-if="selectedIntelReport.intelType === 'enemyResources' && selectedIntelReport.data.resources" class="space-y-2">
<div v-if="selectedIntelReport.data.resources.metal" class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t('resources.metal') }}</span>
<span class="font-medium text-gray-500">{{ selectedIntelReport.data.resources.metal.toLocaleString() }}</span>
</div>
<div v-if="selectedIntelReport.data.resources.crystal" class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t('resources.crystal') }}</span>
<span class="font-medium text-cyan-500">{{ selectedIntelReport.data.resources.crystal.toLocaleString() }}</span>
</div>
<div v-if="selectedIntelReport.data.resources.deuterium" class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t('resources.deuterium') }}</span>
<span class="font-medium text-green-500">{{ selectedIntelReport.data.resources.deuterium.toLocaleString() }}</span>
</div>
</div>
<!-- 移动情报 -->
<div v-else-if="selectedIntelReport.intelType === 'enemyMovement' && selectedIntelReport.data.movement" class="space-y-2">
<div class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t('npcBehavior.intel.targetPosition') }}</span>
<span class="font-mono font-medium">
[{{ selectedIntelReport.data.movement.targetPosition.galaxy }}:{{
selectedIntelReport.data.movement.targetPosition.system
}}:{{ selectedIntelReport.data.movement.targetPosition.position }}]
</span>
</div>
<div class="flex justify-between items-center text-sm">
<span class="text-muted-foreground">{{ t('npcBehavior.intel.missionType') }}</span>
<span class="font-medium">{{ selectedIntelReport.data.movement.missionType }}</span>
</div>
</div>
<!-- 默认 -->
<div v-else class="text-sm text-muted-foreground text-center">
{{ t('npcBehavior.intel.noData') }}
</div>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="detailDialogOpen = false">{{ t('common.close') }}</Button>
<Button @click="goToMessagesFromDialog">{{ t('diplomacy.viewAll') }}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<!-- 联合攻击邀请详情对话框 -->
<Dialog :open="detailDialogOpen && selectedNotification?.type === 'jointAttack'" @update:open="detailDialogOpen = $event">
<DialogContent class="max-w-lg">
<DialogHeader>
<DialogTitle class="flex items-center gap-2">
<Users class="h-5 w-5 text-orange-500" />
{{ t('npcBehavior.jointAttack.title') }}
</DialogTitle>
<DialogDescription class="sr-only">
{{ t('npcBehavior.jointAttack.title') }}
</DialogDescription>
</DialogHeader>
<div v-if="selectedJointAttack" class="space-y-4">
<!-- 邀请方NPC信息 -->
<div class="flex items-center gap-3 p-4 bg-muted/50 rounded-lg">
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<h3 class="font-semibold text-lg">{{ getJointAttackFromNpcName(selectedJointAttack) }}</h3>
<Badge variant="destructive" class="bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/30">
{{ t('diplomacy.notificationBadge.jointAttack') }}
</Badge>
</div>
<p class="text-sm text-muted-foreground">
{{ formatRelativeTime((Date.now() - selectedJointAttack.timestamp) / 1000, t) }}{{ t('diplomacy.ago') }}
</p>
</div>
</div>
<!-- 攻击目标信息 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm flex items-center gap-1.5">
<Sword class="h-4 w-4 text-red-500" />
{{ t('npcBehavior.jointAttack.targetInfo') }}
</h4>
<div class="p-4 bg-red-500/5 border border-red-500/20 rounded-md space-y-3">
<div class="flex justify-between items-center">
<span class="text-sm text-muted-foreground">{{ t('npcBehavior.jointAttack.target') }}</span>
<span class="font-medium">{{ getJointAttackTargetNpcName(selectedJointAttack) }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-muted-foreground">{{ t('npcBehavior.jointAttack.targetPlanet') }}</span>
<span class="font-mono font-medium">{{ getJointAttackTargetPlanet(selectedJointAttack) }}</span>
</div>
</div>
</div>
<!-- 收益和时限 -->
<div class="grid grid-cols-2 gap-4">
<!-- 战利品分成 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm">{{ t('npcBehavior.jointAttack.lootShare') }}</h4>
<div class="p-3 bg-orange-500/10 border border-orange-500/20 rounded-md text-center">
<div class="text-2xl font-bold text-orange-600 dark:text-orange-400">
{{ Math.round(selectedJointAttack.expectedLootRatio * 100) }}%
</div>
<div class="text-xs text-muted-foreground mt-1">{{ t('npcBehavior.jointAttack.expectedShare') }}</div>
</div>
</div>
<!-- 过期时间 -->
<div class="space-y-2">
<h4 class="font-semibold text-sm">{{ t('npcBehavior.jointAttack.expiresIn') }}</h4>
<div class="p-3 bg-muted/30 rounded-md text-center">
<div class="text-2xl font-bold" :class="getJointAttackExpiresClass(selectedJointAttack)">
{{ getJointAttackExpiresText(selectedJointAttack) }}
</div>
<div class="text-xs text-muted-foreground mt-1">{{ t('npcBehavior.jointAttack.remaining') }}</div>
</div>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="detailDialogOpen = false">{{ t('common.close') }}</Button>
<Button @click="goToMessagesFromDialog">{{ t('diplomacy.viewAll') }}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, onUnmounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useGameStore } from '@/stores/gameStore'
import { useNPCStore } from '@/stores/npcStore'
@@ -202,11 +492,24 @@
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog'
import { ScrollArea } from '@/components/ui/scroll-area'
import { ScrollText, Gift, Sword, Eye, Trash2, Skull } from 'lucide-vue-next'
import { ScrollText, Gift, Sword, Eye, Trash2, Skull, ArrowLeftRight, FileSearch, Users, Ship, Coins, Navigation } from 'lucide-vue-next'
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
import { RelationStatus, DiplomaticEventType } from '@/types/game'
import type { DiplomaticReport } from '@/types/game'
import type { DiplomaticReport, TradeOffer, IntelReport, JointAttackInvite } from '@/types/game'
import { formatRelativeTime } from '@/utils/format'
import NumberWithTooltip from '@/components/common/NumberWithTooltip.vue'
import ResourceIcon from '@/components/common/ResourceIcon.vue'
// 统一通知类型
type NotificationType = 'diplomatic' | 'trade' | 'intel' | 'jointAttack'
interface UnifiedNotification {
id: string
type: NotificationType
timestamp: number
read: boolean
data: DiplomaticReport | TradeOffer | IntelReport | JointAttackInvite
}
const router = useRouter()
const gameStore = useGameStore()
@@ -214,35 +517,195 @@
const { t } = useI18n()
const isOpen = ref(false)
const detailDialogOpen = ref(false)
const selectedReport = ref<DiplomaticReport | null>(null)
const selectedNotification = ref<UnifiedNotification | null>(null)
const reports = computed(() => {
return (gameStore.player.diplomaticReports || []).slice().reverse().slice(0, 20) // 最近20条
// 倒计时相关
const currentTime = ref(Date.now())
let countdownTimer: ReturnType<typeof setInterval> | null = null
// 启动倒计时定时器
const startCountdownTimer = () => {
if (countdownTimer) return
countdownTimer = setInterval(() => {
currentTime.value = Date.now()
}, 1000)
}
// 停止倒计时定时器
const stopCountdownTimer = () => {
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
}
// 监听对话框打开状态
watch(detailDialogOpen, open => {
if (open) {
startCountdownTimer()
} else {
stopCountdownTimer()
}
})
// 组件卸载时清理
onUnmounted(() => {
stopCountdownTimer()
})
// 格式化倒计时(毫秒 -> HH:MM:SS 或 MM:SS
const formatCountdown = (ms: number): string => {
if (ms <= 0) return '00:00'
const totalSeconds = Math.floor(ms / 1000)
const hours = Math.floor(totalSeconds / 3600)
const minutes = Math.floor((totalSeconds % 3600) / 60)
const seconds = totalSeconds % 60
if (hours > 0) {
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
}
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
}
// 选中的各类型数据(计算属性)
const selectedReport = computed(() => {
if (selectedNotification.value?.type === 'diplomatic') {
return selectedNotification.value.data as DiplomaticReport
}
return null
})
const selectedTradeOffer = computed(() => {
if (selectedNotification.value?.type === 'trade') {
return selectedNotification.value.data as TradeOffer
}
return null
})
const selectedIntelReport = computed(() => {
if (selectedNotification.value?.type === 'intel') {
return selectedNotification.value.data as IntelReport
}
return null
})
const selectedJointAttack = computed(() => {
if (selectedNotification.value?.type === 'jointAttack') {
return selectedNotification.value.data as JointAttackInvite
}
return null
})
// 统一所有通知到一个列表
const allNotifications = computed((): UnifiedNotification[] => {
const notifications: UnifiedNotification[] = []
// 外交报告
;(gameStore.player.diplomaticReports || []).forEach(report => {
notifications.push({
id: report.id,
type: 'diplomatic',
timestamp: report.timestamp,
read: report.read ?? false,
data: report
})
})
// 贸易提议只显示pending状态的
;(gameStore.player.tradeOffers || [])
.filter(offer => offer.status === 'pending')
.forEach(offer => {
notifications.push({
id: offer.id,
type: 'trade',
timestamp: offer.timestamp,
read: offer.read || false,
data: offer
})
})
// 情报报告
;(gameStore.player.intelReports || []).forEach(report => {
notifications.push({
id: report.id,
type: 'intel',
timestamp: report.timestamp,
read: report.read,
data: report
})
})
// 联合攻击邀请只显示pending状态的
;(gameStore.player.jointAttackInvites || [])
.filter(invite => invite.status === 'pending')
.forEach(invite => {
notifications.push({
id: invite.id,
type: 'jointAttack',
timestamp: invite.timestamp,
read: invite.read || false,
data: invite
})
})
// 按时间倒序排序取最近30条
return notifications.sort((a, b) => b.timestamp - a.timestamp).slice(0, 30)
})
const unreadCount = computed(() => {
return allNotifications.value.filter(n => !n.read).length
})
/**
* 获取NPC当前名称
* 优先使用当前NPC的实际名称如果NPC不存在则尝试从旧名称中提取ID查找
* 获取NPC当前名称(通用)
*/
const getNpcName = (report: DiplomaticReport): string => {
if (!npcStore.npcs?.length) return report.npcName
const getNpcNameById = (npcId: string, fallbackName: string): string => {
if (!npcStore.npcs?.length) return fallbackName
// 1. 先通过 npcId 查找
if (report.npcId) {
const npc = npcStore.npcs.find(n => n.id === report.npcId)
if (npc) return npc.name
}
const npc = npcStore.npcs.find(n => n.id === npcId)
if (npc) return npc.name
// 2. 尝试从旧名称中提取ID并查找
// 旧格式如 "NPC-npc_182"新ID格式为 "npc_182"
const idMatch = report.npcName.match(/npc_\d+/)
// 尝试从旧名称中提取ID并查找
const idMatch = fallbackName.match(/npc_\d+/)
if (idMatch) {
const extractedId = idMatch[0]
const npc = npcStore.npcs.find(n => n.id === extractedId)
if (npc) return npc.name
const foundNpc = npcStore.npcs.find(n => n.id === extractedId)
if (foundNpc) return foundNpc.name
}
return report.npcName
return fallbackName
}
/**
* 获取NPC当前名称外交报告专用
*/
const getNpcName = (report: DiplomaticReport): string => {
return getNpcNameById(report.npcId || '', report.npcName)
}
/**
* 获取通知的NPC名称
*/
const getNotificationNpcName = (notification: UnifiedNotification): string => {
switch (notification.type) {
case 'diplomatic': {
const report = notification.data as DiplomaticReport
return getNpcNameById(report.npcId || '', report.npcName)
}
case 'trade': {
const offer = notification.data as TradeOffer
return getNpcNameById(offer.npcId, offer.npcName)
}
case 'intel': {
const report = notification.data as IntelReport
return getNpcNameById(report.fromNpcId, report.fromNpcName)
}
case 'jointAttack': {
const invite = notification.data as JointAttackInvite
return getNpcNameById(invite.fromNpcId, invite.fromNpcName)
}
default:
return ''
}
}
/**
@@ -256,9 +719,145 @@
}
}
const unreadCount = computed(() => {
return (gameStore.player.diplomaticReports || []).filter(r => !r.read).length
})
/**
* 获取通知图标
*/
const getNotificationIcon = (notification: UnifiedNotification) => {
switch (notification.type) {
case 'trade':
return ArrowLeftRight
case 'intel':
return FileSearch
case 'jointAttack':
return Users
case 'diplomatic': {
const report = notification.data as DiplomaticReport
return getEventIcon(report.eventType)
}
default:
return ScrollText
}
}
/**
* 获取通知图标颜色
*/
const getNotificationIconColor = (notification: UnifiedNotification) => {
switch (notification.type) {
case 'trade':
return 'text-blue-500'
case 'intel':
return 'text-cyan-500'
case 'jointAttack':
return 'text-orange-500'
case 'diplomatic': {
const report = notification.data as DiplomaticReport
return getEventIconColor(report.eventType)
}
default:
return 'text-muted-foreground'
}
}
/**
* 获取通知标题
*/
const getNotificationTitle = (notification: UnifiedNotification): string => {
switch (notification.type) {
case 'trade':
return t('diplomacy.notificationType.tradeOffer')
case 'intel':
return t('diplomacy.notificationType.intelReport')
case 'jointAttack':
return t('diplomacy.notificationType.jointAttack')
case 'diplomatic': {
const report = notification.data as DiplomaticReport
return getEventTypeText(report.eventType)
}
default:
return ''
}
}
/**
* 获取通知徽章变体
*/
const getNotificationBadgeVariant = (notification: UnifiedNotification) => {
switch (notification.type) {
case 'trade':
return 'outline' as const
case 'intel':
return 'secondary' as const
case 'jointAttack':
return 'destructive' as const
case 'diplomatic': {
const report = notification.data as DiplomaticReport
return report.newStatus ? getStatusBadgeVariant(report.newStatus) : ('secondary' as const)
}
default:
return 'secondary' as const
}
}
/**
* 获取通知徽章文字
*/
const getNotificationBadgeText = (notification: UnifiedNotification): string => {
switch (notification.type) {
case 'trade':
return t('diplomacy.notificationBadge.trade')
case 'intel':
return t('diplomacy.notificationBadge.intel')
case 'jointAttack':
return t('diplomacy.notificationBadge.jointAttack')
case 'diplomatic': {
const report = notification.data as DiplomaticReport
return report.newStatus ? getStatusText(report.newStatus) : t('diplomacy.notificationBadge.diplomatic')
}
default:
return ''
}
}
/**
* 获取通知的额外信息(右侧显示)
*/
const getNotificationExtra = (notification: UnifiedNotification): { text: string; colorClass: string } | null => {
switch (notification.type) {
case 'diplomatic': {
const report = notification.data as DiplomaticReport
if (report.reputationChange === undefined) return null
return {
text: `${(report.reputationChange || 0) >= 0 ? '+' : ''}${report.reputationChange || 0}`,
colorClass: (report.reputationChange || 0) >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'
}
}
case 'trade': {
const offer = notification.data as TradeOffer
const isExpired = offer.expiresAt <= Date.now()
return {
text: isExpired ? t('npcBehavior.trade.expired') : t('diplomacy.notificationExtra.pending'),
colorClass: isExpired ? 'text-red-600 dark:text-red-400' : 'text-blue-600 dark:text-blue-400'
}
}
case 'jointAttack': {
const invite = notification.data as JointAttackInvite
const isExpired = invite.expiresAt <= Date.now()
if (isExpired) {
return {
text: t('npcBehavior.jointAttack.expired'),
colorClass: 'text-red-600 dark:text-red-400'
}
}
return {
text: `${Math.round(invite.expectedLootRatio * 100)}%`,
colorClass: 'text-orange-600 dark:text-orange-400'
}
}
default:
return null
}
}
const getEventIcon = (eventType: DiplomaticReport['eventType']) => {
switch (eventType) {
@@ -346,21 +945,63 @@
return 'text-muted-foreground'
}
const handleReportClick = (report: DiplomaticReport) => {
/**
* 处理通知点击 - 标记为已读并打开详情弹窗
*/
const handleNotificationClick = (notification: UnifiedNotification) => {
// 标记为已读
report.read = true
// 设置选中的报告
selectedReport.value = report
// 关闭通知面板
isOpen.value = true
markNotificationAsRead(notification)
// 设置选中的通知
selectedNotification.value = notification
// 打开对话框
detailDialogOpen.value = true
}
/**
* 标记单个通知为已读
*/
const markNotificationAsRead = (notification: UnifiedNotification) => {
switch (notification.type) {
case 'diplomatic': {
const report = notification.data as DiplomaticReport
report.read = true
break
}
case 'trade': {
const offer = notification.data as TradeOffer
offer.read = true
break
}
case 'intel': {
const report = notification.data as IntelReport
report.read = true
break
}
case 'jointAttack': {
const invite = notification.data as JointAttackInvite
invite.read = true
break
}
}
}
const markAllAsRead = () => {
// 标记所有外交报告
gameStore.player.diplomaticReports?.forEach(report => {
report.read = true
})
// 标记所有贸易提议
gameStore.player.tradeOffers?.forEach(offer => {
offer.read = true
})
// 标记所有情报报告
gameStore.player.intelReports?.forEach(report => {
report.read = true
})
// 标记所有联合攻击邀请
gameStore.player.jointAttackInvites?.forEach(invite => {
invite.read = true
})
}
const goToDiplomacy = () => {
@@ -373,4 +1014,108 @@
detailDialogOpen.value = false
router.push(npcId ? `/diplomacy?npcId=${npcId}` : '/diplomacy')
}
const goToMessagesFromDialog = () => {
detailDialogOpen.value = false
isOpen.value = false
router.push('/messages')
}
// ========== 贸易提议详情方法 ==========
const getTradeNpcName = (offer: TradeOffer): string => {
return getNpcNameById(offer.npcId, offer.npcName)
}
const getTradeExpiresText = (offer: TradeOffer): string => {
const remaining = offer.expiresAt - currentTime.value
if (remaining <= 0) return t('npcBehavior.trade.expired')
return formatCountdown(remaining)
}
const getTradeExpiresClass = (offer: TradeOffer): string => {
const remaining = offer.expiresAt - currentTime.value
if (remaining <= 0) return 'text-red-600 dark:text-red-400'
if (remaining < 3600000) return 'text-orange-600 dark:text-orange-400' // < 1小时
return 'text-muted-foreground'
}
const getTradeRatioText = (offer: TradeOffer): string => {
const ratio = offer.offeredResources.amount / offer.requestedResources.amount
return `1:${ratio.toFixed(2)}`
}
// ========== 情报报告详情方法 ==========
const getIntelFromNpcName = (report: IntelReport): string => {
return getNpcNameById(report.fromNpcId, report.fromNpcName)
}
const getIntelTargetNpcName = (report: IntelReport): string => {
return getNpcNameById(report.targetNpcId, report.targetNpcName)
}
const getIntelTypeText = (report: IntelReport): string => {
switch (report.intelType) {
case 'enemyFleet':
return t('npcBehavior.intel.types.enemyFleet')
case 'enemyResources':
return t('npcBehavior.intel.types.enemyResources')
case 'enemyMovement':
return t('npcBehavior.intel.types.enemyMovement')
default:
return report.intelType
}
}
const getIntelTypeIcon = (report: IntelReport) => {
switch (report.intelType) {
case 'enemyFleet':
return Ship
case 'enemyResources':
return Coins
case 'enemyMovement':
return Navigation
default:
return FileSearch
}
}
const getIntelTypeIconColor = (report: IntelReport): string => {
switch (report.intelType) {
case 'enemyFleet':
return 'text-red-500'
case 'enemyResources':
return 'text-yellow-500'
case 'enemyMovement':
return 'text-blue-500'
default:
return 'text-cyan-500'
}
}
// ========== 联合攻击邀请详情方法 ==========
const getJointAttackFromNpcName = (invite: JointAttackInvite): string => {
return getNpcNameById(invite.fromNpcId, invite.fromNpcName)
}
const getJointAttackTargetNpcName = (invite: JointAttackInvite): string => {
return getNpcNameById(invite.targetNpcId, invite.targetNpcName)
}
const getJointAttackTargetPlanet = (invite: JointAttackInvite): string => {
const pos = invite.targetPosition
return `[${pos.galaxy}:${pos.system}:${pos.position}]`
}
const getJointAttackExpiresText = (invite: JointAttackInvite): string => {
const remaining = invite.expiresAt - currentTime.value
if (remaining <= 0) return t('npcBehavior.jointAttack.expired')
return formatCountdown(remaining)
}
const getJointAttackExpiresClass = (invite: JointAttackInvite): string => {
const remaining = invite.expiresAt - currentTime.value
if (remaining <= 0) return 'text-red-600 dark:text-red-400'
if (remaining < 3600000) return 'text-orange-600 dark:text-orange-400' // < 1小时
return 'text-muted-foreground'
}
</script>

View File

@@ -308,10 +308,10 @@ import * as resourceLogic from '@/logic/resourceLogic'
const getStatusDotClass = (item: BuildQueueItem | WaitingQueueItem): string => {
// 等待队列项根据资源是否足够显示不同颜色
if (isWaitingItem(item)) {
return isWaitingItemResourcesReady(item) ? 'bg-green-500' : 'bg-yellow-500'
return isWaitingItemResourcesReady(item) ? 'bg-green-500 dark:bg-green-400' : 'bg-yellow-500 dark:bg-yellow-400'
}
if (item.type === 'demolish') return 'bg-destructive'
if (item.type === 'technology') return 'bg-blue-500'
return 'bg-green-500'
if (item.type === 'technology') return 'bg-blue-500 dark:bg-blue-400'
return 'bg-green-500 dark:bg-green-400'
}
</script>

View File

@@ -7,9 +7,9 @@
<div
class="w-2 h-2 rounded-full shrink-0"
:class="{
'bg-green-500': status === RelationStatus.Friendly,
'bg-red-500': status === RelationStatus.Hostile,
'bg-gray-400': status === RelationStatus.Neutral
'bg-green-500 dark:bg-green-400': status === RelationStatus.Friendly,
'bg-red-500 dark:bg-red-400': status === RelationStatus.Hostile,
'bg-gray-400 dark:bg-gray-500': status === RelationStatus.Neutral
}"
/>
@@ -37,8 +37,8 @@
<!-- 好感度 -->
<div class="flex items-center gap-2 shrink-0">
<div class="w-16 h-1.5 bg-muted rounded-full overflow-hidden relative">
<div v-if="reputation < 0" class="h-full bg-red-500 absolute right-1/2" :style="{ width: `${Math.abs(reputation) / 2}%` }" />
<div v-if="reputation > 0" class="h-full bg-green-500 absolute left-1/2" :style="{ width: `${reputation / 2}%` }" />
<div v-if="reputation < 0" class="h-full bg-red-500 dark:bg-red-400 absolute right-1/2" :style="{ width: `${Math.abs(reputation) / 2}%` }" />
<div v-if="reputation > 0" class="h-full bg-green-500 dark:bg-green-400 absolute left-1/2" :style="{ width: `${reputation / 2}%` }" />
<div class="absolute left-1/2 top-0 bottom-0 w-px bg-border" />
</div>
<span class="text-sm font-medium w-10 text-right" :class="reputationColor">{{ reputation > 0 ? '+' : '' }}{{ reputation }}</span>
@@ -72,9 +72,9 @@
<div
class="w-2 h-2 rounded-full shrink-0"
:class="{
'bg-green-500': status === RelationStatus.Friendly,
'bg-red-500': status === RelationStatus.Hostile,
'bg-gray-400': status === RelationStatus.Neutral
'bg-green-500 dark:bg-green-400': status === RelationStatus.Friendly,
'bg-red-500 dark:bg-red-400': status === RelationStatus.Hostile,
'bg-gray-400 dark:bg-gray-500': status === RelationStatus.Neutral
}"
/>
<div class="flex-1 min-w-0 flex items-center gap-1 flex-wrap">

View File

@@ -0,0 +1,180 @@
<template>
<Dialog v-model:open="isOpen">
<DialogContent class="max-w-md">
<DialogHeader>
<DialogTitle>{{ t('settings.webdav.configTitle') }}</DialogTitle>
<DialogDescription>
{{ t('settings.webdav.configDesc') }}
</DialogDescription>
</DialogHeader>
<div class="space-y-4 py-4">
<!-- 服务器地址 -->
<div class="space-y-2">
<Label for="serverUrl">{{ t('settings.webdav.serverUrl') }}</Label>
<Input
id="serverUrl"
v-model="config.serverUrl"
:placeholder="t('settings.webdav.serverUrlPlaceholder')"
/>
<p class="text-xs text-muted-foreground">
{{ t('settings.webdav.serverUrlHint') }}
</p>
</div>
<!-- 用户名 -->
<div class="space-y-2">
<Label for="username">{{ t('settings.webdav.username') }}</Label>
<Input
id="username"
v-model="config.username"
:placeholder="t('settings.webdav.usernamePlaceholder')"
/>
</div>
<!-- 密码 -->
<div class="space-y-2">
<Label for="password">{{ t('settings.webdav.password') }}</Label>
<Input
id="password"
v-model="config.password"
type="password"
:placeholder="t('settings.webdav.passwordPlaceholder')"
/>
<p class="text-xs text-muted-foreground">
{{ t('settings.webdav.passwordHint') }}
</p>
</div>
<!-- 存档路径 -->
<div class="space-y-2">
<Label for="basePath">{{ t('settings.webdav.basePath') }}</Label>
<Input
id="basePath"
v-model="config.basePath"
:placeholder="t('settings.webdav.basePathPlaceholder')"
/>
</div>
<!-- 测试结果 -->
<div v-if="testResult" :class="['p-3 rounded-md text-sm', testResult.success ? 'bg-green-500/10 text-green-500' : 'bg-red-500/10 text-red-500']">
{{ testResult.message }}
</div>
</div>
<DialogFooter class="flex gap-2">
<Button variant="outline" @click="handleTest" :disabled="isTesting || !isConfigValid">
<Loader2 v-if="isTesting" class="mr-2 h-4 w-4 animate-spin" />
{{ t('settings.webdav.testConnection') }}
</Button>
<Button variant="destructive" v-if="hasExistingConfig" @click="handleClear">
{{ t('settings.webdav.clearConfig') }}
</Button>
<Button @click="handleSave" :disabled="!isConfigValid">
{{ t('common.save') }}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { useI18n } from '@/composables/useI18n'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Loader2 } from 'lucide-vue-next'
import {
type WebDAVConfig,
getWebDAVConfig,
saveWebDAVConfig,
clearWebDAVConfig,
testWebDAVConnection
} from '@/services/webdavService'
const { t } = useI18n()
const isOpen = defineModel<boolean>('open', { default: false })
const emit = defineEmits<{
saved: [config: WebDAVConfig]
cleared: []
}>()
const config = ref<WebDAVConfig>({
serverUrl: '',
username: '',
password: '',
basePath: '/ogame-saves/'
})
const isTesting = ref(false)
const testResult = ref<{ success: boolean; message: string } | null>(null)
const hasExistingConfig = ref(false)
const isConfigValid = computed(() => {
return config.value.serverUrl.trim() !== '' &&
config.value.username.trim() !== '' &&
config.value.password.trim() !== ''
})
// 加载已保存的配置
watch(isOpen, (open) => {
if (open) {
const saved = getWebDAVConfig()
if (saved) {
config.value = { ...saved }
hasExistingConfig.value = true
} else {
config.value = {
serverUrl: '',
username: '',
password: '',
basePath: '/ogame-saves/'
}
hasExistingConfig.value = false
}
testResult.value = null
}
})
const handleTest = async () => {
isTesting.value = true
testResult.value = null
try {
testResult.value = await testWebDAVConnection(config.value)
} finally {
isTesting.value = false
}
}
const handleSave = () => {
saveWebDAVConfig(config.value)
hasExistingConfig.value = true
emit('saved', config.value)
isOpen.value = false
}
const handleClear = () => {
clearWebDAVConfig()
hasExistingConfig.value = false
config.value = {
serverUrl: '',
username: '',
password: '',
basePath: '/ogame-saves/'
}
testResult.value = null
emit('cleared')
}
</script>

View File

@@ -0,0 +1,180 @@
<template>
<Dialog v-model:open="isOpen">
<DialogContent class="max-w-lg">
<DialogHeader>
<DialogTitle>{{ t('settings.webdav.selectFile') }}</DialogTitle>
<DialogDescription>
{{ t('settings.webdav.selectFileDesc') }}
</DialogDescription>
</DialogHeader>
<div class="py-4">
<!-- 加载状态 -->
<div v-if="isLoading" class="flex items-center justify-center py-8">
<Loader2 class="h-8 w-8 animate-spin text-muted-foreground" />
</div>
<!-- 错误状态 -->
<div v-else-if="error" class="text-center py-8">
<p class="text-red-500 mb-4">{{ error }}</p>
<Button variant="outline" @click="loadFiles">
{{ t('common.retry') }}
</Button>
</div>
<!-- 空列表 -->
<div v-else-if="files.length === 0" class="text-center py-8 text-muted-foreground">
{{ t('settings.webdav.noFiles') }}
</div>
<!-- 文件列表 -->
<ScrollArea v-else class="h-[300px]">
<div class="space-y-2">
<div
v-for="file in files"
:key="file.name"
:class="[
'flex items-center justify-between p-3 rounded-md cursor-pointer transition-colors',
selectedFile === file.name ? 'bg-primary/10 border border-primary' : 'hover:bg-muted'
]"
@click="selectedFile = file.name"
>
<div class="flex-1 min-w-0">
<p class="font-medium truncate">{{ file.name }}</p>
<p class="text-xs text-muted-foreground">
{{ formatFileSize(file.size) }} · {{ formatDate(file.lastModified) }}
</p>
</div>
<Button
variant="ghost"
size="icon"
class="shrink-0 ml-2"
@click.stop="handleDelete(file.name)"
>
<Trash2 class="h-4 w-4 text-red-500" />
</Button>
</div>
</div>
</ScrollArea>
</div>
<DialogFooter>
<Button variant="outline" @click="isOpen = false">
{{ t('common.cancel') }}
</Button>
<Button @click="handleSelect" :disabled="!selectedFile">
{{ t('settings.webdav.download') }}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useI18n } from '@/composables/useI18n'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Loader2, Trash2 } from 'lucide-vue-next'
import {
type WebDAVConfig,
type WebDAVFile,
listWebDAVFiles,
deleteFromWebDAV
} from '@/services/webdavService'
import { toast } from 'vue-sonner'
const { t } = useI18n()
const props = defineProps<{
config: WebDAVConfig | null
}>()
const isOpen = defineModel<boolean>('open', { default: false })
const emit = defineEmits<{
select: [fileName: string]
}>()
const files = ref<WebDAVFile[]>([])
const isLoading = ref(false)
const error = ref<string | null>(null)
const selectedFile = ref<string | null>(null)
const loadFiles = async () => {
if (!props.config) return
isLoading.value = true
error.value = null
selectedFile.value = null
try {
const result = await listWebDAVFiles(props.config)
if (result.success && result.files) {
files.value = result.files
} else {
error.value = result.message || t('settings.webdav.loadFailed')
}
} catch (e) {
error.value = e instanceof Error ? e.message : String(e)
} finally {
isLoading.value = false
}
}
watch(isOpen, (open) => {
if (open && props.config) {
loadFiles()
}
})
const formatFileSize = (bytes: number): string => {
if (bytes < 1024) return `${bytes} B`
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
const formatDate = (date: Date): string => {
return new Intl.DateTimeFormat(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).format(date)
}
const handleSelect = () => {
if (selectedFile.value) {
emit('select', selectedFile.value)
isOpen.value = false
}
}
const handleDelete = async (fileName: string) => {
if (!props.config) return
if (!confirm(t('settings.webdav.confirmDelete', { name: fileName }))) {
return
}
const result = await deleteFromWebDAV(props.config, fileName)
if (result.success) {
toast.success(t('settings.webdav.deleteSuccess'))
files.value = files.value.filter(f => f.name !== fileName)
if (selectedFile.value === fileName) {
selectedFile.value = null
}
} else {
toast.error(result.message || t('settings.webdav.deleteFailed'))
}
}
</script>

View File

@@ -94,7 +94,7 @@
// For slot content
defineSlots()
function generateStars(count: number, starColor: string) {
const generateStars = (count: number, starColor: string) => {
const shadows: string[] = []
for (let i = 0; i < count; i++) {
const x = Math.floor(Math.random() * 4000) - 2000
@@ -110,7 +110,7 @@
const springX = useSpring(offsetX, props.transition)
const springY = useSpring(offsetY, props.transition)
function handleMouseMove(e: MouseEvent) {
const handleMouseMove = (e: MouseEvent) => {
const centerX = window.innerWidth / 2
const centerY = window.innerHeight / 2
const newOffsetX = -(e.clientX - centerX) * props.factor

View File

@@ -0,0 +1,15 @@
<template>
<CollapsibleRoot v-slot="slotProps" data-slot="collapsible" v-bind="forwarded">
<slot v-bind="slotProps" />
</CollapsibleRoot>
</template>
<script setup lang="ts">
import type { CollapsibleRootEmits, CollapsibleRootProps } from 'reka-ui'
import { CollapsibleRoot, useForwardPropsEmits } from 'reka-ui'
const props = defineProps<CollapsibleRootProps>()
const emits = defineEmits<CollapsibleRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>

View File

@@ -0,0 +1,12 @@
<template>
<CollapsibleContent data-slot="collapsible-content" v-bind="props">
<slot />
</CollapsibleContent>
</template>
<script setup lang="ts">
import type { CollapsibleContentProps } from 'reka-ui'
import { CollapsibleContent } from 'reka-ui'
const props = defineProps<CollapsibleContentProps>()
</script>

View File

@@ -0,0 +1,12 @@
<template>
<CollapsibleTrigger data-slot="collapsible-trigger" v-bind="props">
<slot />
</CollapsibleTrigger>
</template>
<script setup lang="ts">
import type { CollapsibleTriggerProps } from 'reka-ui'
import { CollapsibleTrigger } from 'reka-ui'
const props = defineProps<CollapsibleTriggerProps>()
</script>

View File

@@ -0,0 +1,3 @@
export { default as Collapsible } from './Collapsible.vue'
export { default as CollapsibleContent } from './CollapsibleContent.vue'
export { default as CollapsibleTrigger } from './CollapsibleTrigger.vue'

View File

@@ -68,45 +68,7 @@
return `${r} ${g} ${b}`
})
onMounted(() => {
if (canvasRef.value) {
context.value = canvasRef.value.getContext('2d')
}
initCanvas()
animate()
window.addEventListener('resize', initCanvas)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', initCanvas)
})
watch([mouseX, mouseY], () => {
onMouseMove()
})
function initCanvas() {
resizeCanvas()
drawParticles()
}
function onMouseMove() {
if (canvasRef.value) {
const rect = canvasRef.value.getBoundingClientRect()
const { w, h } = canvasSize
const x = mouseX.value - rect.left - w / 2
const y = mouseY.value - rect.top - h / 2
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2
if (inside) {
mouse.x = x
mouse.y = y
}
}
}
function resizeCanvas() {
const resizeCanvas = () => {
if (canvasContainerRef.value && canvasRef.value && context.value) {
circles.value.length = 0
canvasSize.w = canvasContainerRef.value.offsetWidth
@@ -119,7 +81,7 @@
}
}
function circleParams(): Circle {
const circleParams = (): Circle => {
const x = Math.floor(Math.random() * canvasSize.w)
const y = Math.floor(Math.random() * canvasSize.h)
const translateX = 0
@@ -144,7 +106,7 @@
}
}
function drawCircle(circle: Circle, update = false) {
const drawCircle = (circle: Circle, update = false) => {
if (context.value) {
const { x, y, translateX, translateY, size, alpha } = circle
context.value.translate(translateX, translateY)
@@ -160,13 +122,13 @@
}
}
function clearContext() {
const clearContext = () => {
if (context.value) {
context.value.clearRect(0, 0, canvasSize.w, canvasSize.h)
}
}
function drawParticles() {
const drawParticles = () => {
clearContext()
const particleCount = props.quantity
for (let i = 0; i < particleCount; i++) {
@@ -175,12 +137,32 @@
}
}
function remapValue(value: number, start1: number, end1: number, start2: number, end2: number): number {
const initCanvas = () => {
resizeCanvas()
drawParticles()
}
const onMouseMove = () => {
if (canvasRef.value) {
const rect = canvasRef.value.getBoundingClientRect()
const { w, h } = canvasSize
const x = mouseX.value - rect.left - w / 2
const y = mouseY.value - rect.top - h / 2
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2
if (inside) {
mouse.x = x
mouse.y = y
}
}
}
const remapValue = (value: number, start1: number, end1: number, start2: number, end2: number): number => {
const remapped = ((value - start1) * (end2 - start2)) / (end1 - start1) + start2
return remapped > 0 ? remapped : 0
}
function animate() {
const animate = () => {
clearContext()
circles.value.forEach((circle, i) => {
// Handle the alpha value
@@ -235,4 +217,22 @@
})
window.requestAnimationFrame(animate)
}
onMounted(() => {
if (canvasRef.value) {
context.value = canvasRef.value.getContext('2d')
}
initCanvas()
animate()
window.addEventListener('resize', initCanvas)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', initCanvas)
})
watch([mouseX, mouseY], () => {
onMouseMove()
})
</script>

View File

@@ -41,7 +41,9 @@ export const useGameConfig = () => {
[BuildingType.LunarBase]: 'lunarBase',
[BuildingType.SensorPhalanx]: 'sensorPhalanx',
[BuildingType.JumpGate]: 'jumpGate',
[BuildingType.PlanetDestroyerFactory]: 'planetDestroyerFactory'
[BuildingType.PlanetDestroyerFactory]: 'planetDestroyerFactory',
[BuildingType.GeoResearchStation]: 'geoResearchStation',
[BuildingType.DeepDrillingFacility]: 'deepDrillingFacility'
}
// 舰船类型枚举值到翻译键的映射
@@ -97,7 +99,8 @@ export const useGameConfig = () => {
[TechnologyType.HyperspaceDrive]: 'hyperspaceDrive',
[TechnologyType.DarkMatterTechnology]: 'darkMatterTechnology',
[TechnologyType.TerraformingTechnology]: 'terraformingTechnology',
[TechnologyType.PlanetDestructionTech]: 'planetDestructionTech'
[TechnologyType.PlanetDestructionTech]: 'planetDestructionTech',
[TechnologyType.MiningTechnology]: 'miningTechnology'
}
// 军官类型枚举值到翻译键的映射

View File

@@ -106,6 +106,14 @@ const hints: Hint[] = [
icon: 'swords',
delay: 500
},
{
id: 'campaign_intro',
route: '/campaign',
titleKey: 'hints.campaign.title',
messageKey: 'hints.campaign.message',
icon: 'map',
delay: 500
},
{
id: 'achievements_intro',
route: '/achievements',
@@ -114,6 +122,14 @@ const hints: Hint[] = [
icon: 'trophy',
delay: 500
},
{
id: 'ranking_intro',
route: '/ranking',
titleKey: 'hints.ranking.title',
messageKey: 'hints.ranking.message',
icon: 'medal',
delay: 500
},
{
id: 'settings_intro',
route: '/settings',
@@ -138,7 +154,7 @@ const isHintVisible = ref(false)
let hintTimeout: ReturnType<typeof setTimeout> | null = null
export function useHints() {
export const useHints = () => {
const router = useRouter()
const gameStore = useGameStore()

View File

@@ -1,22 +1,51 @@
import { computed } from 'vue'
import { ref, computed, watch } from 'vue'
import { useGameStore } from '@/stores/gameStore'
import { locales, type Locale } from '@/locales'
import { loadLocale, getLocale, isLocaleLoaded, type Locale, type TranslationSchema } from '@/locales'
// 全局加载状态
const isLoading = ref(false)
const currentMessages = ref<TranslationSchema | null>(null)
export const useI18n = () => {
const gameStore = useGameStore()
const currentLocale = computed(() => gameStore.locale)
const messages = computed(() => locales[currentLocale.value])
// 初始化或切换语言时加载
const ensureLocaleLoaded = async (locale: Locale) => {
if (isLocaleLoaded(locale)) {
currentMessages.value = getLocale(locale)
return
}
isLoading.value = true
try {
currentMessages.value = await loadLocale(locale)
} finally {
isLoading.value = false
}
}
// 监听语言变化
watch(
currentLocale,
async newLocale => {
await ensureLocaleLoaded(newLocale)
},
{ immediate: true }
)
// 获取当前消息(同步,使用缓存或默认语言)
const messages = computed(() => currentMessages.value || getLocale(currentLocale.value))
// 获取翻译文本的辅助函数
const t = (key: string, params?: Record<string, string | number>): string => {
const keys = key.split('.')
let value: any = messages.value
let value: unknown = messages.value
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k]
value = (value as Record<string, unknown>)[k]
} else {
return key // 如果找不到翻译,返回原始 key
}
@@ -34,7 +63,10 @@ export const useI18n = () => {
return result
}
const setLocale = (locale: Locale) => {
const setLocale = async (locale: Locale) => {
// 预加载语言文件
await ensureLocaleLoaded(locale)
// 然后切换
gameStore.locale = locale
}
@@ -42,6 +74,7 @@ export const useI18n = () => {
t,
locale: currentLocale,
setLocale,
messages
messages,
isLoading: computed(() => isLoading.value)
}
}

View File

@@ -542,12 +542,12 @@ export const TIER_ORDER: AchievementTier[] = [
]
// 获取等级索引
export function getTierIndex(tier: AchievementTier): number {
export const getTierIndex = (tier: AchievementTier): number => {
return TIER_ORDER.indexOf(tier)
}
// 获取下一个等级
export function getNextTier(tier: AchievementTier | null): AchievementTier | null {
export const getNextTier = (tier: AchievementTier | null): AchievementTier | null => {
if (tier === null) return AchievementTier.Bronze
const index = getTierIndex(tier)
if (index >= TIER_ORDER.length - 1) return null

View File

@@ -352,6 +352,70 @@ export const BUILDINGS: Record<BuildingType, BuildingConfig> = {
[TechnologyType.HyperspaceTechnology]: 10
}
}
},
// 资源管理建筑
[BuildingType.GeoResearchStation]: {
id: BuildingType.GeoResearchStation,
name: '地质研究站',
description: '研究地质结构提高矿脉自然恢复速度。每级增加50%恢复速率',
baseCost: { metal: 50000, crystal: 30000, deuterium: 20000, darkMatter: 0, energy: 0 },
baseTime: 60,
costMultiplier: 1.8,
spaceUsage: 4,
planetOnly: true, // 只能在行星上建造
maxLevel: 10, // 最多10级最高6倍恢复速度
requirements: {
[BuildingType.ResearchLab]: 5,
[BuildingType.MetalMine]: 10,
[BuildingType.CrystalMine]: 10,
[BuildingType.DeuteriumSynthesizer]: 8
},
levelRequirements: {
5: {
[BuildingType.ResearchLab]: 8,
[BuildingType.RoboticsFactory]: 5,
[TechnologyType.EnergyTechnology]: 5
},
8: {
[BuildingType.ResearchLab]: 10,
[BuildingType.RoboticsFactory]: 8,
[TechnologyType.EnergyTechnology]: 8,
[BuildingType.NaniteFactory]: 1
}
}
},
// 深层钻探设施 - 提升矿脉上限
[BuildingType.DeepDrillingFacility]: {
id: BuildingType.DeepDrillingFacility,
name: '深层钻探设施',
description: '深入地壳钻探解锁更深层的矿脉储量。每级增加20%矿脉上限',
baseCost: { metal: 100000, crystal: 80000, deuterium: 50000, darkMatter: 0, energy: 0 },
baseTime: 120,
costMultiplier: 2.0,
spaceUsage: 6,
planetOnly: true, // 只能在行星上建造
maxLevel: 10, // 最多10级最高3倍矿脉上限
requirements: {
[BuildingType.ResearchLab]: 8,
[BuildingType.MetalMine]: 15,
[BuildingType.CrystalMine]: 15,
[BuildingType.DeuteriumSynthesizer]: 12,
[BuildingType.GeoResearchStation]: 3
},
levelRequirements: {
5: {
[BuildingType.ResearchLab]: 10,
[BuildingType.RoboticsFactory]: 8,
[BuildingType.NaniteFactory]: 2,
[TechnologyType.EnergyTechnology]: 10
},
8: {
[BuildingType.ResearchLab]: 12,
[BuildingType.NaniteFactory]: 5,
[TechnologyType.EnergyTechnology]: 12,
[TechnologyType.PlasmaTechnology]: 5
}
}
}
}
@@ -582,7 +646,7 @@ export const TECHNOLOGIES: Record<TechnologyType, TechnologyConfig> = {
baseCost: { metal: 100000, crystal: 200000, deuterium: 100000, darkMatter: 0, energy: 0 },
baseTime: 180,
costMultiplier: 2,
requirements: { [BuildingType.ResearchLab]: 8, [TechnologyType.HyperspaceTechnology]: 5 },
requirements: { [BuildingType.ResearchLab]: 10, [TechnologyType.HyperspaceTechnology]: 5 },
levelRequirements: {
3: {
[BuildingType.ResearchLab]: 10,
@@ -634,6 +698,34 @@ export const TECHNOLOGIES: Record<TechnologyType, TechnologyConfig> = {
[BuildingType.NaniteFactory]: 5
}
}
},
[TechnologyType.MiningTechnology]: {
id: TechnologyType.MiningTechnology,
name: '采矿技术',
description: '改进采矿方法和设备提升所有星球矿脉储量上限。每级增加15%矿脉上限',
baseCost: { metal: 50000, crystal: 30000, deuterium: 20000, darkMatter: 0, energy: 0 },
baseTime: 90,
costMultiplier: 1.8,
maxLevel: 15, // 最多15级最高约2.25倍上限加成)
requirements: {
[BuildingType.ResearchLab]: 5,
[TechnologyType.EnergyTechnology]: 4,
[BuildingType.MetalMine]: 12,
[BuildingType.CrystalMine]: 12
},
levelRequirements: {
5: {
[BuildingType.ResearchLab]: 8,
[TechnologyType.EnergyTechnology]: 8,
[BuildingType.RoboticsFactory]: 5
},
10: {
[BuildingType.ResearchLab]: 12,
[TechnologyType.EnergyTechnology]: 12,
[BuildingType.NaniteFactory]: 3,
[TechnologyType.PlasmaTechnology]: 3
}
}
}
}
@@ -652,7 +744,11 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
speed: 12500,
fuelConsumption: 20,
storageUsage: 5,
requirements: { [BuildingType.Shipyard]: 1, [TechnologyType.CombustionDrive]: 1 }
requirements: { [BuildingType.Shipyard]: 1, [TechnologyType.CombustionDrive]: 1 },
rapidFire: {
[ShipType.EspionageProbe]: 5,
[ShipType.SolarSatellite]: 5
}
},
[ShipType.HeavyFighter]: {
id: ShipType.HeavyFighter,
@@ -667,12 +763,17 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
speed: 10000,
fuelConsumption: 75,
storageUsage: 10,
requirements: { [BuildingType.Shipyard]: 3, [TechnologyType.ImpulseDrive]: 2 }
requirements: { [BuildingType.Shipyard]: 3, [TechnologyType.ImpulseDrive]: 2 },
rapidFire: {
[ShipType.SmallCargo]: 3,
[ShipType.EspionageProbe]: 5,
[ShipType.SolarSatellite]: 5
}
},
[ShipType.Cruiser]: {
id: ShipType.Cruiser,
name: '巡洋舰',
description: '中型战舰',
description: '中型战舰,克制轻型战斗机',
cost: { metal: 20000, crystal: 7000, deuterium: 2000, darkMatter: 0, energy: 0 },
buildTime: 60,
cargoCapacity: 800,
@@ -682,7 +783,13 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
speed: 15000,
fuelConsumption: 300,
storageUsage: 15,
requirements: { [BuildingType.Shipyard]: 5, [TechnologyType.ImpulseDrive]: 4, [TechnologyType.IonTechnology]: 2 }
requirements: { [BuildingType.Shipyard]: 5, [TechnologyType.ImpulseDrive]: 4, [TechnologyType.IonTechnology]: 2 },
rapidFire: {
[ShipType.LightFighter]: 6,
[ShipType.EspionageProbe]: 5,
[ShipType.SolarSatellite]: 5,
[DefenseType.RocketLauncher]: 10
}
},
[ShipType.Battleship]: {
id: ShipType.Battleship,
@@ -697,12 +804,16 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
speed: 10000,
fuelConsumption: 500,
storageUsage: 25,
requirements: { [BuildingType.Shipyard]: 7, [TechnologyType.HyperspaceDrive]: 4 }
requirements: { [BuildingType.Shipyard]: 7, [TechnologyType.HyperspaceDrive]: 4 },
rapidFire: {
[ShipType.EspionageProbe]: 5,
[ShipType.SolarSatellite]: 5
}
},
[ShipType.Battlecruiser]: {
id: ShipType.Battlecruiser,
name: '战列巡洋舰',
description: '快速强大的战斗舰船,擅长攻击战列舰',
description: '快速强大的战斗舰船,克制战列舰和巡洋舰',
cost: { metal: 30000, crystal: 40000, deuterium: 15000, darkMatter: 0, energy: 0 },
buildTime: 70,
cargoCapacity: 750,
@@ -717,12 +828,21 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
[TechnologyType.HyperspaceDrive]: 5,
[TechnologyType.HyperspaceTechnology]: 5,
[TechnologyType.LaserTechnology]: 12
},
rapidFire: {
[ShipType.SmallCargo]: 3,
[ShipType.LargeCargo]: 3,
[ShipType.HeavyFighter]: 4,
[ShipType.Cruiser]: 4,
[ShipType.Battleship]: 7,
[ShipType.EspionageProbe]: 5,
[ShipType.SolarSatellite]: 5
}
},
[ShipType.Bomber]: {
id: ShipType.Bomber,
name: '轰炸机',
description: '专门对付防御设施的轰炸舰',
description: '专门对付防御设施的轰炸舰,高效摧毁炮台',
cost: { metal: 50000, crystal: 25000, deuterium: 15000, darkMatter: 0, energy: 0 },
buildTime: 100,
cargoCapacity: 500,
@@ -736,25 +856,42 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
[BuildingType.Shipyard]: 8,
[TechnologyType.ImpulseDrive]: 6,
[TechnologyType.PlasmaTechnology]: 5
},
rapidFire: {
[ShipType.EspionageProbe]: 5,
[ShipType.SolarSatellite]: 5,
[DefenseType.RocketLauncher]: 20,
[DefenseType.LightLaser]: 20,
[DefenseType.HeavyLaser]: 10,
[DefenseType.IonCannon]: 10,
[DefenseType.GaussCannon]: 5,
[DefenseType.PlasmaTurret]: 5
}
},
[ShipType.Destroyer]: {
id: ShipType.Destroyer,
name: '驱逐舰',
description: '专业反大型舰船战舰,高火力低防护',
cost: { metal: 60000, crystal: 50000, deuterium: 15000, darkMatter: 0, energy: 0 },
buildTime: 120,
cargoCapacity: 2000,
attack: 2500,
shield: 250,
armor: 8000,
speed: 5000,
fuelConsumption: 1000,
storageUsage: 40,
description: '轻型快速战舰,擅长护航和反侦察,克制轻型单位和探测器',
cost: { metal: 15000, crystal: 5000, deuterium: 1500, darkMatter: 0, energy: 0 },
buildTime: 45,
cargoCapacity: 500,
attack: 250,
shield: 40,
armor: 2000,
speed: 18000,
fuelConsumption: 200,
storageUsage: 12,
requirements: {
[BuildingType.Shipyard]: 9,
[TechnologyType.HyperspaceDrive]: 6,
[TechnologyType.HyperspaceTechnology]: 5
[BuildingType.Shipyard]: 4,
[TechnologyType.ImpulseDrive]: 3,
[TechnologyType.ComputerTechnology]: 3
},
rapidFire: {
[ShipType.LightFighter]: 4,
[ShipType.EspionageProbe]: 10,
[ShipType.SolarSatellite]: 10,
[ShipType.SmallCargo]: 2,
[DefenseType.RocketLauncher]: 5
}
},
[ShipType.SmallCargo]: {
@@ -883,6 +1020,32 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
[BuildingType.PlanetDestroyerFactory]: 3,
[TechnologyType.PlanetDestructionTech]: 7,
[TechnologyType.HyperspaceDrive]: 7
},
rapidFire: {
// 对所有舰船的快速射击
[ShipType.SmallCargo]: 250,
[ShipType.LargeCargo]: 250,
[ShipType.LightFighter]: 200,
[ShipType.HeavyFighter]: 100,
[ShipType.Cruiser]: 33,
[ShipType.Battleship]: 30,
[ShipType.Battlecruiser]: 15,
[ShipType.Bomber]: 25,
[ShipType.Destroyer]: 5,
[ShipType.ColonyShip]: 250,
[ShipType.Recycler]: 250,
[ShipType.EspionageProbe]: 1250,
[ShipType.SolarSatellite]: 1250,
[ShipType.DarkMatterHarvester]: 50,
// 对所有防御设施的快速射击
[DefenseType.RocketLauncher]: 200,
[DefenseType.LightLaser]: 200,
[DefenseType.HeavyLaser]: 100,
[DefenseType.GaussCannon]: 50,
[DefenseType.IonCannon]: 100,
[DefenseType.PlasmaTurret]: 10,
[DefenseType.SmallShieldDome]: 1250,
[DefenseType.LargeShieldDome]: 1250
}
}
}
@@ -1180,12 +1343,31 @@ export const DIPLOMATIC_CONFIG = {
NPC_GIFT_CONFIG: {
ENABLED: true,
MIN_REPUTATION: 60, // NPC对玩家好感度≥60才会赠送
CHECK_INTERVAL: 24 * 3600, // 每检查一次(秒)
GIFT_PROBABILITY: 0.05, // 5%概率赠送
GIFT_AMOUNT: {
METAL: { min: 1000, max: 5000 },
CRYSTAL: { min: 500, max: 2500 },
DEUTERIUM: { min: 200, max: 1000 }
CHECK_INTERVAL: 4 * 3600, // 每4小时检查一次(秒)
GIFT_PROBABILITY: 0.3, // 30%概率赠送(提高频率)
// 基础赠送量(会根据玩家经济规模动态调整)
BASE_GIFT_AMOUNT: {
METAL: { min: 100000, max: 500000 },
CRYSTAL: { min: 50000, max: 250000 },
DEUTERIUM: { min: 20000, max: 100000 }
},
// 动态赠送配置:根据玩家总产量比例赠送
DYNAMIC_GIFT: {
ENABLED: true,
// 赠送玩家每小时产量的倍数4-8小时产量
PRODUCTION_MULTIPLIER: { min: 4, max: 8 },
// 最小赠送量(保底)
MIN_AMOUNT: {
METAL: 100000,
CRYSTAL: 50000,
DEUTERIUM: 20000
},
// 最大赠送量(封顶,防止过于夸张)
MAX_AMOUNT: {
METAL: 100000000, // 1亿
CRYSTAL: 50000000, // 5千万
DEUTERIUM: 20000000 // 2千万
}
}
},
@@ -1197,16 +1379,264 @@ export const DIPLOMATIC_CONFIG = {
MAX_REJECTION_PROBABILITY: 0.8, // 最大拒绝概率80%,即使关系很差)
GIFT_EXPIRATION_DAYS: 7, // 礼物通知过期天数
REJECTION_REPUTATION_PENALTY: -5 // 拒绝礼物导致的好感度降低
},
// 盟友协防配置
ALLY_DEFENSE_CONFIG: {
ENABLED: true,
// 触发协防的最低好感度
MIN_REPUTATION: 70,
// 检查间隔(秒)
CHECK_INTERVAL: 30,
// 派遣舰队比例(派出多少比例的可用舰队)
FLEET_RATIO: { min: 0.3, max: 0.6 },
// 协防持续时间(毫秒)- 舰队在玩家星球驻守多久
STATION_DURATION: 2 * 60 * 60 * 1000, // 2小时
// 每个NPC最多同时协防几个星球
MAX_CONCURRENT_DEFENSES: 2,
// 协防触发条件:来袭舰队规模最小值
MIN_INCOMING_FLEET_SIZE: 50,
// 协防触发概率(根据好感度调整)
BASE_DEFENSE_PROBABILITY: 0.7
},
// 敌对NPC增强行为配置
HOSTILE_BEHAVIOR_CONFIG: {
// 围攻机制多个敌对NPC协同攻击
SIEGE: {
ENABLED: true,
// 触发围攻的条件
MIN_HOSTILE_NPCS: 2, // 至少2个敌对NPC才能触发围攻
TRIGGER_REPUTATION: -50, // 好感度低于此值的NPC可能参与围攻
// 围攻窗口时间(毫秒)- NPC在此时间内协调攻击
COORDINATION_WINDOW: 60 * 1000, // 1分钟内到达
// 围攻概率(基础概率,会根据玩家实力调整)
BASE_PROBABILITY: 0.3,
// 围攻冷却时间(毫秒)
COOLDOWN: 30 * 60 * 1000, // 30分钟
// 围攻舰队比例每个参与NPC派出的舰队比例
FLEET_RATIO: { min: 0.5, max: 0.8 }
},
// 骚扰战术:小规模持续袭击
HARASSMENT: {
ENABLED: true,
// 触发条件:好感度低于此值
TRIGGER_REPUTATION: -30,
// 骚扰间隔(秒)- 比正常攻击更频繁
INTERVAL: 120, // 2分钟
// 骚扰舰队规模(小规模)
FLEET_SIZE_RATIO: { min: 0.1, max: 0.2 },
// 骚扰概率
PROBABILITY: 0.4,
// 骚扰类型权重
TYPES: {
spy: 0.4, // 40%概率侦查骚扰
raid: 0.4, // 40%概率小规模袭击
intimidate: 0.2 // 20%概率威慑(派舰队但不攻击,消耗玩家注意力)
}
},
// 报复升级:被攻击次数越多,反击越猛烈
REVENGE_ESCALATION: {
ENABLED: true,
// 报复等级配置(基于被攻击次数)
LEVELS: [
{ attackCount: 1, fleetMultiplier: 1.0, attackInterval: 600, name: 'mild' }, // 轻微:正常反击
{ attackCount: 3, fleetMultiplier: 1.5, attackInterval: 300, name: 'moderate' }, // 中等1.5倍舰队5分钟间隔
{ attackCount: 5, fleetMultiplier: 2.0, attackInterval: 180, name: 'severe' }, // 严重2倍舰队3分钟间隔
{ attackCount: 10, fleetMultiplier: 3.0, attackInterval: 60, name: 'extreme' } // 极端3倍舰队1分钟间隔
],
// 报复记忆时间(毫秒)- 超过此时间后报复等级重置
MEMORY_DURATION: 7 * 24 * 60 * 60 * 1000, // 7天
// 报复期间是否呼叫盟友NPC助战
CALL_ALLIES: true,
// 呼叫盟友的概率(报复等级越高概率越大)
ALLY_CALL_BASE_PROBABILITY: 0.2
}
},
// 中立NPC行为配置
NEUTRAL_BEHAVIOR_CONFIG: {
// 有条件贸易中立NPC可以与玩家进行资源交换
TRADE: {
ENABLED: true,
// 贸易前提好感度在此区间内的中立NPC才会贸易
MIN_REPUTATION: -10, // 好感度不能太低
MAX_REPUTATION: 19, // 好感度不能太高(太高就是友好了)
// 贸易检查间隔(秒)
CHECK_INTERVAL: 1800, // 30分钟
// 贸易概率
PROBABILITY: 0.1,
// 玩家最多同时保留的贸易提议数量
MAX_PENDING_OFFERS: 30,
// 汇率配置NPC出售资源的汇率
EXCHANGE_RATES: {
// 金属:晶体:重氢 基准比例 3:2:1
metalToCrystal: 1.5, // 1.5金属换1晶体
metalToDeuterium: 3.0, // 3金属换1重氢
crystalToDeuterium: 2.0 // 2晶体换1重氢
},
// 贸易量配置(基于玩家每小时产量的倍数)
TRADE_AMOUNT: {
min: 2, // 最少2小时产量
max: 6 // 最多6小时产量
},
// 贸易手续费NPC抽取的百分比
FEE_PERCENTAGE: 10,
// 成功贸易增加好感度
REPUTATION_GAIN: 3
},
// 外交摇摆中立NPC会根据玩家行为倾向友好或敌对
SWING: {
ENABLED: true,
// 摇摆检查间隔(秒)
CHECK_INTERVAL: 1800, // 30分钟
// 摇摆触发阈值
SWING_THRESHOLD: {
toFriendly: 15, // 好感度达到15时有概率变友好
toHostile: -15 // 好感度达到-15时有概率变敌对
},
// 摇摆概率(基础概率,根据好感度调整)
BASE_PROBABILITY: 0.2,
// 摇摆时的通知给玩家提示NPC态度变化
NOTIFY_PLAYER: true
},
// 观望行为中立NPC会观察玩家与其他NPC的互动
OBSERVE: {
ENABLED: true,
// 观察范围同一星系内的NPC
SAME_GALAXY_ONLY: true,
// 玩家攻击附近NPC时中立NPC好感度变化
ATTACK_NEARBY_PENALTY: -3,
// 玩家帮助附近NPC时中立NPC好感度变化
HELP_NEARBY_BONUS: 2
}
},
// 友好NPC增强行为配置
FRIENDLY_BEHAVIOR_CONFIG: {
// 情报共享友好NPC会分享其他NPC的情报
INTEL_SHARING: {
ENABLED: true,
// 触发条件:好感度达到此值
MIN_REPUTATION: 50,
// 分享间隔(秒)
INTERVAL: 3600, // 1小时
// 分享概率
PROBABILITY: 0.4,
// 情报类型
INTEL_TYPES: {
enemyFleet: 0.4, // 40%概率分享敌对NPC舰队信息
enemyResources: 0.3, // 30%概率分享敌对NPC资源信息
enemyMovement: 0.3 // 30%概率分享敌对NPC舰队动向
}
},
// 联合攻击邀请友好NPC可能邀请玩家一起攻击敌对NPC
JOINT_ATTACK: {
ENABLED: true,
// 触发条件:好感度达到此值
MIN_REPUTATION: 70,
// 邀请间隔(秒)
INTERVAL: 7200, // 2小时
// 邀请概率
PROBABILITY: 0.25,
// 目标选择:共同敌人优先
PREFER_COMMON_ENEMY: true,
// NPC提供的舰队比例
NPC_FLEET_RATIO: { min: 0.3, max: 0.5 },
// 战利品分配:玩家获得的比例
PLAYER_LOOT_RATIO: 0.7,
// 联合攻击成功增加好感度
SUCCESS_REPUTATION_GAIN: 10,
// 拒绝邀请减少好感度
DECLINE_REPUTATION_PENALTY: -5
},
// 资源援助友好NPC在玩家困难时提供援助
RESOURCE_AID: {
ENABLED: true,
// 触发条件:好感度达到此值
MIN_REPUTATION: 60,
// 检查间隔(秒)
CHECK_INTERVAL: 1800, // 30分钟
// 触发条件玩家资源低于产量的N小时
TRIGGER_LOW_RESOURCES_HOURS: 2,
// 援助量玩家N小时产量
AID_AMOUNT_HOURS: 4,
// 援助概率
PROBABILITY: 0.5
}
},
// NPC联盟系统配置
NPC_ALLIANCE_CONFIG: {
ENABLED: true,
// NPC之间关系初始化
INTER_NPC_RELATIONS: {
// 初始化方式random随机、distance基于距离、type基于AI类型
INIT_MODE: 'mixed',
// 同一星系内NPC更容易成为盟友
SAME_GALAXY_ALLY_BONUS: 0.3,
// AI类型兼容性决定NPC之间关系
TYPE_COMPATIBILITY: {
// 侵略型与防守型互为敌人
aggressive_defensive: -0.8,
// 商人型与任何类型都友好
trader_any: 0.5,
// 扩张型互相竞争
expansionist_expansionist: -0.3,
// 平衡型保持中立
balanced_any: 0
}
},
// 连坐效应配置
GUILT_BY_ASSOCIATION: {
ENABLED: true,
// 攻击NPC时其盟友好感度降低
ALLY_REPUTATION_PENALTY: -10,
// 帮助NPC时其盟友好感度增加
ALLY_REPUTATION_BONUS: 5,
// 盟友关系强度影响(越紧密影响越大)
STRENGTH_MULTIPLIER: true,
// 最大传播深度(防止无限传播)
MAX_PROPAGATION_DEPTH: 2
},
// NPC盟友互助
MUTUAL_DEFENSE: {
ENABLED: true,
// 盟友被攻击时的响应概率
RESPONSE_PROBABILITY: 0.5,
// 响应舰队比例
FLEET_RATIO: { min: 0.2, max: 0.4 },
// 响应冷却时间(秒)
COOLDOWN: 1800 // 30分钟
},
// NPC之间的资源共享
RESOURCE_SHARING: {
ENABLED: true,
// 共享间隔(秒)
INTERVAL: 7200, // 2小时
// 共享比例
SHARE_RATIO: 0.1 // 共享10%资源
}
}
}
// 矿脉储量配置
export const ORE_DEPOSIT_CONFIG = {
// 基础储量(单位:资源量)
// 基础储量(单位:资源量)- 大幅提升以支持后期发展
BASE_DEPOSITS: {
metal: 50_000_000, // 5000万金属
crystal: 30_000_000, // 3000万晶体
deuterium: 15_000_000 // 1500万重氢
metal: 500_000_000_0, // 50亿金属
crystal: 300_000_000_0, // 30亿晶体
deuterium: 150_000_000_0 // 15亿重氢
},
// 位置系数位置1-15影响资源分布
// 内圈(1-4)晶体多,中圈(5-10)均衡,外圈(11-15)重氢多
@@ -1222,77 +1652,87 @@ export const ORE_DEPOSIT_CONFIG = {
// 警告阈值低于10%时显示警告)
WARNING_THRESHOLD: 0.1,
// 产量衰减开始阈值低于5%时产量开始衰减)
DECAY_START_THRESHOLD: 0.05
DECAY_START_THRESHOLD: 0.05,
// 矿脉耗尽后的基础产量保底20%产量,确保游戏可继续)
MIN_PRODUCTION_EFFICIENCY: 0.2,
// 矿脉恢复配置
REGENERATION: {
ENABLED: true,
// 每小时恢复初始储量的百分比0.1% = 每小时恢复0.1%约42天完全恢复
RATE_PER_HOUR: 0.001,
// 最大恢复到初始储量的百分比100%表示可以完全恢复)
MAX_PERCENTAGE: 1.0
}
}
// 探险区域配置
// 探险区域配置 - 大幅提升奖励,降低空手而归概率
export const EXPEDITION_ZONES: Record<ExpeditionZone, ExpeditionZoneConfig> = {
[ExpeditionZone.NearSpace]: {
id: ExpeditionZone.NearSpace,
requiredTechLevel: 0, // 无需求
flightTimeMultiplier: 1.0,
resourceMultiplier: 1.0,
darkMatterMultiplier: 1.0,
fleetFindMultiplier: 1.0,
resourceMultiplier: 1.5, // 1.0 -> 1.5
darkMatterMultiplier: 1.5, // 1.0 -> 1.5
fleetFindMultiplier: 1.5, // 1.0 -> 1.5
dangerMultiplier: 0.5, // 低危险
probabilities: {
resources: 35, // 高概率发现资源
darkMatter: 8,
fleet: 10,
pirates: 7, // 低海盗
aliens: 5, // 低外星人
nothing: 35 // 高概率什么都没发现
resources: 40, // 35 -> 40 高概率发现资源
darkMatter: 12, // 8 -> 12
fleet: 15, // 10 -> 15
pirates: 5, // 7 -> 5 低海盗
aliens: 3, // 5 -> 3 低外星人
nothing: 25 // 35 -> 25 降低空手而归
}
},
[ExpeditionZone.DeepSpace]: {
id: ExpeditionZone.DeepSpace,
requiredTechLevel: 4, // 需要天体物理学4级
flightTimeMultiplier: 1.5,
resourceMultiplier: 1.5,
darkMatterMultiplier: 1.5,
fleetFindMultiplier: 1.5,
resourceMultiplier: 2.5, // 1.5 -> 2.5
darkMatterMultiplier: 2.5, // 1.5 -> 2.5
fleetFindMultiplier: 2.0, // 1.5 -> 2.0
dangerMultiplier: 1.0, // 标准危险
probabilities: {
resources: 30,
darkMatter: 10,
fleet: 15,
pirates: 15,
aliens: 10,
nothing: 20
resources: 35, // 30 -> 35
darkMatter: 15, // 10 -> 15
fleet: 18, // 15 -> 18
pirates: 12, // 15 -> 12
aliens: 8, // 10 -> 8
nothing: 12 // 20 -> 12
}
},
[ExpeditionZone.UnchartedSpace]: {
id: ExpeditionZone.UnchartedSpace,
requiredTechLevel: 8, // 需要天体物理学8级
flightTimeMultiplier: 2.0,
resourceMultiplier: 2.5,
darkMatterMultiplier: 2.0,
fleetFindMultiplier: 2.0,
resourceMultiplier: 4.0, // 2.5 -> 4.0
darkMatterMultiplier: 3.5, // 2.0 -> 3.5
fleetFindMultiplier: 3.0, // 2.0 -> 3.0
dangerMultiplier: 1.5, // 高危险
probabilities: {
resources: 25,
darkMatter: 12,
fleet: 18,
pirates: 18,
aliens: 15,
nothing: 12
resources: 32, // 25 -> 32
darkMatter: 18, // 12 -> 18
fleet: 22, // 18 -> 22
pirates: 12, // 18 -> 12
aliens: 10, // 15 -> 10
nothing: 6 // 12 -> 6
}
},
[ExpeditionZone.DangerousNebula]: {
id: ExpeditionZone.DangerousNebula,
requiredTechLevel: 12, // 需要天体物理学12级
flightTimeMultiplier: 3.0,
resourceMultiplier: 4.0,
darkMatterMultiplier: 3.0,
fleetFindMultiplier: 3.0,
resourceMultiplier: 6.0, // 4.0 -> 6.0
darkMatterMultiplier: 5.0, // 3.0 -> 5.0
fleetFindMultiplier: 5.0, // 3.0 -> 5.0
dangerMultiplier: 2.5, // 极高危险
probabilities: {
resources: 20,
darkMatter: 15,
fleet: 20,
pirates: 20,
aliens: 20,
nothing: 5 // 很少什么都没发现
resources: 30, // 20 -> 30
darkMatter: 20, // 15 -> 20
fleet: 25, // 20 -> 25
pirates: 12, // 20 -> 12
aliens: 10, // 20 -> 10
nothing: 3 // 5 -> 3 极少空手而归
}
}
}

View File

@@ -2,6 +2,6 @@ import type { ClassValue } from 'clsx'
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
export const cn = (...inputs: ClassValue[]) => {
return twMerge(clsx(inputs))
}

View File

@@ -11,29 +11,11 @@ export default {
common: {
confirm: 'Bestätigen',
cancel: 'Abbrechen',
delete: 'Löschen',
edit: 'Bearbeiten',
save: 'Speichern',
close: 'Schließen',
back: 'Zurück',
next: 'Weiter',
gotIt: '',
previous: 'Vorherige',
submit: 'Absenden',
reset: 'Zurücksetzen',
search: 'Suchen',
filter: 'Filtern',
loading: 'Laden...',
noData: 'Keine Daten',
error: 'Fehler',
success: 'Erfolg',
warning: 'Warnung',
info: 'Info',
gotIt: 'Verstanden',
resourceType: 'Ressourcentyp',
playerName: 'Kommandant',
timeHour: 'Std',
timeMinute: 'Min',
timeSecond: 'Sek',
featureLocked: 'Funktion gesperrt',
unlockRequired: 'Gebäude erforderlich',
requiredBuilding: 'Erforderliches Gebäude',
@@ -41,7 +23,6 @@ export default {
goToBuildings: 'Zu Gebäuden',
locked: 'Gesperrt',
viewRequirements: 'Anforderungen anzeigen',
requirements: 'Anforderungen',
requirementsNotMet: 'Anforderungen nicht erfüllt',
current: 'Aktuell',
level: 'Stufe',
@@ -51,7 +32,8 @@ export default {
viewDetails: 'Details anzeigen',
exitConfirmTitle: 'Spiel beenden',
exitConfirmMessage: 'Möchten Sie das Spiel wirklich beenden? Ihr Fortschritt wird automatisch gespeichert.',
points: 'Punkte'
points: 'Punkte',
retry: 'Wiederholen'
},
errors: {
requirementsNotMet: 'Anforderungen nicht erfüllt',
@@ -88,13 +70,13 @@ export default {
galaxy: 'Galaxie',
diplomacy: 'Diplomatie',
achievements: 'Erfolge',
campaign: 'Kampagne',
ranking: 'Rangliste',
messages: 'Nachrichten',
settings: 'Einstellungen',
gm: 'GM'
},
sidebar: {
language: 'Sprache',
lightMode: 'Heller Modus',
darkMode: 'Dunkler Modus',
collapse: 'Einklappen',
@@ -119,9 +101,6 @@ export default {
},
energy: {
lowWarning: 'Energiedefizit! Ressourcenproduktion gestoppt!',
severeWarning: 'Energiedefizit! Ressourcenproduktion gestoppt!',
criticalWarning: 'Energiedefizit! Ressourcenproduktion gestoppt!',
noProduction: 'Energiedefizit! Ressourcenproduktion gestoppt!',
deficitDetail: 'Energiedefizit: {deficit}, bauen Sie mehr Kraftwerke',
buildSolarPlant: 'Kraftwerk bauen'
},
@@ -132,27 +111,20 @@ export default {
lowResources: 'Fast erschöpft: {resources}'
},
planet: {
planet: 'Planet',
moon: 'Mond',
colony: 'Kolonie',
position: 'Position',
coordinates: 'Koordinaten',
switchToMoon: 'Zum Mond',
backToPlanet: 'Zurück zum Planeten',
switchPlanet: 'Planet wechseln',
currentPlanet: 'Aktueller Planet',
fields: 'Felder',
temperature: 'Temperatur',
homePlanet: 'Heimatplanet',
planetPrefix: 'Planet',
moonSuffix: 's Mond',
colonyPrefix: 'Kolonie',
renamePlanet: 'Planet umbenennen',
renamePlanetTitle: 'Planet umbenennen',
newPlanetName: 'Neuer Name',
planetNamePlaceholder: 'Neuen Planetennamen eingeben',
rename: 'Umbenennen',
renameSuccess: 'Planet wurde in {name} umbenannt'
rename: 'Umbenennen'
},
player: {
points: 'Gesamtpunkte'
@@ -179,13 +151,12 @@ export default {
sensorPhalanx: 'Sensorphalanx',
jumpGate: 'Sprungtor',
planetDestroyerFactory: 'Planetenzerstörer-Fabrik',
geoResearchStation: 'Geologische Forschungsstation',
deepDrillingFacility: 'Tiefbohranlage',
buildTime: 'Bauzeit',
build: '',
build: 'Bauen',
production: 'Produktion',
consumption: 'Verbrauch',
totalCost: 'Gesamtkosten',
totalPoints: 'Gesamtpunkte',
levelRange: 'Stufenbereich',
capacity: 'Capacity/Effect',
storageCapacity: 'Capacity',
energyProduction: 'Energy Production',
@@ -194,7 +165,6 @@ export default {
buildQueueBonus: 'Bauauftrag',
spaceBonus: 'Raumbonus',
buildSpeedBonus: 'Baugeschwindigkeitsbonus',
researchSpeedBonus: 'Forschungsgeschwindigkeitsbonus',
planetSpace: 'Planet Space',
moonSpace: 'Moon Space',
missileCapacity: 'Missile Capacity',
@@ -228,7 +198,10 @@ export default {
lunarBase: 'Erhöht verfügbaren Platz auf dem Mond, +30 Platz pro Stufe',
sensorPhalanx: 'Erkennt Flottenaktivitäten in umliegenden Systemen',
jumpGate: 'Überträgt Flotten sofort zu anderen Monden',
planetDestroyerFactory: 'Konstruiert ultimative Waffen zur Zerstörung von Planeten'
planetDestroyerFactory: 'Konstruiert ultimative Waffen zur Zerstörung von Planeten',
geoResearchStation:
'Erforscht geologische Strukturen und erhöht die natürliche Erzvorkommen-Regenerationsrate. +50% Regeneration pro Stufe',
deepDrillingFacility: ''
},
ships: {
lightFighter: 'Leichter Jäger',
@@ -292,9 +265,6 @@ export default {
},
research: {
researchTime: 'Forschungszeit',
totalCost: 'Gesamtkosten',
totalPoints: 'Gesamtpunkte',
levelRange: 'Stufenbereich',
capacity: 'Capacity/Effect',
storageCapacity: 'Capacity',
energyProduction: 'Energy Production',
@@ -302,7 +272,15 @@ export default {
buildQueue: 'Build Queue',
planetSpace: 'Planet Space',
moonSpace: 'Moon Space',
missileCapacity: 'Missile Capacity'
missileCapacity: 'Missile Capacity',
attackBonus: 'Angriffsbonus',
shieldBonus: 'Schildbonus',
armorBonus: 'Panzerungsbonus',
spyLevel: 'Spionagestufe',
researchQueueBonus: 'Forschungswarteschlange',
colonySlots: 'Kolonieplätze',
forAllPlanets: '(Global)',
speedBonus: 'Geschwindigkeitsbonus'
},
technologies: {
energyTechnology: 'Energietechnik',
@@ -322,7 +300,8 @@ export default {
hyperspaceDrive: 'Hyperraumantrieb',
darkMatterTechnology: 'Dunkle-Materie-Technologie',
terraformingTechnology: 'Terraforming-Technologie',
planetDestructionTech: 'Planetenzerstörungstechnologie'
planetDestructionTech: 'Planetenzerstörungstechnologie',
miningTechnology: ''
},
technologyDescriptions: {
energyTechnology: 'Verbessert Energieeffizienz',
@@ -343,7 +322,8 @@ export default {
hyperspaceDrive: 'Fortgeschrittene Antriebstechnologie',
darkMatterTechnology: 'Forschung zu Eigenschaften und Anwendungen von Dunkler Materie',
terraformingTechnology: 'Forschung zur Planeten-Terraforming-Technologie, erhöht verfügbaren Platz aller Planeten um 30 pro Stufe',
planetDestructionTech: 'Schreckliche Technologie zur Zerstörung ganzer Planeten'
planetDestructionTech: 'Schreckliche Technologie zur Zerstörung ganzer Planeten',
miningTechnology: ''
},
officers: {
commander: 'Kommandant',
@@ -378,8 +358,12 @@ export default {
cancelResearch: 'Forschung abbrechen',
confirmCancel: 'Möchten Sie wirklich abbrechen? 50% der Ressourcen werden zurückerstattet.',
level: 'Stufe',
gmModeActivated: '',
gmModeActivated: 'GM-Modus aktiviert! Überprüfen Sie das Navigationsmenü.',
upgradeToLevel: 'Auf Stufe aufrüsten',
buildQueueBonus: 'Bauauftrag',
spaceBonus: 'Raumbonus',
researchQueueBonus: 'Forschungswarteschlange',
quantity: 'Anzahl',
tabs: {
all: 'Alle',
buildings: 'Gebäude',
@@ -397,14 +381,9 @@ export default {
movedToQueue: 'Aufgabe in Warteschlange verschoben'
},
overview: {
title: 'Planetenübersicht',
resourceOverview: 'Ressourcen',
fleetInfo: 'Flotte',
currentShips: 'Schiffe auf diesem Planeten',
productionSources: 'Produktionsquellen',
productionSourcesDesc: 'Detaillierte Ressourcenproduktion und Bonusinformationen',
consumptionSources: 'Verbrauchsquellen',
consumptionSourcesDesc: 'Energieverbrauchsdetails für Gebäude',
totalProduction: 'Gesamtproduktion',
totalConsumption: 'Gesamtverbrauch',
noConsumption: 'Kein Energieverbrauch',
@@ -413,14 +392,8 @@ export default {
tabConsumption: 'Verbrauchsdetails'
},
buildingsView: {
title: 'Gebäude',
usedSpace: 'Verwendeter Platz',
spaceUsage: 'Platzbedarf',
level: 'Stufe',
gmModeActivated: '',
upgradeCost: 'Ausbaukosten',
buildTime: 'Bauzeit',
build: 'Bauen',
upgrade: 'Ausbauen',
maxLevelReached: 'Maximale Stufe erreicht',
requirementsNotMet: 'Anforderungen nicht erfüllt',
@@ -430,8 +403,7 @@ export default {
demolishRefund: 'Abriss-Rückerstattung',
demolishFailed: 'Abriss fehlgeschlagen',
demolishFailedMessage: 'Abriss nicht möglich. Bitte überprüfen Sie, ob die Bauqueue voll ist oder die Gebäudestufe 0 ist.',
confirmDemolish: '',
confirmDemolishMessage: ''
confirmDemolish: 'Abriss bestätigen'
},
researchView: {
title: 'Forschung',
@@ -443,26 +415,14 @@ export default {
'Bitte überprüfen Sie, ob Sie genügend Ressourcen haben, die Voraussetzungen erfüllt sind oder keine anderen Forschungsaufträge vorhanden sind.'
},
shipyard: {
attack: 'Angriff',
missileAttack: 'Raketenangriff',
shield: 'Schild',
armor: 'Panzerung',
speed: 'Geschwindigkeit',
cargoCapacity: 'Ladekapazität',
fuelConsumption: 'Treibstoffverbrauch',
buildCost: 'Baukosten',
buildTime: 'Bauzeit',
build: '',
perUnit: 'Pro Einheit',
batchCalculator: 'Batch-Rechner',
quantity: 'Menge',
totalCost: 'Gesamtkosten',
totalTime: 'Gesamtzeit'
fuelConsumption: 'Treibstoffverbrauch'
},
shipyardView: {
title: 'Raumschiffwerft',
fleetStorage: 'Flottenspeicher',
owned: 'Besitz',
attack: 'Angriff',
missileAttack: 'Raketenangriff',
shield: 'Schild',
@@ -484,7 +444,7 @@ export default {
armor: 'Panzerung',
buildCost: 'Baukosten',
buildTime: 'Bauzeit',
build: '',
build: 'Bauen',
perUnit: 'Pro Einheit',
batchCalculator: 'Batch-Rechner',
quantity: 'Menge',
@@ -538,6 +498,7 @@ export default {
missionInfo: 'Missionsinfo',
fuelConsumption: 'Treibstoffverbrauch',
flightTime: 'Flugzeit',
outOfRange: 'Außer Reichweite',
attackMission: 'Angriff',
transport: 'Transport',
colonize: 'Kolonisieren',
@@ -549,6 +510,7 @@ export default {
requiresAstro: 'Benötigt Astrophysik Stufe {level}',
reward: 'Belohnung',
danger: 'Gefahr',
destroy: 'Vernichten',
zones: {
nearSpace: {
name: 'Naher Weltraum',
@@ -579,11 +541,12 @@ export default {
arrivalTime: 'Ankunftszeit',
returnTime: 'Rückkehrzeit',
recallFleet: 'Flotte zurückrufen',
abortMission: '',
abortMissionTitle: '',
abortMissionWarning: '',
abortMissionSuccess: '',
abortMissionSuccessMessage: '',
abortMission: 'Mission abbrechen',
abortMissionTitle: 'Mission abbrechen bestätigen',
abortMissionWarning:
'WARNUNG: Das Abbrechen dieser Mission führt zum permanenten Verlust von {ships} Schiffen und {resources} Ressourcen!\n\nDiese Aktion ist unwiderruflich und die Flotte und Ressourcen werden nicht zurückkehren.',
abortMissionSuccess: 'Mission abgebrochen',
abortMissionSuccessMessage: 'Mission wurde abgebrochen, Flotte und Ressourcen sind verloren.',
sendFailed: 'Senden fehlgeschlagen',
sendFailedMessage: 'Bitte überprüfen Sie Flottenanzahl, Treibstoffverfügbarkeit oder Ladekapazitätsgrenzen.',
recallFailed: 'Zurückrufen fehlgeschlagen',
@@ -652,7 +615,6 @@ export default {
benefitsBonus: 'Vorteile',
resourceProduction: 'Ressourcenproduktion',
darkMatterProduction: 'Dunkle-Materie-Produktion',
energyProduction: 'Energieproduktion',
buildingSpeed: 'Baugeschwindigkeit',
researchSpeed: 'Forschungsgeschwindigkeit',
fleetSpeed: 'Flottengeschwindigkeit',
@@ -681,14 +643,11 @@ export default {
selectGalaxy: 'Galaxie auswählen',
system: 'System',
selectSystem: 'System auswählen',
view: 'Anzeigen',
myPlanet: 'Mein Planet',
myPlanets: 'Meine Systeme ansehen',
npcPlanets: 'NPC-Planeten',
selectPlanetToView: 'Planet auswählen, um sein System anzuzeigen',
totalPositions: 'Insgesamt 10 Planetenpositionen',
mine: 'Mein',
hostile: 'Feindlich',
emptySlot: 'Leer - Kolonisierbar',
scout: 'Spähen',
attack: 'Angriff',
@@ -715,7 +674,9 @@ export default {
systems: 'Systeme',
distance: 'Entfernung',
flightTime: 'Flugzeit',
outOfRange: 'Außer Reichweite',
launchMissile: 'Starten',
missileLaunched: 'Rakete abgefeuert',
cancel: 'Abbrechen',
colonizePlanetMessage:
'Möchten Sie wirklich Position [{coordinates}] kolonisieren?\n\nBitte gehen Sie zur Flottenseite, um ein Kolonieschiff zu senden.',
@@ -728,20 +689,34 @@ export default {
'Möchten Sie wirklich Ressourcen als Geschenk an Planet [{coordinates}] senden?\n\nBitte gehen Sie zur Flottenseite, um Transporter auszuwählen und Ressourcen zu laden.',
npcPlanetName: '{name}s Planet',
intercepted: 'Abgefangen',
defenseLosses: 'Verteidigungsverluste'
defenseLosses: 'Verteidigungsverluste',
phalanxScan: 'Sensorabtastung',
phalanxScanTitle: 'Sensorarray-Scan',
phalanxScanDescription: 'Flottenaktivität von Planet [{coordinates}] scannen',
phalanxNoMoon: 'Benötigt Mond mit Sensorarray zum Scannen',
phalanxCost: 'Scan-Kosten',
phalanxNoFleets: 'Keine Flottenaktivität erkannt',
phalanxFleetDetected: '{count} Flotte(n) erkannt',
phalanxOrigin: 'Ausgangsort',
phalanxDestination: 'Zielort',
phalanxArrival: 'Ankunftszeit',
phalanxReturn: 'Rückkehrzeit',
phalanxStatusOutbound: 'Unterwegs',
phalanxStatusReturning: 'Rückkehr',
phalanxInsufficientDeuterium: 'Unzureichend Deuterium'
},
messagesView: {
title: 'Nachrichten',
battles: 'Kämpfe',
spy: 'Spionage',
npc: 'NPC',
diplomacy: '',
diplomacy: 'Diplomatie',
spied: 'Ausspioniert',
battleReports: 'Kampfberichte',
spyReports: 'Spionageberichte',
noBattleReports: 'Keine Kampfberichte',
noSpyReports: 'Keine Spionageberichte',
noDiplomaticReports: '',
noDiplomaticReports: 'Keine diplomatischen Berichte',
noSpiedNotifications: 'Keine Ausspionierungs-Benachrichtigungen',
battleReport: 'Kampfbericht',
spyReport: 'Spionagebericht',
@@ -766,6 +741,8 @@ export default {
defense: 'Verteidigung',
buildings: 'Gebäude',
unread: 'Ungelesen',
pending: 'Ausstehend',
invalidData: 'Ungültige Daten',
targetPlanet: 'Zielplanet',
attackerRemaining: 'Angreifer verblieben',
defenderRemaining: 'Verteidiger verblieben',
@@ -813,36 +790,37 @@ export default {
polite_decline: 'Sie lehnten höflich ab'
},
// Spied notification dialog
spiedNotificationDetails: '',
spyDetected: '',
detectionResult: '',
detectionSuccess: '',
spiedNotificationMessage: '',
spiedNotificationTip: '',
viewInGalaxy: '',
spiedNotificationDetails: 'Ausspionierung Details',
spyDetected: 'Spion entdeckt',
detectionResult: 'Erkennungsergebnis',
detectionSuccess: 'Du hast den feindlichen Spion entdeckt!',
spiedNotificationMessage: '{npc} hat versucht, deinen Planeten {planet} auszuspionieren',
spiedNotificationTip: 'Erwäge, deine Verteidigung zu erhöhen oder einen Gegenangriff zu starten, wenn dieser NPC feindlich ist',
viewInGalaxy: 'In Galaxie anzeigen',
// Mission report dialog
missionReportDetails: '',
missionSuccess: '',
missionFailed: '',
origin: '',
destination: '',
missionDetails: '',
transportedResources: '',
recycledResources: '',
remainingDebris: '',
newPlanet: '',
missionReportDetails: 'Missionsbericht Details',
missionSuccess: 'Erfolg',
missionFailed: 'Fehlgeschlagen',
origin: 'Herkunft',
destination: 'Ziel',
missionDetails: 'Missionsdetails',
transportedResources: 'Transportierte Ressourcen',
recycledResources: 'Recycelte Ressourcen',
remainingDebris: 'Verbleibende Trümmer',
newPlanet: 'Neuer Planet',
// NPC activity dialog
npcActivityDetails: '',
npcActivityDetails: 'NPC-Aktivität Details',
activityType: {
recycle: ''
recycle: 'Trümmer recyceln'
},
activityLocation: '',
position: '',
nearPlanet: '',
activityDescription: '',
npcActivityMessage: '',
arrivalTime: '',
npcActivityTip: '',
activityLocation: 'Aktivitätsort',
position: 'Position',
nearPlanet: 'In der Nähe von Planet',
activityDescription: 'Aktivitätsbeschreibung',
npcActivityMessage: '{npc} {activity} bei {position}',
arrivalTime: 'Ankunftszeit',
npcActivityTip:
'NPCs können Trümmer aus Kämpfen sammeln. Du kannst versuchen, den Ort zuerst zu erreichen, wenn du um Ressourcen konkurrieren möchtest',
clearMessages: 'Nachrichten löschen',
clearMessageTypes: 'Nachrichtentypen zum Löschen auswählen',
clearBattleReports: 'Kampfberichte',
@@ -852,7 +830,11 @@ export default {
clearNPCActivity: 'NPC-Aktivität',
clearGiftNotifications: 'Geschenkbenachrichtigungen',
clearGiftRejected: 'Abgelehnte Geschenke',
clearNow: 'Jetzt löschen'
clearTradeOffers: 'Handelsangebote',
clearIntelReports: 'Geheimdienstberichte',
clearJointAttackInvites: 'Gemeinsame Angriffseinladungen',
clearNow: 'Jetzt löschen',
clearSuccess: 'Nachrichten gelöscht'
},
missionReports: {
transportSuccess: 'Transportmission erfolgreich abgeschlossen',
@@ -963,7 +945,6 @@ export default {
resume: 'Fortsetzen',
gamePaused: 'Spiel pausiert',
gameResumed: 'Spiel fortgesetzt',
playerName: 'Spielername',
gameSpeed: 'Ressourcenproduktionsgeschwindigkeit',
gameSpeedDesc: 'Aktueller Ressourcenproduktionsgeschwindigkeitsmultiplikator',
speedChanged: 'Ressourcenproduktionsgeschwindigkeit auf {speed}x geändert',
@@ -971,17 +952,18 @@ export default {
reset: 'Zurücksetzen',
about: 'Über',
version: 'Version',
latestVersion: 'Neueste Version',
checkUpdate: 'Update prüfen',
checking: 'Prüfen...',
newVersionAvailable: 'Neue Version {version} verfügbar',
upToDate: 'Bereits auf dem neuesten Stand',
checkUpdateCooldown: 'Bitte versuchen Sie es später erneut (5 Minuten Abklingzeit)',
checkUpdateFailed: 'Update-Prüfung fehlgeschlagen, bitte überprüfen Sie Ihre Netzwerkverbindung',
viewUpdate: 'Update ansehen',
updateAvailable: 'Eine neue Version ist verfügbar. Klicken Sie, um die Versionshinweise anzuzeigen.',
download: 'Herunterladen',
goToDownload: 'Zum Download',
displaySettings: 'Anzeigeeinstellungen',
displaySettingsDesc: 'Visuelle Effekte des Spiels anpassen',
backgroundAnimation: 'Hintergrundanimation',
backgroundAnimationDesc: 'Sternenhimmel-/Partikel-Hintergrundanimation anzeigen (kann die Leistung beeinträchtigen)',
buildDate: 'Build-Datum',
community: 'Community',
github: 'GitHub-Repository',
@@ -1004,13 +986,53 @@ export default {
expandTypes: 'Details anzeigen',
collapseTypes: 'Details ausblenden',
// NPC-Namen-Update
npcNameUpdate: 'NPC-Namen-Update',
npcNameUpdateTitle: 'Alte NPC-Namen erkannt',
npcNameUpdateMessage: '{count} NPCs mit altem Namensformat gefunden. Möchten Sie auf neue lokalisierte Namen aktualisieren?',
npcNameUpdateConfirm: 'Namen aktualisieren',
npcNameUpdateCancel: 'Beibehalten',
npcNameUpdateSuccess: '{count} NPC-Namen erfolgreich aktualisiert',
npcNameUpdateSkipped: 'NPC-Namen-Update übersprungen'
npcNameUpdateSkipped: 'NPC-Namen-Update übersprungen',
// WebDAV
webdav: {
title: 'Cloud-Sync',
desc: 'Spielstände über WebDAV synchronisieren',
config: 'Konfigurieren',
configTitle: 'WebDAV-Konfiguration',
configDesc: 'WebDAV-Server für Cloud-Sync konfigurieren',
notConfigured: 'Bitte zuerst WebDAV-Server konfigurieren',
serverUrl: 'Server-URL',
serverUrlPlaceholder: 'z.B. https://dav.example.com',
serverUrlHint: 'Gib deine WebDAV-Server-Adresse ein',
username: 'Benutzername',
usernamePlaceholder: 'Benutzername eingeben',
password: 'Passwort',
passwordPlaceholder: 'Passwort eingeben',
passwordHint: 'Passwort wird nur lokal gespeichert',
basePath: 'Speicherpfad',
basePathPlaceholder: 'z.B. /ogame-saves/',
testConnection: 'Verbindung testen',
testing: 'Teste...',
testSuccess: 'Verbindung erfolgreich',
testFailed: 'Verbindung fehlgeschlagen',
save: 'Speichern',
clearConfig: 'Löschen',
configSaved: 'Konfiguration gespeichert',
configCleared: 'Konfiguration gelöscht',
upload: 'Hochladen',
uploading: 'Lädt hoch...',
uploadSuccess: 'Upload erfolgreich',
uploadFailed: 'Upload fehlgeschlagen',
download: 'Herunterladen',
downloadSuccess: 'Download erfolgreich',
downloadFailed: 'Download fehlgeschlagen',
selectFile: 'Spielstand wählen',
selectFileDesc: 'Wähle eine Spielstand-Datei zum Wiederherstellen',
noFiles: 'Keine Spielstände gefunden',
loadFailed: 'Dateiliste konnte nicht geladen werden',
confirmDelete: 'Möchtest du "{name}" wirklich löschen?',
deleteSuccess: 'Datei gelöscht',
deleteFailed: 'Löschen fehlgeschlagen'
}
},
notifications: {
constructionComplete: 'Bau abgeschlossen',
@@ -1032,8 +1054,8 @@ export default {
officers: 'Offiziere',
modifyResources: 'Ressourcen ändern',
resourcesDesc: 'Planetenressourcen schnell ändern',
maxAllResources: '',
maxAllResourcesSuccess: '',
maxAllResources: 'Alle maximieren',
maxAllResourcesSuccess: 'Alle Ressourcen wurden maximiert',
modifyBuildings: 'Gebäude ändern',
buildingsDesc: 'Gebäudelevel schnell festlegen',
modifyResearch: 'Forschung ändern',
@@ -1075,10 +1097,11 @@ export default {
dangerZoneDesc: 'Die folgenden Vorgänge sind irreversibel',
resetGame: 'Spiel zurücksetzen',
resetGameConfirm: 'Möchten Sie das Spiel wirklich zurücksetzen? Alle Daten werden gelöscht!',
completeAllQueues: '',
completeAllQueuesDesc: '',
completeQueues: '',
completeQueuesSuccess: ''
completeAllQueues: 'Alle Warteschlangen abschließen',
completeAllQueuesDesc: 'Alle Bau-, Forschungs-, Schiffs- und Verteidigungswarteschlangen sowie Flottenmissionen sofort abschließen',
completeQueues: 'Warteschlangen abschließen',
completeQueuesSuccess:
'{buildingCount} Bauwarteschlangen, {researchCount} Forschungswarteschlangen, {missionCount} Flottenmissionen, {missileCount} Raketenangriffe abgeschlossen'
},
alerts: {
incomingFleets: '{count} feindliche Flotten im Anmarsch',
@@ -1109,11 +1132,13 @@ export default {
missionType: {
spy: 'Spionage',
attack: 'Angriff',
recycle: 'Recyceln',
unknown: 'Unbekannt'
},
warning: {
spy: 'Feindliche Spionage im Anmarsch!',
attack: 'Feindlicher Angriff im Anmarsch!',
recycle: 'Feind recycelt Trümmer in deiner Nähe!',
unknown: 'Feindliche Flotte im Anmarsch!'
}
},
@@ -1130,13 +1155,11 @@ export default {
noFriendlyNpcs: 'Keine freundlichen NPCs',
noNeutralNpcs: 'Keine neutralen NPCs',
noHostileNpcs: 'Keine feindlichen NPCs',
recentEvents: 'Aktuelle Ereignisse',
recentEventsDescription: 'Protokoll der jüngsten diplomatischen Aktivitäten',
ago: 'vor',
notifications: '',
markAllRead: '',
noReports: '',
viewAll: '',
notifications: 'Diplomatische Benachrichtigungen',
markAllRead: 'Alle als gelesen markieren',
noReports: 'Keine diplomatischen Ereignisse',
viewAll: 'Alle anzeigen',
status: {
friendly: 'Freundlich',
neutral: 'Neutral',
@@ -1155,7 +1178,6 @@ export default {
},
note: 'Notiz',
notePlaceholder: 'Notiz eingeben...',
noteEmpty: 'Keine Notiz',
lastEvent: 'Letztes Ereignis',
reportDetails: 'Diplomatischer Bericht Details',
eventDescription: 'Ereignisbeschreibung',
@@ -1211,6 +1233,19 @@ export default {
npcEliminatedMessage: 'Sie haben alle Planeten von {npcName} zerstört! Diese Fraktion wurde vollständig ausgelöscht.'
},
searchPlaceholder: 'NPC-Name suchen...',
notificationType: {
tradeOffer: 'Handelsangebot',
intelReport: 'Geheimdienstbericht',
jointAttack: 'Gemeinsame Angriffseinladung'
},
notificationBadge: {
trade: 'Handel',
intel: 'Intel',
jointAttack: 'Einladung'
},
notificationExtra: {
pending: 'Ausstehend'
},
viewMode: {
card: 'Karte',
list: 'Liste'
@@ -1227,6 +1262,21 @@ export default {
medium: 'Mittel',
hard: 'Schwer'
},
aiType: 'KI-Typ',
aiTypes: {
aggressive: 'Aggressiv',
defensive: 'Defensiv',
trader: 'Händler',
expansionist: 'Expansionist',
balanced: 'Ausgewogen'
},
aiTypeDescriptions: {
aggressive: 'Spioniert und greift aktiv an, starke Vergeltung',
defensive: 'Greift selten an, starke Vergeltung bei Angriff',
trader: 'Greift fast nie an, bevorzugt Handel und Geschenke',
expansionist: 'Konzentriert auf Entwicklung, weniger aggressiv',
balanced: 'Passt Strategie dynamisch an die Situation an'
},
reputation: 'Ansehen',
spyProbes: 'Spionagesonden',
fleetPower: 'Flottenstärke',
@@ -1253,27 +1303,11 @@ export default {
attackCooldown: 'Angriff auf Abklingzeit ({min}m {sec}s)',
notSpiedYet: 'Noch nicht spioniert, zuerst Spionage nötig'
}
},
aiType: 'KI-Typ',
aiTypes: {
aggressive: 'Aggressiv',
defensive: 'Defensiv',
trader: 'Händler',
expansionist: 'Expansionist',
balanced: 'Ausgewogen'
},
aiTypeDescriptions: {
aggressive: 'Spioniert und greift aktiv an, starke Vergeltung',
defensive: 'Greift selten an, starke Vergeltung bei Angriff',
trader: 'Greift fast nie an, bevorzugt Handel und Geschenke',
expansionist: 'Konzentriert auf Entwicklung, weniger aggressiv',
balanced: 'Passt Strategie dynamisch an die Situation an'
}
},
pagination: {
previous: 'Vorherige',
next: 'Nächste',
gotIt: '',
first: 'Erste',
last: 'Letzte',
page: 'Seite {page}'
@@ -1337,30 +1371,132 @@ export default {
seconds: 'Sekunden'
},
tutorial: {
progress: 'Fortschritt',
previous: 'Zurück',
next: 'Weiter',
gotIt: 'Verstanden',
completeButton: 'Fertig',
skip: 'Anleitung überspringen',
welcome: {
title: 'Willkommen bei OGame',
content: 'Willkommen, Kommandant! Beginnen wir mit den Grundlagen und bauen Sie Ihr Weltraum-Imperium auf.'
content: 'Willkommen, Kommandant! Diese Anleitung führt Sie durch die Grundlagen des Imperiumsaufbaus. Klicken Sie auf "Weiter", um Ihre Reise zu beginnen.'
},
resources: {
title: 'Ressourcenübersicht',
content: 'Dies sind Ihre Ressourcen: Metall, Kristall und Deuterium. Sie sind für den Bau von Gebäuden und die Erforschung von Technologien unerlässlich. Energie ist ebenfalls wichtig, um Ihre Infrastruktur zu betreiben.'
},
planet: {
title: 'Ihr Planet',
content: 'Dies ist Ihr Heimatplanet. Hier sehen Sie den Planetennamen, die Koordinaten und können zwischen Planeten wechseln, wenn Sie Ihr Imperium erweitern.'
},
navigation: {
title: 'Navigationsmenü',
content: 'Verwenden Sie dieses Menü, um zwischen verschiedenen Bereichen zu navigieren: Gebäude, Forschung, Flotte, Galaxie usw. Jeder Bereich bietet einzigartige Spielfunktionen.'
},
gotoBuildings: {
title: 'Zu Gebäuden gehen',
content: 'Beginnen wir mit dem Bau einiger Gebäude. Klicken Sie auf den Menüpunkt "Gebäude", um verfügbare Gebäude anzuzeigen.'
},
buildSolarPlant: {
title: 'Solarkraftwerk bauen',
content:
'Bauen Sie zuerst ein Solarkraftwerk! Es liefert Energie für Ihren Planeten. Ohne Energie können andere Ressourcengebäude nicht funktionieren. Dies ist der wichtigste erste Schritt.'
content: 'Bauen Sie zuerst ein Solarkraftwerk! Es versorgt Ihren Planeten mit Energie. Ohne Energie können andere Ressourcengebäude nicht funktionieren. Dies ist der wichtigste erste Schritt.'
},
waitBuild: {
title: 'Bauauftrag',
content:
'Ihr Gebäude befindet sich jetzt in der Bauauftragsliste. Klicken Sie auf das Warteschlangensymbol oben rechts, um alle laufenden Bau- und Forschungsaufträge anzuzeigen. Gebäude brauchen Zeit zum Fertigstellen, aber Sie können während des Wartens weitermachen.'
content: 'Ihr Gebäude befindet sich jetzt im Bauauftrag. Klicken Sie auf das Warteschlangensymbol oben rechts, um alle laufenden Bau- und Forschungsaufgaben anzuzeigen. Gebäude brauchen Zeit zur Fertigstellung, aber Sie können während des Wartens weitermachen.'
},
buildMetalMine: {
title: 'Metallmine bauen',
content: 'Jetzt, da Sie Energie haben, bauen Sie eine Metallmine. Die Metallmine ist Ihre Hauptmetallquelle, und Metall wird für fast jedes Gebäude und Schiff benötigt.'
},
buildCrystalMine: {
title: 'Kristallmine bauen',
content: 'Kristall ist seltener, aber für fortgeschrittene Technologien entscheidend. Bauen Sie eine Kristallmine, um diese wertvolle Ressource zu sammeln.'
},
buildDeuterium: {
title: 'Deuteriumsynthesizer bauen',
content: 'Deuterium ist für Schiffstreibstoff und fortgeschrittene Forschung unerlässlich. Bauen Sie einen Deuteriumsynthesizer, um diese wichtige Ressource zu produzieren.'
},
upgradeMines: {
title: 'Ressourcenminen upgraden',
content: 'Als Nächstes müssen Sie die drei Ressourcenminen (Metall, Kristall, Deuterium) auf Level 2 upgraden, um die Anforderungen für den Bau der Roboterfabrik zu erfüllen. Upgraden Sie sie weiter, wenn Sie genügend Ressourcen haben.'
},
buildRobotics: {
title: 'Roboterfabrik bauen',
content: 'Die Roboterfabrik kann die Baugeschwindigkeit erheblich erhöhen. Sie benötigt Metallmine, Kristallmine und Deuteriumsynthesizer jeweils auf Level 2. Bauen Sie sie, um die Baueffizienz zu steigern!'
},
upgradeMinesForLab: {
title: 'Ressourcenminen weiter upgraden',
content: 'Jetzt müssen Sie die drei Ressourcenminen auf Level 3 upgraden, um die Bauanforderungen des Forschungslabors zu erfüllen. Entwickeln Sie Ihre Ressourcenproduktion weiter.'
},
buildResearchLab: {
title: 'Forschungslabor bauen',
content: 'Das Forschungslabor ist die Grundlage für technologischen Fortschritt. Es benötigt alle drei Ressourcenminen auf Level 3. Bauen Sie es, um Technologieforschung freizuschalten!'
},
gotoResearch: {
title: 'Zur Forschung gehen',
content: 'Da Sie nun ein Forschungslabor haben, klicken Sie auf das Menü "Forschung", um verfügbare Technologien anzuzeigen.'
},
researchEnergy: {
title: 'Energietechnik erforschen',
content: 'Die Energietechnik kann Ihre Energieproduktion verbessern und fortgeschrittene Gebäude freischalten. Dies ist eine der grundlegendsten und wichtigsten Technologien.'
},
shipyardIntro: {
title: 'Flotte & Werft',
content: 'Schiffe ermöglichen es Ihnen, die Galaxie zu erkunden, Ressourcen zu transportieren und Ihr Imperium zu verteidigen. Um Schiffe zu bauen, benötigen Sie eine Raumschiffwerft (benötigt Roboterfabrik Level 2).'
},
gotoBuildingsForShipyard: {
title: 'Zurück zur Gebäudeseite',
content: 'Kehren Sie zur Gebäudeseite zurück, um Ihre Raumschiffwerft zu bauen.'
},
buildShipyard: {
title: 'Raumschiffwerft bauen',
content: 'Die Raumschiffwerft ermöglicht es Ihnen, Schiffe und Verteidigungssysteme zu bauen. Dies ist für Flottenoperationen unerlässlich.'
},
fleetIntro: {
title: 'Flottenoperationen',
content: 'Sobald Sie Schiffe haben, können Sie sie auf Missionen schicken: Ressourcen transportieren, Planeten kolonisieren, Feinde angreifen oder Trümmerfelder erkunden.'
},
galaxyIntro: {
title: 'Galaxie erkunden',
content: 'Die Galaxieansicht zeigt andere Planeten, Trümmerfelder und Expansionsmöglichkeiten. Verwenden Sie sie, um Ziele auszukundschaften und Ihre Strategie zu planen.'
},
complete: {
title: 'Anleitung abgeschlossen!',
content: 'Glückwunsch, Kommandant! Sie kennen jetzt die Grundlagen. Bauen Sie Ihr Imperium weiter auf, erforschen Sie Technologien und erkunden Sie die Galaxie. Denken Sie daran: Zuerst Energie entwickeln, dann Ressourcen, dann Fabriken und Forschung! Viel Glück!'
},
mobile: {
welcome: {
title: 'Willkommen bei OGame (Mobil)',
content:
'Willkommen, Kommandant! Dies ist ein optimiertes Tutorial für Touchscreens. Wir werden schnell die Kernfunktionen durchgehen, damit Sie mit dem Aufbau Ihres Imperiums beginnen können.'
content: 'Willkommen, Kommandant! Dies ist eine vereinfachte Anleitung für Touchscreens. Wir werden schnell die Kernfunktionen durchgehen, damit Sie mit dem Aufbau Ihres Imperiums beginnen können.'
},
resources: {
title: 'Obere Ressourcenleiste',
content: 'Oben werden Ihre Ressourcen angezeigt: Metall, Kristall und Deuterium. Tippen Sie, um detaillierte Produktionsinformationen anzuzeigen.'
},
menu: {
title: 'Navigationsmenü öffnen',
content: 'Tippen Sie auf dieses Menüsymbol, um die Navigationsleiste zu öffnen. Sie können auf alle Funktionen zugreifen: Gebäude, Forschung, Flotte usw.'
},
gotoBuildings: {
title: 'Zur Gebäudeseite gehen',
content: 'Das Menü ist jetzt geöffnet! Tippen Sie nun auf die Option "Gebäude", um mit dem Bau der Infrastruktur zu beginnen.'
},
buildSolarPlant: {
title: 'Solarkraftwerk bauen',
content: 'Bauen Sie zuerst ein Solarkraftwerk! Scrollen Sie nach unten, um es zu finden, und tippen Sie auf die Karte zum Bauen. Energie ist die Grundlage für alles.'
},
waitBuild: {
title: 'Bauauftrag',
content:
'Klicken Sie auf das Warteschlangensymbol oben rechts, um den Baufortschritt anzuzeigen. Sie können weiter andere Seiten durchsuchen - der Bau läuft im Hintergrund.'
content: 'Tippen Sie auf das Warteschlangensymbol oben rechts, um den Baufortschritt anzuzeigen. Sie können weiter andere Seiten durchsuchen - der Bau läuft im Hintergrund.'
},
buildMetalMine: {
title: 'Metallmine bauen',
content: 'Nachdem Sie Energie haben, bauen Sie eine Metallmine. Scrollen Sie nach unten, um die Metallmine zu finden, und tippen Sie zum Bauen.'
},
complete: {
title: 'Schnellanleitung abgeschlossen!',
content: 'Sehr gut! Sie haben die Grundlagen gemeistert. Bauen Sie weiter die Kristallmine und den Deuteriumsynthesizer und erkunden Sie andere Funktionen. Denken Sie daran: Zuerst Energie, dann Ressourcen!'
}
}
},
@@ -1427,11 +1563,20 @@ export default {
message:
'Simulieren Sie Kampfergebnisse vor dem Angriff. Geben Sie Flotten und Technologiestufen ein, um Sieg, Verluste und Beute vorherzusagen.'
},
campaign: {
title: 'Kampagnenmodus',
message: 'Erkunden Sie die Galaxie-Story-Kampagne! Schließen Sie Missionen ab, um Ressourcenbelohnungen zu erhalten und neue Herausforderungen freizuschalten. Jeder Knoten hat einzigartige Ziele und Feinde.'
},
achievements: {
title: 'Erfolgssystem',
message:
'Schließen Sie Spielziele ab, um Erfolge freizuschalten und Dunkle Materie-Belohnungen zu erhalten! Erfolge haben mehrere Stufen - streben Sie höhere Herausforderungen an, um bessere Belohnungen zu erhalten.'
},
ranking: {
title: 'Rangliste',
message:
'Vergleichen Sie Ihren Fortschritt mit anderen Spielern und NPCs. Sehen Sie Rankings basierend auf Punkten aus Gebäuden, Forschung, Flotte und Verteidigung. Streben Sie nach höheren Platzierungen!'
},
settings: {
title: 'Einstellungen',
message: 'Verwalten Sie hier Spieldaten, Benachrichtigungen und Import/Export. Sichern Sie regelmäßig Ihren Fortschritt!'
@@ -1557,5 +1702,350 @@ export default {
you: 'Du',
scoreBreakdown: 'Punkteübersicht',
noData: 'Keine Ranglistendaten'
},
// NPC Enhanced Behavior Notifications
npcBehavior: {
tradeOfferReceived: 'Handelsangebot erhalten',
tradeOfferDesc: '{npcName} hat dir ein Handelsangebot gesendet',
attitudeChanged: 'NPC-Haltung geändert',
becameFriendly: '{npcName} ist dir gegenüber freundlich geworden',
becameHostile: '{npcName} ist dir gegenüber feindlich geworden',
intelReceived: 'Informationen erhalten',
intelReceivedDesc: '{npcName} hat Feindinformationen mit dir geteilt',
jointAttackInvite: 'Gemeinsamer Angriffseinladung',
jointAttackInviteDesc: '{npcName} lädt dich ein, gemeinsam einen Feind anzugreifen',
aidReceived: 'Hilfe erhalten',
aidReceivedDesc: '{npcName} hat dir {amount} Ressourcen gesendet',
allyDefense: 'Verbündete Verteidigung',
allyDefenseDesc: '{npcName} sendet eine Flotte, um deinen Planeten zu verteidigen',
trade: {
title: 'Handelsangebote',
from: 'Von',
offers: 'Bietet',
requests: 'Fordert',
expiresIn: 'Läuft ab in',
expired: 'Abgelaufen',
accept: 'Annehmen',
decline: 'Ablehnen',
noOffers: 'Keine Handelsangebote',
acceptSuccess: 'Handel abgeschlossen!',
acceptFailed: 'Nicht genug Ressourcen für den Handel',
declined: 'Handel abgelehnt',
ratio: 'Tauschverhältnis'
},
intel: {
title: 'Geheimdienstberichte',
from: 'Quelle',
target: 'Ziel-NPC',
type: 'Informationstyp',
types: {
enemyFleet: 'Flotteninformation',
enemyResources: 'Ressourceninformation',
enemyMovement: 'Bewegungsinformation'
},
fleetInfo: 'Flotteninformation',
resourceInfo: 'Ressourceninformation',
movementInfo: 'Bewegungsinformation',
noReports: 'Keine Geheimdienstberichte',
markAsRead: 'Als gelesen markieren',
content: 'Informationsinhalt',
noFleet: 'Keine Flotte erkannt',
noData: 'Keine Daten verfügbar',
targetPosition: 'Zielposition',
missionType: 'Missionstyp'
},
jointAttack: {
title: 'Gemeinsame Angriffseinladungen',
from: 'Initiator',
target: 'Ziel-NPC',
targetPlanet: 'Zielplanet',
npcFleet: 'NPC-Flotte',
lootShare: 'Beuteanteil',
expiresIn: 'Läuft ab in',
expired: 'Abgelaufen',
accept: 'Am Angriff teilnehmen',
decline: 'Ablehnen',
noInvites: 'Keine gemeinsamen Angriffseinladungen',
acceptSuccess: 'Dem gemeinsamen Angriff beigetreten!',
declined: 'Einladung abgelehnt',
targetInfo: 'Angriffsziel',
expectedShare: 'Erwarteter Anteil',
remaining: 'Verbleibende Zeit'
},
aid: {
title: 'Ressourcenhilfe',
from: 'Quelle',
resources: 'Hilfsressourcen',
noAid: 'Keine Hilfsaufzeichnungen'
},
attitudeChange: {
title: 'Haltungsänderungen',
npc: 'NPC',
previous: 'Vorher',
current: 'Jetzt',
reason: 'Grund',
reasons: {
naturalSwing: 'Natürliche Veränderung',
giftReceived: 'Geschenk erhalten',
attacked: 'Wurde angegriffen',
reputationThreshold: 'Reputationsschwelle',
attitude_swing: 'Haltungsschwankung',
gift: 'Geschenk erhalten',
attack: 'Angriff erlitten'
}
},
allyAction: {
title: 'Verbündete Aktionen',
defense: 'Verteidigungsunterstützung',
defenseDesc: '{npcName} sendet Flotte zur Verteidigung von {targetPlanet}',
jointAttackStarted: 'Gemeinsamer Angriff gestartet',
jointAttackStartedDesc: 'Gemeinsamer Angriff auf {targetNpc} hat begonnen',
reputationBonus: 'Reputationsbonus',
reputationBonusDesc: 'Dein Verbündeter {npcName} spricht gut von dir zu {targetNpc}'
}
},
campaign: {
name: 'Kampagne',
description: 'Erkunde die mysteriöse Galaxie und entdecke antike Geheimnisse',
totalProgress: 'Gesamtfortschritt',
questsCompleted: 'Quests abgeschlossen',
chapter: 'Kapitel',
branch: 'Zweig',
startQuest: 'Quest starten',
claimRewards: 'Belohnungen abholen',
objectives: 'Ziele',
rewards: 'Belohnungen',
completed: 'Abgeschlossen',
inProgress: 'In Bearbeitung',
available: 'Verfügbar',
locked: 'Gesperrt',
notifications: {
questStarted: 'Quest gestartet',
questCompleted: 'Quest abgeschlossen!',
rewardsClaimed: 'Belohnungen erhalten',
objectiveCompleted: 'Ziel erreicht',
chapterUnlocked: 'Neues Kapitel freigeschaltet',
reputationUp: 'Reputation bei {npcName} um {value} erhöht',
reputationDown: 'Reputation bei {npcName} um {value} verringert',
branchUnlocked: 'Neuer Storyzweig freigeschaltet!'
},
dialogue: {
skip: 'Überspringen',
continue: 'Weiter',
finish: 'Beenden',
player: 'Kommandant',
npc: 'NPC',
narrator: 'Erzähler',
mysterious: 'Mysteriöses Signal',
unknownSource: 'Unbekannte Quelle',
choiceEffect: 'Dialogauswahl-Effekt'
},
chapters: {
'1': {
title: 'Ursprung',
description: 'Baue deine Heimat auf und mache den ersten Schritt ins All',
backgroundStory:
'Du bist ein junger Weltraumkommandant, der gerade seinen ersten Planeten erworben hat. In diesem weiten Universum wirst du deine Heimat aufbauen, Technologie entwickeln und die Tiefen der Galaxie erkunden...'
},
'2': {
title: 'Erkundung',
description: 'Erkunde das Universum und entdecke antike Ruinen',
backgroundStory:
'Während deine Macht wächst, erregen mysteriöse Signale aus dem tiefen Weltraum deine Aufmerksamkeit. Diese Signale scheinen auf ein antikes Geheimnis hinzuweisen, das mutige Entdecker erwartet...'
},
'3': {
title: 'Diplomatie',
description: 'Knüpfe Verbindungen mit anderen Fraktionen',
backgroundStory:
'Du bist nicht allein in der Galaxie. Andere Zivilisationen erheben sich. Du musst entscheiden, ob du ihr Feind oder Verbündeter sein willst. Diplomatische Weisheit wird bestimmen, wie weit dein Imperium gehen kann...'
},
'4': {
title: 'Aufziehende Schatten',
description: 'Stelle dich mächtigen Feinden und verteidige dein Territorium',
backgroundStory:
'Gefahr lauert in den Schatten. Eine mächtige feindliche Macht hat dein Territorium ins Visier genommen. Krieg ist unvermeidlich. Du musst dich auf den kommenden Sturm vorbereiten...'
},
'5': {
title: 'Antike Geheimnisse',
description: 'Enthülle die tiefsten Geheimnisse der Galaxie',
backgroundStory:
'Alle Hinweise deuten auf die mysteriöseste Region der Galaxie. Dort warten die ultimativen Geheimnisse antiker Zivilisationen. Bist du bereit, alles zu enthüllen?'
}
},
quests: {
'1_1': { title: 'Heimataufbau', description: 'Baue Infrastruktur auf, um die Grundlage für deinen Planeten zu legen' },
'1_2': { title: 'Technische Erleuchtung', description: 'Erforsche Basistechnologie, um deine technische Reise zu beginnen' },
'1_3': { title: 'Erstes Schiff', description: 'Baue dein erstes Kriegsschiff' },
'1_4': { title: 'Seltsame Nachbarn', description: 'Erkunde andere Fraktionen in nahen Systemen' },
'1_5': { title: 'Erster Kontakt', description: 'Stelle ersten Kontakt mit nahen NPC-Fraktionen her' },
'2_1': { title: 'Pionierkolonie', description: 'Kolonisiere deinen ersten neuen Planeten' },
'2_2': { title: 'Tiefraum-Expedition', description: 'Sende Flotte auf Expeditionsmissionen' },
'2_3': { title: 'Mysteriöses Signal', description: 'Untersuche mysteriöse Signale aus dem tiefen Weltraum' },
'2_4': { title: 'Ruinenuntersuchung', description: 'Erkunde entdeckte antike Ruinen' },
'2_5': { title: 'Archive entschlüsseln', description: 'Erforsche Daten aus den Ruinen' },
'3_1': { title: 'Friedensstifter', description: 'Verbessere Beziehungen zu NPCs durch Diplomatie' },
'3_2': { title: 'Handelsbeziehungen', description: 'Baue stabile Beziehungen zu freundlichen Fraktionen auf' },
'3_3': { title: 'Gemeinsame Bedrohung', description: 'Entdecke potenzielle feindliche Kräfte' },
'3_4': { title: 'Allianzverhandlungen', description: 'Schließe eine formelle Allianz mit freundlichen NPCs' },
'3_5': { title: 'Sturmvorbereitung', description: 'Baue Verteidigungen zur Vorbereitung auf Herausforderungen' },
'4_1': { title: 'Außenpostenangriff', description: 'Wehre den ersten Angriff feindlicher Kräfte ab' },
'4_2': { title: 'Informationsbeschaffung', description: 'Erkunde feindliche Militäraufstellungen' },
'4_3': { title: 'Gegenangriff', description: 'Starte einen Gegenangriff gegen den Feind' },
'4_4': { title: 'Ressourcenkampf', description: 'Recycel Schlachtfeldtrümmer für Ressourcen' },
'4_5': { title: 'Vorabend der Schlacht', description: 'Baue eine mächtige Flotte für die letzte Schlacht' },
'5_1': { title: 'Tiefen der Ruinen', description: 'Erkunde die tiefsten Teile der Ruinen' },
'5_2': { title: 'Antike Technologie', description: 'Schalte Technologie antiker Zivilisationen frei' },
'5_3': { title: 'Letzte Konfrontation', description: 'Stelle dich dem letzten Kampf gegen den mysteriösen Feind' },
'5_4': { title: 'Neue Ära', description: 'Gründe neue Kolonien und beginne ein neues Zeitalter' },
'5_5': { title: 'Vermächtnis', description: 'Entwickle weiter und erobere mehr Systeme' }
},
objectiveTypes: {
buildBuilding: 'Baue {building} auf Stufe {level}',
researchTech: 'Erforsche {tech} auf Stufe {level}',
produceShips: 'Produziere {count} {ship}',
accumulateResources: 'Sammle {amount} {resource}',
defeatNPC: 'Besiege {npc}',
winBattles: 'Gewinne {count} Kämpfe',
recycleDebris: 'Recycel {amount} Trümmer',
reachRelation: 'Erreiche {level} Beziehung mit {npc}',
sendGift: 'Sende {count} Geschenke an {npc}',
formAlliance: 'Schließe Allianz mit {npc}',
colonize: 'Kolonisiere {count} Planeten',
expedition: 'Schließe {count} Expeditionen ab',
spyTarget: 'Spioniere {target} aus'
},
errors: {
questNotFound: 'Quest nicht gefunden',
questNotAvailable: 'Quest nicht verfügbar',
questNotActive: 'Quest nicht aktiv',
questNotCompleted: 'Quest nicht abgeschlossen',
rewardsAlreadyClaimed: 'Belohnungen bereits erhalten',
prerequisiteNotMet: 'Voraussetzungs-Quest nicht abgeschlossen'
},
speakers: {
ancientVoice: 'Antike Stimme',
neighborNPC: 'Nachbar-Fraktion',
mysteriousSignal: 'Mysteriöses Signal',
enemyCommander: 'Feindlicher Kommandant'
},
objectiveDescriptions: {
buildMetalMine: 'Baue Metallmine auf Stufe 2',
buildCrystalMine: 'Baue Kristallmine auf Stufe 2',
buildSolarPlant: 'Baue Solarkraftwerk auf Stufe 2',
buildResearchLab: 'Baue Forschungslabor auf Stufe 1',
researchEnergy: 'Erforsche Energietechnik auf Stufe 1',
buildShipyard: 'Baue Raumschiffwerft auf Stufe 2',
researchCombustion: 'Erforsche Verbrennungstriebwerk auf Stufe 1',
buildLightFighters: 'Baue 5 Leichte Jäger',
researchEspionage: 'Erforsche Spionagetechnik auf Stufe 2',
buildSpyProbes: 'Baue 3 Spionagesonden',
spyAnyNPC: 'Spioniere einen NPC-Planeten aus',
sendGiftToNPC: 'Sende ein Geschenk an einen NPC',
researchAstrophysics: 'Erforsche Astrophysik auf Stufe 1',
buildColonyShip: 'Baue ein Kolonieschiff',
colonizeNewPlanet: 'Kolonisiere einen neuen Planeten',
completeExpedition: 'Schließe eine Expeditionsmission ab',
discoverRuins: 'Entdecke antike Ruinen',
researchComputer: 'Erforsche Computertechnik auf Stufe 4',
improveRelation: 'Verbessere Beziehungen zu einem NPC',
reachFriendly: 'Erreiche freundlichen Status mit einem NPC',
spyHostileNPC: 'Spioniere einen feindlichen NPC aus',
formAlliance: 'Schließe Allianz mit einem freundlichen NPC',
buildDefenses: 'Baue Verteidigungsanlagen',
winDefenseBattle: 'Gewinne eine Verteidigungsschlacht',
spyEnemyPlanet: 'Spioniere Feindplaneten aus',
attackEnemy: 'Greife den Feind an',
recycleDebris: 'Recycel Trümmer',
buildBattleships: 'Baue 10 Schlachtschiffe',
exploreDeepRuins: 'Erkunde tiefe Ruinen',
researchHyperspace: 'Erforsche Hyperraumtechnologie',
defeatBoss: 'Besiege den mysteriösen Feind',
colonizeSpecial: 'Kolonisiere besonderen Standort',
continueDevelopment: 'Setze Entwicklung fort'
},
dialogues: {
'1_1': {
prologue_1:
'Willkommen in der Galaxie, junger Kommandant. Dieses weite Universum wartet auf deine Erkundung. Zuerst bauen wir deinen Heimatplaneten auf.',
prologue_2: 'Ich spüre ein neues Bewusstsein erwachen... Interessant... Mal sehen, wie weit du kommst...'
},
'1_2': {
prologue_1:
'Grundlegende Infrastruktur ist fertig. Jetzt ist es Zeit, Technologie zu entwickeln. Baue ein Forschungslabor und beginne deine technische Reise.'
},
'1_3': {
prologue_1:
'Mit technologischer Unterstützung kannst du beginnen, deine Flotte aufzubauen. Baue eine Werft und produziere dein erstes Kriegsschiff.'
},
'1_4': {
prologue_1:
'Deine Flotte nimmt Form an. Jetzt lass uns deine Umgebung kennenlernen. Sende Spionagesonden aus, um nahe Fraktionen zu erkunden.',
prologue_2: 'Du bist nicht allein... Andere Zivilisationen existieren in dieser Galaxie...'
},
'1_5': {
prologue_1: 'Du hast nahe Fraktionen entdeckt. Diplomatie ist eine Kunst. Versuche, Kontakt mit ihnen aufzunehmen.',
epilogue_1: 'Danke für dein Geschenk, Kommandant. Ich hoffe, wir können Freunde werden.',
epilogue_2: 'Gut... Verbindungen aufzubauen ist der erste Schritt, tiefere Geheimnisse zu enthüllen...'
},
'2_1': {
prologue_1:
'Deine Macht ist etabliert. Es ist Zeit, dein Territorium zu erweitern. Erforsche Astrophysik, baue ein Kolonieschiff und erkunde neue Planeten.',
prologue_2: 'Das Universum ist unendlich... Mehr Planeten bedeuten mehr Möglichkeiten...'
},
'2_2': {
prologue_1: 'Kolonisierung erfolgreich! Aber tiefere Geheimnisse warten im Universum. Sende deine Flotte auf Expeditionsmissionen.',
prologue_2: 'Schwache Signale aus der Ferne... Etwas erwartet dich dort...'
},
'2_3': {
prologue_1:
'Deine Expedition entdeckte anomale Signale. Diese Signale scheinen von einer antiken Zivilisation zu stammen... Untersuche ihre Quelle.',
epilogue_1: 'Diese Symbole... Sie sind Ruinen einer antiken Zivilisation! Untersuche weiter, um ihre Geheimnisse zu enthüllen.'
},
'2_4': {
prologue_1: 'Du hast den Standort antiker Ruinen gefunden. Sende deine Flotte zur Erkundung und sieh, was du entdecken kannst.'
},
'2_5': {
prologue_1: 'Datenarchive wurden in den Ruinen gefunden. Studiere diese Daten, vielleicht kannst du neue Technologie freischalten.'
},
'3_1': { prologue_1: 'Beim Erkunden vergiss nicht die Diplomatie. Gute Beziehungen zu umliegenden Fraktionen zu pflegen nützt dir.' },
'3_2': {
prologue_1:
'Einige Fraktionen haben Freundlichkeit gezeigt. Vertiefe die Beziehungen weiter, vielleicht erhältst du mehr Unterstützung.'
},
'3_3': {
prologue_1:
'Geheimdienstberichte deuten darauf hin, dass feindliche Kräfte dich aus den Schatten beobachten. Bleibe wachsam und erkunde ihre Bewegungen.'
},
'3_4': {
prologue_1: 'Schließe eine formelle Allianz mit freundlichen Fraktionen, um euch gegenseitig gegen Bedrohungen zu unterstützen.'
},
'3_5': { prologue_1: 'Bedrohungen nähern sich. Baue Verteidigungsanlagen und bereite dich auf mögliche Konflikte vor.' },
'4_1': {
prologue_1: 'Der Feind hat einen Angriff gestartet! Verteidige deinen Planeten!',
epilogue_1: 'Du hast die erste Welle des Feindes erfolgreich abgewehrt. Aber das ist erst der Anfang...'
},
'4_2': {
prologue_1: 'Der Feind hat sich zurückgezogen, aber er wird zurückkehren. Erkunde ihre Planeten, um ihre Stärke zu verstehen.'
},
'4_3': { prologue_1: 'Es ist Zeit zurückzuschlagen. Greife die feindlichen Planeten an und schwäche ihre Kräfte.' },
'4_4': {
prologue_1: 'Viele Trümmer bleiben auf dem Schlachtfeld. Recycel diese Ressourcen, um dich auf die nächste Schlacht vorzubereiten.'
},
'4_5': { prologue_1: 'Die letzte Schlacht naht. Baue eine mächtige Flotte und bereite dich auf die ultimative Herausforderung vor.' },
'5_1': {
prologue_1: 'Alle Hinweise deuten auf den tiefsten Teil der Ruinen. Die Kerngeheimnisse der antiken Zivilisation liegen dort.',
prologue_2: 'Du bist endlich angekommen... Die Wahrheit wird bald enthüllt...'
},
'5_2': { prologue_1: 'In den Tiefen der Ruinen entdecktest du verlorene antike Technologie. Erforsche und schalte ihre Macht frei.' },
'5_3': {
prologue_1: 'Ein mysteriöser Feind ist aufgetaucht. Dies ist die letzte Herausforderung. Besiege ihn!',
epilogue_1: 'Du hast es geschafft! Der antike Wächter wurde besiegt. Die Geheimnisse der Galaxie stehen dir nun offen.'
},
'5_4': { prologue_1: 'Frieden ist endlich eingekehrt. In dieser neuen Ära gründe neue Kolonien und erweitere dein Imperium.' },
'5_5': {
prologue_1: 'Deine Legende hat gerade erst begonnen. Erkunde weiter und erobere mehr Sternensysteme!',
epilogue_1: 'Die Galaxie ist weit und grenzenlos, mit zahllosen Geheimnissen, die auf dich warten...'
}
}
}
}

View File

@@ -11,28 +11,10 @@ export default {
common: {
confirm: 'Confirm',
cancel: 'Cancel',
delete: 'Delete',
edit: 'Edit',
save: 'Save',
close: 'Close',
back: 'Back',
next: 'Next',
previous: 'Previous',
submit: 'Submit',
reset: 'Reset',
search: 'Search',
filter: 'Filter',
loading: 'Loading...',
noData: 'No Data',
error: 'Error',
success: 'Success',
warning: 'Warning',
info: 'Info',
resourceType: 'Resource Type',
playerName: 'Commander',
timeHour: 'h',
timeMinute: 'm',
timeSecond: 's',
featureLocked: 'Feature Locked',
unlockRequired: 'Building Required',
requiredBuilding: 'Required Building',
@@ -40,7 +22,6 @@ export default {
goToBuildings: 'Go to Buildings',
locked: 'Locked',
viewRequirements: 'View Requirements',
requirements: 'Requirements',
requirementsNotMet: 'Requirements Not Met',
current: 'Current',
level: 'Level',
@@ -50,7 +31,8 @@ export default {
viewDetails: 'View Details',
exitConfirmTitle: 'Exit Game',
exitConfirmMessage: 'Are you sure you want to exit? Your progress is saved automatically.',
points: 'Points'
points: 'Points',
retry: 'Retry'
},
errors: {
requirementsNotMet: 'Requirements not met',
@@ -94,7 +76,6 @@ export default {
gm: 'GM'
},
sidebar: {
language: 'Language',
lightMode: 'Light Mode',
darkMode: 'Dark Mode',
collapse: 'Collapse',
@@ -119,9 +100,6 @@ export default {
},
energy: {
lowWarning: 'Energy deficit! Resource production stopped!',
severeWarning: 'Energy deficit! Resource production stopped!',
criticalWarning: 'Energy deficit! Resource production stopped!',
noProduction: 'Energy deficit! Resource production stopped!',
deficitDetail: 'Energy deficit: {deficit}, build more power plants',
buildSolarPlant: 'Build Power Plant'
},
@@ -132,27 +110,20 @@ export default {
lowResources: 'Running low: {resources}'
},
planet: {
planet: 'Planet',
moon: 'Moon',
colony: 'Colony',
position: 'Position',
coordinates: 'Coordinates',
switchToMoon: 'View Moon',
backToPlanet: 'Back to Planet',
switchPlanet: 'Switch Planet',
currentPlanet: 'Current Planet',
fields: 'Fields',
temperature: 'Temperature',
homePlanet: 'Home Planet',
planetPrefix: 'Planet',
moonSuffix: "'s Moon",
colonyPrefix: 'Colony',
renamePlanet: 'Rename Planet',
renamePlanetTitle: 'Rename Planet',
newPlanetName: 'New Name',
planetNamePlaceholder: 'Enter new planet name',
rename: 'Rename',
renameSuccess: 'Planet renamed to {name}'
rename: 'Rename'
},
player: {
points: 'Total Points'
@@ -179,12 +150,11 @@ export default {
sensorPhalanx: 'Sensor Phalanx',
jumpGate: 'Jump Gate',
planetDestroyerFactory: 'Planet Destroyer Factory',
geoResearchStation: 'Geological Research Station',
deepDrillingFacility: 'Deep Drilling Facility',
buildTime: 'Build Time',
production: 'Production',
consumption: 'Consumption',
totalCost: 'Total Cost',
totalPoints: 'Total Points',
levelRange: 'Level Range',
storageCapacity: 'Capacity',
energyProduction: 'Energy Production',
@@ -192,7 +162,6 @@ export default {
buildQueueBonus: 'Build Queue',
spaceBonus: 'Space Bonus',
buildSpeedBonus: 'Build Speed Bonus',
researchSpeedBonus: 'Research Speed Bonus',
missileCapacity: 'Missile Capacity',
@@ -225,7 +194,9 @@ export default {
lunarBase: 'Increases available space on the moon, +30 space per level',
sensorPhalanx: 'Detects fleet activities in surrounding systems',
jumpGate: 'Instantly transfers fleets to other moons',
planetDestroyerFactory: 'Constructs ultimate weapons capable of destroying planets'
planetDestroyerFactory: 'Constructs ultimate weapons capable of destroying planets',
geoResearchStation: 'Researches geological structures and increases ore deposit regeneration rate. +50% regeneration per level',
deepDrillingFacility: 'Drills deep into the crust to access deeper ore veins. +20% ore deposit capacity per level'
},
ships: {
lightFighter: 'Light Fighter',
@@ -289,9 +260,6 @@ export default {
},
research: {
researchTime: 'Research Time',
totalCost: 'Total Cost',
totalPoints: 'Total Points',
levelRange: 'Level Range',
attackBonus: 'Attack Bonus',
shieldBonus: 'Shield Bonus',
@@ -300,8 +268,7 @@ export default {
researchQueueBonus: 'Research Queue',
colonySlots: 'Colony Slots',
forAllPlanets: '(Global)',
speedBonus: 'Speed Bonus',
researchSpeedBonus: 'Research Speed Bonus'
speedBonus: 'Speed Bonus'
},
technologies: {
energyTechnology: 'Energy Technology',
@@ -321,7 +288,8 @@ export default {
hyperspaceDrive: 'Hyperspace Drive',
darkMatterTechnology: 'Dark Matter Technology',
terraformingTechnology: 'Terraforming Technology',
planetDestructionTech: 'Planet Destruction Technology'
planetDestructionTech: 'Planet Destruction Technology',
miningTechnology: 'Mining Technology'
},
technologyDescriptions: {
energyTechnology: 'Improves energy efficiency',
@@ -342,7 +310,8 @@ export default {
hyperspaceDrive: 'Advanced propulsion technology',
darkMatterTechnology: 'Research into dark matter properties and applications',
terraformingTechnology: 'Research planet terraforming technology, adds 30 available space to all planets per level',
planetDestructionTech: 'Terrifying technology for destroying entire planets'
planetDestructionTech: 'Terrifying technology for destroying entire planets',
miningTechnology: 'Improves mining methods and equipment, increases ore deposit capacity on all planets. +15% capacity per level'
},
officers: {
commander: 'Commander',
@@ -399,14 +368,9 @@ export default {
movedToQueue: 'Task moved to queue'
},
overview: {
title: 'Planet Overview',
resourceOverview: 'Resources',
fleetInfo: 'Fleet',
currentShips: 'Ships on this planet',
productionSources: 'Production Sources',
productionSourcesDesc: 'Detailed resource production and bonus information',
consumptionSources: 'Consumption Sources',
consumptionSourcesDesc: 'Energy consumption details for buildings',
totalProduction: 'Total Production',
totalConsumption: 'Total Consumption',
noConsumption: 'No energy consumption',
@@ -416,12 +380,8 @@ export default {
},
buildingsView: {
title: 'Buildings',
usedSpace: 'Used Space',
spaceUsage: 'Space Usage',
level: 'Level',
upgradeCost: 'Upgrade Cost',
buildTime: 'Build Time',
build: 'Build',
upgrade: 'Upgrade',
maxLevelReached: 'Max Level Reached',
requirementsNotMet: 'Requirements Not Met',
@@ -431,8 +391,7 @@ export default {
demolishRefund: 'Demolish Refund',
demolishFailed: 'Demolish Failed',
demolishFailedMessage: 'Unable to demolish this building. Please check if the build queue is full or the building level is 0.',
confirmDemolish: 'Confirm Demolish',
confirmDemolishMessage: 'Are you sure you want to demolish'
confirmDemolish: 'Confirm Demolish'
},
researchView: {
title: 'Research',
@@ -443,25 +402,14 @@ export default {
researchFailedMessage: 'Please check if you have enough resources, prerequisites are met, or if there are other research tasks.'
},
shipyard: {
attack: 'Attack',
missileAttack: 'Missile Attack',
shield: 'Shield',
armor: 'Armor',
speed: 'Speed',
cargoCapacity: 'Cargo Capacity',
fuelConsumption: 'Fuel Consumption',
buildCost: 'Build Cost',
buildTime: 'Build Time',
perUnit: 'Per Unit',
batchCalculator: 'Batch Calculator',
quantity: 'Quantity',
totalCost: 'Total Cost',
totalTime: 'Total Time'
fuelConsumption: 'Fuel Consumption'
},
shipyardView: {
title: 'Shipyard',
fleetStorage: 'Fleet Storage',
owned: 'Owned',
attack: 'Attack',
missileAttack: 'Missile Attack',
shield: 'Shield',
@@ -477,17 +425,7 @@ export default {
buildFailedMessage: 'Please check if you have enough resources or if prerequisites are met.'
},
defense: {
attack: 'Attack',
missileAttack: 'Missile Attack',
shield: 'Shield',
armor: 'Armor',
buildCost: 'Build Cost',
buildTime: 'Build Time',
perUnit: 'Per Unit',
batchCalculator: 'Batch Calculator',
quantity: 'Quantity',
totalCost: 'Total Cost',
totalTime: 'Total Time'
missileAttack: 'Missile Attack'
},
defenseView: {
title: 'Defense',
@@ -651,14 +589,13 @@ export default {
benefitsBonus: 'Benefits Bonus',
resourceProduction: 'Resource Production',
darkMatterProduction: 'Dark Matter Production',
energyProduction: 'Energy Production',
buildingSpeed: 'Building Speed',
researchSpeed: 'Research Speed',
fleetSpeed: 'Fleet Speed',
fuelConsumption: 'Fuel Consumption',
defense: 'Defense',
storageCapacity: 'Storage Capacity',
buildQueueBonus: 'Build Queue',
buildQueue: 'Build Queue',
spaceBonus: 'Space Bonus',
buildSpeedBonus: 'Build Speed Bonus',
researchSpeedBonus: 'Research Speed Bonus',
@@ -683,14 +620,11 @@ export default {
selectGalaxy: 'Select Galaxy',
system: 'System',
selectSystem: 'Select System',
view: 'View',
myPlanet: 'My Planet',
myPlanets: 'View My Systems',
npcPlanets: 'NPC Planets',
selectPlanetToView: 'Select planet to view its system',
totalPositions: '10 planet positions total',
mine: 'Mine',
hostile: 'Hostile',
emptySlot: 'Empty - Colonizable',
scout: 'Scout',
attack: 'Attack',
@@ -716,7 +650,9 @@ export default {
systems: 'systems',
distance: 'Distance',
flightTime: 'Flight Time',
outOfRange: 'Out of Range',
launchMissile: 'Launch',
missileLaunched: 'Missile Launched',
cancel: 'Cancel',
colonizePlanetMessage:
'Are you sure you want to colonize position [{coordinates}]?\n\nPlease go to the fleet page to send a colony ship.',
@@ -733,17 +669,13 @@ export default {
phalanxScanTitle: 'Sensor Phalanx Scan',
phalanxScanDescription: 'Scanning fleet activity at planet [{coordinates}]',
phalanxNoMoon: 'Requires a moon with Sensor Phalanx to scan',
phalanxOutOfRange: 'Target is out of scan range',
phalanxRange: 'Scan Range',
phalanxCost: 'Scan Cost',
phalanxNoFleets: 'No fleet activity detected',
phalanxFleetDetected: '{count} fleet(s) detected',
phalanxMission: 'Mission',
phalanxOrigin: 'Origin',
phalanxDestination: 'Destination',
phalanxArrival: 'Arrival',
phalanxReturn: 'Return',
phalanxStatus: 'Status',
phalanxStatusOutbound: 'Outbound',
phalanxStatusReturning: 'Returning',
phalanxInsufficientDeuterium: 'Insufficient Deuterium',
@@ -781,10 +713,29 @@ export default {
defense: 'Defense',
buildings: 'Buildings',
unread: 'Unread',
pending: 'Pending',
invalidData: 'Invalid Data',
targetPlanet: 'Target Planet',
attackerRemaining: 'Attacker Remaining',
defenderRemaining: 'Defender Remaining',
allDestroyed: 'All destroyed',
moonChance: 'Moon Chance',
showRoundDetails: 'Show Round Details',
hideRoundDetails: 'Hide Round Details',
round: 'Round {round}',
attackerRemainingPower: 'Attacker Remaining Power',
defenderRemainingPower: 'Defender Remaining Power',
playAnimation: 'Play Animation',
showDetails: 'Show Details',
speed: 'Speed',
power: 'Power',
battleLogEmpty: 'Battle log is empty',
roundStarted: 'Round {round} started',
shipDestroyed: '{count} {ship} destroyed',
defenseDestroyed: '{count} {defense} destroyed',
attackerWins: 'Attacker Wins',
defenderWins: 'Defender Wins',
roundsPlayed: 'rounds played',
spied: 'Spied',
spiedNotification: 'Spied Notification',
noSpiedNotifications: 'No spied notifications',
@@ -855,7 +806,11 @@ export default {
clearNPCActivity: 'NPC Activity',
clearGiftNotifications: 'Gift Notifications',
clearGiftRejected: 'Rejected Gifts',
clearNow: 'Clear Now'
clearTradeOffers: 'Trade Offers',
clearIntelReports: 'Intel Reports',
clearJointAttackInvites: 'Joint Attack Invitations',
clearNow: 'Clear Now',
clearSuccess: 'Messages cleared'
},
missionReports: {
transportSuccess: 'Transport mission completed successfully',
@@ -980,7 +935,6 @@ export default {
resume: 'Resume',
gamePaused: 'Game paused',
gameResumed: 'Game resumed',
playerName: 'Player Name',
gameSpeed: 'Resource Production Speed',
gameSpeedDesc: 'Current resource production speed multiplier',
speedChanged: 'Resource production speed changed to {speed}x',
@@ -988,17 +942,14 @@ export default {
reset: 'Reset',
about: 'About',
version: 'Version',
latestVersion: 'Latest Version',
checkUpdate: 'Check Update',
checking: 'Checking...',
newVersionAvailable: 'New version {version} available',
upToDate: 'Already up to date',
checkUpdateCooldown: 'Please try again later (5 minute cooldown)',
checkUpdateFailed: 'Failed to check for updates, please check your network connection',
viewUpdate: 'View Update',
updateAvailable: 'A new version is available. Click to view release notes.',
download: 'Download',
goToDownload: 'Go to Download',
buildDate: 'Build Date',
community: 'Community',
github: 'GitHub Repository',
@@ -1025,13 +976,53 @@ export default {
expandTypes: 'Expand Details',
collapseTypes: 'Collapse Details',
// NPC name update
npcNameUpdate: 'NPC Name Update',
npcNameUpdateTitle: 'Old NPC Names Detected',
npcNameUpdateMessage: 'Found {count} NPCs using old name format. Would you like to update them to new localized names?',
npcNameUpdateConfirm: 'Update Names',
npcNameUpdateCancel: 'Keep Current',
npcNameUpdateSuccess: 'Successfully updated {count} NPC names',
npcNameUpdateSkipped: 'NPC name update skipped'
npcNameUpdateSkipped: 'NPC name update skipped',
// WebDAV
webdav: {
title: 'Cloud Sync',
desc: 'Sync game saves via WebDAV',
config: 'Configure',
configTitle: 'WebDAV Configuration',
configDesc: 'Configure your WebDAV server for cloud sync',
notConfigured: 'Please configure WebDAV server first',
serverUrl: 'Server URL',
serverUrlPlaceholder: 'e.g. https://dav.example.com',
serverUrlHint: 'Enter your WebDAV server address',
username: 'Username',
usernamePlaceholder: 'Enter username',
password: 'Password',
passwordPlaceholder: 'Enter password',
passwordHint: 'Password is stored locally only',
basePath: 'Save Path',
basePathPlaceholder: 'e.g. /ogame-saves/',
testConnection: 'Test Connection',
testing: 'Testing...',
testSuccess: 'Connection successful',
testFailed: 'Connection failed',
save: 'Save',
clearConfig: 'Clear',
configSaved: 'Configuration saved',
configCleared: 'Configuration cleared',
upload: 'Upload',
uploading: 'Uploading...',
uploadSuccess: 'Upload successful',
uploadFailed: 'Upload failed',
download: 'Download',
downloadSuccess: 'Download successful',
downloadFailed: 'Download failed',
selectFile: 'Select Save File',
selectFileDesc: 'Choose a save file to restore',
noFiles: 'No save files found',
loadFailed: 'Failed to load file list',
confirmDelete: 'Are you sure you want to delete "{name}"?',
deleteSuccess: 'File deleted',
deleteFailed: 'Delete failed'
}
},
notifications: {
constructionComplete: 'Construction Complete',
@@ -1154,8 +1145,6 @@ export default {
noFriendlyNpcs: 'No friendly NPCs',
noNeutralNpcs: 'No neutral NPCs',
noHostileNpcs: 'No hostile NPCs',
recentEvents: 'Recent Events',
recentEventsDescription: 'Recent diplomatic activity log',
ago: 'ago',
notifications: 'Diplomatic Notifications',
markAllRead: 'Mark All Read',
@@ -1179,7 +1168,6 @@ export default {
},
note: 'Note',
notePlaceholder: 'Enter note...',
noteEmpty: 'No note',
lastEvent: 'Last Event',
reportDetails: 'Diplomatic Report Details',
eventDescription: 'Event Description',
@@ -1235,6 +1223,22 @@ export default {
npcEliminatedMessage: "You destroyed all of {npcName}'s planets! This faction has been completely wiped out."
},
searchPlaceholder: 'Search NPC name...',
// Notification types
notificationType: {
tradeOffer: 'Trade Offer',
intelReport: 'Intel Report',
jointAttack: 'Joint Attack Invitation'
},
// Notification badges
notificationBadge: {
trade: 'Trade',
intel: 'Intel',
jointAttack: 'Invite'
},
// Notification extra info
notificationExtra: {
pending: 'Pending'
},
viewMode: {
card: 'Card',
list: 'List'
@@ -1251,6 +1255,21 @@ export default {
medium: 'Medium',
hard: 'Hard'
},
aiType: 'AI Type',
aiTypes: {
aggressive: 'Aggressive',
defensive: 'Defensive',
trader: 'Trader',
expansionist: 'Expansionist',
balanced: 'Balanced'
},
aiTypeDescriptions: {
aggressive: 'Actively scouts and attacks, retaliates fiercely',
defensive: 'Rarely attacks first, retaliates strongly when attacked',
trader: 'Rarely attacks, prefers trading and gifting',
expansionist: 'Focuses on development, attacks less',
balanced: 'Dynamically adjusts strategy based on situation'
},
reputation: 'Reputation',
spyProbes: 'Spy Probes',
fleetPower: 'Fleet Power',
@@ -1567,11 +1586,21 @@ export default {
title: 'Battle Simulator',
message: 'Simulate battle outcomes before attacking. Enter both fleets and tech levels to predict victory, losses, and loot.'
},
campaign: {
title: 'Campaign Mode',
message:
'Explore the galaxy story campaign! Complete missions to earn resource rewards and unlock new challenges. Each node has unique objectives and enemies.'
},
achievements: {
title: 'Achievement System',
message:
'Complete game objectives to unlock achievements and earn Dark Matter rewards! Achievements have multiple tiers - aim for higher challenges to get better rewards.'
},
ranking: {
title: 'Ranking',
message:
'Compare your progress with other players and NPCs. See rankings based on points from buildings, research, fleet and defense. Strive to climb the leaderboard!'
},
settings: {
title: 'Settings',
message: 'Manage game data, adjust notifications, export/import saves here. Remember to backup your progress regularly!'
@@ -1708,7 +1737,6 @@ export default {
startQuest: 'Start Quest',
claimRewards: 'Claim Rewards',
objectives: 'Objectives',
objectivesLabel: 'Goals',
rewards: 'Rewards',
completed: 'Completed',
inProgress: 'In Progress',
@@ -1719,7 +1747,10 @@ export default {
questCompleted: 'Quest completed!',
rewardsClaimed: 'Rewards claimed',
objectiveCompleted: 'Objective completed',
chapterUnlocked: 'New chapter unlocked'
chapterUnlocked: 'New chapter unlocked',
reputationUp: 'Reputation with {npcName} increased by {value}',
reputationDown: 'Reputation with {npcName} decreased by {value}',
branchUnlocked: 'New story branch unlocked!'
},
dialogue: {
skip: 'Skip',
@@ -1729,33 +1760,39 @@ export default {
npc: 'NPC',
narrator: 'Narrator',
mysterious: 'Mysterious Signal',
unknownSource: 'Unknown source'
unknownSource: 'Unknown source',
choiceEffect: 'Dialogue choice effect'
},
chapters: {
'1': {
title: 'Origin',
description: 'Build your home and take the first step into space',
backgroundStory: 'You are a young space commander who has just acquired your first planet. In this vast universe, you will build your home, develop technology, and explore the depths of the galaxy...'
backgroundStory:
'You are a young space commander who has just acquired your first planet. In this vast universe, you will build your home, develop technology, and explore the depths of the galaxy...'
},
'2': {
title: 'Exploration',
description: 'Explore the universe and discover ancient ruins',
backgroundStory: 'As your power grows, mysterious signals from deep space catch your attention. These signals seem to point to an ancient secret, waiting for brave explorers to uncover...'
backgroundStory:
'As your power grows, mysterious signals from deep space catch your attention. These signals seem to point to an ancient secret, waiting for brave explorers to uncover...'
},
'3': {
title: 'Diplomacy',
description: 'Establish connections with other factions',
backgroundStory: 'You are not alone in the galaxy. Other civilizations are rising. You must decide whether to be their enemy or ally. Diplomatic wisdom will determine how far your empire can go...'
backgroundStory:
'You are not alone in the galaxy. Other civilizations are rising. You must decide whether to be their enemy or ally. Diplomatic wisdom will determine how far your empire can go...'
},
'4': {
title: 'Shadow Rising',
description: 'Face powerful enemies and defend your territory',
backgroundStory: 'Danger lurks in the shadows. A powerful hostile force has targeted your territory. War is inevitable. You must prepare to face the coming storm...'
backgroundStory:
'Danger lurks in the shadows. A powerful hostile force has targeted your territory. War is inevitable. You must prepare to face the coming storm...'
},
'5': {
title: 'Ancient Secrets',
description: 'Uncover the deepest secrets of the galaxy',
backgroundStory: 'All clues point to the most mysterious region of the galaxy. There, the ultimate secrets left by ancient civilizations await. Are you ready to uncover everything?'
backgroundStory:
'All clues point to the most mysterious region of the galaxy. There, the ultimate secrets left by ancient civilizations await. Are you ready to uncover everything?'
}
},
quests: {
@@ -1926,11 +1963,13 @@ export default {
},
dialogues: {
'1_1': {
prologue_1: 'Welcome to the galaxy, young commander. This vast universe awaits your exploration. First, let us build up your home planet.',
prologue_1:
'Welcome to the galaxy, young commander. This vast universe awaits your exploration. First, let us build up your home planet.',
prologue_2: 'I sense a new consciousness awakening... Interesting... Let us see how far you can go...'
},
'1_2': {
prologue_1: 'Basic infrastructure is complete. Now it is time to develop technology. Build a Research Lab and begin your tech journey.'
prologue_1:
'Basic infrastructure is complete. Now it is time to develop technology. Build a Research Lab and begin your tech journey.'
},
'1_3': {
prologue_1: 'With technology support, you can start building your fleet. Build a Shipyard and produce your first warship.'
@@ -1945,7 +1984,8 @@ export default {
epilogue_2: 'Good... Establishing connections is the first step to uncovering deeper secrets...'
},
'2_1': {
prologue_1: 'Your power is established. It is time to expand your territory. Research Astrophysics, build a colony ship, and explore new planets.',
prologue_1:
'Your power is established. It is time to expand your territory. Research Astrophysics, build a colony ship, and explore new planets.',
prologue_2: 'The universe is infinite... More planets mean more possibilities...'
},
'2_2': {
@@ -1953,7 +1993,8 @@ export default {
prologue_2: 'Faint signals from afar... Something awaits you there...'
},
'2_3': {
prologue_1: 'Your expedition discovered anomalous signals. These signals seem to come from an ancient civilization... Investigate their source.',
prologue_1:
'Your expedition discovered anomalous signals. These signals seem to come from an ancient civilization... Investigate their source.',
epilogue_1: 'These symbols... They are ruins of an ancient civilization! Continue investigating to uncover their secrets.'
},
'2_4': {
@@ -1979,7 +2020,7 @@ export default {
},
'4_1': {
prologue_1: 'The enemy has launched an attack! Defend your planet!',
epilogue_1: 'You successfully repelled the enemy\'s first wave. But this is just the beginning...'
epilogue_1: "You successfully repelled the enemy's first wave. But this is just the beginning..."
},
'4_2': {
prologue_1: 'The enemy has retreated, but they will return. Scout their planets to understand their strength.'
@@ -2012,5 +2053,114 @@ export default {
epilogue_1: 'The galaxy is vast and boundless, with countless secrets waiting for you to discover...'
}
}
},
// NPC Enhanced Behavior Notifications
npcBehavior: {
// Neutral NPC behavior
tradeOfferReceived: 'Trade Offer Received',
tradeOfferDesc: '{npcName} has sent you a trade offer',
attitudeChanged: 'NPC Attitude Changed',
becameFriendly: '{npcName} has become friendly towards you',
becameHostile: '{npcName} has become hostile towards you',
// Friendly NPC behavior
intelReceived: 'Intel Received',
intelReceivedDesc: '{npcName} has shared enemy intelligence with you',
jointAttackInvite: 'Joint Attack Invitation',
jointAttackInviteDesc: '{npcName} invites you to attack an enemy together',
aidReceived: 'Aid Received',
aidReceivedDesc: '{npcName} has sent you {amount} resources',
allyDefense: 'Ally Defense',
allyDefenseDesc: '{npcName} is sending a fleet to help defend your planet',
// Trade related
trade: {
title: 'Trade Offers',
from: 'From',
offers: 'Offers',
requests: 'Requests',
expiresIn: 'Expires In',
expired: 'Expired',
accept: 'Accept',
decline: 'Decline',
noOffers: 'No trade offers',
acceptSuccess: 'Trade completed!',
acceptFailed: 'Insufficient resources to complete trade',
declined: 'Trade declined',
ratio: 'Exchange Ratio'
},
// Intel related
intel: {
title: 'Intel Reports',
from: 'Source',
target: 'Target NPC',
type: 'Intel Type',
types: {
enemyFleet: 'Fleet Intel',
enemyResources: 'Resource Intel',
enemyMovement: 'Movement Intel'
},
fleetInfo: 'Fleet Information',
resourceInfo: 'Resource Information',
movementInfo: 'Movement Information',
noReports: 'No intel reports',
markAsRead: 'Mark as Read',
content: 'Intel Content',
noFleet: 'No fleet detected',
noData: 'No data available',
targetPosition: 'Target Position',
missionType: 'Mission Type'
},
// Joint attack related
jointAttack: {
title: 'Joint Attack Invitations',
from: 'Initiator',
target: 'Target NPC',
targetPlanet: 'Target Planet',
npcFleet: 'NPC Fleet',
lootShare: 'Loot Share',
expiresIn: 'Expires In',
expired: 'Expired',
accept: 'Join Attack',
decline: 'Decline',
noInvites: 'No joint attack invitations',
acceptSuccess: 'Joined joint attack!',
declined: 'Invitation declined',
targetInfo: 'Attack Target',
expectedShare: 'Expected Share',
remaining: 'Time Remaining'
},
// Aid related
aid: {
title: 'Resource Aid',
from: 'Source',
resources: 'Aid Resources',
noAid: 'No aid records'
},
// Attitude change related
attitudeChange: {
title: 'Attitude Changes',
npc: 'NPC',
previous: 'Previous',
current: 'Current',
reason: 'Reason',
reasons: {
attitude_swing: 'Attitude swing',
gift: 'Received gift',
attack: 'Attacked',
naturalSwing: 'Natural shift',
giftReceived: 'Received gift',
attacked: 'Was attacked',
reputationThreshold: 'Reputation threshold'
}
},
// Ally actions related
allyAction: {
title: 'Ally Actions',
defense: 'Defensive Support',
defenseDesc: '{npcName} is sending fleet to help defend {targetPlanet}',
jointAttackStarted: 'Joint Attack Started',
jointAttackStartedDesc: 'Joint attack on {targetNpc} has begun',
reputationBonus: 'Reputation Bonus',
reputationBonusDesc: 'Your ally {npcName} speaks well of you to {targetNpc}'
}
}
}

2188
src/locales/es-LA.ts Normal file
View File

@@ -0,0 +1,2188 @@
export default {
home: {
subtitle: 'Conquista las Estrellas',
startGame: 'Iniciar Juego',
privacyAgreement: 'Acuerdo de Privacidad',
privacyAgreementDesc: 'Por favor, lee y acepta nuestra política de privacidad antes de iniciar el juego.',
agreeToPrivacy: 'He leído y acepto',
viewFullPolicy: 'Ver Política Completa',
agreeAndStart: 'Aceptar e Iniciar'
},
common: {
confirm: 'Confirmar',
cancel: 'Cancelar',
save: 'Guardar',
close: 'Cerrar',
success: 'Éxito',
resourceType: 'Tipo de Recurso',
featureLocked: 'Característica Bloqueada',
unlockRequired: 'Edificio Requerido',
requiredBuilding: 'Edificio Requerido',
currentLevel: 'Nivel Actual',
goToBuildings: 'Ir a Edificios',
locked: 'Bloqueado',
viewRequirements: 'Ver Requisitos',
requirementsNotMet: 'Requisitos No Cumplidos',
current: 'Actual',
level: 'Nivel',
to: 'a',
gmModeActivated: '¡Modo GM Activado! Revisa el menú de navegación.',
view: 'Ver',
viewDetails: 'Ver Detalles',
exitConfirmTitle: 'Salir del Juego',
exitConfirmMessage: '¿Estás seguro de que quieres salir? Tu progreso se guarda automáticamente.',
points: 'Puntos',
retry: 'Reintentar'
},
errors: {
requirementsNotMet: 'Requisitos no cumplidos',
insufficientResources: 'Recursos insuficientes',
insufficientFleetStorage: 'Almacenamiento de flota insuficiente',
shieldDomeLimit: 'Límite de cúpula de escudo alcanzado',
missileSiloLimit: 'Capacidad del silo de misiles excedida',
insufficientMissiles: 'Misiles interplanetarios insuficientes',
invalidMissileCount: 'Cantidad de misiles inválida',
targetOutOfRange: 'Objetivo fuera de alcance',
cannotAttackOwnPlanet: 'No puedes atacar tu propio planeta',
fleetMissionsFull: 'Espacios de misión de flota llenos',
insufficientFleet: 'Flota insuficiente',
insufficientFuel: 'Combustible insuficiente',
planetOnly: 'Este edificio solo puede construirse en planetas',
moonOnly: 'Este edificio solo puede construirse en lunas',
buildQueueFull: 'Cola de construcción llena',
insufficientSpace: 'Espacio insuficiente',
buildingLevelZero: 'El nivel del edificio es 0, no se puede demoler',
researchQueueFull: 'Cola de investigación llena',
moonExists: 'La luna ya existe',
insufficientDebris: 'Campo de escombros insuficiente',
launchFailed: 'Lanzamiento fallido'
},
nav: {
overview: 'Resumen',
buildings: 'Edificios',
research: 'Investigación',
shipyard: 'Astillero',
defense: 'Defensa',
fleet: 'Flota',
officers: 'Oficiales',
simulator: 'Simulador',
galaxy: 'Galaxia',
diplomacy: 'Diplomacia',
achievements: 'Logros',
campaign: 'Campaña',
ranking: 'Clasificación',
messages: 'Mensajes',
settings: 'Configuración',
gm: 'GM'
},
sidebar: {
lightMode: 'Modo Claro',
darkMode: 'Modo Oscuro',
collapse: 'Contraer',
expand: 'Expandir'
},
resources: {
metal: 'Metal',
crystal: 'Cristal',
deuterium: 'Deuterio',
darkMatter: 'Materia Oscura',
energy: 'Energía',
production: 'Producción',
consumption: 'Consumo',
capacity: 'Capacidad',
current: 'Actual',
max: 'Capacidad Máxima',
perHour: 'hora',
perMinute: 'min',
hour: 'hora',
noEnergy: 'Sin Energía',
temperatureBonus: 'Bono de Temperatura'
},
energy: {
lowWarning: '¡Déficit de energía! ¡Producción de recursos detenida!',
deficitDetail: 'Déficit de energía: {deficit}, construye más plantas de energía',
buildSolarPlant: 'Construir Planta de Energía'
},
oreDeposit: {
lowWarning: '¡Los depósitos de mineral se están agotando!',
depletedWarning: '¡Depósitos de mineral agotados!',
depletedResources: 'Agotados: {resources}',
lowResources: 'Agotándose: {resources}'
},
planet: {
moon: 'Luna',
position: 'Posición',
switchToMoon: 'Ver Luna',
backToPlanet: 'Volver al Planeta',
switchPlanet: 'Cambiar Planeta',
currentPlanet: 'Planeta Actual',
temperature: 'Temperatura',
homePlanet: 'Planeta Principal',
planetPrefix: 'Planeta',
colonyPrefix: 'Colonia',
renamePlanet: 'Renombrar Planeta',
renamePlanetTitle: 'Renombrar Planeta',
planetNamePlaceholder: 'Ingresa el nuevo nombre del planeta',
rename: 'Renombrar'
},
player: {
points: 'Puntos Totales'
},
buildings: {
metalMine: 'Mina de Metal',
crystalMine: 'Mina de Cristal',
deuteriumSynthesizer: 'Sintetizador de Deuterio',
solarPlant: 'Planta Solar',
fusionReactor: 'Reactor de Fusión',
roboticsFactory: 'Fábrica de Robótica',
naniteFactory: 'Fábrica de Nanites',
shipyard: 'Astillero',
hangar: 'Hangar',
researchLab: 'Laboratorio de Investigación',
metalStorage: 'Almacén de Metal',
crystalStorage: 'Almacén de Cristal',
deuteriumTank: 'Tanque de Deuterio',
darkMatterCollector: 'Colector de Materia Oscura',
darkMatterTank: 'Tanque de Materia Oscura',
missileSilo: 'Silo de Misiles',
terraformer: 'Terraformador',
lunarBase: 'Base Lunar',
sensorPhalanx: 'Falange de Sensores',
jumpGate: 'Portal de Salto',
planetDestroyerFactory: 'Fábrica de Destructores de Planetas',
geoResearchStation: 'Estación de Investigación Geológica',
deepDrillingFacility: 'Instalación de Perforación Profunda',
buildTime: 'Tiempo de Construcción',
production: 'Producción',
consumption: 'Consumo',
storageCapacity: 'Capacidad',
energyProduction: 'Producción de Energía',
fleetStorage: 'Almacenamiento de Flota',
buildQueueBonus: 'Cola de Construcción',
spaceBonus: 'Bono de Espacio',
buildSpeedBonus: 'Bono de Velocidad de Construcción',
missileCapacity: 'Capacidad de Misiles',
// Ore deposits
oreDeposit: 'Depósito de Mineral',
remainingDeposit: 'Restante',
depletionTime: 'Agotamiento Est.',
depositDepleted: 'Agotado',
depositWarning: '¡Advertencia: Los depósitos de mineral se están agotando (por debajo del 10%)!',
depositDepletedMessage: 'Los depósitos de mineral se han agotado. La producción se ha detenido.'
},
buildingDescriptions: {
metalMine: 'Extrae recursos de metal',
crystalMine: 'Extrae recursos de cristal',
deuteriumSynthesizer: 'Sintetiza deuterio (mayor producción en temperaturas frías)',
solarPlant: 'Proporciona energía',
fusionReactor: 'Usa deuterio para generar grandes cantidades de energía',
roboticsFactory: 'Acelera la velocidad de construcción',
naniteFactory: 'Aumenta la capacidad de la cola de construcción, +1 por nivel (máximo 10 niveles)',
shipyard: 'Construye naves',
hangar: 'Instalación especializada para expandir la capacidad de almacenamiento de flota, apoya la especialización planetaria',
researchLab: 'Investiga tecnologías',
metalStorage: 'Aumenta la capacidad de almacenamiento de metal',
crystalStorage: 'Aumenta la capacidad de almacenamiento de cristal',
deuteriumTank: 'Aumenta la capacidad de almacenamiento de deuterio',
darkMatterCollector: 'Recolecta recursos raros de materia oscura',
darkMatterTank: 'Aumenta la capacidad de almacenamiento de materia oscura',
missileSilo: 'Almacena y lanza misiles, 10 misiles por nivel',
terraformer: 'Terraforma la superficie del planeta, añade 30 espacios disponibles por nivel',
lunarBase: 'Aumenta el espacio disponible en la luna, +30 espacios por nivel',
sensorPhalanx: 'Detecta actividades de flotas en sistemas circundantes',
jumpGate: 'Transfiere flotas instantáneamente a otras lunas',
planetDestroyerFactory: 'Construye armas definitivas capaces de destruir planetas',
geoResearchStation:
'Investiga estructuras geológicas y aumenta la tasa de regeneración de depósitos de mineral. +50% de regeneración por nivel',
deepDrillingFacility:
'Perfora profundamente en la corteza para acceder a vetas de mineral más profundas. +20% de capacidad de depósito de mineral por nivel'
},
ships: {
lightFighter: 'Caza Ligero',
heavyFighter: 'Caza Pesado',
cruiser: 'Crucero',
battleship: 'Nave de Batalla',
battlecruiser: 'Crucero de Batalla',
bomber: 'Bombardero',
destroyer: 'Destructor',
smallCargo: 'Nave de Carga Pequeña',
largeCargo: 'Nave de Carga Grande',
colonyShip: 'Nave Colonizadora',
recycler: 'Reciclador',
espionageProbe: 'Sonda de Espionaje',
solarSatellite: 'Satélite Solar',
darkMatterHarvester: 'Recolector de Materia Oscura',
deathstar: 'Estrella de la Muerte'
},
shipDescriptions: {
lightFighter: 'Unidad de combate básica',
heavyFighter: 'Caza fuertemente blindado',
cruiser: 'Nave de guerra mediana, ofensiva y defensa equilibradas',
battleship: 'Principal nave de guerra pesada con potente poder de fuego y defensa fuerte',
battlecruiser: 'Nave de guerra rápida y poderosa, sobresale atacando naves de batalla',
bomber: 'Nave especializada para atacar estructuras de defensa',
destroyer: 'Nave anti-capital especializada con alto poder de fuego pero baja defensa',
smallCargo: 'Transporta pequeñas cantidades de recursos',
largeCargo: 'Transporta grandes cantidades de recursos',
colonyShip: 'Usada para colonizar nuevos planetas',
recycler: 'Recolecta recursos del campo de escombros',
espionageProbe: 'Explora planetas enemigos',
solarSatellite: 'Proporciona energía extra, producción basada en la temperatura del planeta (mayor en climas cálidos)',
darkMatterHarvester: 'Nave especial para cosechar materia oscura',
deathstar: 'Arma definitiva capaz de destruir planetas enteros'
},
defenses: {
rocketLauncher: 'Lanzacohetes',
lightLaser: 'Láser Ligero',
heavyLaser: 'Láser Pesado',
gaussCannon: 'Cañón Gauss',
ionCannon: 'Cañón de Iones',
plasmaTurret: 'Torreta de Plasma',
smallShieldDome: 'Cúpula de Escudo Pequeña',
largeShieldDome: 'Cúpula de Escudo Grande',
antiBallisticMissile: 'Misil Anti-Balístico',
interplanetaryMissile: 'Misil Interplanetario',
planetaryShield: 'Escudo Planetario'
},
defenseDescriptions: {
rocketLauncher: 'Instalación de defensa básica',
lightLaser: 'Arma de energía ligera',
heavyLaser: 'Arma de energía pesada',
gaussCannon: 'Arma cinética de alta velocidad',
ionCannon: 'Efectivo contra escudos',
plasmaTurret: 'Instalación de defensa poderosa',
smallShieldDome: 'Escudo pequeño que protege todo el planeta',
largeShieldDome: 'Escudo grande que protege todo el planeta',
antiBallisticMissile: 'Intercepta misiles enemigos, puede interceptar 1 misil interplanetario cada uno',
interplanetaryMissile: 'Puede atacar estructuras de defensa en otros planetas',
planetaryShield: 'Súper escudo que protege el planeta de ataques de destrucción'
},
research: {
researchTime: 'Tiempo de Investigación',
totalCost: 'Costo Total',
totalPoints: 'Puntos Totales',
levelRange: 'Rango de Nivel',
attackBonus: 'Bono de Ataque',
shieldBonus: 'Bono de Escudo',
armorBonus: 'Bono de Blindaje',
spyLevel: 'Nivel de Espionaje',
researchQueueBonus: 'Cola de Investigación',
colonySlots: 'Espacios de Colonia',
forAllPlanets: '(Global)',
speedBonus: 'Bono de Velocidad',
researchSpeedBonus: 'Bono de Velocidad de Investigación'
},
technologies: {
energyTechnology: 'Tecnología de Energía',
laserTechnology: 'Tecnología Láser',
ionTechnology: 'Tecnología de Iones',
hyperspaceTechnology: 'Tecnología de Hiperespacio',
plasmaTechnology: 'Tecnología de Plasma',
computerTechnology: 'Tecnología Computacional',
espionageTechnology: 'Tecnología de Espionaje',
weaponsTechnology: 'Tecnología de Armas',
shieldingTechnology: 'Tecnología de Escudos',
armourTechnology: 'Tecnología de Blindaje',
astrophysics: 'Astrofísica',
gravitonTechnology: 'Tecnología de Gravitones',
combustionDrive: 'Motor de Combustión',
impulseDrive: 'Motor de Impulso',
hyperspaceDrive: 'Motor de Hiperespacio',
darkMatterTechnology: 'Tecnología de Materia Oscura',
terraformingTechnology: 'Tecnología de Terraformación',
planetDestructionTech: 'Tecnología de Destrucción Planetaria',
miningTechnology: 'Tecnología de Minería'
},
technologyDescriptions: {
energyTechnology: 'Mejora la eficiencia energética',
laserTechnology: 'Fundamento de armas y defensa láser',
ionTechnology: 'Tecnología de armas de iones',
hyperspaceTechnology: 'Tecnología de salto hiperespacial',
plasmaTechnology: 'Tecnología de armas de plasma',
computerTechnology: 'Aumenta la cola de investigación y espacios de misión de flota, +1 cola +1 espacio por nivel (máximo 10 niveles)',
espionageTechnology:
'Mejora la efectividad de la sonda de espionaje, +1 nivel de espionaje por nivel. Nivel de espionaje = tu nivel - nivel enemigo + sondas/5. ≥-1 muestra flota, ≥1 muestra defensa, ≥3 muestra edificios, ≥5 muestra tecnologías',
weaponsTechnology: 'Aumenta el poder de ataque de naves y defensas en un 10% por nivel',
shieldingTechnology: 'Aumenta los escudos de naves y defensas en un 10% por nivel',
armourTechnology: 'Aumenta el blindaje de naves y defensas en un 10% por nivel',
astrophysics: 'Cada nivel añade 1 espacio de colonia y aumenta la tasa de éxito de expediciones',
gravitonTechnology: 'Estudia la manipulación de gravitones, requerido para la Estrella de la Muerte',
combustionDrive: 'Tecnología de propulsión básica',
impulseDrive: 'Tecnología de propulsión intermedia',
hyperspaceDrive: 'Tecnología de propulsión avanzada',
darkMatterTechnology: 'Investigación de las propiedades y aplicaciones de la materia oscura',
terraformingTechnology:
'Investigación de tecnología de terraformación planetaria, añade 30 espacios disponibles a todos los planetas por nivel',
planetDestructionTech: 'Tecnología aterradora para destruir planetas enteros',
miningTechnology:
'Mejora los métodos y equipos de minería, aumenta la capacidad de depósito de mineral en todos los planetas. +15% de capacidad por nivel'
},
officers: {
commander: 'Comandante',
admiral: 'Almirante',
engineer: 'Ingeniero',
geologist: 'Geólogo',
technocrat: 'Tecnócrata',
darkMatterSpecialist: 'Especialista en Materia Oscura',
resourceBonus: 'Bono de Producción de Recursos',
darkMatterBonus: 'Bono de Producción de Materia Oscura',
energyBonus: 'Bono de Producción de Energía'
},
officerDescriptions: {
commander: 'Mejora la velocidad de construcción y gestión',
admiral: 'Mejora el combate y la velocidad de la flota',
engineer: 'Mejora la energía y defensa',
geologist: 'Mejora la producción de recursos',
technocrat: 'Mejora la velocidad de investigación y espionaje',
darkMatterSpecialist: 'Mejora la eficiencia de recolección de materia oscura'
},
queue: {
title: 'Tareas Activas',
empty: 'No hay colas activas',
buildQueueBonus: 'Cola de Construcción',
spaceBonus: 'Bono de Espacio',
buildSpeedBonus: 'Bono de Velocidad de Construcción',
researchSpeedBonus: 'Bono de Velocidad de Investigación',
researchQueueBonus: 'Cola de Investigación',
building: 'Construyendo',
researching: 'Investigando',
demolishing: 'Demoliendo',
remaining: 'Restante',
cancel: 'Cancelar',
cancelBuild: 'Cancelar Construcción',
cancelResearch: 'Cancelar Investigación',
confirmCancel: '¿Estás seguro de que quieres cancelar? Se reembolsará el 50% de los recursos.',
level: 'Nivel',
quantity: 'Cantidad',
upgradeToLevel: 'Mejorar a Nivel',
tabs: {
all: 'Todo',
buildings: 'Edificios',
research: 'Investigación',
ships: 'Naves',
defense: 'Defensa',
waiting: 'Esperando'
},
waitingEmpty: 'No hay tareas en espera',
addToWaiting: 'Añadir a Cola de Espera',
remove: 'Remover',
resourcesReady: 'Listo',
waitingResources: 'Esperando',
waitingQueueFull: 'La cola de espera está llena',
movedToQueue: 'Tarea movida a la cola'
},
overview: {
resourceOverview: 'Recursos',
fleetInfo: 'Flota',
currentShips: 'Naves en este planeta',
totalProduction: 'Producción Total',
totalConsumption: 'Consumo Total',
noConsumption: 'Sin consumo de energía',
tabOverview: 'Resumen',
tabProduction: 'Detalles de Producción',
tabConsumption: 'Detalles de Consumo'
},
buildingsView: {
spaceUsage: 'Uso de Espacio',
upgradeCost: 'Costo de Mejora',
upgrade: 'Mejorar',
maxLevelReached: 'Nivel Máximo Alcanzado',
requirementsNotMet: 'Requisitos No Cumplidos',
upgradeFailed: 'Mejora Fallida',
upgradeFailedMessage: 'Por favor verifica si tienes suficientes recursos, espacio, o si hay otras tareas de construcción.',
demolish: 'Demoler',
demolishRefund: 'Reembolso de Demolición',
demolishFailed: 'Demolición Fallida',
demolishFailedMessage:
'No se puede demoler este edificio. Por favor verifica si la cola de construcción está llena o si el nivel del edificio es 0.',
confirmDemolish: 'Confirmar Demolición'
},
researchView: {
title: 'Investigación',
researchCost: 'Costo de Investigación',
research: 'Investigar',
maxLevelReached: 'Nivel Máximo Alcanzado',
researchFailed: 'Investigación Fallida',
researchFailedMessage:
'Por favor verifica si tienes suficientes recursos, si se cumplen los prerrequisitos, o si hay otras tareas de investigación.'
},
shipyard: {
missileAttack: 'Ataque de Misil',
speed: 'Velocidad',
cargoCapacity: 'Capacidad de Carga',
fuelConsumption: 'Consumo de Combustible'
},
shipyardView: {
title: 'Astillero',
fleetStorage: 'Almacenamiento de Flota',
attack: 'Ataque',
missileAttack: 'Ataque de Misil',
shield: 'Escudo',
speed: 'Velocidad',
cargoCapacity: 'Capacidad de Carga',
unitCost: 'Costo por Unidad',
buildQuantity: 'Cantidad a Construir',
totalCost: 'Costo Total',
build: 'Construir',
inputError: 'Error de Entrada',
inputErrorMessage: '¡Por favor ingresa la cantidad a construir!',
buildFailed: 'Construcción Fallida',
buildFailedMessage: 'Por favor verifica si tienes suficientes recursos o si se cumplen los prerrequisitos.'
},
defense: {
attack: 'Ataque',
missileAttack: 'Ataque de Misil',
shield: 'Escudo',
armor: 'Blindaje',
buildCost: 'Costo de Construcción',
buildTime: 'Tiempo de Construcción',
perUnit: 'Por Unidad',
batchCalculator: 'Calculadora de Lotes',
quantity: 'Cantidad',
totalCost: 'Costo Total',
totalTime: 'Tiempo Total'
},
defenseView: {
title: 'Defensa',
attack: 'Ataque',
missileAttack: 'Ataque de Misil',
shield: 'Escudo',
armor: 'Blindaje',
buildTime: 'Tiempo de Construcción',
seconds: 's',
unitCost: 'Costo por Unidad',
buildQuantity: 'Cantidad a Construir',
totalCost: 'Costo Total',
build: 'Construir',
shieldDomeBuilt: 'Cúpula de escudo ya construida',
missileCapacity: 'Capacidad de Misiles',
inputError: 'Error de Entrada',
inputErrorMessage: '¡Por favor ingresa la cantidad a construir!',
buildFailed: 'Construcción Fallida',
buildFailedMessage:
'Por favor verifica si tienes suficientes recursos o si se cumplen los prerrequisitos. Las cúpulas de escudo solo se pueden construir una vez.'
},
fleetView: {
title: 'Gestión de Flota',
fleetOverview: 'Resumen de Flota',
sendFleet: 'Enviar Flota',
flightMissions: 'Misiones de Vuelo',
currentPlanetFleet: 'Flota del Planeta Actual',
attack: 'Ataque',
missileAttack: 'Ataque de Misil',
shield: 'Escudo',
armor: 'Blindaje',
speed: 'Velocidad',
cargo: 'Carga',
selectFleet: 'Seleccionar Flota',
selectFleetDescription: 'Selecciona el número de naves a enviar',
available: 'Disponible',
all: 'Todas',
targetCoordinates: 'Coordenadas de Destino',
targetType: 'Tipo de Destino',
planet: 'Planeta',
moon: 'Luna',
galaxy: 'Galaxia',
system: 'Sistema',
position: 'Posición',
missionType: 'Tipo de Misión',
missionInfo: 'Información de Misión',
fuelConsumption: 'Consumo de Combustible',
flightTime: 'Tiempo de Vuelo',
attackMission: 'Atacar',
transport: 'Transportar',
colonize: 'Colonizar',
spy: 'Espiar',
deploy: 'Desplegar',
expedition: 'Expedición',
expeditionZone: 'Zona de Expedición',
expeditionZoneDesc: 'Selecciona la zona de destino. Diferentes zonas tienen diferentes riesgos y recompensas',
requiresAstro: 'Requiere nivel de Astrofísica {level}',
reward: 'Recompensa',
danger: 'Peligro',
zones: {
nearSpace: {
name: 'Espacio Cercano',
desc: 'Área de espacio cercano segura, bajo riesgo pero menos recompensas'
},
deepSpace: {
name: 'Espacio Profundo',
desc: 'Lejos de las estrellas, se pueden encontrar más recursos'
},
unchartedSpace: {
name: 'Espacio Inexplorado',
desc: 'Área no explorada, alto riesgo alta recompensa'
},
dangerousNebula: {
name: 'Nebulosa Peligrosa',
desc: 'Nebulosa llena de peligros desconocidos, pero contiene tesoros extremadamente ricos'
}
},
recycle: 'Reciclar',
destroy: 'Destrucción Planetaria',
transportResources: 'Transportar Recursos',
totalCargoCapacity: 'Capacidad de Carga Total',
used: 'Usado',
noFlightMissions: 'No hay misiones de vuelo',
outbound: 'En Ruta',
returning: 'Regresando',
fleetComposition: 'Composición de Flota',
carryingResources: 'Transportando Recursos',
arrivalTime: 'Hora de Llegada',
returnTime: 'Hora de Regreso',
recallFleet: 'Recuperar Flota',
abortMission: 'Abortar Misión',
abortMissionTitle: 'Confirmar Abortar Misión',
abortMissionWarning:
'ADVERTENCIA: ¡Abortar esta misión perderá permanentemente {ships} naves y {resources} recursos!\n\nEsta acción es irreversible y la flota y los recursos no regresarán.',
abortMissionSuccess: 'Misión Abortada',
abortMissionSuccessMessage: 'La misión ha sido abortada, la flota y los recursos se han perdido.',
sendFailed: 'Envío Fallido',
sendFailedMessage: 'Por favor verifica el número de naves, disponibilidad de combustible, o límites de capacidad de carga.',
recallFailed: 'Recuperación Fallida',
recallFailedMessage: 'Esta misión no puede ser recuperada.',
unknownPlanet: 'Planeta Desconocido',
fleetMissionSlots: 'Espacios de Misión de Flota',
noShipsSelected: 'No se han seleccionado naves',
cannotSendToOwnPlanet: 'No puedes enviar flota a tu propio planeta',
cargoExceedsCapacity: 'La carga excede la capacidad',
noColonyShip: 'Se requiere nave colonizadora para misión de colonización',
noDebrisAtTarget: 'No hay campo de escombros en las coordenadas de destino o el campo de escombros está vacío',
noDeathstar: 'Se requiere Estrella de la Muerte para misión de destrucción',
giftMode: 'Modo Regalo',
giftModeDescription: 'Enviar recursos como regalo a',
estimatedReputationGain: 'Ganancia de reputación estimada',
// Fleet presets
fleetPresets: 'Presets de Flota',
fleetPresetsDescription: 'Guarda configuraciones comunes de flota para envío rápido (máximo 3)',
savePreset: 'Guardar Preset',
noPresets: 'No hay presets aún, selecciona flota y haz clic en "Guardar Preset" para crear',
shipTypes: 'tipos de nave',
editPreset: 'Editar contenido del preset',
renamePreset: 'Renombrar',
deletePreset: 'Eliminar preset',
editingPresetHint: 'Editando preset, modifica la configuración de flota y haz clic en "Guardar" para actualizar',
presetLimitReached: 'Límite de presets alcanzado',
presetLimitReachedMessage: 'Se permiten máximo {max} presets',
presetError: 'Guardado fallido',
presetNoShips: 'Por favor selecciona al menos una nave primero',
presetDefaultName: 'Preset {number}',
savePresetTitle: 'Guardar Preset de Flota',
savePresetDescription: 'Nombra esta configuración de flota',
renamePresetTitle: 'Renombrar Preset',
renamePresetDescription: 'Ingresa un nuevo nombre de preset',
presetName: 'Nombre del Preset',
presetNamePlaceholder: 'Ingresa nombre del preset',
deletePresetTitle: 'Eliminar Preset',
deletePresetMessage: '¿Estás seguro de que quieres eliminar el preset "{name}"? Esta acción no se puede deshacer.',
// Jump Gate
jumpGate: 'Portal de Salto',
jumpGateDescription: 'Usa el Portal de Salto para transferir instantáneamente la flota a otra luna con Portal de Salto',
jumpGateNotAvailable: 'Portal de Salto No Disponible',
jumpGateRequiresMoon: 'El Portal de Salto solo se puede usar en lunas',
jumpGateNotBuilt: 'La luna actual no tiene un Portal de Salto',
jumpGateCooldown: 'Portal de Salto en Enfriamiento',
jumpGateCooldownRemaining: 'Enfriamiento Restante',
jumpGateReady: 'Portal de Salto Listo',
jumpGateSelectTarget: 'Seleccionar Luna de Destino',
jumpGateNoTargetMoons: 'No hay lunas de destino disponibles (requiere Portal de Salto y enfriamiento completo)',
jumpGateSelectFleet: 'Seleccionar Flota para Transferir',
jumpGateTransfer: 'Transferir Flota',
jumpGateSuccess: 'Transferencia de Portal de Salto Exitosa',
jumpGateSuccessMessage: 'La flota ha sido transferida instantáneamente a {target}',
jumpGateFailed: 'Transferencia de Portal de Salto Fallida',
jumpGateFailedMessage: 'Por favor verifica el estado del Portal de Salto y la configuración de flota'
},
officersView: {
title: 'Oficiales',
activated: 'Activado',
inactive: 'Inactivo',
activeStatus: 'Estado Activo',
expirationTime: 'Tiempo de Expiración',
remainingTime: 'Tiempo Restante',
recruitCost: 'Costo de Reclutamiento',
days: 'días',
benefitsBonus: 'Bono de Beneficios',
resourceProduction: 'Producción de Recursos',
darkMatterProduction: 'Producción de Materia Oscura',
buildingSpeed: 'Velocidad de Construcción',
researchSpeed: 'Velocidad de Investigación',
fleetSpeed: 'Velocidad de Flota',
fuelConsumption: 'Consumo de Combustible',
defense: 'Defensa',
storageCapacity: 'Capacidad de Almacenamiento',
buildQueueBonus: 'Cola de Construcción',
spaceBonus: 'Bono de Espacio',
buildSpeedBonus: 'Bono de Velocidad de Construcción',
researchSpeedBonus: 'Bono de Velocidad de Investigación',
fleetSlots: 'Espacios de Flota',
buildQueue: 'Cola de Edificios',
hire: 'Contratar',
renew: 'Renovar',
dismiss: 'Despedir',
hireTitle: 'Contratar Oficial',
hireMessage: '¿Estás seguro de que quieres contratar a {name}? Válido por 7 días.',
renewTitle: 'Renovar Oficial',
renewMessage: '¿Estás seguro de que quieres renovar a {name} por 7 días?',
dismissTitle: 'Despedir Oficial',
dismissMessage: '¿Estás seguro de que quieres despedir a {name}? No se darán reembolsos.',
hireFailed: 'Contratación Fallida',
renewFailed: 'Renovación Fallida',
insufficientResources: '¡Recursos insuficientes!'
},
galaxyView: {
selectCoordinates: 'Seleccionar Coordenadas',
galaxy: 'Galaxia',
selectGalaxy: 'Seleccionar Galaxia',
system: 'Sistema',
selectSystem: 'Seleccionar Sistema',
myPlanets: 'Ver Mis Sistemas',
npcPlanets: 'Planetas NPC',
selectPlanetToView: 'Selecciona planeta para ver su sistema',
totalPositions: '10 posiciones de planeta en total',
mine: 'Mío',
emptySlot: 'Vacío - Colonizable',
scout: 'Explorar',
attack: 'Atacar',
missileAttack: 'Ataque de Misil',
colonize: 'Colonizar',
switch: 'Cambiar',
recycle: 'Reciclar',
debrisField: 'Campo de Escombros',
oreDeposits: 'Depósitos de Mineral',
deposits: 'Depósitos',
scoutPlanetTitle: 'Explorar Planeta',
attackPlanetTitle: 'Atacar Planeta',
missileAttackTitle: 'Ataque de Misil',
colonizePlanetTitle: 'Colonizar Planeta',
recyclePlanetTitle: 'Reciclar Escombros',
scoutPlanetMessage:
'¿Estás seguro de que quieres enviar sondas de espionaje para explorar el planeta [{coordinates}]?\n\nPor favor ve a la página de flota para seleccionar naves y enviar.',
attackPlanetMessage:
'¿Estás seguro de que quieres atacar el planeta [{coordinates}]?\n\nPor favor ve a la página de flota para seleccionar naves y enviar.',
missileAttackMessage: 'Lanzar misiles interplanetarios para atacar el planeta [{coordinates}]',
missileCount: 'Cantidad de Misiles',
availableMissiles: 'Misiles Disponibles',
missileRange: 'Alcance de Misil',
systems: 'sistemas',
distance: 'Distancia',
flightTime: 'Tiempo de Vuelo',
outOfRange: 'Fuera de Alcance',
launchMissile: 'Lanzar',
cancel: 'Cancelar',
colonizePlanetMessage:
'¿Estás seguro de que quieres colonizar la posición [{coordinates}]?\n\nPor favor ve a la página de flota para enviar una nave colonizadora.',
recyclePlanetMessage:
'¿Estás seguro de que quieres reciclar escombros en la posición [{coordinates}]?\n\nPor favor ve a la página de flota para enviar naves recicladoras.',
sendGift: 'Enviar Regalo',
debris: 'Escombros',
giftPlanetTitle: 'Enviar Regalo',
giftPlanetMessage:
'¿Estás seguro de que quieres enviar recursos como regalo al planeta [{coordinates}]?\n\nPor favor ve a la página de flota para seleccionar naves de transporte y cargar recursos.',
npcPlanetName: 'Planeta de {name}',
// Sensor Phalanx Scan
phalanxScan: 'Escaneo de Falange',
phalanxScanTitle: 'Escaneo de Falange de Sensores',
phalanxScanDescription: 'Escaneando actividad de flota en el planeta [{coordinates}]',
phalanxNoMoon: 'Requiere una luna con Falange de Sensores para escanear',
phalanxCost: 'Costo de Escaneo',
phalanxNoFleets: 'No se detectó actividad de flota',
phalanxFleetDetected: '{count} flota(s) detectada(s)',
phalanxOrigin: 'Origen',
phalanxDestination: 'Destino',
phalanxArrival: 'Llegada',
phalanxReturn: 'Regreso',
phalanxStatusOutbound: 'En Ruta',
phalanxStatusReturning: 'Regresando',
phalanxInsufficientDeuterium: 'Deuterio Insuficiente',
intercepted: 'Interceptado',
defenseLosses: 'Pérdidas de Defensa',
missileLaunched: 'Misil lanzado'
},
messagesView: {
title: 'Mensajes',
battles: 'Batallas',
spy: 'Espionaje',
npc: 'NPC',
diplomacy: 'Diplomacia',
battleReports: 'Reportes de Batalla',
spyReports: 'Reportes de Espionaje',
noBattleReports: 'No hay reportes de batalla',
noSpyReports: 'No hay reportes de espionaje',
noDiplomaticReports: 'No hay reportes diplomáticos',
battleReport: 'Reporte de Batalla',
spyReport: 'Reporte de Espionaje',
victory: 'Victoria',
defeat: 'Derrota',
draw: 'Empate',
attackerFleet: 'Flota Atacante',
defenderFleet: 'Flota Defensora',
defenderDefense: 'Defensa del Defensor',
attackerLosses: 'Pérdidas del Atacante',
defenderLosses: 'Pérdidas del Defensor',
noLosses: 'Sin pérdidas',
losses: 'Pérdidas',
remainingUnits: 'Unidades Restantes',
plunder: 'Botín',
debrisField: 'Campo de Escombros',
resources: 'Recursos',
fleet: 'Flota',
defense: 'Defensa',
buildings: 'Edificios',
unread: 'No leído',
pending: 'Pendiente',
invalidData: 'Datos Inválidos',
targetPlanet: 'Planeta Objetivo',
attackerRemaining: 'Atacante Restante',
defenderRemaining: 'Defensor Restante',
allDestroyed: 'Todo destruido',
moonChance: 'Probabilidad de generación de luna',
showRoundDetails: 'Mostrar detalles de ronda',
hideRoundDetails: 'Ocultar detalles de ronda',
round: 'Ronda {round}',
attackerRemainingPower: 'Potencia de fuego restante del atacante',
defenderRemainingPower: 'Potencia de fuego restante del defensor',
playAnimation: 'Reproducir animación',
showDetails: 'Mostrar detalles',
speed: 'Velocidad',
power: 'Poder de combate',
battleLogEmpty: 'Registro de batalla vacío',
roundStarted: 'Ronda {round} iniciada',
shipDestroyed: '{count} {ship} destruidos',
defenseDestroyed: '{count} {defense} destruidos',
attackerWins: 'El atacante gana',
defenderWins: 'El defensor gana',
roundsPlayed: 'Rondas reproducidas',
spied: 'Espiado',
spiedNotification: 'Notificación de Espionaje',
noSpiedNotifications: 'No hay notificaciones de espionaje',
detected: 'Detectado',
undetected: 'No detectado',
missions: 'Misiones',
noMissionReports: 'No hay reportes de misión',
success: 'Éxito',
failed: 'Fallido',
npcActivity: 'Actividad NPC',
noNPCActivity: 'No hay notificaciones de actividad NPC',
npcRecycleActivity: 'NPC Reciclando Escombros',
gifts: 'Regalos',
giftRejected: 'Rechazado',
noGiftNotifications: 'No hay notificaciones de regalos',
noGiftRejected: 'No hay regalos rechazados',
giftFrom: 'Regalo de {npcName}',
giftRejectedBy: '{npcName} rechazó el regalo',
giftResources: 'Recursos de regalo',
rejectedResources: 'Recursos rechazados',
expectedReputation: 'Reputación esperada',
currentReputation: 'Reputación actual',
acceptGift: 'Aceptar',
rejectGift: 'Rechazar',
rejectionReason: {
hostile: 'Son hostiles hacia ti y no aceptan regalos',
neutral_distrust: 'Les falta confianza en ti',
polite_decline: 'Declinaron cortésmente'
},
// Spied notification dialog
spiedNotificationDetails: 'Detalles de Notificación de Espionaje',
spyDetected: 'Espía Detectado',
detectionResult: 'Resultado de Detección',
detectionSuccess: '¡Detectaste al espía enemigo!',
spiedNotificationMessage: '{npc} intentó espiar tu planeta {planet}',
spiedNotificationTip: 'Considera aumentar tu defensa o contraatacar si este NPC es hostil',
viewInGalaxy: 'Ver en Galaxia',
// Mission report dialog
missionReportDetails: 'Detalles del Reporte de Misión',
missionSuccess: 'Éxito',
missionFailed: 'Fallido',
origin: 'Origen',
destination: 'Destino',
missionDetails: 'Detalles de Misión',
transportedResources: 'Recursos Transportados',
recycledResources: 'Recursos Reciclados',
remainingDebris: 'Escombros Restantes',
newPlanet: 'Nuevo Planeta',
// NPC activity dialog
npcActivityDetails: 'Detalles de Actividad NPC',
activityType: {
recycle: 'Reciclando Escombros'
},
activityLocation: 'Ubicación de Actividad',
position: 'Posición',
nearPlanet: 'Cerca del Planeta',
activityDescription: 'Descripción de Actividad',
npcActivityMessage: '{npc} está {activity} en {position}',
arrivalTime: 'Hora de Llegada',
npcActivityTip:
'Los NPCs pueden recolectar escombros de batallas. Puedes intentar llegar primero a la ubicación si quieres competir por los recursos',
// Clear messages
clearMessages: 'Limpiar Mensajes',
clearMessageTypes: 'Selecciona tipos de mensaje a limpiar',
clearBattleReports: 'Reportes de Batalla',
clearSpyReports: 'Reportes de Espionaje',
clearSpiedNotifications: 'Notificaciones de Espionaje',
clearMissionReports: 'Reportes de Misión',
clearNPCActivity: 'Actividad NPC',
clearGiftNotifications: 'Notificaciones de Regalos',
clearGiftRejected: 'Regalos Rechazados',
clearTradeOffers: 'Ofertas de Comercio',
clearIntelReports: 'Reportes de Inteligencia',
clearJointAttackInvites: 'Invitaciones de Ataque Conjunto',
clearNow: 'Limpiar Ahora',
clearSuccess: 'Mensajes limpiados'
},
missionReports: {
transportSuccess: 'Misión de transporte completada exitosamente',
transportFailed: 'Misión de transporte fallida',
transportFailedTargetNotFound: 'Transporte fallido: El planeta objetivo no existe',
transportFailedGiftRejected: 'Transporte fallido: El regalo fue rechazado',
colonizeSuccess: 'Misión de colonización exitosa, nuevo planeta establecido',
colonizeFailed: 'Misión de colonización fallida',
colonizeFailedOccupied: 'Colonización fallida: La posición objetivo ya está ocupada por otro planeta',
colonizeFailedMaxColonies: 'Colonización fallida: Número máximo de colonias alcanzado. Investiga Astrofísica para aumentar el límite.',
spySuccess: 'Misión de espionaje completada exitosamente',
spyFailed: 'Misión de espionaje fallida',
spyFailedTargetNotFound: 'Espionaje fallido: El planeta objetivo no existe',
deploySuccess: 'Misión de despliegue completada exitosamente',
deployFailed: 'Misión de despliegue fallida',
deployFailedTargetNotFound: 'Despliegue fallido: El planeta objetivo no existe',
deployFailedNotOwnPlanet: 'Despliegue fallido: El planeta objetivo no te pertenece',
recycleSuccess: 'Misión de reciclaje completada exitosamente',
recycleFailed: 'Misión de reciclaje fallida, no hay escombros en la ubicación objetivo',
recycleFailedNoDebris: 'Reciclaje fallido: No hay campo de escombros en la ubicación objetivo',
recycleFailedDebrisEmpty: 'Reciclaje fallido: El campo de escombros ha sido limpiado',
destroySuccess: 'Misión de destrucción planetaria ejecutada exitosamente',
destroyFailed: 'Misión de destrucción planetaria fallida',
destroyFailedTargetNotFound: 'Destrucción fallida: El planeta objetivo no existe',
destroyFailedOwnPlanet: 'Destrucción fallida: No puedes destruir tu propio planeta',
destroyFailedNoDeathstar: 'Destrucción fallida: No hay Estrella de la Muerte para ejecutar la misión',
destroyFailedChance: 'Destrucción fallida: Verificación de probabilidad fallida (Tasa de éxito: {chance}%)',
missileAttackSuccess: 'Ataque de misil exitoso',
missileAttackFailed: 'Ataque de misil fallido, el planeta objetivo no existe',
missileAttackIntercepted: 'Todos los misiles interceptados',
hits: 'impactos',
expeditionResources: '¡La expedición encontró recursos!',
expeditionDarkMatter: '¡La expedición encontró materia oscura!',
expeditionFleet: '¡La expedición encontró naves abandonadas!',
expeditionPiratesAttack: 'La expedición fue atacada por piratas y perdió algunas naves',
expeditionPiratesEscaped: 'La expedición se encontró con piratas pero escapó exitosamente',
expeditionAliensAttack: 'La expedición fue atacada por alienígenas y perdió algunas naves',
expeditionAliensEscaped: 'La expedición se encontró con alienígenas pero escapó exitosamente',
expeditionNothing: 'La expedición no encontró nada'
},
simulatorView: {
title: 'Simulador de Batalla',
attacker: 'Atacante',
defender: 'Defensor',
attackerConfig: 'Configuración del Atacante',
attackerConfigDesc: 'Configura flota del atacante y niveles de tecnología',
defenderConfig: 'Configuración del Defensor',
defenderConfigDesc: 'Configura flota del defensor, defensa, y niveles de tecnología',
fleet: 'Flota',
defenseStructures: 'Estructuras de Defensa',
techLevels: 'Niveles de Tecnología',
weapon: 'Arma',
shield: 'Escudo',
armor: 'Blindaje',
defenderResources: 'Recursos del Defensor (para cálculo de botín)',
startSimulation: 'Iniciar Simulación',
reset: 'Reiniciar',
battleResult: 'Resultado de Batalla',
attackerVictory: 'Victoria del Atacante',
defenderVictory: 'Victoria del Defensor',
draw: 'Empate',
afterRounds: 'Después de {rounds} rondas',
attackerLosses: 'Pérdidas del Atacante',
defenderLosses: 'Pérdidas del Defensor',
noLosses: 'Sin pérdidas',
attackerRemaining: 'Atacante Restante',
defenderRemaining: 'Defensor Restante',
allDestroyed: 'Todo destruido',
plunderableResources: 'Recursos Saqueables',
debrisField: 'Campo de Escombros',
moonChance: 'Probabilidad de luna',
showRoundDetails: 'Mostrar detalles de rondas',
hideRoundDetails: 'Ocultar detalles de rondas',
round: 'Ronda {round}',
attackerRemainingPower: 'Poder restante del atacante',
defenderRemainingPower: 'Poder restante del defensor',
// Battle animation
playAnimation: 'Reproducir Animación',
showDetails: 'Mostrar Detalles',
speed: 'Velocidad',
power: 'Poder',
battleLogEmpty: 'El registro de batalla está vacío',
roundStarted: 'Ronda {round} iniciada',
shipDestroyed: '{count} {ship} destruido(s)',
defenseDestroyed: '{count} {defense} destruido(s)',
attackerWins: 'El Atacante Gana',
defenderWins: 'El Defensor Gana',
roundsPlayed: 'rondas jugadas',
spied: 'Espiado',
spiedNotification: 'Notificación de Espionaje',
noSpiedNotifications: 'No hay notificaciones de espionaje',
detected: 'Detectado',
undetected: 'No detectado'
},
settings: {
dataManagement: 'Gestión de Datos',
dataManagementDesc: 'Exportar, importar o limpiar datos del juego',
exportData: 'Exportar Datos',
exportDataDesc: 'Exportar progreso del juego como archivo JSON',
export: 'Exportar',
exporting: 'Exportando...',
exportSuccess: 'Exportación exitosa',
exportSuccessWithPath: 'Exportación exitosa, archivo guardado en: {path}',
exportFailed: 'Exportación fallida, por favor intenta de nuevo',
importData: 'Importar Datos',
importDataDesc: 'Restaurar progreso del juego desde archivo JSON',
selectFile: 'Seleccionar Archivo',
importSuccess: 'Importación exitosa',
importConfirmTitle: 'Confirmar Importación',
importConfirmMessage: 'Importar sobrescribirá el progreso actual del juego. Esta acción no se puede deshacer. ¿Continuar?',
importFailed: 'Importación fallida, por favor verifica el formato del archivo',
clearData: 'Limpiar Datos',
clearDataDesc: 'Eliminar todos los datos del juego y reiniciar',
clear: 'Limpiar',
clearConfirmTitle: 'Confirmar Limpiar Datos',
clearConfirmMessage: 'Esto eliminará todos los datos del juego y empezará de nuevo. Esta acción no se puede deshacer. ¿Continuar?',
gameSettings: 'Configuración del Juego',
gameSettingsDesc: 'Ajustar parámetros y preferencias del juego',
gamePause: 'Pausa del Juego',
gamePauseDesc: 'Pausar o reanudar el tiempo del juego y la producción de recursos',
pause: 'Pausar',
resume: 'Reanudar',
gamePaused: 'Juego pausado',
gameResumed: 'Juego reanudado',
gameSpeed: 'Velocidad de Producción de Recursos',
gameSpeedDesc: 'Multiplicador actual de velocidad de producción de recursos',
speedChanged: 'Velocidad de producción de recursos cambiada a {speed}x',
speedReset: 'Velocidad de producción de recursos reiniciada a 1x',
reset: 'Reiniciar',
about: 'Acerca de',
version: 'Versión',
checkUpdate: 'Verificar Actualización',
checking: 'Verificando...',
newVersionAvailable: 'Nueva versión {version} disponible',
upToDate: 'Ya está actualizado',
checkUpdateFailed: 'Falló la verificación de actualizaciones, por favor verifica tu conexión de red',
viewUpdate: 'Ver Actualización',
updateAvailable: 'Una nueva versión está disponible. Haz clic para ver las notas de la versión.',
download: 'Descargar',
buildDate: 'Fecha de Compilación',
community: 'Comunidad',
github: 'Repositorio GitHub',
qqGroup: 'Grupo QQ',
privacyPolicy: 'Política de Privacidad',
displaySettings: 'Configuración de Pantalla',
displaySettingsDesc: 'Ajustar efectos visuales',
backgroundAnimation: 'Animación de Fondo',
backgroundAnimationDesc: 'Mostrar animación de fondo de campo estelar/partículas (puede afectar el rendimiento)',
notifications: 'Configuración de Notificaciones',
notificationsDesc: 'Gestionar alertas de notificación en el juego',
notificationTypes: 'Tipos de Notificación',
browserNotifications: 'Notificaciones del Navegador',
inAppNotifications: 'Notificaciones en la Aplicación',
constructionComplete: 'Construcción Completa',
researchComplete: 'Investigación Completa',
unlockNotification: 'Notificación de Desbloqueo',
browserPermission: 'Habilitar Notificaciones del Navegador',
permissionGranted: 'Permiso Concedido',
permissionDenied: 'Permiso Denegado/No Concedido',
inAppNotificationsDesc: 'Mostrar mediante ventanas emergentes en la página',
notificationsDisabled: 'Habilita cualquier interruptor arriba para configurar notificaciones específicas',
suppressInFocus: 'Suprimir notificaciones del navegador cuando la página está enfocada',
expandTypes: 'Expandir Detalles',
collapseTypes: 'Contraer Detalles',
// NPC name update
npcNameUpdateTitle: 'Nombres NPC Antiguos Detectados',
npcNameUpdateMessage:
'Se encontraron {count} NPCs usando formato de nombre antiguo. ¿Te gustaría actualizarlos a nuevos nombres localizados?',
npcNameUpdateConfirm: 'Actualizar Nombres',
npcNameUpdateCancel: 'Mantener Actual',
npcNameUpdateSuccess: 'Se actualizaron exitosamente {count} nombres NPC',
npcNameUpdateSkipped: 'Actualización de nombres NPC omitida',
// WebDAV
webdav: {
title: 'Sincronización en la Nube',
desc: 'Sincronizar guardados mediante WebDAV',
config: 'Configurar',
configTitle: 'Configuración WebDAV',
configDesc: 'Configure su servidor WebDAV para sincronización en la nube',
notConfigured: 'Por favor configure primero el servidor WebDAV',
serverUrl: 'URL del Servidor',
serverUrlPlaceholder: 'ej. https://dav.example.com',
serverUrlHint: 'Ingrese la dirección de su servidor WebDAV',
username: 'Usuario',
usernamePlaceholder: 'Ingrese usuario',
password: 'Contraseña',
passwordPlaceholder: 'Ingrese contraseña',
passwordHint: 'La contraseña solo se almacena localmente',
basePath: 'Ruta de Guardados',
basePathPlaceholder: 'ej. /ogame-saves/',
testConnection: 'Probar Conexión',
testing: 'Probando...',
testSuccess: 'Conexión exitosa',
testFailed: 'Conexión fallida',
save: 'Guardar',
clearConfig: 'Limpiar',
configSaved: 'Configuración guardada',
configCleared: 'Configuración eliminada',
upload: 'Subir',
uploading: 'Subiendo...',
uploadSuccess: 'Subida exitosa',
uploadFailed: 'Subida fallida',
download: 'Descargar',
downloadSuccess: 'Descarga exitosa',
downloadFailed: 'Descarga fallida',
selectFile: 'Seleccionar Guardado',
selectFileDesc: 'Elige un archivo de guardado para restaurar',
noFiles: 'No se encontraron archivos de guardado',
loadFailed: 'Error al cargar lista de archivos',
confirmDelete: '¿Estás seguro de eliminar "{name}"?',
deleteSuccess: 'Archivo eliminado',
deleteFailed: 'Error al eliminar'
}
},
notifications: {
constructionComplete: 'Construcción Completa',
researchComplete: 'Investigación Completa',
newUnlock: 'Nuevo Contenido Desbloqueado',
building: 'Edificio',
technology: 'Tecnología'
},
gmView: {
title: 'Panel de Control GM',
adminOnly: 'Solo Administrador',
selectPlanet: 'Seleccionar Planeta',
choosePlanet: 'Elige un planeta',
resources: 'Recursos',
buildings: 'Edificios',
research: 'Investigación',
ships: 'Naves',
defense: 'Defensa',
officers: 'Oficiales',
modifyResources: 'Modificar Recursos',
resourcesDesc: 'Modificar rápidamente los recursos del planeta',
maxAllResources: 'Maximizar Todos',
maxAllResourcesSuccess: 'Todos los recursos maximizados',
modifyBuildings: 'Modificar Edificios',
buildingsDesc: 'Establecer rápidamente niveles de edificios',
modifyResearch: 'Modificar Investigación',
researchDesc: 'Establecer rápidamente niveles de investigación',
modifyShips: 'Modificar Naves',
shipsDesc: 'Establecer rápidamente cantidades de naves',
modifyDefense: 'Modificar Defensa',
defenseDesc: 'Establecer rápidamente cantidades de defensa',
modifyOfficers: 'Modificar Oficiales',
officersDesc: 'Establecer rápidamente tiempo de expiración de oficiales',
days: 'd',
npcTesting: 'Pruebas NPC',
npcTestingDesc: 'Probar comportamiento de espionaje y ataque NPC',
selectNPC: 'Seleccionar NPC',
chooseNPC: 'Elige un NPC',
targetPlanet: 'Planeta Objetivo',
chooseTarget: 'Elige planeta objetivo',
testSpy: 'Probar Espionaje',
testAttack: 'Probar Ataque',
testSpyAndAttack: 'Probar Espionaje y Ataque',
testSpyMessage: 'Haz clic en confirmar para acelerar la misión de espionaje',
testAttackMessage: 'Haz clic en confirmar para acelerar la misión de ataque',
testSpyAndAttackMessage: 'Haz clic en confirmar para acelerar las misiones',
initializeFleet: 'Inicializar Flota NPC',
accelerateMissions: 'Acelerar Todas las Misiones (5s)',
selectNPCFirst: 'Por favor selecciona un NPC primero',
npcNoProbes: 'El NPC no tiene sondas de espionaje',
npcNoSpyReport: 'El NPC necesita espiar primero',
npcMissionFailed: 'Falló la creación de la misión',
npcNoPlanets: 'El NPC no tiene planetas',
npcWillSpyIn5s: '{npcName} espiará en 5 segundos',
npcWillAttackIn5s: '{npcName} atacará en 5 segundos',
npcWillSpyAndAttack: '{npcName} espiará en 5s y atacará en 10s',
acceleratedMissions: '{count} misiones aceleradas a 5 segundos',
npcFleetInitialized: 'Flota de {npcName} inicializada',
npcFleetDetails:
'100 Sondas de Espionaje\n500 Cazas Ligeros\n300 Cazas Pesados\n200 Cruceros\n100 Naves de Batalla\n50 Bombarderos\n30 Destructores\n20 Cruceros de Batalla',
dangerZone: 'Zona de Peligro',
dangerZoneDesc: 'Las siguientes operaciones son irreversibles',
resetGame: 'Reiniciar Juego',
resetGameConfirm: '¿Estás seguro de que quieres reiniciar el juego? ¡Esto eliminará todos los datos!',
completeAllQueues: 'Completar Todas las Colas',
completeAllQueuesDesc: 'Completar instantáneamente todas las colas de edificios, investigación, naves, defensa y misiones de flota',
completeQueues: 'Completar Colas',
completeQueuesSuccess:
'Completadas {buildingCount} colas de edificios, {researchCount} colas de investigación, {missionCount} misiones de flota, {missileCount} ataques de misiles'
},
alerts: {
incomingFleets: '{count} Flotas Enemigas Entrantes',
npcSpyIncoming: 'Sonda de Espionaje NPC Entrante',
npcAttackIncoming: '¡Ataque de Flota NPC Entrante!',
npcFleetIncoming: 'Flota NPC Acercándose',
ships: 'naves',
spiedBy: 'Espiado Por',
attackedBy: 'Atacado Por',
detectionSuccess: 'Espía detectado',
detectionFailed: 'Espía no detectado',
npcSpiedYourPlanet: 'NPC espió tu planeta',
npcAttackedYourPlanet: 'NPC atacó tu planeta'
},
enemyAlert: {
title: 'Alerta de Enemigo',
markAllRead: 'Marcar Todos como Leídos',
noAlerts: 'No hay alertas',
fleetSize: 'Tamaño de Flota',
ships: 'naves',
viewFleet: 'Ver Flota',
alertDetails: 'Detalles de Alerta',
targetInfo: 'Información del Objetivo',
arrivalTime: 'Hora de Llegada',
countdown: 'Cuenta Regresiva',
viewMessages: 'Ver Mensajes',
arrived: 'Llegó',
missionType: {
spy: 'Espionaje',
attack: 'Ataque',
recycle: 'Reciclaje',
unknown: 'Desconocido'
},
warning: {
spy: '¡Espía enemigo entrante!',
attack: '¡Ataque enemigo entrante!',
recycle: '¡El enemigo está reciclando escombros cerca de ti!',
unknown: '¡Flota enemiga entrante!'
}
},
diplomacy: {
title: 'Diplomacia',
description: 'Gestionar relaciones diplomáticas con NPCs',
tabs: {
all: 'Todos',
friendly: 'Amigables',
neutral: 'Neutrales',
hostile: 'Hostiles'
},
noNpcs: 'Sin NPCs',
noFriendlyNpcs: 'Sin NPCs amigables',
noNeutralNpcs: 'Sin NPCs neutrales',
noHostileNpcs: 'Sin NPCs hostiles',
ago: 'hace',
notifications: 'Notificaciones Diplomáticas',
markAllRead: 'Marcar Todas como Leídas',
noReports: 'Sin eventos diplomáticos',
viewAll: 'Ver Todas',
status: {
friendly: 'Amigable',
neutral: 'Neutral',
hostile: 'Hostil'
},
planets: 'planetas',
allies: 'aliados',
reputation: 'Reputación',
alliedWith: 'Aliado con',
more: 'más',
actions: {
gift: 'Enviar Regalo',
viewPlanets: 'Ver Planetas',
addNote: 'Agregar Nota',
editNote: 'Editar Nota'
},
note: 'Nota',
notePlaceholder: 'Ingresar nota...',
lastEvent: 'Último Evento',
reportDetails: 'Detalles del Reporte Diplomático',
eventDescription: 'Descripción del Evento',
reputationChange: 'Cambio de Reputación',
before: 'Antes',
after: 'Después',
statusChange: 'Cambio de Estado',
viewDiplomacy: 'Ver Página de Diplomacia',
eventType: {
gift: 'Envió recursos',
attack: 'Lanzó un ataque',
allyAttacked: 'Atacó a un aliado',
spy: 'Realizó espionaje',
stealDebris: 'Robó escombros',
destroyPlanet: 'Destruyó un planeta',
unknown: 'Evento desconocido'
},
events: {
gift: 'Regalo Enviado',
attack: 'Ataque',
missileAttack: 'Ataque de Misil',
allyAttacked: 'Aliado Atacado',
spy: 'Espionaje',
stealDebris: 'Escombros Robados'
},
reports: {
giftedResources: 'Regaló {metal}M {crystal}C {deuterium}D',
receivedGiftFromPlayer: 'Recibió regalo del jugador',
giftedToNpc: 'Regalaste recursos a {npcName}. Reputación +{reputation}',
rejectedPlayerGift: 'Rechazó el regalo del jugador',
npcRejectedGift: '{npcName} rechazó tu regalo. Reputación {reputation}',
attackedNpc: 'Atacó a {npcName}',
wasAttackedByPlayer: 'Fue atacado por el jugador',
youAttackedNpc: 'Atacaste a {npcName}',
playerAttackedAlly: 'El jugador atacó al aliado {allyName}',
allyDispleased: '{allyName} está disgustado de que atacaras a su aliado {targetName}',
wasSpiedByPlayer: 'Fue espiado por el jugador (detectado: {detected})',
spyDetected: 'Tu espionaje fue detectado por {npcName}',
stoleDebrisFromTerritory: 'Robó escombros del territorio de {npcName}',
playerStoleDebris: 'El jugador robó escombros del territorio',
recycledDebrisNearNpc: 'Reciclaste escombros cerca del planeta de {npcName}. Están disgustados.',
giftedResourcesToPlayer: 'Regaló recursos al jugador',
receivedGiftFromNpc: 'Recibiste un regalo de {npcName}',
acceptedGiftFromNpc: 'Aceptaste un regalo de {npcName}: {metal}M {crystal}C {deuterium}D',
playerRejectedGift: 'El jugador rechazó el regalo',
rejectedGiftFromNpc: 'Rechazaste un regalo de {npcName}. Reputación {reputation}',
destroyedNpcPlanet: 'Destruyó {planetName} de {npcName}',
playerDestroyedPlanet: 'El jugador destruyó {planetName}',
youDestroyedNpcPlanet: 'Destruiste {planetName} de {npcName}. Reputación {reputation}',
playerDestroyedAllyPlanet: 'El jugador destruyó {planetName} del aliado {allyName}',
allyOutraged: '{allyName} está indignado de que destruyeras {planetName} de su aliado {targetName}',
npcEliminated: 'NPC {npcName} ha sido completamente eliminado',
npcEliminatedMessage: '¡Destruiste todos los planetas de {npcName}! Esta facción ha sido completamente eliminada.'
},
searchPlaceholder: 'Buscar nombre de NPC...',
// Notification types
notificationType: {
tradeOffer: 'Oferta Comercial',
intelReport: 'Reporte de Inteligencia',
jointAttack: 'Invitación de Ataque Conjunto'
},
// Notification badges
notificationBadge: {
trade: 'Comercio',
intel: 'Inteligencia',
jointAttack: 'Invitación'
},
// Notification extra info
notificationExtra: {
pending: 'Pendiente'
},
viewMode: {
card: 'Tarjeta',
list: 'Lista'
},
diagnostic: {
button: 'Diagnóstico de NPC',
title: 'Diagnóstico de Estado de NPC',
description:
'Puntos del jugador: {points}, Intervalo de espionaje: {spyInterval}min, Intervalo de ataque: {attackInterval}min, Probabilidad de ataque: {attackProb}%',
noData: 'Sin datos de NPC',
difficulty: 'Dificultad',
difficultyLevels: {
easy: 'Fácil',
medium: 'Medio',
hard: 'Difícil'
},
reputation: 'Reputación',
spyProbes: 'Sondas de Espionaje',
fleetPower: 'Poder de Flota',
canSpy: 'Puede Espiar',
canAttack: 'Puede Atacar',
attackProbability: 'Probabilidad de Ataque',
nextSpy: 'Próximo Espionaje',
nextAttack: 'Próximo Ataque',
yes: 'Sí',
no: 'No',
timeFormat: '{min}m {sec}s',
anytime: 'En cualquier momento',
statusExplanation: 'Explicación de Estado',
noRelation: 'Sin Relación',
noRelationNeutral: 'Sin Relación (Neutral)',
reasons: {
friendlyNoAction: 'Relación amigable, no actuará',
neutralNoAction: 'Relación neutral, no actuará',
hostileWillAct: 'Relación hostil, puede tomar acción',
noRelationNeutral: 'Sin relación diplomática, tratado como neutral',
insufficientProbes: 'Sondas insuficientes (Actual: {current}, Requerido: {required})',
noFleet: 'Sin flota de combate',
spyCooldown: 'Espionaje en espera ({min}m {sec}s)',
attackCooldown: 'Ataque en espera ({min}m {sec}s)',
notSpiedYet: 'Aún no espiado, necesita espiar primero'
},
aiType: 'Tipo de IA',
aiTypes: {
aggressive: 'Agresivo',
defensive: 'Defensivo',
trader: 'Comerciante',
expansionist: 'Expansionista',
balanced: 'Equilibrado'
},
aiTypeDescriptions: {
aggressive: 'Espía y ataca activamente, fuerte represalia',
defensive: 'Rara vez ataca, fuerte represalia cuando es atacado',
trader: 'Casi nunca ataca, prefiere comerciar y dar regalos',
expansionist: 'Se enfoca en el desarrollo, menos agresivo',
balanced: 'Ajusta dinámicamente la estrategia según la situación'
}
}
},
pagination: {
previous: 'Previous',
next: 'Next',
first: 'First',
last: 'Last',
page: 'Page {page}'
},
notFound: {
title: 'Page Not Found',
description: 'Sorry, the page you are looking for does not exist',
goHome: 'Go Home'
},
privacy: {
title: 'Privacy Policy',
sections: {
introduction: {
title: 'Introduction',
content:
'This privacy policy explains how OGame-Vue-Ts handles your data. We are committed to protecting your privacy, and this game is designed with complete respect for user privacy.'
},
dataCollection: {
title: 'Data Collection',
content: 'This game only collects and stores the following data locally in your browser:',
items: {
gameProgress: 'Game progress (building levels, fleets, resources, etc.)',
settings: 'Game settings (notification preferences, display options, etc.)',
language: 'Language preference'
}
},
dataStorage: {
title: 'Data Storage',
content:
"All data is stored in your browser's local storage (localStorage). This means your data always remains on your own device, and we cannot access, view, or collect any of your game data."
},
noServer: {
title: 'No Server Communication',
content:
'This game is a completely offline single-player game. Except for the update check feature (which fetches version information from GitHub), the game does not communicate with any server. Your game data never leaves your device.'
},
thirdParty: {
title: 'Third-Party Services',
content:
'This game uses third-party analytics services to track visitor statistics and traffic sources, helping us understand usage patterns and improve the gaming experience. This analytics data is anonymous and does not contain any personally identifiable information. We do not use any advertising services or other commercial tracking tools.'
},
dataControl: {
title: 'Data Control',
content: 'You have complete control over your data:',
items: {
export: 'You can export your game data at any time',
import: 'You can import data from backup files',
delete: 'You can delete all data by clearing browser data or using the in-game "Clear Data" feature'
}
},
contact: {
title: 'Contact Us',
content: 'If you have any questions about this privacy policy, please contact us via:'
}
}
},
time: {
days: 'días',
hours: 'horas',
minutes: 'minutos',
seconds: 'segundos'
},
tutorial: {
progress: 'Progreso',
previous: 'Anterior',
next: 'Siguiente',
gotIt: 'Entendido',
completeButton: 'Completar',
skip: 'Saltar Tutorial',
welcome: {
title: 'Bienvenido a OGame',
content:
'¡Bienvenido, Comandante! Este tutorial te guiará a través de los conceptos básicos para construir tu imperio. Haz clic en "Siguiente" para comenzar tu aventura.'
},
resources: {
title: 'Resumen de Recursos',
content:
'Estos son tus recursos: Metal, Cristal y Deuterio. Son esenciales para construir estructuras e investigar tecnologías. La energía también es importante para alimentar tu infraestructura.'
},
planet: {
title: 'Tu Planeta',
content: 'Este es tu planeta natal. Aquí puedes ver su nombre, coordenadas y cambiar entre planetas a medida que expandes tu imperio.'
},
navigation: {
title: 'Menú de Navegación',
content:
'Usa este menú para navegar entre diferentes secciones: Edificios, Investigación, Flota, Galaxia y más. Cada sección ofrece características de juego únicas.'
},
gotoBuildings: {
title: 'Ir a Edificios',
content:
'Comencemos construyendo algunas estructuras. Haz clic en el elemento de menú "Edificios" para ver las estructuras disponibles.'
},
buildSolarPlant: {
title: 'Construir Planta Solar',
content:
'¡Primero, construye una Planta Solar! Genera energía para tu planeta. Sin energía, otros edificios de recursos no pueden funcionar. Este es el primer paso más importante.'
},
waitBuild: {
title: 'Cola de Construcción',
content:
'Tu edificio está ahora en la cola de construcción. Haz clic en el ícono de la cola en la esquina superior derecha para ver todas las tareas de construcción e investigación en curso. Los edificios tardan tiempo en completarse, pero puedes seguir trabajando mientras esperas.'
},
buildMetalMine: {
title: 'Construir Mina de Metal',
content:
'Ahora que tienes energía, puedes construir la Mina de Metal. Es tu fuente principal de metal, que se usa en casi todas las estructuras y naves.'
},
buildCrystalMine: {
title: 'Construir Mina de Cristal',
content:
'El cristal es más escaso pero esencial para tecnologías avanzadas. Construye una Mina de Cristal para comenzar a recolectar este valioso recurso.'
},
buildDeuterium: {
title: 'Construir Sintetizador de Deuterio',
content:
'El deuterio es esencial para el combustible de las naves y la investigación avanzada. Construye un Sintetizador de Deuterio para comenzar a producir este recurso crítico.'
},
upgradeMines: {
title: 'Mejorar Minas de Recursos',
content:
'A continuación, necesitas mejorar las tres minas de recursos (Metal, Cristal, Deuterio) al nivel 2 para cumplir los requisitos de construcción de una Fábrica de Robots. Mejóralas cuando tengas suficientes recursos.'
},
buildRobotics: {
title: 'Construir Fábrica de Robots',
content:
'La Fábrica de Robots acelera significativamente la construcción. Requiere Mina de Metal, Mina de Cristal y Sintetizador de Deuterio en nivel 2 cada uno. ¡Constrúyela para mejorar la eficiencia de construcción!'
},
upgradeMinesForLab: {
title: 'Continuar Mejorando Minas',
content:
'Ahora necesitas mejorar las tres minas de recursos al nivel 3 para cumplir los requisitos del Laboratorio de Investigación. Continúa desarrollando tu producción de recursos.'
},
buildResearchLab: {
title: 'Construir Laboratorio de Investigación',
content:
'El Laboratorio de Investigación es la base del avance tecnológico. Requiere todas las tres minas de recursos en nivel 3. ¡Constrúyelo para desbloquear la investigación tecnológica!'
},
gotoResearch: {
title: 'Ir a Investigación',
content: 'Ahora que tienes un Laboratorio de Investigación, haz clic en el menú "Investigación" para ver las tecnologías disponibles.'
},
researchEnergy: {
title: 'Investigar Tecnología de Energía',
content:
'La Tecnología de Energía mejora tu producción de energía y desbloquea estructuras avanzadas. Es una de las tecnologías más fundamentales e importantes.'
},
shipyardIntro: {
title: 'Flota y Astillero',
content:
'Las naves te permiten explorar la galaxia, transportar recursos y defender tu imperio. Para construir naves, necesitas un Astillero (requiere Fábrica de Robots nivel 2).'
},
gotoBuildingsForShipyard: {
title: 'Volver a Edificios',
content: 'Regresa a la página de Edificios para construir tu Astillero.'
},
buildShipyard: {
title: 'Construir Astillero',
content: 'El Astillero te permite construir naves y sistemas de defensa. Esto es crucial para las operaciones de flota.'
},
fleetIntro: {
title: 'Operaciones de Flota',
content:
'Una vez que tengas naves, puedes enviarlas en misiones: transportar recursos, colonizar planetas, atacar enemigos o explorar campos de escombros.'
},
galaxyIntro: {
title: 'Explorar la Galaxia',
content:
'La vista de Galaxia muestra otros planetas, campos de escombros y oportunidades de expansión. Úsala para explorar objetivos y planificar tu estrategia.'
},
complete: {
title: '¡Tutorial Completo!',
content:
'¡Felicitaciones, Comandante! Ahora conoces los conceptos básicos. Continúa construyendo tu imperio, investigando tecnologías y explorando la galaxia. ¡Recuerda: desarrolla la energía primero, luego recursos, luego fábricas e investigación! ¡Buena suerte!'
},
// Mobile tutorial
mobile: {
welcome: {
title: 'Bienvenido a OGame (Móvil)',
content:
'¡Bienvenido, Comandante! Este es un tutorial simplificado diseñado para pantallas táctiles. Cubriremos rápidamente las características principales para que comiences a construir tu imperio.'
},
resources: {
title: 'Barra de Recursos Superior',
content: 'En la parte superior, ves tus recursos: Metal, Cristal y Deuterio. Toca para ver información detallada de producción.'
},
menu: {
title: 'Abrir Menú de Navegación',
content:
'Toca este ícono de menú para abrir la barra de navegación. Puedes acceder a Edificios, Investigación, Flota y todas las demás características desde aquí.'
},
gotoBuildings: {
title: 'Ir a Página de Edificios',
content: '¡El menú está ahora abierto! Ahora toca "Edificios" para comenzar a construir tu infraestructura.'
},
buildSolarPlant: {
title: 'Construir Planta Solar',
content:
'¡Primero, construye una Planta Solar! Desplázate hacia abajo para encontrarla y toca la tarjeta para construir. La energía es la base de todo.'
},
waitBuild: {
title: 'Cola de Construcción',
content:
'Haz clic en el ícono de la cola en la esquina superior derecha para ver el progreso de construcción. Puedes seguir navegando por otras páginas: la construcción ocurre en segundo plano.'
},
buildMetalMine: {
title: 'Construir Mina de Metal',
content: 'Ahora que tienes energía, construye una Mina de Metal. Desplázate hacia abajo para encontrarla y toca para construir.'
},
complete: {
title: '¡Tutorial Rápido Completo!',
content:
'¡Genial! Has dominado los conceptos básicos. Continúa construyendo instalaciones de Cristal y Deuterio, luego explora otras características. ¡Recuerda: energía primero, luego recursos!'
}
}
},
hints: {
close: 'Cerrar',
gotIt: 'Entendido',
dontShowAgain: 'No mostrar de nuevo',
resetHints: 'Reiniciar Consejos',
resetHintsDesc: 'Mostrar todos los consejos nuevamente',
hintsEnabled: 'Habilitar Consejos',
hintsEnabledDesc: 'Mostrar consejos útiles al visitar páginas',
overview: {
title: 'Resumen del Planeta',
message:
'Aquí puedes ver los recursos de tu planeta, estado de flota y detalles de producción. ¡Revisa con frecuencia para monitorear tu progreso!'
},
buildings: {
title: 'Edificios',
message:
'Construye y mejora estructuras aquí. Comienza con la Planta Solar para energía, luego minas de recursos. Consejo: ¡La Fábrica de Robots acelera la construcción!'
},
research: {
title: 'Laboratorio de Investigación',
message:
'Investiga tecnologías para desbloquear nuevas naves, mejorar el combate y avanzar tu civilización. ¡La Tecnología de Energía es un gran comienzo!'
},
shipyard: {
title: 'Astillero',
message:
'Construye naves para explorar, transportar recursos y defender tu imperio. Las naves de carga ayudan a mover recursos entre planetas.'
},
fleet: {
title: 'Comando de Flota',
message:
'Envía tus naves en misiones: atacar enemigos, transportar recursos, colonizar nuevos planetas o explorar campos de escombros.'
},
galaxy: {
title: 'Mapa de la Galaxia',
message:
'Explora la galaxia para encontrar planetas vacíos que colonizar, campos de escombros que recolectar y enemigos que atacar. ¡Usa sondas espía primero!'
},
diplomacy: {
title: 'Diplomacia',
message:
'Gestiona relaciones con NPCs. Envía regalos para mejorar la reputación, o enfrenta ataques hostiles. ¡Los aliados de tus enemigos también pueden volverse hostiles!'
},
messages: {
title: 'Mensajes',
message:
'Ve reportes de batalla, reportes de espionaje y notificaciones diplomáticas aquí. Mantén un registro de tus actividades y encuentros con enemigos.'
},
defense: {
title: 'Defensa Planetaria',
message:
'Construye estructuras de defensa para proteger tu planeta de ataques. ¡Las cúpulas de escudo y torretas pueden disuadir a los asaltantes!'
},
officers: {
title: 'Oficiales',
message:
'¡Recluta oficiales para obtener varios bonos! El Comandante acelera la construcción, el Geólogo aumenta la producción de recursos, el Almirante mejora las capacidades de flota.'
},
simulator: {
title: 'Simulador de Batalla',
message:
'Simula resultados de batalla antes de atacar. Ingresa ambas flotas y niveles tecnológicos para predecir victoria, pérdidas y botín.'
},
campaign: {
title: 'Modo Campaña',
message:
'¡Explora la campaña de historia galáctica! Completa misiones para obtener recompensas de recursos y desbloquear nuevos desafíos. Cada nodo tiene objetivos y enemigos únicos.'
},
achievements: {
title: 'Sistema de Logros',
message:
'¡Completa objetivos del juego para desbloquear logros y ganar recompensas de Materia Oscura! Los logros tienen múltiples niveles: apunta a desafíos más altos para obtener mejores recompensas.'
},
ranking: {
title: 'Ranking',
message:
'Compara tu progreso con otros jugadores y NPCs. Consulta rankings basados en puntos de edificios, investigación, flota y defensa. ¡Esfuérzate por escalar en la tabla de clasificación!'
},
settings: {
title: 'Configuración',
message:
'Gestiona datos del juego, ajusta notificaciones, exporta/importa guardados aquí. ¡Recuerda respaldar tu progreso regularmente!'
},
gm: {
title: 'Panel GM',
message:
'El modo GM permite la modificación rápida de recursos, edificios y niveles tecnológicos. Úsalo para pruebas o para experimentar el contenido completo del juego.'
}
},
achievements: {
title: 'Logros',
unlocked: 'Logro Desbloqueado',
progress: 'Progreso',
nextTier: 'Siguiente Nivel',
maxTierReached: 'Nivel Máximo Alcanzado',
tiers: {
bronze: 'Bronce',
silver: 'Plata',
gold: 'Oro',
platinum: 'Platino',
diamond: 'Diamante'
},
categories: {
resource: 'Recurso',
building: 'Edificio',
combat: 'Combate',
mission: 'Misión',
diplomacy: 'Diplomacia'
},
names: {
metalCollector: 'Colector de Metal',
crystalCollector: 'Colector de Cristal',
deuteriumCollector: 'Colector de Deuterio',
darkMatterCollector: 'Colector de Materia Oscura',
resourceConsumer: 'Consumidor de Recursos',
masterBuilder: 'Maestro Constructor',
researcher: 'Investigador',
shipwright: 'Constructor de Naves',
fortifier: 'Fortificador',
warmonger: 'Belicista',
conqueror: 'Conquistador',
defender: 'Defensor',
fleetDestroyer: 'Destructor de Flotas',
debrisCreator: 'Creador de Escombros',
fleetSacrifice: 'Sacrificio de Flota',
defenseSacrifice: 'Sacrificio de Defensa',
pilot: 'Piloto',
transporter: 'Transportista',
cargoMaster: 'Maestro de Carga',
colonizer: 'Colonizador',
spy: 'Maestro Espía',
deployer: 'Desplegador',
explorer: 'Explorador',
luckyExplorer: 'Explorador Afortunado',
recycler: 'Reciclador',
scavenger: 'Recolector',
destroyer: 'Destructor',
fuelBurner: 'Quemador de Combustible',
diplomat: 'Diplomático',
nemesis: 'Némesis',
generous: 'Generoso',
philanthropist: 'Filántropo',
target: 'Objetivo',
watched: 'Vigilado',
robbed: 'Robado',
lostToNPC: 'Perdido ante NPC'
},
descriptions: {
metalCollector: 'Total de metal producido',
crystalCollector: 'Total de cristal producido',
deuteriumCollector: 'Total de deuterio producido',
darkMatterCollector: 'Total de materia oscura producida',
resourceConsumer: 'Total de recursos consumidos',
masterBuilder: 'Total de edificios mejorados',
researcher: 'Total de investigaciones completadas',
shipwright: 'Total de naves producidas',
fortifier: 'Total de defensas construidas',
warmonger: 'Total de ataques lanzados',
conqueror: 'Total de ataques ganados',
defender: 'Total de defensas exitosas',
fleetDestroyer: 'Flota enemiga destruida en defensa',
debrisCreator: 'Total de escombros creados en batallas',
fleetSacrifice: 'Total de flota perdida',
defenseSacrifice: 'Total de defensas perdidas en defensa',
pilot: 'Total de misiones de vuelo',
transporter: 'Total de misiones de transporte',
cargoMaster: 'Total de recursos transportados',
colonizer: 'Planetas colonizados',
spy: 'Misiones de espionaje completadas',
deployer: 'Misiones de despliegue completadas',
explorer: 'Total de expediciones',
luckyExplorer: 'Expediciones exitosas',
recycler: 'Total de misiones de reciclaje',
scavenger: 'Total de recursos reciclados',
destroyer: 'Planetas destruidos',
fuelBurner: 'Total de combustible consumido',
diplomat: 'Número de NPCs amigables',
nemesis: 'Número de NPCs hostiles',
generous: 'Total de regalos enviados',
philanthropist: 'Total de recursos regalados',
target: 'Veces atacado por NPC',
watched: 'Veces espiado por NPC',
robbed: 'Veces que escombros fueron reciclados por NPC',
lostToNPC: 'Total de recursos de escombros perdidos ante NPC'
}
},
ranking: {
title: 'Clasificación',
totalPlayers: '{count} Jugadores',
yourRanking: 'Tu Clasificación',
categories: {
total: 'Total',
building: 'Edificio',
research: 'Investigación',
fleet: 'Flota',
defense: 'Defensa'
},
points: 'pts',
name: 'Nombre',
planets: 'Planetas',
details: 'Detalles',
you: 'Tú',
scoreBreakdown: 'Desglose de Puntuación',
noData: 'Sin datos de clasificación'
},
campaign: {
name: 'Campaña',
description: 'Explora la misteriosa galaxia y descubre secretos antiguos',
totalProgress: 'Progreso Total',
questsCompleted: 'Misiones Completadas',
chapter: 'Capítulo',
branch: 'Rama',
startQuest: 'Iniciar Misión',
claimRewards: 'Reclamar Recompensas',
objectives: 'Objetivos',
rewards: 'Recompensas',
completed: 'Completado',
inProgress: 'En Progreso',
available: 'Disponible',
locked: 'Bloqueado',
notifications: {
questStarted: 'Misión iniciada',
questCompleted: '¡Misión completada!',
rewardsClaimed: 'Recompensas reclamadas',
objectiveCompleted: 'Objetivo completado',
chapterUnlocked: 'Nuevo capítulo desbloqueado',
reputationUp: 'Reputación con {npcName} aumentada en {value}',
reputationDown: 'Reputación con {npcName} disminuida en {value}',
branchUnlocked: '¡Nueva rama de historia desbloqueada!'
},
dialogue: {
skip: 'Saltar',
continue: 'Continuar',
finish: 'Finalizar',
player: 'Comandante',
npc: 'NPC',
narrator: 'Narrador',
mysterious: 'Señal Misteriosa',
unknownSource: 'Fuente desconocida',
choiceEffect: 'Efecto de elección de diálogo'
},
chapters: {
'1': {
title: 'Origen',
description: 'Construye tu hogar y da el primer paso al espacio',
backgroundStory:
'Eres un joven comandante espacial que acaba de adquirir tu primer planeta. En este vasto universo, construirás tu hogar, desarrollarás tecnología y explorarás las profundidades de la galaxia...'
},
'2': {
title: 'Exploración',
description: 'Explora el universo y descubre ruinas antiguas',
backgroundStory:
'A medida que tu poder crece, señales misteriosas del espacio profundo captan tu atención. Estas señales parecen apuntar a un secreto antiguo, esperando que exploradores valientes lo descubran...'
},
'3': {
title: 'Diplomacia',
description: 'Establece conexiones con otras facciones',
backgroundStory:
'No estás solo en la galaxia. Otras civilizaciones están surgiendo. Debes decidir si ser su enemigo o aliado. La sabiduría diplomática determinará hasta dónde puede llegar tu imperio...'
},
'4': {
title: 'Ascenso de las Sombras',
description: 'Enfrenta enemigos poderosos y defiende tu territorio',
backgroundStory:
'El peligro acecha en las sombras. Una poderosa fuerza hostil ha puesto los ojos en tu territorio. La guerra es inevitable. Debes prepararte para enfrentar la tormenta que se avecina...'
},
'5': {
title: 'Secretos Antiguos',
description: 'Descubre los secretos más profundos de la galaxia',
backgroundStory:
'Todas las pistas apuntan a la región más misteriosa de la galaxia. Allí, los secretos últimos dejados por civilizaciones antiguas esperan. ¿Estás listo para descubrir todo?'
}
},
quests: {
'1_1': {
title: 'Construcción del Hogar',
description: 'Construye infraestructura para sentar las bases de tu planeta'
},
'1_2': {
title: 'Iluminación Tecnológica',
description: 'Investiga tecnología básica para comenzar tu viaje tecnológico'
},
'1_3': {
title: 'Primera Nave',
description: 'Construye tu primera nave de guerra'
},
'1_4': {
title: 'Vecinos Extraños',
description: 'Explora otras facciones en sistemas cercanos'
},
'1_5': {
title: 'Primer Contacto',
description: 'Establece contacto inicial con facciones NPC cercanas'
},
'2_1': {
title: 'Colonia Pionera',
description: 'Coloniza tu primer planeta nuevo'
},
'2_2': {
title: 'Expedición al Espacio Profundo',
description: 'Envía flota en misiones de expedición'
},
'2_3': {
title: 'Señal Misteriosa',
description: 'Investiga señales misteriosas del espacio profundo'
},
'2_4': {
title: 'Investigación de Ruinas',
description: 'Explora las ruinas antiguas descubiertas'
},
'2_5': {
title: 'Descifrar Archivos',
description: 'Investiga datos obtenidos de las ruinas'
},
'3_1': {
title: 'Pacificador',
description: 'Mejora relaciones con NPCs a través de la diplomacia'
},
'3_2': {
title: 'Relaciones Comerciales',
description: 'Establece relaciones estables con facciones amigables'
},
'3_3': {
title: 'Amenaza Común',
description: 'Descubre fuerzas hostiles potenciales'
},
'3_4': {
title: 'Negociaciones de Alianza',
description: 'Forma una alianza formal con NPCs amigables'
},
'3_5': {
title: 'Preparación para la Tormenta',
description: 'Construye defensas para prepararte para los desafíos'
},
'4_1': {
title: 'Ataque al Puesto',
description: 'Repele el primer ataque de fuerzas hostiles'
},
'4_2': {
title: 'Recopilación de Inteligencia',
description: 'Explora despliegues militares enemigos'
},
'4_3': {
title: 'Contraataque',
description: 'Lanza un contraataque contra el enemigo'
},
'4_4': {
title: 'Disputa de Recursos',
description: 'Recicla escombros del campo de batalla para obtener recursos'
},
'4_5': {
title: 'Víspera de Batalla',
description: 'Construye una flota poderosa para la batalla final'
},
'5_1': {
title: 'Profundidades de las Ruinas',
description: 'Explora las partes más profundas de las ruinas'
},
'5_2': {
title: 'Tecnología Antigua',
description: 'Desbloquea tecnología de civilización antigua'
},
'5_3': {
title: 'Confrontación Final',
description: 'Enfrenta la batalla final contra el enemigo misterioso'
},
'5_4': {
title: 'Nueva Era',
description: 'Establece nuevas colonias y comienza una nueva era'
},
'5_5': {
title: 'El Legado Continúa',
description: 'Continúa desarrollando y conquistando más sistemas'
}
},
objectiveTypes: {
buildBuilding: 'Construir {building} al nivel {level}',
researchTech: 'Investigar {tech} al nivel {level}',
produceShips: 'Producir {count} {ship}',
accumulateResources: 'Acumular {amount} {resource}',
defeatNPC: 'Derrotar {npc}',
winBattles: 'Ganar {count} batallas',
recycleDebris: 'Reciclar {amount} de escombros',
reachRelation: 'Alcanzar relación {level} con {npc}',
sendGift: 'Enviar {count} regalos a {npc}',
formAlliance: 'Formar alianza con {npc}',
colonize: 'Colonizar {count} planetas',
expedition: 'Completar {count} expediciones',
spyTarget: 'Espiar {target}'
},
errors: {
questNotFound: 'Misión no encontrada',
questNotAvailable: 'Misión no disponible',
questNotActive: 'Misión no activa',
questNotCompleted: 'Misión no completada',
rewardsAlreadyClaimed: 'Recompensas ya reclamadas',
prerequisiteNotMet: 'Misión prerequisito no completada'
},
speakers: {
ancientVoice: 'Voz Antigua',
neighborNPC: 'Facción Vecina',
mysteriousSignal: 'Señal Misteriosa',
enemyCommander: 'Comandante Enemigo'
},
objectiveDescriptions: {
buildMetalMine: 'Construir Mina de Metal al nivel 2',
buildCrystalMine: 'Construir Mina de Cristal al nivel 2',
buildSolarPlant: 'Construir Planta Solar al nivel 2',
buildResearchLab: 'Construir Laboratorio de Investigación al nivel 1',
researchEnergy: 'Investigar Tecnología de Energía al nivel 1',
buildShipyard: 'Construir Astillero al nivel 2',
researchCombustion: 'Investigar Motor de Combustión al nivel 1',
buildLightFighters: 'Construir 5 Cazadores Ligeros',
researchEspionage: 'Investigar Tecnología de Espionaje al nivel 2',
buildSpyProbes: 'Construir 3 Sondas de Espionaje',
spyAnyNPC: 'Espiar cualquier planeta NPC',
sendGiftToNPC: 'Enviar un regalo a cualquier NPC',
researchAstrophysics: 'Investigar Astrofísica al nivel 1',
buildColonyShip: 'Construir una Nave Colonial',
colonizeNewPlanet: 'Colonizar un nuevo planeta',
completeExpedition: 'Completar una misión de expedición',
discoverRuins: 'Descubrir ruinas antiguas',
researchComputer: 'Investigar Tecnología de Computación al nivel 4',
improveRelation: 'Mejorar relaciones con un NPC',
reachFriendly: 'Alcanzar estatus amigable con un NPC',
spyHostileNPC: 'Espiar un NPC hostil',
formAlliance: 'Formar alianza con un NPC amigable',
buildDefenses: 'Construir instalaciones de defensa',
winDefenseBattle: 'Ganar una batalla defensiva',
spyEnemyPlanet: 'Espiar planeta enemigo',
attackEnemy: 'Atacar al enemigo',
recycleDebris: 'Reciclar escombros',
buildBattleships: 'Construir 10 Naves de Batalla',
exploreDeepRuins: 'Explorar ruinas profundas',
researchHyperspace: 'Investigar Tecnología de Hiperespacio',
defeatBoss: 'Derrotar al enemigo misterioso',
colonizeSpecial: 'Colonizar ubicación especial',
continueDevelopment: 'Continuar desarrollo'
},
dialogues: {
'1_1': {
prologue_1:
'Bienvenido a la galaxia, joven comandante. Este vasto universo espera tu exploración. Primero, construyamos tu planeta natal.',
prologue_2: 'Siento una nueva consciencia despertando... Interesante... Veamos hasta dónde puedes llegar...'
},
'1_2': {
prologue_1:
'La infraestructura básica está completa. Ahora es momento de desarrollar tecnología. Construye un Laboratorio de Investigación y comienza tu viaje tecnológico.'
},
'1_3': {
prologue_1:
'Con apoyo tecnológico, puedes comenzar a construir tu flota. Construye un Astillero y produce tu primera nave de guerra.'
},
'1_4': {
prologue_1:
'Tu flota está tomando forma. Ahora aprendamos sobre tus alrededores. Envía sondas espía para explorar facciones cercanas.',
prologue_2: 'No estás solo... Otras civilizaciones existen en esta galaxia...'
},
'1_5': {
prologue_1: 'Has descubierto facciones cercanas. La diplomacia es un arte. Intenta establecer contacto con ellos.',
epilogue_1: 'Gracias por tu regalo, comandante. Espero que podamos ser amigos.',
epilogue_2: 'Bien... Establecer conexiones es el primer paso para descubrir secretos más profundos...'
},
'2_1': {
prologue_1:
'Tu poder está establecido. Es momento de expandir tu territorio. Investiga Astrofísica, construye una nave colonial y explora nuevos planetas.',
prologue_2: 'El universo es infinito... Más planetas significan más posibilidades...'
},
'2_2': {
prologue_1: '¡Colonización exitosa! Pero secretos más profundos esperan en el universo. Envía tu flota en misiones de expedición.',
prologue_2: 'Señales débiles desde lejos... Algo te espera allí...'
},
'2_3': {
prologue_1:
'Tu expedición descubrió señales anómalas. Estas señales parecen venir de una civilización antigua... Investiga su origen.',
epilogue_1: 'Estos símbolos... ¡Son ruinas de una civilización antigua! Continúa investigando para descubrir sus secretos.'
},
'2_4': {
prologue_1: 'Has encontrado la ubicación de ruinas antiguas. Envía tu flota a explorar y ver qué puedes descubrir.'
},
'2_5': {
prologue_1: 'Se encontraron archivos de datos en las ruinas. Estudia estos datos, quizás puedas desbloquear nueva tecnología.'
},
'3_1': {
prologue_1: 'Mientras exploras, no olvides la diplomacia. Mantener buenas relaciones con las facciones circundantes te beneficia.'
},
'3_2': {
prologue_1: 'Algunas facciones han mostrado amabilidad. Continúa profundizando las relaciones, quizás puedas obtener más apoyo.'
},
'3_3': {
prologue_1:
'La inteligencia indica que fuerzas hostiles te están vigilando desde las sombras. Mantente alerta y explora sus movimientos.'
},
'3_4': {
prologue_1: 'Establece una alianza formal con facciones amigables para apoyarse mutuamente contra las amenazas.'
},
'3_5': {
prologue_1: 'Las amenazas se acercan. Construye instalaciones de defensa y prepárate para posibles conflictos.'
},
'4_1': {
prologue_1: '¡El enemigo ha lanzado un ataque! ¡Defiende tu planeta!',
epilogue_1: 'Repeliste exitosamente la primera ola del enemigo. Pero esto es solo el comienzo...'
},
'4_2': {
prologue_1: 'El enemigo se ha retirado, pero regresarán. Explora sus planetas para entender su fuerza.'
},
'4_3': {
prologue_1: 'Es momento de contraatacar. Ataca los planetas enemigos y debilita sus fuerzas.'
},
'4_4': {
prologue_1: 'Muchos escombros permanecen en el campo de batalla. Recicla estos recursos para prepararte para la próxima batalla.'
},
'4_5': {
prologue_1: 'La batalla final se acerca. Construye una flota poderosa y prepárate para el desafío definitivo.'
},
'5_1': {
prologue_1:
'Todas las pistas apuntan a la parte más profunda de las ruinas. Los secretos centrales de la civilización antigua yacen allí.',
prologue_2: 'Finalmente has llegado... La verdad pronto será revelada...'
},
'5_2': {
prologue_1: 'En las profundidades de las ruinas, descubriste tecnología antigua perdida. Investiga y desbloquea su poder.'
},
'5_3': {
prologue_1: 'Ha aparecido un enemigo misterioso. Este es el desafío final. ¡Derrótalo!',
epilogue_1: '¡Lo lograste! El guardián antiguo ha sido derrotado. Los secretos de la galaxia ahora están abiertos para ti.'
},
'5_4': {
prologue_1: 'La paz ha llegado finalmente. En esta nueva era, establece nuevas colonias y expande tu imperio.'
},
'5_5': {
prologue_1: 'Tu leyenda acaba de comenzar. ¡Continúa explorando y conquistando más sistemas estelares!',
epilogue_1: 'La galaxia es vasta e infinita, con innumerables secretos esperando que los descubras...'
}
}
},
// NPC Enhanced Behavior Notifications
npcBehavior: {
// Neutral NPC behavior
tradeOfferReceived: 'Oferta Comercial Recibida',
tradeOfferDesc: '{npcName} te ha enviado una oferta comercial',
attitudeChanged: 'Actitud de NPC Cambiada',
becameFriendly: '{npcName} se ha vuelto amigable hacia ti',
becameHostile: '{npcName} se ha vuelto hostil hacia ti',
// Friendly NPC behavior
intelReceived: 'Inteligencia Recibida',
intelReceivedDesc: '{npcName} ha compartido inteligencia enemiga contigo',
jointAttackInvite: 'Invitación de Ataque Conjunto',
jointAttackInviteDesc: '{npcName} te invita a atacar a un enemigo juntos',
aidReceived: 'Ayuda Recibida',
aidReceivedDesc: '{npcName} te ha enviado {amount} recursos',
allyDefense: 'Defensa de Aliado',
allyDefenseDesc: '{npcName} está enviando una flota para ayudar a defender tu planeta',
// Trade related
trade: {
title: 'Ofertas Comerciales',
from: 'De',
offers: 'Ofrece',
requests: 'Solicita',
expiresIn: 'Expira En',
expired: 'Expirado',
accept: 'Aceptar',
decline: 'Rechazar',
noOffers: 'Sin ofertas comerciales',
acceptSuccess: '¡Comercio completado!',
acceptFailed: 'Recursos insuficientes para completar el comercio',
declined: 'Comercio rechazado',
ratio: 'Tasa de Cambio'
},
// Intel related
intel: {
title: 'Reportes de Inteligencia',
from: 'Fuente',
target: 'NPC Objetivo',
type: 'Tipo de Inteligencia',
types: {
enemyFleet: 'Inteligencia de Flota',
enemyResources: 'Inteligencia de Recursos',
enemyMovement: 'Inteligencia de Movimiento'
},
fleetInfo: 'Información de Flota',
resourceInfo: 'Información de Recursos',
movementInfo: 'Información de Movimiento',
noReports: 'Sin reportes de inteligencia',
markAsRead: 'Marcar como Leído',
content: 'Contenido de Inteligencia',
noFleet: 'No se detectó flota',
noData: 'Sin datos disponibles',
targetPosition: 'Posición Objetivo',
missionType: 'Tipo de Misión'
},
// Joint attack related
jointAttack: {
title: 'Invitaciones de Ataque Conjunto',
from: 'Iniciador',
target: 'NPC Objetivo',
targetPlanet: 'Planeta Objetivo',
npcFleet: 'Flota NPC',
lootShare: 'Reparto de Botín',
expiresIn: 'Expira En',
expired: 'Expirado',
accept: 'Unirse al Ataque',
decline: 'Rechazar',
noInvites: 'Sin invitaciones de ataque conjunto',
acceptSuccess: '¡Te has unido al ataque conjunto!',
declined: 'Invitación rechazada',
targetInfo: 'Objetivo del Ataque',
expectedShare: 'Parte Esperada',
remaining: 'Tiempo Restante'
},
// Aid related
aid: {
title: 'Ayuda de Recursos',
from: 'Fuente',
resources: 'Recursos de Ayuda',
noAid: 'Sin registros de ayuda'
},
// Attitude change related
attitudeChange: {
title: 'Cambios de Actitud',
npc: 'NPC',
previous: 'Anterior',
current: 'Actual',
reason: 'Razón',
reasons: {
naturalSwing: 'Cambio natural',
giftReceived: 'Regalo recibido',
attacked: 'Fue atacado',
reputationThreshold: 'Umbral de reputación',
attitude_swing: 'Oscilación de actitud',
gift: 'Recibió regalo',
attack: 'Sufrió ataque'
}
},
// Ally actions related
allyAction: {
title: 'Acciones de Aliado',
defense: 'Apoyo Defensivo',
defenseDesc: '{npcName} está enviando flota para ayudar a defender {targetPlanet}',
jointAttackStarted: 'Ataque Conjunto Iniciado',
jointAttackStartedDesc: 'El ataque conjunto contra {targetNpc} ha comenzado',
reputationBonus: 'Bonificación de Reputación',
reputationBonusDesc: 'Tu aliado {npcName} habla bien de ti ante {targetNpc}'
}
}
}

View File

@@ -1,14 +1,73 @@
// 默认语言(打包在主包中)
import zhCN from './zh-CN'
import zhTW from './zh-TW'
import en from './en'
import de from './de'
import ru from './ru'
import ko from './ko'
import ja from './ja'
export type Locale = 'zh-CN' | 'zh-TW' | 'en' | 'de' | 'ru' | 'ko' | 'ja'
export type Locale = 'zh-CN' | 'zh-TW' | 'en' | 'de' | 'ru' | 'ko' | 'ja' | 'es-LA'
export const locales = { 'zh-CN': zhCN, 'zh-TW': zhTW, en, de, ru, ko, ja }
export type TranslationSchema = typeof zhCN
// 语言加载器 - 使用动态导入实现按需加载
// 注意: 各语言文件可能有细微差异,缺失的键会在运行时回退到 key 本身
type LocaleModule = { default: TranslationSchema }
const localeLoaders: Record<Locale, () => Promise<LocaleModule>> = {
'zh-CN': () => Promise.resolve({ default: zhCN }),
'zh-TW': () => import('./zh-TW') as unknown as Promise<LocaleModule>,
en: () => import('./en') as unknown as Promise<LocaleModule>,
de: () => import('./de') as unknown as Promise<LocaleModule>,
ru: () => import('./ru') as unknown as Promise<LocaleModule>,
ko: () => import('./ko') as unknown as Promise<LocaleModule>,
ja: () => import('./ja') as unknown as Promise<LocaleModule>,
'es-LA': () => import('./es-LA') as unknown as Promise<LocaleModule>
}
// 已加载的语言缓存
const loadedLocales: Partial<Record<Locale, TranslationSchema>> = {
'zh-CN': zhCN // 默认语言预加载
}
/**
* 异步加载指定语言
* @param locale 语言代码
* @returns 语言翻译对象
*/
export const loadLocale = async (locale: Locale): Promise<TranslationSchema> => {
// 如果已经加载过,直接返回缓存
if (loadedLocales[locale]) {
return loadedLocales[locale]!
}
// 动态加载语言文件
const loader = localeLoaders[locale]
if (!loader) {
console.warn(`Locale "${locale}" not found, falling back to zh-CN`)
return zhCN
}
try {
const module = await loader()
loadedLocales[locale] = module.default
return module.default
} catch (error) {
console.error(`Failed to load locale "${locale}":`, error)
return zhCN
}
}
/**
* 同步获取已加载的语言(如果未加载则返回默认语言)
* @param locale 语言代码
* @returns 语言翻译对象
*/
export const getLocale = (locale: Locale): TranslationSchema => {
return loadedLocales[locale] || zhCN
}
/**
* 检查语言是否已加载
* @param locale 语言代码
*/
export const isLocaleLoaded = (locale: Locale): boolean => {
return locale in loadedLocales
}
export const localeNames: Record<Locale, string> = {
'zh-CN': '简体中文',
@@ -17,7 +76,8 @@ export const localeNames: Record<Locale, string> = {
de: 'Deutsch',
ru: 'Русский',
ko: '한국어',
ja: '日本語'
ja: '日本語',
'es-LA': 'Español (LA)'
}
/**
@@ -44,10 +104,9 @@ export const detectBrowserLocale = (): Locale => {
return 'de'
} else if (lang.startsWith('ru')) {
return 'ru'
} else if (lang.startsWith('es')) {
return 'es-LA'
}
// 默认返回简体中文
return 'zh-CN'
}
export type TranslationSchema = typeof zhCN

View File

@@ -11,29 +11,11 @@ export default {
common: {
confirm: '確認',
cancel: 'キャンセル',
delete: '削除',
edit: '編集',
save: '保存',
close: '閉じる',
back: '戻る',
next: '次へ',
gotIt: '',
previous: '前へ',
submit: '送信',
reset: 'リセット',
search: '検索',
filter: 'フィルター',
loading: '読み込み中...',
noData: 'データなし',
error: 'エラー',
success: '成功',
warning: '警告',
info: '情報',
gotIt: '了解',
resourceType: '資源タイプ',
playerName: '司令官',
timeHour: '時間',
timeMinute: '分',
timeSecond: '秒',
featureLocked: '機能がロックされています',
unlockRequired: '建物が必要です',
requiredBuilding: '必要な建物',
@@ -41,7 +23,6 @@ export default {
goToBuildings: '建物へ移動',
locked: 'ロック済み',
viewRequirements: '必要条件を表示',
requirements: '必要条件',
requirementsNotMet: '必要条件が満たされていません',
current: '現在',
level: 'レベル',
@@ -51,7 +32,8 @@ export default {
viewDetails: '詳細を見る',
exitConfirmTitle: 'ゲーム終了',
exitConfirmMessage: 'ゲームを終了しますか?進行状況は自動的に保存されます。',
points: 'ポイント'
points: 'ポイント',
retry: '再試行'
},
errors: {
requirementsNotMet: '前提条件を満たしていません',
@@ -87,6 +69,7 @@ export default {
simulator: 'シミュレーター',
galaxy: '銀河',
diplomacy: '外交',
campaign: 'キャンペーン',
achievements: '実績',
ranking: 'ランキング',
messages: 'メッセージ',
@@ -180,8 +163,10 @@ export default {
sensorPhalanx: 'センサーファランクス',
jumpGate: 'ジャンプゲート',
planetDestroyerFactory: '惑星破壊工場',
geoResearchStation: '地質研究所',
deepDrillingFacility: '深部掘削施設',
buildTime: '建設時間',
build: '',
build: '建設',
production: '生産量',
consumption: '消費',
totalCost: '総コスト',
@@ -229,7 +214,9 @@ export default {
lunarBase: '月の利用可能スペースを増加、レベル毎に+30スペース',
sensorPhalanx: '周辺星系の艦隊活動を探知',
jumpGate: '他の月へ艦隊を瞬間移動',
planetDestroyerFactory: '惑星を破壊できる究極兵器を建造'
planetDestroyerFactory: '惑星を破壊できる究極兵器を建造',
geoResearchStation: '地質構造を研究し、鉱脈の自然回復速度を向上。レベル毎に回復速度50%増加',
deepDrillingFacility: ''
},
ships: {
lightFighter: '軽戦闘機',
@@ -303,7 +290,16 @@ export default {
buildQueue: 'Build Queue',
planetSpace: 'Planet Space',
moonSpace: 'Moon Space',
missileCapacity: 'Missile Capacity'
missileCapacity: 'Missile Capacity',
attackBonus: '攻撃ボーナス',
shieldBonus: 'シールドボーナス',
armorBonus: '装甲ボーナス',
spyLevel: '偵察レベル',
researchQueueBonus: '研究キュー',
colonySlots: '植民地スロット',
forAllPlanets: '(全惑星)',
speedBonus: '速度ボーナス',
researchSpeedBonus: '研究速度ボーナス'
},
technologies: {
energyTechnology: 'エネルギー技術',
@@ -323,7 +319,8 @@ export default {
hyperspaceDrive: 'ハイパースペースドライブ',
darkMatterTechnology: 'ダークマター技術',
terraformingTechnology: 'テラフォーミング技術',
planetDestructionTech: '惑星破壊技術'
planetDestructionTech: '惑星破壊技術',
miningTechnology: ''
},
technologyDescriptions: {
energyTechnology: 'エネルギー利用効率を向上',
@@ -344,7 +341,8 @@ export default {
hyperspaceDrive: '高級推進技術',
darkMatterTechnology: 'ダークマターの性質と応用を研究',
terraformingTechnology: '惑星地形改造技術を研究、レベル毎に全惑星の利用可能スペース30増加',
planetDestructionTech: '惑星全体を破壊する恐怖の技術を研究'
planetDestructionTech: '惑星全体を破壊する恐怖の技術を研究',
miningTechnology: ''
},
officers: {
commander: '司令官',
@@ -379,8 +377,12 @@ export default {
cancelResearch: '研究キャンセル',
confirmCancel: 'キャンセルしますか資源の50%が返還されます。',
level: 'レベル',
gmModeActivated: '',
gmModeActivated: 'GMモードが有効になりましたナビゲーションメニューを確認してください。',
upgradeToLevel: 'レベルにアップグレード',
buildQueueBonus: '建設キュー',
spaceBonus: '空間ボーナス',
researchQueueBonus: '研究キュー',
quantity: '数量',
tabs: {
all: 'すべて',
buildings: '建物',
@@ -407,7 +409,7 @@ export default {
fuelConsumption: '燃料消費',
buildCost: '建設コスト',
buildTime: '建設時間',
build: '',
build: '建設',
perUnit: 'ユニットあたり',
batchCalculator: '一括計算機',
quantity: '数量',
@@ -435,7 +437,6 @@ export default {
usedSpace: '使用済みスペース',
spaceUsage: 'スペース使用量',
level: 'レベル',
gmModeActivated: '',
upgradeCost: 'アップグレードコスト',
buildTime: '建設時間',
build: '建設',
@@ -466,7 +467,7 @@ export default {
armor: '装甲',
buildCost: '建設コスト',
buildTime: '建設時間',
build: '',
build: '建設',
perUnit: 'ユニットあたり',
batchCalculator: '一括計算機',
quantity: '数量',
@@ -537,6 +538,7 @@ export default {
missionInfo: 'ミッション情報',
fuelConsumption: '燃料消費',
flightTime: '飛行時間',
outOfRange: '射程外',
attackMission: '攻撃',
transport: '輸送',
colonize: '植民',
@@ -578,11 +580,12 @@ export default {
arrivalTime: '到着時刻',
returnTime: '帰還時刻',
recallFleet: '艦隊召還',
abortMission: '',
abortMissionTitle: '',
abortMissionWarning: '',
abortMissionSuccess: '',
abortMissionSuccessMessage: '',
abortMission: 'ミッション中止',
abortMissionTitle: 'ミッション中止の確認',
abortMissionWarning:
'警告: このミッションを中止すると、{ships}隻の艦船と{resources}の資源が永久に失われます!\n\nこの操作は取り消すことができず、艦隊と資源は戻ってきません。',
abortMissionSuccess: 'ミッション中止',
abortMissionSuccessMessage: 'ミッションが中止され、艦隊と資源は失われました。',
sendFailed: '派遣失敗',
sendFailedMessage: '艦隊数、燃料の充足、または積載量の制限を確認してください。',
recallFailed: '召還失敗',
@@ -637,7 +640,8 @@ export default {
jumpGateSuccess: 'ジャンプゲート転送成功',
jumpGateSuccessMessage: '艦隊は{target}へ瞬時に転送されました',
jumpGateFailed: 'ジャンプゲート転送失敗',
jumpGateFailedMessage: 'ジャンプゲートの状態と艦隊構成を確認してください'
jumpGateFailedMessage: 'ジャンプゲートの状態と艦隊構成を確認してください',
destroy: '破壊'
},
officersView: {
title: '士官',
@@ -709,10 +713,12 @@ export default {
missileCount: 'ミサイル数',
availableMissiles: '利用可能なミサイル',
missileRange: 'ミサイル射程',
systems: 'システム',
systems: '星系',
distance: '距離',
flightTime: '飛行時間',
outOfRange: '射程外',
launchMissile: '発射',
missileLaunched: 'ミサイル発射完了',
cancel: 'キャンセル',
colonizePlanetMessage: '位置[{coordinates}]を植民しますか?\n\n艦隊ページに移動してコロニーシップを派遣してください。',
recyclePlanetMessage: '位置[{coordinates}]のデブリを回収しますか?\n\n艦隊ページに移動してリサイクラーを派遣してください。',
@@ -721,6 +727,25 @@ export default {
giftPlanetTitle: 'ギフト送信',
giftPlanetMessage: '惑星[{coordinates}]にリソースを贈りますか?\n\n艦隊ページに移動して輸送船を選択し、リソースを積載してください。',
npcPlanetName: '{name}の惑星',
// センサーアレイスキャン
phalanxScan: 'センサースキャン',
phalanxScanTitle: 'センサーアレイスキャン',
phalanxScanDescription: '惑星 [{coordinates}] の艦隊活動をスキャン',
phalanxNoMoon: 'センサーアレイを持つ月が必要です',
phalanxOutOfRange: 'ターゲットがスキャン範囲外です',
phalanxRange: 'スキャン範囲',
phalanxCost: 'スキャンコスト',
phalanxNoFleets: '艦隊活動が検出されませんでした',
phalanxFleetDetected: '{count}隻の艦隊を検出',
phalanxMission: 'ミッション',
phalanxOrigin: '出発地',
phalanxDestination: '目的地',
phalanxArrival: '到着時刻',
phalanxReturn: '帰還時刻',
phalanxStatus: 'ステータス',
phalanxStatusOutbound: '往路中',
phalanxStatusReturning: '帰還中',
phalanxInsufficientDeuterium: '重氢不足',
intercepted: '迎撃済み',
defenseLosses: '防衛損失'
},
@@ -729,12 +754,12 @@ export default {
battles: '戦闘',
spy: 'スパイ',
npc: 'NPC',
diplomacy: '',
diplomacy: '外交',
battleReports: '戦闘レポート',
spyReports: 'スパイレポート',
noBattleReports: '戦闘レポートなし',
noSpyReports: 'スパイレポートなし',
noDiplomaticReports: '',
noDiplomaticReports: '外交レポートなし',
battleReport: '戦闘レポート',
spyReport: 'スパイレポート',
victory: '勝利',
@@ -755,6 +780,8 @@ export default {
defense: '防衛',
buildings: '建物',
unread: '未読',
pending: '保留中',
invalidData: '無効なデータ',
targetPlanet: '目標惑星',
attackerRemaining: '攻撃側残存',
defenderRemaining: '防御側残存',
@@ -807,36 +834,36 @@ export default {
polite_decline: '丁重に断りました'
},
// Spied notification dialog
spiedNotificationDetails: '',
spyDetected: '',
detectionResult: '',
detectionSuccess: '',
spiedNotificationMessage: '',
spiedNotificationTip: '',
viewInGalaxy: '',
spiedNotificationDetails: '偵察通知の詳細',
spyDetected: 'スパイを検出',
detectionResult: '検出結果',
detectionSuccess: '敵のスパイを検出しました!',
spiedNotificationMessage: '{npc}があなたの惑星{planet}を偵察しようとしました',
spiedNotificationTip: 'このNPCが敵対的な場合は、防衛を強化するか反撃を検討してください',
viewInGalaxy: '銀河で表示',
// Mission report dialog
missionReportDetails: '',
missionSuccess: '',
missionFailed: '',
origin: '',
destination: '',
missionDetails: '',
transportedResources: '',
recycledResources: '',
remainingDebris: '',
newPlanet: '',
missionReportDetails: 'ミッションレポートの詳細',
missionSuccess: '成功',
missionFailed: '失敗',
origin: '出発地',
destination: '目的地',
missionDetails: 'ミッション詳細',
transportedResources: '輸送資源',
recycledResources: 'リサイクル資源',
remainingDebris: '残りの破片',
newPlanet: '新惑星',
// NPC activity dialog
npcActivityDetails: '',
npcActivityDetails: 'NPCアクティビティの詳細',
activityType: {
recycle: ''
recycle: '破片をリサイクル中'
},
activityLocation: '',
position: '',
nearPlanet: '',
activityDescription: '',
npcActivityMessage: '',
arrivalTime: '',
npcActivityTip: '',
activityLocation: 'アクティビティの場所',
position: '位置',
nearPlanet: '近くの惑星',
activityDescription: 'アクティビティの説明',
npcActivityMessage: '{npc}が{position}で{activity}',
arrivalTime: '到着時刻',
npcActivityTip: 'NPCは戦闘の破片を収集することがあります。資源を競いたい場合は、先に現地に到着することを試みてください',
clearMessages: 'メッセージをクリア',
clearMessageTypes: 'クリアするメッセージタイプを選択',
clearBattleReports: '戦闘レポート',
@@ -846,7 +873,11 @@ export default {
clearNPCActivity: 'NPCアクティビティ',
clearGiftNotifications: 'ギフト通知',
clearGiftRejected: '拒否されたギフト',
clearNow: '今すぐクリア'
clearTradeOffers: '貿易提案',
clearIntelReports: '諜報報告',
clearJointAttackInvites: '共同攻撃の招待',
clearNow: '今すぐクリア',
clearSuccess: 'メッセージがクリアされました'
},
missionReports: {
transportSuccess: '輸送ミッションが正常に完了しました',
@@ -978,6 +1009,10 @@ export default {
github: 'GitHubリポジトリ',
qqGroup: 'QQグループ',
privacyPolicy: 'プライバシーポリシー',
displaySettings: '表示設定',
displaySettingsDesc: 'ゲームの視覚効果を調整',
backgroundAnimation: '背景アニメーション',
backgroundAnimationDesc: '有効にすると星空/パーティクル背景アニメーションを表示(パフォーマンスに影響する可能性があります)',
notifications: '通知設定',
notificationsDesc: 'ゲーム内の通知アラートを管理',
notificationTypes: '通知タイプ',
@@ -1001,7 +1036,48 @@ export default {
npcNameUpdateConfirm: '名前を更新',
npcNameUpdateCancel: '現状維持',
npcNameUpdateSuccess: '{count}件のNPC名を更新しました',
npcNameUpdateSkipped: 'NPC名の更新をスキップしました'
npcNameUpdateSkipped: 'NPC名の更新をスキップしました',
// WebDAV
webdav: {
title: 'クラウド同期',
desc: 'WebDAVでセーブデータを同期',
config: '設定',
configTitle: 'WebDAV設定',
configDesc: 'クラウド同期のためのWebDAVサーバーを設定',
notConfigured: '先にWebDAVサーバーを設定してください',
serverUrl: 'サーバーURL',
serverUrlPlaceholder: '例: https://dav.example.com',
serverUrlHint: 'WebDAVサーバーのアドレスを入力',
username: 'ユーザー名',
usernamePlaceholder: 'ユーザー名を入力',
password: 'パスワード',
passwordPlaceholder: 'パスワードを入力',
passwordHint: 'パスワードはローカルにのみ保存されます',
basePath: '保存パス',
basePathPlaceholder: '例: /ogame-saves/',
testConnection: '接続テスト',
testing: 'テスト中...',
testSuccess: '接続成功',
testFailed: '接続失敗',
save: '保存',
clearConfig: 'クリア',
configSaved: '設定を保存しました',
configCleared: '設定をクリアしました',
upload: 'アップロード',
uploading: 'アップロード中...',
uploadSuccess: 'アップロード成功',
uploadFailed: 'アップロード失敗',
download: 'ダウンロード',
downloadSuccess: 'ダウンロード成功',
downloadFailed: 'ダウンロード失敗',
selectFile: 'セーブファイルを選択',
selectFileDesc: '復元するセーブファイルを選択してください',
noFiles: 'セーブファイルが見つかりません',
loadFailed: 'ファイル一覧の読み込みに失敗しました',
confirmDelete: '"{name}" を削除してもよろしいですか?',
deleteSuccess: 'ファイルを削除しました',
deleteFailed: '削除に失敗しました'
}
},
notifications: {
constructionComplete: '建設完了',
@@ -1023,8 +1099,8 @@ export default {
officers: '士官',
modifyResources: '資源を変更',
resourcesDesc: '惑星の資源を素早く変更',
maxAllResources: '',
maxAllResourcesSuccess: '',
maxAllResources: 'すべて最大化',
maxAllResourcesSuccess: 'すべての資源が最大化されました',
modifyBuildings: '建物を変更',
buildingsDesc: '建物レベルを素早く設定',
modifyResearch: '研究を変更',
@@ -1065,10 +1141,11 @@ export default {
dangerZoneDesc: '以下の操作は元に戻せません',
resetGame: 'ゲームをリセット',
resetGameConfirm: 'ゲームをリセットしてもよろしいですか?すべてのデータが削除されます!',
completeAllQueues: '',
completeAllQueuesDesc: '',
completeQueues: '',
completeQueuesSuccess: ''
completeAllQueues: 'すべてのキューを完了',
completeAllQueuesDesc: 'すべての建設、研究、艦船、防衛キューおよび艦隊ミッションを即座に完了',
completeQueues: 'キューを完了',
completeQueuesSuccess:
'{buildingCount}件の建設キュー、{researchCount}件の研究キュー、{missionCount}件の艦隊ミッション、{missileCount}件のミサイル攻撃を完了しました'
},
alerts: {
incomingFleets: '{count}機の敵艦隊が接近中',
@@ -1125,10 +1202,10 @@ export default {
recentEvents: '最近のイベント',
recentEventsDescription: '最近の外交活動ログ',
ago: '前',
notifications: '',
markAllRead: '',
noReports: '',
viewAll: '',
notifications: '外交通知',
markAllRead: 'すべて既読にする',
noReports: '外交イベントなし',
viewAll: 'すべて表示',
status: {
friendly: '友好的',
neutral: '中立',
@@ -1203,6 +1280,19 @@ export default {
npcEliminatedMessage: 'あなたは{npcName}のすべての惑星を破壊しました!この勢力は完全に壊滅しました。'
},
searchPlaceholder: 'NPC名で検索...',
notificationType: {
tradeOffer: '貿易提案',
intelReport: '諜報報告',
jointAttack: '共同攻撃の招待'
},
notificationBadge: {
trade: '貿易',
intel: '諜報',
jointAttack: '招待'
},
notificationExtra: {
pending: '保留中'
},
viewMode: {
card: 'カード',
list: 'リスト'
@@ -1218,6 +1308,21 @@ export default {
medium: '普通',
hard: '難しい'
},
aiType: 'AIタイプ',
aiTypes: {
aggressive: '侵略型',
defensive: '防御型',
trader: '商人型',
expansionist: '拡張型',
balanced: 'バランス型'
},
aiTypeDescriptions: {
aggressive: '積極的に偵察と攻撃、強烈な反撃',
defensive: 'めったに攻撃しない、攻撃されると強く反撃',
trader: 'ほとんど攻撃せず、取引と贈り物を好む',
expansionist: '発展に注力、攻撃は少ない',
balanced: '状況に応じて戦略を動的に調整'
},
reputation: '評判',
spyProbes: '偵察機数',
fleetPower: '艦隊戦力',
@@ -1244,27 +1349,11 @@ export default {
attackCooldown: '攻撃クールダウン中({min}分{sec}秒)',
notSpiedYet: '未偵察、先に偵察が必要'
}
},
aiType: 'AIタイプ',
aiTypes: {
aggressive: '攻撃型',
defensive: '防御型',
trader: '商人型',
expansionist: '拡張型',
balanced: 'バランス型'
},
aiTypeDescriptions: {
aggressive: '積極的に偵察・攻撃、強い報復',
defensive: '滅多に攻撃しない、攻撃されると強く報復',
trader: 'ほとんど攻撃しない、取引と贈り物を好む',
expansionist: '発展に集中、攻撃性が低い',
balanced: '状況に応じて戦略を動的に調整'
}
},
pagination: {
previous: '前へ',
next: '次へ',
gotIt: '',
first: '最初',
last: '最後',
page: '{page}ページ'
@@ -1328,9 +1417,33 @@ export default {
seconds: '秒'
},
tutorial: {
progress: '進捗',
previous: '前へ',
next: '次へ',
gotIt: '了解',
completeButton: '完了',
skip: 'ガイドをスキップ',
welcome: {
title: 'OGame-Vue-Ts へようこそ',
content: 'ようこそ、司令官!基礎から始めて、宇宙帝国を築きましょう。'
title: 'OGameへようこそ',
content: 'ようこそ、司令官!このチュートリアルでは帝国建設の基礎を学びます。「次へ」をクリックして征服の旅を始めましょう。'
},
resources: {
title: '資源概要',
content:
'これらがあなたの資源です:金属、クリスタル、デューテリウム。建物の建設と科学技術の研究に不可欠です。エネルギーも重要で、インフラに電力を供給します。'
},
planet: {
title: 'あなたの惑星',
content: 'これがあなたの母星です。ここで惑星名、座標を確認でき、帝国を拡張する際に惑星を切り替えることができます。'
},
navigation: {
title: 'ナビゲーションメニュー',
content:
'このメニューを使用して、建物、研究、艦隊、銀河などの異なるセクション間を移動します。各セクションには独自のゲーム機能があります。'
},
gotoBuildings: {
title: '建物ページへ移動',
content: 'まず建物の建設から始めましょう。「建物」メニューをクリックして利用可能な建物を表示します。'
},
buildSolarPlant: {
title: '太陽光発電所を建設',
@@ -1342,16 +1455,108 @@ export default {
content:
'建物は建設キューに追加されました。右上のキューアイコンをクリックすると、進行中のすべての建設と研究タスクを確認できます。建設には時間がかかりますが、待機中も作業を続けられます。'
},
buildMetalMine: {
title: '金属鉱山を建設',
content: 'エネルギーがあれば、金属鉱山を建設できます。金属鉱山は主要な金属源であり、金属はほぼすべての建物と艦船に使用されます。'
},
buildCrystalMine: {
title: 'クリスタル鉱山を建設',
content: 'クリスタルはより希少ですが、高度な技術には不可欠です。クリスタル鉱山を建設してこの貴重な資源の収集を開始します。'
},
buildDeuterium: {
title: 'デューテリウム合成器を建設',
content: 'デューテリウムは艦船の燃料と高度な研究に必要です。デューテリウム合成器を建設してこの重要な資源の生産を開始します。'
},
upgradeMines: {
title: '資源鉱山をアップグレード',
content:
'次に、3つの資源鉱山金属、クリスタル、デューテリウムをレベル2にアップグレードして、ロボット工場の建設要件を満たす必要があります。資源が十分になったら、アップグレードを続けてください。'
},
buildRobotics: {
title: 'ロボット工場を建設',
content:
'ロボット工場は建設速度を大幅に向上させます。金属鉱山、クリスタル鉱山、デューテリウム合成器がそれぞれレベル2に達している必要があります。建設して建設効率を向上させましょう'
},
upgradeMinesForLab: {
title: '資源鉱山のアップグレードを続ける',
content:
'今度は3つの資源鉱山をレベル3にアップグレードして、研究ラボの建設要件を満たす必要があります。資源生産能力の発展を続けてください。'
},
buildResearchLab: {
title: '研究ラボを建設',
content:
'研究ラボは技術進歩の基礎です。3つの資源鉱山がそれぞれレベル3に達している必要があります。建設して科学技術研究をアンロックしましょう'
},
gotoResearch: {
title: '研究ページへ移動',
content: '研究ラボができたので、「研究」メニューをクリックして利用可能な技術を表示します。'
},
researchEnergy: {
title: 'エネルギー技術を研究',
content: 'エネルギー技術はエネルギー出力を向上させ、高度な建物をアンロックします。これは最も基本的で重要な技術の1つです。'
},
shipyardIntro: {
title: '艦隊と造船所',
content:
'艦船により銀河を探索し、資源を輸送し、帝国を守ることができます。艦船を建造するには、造船所が必要ですロボット工場レベル2が必要。'
},
gotoBuildingsForShipyard: {
title: '建物ページに戻る',
content: '建物ページに戻って造船所を建設します。'
},
buildShipyard: {
title: '造船所を建設',
content: '造船所では艦船と防御システムを建造できます。これは艦隊作戦に不可欠です。'
},
fleetIntro: {
title: '艦隊作戦',
content: '艦船を所有したら、ミッションに派遣できます:資源を輸送、惑星を植民、敵を攻撃、または廃墟場を探索します。'
},
galaxyIntro: {
title: '銀河を探索',
content: '銀河ビューには他の惑星、廃墟場、拡張の機会が表示されます。これを使用してターゲットを偵察し、戦略を計画します。'
},
complete: {
title: 'チュートリアル完了!',
content:
'おめでとうございます、司令官!基礎知識を習得しました。帝国の建設を続け、技術を研究し、銀河を探索してください。覚えておいてください:まずエネルギーを発展させ、次に資源を建設し、その後に工場と研究を!幸運を祈ります!'
},
// モバイルチュートリアル
mobile: {
welcome: {
title: 'OGame-Vue-Ts へようこそ(モバイル版)',
title: 'OGameへようこそモバイル版',
content:
'ようこそ、司令官!これはタッチスクリーン向けに最適化された簡易チュートリアルです。帝国建設を始めるために、コア機能を素早くご紹介します。'
},
resources: {
title: '上部リソースバー',
content: '上部にあなたの資源が表示されます:金属、クリスタル、デューテリウム。タップすると詳細な生産情報を表示できます。'
},
menu: {
title: 'ナビゲーションメニューを開く',
content: 'このメニューアイコンをタップしてナビゲーションバーを開き、建物、研究、艦隊などのすべての機能にアクセスできます。'
},
gotoBuildings: {
title: '建物ページへ移動',
content: 'メニューが開きました!「建物」オプションをタップして、インフラの建設を開始します。'
},
buildSolarPlant: {
title: '太陽光発電所を建設',
content: 'まず太陽光発電所を建設しましょう!下にスクロールして見つけ、カードをタップして建設します。エネルギーはすべての基礎です。'
},
waitBuild: {
title: '建設キュー',
content:
'右上のキューアイコンをクリックして建設進度を確認できます。他のページを閲覧し続けることができます。建設はバックグラウンドで進行します。'
'右上のキューアイコンをタップして建設進度を確認できます。他のページを閲覧し続けることができます。建設はバックグラウンドで進行します。'
},
buildMetalMine: {
title: '金属鉱山を建設',
content: 'エネルギーがあれば、金属鉱山を建設します。下にスクロールして金属鉱山を見つけ、建設をタップします。'
},
complete: {
title: 'クイックチュートリアル完了!',
content:
'素晴らしい!基本操作をマスターしました。クリスタル鉱山とデューテリウム合成器の建設を続け、他の機能を探索してください。覚えておいてください:まずエネルギー、次に資源!'
}
}
},
@@ -1408,11 +1613,21 @@ export default {
title: '戦闘シミュレーター',
message: '攻撃前に戦闘結果をシミュレート。双方の艦隊と技術レベルを入力して、勝敗と損失を予測。'
},
campaign: {
title: 'キャンペーンモード',
message:
'銀河のストーリーキャンペーンを探索!ミッションを完了してリソース報酬を獲得し、新しい挑戦をアンロック。各ノードにはユニークな目標と敵がいます。'
},
achievements: {
title: '実績システム',
message:
'ゲーム目標を達成して実績をアンロックし、ダークマター報酬を獲得!実績には複数のティアがあり、高い目標に挑戦してより良い報酬を手に入れましょう。'
},
ranking: {
title: 'ランキング',
message:
'他のプレイヤーやNPCと進捗を比較。建物、研究、艦隊、防御のポイントに基づくランキングを確認。ランキング上位を目指しましょう'
},
settings: {
title: '設定',
message: 'ここでゲームデータの管理、通知設定、セーブのエクスポート/インポートができます。定期的にバックアップを!'
@@ -1537,5 +1752,325 @@ export default {
you: 'あなた',
scoreBreakdown: 'スコア内訳',
noData: 'ランキングデータがありません'
},
// NPC強化行動通知
npcBehavior: {
tradeOfferReceived: '取引提案を受信',
tradeOfferDesc: '{npcName}から取引提案が届きました',
attitudeChanged: 'NPC態度変化',
becameFriendly: '{npcName}があなたに友好的になりました',
becameHostile: '{npcName}があなたに敵対的になりました',
intelReceived: '情報を受信',
intelReceivedDesc: '{npcName}が敵の情報を共有しました',
jointAttackInvite: '共同攻撃への招待',
jointAttackInviteDesc: '{npcName}が敵への共同攻撃に招待しています',
aidReceived: '援助を受信',
aidReceivedDesc: '{npcName}が{amount}の資源を送りました',
allyDefense: '同盟防衛',
allyDefenseDesc: '{npcName}があなたの惑星を防衛するために艦隊を派遣しています',
trade: {
title: '取引提案',
from: '送信者',
offers: '提供',
requests: '要求',
expiresIn: '有効期限',
expired: '期限切れ',
accept: '承諾',
decline: '拒否',
noOffers: '取引提案なし',
acceptSuccess: '取引完了!',
acceptFailed: '資源不足で取引できません',
declined: '取引を拒否しました',
ratio: '交換レート'
},
intel: {
title: '情報レポート',
from: '情報源',
target: '対象NPC',
type: '情報タイプ',
types: {
enemyFleet: '艦隊情報',
enemyResources: '資源情報',
enemyMovement: '動向情報'
},
fleetInfo: '艦隊情報',
resourceInfo: '資源情報',
movementInfo: '動向情報',
noReports: '情報レポートなし',
markAsRead: '既読にする',
content: '情報内容',
noFleet: '艦隊は検出されませんでした',
noData: 'データなし',
targetPosition: '目標座標',
missionType: 'ミッションタイプ'
},
jointAttack: {
title: '共同攻撃招待',
from: '発起者',
target: '対象NPC',
targetPlanet: '対象惑星',
npcFleet: 'NPC艦隊',
lootShare: '戦利品分配',
expiresIn: '有効期限',
expired: '期限切れ',
accept: '攻撃に参加',
decline: '拒否',
noInvites: '共同攻撃招待なし',
acceptSuccess: '共同攻撃に参加しました!',
declined: '招待を拒否しました',
targetInfo: '攻撃目標',
expectedShare: '予想分配',
remaining: '残り時間'
},
aid: {
title: '資源援助',
from: '送信者',
resources: '援助資源',
noAid: '援助記録なし'
},
attitudeChange: {
title: '態度変化',
npc: 'NPC',
previous: '以前',
current: '現在',
reason: '理由',
reasons: {
attitude_swing: '態度の変動',
gift: '贈り物を受け取った',
attack: '攻撃された',
naturalSwing: '自然変動',
giftReceived: '贈り物を受け取った',
attacked: '攻撃された',
reputationThreshold: '評判閾値'
}
},
allyAction: {
title: '同盟行動',
defense: '防衛支援',
defenseDesc: '{npcName}が{targetPlanet}の防衛に艦隊を派遣',
jointAttackStarted: '共同攻撃開始',
jointAttackStartedDesc: '{targetNpc}への共同攻撃が開始されました',
reputationBonus: '評判ボーナス',
reputationBonusDesc: '同盟の{npcName}が{targetNpc}にあなたのことを良く言っています'
}
},
campaign: {
name: 'キャンペーン',
description: '神秘的な銀河を探索し、古代の秘密を解き明かす',
totalProgress: '総進捗',
questsCompleted: 'クエスト完了',
chapter: '章',
branch: '分岐',
startQuest: 'クエスト開始',
claimRewards: '報酬を受け取る',
objectives: '目標',
objectivesLabel: '目標',
rewards: '報酬',
completed: '完了',
inProgress: '進行中',
available: '受注可能',
locked: 'ロック中',
notifications: {
questStarted: 'クエスト開始',
questCompleted: 'クエスト完了!',
rewardsClaimed: '報酬を獲得しました',
objectiveCompleted: '目標達成',
chapterUnlocked: '新章が解放されました',
reputationUp: '{npcName}との評判が{value}上昇しました',
reputationDown: '{npcName}との評判が{value}低下しました',
branchUnlocked: '新しいストーリー分岐が解放されました!'
},
dialogue: {
skip: 'スキップ',
continue: '続ける',
finish: '完了',
player: '司令官',
npc: 'NPC',
narrator: 'ナレーター',
mysterious: '謎の信号',
unknownSource: '不明な発信源',
choiceEffect: '選択肢の効果'
},
chapters: {
'1': {
title: '起源',
description: '故郷を築き、宇宙への第一歩を踏み出す',
backgroundStory:
'あなたは初めての惑星を手に入れた若き宇宙司令官です。この広大な宇宙で、故郷を築き、技術を発展させ、銀河の深淵を探索していきます...'
},
'2': {
title: '探索',
description: '宇宙を探索し、古代遺跡を発見する',
backgroundStory:
'勢力が拡大するにつれ、深宇宙からの謎の信号があなたの注意を引きます。これらの信号は古代の秘密を指し示し、勇敢な探検者を待っているようです...'
},
'3': {
title: '外交',
description: '他の勢力との関係を構築する',
backgroundStory:
'銀河にはあなただけではありません。他の文明が台頭しています。敵となるか同盟を結ぶか決めなければなりません。外交的知恵があなたの帝国の行く末を決めるでしょう...'
},
'4': {
title: '暗影の台頭',
description: '強大な敵に立ち向かい、領土を守る',
backgroundStory:
'影に危険が潜んでいます。強大な敵対勢力があなたの領土を狙っています。戦争は避けられません。来たる嵐に備えなければなりません...'
},
'5': {
title: '古代の秘密',
description: '銀河最深部の秘密を解き明かす',
backgroundStory:
'すべての手がかりは銀河で最も神秘的な領域を指しています。そこには古代文明が残した究極の秘密が眠っています。すべてを明らかにする準備はできていますか?'
}
},
quests: {
'1_1': { title: '故郷建設', description: 'インフラを整備し、惑星の基盤を築く' },
'1_2': { title: '技術啓蒙', description: '基礎技術を研究し、技術の旅を始める' },
'1_3': { title: '最初の船', description: '最初の戦艦を建造する' },
'1_4': { title: '奇妙な隣人', description: '近隣システムの他の勢力を偵察する' },
'1_5': { title: 'ファーストコンタクト', description: '近隣のNPC勢力と初期接触を確立する' },
'2_1': { title: '開拓植民地', description: '最初の新惑星を植民地化する' },
'2_2': { title: '深宇宙遠征', description: '艦隊を遠征ミッションに派遣する' },
'2_3': { title: '謎の信号', description: '深宇宙からの謎の信号を調査する' },
'2_4': { title: '遺跡調査', description: '発見した古代遺跡を探索する' },
'2_5': { title: 'アーカイブ解読', description: '遺跡から得たデータを研究する' },
'3_1': { title: '平和の使者', description: '外交でNPCとの関係を改善する' },
'3_2': { title: '通商関係', description: '友好勢力と安定した関係を築く' },
'3_3': { title: '共通の脅威', description: '潜在的な敵対勢力を発見する' },
'3_4': { title: '同盟交渉', description: '友好NPCと正式な同盟を結ぶ' },
'3_5': { title: '嵐への備え', description: '防衛施設を建設し、課題に備える' },
'4_1': { title: '前哨攻撃', description: '敵対勢力の最初の攻撃を撃退する' },
'4_2': { title: '情報収集', description: '敵の軍事配置を偵察する' },
'4_3': { title: '反撃', description: '敵への反撃を開始する' },
'4_4': { title: '資源争奪', description: '戦場のデブリをリサイクルして資源を得る' },
'4_5': { title: '決戦前夜', description: '最終決戦に向けて強力な艦隊を建造する' },
'5_1': { title: '遺跡の深部', description: '遺跡の最深部を探索する' },
'5_2': { title: '古代技術', description: '古代文明の技術を解放する' },
'5_3': { title: '最終対決', description: '謎の敵との最終決戦に臨む' },
'5_4': { title: '新時代', description: '新たな植民地を築き、新時代を開く' },
'5_5': { title: '遺産継承', description: '発展を続け、より多くのシステムを征服する' }
},
objectiveTypes: {
buildBuilding: '{building}をレベル{level}に建設',
researchTech: '{tech}をレベル{level}まで研究',
produceShips: '{ship}を{count}隻生産',
accumulateResources: '{resource}を{amount}蓄積',
defeatNPC: '{npc}を撃破',
winBattles: '{count}回戦闘に勝利',
recycleDebris: 'デブリを{amount}リサイクル',
reachRelation: '{npc}と{level}の関係に到達',
sendGift: '{npc}に{count}回贈り物を送る',
formAlliance: '{npc}と同盟を結ぶ',
colonize: '{count}個の惑星を植民地化',
expedition: '{count}回の遠征を完了',
spyTarget: '{target}をスパイ'
},
errors: {
questNotFound: 'クエストが見つかりません',
questNotAvailable: 'クエストは利用できません',
questNotActive: 'クエストはアクティブではありません',
questNotCompleted: 'クエスト未完了',
rewardsAlreadyClaimed: '報酬は既に受け取り済み',
prerequisiteNotMet: '前提クエスト未完了'
},
speakers: {
ancientVoice: '古代の声',
neighborNPC: '隣接勢力',
mysteriousSignal: '謎の信号',
enemyCommander: '敵司令官'
},
objectiveDescriptions: {
buildMetalMine: '金属鉱山をレベル2に建設',
buildCrystalMine: 'クリスタル鉱山をレベル2に建設',
buildSolarPlant: 'ソーラープラントをレベル2に建設',
buildResearchLab: '研究所をレベル1に建設',
researchEnergy: 'エネルギー技術をレベル1まで研究',
buildShipyard: '造船所をレベル2に建設',
researchCombustion: '燃焼ドライブをレベル1まで研究',
buildLightFighters: 'ライトファイター5機を建造',
researchEspionage: 'スパイ技術をレベル2まで研究',
buildSpyProbes: 'スパイプローブ3機を建造',
spyAnyNPC: 'NPC惑星をスパイ',
sendGiftToNPC: 'NPCに贈り物を送る',
researchAstrophysics: '宇宙物理学をレベル1まで研究',
buildColonyShip: 'コロニーシップを建造',
colonizeNewPlanet: '新惑星を植民地化',
completeExpedition: '遠征ミッションを完了',
discoverRuins: '古代遺跡を発見',
researchComputer: 'コンピュータ技術をレベル4まで研究',
improveRelation: 'NPCとの関係を改善',
reachFriendly: 'NPCと友好ステータスに到達',
spyHostileNPC: '敵対NPCをスパイ',
formAlliance: '友好NPCと同盟を結ぶ',
buildDefenses: '防衛施設を建設',
winDefenseBattle: '防衛戦に勝利',
spyEnemyPlanet: '敵惑星をスパイ',
attackEnemy: '敵を攻撃',
recycleDebris: 'デブリをリサイクル',
buildBattleships: 'バトルシップ10隻を建造',
exploreDeepRuins: '深部遺跡を探索',
researchHyperspace: 'ハイパースペース技術を研究',
defeatBoss: '謎の敵を撃破',
colonizeSpecial: '特別な場所を植民地化',
continueDevelopment: '発展を継続'
},
dialogues: {
'1_1': {
prologue_1: '銀河へようこそ、若き司令官。この広大な宇宙があなたの探索を待っています。まずは故郷の惑星を建設しましょう。',
prologue_2: '新しい意識が目覚めるのを感じる...興味深い...どこまで行けるか見てみよう...'
},
'1_2': { prologue_1: '基本インフラが完成しました。次は技術を発展させる時です。研究所を建設し、技術の旅を始めましょう。' },
'1_3': { prologue_1: '技術のサポートで艦隊の建造が可能になりました。造船所を建設し、最初の戦艦を生産しましょう。' },
'1_4': {
prologue_1: '艦隊が形になってきました。周囲の状況を把握しましょう。スパイプローブを送り出し、近隣の勢力を偵察してください。',
prologue_2: 'あなたは一人ではない...この銀河には他の文明が存在する...'
},
'1_5': {
prologue_1: '近隣の勢力を発見しました。外交は芸術です。彼らとの接触を試みてください。',
epilogue_1: '贈り物をありがとう、司令官。友人になれることを願っています。',
epilogue_2: 'よい...繋がりを築くことは、より深い秘密を解き明かす第一歩だ...'
},
'2_1': {
prologue_1: '勢力が確立しました。領土を拡大する時です。宇宙物理学を研究し、コロニーシップを建造して新しい惑星を探索しましょう。',
prologue_2: '宇宙は無限...より多くの惑星はより多くの可能性を意味する...'
},
'2_2': {
prologue_1: '植民地化成功!しかし宇宙にはより深い秘密が待っています。艦隊を遠征ミッションに派遣しましょう。',
prologue_2: '遠くから微かな信号が...何かがそこであなたを待っている...'
},
'2_3': {
prologue_1: '遠征で異常な信号を発見しました。これらの信号は古代文明からのようです...発信源を調査してください。',
epilogue_1: 'これらの記号は...古代文明の遺跡だ!さらに調査して秘密を解き明かそう。'
},
'2_4': { prologue_1: '古代遺跡の場所を発見しました。艦隊を送り、何が見つかるか探索してください。' },
'2_5': { prologue_1: '遺跡でデータアーカイブが見つかりました。このデータを研究すれば、新しい技術が解放できるかもしれません。' },
'3_1': { prologue_1: '探索中も外交を忘れないでください。周囲の勢力と良好な関係を維持することは有益です。' },
'3_2': { prologue_1: 'いくつかの勢力が友好を示しています。関係を深め続ければ、より多くのサポートを得られるかもしれません。' },
'3_3': { prologue_1: '情報によると敵対勢力が影からあなたを監視しています。警戒を怠らず、彼らの動きを偵察してください。' },
'3_4': { prologue_1: '友好勢力と正式な同盟を結び、脅威に対してお互いをサポートしましょう。' },
'3_5': { prologue_1: '脅威が迫っています。防衛施設を建設し、可能な紛争に備えてください。' },
'4_1': {
prologue_1: '敵が攻撃を開始しました!惑星を守ってください!',
epilogue_1: '敵の第一波を撃退しました。しかしこれは始まりに過ぎません...'
},
'4_2': { prologue_1: '敵は撤退しましたが、戻ってくるでしょう。彼らの惑星を偵察して戦力を把握してください。' },
'4_3': { prologue_1: '反撃の時です。敵の惑星を攻撃し、彼らの戦力を弱めてください。' },
'4_4': { prologue_1: '戦場に多くのデブリが残っています。これらの資源をリサイクルして次の戦闘に備えてください。' },
'4_5': { prologue_1: '最終決戦が近づいています。強力な艦隊を建造し、究極の挑戦に備えてください。' },
'5_1': {
prologue_1: 'すべての手がかりは遺跡の最深部を指しています。古代文明の核心的な秘密がそこにあります。',
prologue_2: 'ついに到着した...真実がまもなく明かされる...'
},
'5_2': { prologue_1: '遺跡の深部で失われた古代技術を発見しました。研究してその力を解放してください。' },
'5_3': {
prologue_1: '謎の敵が現れました。これが最後の挑戦です。撃破してください!',
epilogue_1: 'やりました!古代の守護者は倒されました。銀河の秘密は今やあなたに開かれています。'
},
'5_4': { prologue_1: 'ついに平和が訪れました。この新時代に新しい植民地を築き、帝国を拡大してください。' },
'5_5': {
prologue_1: 'あなたの伝説は始まったばかりです。さらに探索を続け、より多くの星系を征服してください!',
epilogue_1: '銀河は広大で果てしなく、数え切れない秘密があなたを待っています...'
}
}
}
}

View File

@@ -11,29 +11,11 @@ export default {
common: {
confirm: '확인',
cancel: '취소',
delete: '삭제',
edit: '편집',
save: '저장',
close: '닫기',
back: '돌아가기',
next: '다음',
gotIt: '',
previous: '이전',
submit: '제출',
reset: '초기화',
search: '검색',
filter: '필터',
loading: '로딩 중...',
noData: '데이터 없음',
error: '오류',
gotIt: '알겠습니다',
success: '성공',
warning: '경고',
info: '정보',
resourceType: '자원 유형',
playerName: '사령관',
timeHour: '시간',
timeMinute: '분',
timeSecond: '초',
featureLocked: '기능 잠김',
unlockRequired: '건물 필요',
requiredBuilding: '필요한 건물',
@@ -41,7 +23,6 @@ export default {
goToBuildings: '건물로 이동',
locked: '잠김',
viewRequirements: '요구사항 보기',
requirements: '요구사항',
requirementsNotMet: '요구사항 미충족',
current: '현재',
level: '레벨',
@@ -51,7 +32,8 @@ export default {
viewDetails: '상세 보기',
exitConfirmTitle: '게임 종료',
exitConfirmMessage: '게임을 종료하시겠습니까? 진행 상황은 자동으로 저장됩니다.',
points: '포인트'
points: '포인트',
retry: '재시도'
},
errors: {
requirementsNotMet: '전제 조건 미충족',
@@ -88,13 +70,13 @@ export default {
galaxy: '은하계',
diplomacy: '외교',
achievements: '업적',
campaign: '캠페인',
ranking: '랭킹',
messages: '메시지',
settings: '설정',
gm: 'GM'
},
sidebar: {
language: '언어',
lightMode: '라이트 모드',
darkMode: '다크 모드',
collapse: '메뉴 접기',
@@ -119,9 +101,6 @@ export default {
},
energy: {
lowWarning: '에너지 부족! 자원 생산 중단!',
severeWarning: '에너지 부족! 자원 생산 중단!',
criticalWarning: '에너지 부족! 자원 생산 중단!',
noProduction: '에너지 부족! 자원 생산 중단!',
deficitDetail: '에너지 부족: {deficit}, 발전소를 더 건설하세요',
buildSolarPlant: '발전소 건설'
},
@@ -132,27 +111,20 @@ export default {
lowResources: '곧 고갈: {resources}'
},
planet: {
planet: '행성',
moon: '위성',
colony: '식민지',
position: '위치',
coordinates: '좌표',
switchToMoon: '위성 보기',
backToPlanet: '모행성으로 돌아가기',
switchPlanet: '행성 전환',
currentPlanet: '현재 행성',
fields: '필드',
temperature: '온도',
homePlanet: '모행성',
planetPrefix: '행성',
moonSuffix: '의 위성',
colonyPrefix: '식민지',
renamePlanet: '행성 이름 변경',
renamePlanetTitle: '행성 이름 변경',
newPlanetName: '새 이름',
planetNamePlaceholder: '새 행성 이름 입력',
rename: '이름 변경',
renameSuccess: '행성 이름이 {name}(으)로 변경되었습니다'
rename: '이름 변경'
},
player: {
points: '총 점수'
@@ -179,13 +151,12 @@ export default {
sensorPhalanx: '센서 팔랑크스',
jumpGate: '점프 게이트',
planetDestroyerFactory: '행성 파괴 공장',
geoResearchStation: '지질 연구소',
deepDrillingFacility: '심층 시추 시설',
buildTime: '건설 시간',
build: '',
build: '건설',
production: '생산량',
consumption: '소비',
totalCost: '총 비용',
totalPoints: '총 점수',
levelRange: '레벨 범위',
capacity: 'Capacity/Effect',
storageCapacity: 'Capacity',
energyProduction: 'Energy Production',
@@ -194,7 +165,6 @@ export default {
buildQueueBonus: '건설 대기열',
spaceBonus: '공간 보너스',
buildSpeedBonus: '건설 속도 보너스',
researchSpeedBonus: '연구 속도 보너스',
planetSpace: 'Planet Space',
moonSpace: 'Moon Space',
missileCapacity: 'Missile Capacity',
@@ -228,7 +198,9 @@ export default {
lunarBase: '달 가용 공간 증가, 레벨당 +30 공간',
sensorPhalanx: '주변 행성계의 함대 활동 감지',
jumpGate: '다른 위성으로 함대 순간 이동',
planetDestroyerFactory: '행성을 파괴할 수 있는 궁극 병기 건조'
planetDestroyerFactory: '행성을 파괴할 수 있는 궁극 병기 건조',
geoResearchStation: '지질 구조를 연구하여 광맥 자연 회복 속도를 높입니다. 레벨당 회복 속도 50% 증가',
deepDrillingFacility: ''
},
ships: {
lightFighter: '경전투기',
@@ -295,14 +267,16 @@ export default {
totalCost: '총 비용',
totalPoints: '총 점수',
levelRange: '레벨 범위',
capacity: 'Capacity/Effect',
storageCapacity: 'Capacity',
energyProduction: 'Energy Production',
fleetStorage: 'Fleet Storage',
buildQueue: 'Build Queue',
planetSpace: 'Planet Space',
moonSpace: 'Moon Space',
missileCapacity: 'Missile Capacity'
attackBonus: '공격 보너스',
shieldBonus: '실드 보너스',
armorBonus: '장갑 보너스',
spyLevel: '정찰 레벨',
researchQueueBonus: '연구 대기열',
colonySlots: '식민지 슬롯',
forAllPlanets: '(전역)',
speedBonus: '속도 보너스',
researchSpeedBonus: '연구 속도 보너스'
},
technologies: {
energyTechnology: '에너지 기술',
@@ -322,7 +296,8 @@ export default {
hyperspaceDrive: '초공간 엔진',
darkMatterTechnology: '암흑 물질 기술',
terraformingTechnology: '지형 변환 기술',
planetDestructionTech: '행성 파괴 기술'
planetDestructionTech: '행성 파괴 기술',
miningTechnology: ''
},
technologyDescriptions: {
energyTechnology: '에너지 이용 효율 향상',
@@ -343,7 +318,8 @@ export default {
hyperspaceDrive: '고급 추진 기술',
darkMatterTechnology: '암흑 물질의 성질과 응용 연구',
terraformingTechnology: '행성 지형 개조 기술 연구, 레벨당 모든 행성의 가용 공간 30 증가',
planetDestructionTech: '행성 전체를 파괴하는 공포의 기술 연구'
planetDestructionTech: '행성 전체를 파괴하는 공포의 기술 연구',
miningTechnology: ''
},
officers: {
commander: '사령관',
@@ -367,8 +343,9 @@ export default {
queue: {
title: '진행 중인 작업',
empty: '활성 대기열 없음',
buildQueue: '건설 대기열',
researchQueue: '연구 대기열',
buildQueueBonus: '건설 대기열',
spaceBonus: '공간 보너스',
researchQueueBonus: '연구 대기열',
building: '건설 중',
researching: '연구 중',
demolishing: '철거 중',
@@ -378,7 +355,7 @@ export default {
cancelResearch: '연구 취소',
confirmCancel: '취소하시겠습니까? 자원의 50%가 환불됩니다.',
level: '레벨',
gmModeActivated: '',
quantity: '수량',
upgradeToLevel: '레벨로 업그레이드',
tabs: {
all: '전체',
@@ -397,14 +374,9 @@ export default {
movedToQueue: '작업이 대기열로 이동되었습니다'
},
overview: {
title: '행성 개요',
resourceOverview: '자원 개요',
fleetInfo: '함대',
currentShips: '현재 행성의 함선 수',
productionSources: '생산 소스',
productionSourcesDesc: '상세 자원 생산 및 보너스 정보',
consumptionSources: '소비 소스',
consumptionSourcesDesc: '건물 에너지 소비 세부 정보',
totalProduction: '총 생산량',
totalConsumption: '총 소비량',
noConsumption: '에너지 소비 없음',
@@ -414,13 +386,8 @@ export default {
},
buildingsView: {
title: '건물',
usedSpace: '사용된 공간',
spaceUsage: '공간 사용',
level: '레벨',
gmModeActivated: '',
upgradeCost: '업그레이드 비용',
buildTime: '건설 시간',
build: '건설',
upgrade: '업그레이드',
maxLevelReached: '최대 레벨 도달',
requirementsNotMet: '요구 사항 미충족',
@@ -430,8 +397,7 @@ export default {
demolishRefund: '철거 환불',
demolishFailed: '철거 실패',
demolishFailedMessage: '이 건물을 철거할 수 없습니다. 건설 대기열이 가득 찼거나 건물 레벨이 0인지 확인하세요.',
confirmDemolish: '철거 확인',
confirmDemolishMessage: '다음 건물을 철거하시겠습니까?'
confirmDemolish: '철거 확인'
},
researchView: {
title: '연구',
@@ -442,26 +408,13 @@ export default {
researchFailedMessage: '자원이 충분한지, 전제 조건이 충족되었는지, 또는 다른 연구 작업이 있는지 확인하세요.'
},
shipyard: {
attack: '공격력',
missileAttack: '미사일 공격',
shield: '쉴드',
armor: '장갑',
speed: '속도',
cargoCapacity: '화물 용량',
fuelConsumption: '연료 소비',
buildCost: '건설 비용',
buildTime: '건설 시간',
build: '',
perUnit: '단위당',
batchCalculator: '일괄 계산기',
quantity: '수량',
totalCost: '총 비용',
totalTime: '총 시간'
fuelConsumption: '연료 소비'
},
shipyardView: {
title: '조선소',
fleetStorage: '함대 저장소',
owned: '보유',
attack: '공격력',
missileAttack: '미사일 공격',
shield: '실드',
@@ -483,7 +436,7 @@ export default {
armor: '장갑',
buildCost: '건설 비용',
buildTime: '건설 시간',
build: '',
build: '건설',
perUnit: '단위당',
batchCalculator: '일괄 계산기',
quantity: '수량',
@@ -536,6 +489,7 @@ export default {
missionInfo: '임무 정보',
fuelConsumption: '연료 소비',
flightTime: '비행 시간',
outOfRange: '사정거리 밖',
attackMission: '공격',
transport: '수송',
colonize: '식민',
@@ -566,6 +520,7 @@ export default {
}
},
recycle: '회수',
destroy: '행성 파괴',
transportResources: '자원 수송',
totalCargoCapacity: '총 적재량',
used: '사용됨',
@@ -577,11 +532,12 @@ export default {
arrivalTime: '도착 시간',
returnTime: '귀환 시간',
recallFleet: '함대 소환',
abortMission: '',
abortMissionTitle: '',
abortMissionWarning: '',
abortMissionSuccess: '',
abortMissionSuccessMessage: '',
abortMission: '임무 중단',
abortMissionTitle: '임무 중단 확인',
abortMissionWarning:
'경고: 이 임무를 중단하면 {ships}척의 함선과 {resources}의 자원이 영구적으로 손실됩니다!\n\n이 작업은 되돌릴 수 없으며 함대와 자원은 돌아오지 않습니다.',
abortMissionSuccess: '임무 중단됨',
abortMissionSuccessMessage: '임무가 중단되었으며, 함대와 자원이 손실되었습니다.',
sendFailed: '파견 실패',
sendFailedMessage: '함대 수, 연료 충분 여부 또는 적재량 한계를 확인하세요.',
recallFailed: '소환 실패',
@@ -650,7 +606,6 @@ export default {
benefitsBonus: '효과 보너스',
resourceProduction: '자원 생산량',
darkMatterProduction: '암흑 물질 생산량',
energyProduction: '에너지 생산량',
buildingSpeed: '건설 속도',
researchSpeed: '연구 속도',
fleetSpeed: '함대 속도',
@@ -679,14 +634,11 @@ export default {
selectGalaxy: '은하계 선택',
system: '행성계',
selectSystem: '행성계 선택',
view: '보기',
myPlanet: '내 행성',
myPlanets: '내 행성계 보기',
npcPlanets: 'NPC 행성들',
selectPlanetToView: '행성을 선택하여 행성계 보기',
totalPositions: '총 10개 행성 위치',
mine: '내 것',
hostile: '적대',
emptySlot: '빈 자리 - 식민 가능',
scout: '정찰',
attack: '공격',
@@ -709,10 +661,12 @@ export default {
missileCount: '미사일 수량',
availableMissiles: '사용 가능한 미사일',
missileRange: '미사일 사정거리',
systems: '시스템',
systems: '성계',
distance: '거리',
flightTime: '비행 시간',
outOfRange: '사정거리 밖',
launchMissile: '발사',
missileLaunched: '미사일이 발사되었습니다',
cancel: '취소',
colonizePlanetMessage: '위치 [{coordinates}]을(를) 식민하시겠습니까?\n\n함대 페이지로 이동하여 식민선을 파견하세요.',
recyclePlanetMessage: '위치 [{coordinates}]의 잔해를 회수하시겠습니까?\n\n함대 페이지로 이동하여 회수선을 파견하세요.',
@@ -722,6 +676,20 @@ export default {
giftPlanetMessage:
'행성 [{coordinates}]에 자원을 선물로 보내시겠습니까?\n\n함대 페이지로 이동하여 수송선을 선택하고 자원을 적재하세요.',
npcPlanetName: '{name}의 행성',
phalanxScan: '센서 스캔',
phalanxScanTitle: '센서 팔랑크스 스캔',
phalanxScanDescription: '행성 [{coordinates}]의 함대 활동 스캔',
phalanxNoMoon: '스캔하려면 센서 팔랑크스가 있는 위성이 필요합니다',
phalanxCost: '스캔 비용',
phalanxNoFleets: '함대 활동 감지되지 않음',
phalanxFleetDetected: '{count}개의 함대 감지됨',
phalanxOrigin: '출발지',
phalanxDestination: '목적지',
phalanxArrival: '도착 시간',
phalanxReturn: '귀환 시간',
phalanxStatusOutbound: '이동 중',
phalanxStatusReturning: '귀환 중',
phalanxInsufficientDeuterium: '중수소 부족',
intercepted: '요격됨',
defenseLosses: '방어 손실'
},
@@ -730,12 +698,12 @@ export default {
battles: '전투',
spy: '정찰',
npc: 'NPC',
diplomacy: '',
diplomacy: '외교',
battleReports: '전투 보고서',
spyReports: '정찰 보고서',
noBattleReports: '전투 보고서 없음',
noSpyReports: '정찰 보고서 없음',
noDiplomaticReports: '',
noDiplomaticReports: '외교 보고서 없음',
battleReport: '전투 보고서',
spyReport: '정찰 보고서',
victory: '승리',
@@ -756,6 +724,8 @@ export default {
defense: '방어',
buildings: '건물',
unread: '읽지 않음',
pending: '대기 중',
invalidData: '잘못된 데이터',
targetPlanet: '목표 행성',
attackerRemaining: '공격자 잔여',
defenderRemaining: '방어자 잔여',
@@ -808,36 +778,36 @@ export default {
polite_decline: '정중하게 거절했습니다'
},
// Spied notification dialog
spiedNotificationDetails: '',
spyDetected: '',
detectionResult: '',
detectionSuccess: '',
spiedNotificationMessage: '',
spiedNotificationTip: '',
viewInGalaxy: '',
spiedNotificationDetails: '정찰 알림 상세',
spyDetected: '스파이 탐지됨',
detectionResult: '탐지 결과',
detectionSuccess: '적의 스파이를 탐지했습니다!',
spiedNotificationMessage: '{npc}가 당신의 행성 {planet}을 정찰하려 했습니다',
spiedNotificationTip: '이 NPC가 적대적이라면 방어를 강화하거나 반격을 고려하세요',
viewInGalaxy: '은하에서 보기',
// Mission report dialog
missionReportDetails: '',
missionSuccess: '',
missionFailed: '',
origin: '',
destination: '',
missionDetails: '',
transportedResources: '',
recycledResources: '',
remainingDebris: '',
newPlanet: '',
missionReportDetails: '임무 보고서 상세',
missionSuccess: '성공',
missionFailed: '실패',
origin: '출발지',
destination: '목적지',
missionDetails: '임무 상세',
transportedResources: '운송된 자원',
recycledResources: '재활용된 자원',
remainingDebris: '남은 잔해',
newPlanet: '새 행성',
// NPC activity dialog
npcActivityDetails: '',
npcActivityDetails: 'NPC 활동 상세',
activityType: {
recycle: ''
recycle: '잔해 재활용 중'
},
activityLocation: '',
position: '',
nearPlanet: '',
activityDescription: '',
npcActivityMessage: '',
arrivalTime: '',
npcActivityTip: '',
activityLocation: '활동 위치',
position: '위치',
nearPlanet: '근처 행성',
activityDescription: '활동 설명',
npcActivityMessage: '{npc}가 {position}에서 {activity}',
arrivalTime: '도착 시간',
npcActivityTip: 'NPC가 전투 잔해를 수집할 수 있습니다. 자원을 경쟁하려면 먼저 현장에 도착하세요',
clearMessages: '메시지 삭제',
clearMessageTypes: '삭제할 메시지 유형 선택',
clearBattleReports: '전투 보고서',
@@ -847,7 +817,11 @@ export default {
clearNPCActivity: 'NPC 활동',
clearGiftNotifications: '선물 알림',
clearGiftRejected: '거절된 선물',
clearNow: '지금 삭제'
clearTradeOffers: '무역 제안',
clearIntelReports: '정보 보고서',
clearJointAttackInvites: '공동 공격 초대',
clearNow: '지금 삭제',
clearSuccess: '메시지가 삭제되었습니다'
},
missionReports: {
transportSuccess: '수송 임무가 성공적으로 완료되었습니다',
@@ -955,7 +929,6 @@ export default {
resume: '재개',
gamePaused: '게임이 일시정지되었습니다',
gameResumed: '게임이 재개되었습니다',
playerName: '플레이어 이름',
gameSpeed: '자원 생산 속도',
gameSpeedDesc: '현재 자원 생산 속도 배율',
speedChanged: '자원 생산 속도가 {speed}x로 변경되었습니다',
@@ -963,22 +936,23 @@ export default {
reset: '재설정',
about: '정보',
version: '버전',
latestVersion: '최신 버전',
checkUpdate: '업데이트 확인',
checking: '확인 중...',
newVersionAvailable: '새 버전 {version} 사용 가능',
upToDate: '이미 최신 버전입니다',
checkUpdateCooldown: '나중에 다시 시도해주세요 (5분 쿨다운)',
checkUpdateFailed: '업데이트 확인 실패, 네트워크 연결을 확인하세요',
viewUpdate: '업데이트 보기',
updateAvailable: '새 버전이 사용 가능합니다. 릴리스 노트를 보려면 클릭하세요.',
download: '다운로드',
goToDownload: '다운로드로 이동',
buildDate: '빌드 날짜',
community: '커뮤니티',
github: 'GitHub 저장소',
qqGroup: 'QQ 그룹',
privacyPolicy: '개인정보처리방침',
displaySettings: '디스플레이 설정',
displaySettingsDesc: '게임 시각 효과 조정',
backgroundAnimation: '배경 애니메이션',
backgroundAnimationDesc: '별하늘/입자 배경 애니메이션 표시 (성능에 영향을 줄 수 있음)',
notifications: '알림 설정',
notificationsDesc: '게임 내 알림 관리',
notificationTypes: '알림 유형',
@@ -996,13 +970,53 @@ export default {
expandTypes: '세부 정보 펼치기',
collapseTypes: '세부 정보 접기',
// NPC 이름 업데이트
npcNameUpdate: 'NPC 이름 업데이트',
npcNameUpdateTitle: '이전 형식 NPC 이름 감지됨',
npcNameUpdateMessage: '{count}개의 NPC가 이전 형식의 이름을 사용 중입니다. 새로운 현지화된 이름으로 업데이트하시겠습니까?',
npcNameUpdateTitle: '이전 NPC 이름 감지됨',
npcNameUpdateMessage: '이전 이름 형식을 사용하는 {count}개의 NPC가 발견되었습니다. 새로운 현지화 이름으로 업데이트하시겠습니까?',
npcNameUpdateConfirm: '이름 업데이트',
npcNameUpdateCancel: '현재 유지',
npcNameUpdateSuccess: '{count}개의 NPC 이름이 업데이트되었습니다',
npcNameUpdateSkipped: 'NPC 이름 업데이트 건너뜀'
npcNameUpdateSuccess: '{count}개의 NPC 이름이 성공적으로 업데이트되었습니다',
npcNameUpdateSkipped: 'NPC 이름 업데이트 건너뜀',
// WebDAV
webdav: {
title: '클라우드 동기화',
desc: 'WebDAV를 통해 세이브 파일 동기화',
config: '설정',
configTitle: 'WebDAV 설정',
configDesc: '클라우드 동기화를 위한 WebDAV 서버 설정',
notConfigured: '먼저 WebDAV 서버를 설정하세요',
serverUrl: '서버 URL',
serverUrlPlaceholder: '예: https://dav.example.com',
serverUrlHint: 'WebDAV 서버 주소를 입력하세요',
username: '사용자 이름',
usernamePlaceholder: '사용자 이름 입력',
password: '비밀번호',
passwordPlaceholder: '비밀번호 입력',
passwordHint: '비밀번호는 로컬에만 저장됩니다',
basePath: '저장 경로',
basePathPlaceholder: '예: /ogame-saves/',
testConnection: '연결 테스트',
testing: '테스트 중...',
testSuccess: '연결 성공',
testFailed: '연결 실패',
save: '저장',
clearConfig: '지우기',
configSaved: '설정이 저장되었습니다',
configCleared: '설정이 삭제되었습니다',
upload: '업로드',
uploading: '업로드 중...',
uploadSuccess: '업로드 성공',
uploadFailed: '업로드 실패',
download: '다운로드',
downloadSuccess: '다운로드 성공',
downloadFailed: '다운로드 실패',
selectFile: '세이브 파일 선택',
selectFileDesc: '복원할 세이브 파일을 선택하세요',
noFiles: '세이브 파일을 찾을 수 없습니다',
loadFailed: '파일 목록 로드 실패',
confirmDelete: '"{name}"을(를) 삭제하시겠습니까?',
deleteSuccess: '파일이 삭제되었습니다',
deleteFailed: '삭제 실패'
}
},
notifications: {
constructionComplete: '건설 완료',
@@ -1024,8 +1038,8 @@ export default {
officers: '장교',
modifyResources: '자원 수정',
resourcesDesc: '행성 자원을 빠르게 수정',
maxAllResources: '',
maxAllResourcesSuccess: '',
maxAllResources: '모두 최대화',
maxAllResourcesSuccess: '모든 자원이 최대화되었습니다',
modifyBuildings: '건물 수정',
buildingsDesc: '건물 레벨을 빠르게 설정',
modifyResearch: '연구 수정',
@@ -1066,10 +1080,11 @@ export default {
dangerZoneDesc: '다음 작업은 되돌릴 수 없습니다',
resetGame: '게임 초기화',
resetGameConfirm: '게임을 초기화하시겠습니까? 모든 데이터가 삭제됩니다!',
completeAllQueues: '',
completeAllQueuesDesc: '',
completeQueues: '',
completeQueuesSuccess: ''
completeAllQueues: '모든 대기열 완료',
completeAllQueuesDesc: '모든 건설, 연구, 함선, 방어 대기열 및 함대 임무를 즉시 완료',
completeQueues: '대기열 완료',
completeQueuesSuccess:
'{buildingCount}개의 건설 대기열, {researchCount}개의 연구 대기열, {missionCount}개의 함대 임무, {missileCount}개의 미사일 공격을 완료했습니다'
},
alerts: {
incomingFleets: '{count}개의 적 함대 접근 중',
@@ -1123,13 +1138,11 @@ export default {
noFriendlyNpcs: '우호적인 NPC 없음',
noNeutralNpcs: '중립적인 NPC 없음',
noHostileNpcs: '적대적인 NPC 없음',
recentEvents: '최근 이벤트',
recentEventsDescription: '최근 외교 활동 로그',
ago: '전',
notifications: '',
markAllRead: '',
noReports: '',
viewAll: '',
notifications: '외교 알림',
markAllRead: '모두 읽음으로 표시',
noReports: '외교 이벤트 없음',
viewAll: '모두 보기',
status: {
friendly: '우호적',
neutral: '중립',
@@ -1148,7 +1161,6 @@ export default {
},
note: '메모',
notePlaceholder: '메모 입력...',
noteEmpty: '메모 없음',
lastEvent: '최근 이벤트',
reportDetails: '외교 보고서 상세',
eventDescription: '이벤트 설명',
@@ -1204,6 +1216,19 @@ export default {
npcEliminatedMessage: '당신은 {npcName}의 모든 행성을 파괴했습니다! 이 세력은 완전히 소멸되었습니다.'
},
searchPlaceholder: 'NPC 이름 검색...',
notificationType: {
tradeOffer: '무역 제안',
intelReport: '정보 보고서',
jointAttack: '공동 공격 초대'
},
notificationBadge: {
trade: '무역',
intel: '정보',
jointAttack: '초대'
},
notificationExtra: {
pending: '대기 중'
},
viewMode: {
card: '카드',
list: '목록'
@@ -1219,6 +1244,21 @@ export default {
medium: '보통',
hard: '어려움'
},
aiType: 'AI 유형',
aiTypes: {
aggressive: '공격형',
defensive: '방어형',
trader: '상인형',
expansionist: '확장형',
balanced: '균형형'
},
aiTypeDescriptions: {
aggressive: '적극적으로 정찰하고 공격하며, 반격이 격렬함',
defensive: '거의 공격하지 않지만, 공격받으면 강력하게 반격함',
trader: '거의 공격하지 않으며, 무역과 선물을 선호함',
expansionist: '발전에 집중하며, 공격은 적음',
balanced: '상황에 따라 전략을 동적으로 조정함'
},
reputation: '평판',
spyProbes: '정찰기 수',
fleetPower: '함대 전력',
@@ -1245,27 +1285,11 @@ export default {
attackCooldown: '공격 쿨다운 중 ({min}분 {sec}초)',
notSpiedYet: '아직 정찰하지 않음, 먼저 정찰 필요'
}
},
aiType: 'AI 유형',
aiTypes: {
aggressive: '공격형',
defensive: '방어형',
trader: '상인형',
expansionist: '확장형',
balanced: '균형형'
},
aiTypeDescriptions: {
aggressive: '적극적으로 정찰 및 공격, 강한 보복',
defensive: '거의 공격하지 않음, 공격받으면 강하게 보복',
trader: '거의 공격하지 않음, 거래와 선물 선호',
expansionist: '발전에 집중, 공격성 낮음',
balanced: '상황에 따라 전략을 동적으로 조정'
}
},
pagination: {
previous: '이전',
next: '다음',
gotIt: '',
first: '처음',
last: '마지막',
page: '{page}페이지'
@@ -1329,30 +1353,144 @@ export default {
seconds: '초'
},
tutorial: {
progress: '진행률',
previous: '이전',
next: '다음',
gotIt: '알겠습니다',
completeButton: '완료',
skip: '튜토리얼 건너뛰기',
welcome: {
title: 'OGame에 오신 것을 환영합니다',
content: '환영합니다, 사령관! 기초부터 시작하여 우주 제국 건설해 봅시다.'
content: '환영합니다, 사령관! 이 튜토리얼은 제국 건설의 기초를 안내합니다. "다음"을 클릭하여 여정을 시작하세요.'
},
resources: {
title: '자원 개요',
content:
'이것은 당신의 자원입니다: 금속, 크리스탈, 중수소. 건물 건설과 기술 연구에 필수적입니다. 에너지도 중요하며, 인프라에 전력을 공급합니다.'
},
planet: {
title: '당신의 행성',
content: '이것은 당신의 모행성입니다. 여기서 행성 이름, 좌표를 확인하고, 제국이 확장되면 행성을 전환할 수 있습니다.'
},
navigation: {
title: '탐색 메뉴',
content: '이 메뉴를 사용하여 다양한 섹션을 탐색하세요: 건물, 연구, 함대, 은하계 등. 각 섹션은 고유한 게임 기능을 제공합니다.'
},
gotoBuildings: {
title: '건물 페이지로 이동',
content: '몇 가지 건물을 건설해 봅시다. "건물" 메뉴 항목을 클릭하여 사용 가능한 건물을 확인하세요.'
},
buildSolarPlant: {
title: '태양광 발전소 건설',
content:
'먼저 태양광 발전소를 건설하세요! 행성에 에너지를 공합니다. 에너지가 없으면 다른 자원 건물이 작동할 수 없습니다. 가장 중요한 첫 단계입니다.'
'먼저 태양광 발전소를 건설하세요! 행성에 에너지를 공합니다. 에너지가 없으면 다른 자원 건물이 작동할 수 없습니다. 가장 중요한 첫 단계입니다.'
},
waitBuild: {
title: '건설 대기열',
content:
'건물이 건설 대기열에 추가되었습니다. 오른쪽 상단의 대기열 아이콘을 클릭하 진행 중인 모든 건설 및 연구 작업을 확인할 수 있습니다. 건설에는 시간이 걸리지만 대기하는 동안 계속 작업할 수 있습니다.'
'건물이 이제 건설 대기열에 습니다. 오른쪽 상단의 대기열 아이콘을 클릭하 진행 중인 모든 건설 및 연구 작업을 확인할 수 있습니다. 건설에는 시간이 걸리지만 대기하는 동안 계속 작업할 수 있습니다.'
},
buildMetalMine: {
title: '금속 광산 건설',
content:
'이제 에너지가 있으니 금속 광산을 건설할 수 있습니다. 금속 광산은 주요 금속 공급원이며, 금속은 거의 모든 건물과 함선에 사용됩니다.'
},
buildCrystalMine: {
title: '크리스탈 광산 건설',
content: '크리스탈은 더 희귀하지만 고급 기술에 필수적입니다. 크리스탈 광산을 건설하여 이 귀중한 자원을 수집하기 시작하세요.'
},
buildDeuterium: {
title: '중수소 합성기 건설',
content: '중수소는 함선 연료와 고급 연구에 필수적입니다. 중수소 합성기를 건설하여 이 핵심 자원을 생산하기 시작하세요.'
},
upgradeMines: {
title: '자원 광산 업그레이드',
content:
'다음으로, 로봇 공장 건설 요구 사항을 충족하기 위해 세 가지 자원 광산(금속, 크리스탈, 중수소)을 2레벨로 업그레이드해야 합니다. 자원이 충분하면 계속 업그레이드하세요.'
},
buildRobotics: {
title: '로봇 공장 건설',
content:
'로봇 공장은 건설 속도를 크게 높일 수 있습니다. 금속 광산, 크리스탈 광산, 중수소 합성기가 각각 2레벨이어야 합니다. 건설 효율을 높이기 위해 건설하세요!'
},
upgradeMinesForLab: {
title: '자원 광산 계속 업그레이드',
content:
'이제 연구 실험실 건설 요구 사항을 충족하기 위해 세 가지 자원 광산을 3레벨로 업그레이드해야 합니다. 자원 생산 능력을 계속 발전시키세요.'
},
buildResearchLab: {
title: '연구 실험실 건설',
content: '연구 실험실은 기술 발전의 기초입니다. 세 가지 자원 광산이 각각 3레벨이어야 합니다. 기술 연구를 해제하기 위해 건설하세요!'
},
gotoResearch: {
title: '연구 페이지로 이동',
content: '이제 연구 실험실이 있으니 "연구" 메뉴를 클릭하여 사용 가능한 기술을 확인하세요.'
},
researchEnergy: {
title: '에너지 기술 연구',
content: '에너지 기술은 에너지 생산을 높이고 고급 건물을 해제합니다. 가장 기본적이고 중요한 기술 중 하나입니다.'
},
shipyardIntro: {
title: '함대와 조선소',
content:
'함선을 통해 은하계를 탐험하고, 자원을 운송하고, 제국을 방어할 수 있습니다. 함선을 건조하려면 조선소(로봇 공장 2레벨 필요)가 필요합니다.'
},
gotoBuildingsForShipyard: {
title: '건물 페이지로 돌아가기',
content: '조선소를 건설하기 위해 건물 페이지로 돌아가세요.'
},
buildShipyard: {
title: '조선소 건설',
content: '조선소를 통해 함선과 방어 시스템을 건조할 수 있습니다. 함대 작전에 필수적입니다.'
},
fleetIntro: {
title: '함대 작전',
content: '함선을 보유하면 임무를 수행할 수 있습니다: 자원 운송, 행성 식민, 적 공격 또는 잔해장 탐험.'
},
galaxyIntro: {
title: '은하계 탐험',
content: '은하계 뷰는 다른 행성, 잔해장, 확장 기회를 보여줍니다. 목표를 정찰하고 전략을 계획하는 데 사용하세요.'
},
complete: {
title: '튜토리얼 완료!',
content:
'축하합니다, 사령관! 이제 기본 사항을 이해했습니다. 계속해서 제국을 건설하고, 기술을 연구하고, 은하계를 탐험하세요. 기억하세요: 먼저 에너지를 개발하고, 그다음 자원, 그다음 공장과 연구입니다! 행운을 빕니다!'
},
mobile: {
welcome: {
title: 'OGame에 오신 것을 환영합니다 (모바일)',
content:
'환영합니다, 사령관! 터치스크린용으로 설계된 간소화된 튜토리얼입니다. 제국 건설을 시작할 수 있도록 핵심 기능을 빠르게 소개하겠습니다.'
'환영합니다, 사령관! 터치스크린을 위해 설계된 간소화된 튜토리얼입니다. 핵심 기능을 빠르게 소개하여 제국 건설을 시작하겠습니다.'
},
resources: {
title: '상단 자원 바',
content: '상단에 자원이 표시됩니다: 금속, 크리스탈, 중수소. 클릭하여 자세한 생산 정보를 확인하세요.'
},
menu: {
title: '탐색 메뉴 열기',
content: '이 메뉴 아이콘을 클릭하여 탐색 바를 열면 건물, 연구, 함대 등 모든 기능에 액세스할 수 있습니다.'
},
gotoBuildings: {
title: '건물 페이지로 이동',
content: '메뉴가 열렸습니다! 이제 "건물" 옵션을 클릭하여 인프라 건설을 시작하세요.'
},
buildSolarPlant: {
title: '태양광 발전소 건설',
content: '먼저 태양광 발전소를 건설하세요! 아래로 스크롤하여 찾아 카드를 클릭하여 건설하세요. 에너지는 모든 것의 기초입니다.'
},
waitBuild: {
title: '건설 대기열',
content:
'오른쪽 상단의 대기열 아이콘을 클릭하여 건설 진행 상황을 확인하세요. 다른 페이지를 계속 탐색할 수 있으며, 건설은 백그라운드에서 진행됩니다.'
'오른쪽 상단의 대기열 아이콘을 클릭하여 건설 진행 상황을 확인하세요. 다른 페이지를 계속 탐색할 수 있으며 건설은 백그라운드에서 진행됩니다.'
},
buildMetalMine: {
title: '금속 광산 건설',
content: '에너지가 있으면 금속 광산을 건설하세요. 아래로 스크롤하여 금속 광산을 찾아 건설을 클릭하세요.'
},
complete: {
title: '빠른 튜토리얼 완료!',
content:
'잘하셨습니다! 이제 기본 작업을 익혔습니다. 계속해서 크리스탈 광산과 중수소 합성기를 건설한 다음 다른 기능을 탐험하세요. 기억하세요: 먼저 에너지, 그다음 자원!'
}
}
},
@@ -1409,11 +1547,21 @@ export default {
title: '전투 시뮬레이터',
message: '공격 전에 전투 결과를 시뮬레이션하세요. 양측 함대와 기술 레벨을 입력하여 승패와 손실을 예측.'
},
campaign: {
title: '캠페인 모드',
message:
'은하계 스토리 캠페인을 탐험하세요! 미션을 완료하여 자원 보상을 획득하고 새로운 도전을 해제하세요. 각 노드에는 고유한 목표와 적이 있습니다.'
},
achievements: {
title: '업적 시스템',
message:
'게임 목표를 완료하여 업적을 해제하고 암흑 물질 보상을 획득하세요! 업적은 여러 등급이 있으며, 더 높은 도전으로 더 좋은 보상을 받으세요.'
},
ranking: {
title: '랭킹',
message:
'다른 플레이어 및 NPC와 진행도를 비교하세요. 건물, 연구, 함대, 방어 점수에 따른 순위를 확인하세요. 리더보드 상위권을 목표로 하세요!'
},
settings: {
title: '설정',
message: '여기서 게임 데이터 관리, 알림 설정, 저장 내보내기/가져오기가 가능합니다. 정기적으로 백업하세요!'
@@ -1538,5 +1686,323 @@ export default {
you: '나',
scoreBreakdown: '점수 상세',
noData: '랭킹 데이터 없음'
},
// NPC 강화 행동 알림
npcBehavior: {
tradeOfferReceived: '거래 제안 수신',
tradeOfferDesc: '{npcName}이(가) 거래 제안을 보냈습니다',
attitudeChanged: 'NPC 태도 변화',
becameFriendly: '{npcName}이(가) 당신에게 우호적으로 변했습니다',
becameHostile: '{npcName}이(가) 당신에게 적대적으로 변했습니다',
intelReceived: '정보 수신',
intelReceivedDesc: '{npcName}이(가) 적 정보를 공유했습니다',
jointAttackInvite: '공동 공격 초대',
jointAttackInviteDesc: '{npcName}이(가) 적에 대한 공동 공격에 초대합니다',
aidReceived: '지원 수신',
aidReceivedDesc: '{npcName}이(가) {amount} 자원을 보냈습니다',
allyDefense: '동맹 방어',
allyDefenseDesc: '{npcName}이(가) 당신의 행성 방어를 위해 함대를 보내고 있습니다',
trade: {
title: '거래 제안',
from: '발신자',
offers: '제공',
requests: '요청',
expiresIn: '만료까지',
expired: '만료됨',
accept: '수락',
decline: '거절',
noOffers: '거래 제안 없음',
acceptSuccess: '거래 완료!',
acceptFailed: '자원 부족으로 거래할 수 없습니다',
declined: '거래를 거절했습니다',
ratio: '교환 비율'
},
intel: {
title: '정보 보고서',
from: '정보원',
target: '대상 NPC',
type: '정보 유형',
types: {
enemyFleet: '함대 정보',
enemyResources: '자원 정보',
enemyMovement: '동향 정보'
},
fleetInfo: '함대 정보',
resourceInfo: '자원 정보',
movementInfo: '동향 정보',
noReports: '정보 보고서 없음',
markAsRead: '읽음으로 표시',
content: '정보 내용',
noFleet: '함대가 감지되지 않음',
noData: '데이터 없음',
targetPosition: '목표 좌표',
missionType: '임무 유형'
},
jointAttack: {
title: '공동 공격 초대',
from: '발기자',
target: '대상 NPC',
targetPlanet: '대상 행성',
npcFleet: 'NPC 함대',
lootShare: '전리품 분배',
expiresIn: '만료까지',
expired: '만료됨',
accept: '공격 참여',
decline: '거절',
noInvites: '공동 공격 초대 없음',
acceptSuccess: '공동 공격에 참여했습니다!',
declined: '초대를 거절했습니다',
targetInfo: '공격 대상',
expectedShare: '예상 분배',
remaining: '남은 시간'
},
aid: {
title: '자원 지원',
from: '발신자',
resources: '지원 자원',
noAid: '지원 기록 없음'
},
attitudeChange: {
title: '태도 변화',
npc: 'NPC',
previous: '이전',
current: '현재',
reason: '이유',
reasons: {
attitude_swing: '태도 변동',
gift: '선물 수령',
attack: '공격 받음'
}
},
allyAction: {
title: '동맹 행동',
defense: '방어 지원',
defenseDesc: '{npcName}이(가) {targetPlanet} 방어를 위해 함대 파견',
jointAttackStarted: '공동 공격 시작',
jointAttackStartedDesc: '{targetNpc}에 대한 공동 공격이 시작되었습니다',
reputationBonus: '평판 보너스',
reputationBonusDesc: '동맹 {npcName}이(가) {targetNpc}에게 당신을 좋게 말하고 있습니다'
}
},
campaign: {
name: '캠페인',
description: '신비로운 은하를 탐험하고 고대의 비밀을 밝혀내세요',
totalProgress: '총 진행률',
questsCompleted: '퀘스트 완료',
chapter: '챕터',
branch: '분기',
startQuest: '퀘스트 시작',
claimRewards: '보상 받기',
objectives: '목표',
rewards: '보상',
completed: '완료',
inProgress: '진행 중',
available: '수락 가능',
locked: '잠김',
notifications: {
questStarted: '퀘스트 시작',
questCompleted: '퀘스트 완료!',
rewardsClaimed: '보상을 받았습니다',
objectiveCompleted: '목표 달성',
chapterUnlocked: '새 챕터가 해금되었습니다',
reputationUp: '{npcName}와(과)의 평판이 {value} 상승했습니다',
reputationDown: '{npcName}와(과)의 평판이 {value} 하락했습니다',
branchUnlocked: '새로운 스토리 분기가 해금되었습니다!'
},
dialogue: {
skip: '건너뛰기',
continue: '계속',
finish: '완료',
player: '사령관',
npc: 'NPC',
narrator: '내레이터',
mysterious: '신비한 신호',
unknownSource: '알 수 없는 출처',
choiceEffect: '선택지 효과'
},
chapters: {
'1': {
title: '기원',
description: '고향을 건설하고 우주로 첫 발을 내딛으세요',
backgroundStory:
'당신은 첫 행성을 얻은 젊은 우주 사령관입니다. 이 광활한 우주에서 고향을 건설하고, 기술을 발전시키며, 은하의 깊은 곳을 탐험하게 됩니다...'
},
'2': {
title: '탐험',
description: '우주를 탐험하고 고대 유적을 발견하세요',
backgroundStory:
'세력이 커짐에 따라 심우주에서 오는 신비한 신호가 당신의 주의를 끕니다. 이 신호들은 고대의 비밀을 가리키며, 용감한 탐험가를 기다리고 있는 것 같습니다...'
},
'3': {
title: '외교',
description: '다른 세력과 관계를 구축하세요',
backgroundStory:
'은하에 당신만 있는 것은 아닙니다. 다른 문명들이 부상하고 있습니다. 그들의 적이 될지 동맹을 맺을지 결정해야 합니다. 외교적 지혜가 제국의 운명을 결정할 것입니다...'
},
'4': {
title: '그림자의 부상',
description: '강력한 적에 맞서고 영토를 방어하세요',
backgroundStory:
'그림자 속에 위험이 도사리고 있습니다. 강력한 적대 세력이 당신의 영토를 노리고 있습니다. 전쟁은 불가피합니다. 다가오는 폭풍에 대비해야 합니다...'
},
'5': {
title: '고대의 비밀',
description: '은하 깊은 곳의 비밀을 밝혀내세요',
backgroundStory:
'모든 단서가 은하에서 가장 신비로운 지역을 가리킵니다. 그곳에는 고대 문명이 남긴 궁극의 비밀이 잠들어 있습니다. 모든 것을 밝힐 준비가 되었습니까?'
}
},
quests: {
'1_1': { title: '고향 건설', description: '기반 시설을 구축하여 행성의 토대를 마련하세요' },
'1_2': { title: '기술 계몽', description: '기초 기술을 연구하여 기술 여정을 시작하세요' },
'1_3': { title: '첫 번째 함선', description: '첫 전함을 건조하세요' },
'1_4': { title: '낯선 이웃', description: '인근 시스템의 다른 세력을 정찰하세요' },
'1_5': { title: '첫 접촉', description: '인근 NPC 세력과 초기 접촉을 확립하세요' },
'2_1': { title: '개척 식민지', description: '첫 번째 새 행성을 식민지화하세요' },
'2_2': { title: '심우주 탐험', description: '함대를 탐험 임무에 보내세요' },
'2_3': { title: '신비한 신호', description: '심우주에서 오는 신비한 신호를 조사하세요' },
'2_4': { title: '유적 조사', description: '발견한 고대 유적을 탐험하세요' },
'2_5': { title: '아카이브 해독', description: '유적에서 얻은 데이터를 연구하세요' },
'3_1': { title: '평화의 사절', description: '외교로 NPC와의 관계를 개선하세요' },
'3_2': { title: '무역 관계', description: '우호 세력과 안정적인 관계를 구축하세요' },
'3_3': { title: '공통의 위협', description: '잠재적인 적대 세력을 발견하세요' },
'3_4': { title: '동맹 협상', description: '우호 NPC와 공식 동맹을 맺으세요' },
'3_5': { title: '폭풍 대비', description: '방어 시설을 건설하여 도전에 대비하세요' },
'4_1': { title: '전초 기지 공격', description: '적대 세력의 첫 공격을 격퇴하세요' },
'4_2': { title: '정보 수집', description: '적의 군사 배치를 정찰하세요' },
'4_3': { title: '반격', description: '적에게 반격을 시작하세요' },
'4_4': { title: '자원 쟁탈', description: '전장 잔해를 재활용하여 자원을 얻으세요' },
'4_5': { title: '결전 전야', description: '최종 전투를 위한 강력한 함대를 건조하세요' },
'5_1': { title: '유적의 깊은 곳', description: '유적의 가장 깊은 곳을 탐험하세요' },
'5_2': { title: '고대 기술', description: '고대 문명의 기술을 해금하세요' },
'5_3': { title: '최종 대결', description: '신비한 적과의 최종 전투에 임하세요' },
'5_4': { title: '새 시대', description: '새 식민지를 세우고 새 시대를 열으세요' },
'5_5': { title: '유산 계승', description: '계속 발전하고 더 많은 시스템을 정복하세요' }
},
objectiveTypes: {
buildBuilding: '{building}을(를) 레벨 {level}로 건설',
researchTech: '{tech}을(를) 레벨 {level}까지 연구',
produceShips: '{ship} {count}대 생산',
accumulateResources: '{resource}을(를) {amount} 축적',
defeatNPC: '{npc} 격파',
winBattles: '{count}회 전투 승리',
recycleDebris: '잔해 {amount} 재활용',
reachRelation: '{npc}와 {level} 관계 도달',
sendGift: '{npc}에게 {count}회 선물 보내기',
formAlliance: '{npc}와 동맹 체결',
colonize: '행성 {count}개 식민지화',
expedition: '탐험 {count}회 완료',
spyTarget: '{target} 정찰'
},
errors: {
questNotFound: '퀘스트를 찾을 수 없습니다',
questNotAvailable: '퀘스트를 이용할 수 없습니다',
questNotActive: '퀘스트가 활성화되지 않았습니다',
questNotCompleted: '퀘스트 미완료',
rewardsAlreadyClaimed: '이미 보상을 받았습니다',
prerequisiteNotMet: '선행 퀘스트 미완료'
},
speakers: {
ancientVoice: '고대의 목소리',
neighborNPC: '인접 세력',
mysteriousSignal: '신비한 신호',
enemyCommander: '적 사령관'
},
objectiveDescriptions: {
buildMetalMine: '금속 광산을 레벨 2로 건설',
buildCrystalMine: '크리스탈 광산을 레벨 2로 건설',
buildSolarPlant: '태양열 발전소를 레벨 2로 건설',
buildResearchLab: '연구소를 레벨 1로 건설',
researchEnergy: '에너지 기술을 레벨 1까지 연구',
buildShipyard: '조선소를 레벨 2로 건설',
researchCombustion: '연소 드라이브를 레벨 1까지 연구',
buildLightFighters: '경전투기 5대 건조',
researchEspionage: '스파이 기술을 레벨 2까지 연구',
buildSpyProbes: '스파이 탐사선 3대 건조',
spyAnyNPC: 'NPC 행성 정찰',
sendGiftToNPC: 'NPC에게 선물 보내기',
researchAstrophysics: '천체물리학을 레벨 1까지 연구',
buildColonyShip: '식민선 건조',
colonizeNewPlanet: '새 행성 식민지화',
completeExpedition: '탐험 임무 완료',
discoverRuins: '고대 유적 발견',
researchComputer: '컴퓨터 기술을 레벨 4까지 연구',
improveRelation: 'NPC와의 관계 개선',
reachFriendly: 'NPC와 우호 상태 도달',
spyHostileNPC: '적대 NPC 정찰',
formAlliance: '우호 NPC와 동맹 체결',
buildDefenses: '방어 시설 건설',
winDefenseBattle: '방어전 승리',
spyEnemyPlanet: '적 행성 정찰',
attackEnemy: '적 공격',
recycleDebris: '잔해 재활용',
buildBattleships: '전함 10대 건조',
exploreDeepRuins: '깊은 유적 탐험',
researchHyperspace: '하이퍼스페이스 기술 연구',
defeatBoss: '신비한 적 격파',
colonizeSpecial: '특별한 장소 식민지화',
continueDevelopment: '개발 계속'
},
dialogues: {
'1_1': {
prologue_1:
'은하에 오신 것을 환영합니다, 젊은 사령관. 이 광활한 우주가 당신의 탐험을 기다리고 있습니다. 먼저 고향 행성을 건설합시다.',
prologue_2: '새로운 의식이 깨어나는 것을 느낀다... 흥미롭군... 어디까지 갈 수 있는지 보자...'
},
'1_2': { prologue_1: '기본 인프라가 완성되었습니다. 이제 기술을 발전시킬 시간입니다. 연구소를 건설하고 기술 여정을 시작하세요.' },
'1_3': { prologue_1: '기술 지원으로 함대 건조가 가능해졌습니다. 조선소를 건설하고 첫 전함을 생산하세요.' },
'1_4': {
prologue_1: '함대가 형태를 갖추고 있습니다. 주변 상황을 파악합시다. 스파이 탐사선을 보내 인근 세력을 정찰하세요.',
prologue_2: '당신은 혼자가 아니다... 이 은하에는 다른 문명이 존재한다...'
},
'1_5': {
prologue_1: '인근 세력을 발견했습니다. 외교는 예술입니다. 그들과의 접촉을 시도하세요.',
epilogue_1: '선물에 감사합니다, 사령관. 친구가 될 수 있기를 바랍니다.',
epilogue_2: '좋아... 연결을 구축하는 것은 더 깊은 비밀을 밝히는 첫 단계다...'
},
'2_1': {
prologue_1: '세력이 확립되었습니다. 영토를 확장할 시간입니다. 천체물리학을 연구하고, 식민선을 건조하여 새 행성을 탐험하세요.',
prologue_2: '우주는 무한하다... 더 많은 행성은 더 많은 가능성을 의미한다...'
},
'2_2': {
prologue_1: '식민지화 성공! 하지만 우주에는 더 깊은 비밀이 기다리고 있습니다. 함대를 탐험 임무에 보내세요.',
prologue_2: '저 멀리서 희미한 신호가... 무언가 그곳에서 당신을 기다리고 있다...'
},
'2_3': {
prologue_1: '탐험에서 이상한 신호를 발견했습니다. 이 신호들은 고대 문명에서 온 것 같습니다... 출처를 조사하세요.',
epilogue_1: '이 기호들은... 고대 문명의 유적이다! 계속 조사하여 비밀을 밝히세요.'
},
'2_4': { prologue_1: '고대 유적의 위치를 발견했습니다. 함대를 보내 무엇을 발견할 수 있는지 탐험하세요.' },
'2_5': { prologue_1: '유적에서 데이터 아카이브가 발견되었습니다. 이 데이터를 연구하면 새 기술을 해금할 수 있을지도 모릅니다.' },
'3_1': { prologue_1: '탐험 중에도 외교를 잊지 마세요. 주변 세력과 좋은 관계를 유지하는 것이 유익합니다.' },
'3_2': { prologue_1: '일부 세력이 우호를 보이고 있습니다. 관계를 계속 깊게 하면 더 많은 지원을 받을 수 있을지도 모릅니다.' },
'3_3': {
prologue_1: '정보에 따르면 적대 세력이 그림자에서 당신을 감시하고 있습니다. 경계를 늦추지 말고 그들의 움직임을 정찰하세요.'
},
'3_4': { prologue_1: '우호 세력과 공식 동맹을 맺어 위협에 대해 서로를 지원하세요.' },
'3_5': { prologue_1: '위협이 다가오고 있습니다. 방어 시설을 건설하고 가능한 충돌에 대비하세요.' },
'4_1': {
prologue_1: '적이 공격을 시작했습니다! 행성을 방어하세요!',
epilogue_1: '적의 첫 번째 파도를 성공적으로 격퇴했습니다. 하지만 이것은 시작에 불과합니다...'
},
'4_2': { prologue_1: '적이 후퇴했지만 돌아올 것입니다. 그들의 행성을 정찰하여 전력을 파악하세요.' },
'4_3': { prologue_1: '반격할 시간입니다. 적 행성을 공격하여 그들의 전력을 약화시키세요.' },
'4_4': { prologue_1: '전장에 많은 잔해가 남아 있습니다. 이 자원을 재활용하여 다음 전투에 대비하세요.' },
'4_5': { prologue_1: '최종 전투가 다가오고 있습니다. 강력한 함대를 건조하고 궁극의 도전에 대비하세요.' },
'5_1': {
prologue_1: '모든 단서가 유적의 가장 깊은 곳을 가리킵니다. 고대 문명의 핵심 비밀이 그곳에 있습니다.',
prologue_2: '드디어 도착했다... 진실이 곧 밝혀질 것이다...'
},
'5_2': { prologue_1: '유적 깊은 곳에서 잃어버린 고대 기술을 발견했습니다. 연구하여 그 힘을 해방하세요.' },
'5_3': {
prologue_1: '신비한 적이 나타났습니다. 이것이 마지막 도전입니다. 격파하세요!',
epilogue_1: '해냈습니다! 고대의 수호자가 쓰러졌습니다. 은하의 비밀이 이제 당신에게 열려 있습니다.'
},
'5_4': { prologue_1: '마침내 평화가 찾아왔습니다. 이 새 시대에 새 식민지를 세우고 제국을 확장하세요.' },
'5_5': {
prologue_1: '당신의 전설은 이제 막 시작되었습니다. 계속 탐험하고 더 많은 성계를 정복하세요!',
epilogue_1: '은하는 광대하고 끝이 없으며, 셀 수 없는 비밀이 당신을 기다리고 있습니다...'
}
}
}
}

View File

@@ -11,29 +11,12 @@ export default {
common: {
confirm: 'Подтвердить',
cancel: 'Отмена',
delete: 'Удалить',
edit: 'Редактировать',
save: 'Сохранить',
close: 'Закрыть',
back: 'Назад',
next: 'Далее',
gotIt: '',
previous: 'Предыдущий',
submit: 'Отправить',
reset: 'Сбросить',
search: 'Поиск',
filter: 'Фильтр',
loading: 'Загрузка...',
noData: 'Нет данных',
error: 'Ошибка',
gotIt: 'Понятно',
success: 'Успешно',
warning: 'Предупреждение',
info: 'Информация',
resourceType: 'Тип ресурса',
playerName: 'Командир',
timeHour: 'ч',
timeMinute: 'мин',
timeSecond: 'сек',
featureLocked: 'Функция заблокирована',
unlockRequired: 'Требуется здание',
requiredBuilding: 'Необходимое здание',
@@ -41,7 +24,6 @@ export default {
goToBuildings: 'К зданиям',
locked: 'Заблокировано',
viewRequirements: 'Просмотр требований',
requirements: 'Требования',
requirementsNotMet: 'Требования не выполнены',
current: 'Текущий',
level: 'Уровень',
@@ -51,7 +33,8 @@ export default {
viewDetails: 'Подробнее',
exitConfirmTitle: 'Выйти из игры',
exitConfirmMessage: 'Вы уверены, что хотите выйти? Прогресс сохраняется автоматически.',
points: 'Очки'
points: 'Очки',
retry: 'Повторить'
},
errors: {
requirementsNotMet: 'Требования не выполнены',
@@ -88,13 +71,13 @@ export default {
galaxy: 'Галактика',
diplomacy: 'Дипломатия',
achievements: 'Достижения',
campaign: 'Кампания',
ranking: 'Рейтинг',
messages: 'Сообщения',
settings: 'Настройки',
gm: 'GM'
},
sidebar: {
language: 'Язык',
lightMode: 'Светлая тема',
darkMode: 'Тёмная тема',
collapse: 'Свернуть',
@@ -119,9 +102,6 @@ export default {
},
energy: {
lowWarning: 'Дефицит энергии! Производство ресурсов остановлено!',
severeWarning: 'Дефицит энергии! Производство ресурсов остановлено!',
criticalWarning: 'Дефицит энергии! Производство ресурсов остановлено!',
noProduction: 'Дефицит энергии! Производство ресурсов остановлено!',
deficitDetail: 'Дефицит энергии: {deficit}, постройте больше электростанций',
buildSolarPlant: 'Построить электростанцию'
},
@@ -132,27 +112,20 @@ export default {
lowResources: 'Почти истощено: {resources}'
},
planet: {
planet: 'Планета',
moon: 'Луна',
colony: 'Колония',
position: 'Позиция',
coordinates: 'Координаты',
switchToMoon: 'На луну',
backToPlanet: 'Вернуться на планету',
switchPlanet: 'Переключить планету',
currentPlanet: 'Текущая планета',
fields: 'Поля',
temperature: 'Температура',
homePlanet: 'Родная планета',
planetPrefix: 'Планета',
moonSuffix: 'я луна',
colonyPrefix: 'Колония',
renamePlanet: 'Переименовать планету',
renamePlanetTitle: 'Переименовать планету',
newPlanetName: 'Новое название',
planetNamePlaceholder: 'Введите новое название планеты',
rename: 'Переименовать',
renameSuccess: 'Планета переименована в {name}'
rename: 'Переименовать'
},
player: {
points: 'Всего очков'
@@ -179,8 +152,10 @@ export default {
sensorPhalanx: 'Сенсорная фаланга',
jumpGate: 'Прыжковые ворота',
planetDestroyerFactory: 'Фабрика разрушителей планет',
geoResearchStation: 'Геологическая станция',
deepDrillingFacility: 'Глубинная буровая установка',
buildTime: 'Время строительства',
build: '',
build: 'Построить',
production: 'Производство',
consumption: 'Потребление',
totalCost: 'Общая стоимость',
@@ -194,7 +169,6 @@ export default {
buildQueueBonus: 'Очередь строительства',
spaceBonus: 'Бонус пространства',
buildSpeedBonus: 'Бонус скорости строительства',
researchSpeedBonus: 'Бонус скорости исследования',
planetSpace: 'Planet Space',
moonSpace: 'Moon Space',
missileCapacity: 'Missile Capacity',
@@ -228,7 +202,10 @@ export default {
lunarBase: 'Увеличивает доступное пространство на луне, +30 пространства за уровень',
sensorPhalanx: 'Обнаруживает активность флота в окружающих системах',
jumpGate: 'Мгновенно переносит флоты на другие луны',
planetDestroyerFactory: 'Производит абсолютное оружие, способное уничтожать планеты'
planetDestroyerFactory: 'Производит абсолютное оружие, способное уничтожать планеты',
geoResearchStation:
'Исследует геологическую структуру и повышает скорость восстановления рудных месторождений. +50% скорости восстановления за уровень',
deepDrillingFacility: ''
},
ships: {
lightFighter: 'Лёгкий истребитель',
@@ -292,9 +269,6 @@ export default {
},
research: {
researchTime: 'Время исследования',
totalCost: 'Общая стоимость',
totalPoints: 'Общие очки',
levelRange: 'Диапазон уровней',
capacity: 'Capacity/Effect',
storageCapacity: 'Capacity',
energyProduction: 'Energy Production',
@@ -302,7 +276,15 @@ export default {
buildQueue: 'Build Queue',
planetSpace: 'Planet Space',
moonSpace: 'Moon Space',
missileCapacity: 'Missile Capacity'
missileCapacity: 'Missile Capacity',
attackBonus: 'Бонус атаки',
shieldBonus: 'Бонус щита',
armorBonus: 'Бонус брони',
spyLevel: 'Уровень шпионажа',
researchQueueBonus: 'Очередь исследований',
colonySlots: 'Слоты колоний',
forAllPlanets: '(Глобально)',
speedBonus: 'Бонус скорости'
},
technologies: {
energyTechnology: 'Энергетическая технология',
@@ -322,7 +304,8 @@ export default {
hyperspaceDrive: 'Гиперпространственный двигатель',
darkMatterTechnology: 'Технология тёмной материи',
terraformingTechnology: 'Технология терраформирования',
planetDestructionTech: 'Технология уничтожения планет'
planetDestructionTech: 'Технология уничтожения планет',
miningTechnology: ''
},
technologyDescriptions: {
energyTechnology: 'Улучшает энергоэффективность',
@@ -344,7 +327,8 @@ export default {
darkMatterTechnology: 'Исследование свойств и применения тёмной материи',
terraformingTechnology:
'Исследование технологии терраформирования планет, увеличивает доступное пространство всех планет на 30 за уровень',
planetDestructionTech: 'Исследование ужасающей технологии уничтожения целых планет'
planetDestructionTech: 'Исследование ужасающей технологии уничтожения целых планет',
miningTechnology: ''
},
officers: {
commander: 'Командир',
@@ -379,7 +363,7 @@ export default {
cancelResearch: 'Отменить исследование',
confirmCancel: 'Вы уверены, что хотите отменить? 50% ресурсов будет возвращено.',
level: 'Уровень',
gmModeActivated: '',
gmModeActivated: 'Режим GM активирован! Проверьте меню навигации.',
upgradeToLevel: 'Улучшить до уровня',
tabs: {
all: 'Все',
@@ -395,17 +379,17 @@ export default {
resourcesReady: 'Готово',
waitingResources: 'Ожидание',
waitingQueueFull: 'Очередь ожидания заполнена',
movedToQueue: 'Задача перемещена в очередь'
movedToQueue: 'Задача перемещена в очередь',
buildQueueBonus: 'Очередь строительства',
spaceBonus: 'Бонус пространства',
researchQueueBonus: 'Очередь исследований',
quantity: 'Количество'
},
overview: {
title: 'Обзор планеты',
resourceOverview: 'Ресурсы',
fleetInfo: 'Флот',
currentShips: 'Корабли на этой планете',
productionSources: 'Источники производства',
productionSourcesDesc: 'Подробная информация о производстве ресурсов и бонусах',
consumptionSources: 'Источники потребления',
consumptionSourcesDesc: 'Детали потребления энергии зданиями',
totalProduction: 'Общее производство',
totalConsumption: 'Общее потребление',
noConsumption: 'Нет потребления энергии',
@@ -415,13 +399,9 @@ export default {
},
buildingsView: {
title: 'Здания',
usedSpace: 'Использовано полей',
spaceUsage: 'Использование полей',
level: 'Уровень',
gmModeActivated: '',
upgradeCost: 'Стоимость улучшения',
buildTime: 'Время строительства',
build: 'Построить',
upgrade: 'Улучшить',
maxLevelReached: 'Достигнут максимальный уровень',
requirementsNotMet: 'Требования не выполнены',
@@ -431,8 +411,7 @@ export default {
demolishRefund: 'Возврат от сноса',
demolishFailed: 'Снос не удался',
demolishFailedMessage: 'Невозможно снести это здание. Проверьте, не заполнена ли очередь строительства или уровень здания не равен 0.',
confirmDemolish: 'Подтвердить снос',
confirmDemolishMessage: 'Вы уверены, что хотите снести следующее здание?'
confirmDemolish: 'Подтвердить снос'
},
researchView: {
title: 'Исследования',
@@ -444,27 +423,13 @@ export default {
'Пожалуйста, проверьте, достаточно ли у вас ресурсов, выполнены ли предварительные условия или нет других исследовательских задач.'
},
shipyard: {
attack: 'Атака',
missileAttack: 'Ракетная атака',
shield: 'Щит',
armor: 'Броня',
speed: 'Скорость',
cargoCapacity: 'Грузоподъёмность',
fuelConsumption: 'Расход топлива',
buildCost: 'Стоимость постройки',
buildTime: 'Время строительства',
build: '',
perUnit: 'За единицу',
batchCalculator: 'Калькулятор партий',
quantity: 'Количество',
totalCost: 'Общая стоимость',
totalTime: 'Общее время'
fuelConsumption: 'Расход топлива'
},
shipyardView: {
title: 'Верфь',
fleetStorage: 'Хранилище флота',
owned: 'В наличии',
attack: 'Атака',
missileAttack: 'Ракетная атака',
shield: 'Щит',
speed: 'Скорость',
@@ -485,7 +450,7 @@ export default {
armor: 'Броня',
buildCost: 'Стоимость постройки',
buildTime: 'Время строительства',
build: '',
build: 'Построить',
perUnit: 'За единицу',
batchCalculator: 'Калькулятор партий',
quantity: 'Количество',
@@ -539,6 +504,7 @@ export default {
missionInfo: 'Информация о миссии',
fuelConsumption: 'Расход топлива',
flightTime: 'Время полета',
outOfRange: 'Вне зоны досягаемости',
attackMission: 'Атака',
transport: 'Транспорт',
colonize: 'Колонизация',
@@ -580,11 +546,12 @@ export default {
arrivalTime: 'Время прибытия',
returnTime: 'Время возврата',
recallFleet: 'Отозвать флот',
abortMission: '',
abortMissionTitle: '',
abortMissionWarning: '',
abortMissionSuccess: '',
abortMissionSuccessMessage: '',
abortMission: 'Прервать миссию',
abortMissionTitle: 'Подтверждение прерывания миссии',
abortMissionWarning:
'ВНИМАНИЕ: Прерывание этой миссии приведёт к необратимой потере {ships} кораблей и {resources} ресурсов!\n\nЭто действие необратимо, флот и ресурсы не вернутся.',
abortMissionSuccess: 'Миссия прервана',
abortMissionSuccessMessage: 'Миссия была прервана, флот и ресурсы потеряны.',
sendFailed: 'Отправка не удалась',
sendFailedMessage: 'Пожалуйста, проверьте количество флота, наличие топлива или ограничения грузоподъёмности.',
recallFailed: 'Отзыв не удался',
@@ -639,7 +606,8 @@ export default {
jumpGateSuccess: 'Переброска через ворота успешна',
jumpGateSuccessMessage: 'Флот мгновенно переброшен к {target}',
jumpGateFailed: 'Переброска через ворота не удалась',
jumpGateFailedMessage: 'Проверьте состояние ворот и конфигурацию флота'
jumpGateFailedMessage: 'Проверьте состояние ворот и конфигурацию флота',
destroy: 'Уничтожение'
},
officersView: {
title: 'Офицеры',
@@ -653,7 +621,6 @@ export default {
benefitsBonus: 'Бонусы',
resourceProduction: 'Производство ресурсов',
darkMatterProduction: 'Производство тёмной материи',
energyProduction: 'Производство энергии',
buildingSpeed: 'Скорость строительства',
researchSpeed: 'Скорость исследований',
fleetSpeed: 'Скорость флота',
@@ -682,14 +649,11 @@ export default {
selectGalaxy: 'Выбрать галактику',
system: 'Система',
selectSystem: 'Выбрать систему',
view: 'Показать',
myPlanet: 'Моя планета',
myPlanets: 'Просмотр моих систем',
npcPlanets: 'Планеты NPC',
selectPlanetToView: 'Выберите планету для просмотра её системы',
totalPositions: 'Всего 10 позиций планет',
mine: 'Моя',
hostile: 'Враждебная',
emptySlot: 'Пусто - можно колонизировать',
scout: 'Разведка',
attack: 'Атака',
@@ -716,7 +680,9 @@ export default {
systems: 'систем',
distance: 'Расстояние',
flightTime: 'Время полета',
outOfRange: 'Вне зоны досягаемости',
launchMissile: 'Запустить',
missileLaunched: 'Ракета запущена',
cancel: 'Отмена',
colonizePlanetMessage:
'Вы уверены, что хотите колонизировать позицию [{coordinates}]?\n\nПерейдите на страницу флота, чтобы отправить колонизационный корабль.',
@@ -729,19 +695,33 @@ export default {
'Вы уверены, что хотите отправить ресурсы в подарок планете [{coordinates}]?\n\nПерейдите на страницу флота, чтобы выбрать транспортные корабли и загрузить ресурсы.',
npcPlanetName: 'Планета {name}',
intercepted: 'Перехвачено',
defenseLosses: 'Потери обороны'
defenseLosses: 'Потери обороны',
phalanxScan: 'Сканирование сенсором',
phalanxScanTitle: 'Сканирование сенсорной решёткой',
phalanxScanDescription: 'Сканировать активность флота планеты [{coordinates}]',
phalanxNoMoon: 'Требуется луна с сенсорной решёткой для сканирования',
phalanxCost: 'Стоимость сканирования',
phalanxNoFleets: 'Активность флота не обнаружена',
phalanxFleetDetected: 'Обнаружено флотов: {count}',
phalanxOrigin: 'Отправление',
phalanxDestination: 'Назначение',
phalanxArrival: 'Время прибытия',
phalanxReturn: 'Время возвращения',
phalanxStatusOutbound: 'В пути',
phalanxStatusReturning: 'Возвращается',
phalanxInsufficientDeuterium: 'Недостаточно дейтерия'
},
messagesView: {
title: 'Сообщения',
battles: 'Битвы',
spy: 'Разведка',
npc: 'NPC',
diplomacy: '',
diplomacy: 'Дипломатия',
battleReports: 'Отчёты о боях',
spyReports: 'Отчёты разведки',
noBattleReports: 'Нет отчётов о боях',
noSpyReports: 'Нет отчётов разведки',
noDiplomaticReports: '',
noDiplomaticReports: 'Нет дипломатических отчётов',
battleReport: 'Отчёт о бое',
spyReport: 'Отчёт разведки',
victory: 'Победа',
@@ -762,6 +742,8 @@ export default {
defense: 'Оборона',
buildings: 'Здания',
unread: 'Непрочитано',
pending: 'Ожидает',
invalidData: 'Недействительные данные',
targetPlanet: 'Целевая планета',
attackerRemaining: 'Осталось у нападающего',
defenderRemaining: 'Осталось у защитника',
@@ -814,36 +796,36 @@ export default {
polite_decline: 'Вежливо отказались'
},
// Spied notification dialog
spiedNotificationDetails: '',
spyDetected: '',
detectionResult: '',
detectionSuccess: '',
spiedNotificationMessage: '',
spiedNotificationTip: '',
viewInGalaxy: '',
spiedNotificationDetails: 'Детали уведомления о шпионаже',
spyDetected: 'Шпион обнаружен',
detectionResult: 'Результат обнаружения',
detectionSuccess: 'Вы обнаружили вражеского шпиона!',
spiedNotificationMessage: '{npc} попытался шпионить за вашей планетой {planet}',
spiedNotificationTip: 'Рассмотрите возможность усиления обороны или контратаки, если этот NPC враждебен',
viewInGalaxy: 'Показать в галактике',
// Mission report dialog
missionReportDetails: '',
missionSuccess: '',
missionFailed: '',
origin: '',
destination: '',
missionDetails: '',
transportedResources: '',
recycledResources: '',
remainingDebris: '',
newPlanet: '',
missionReportDetails: 'Детали отчёта о миссии',
missionSuccess: 'Успех',
missionFailed: 'Неудача',
origin: 'Происхождение',
destination: 'Назначение',
missionDetails: 'Детали миссии',
transportedResources: 'Перевезённые ресурсы',
recycledResources: 'Переработанные ресурсы',
remainingDebris: 'Оставшиеся обломки',
newPlanet: 'Новая планета',
// NPC activity dialog
npcActivityDetails: '',
npcActivityDetails: 'Детали активности NPC',
activityType: {
recycle: ''
recycle: 'Переработка обломков'
},
activityLocation: '',
position: '',
nearPlanet: '',
activityDescription: '',
npcActivityMessage: '',
arrivalTime: '',
npcActivityTip: '',
activityLocation: 'Местоположение активности',
position: 'Позиция',
nearPlanet: 'Возле планеты',
activityDescription: 'Описание активности',
npcActivityMessage: '{npc} {activity} в {position}',
arrivalTime: 'Время прибытия',
npcActivityTip: 'NPC могут собирать обломки после боёв. Вы можете попытаться прибыть на место первым, чтобы конкурировать за ресурсы',
clearMessages: 'Очистить сообщения',
clearMessageTypes: 'Выберите типы сообщений для очистки',
clearBattleReports: 'Боевые отчёты',
@@ -853,7 +835,11 @@ export default {
clearNPCActivity: 'Активность NPC',
clearGiftNotifications: 'Уведомления о подарках',
clearGiftRejected: 'Отклонённые подарки',
clearNow: 'Очистить сейчас'
clearTradeOffers: 'Торговые предложения',
clearIntelReports: 'Разведывательные отчёты',
clearJointAttackInvites: 'Приглашения на совместную атаку',
clearNow: 'Очистить сейчас',
clearSuccess: 'Сообщения очищены'
},
missionReports: {
transportSuccess: 'Миссия транспортировки успешно завершена',
@@ -980,7 +966,6 @@ export default {
viewUpdate: 'Просмотреть обновление',
updateAvailable: 'Доступна новая версия. Нажмите, чтобы просмотреть примечания к выпуску.',
download: 'Скачать',
goToDownload: 'Перейти к загрузке',
buildDate: 'Дата сборки',
community: 'Сообщество',
github: 'Репозиторий GitHub',
@@ -1002,14 +987,58 @@ export default {
suppressInFocus: 'Не отправлять уведомления браузера, когда страница в фокусе',
expandTypes: 'Развернуть детали',
collapseTypes: 'Свернуть детали',
displaySettings: 'Настройки отображения',
displaySettingsDesc: 'Настройка визуальных эффектов игры',
backgroundAnimation: 'Фоновая анимация',
backgroundAnimationDesc: 'Показывать анимацию звёздного неба/частиц на фоне (может влиять на производительность)',
// Обновление имен NPC
npcNameUpdate: 'Обновление имен NPC',
npcNameUpdateTitle: 'Обнаружены устаревшие имена NPC',
npcNameUpdateMessage: 'Найдено {count} NPC с устаревшим форматом имен. Хотите обновить их до новых локализованных имен?',
npcNameUpdateConfirm: 'Обновить имена',
npcNameUpdateCancel: 'Оставить как есть',
npcNameUpdateSuccess: 'Успешно обновлено {count} имен NPC',
npcNameUpdateSkipped: 'Обновление имен NPC пропущено'
npcNameUpdateSkipped: 'Обновление имен NPC пропущено',
// WebDAV
webdav: {
title: 'Облачная синхронизация',
desc: 'Синхронизация сохранений через WebDAV',
config: 'Настроить',
configTitle: 'Настройка WebDAV',
configDesc: 'Настройте сервер WebDAV для облачной синхронизации',
notConfigured: 'Сначала настройте сервер WebDAV',
serverUrl: 'URL сервера',
serverUrlPlaceholder: 'напр. https://dav.example.com',
serverUrlHint: 'Введите адрес вашего WebDAV сервера',
username: 'Имя пользователя',
usernamePlaceholder: 'Введите имя пользователя',
password: 'Пароль',
passwordPlaceholder: 'Введите пароль',
passwordHint: 'Пароль хранится только локально',
basePath: 'Путь сохранений',
basePathPlaceholder: 'напр. /ogame-saves/',
testConnection: 'Проверить соединение',
testing: 'Проверка...',
testSuccess: 'Соединение успешно',
testFailed: 'Соединение не удалось',
save: 'Сохранить',
clearConfig: 'Очистить',
configSaved: 'Настройки сохранены',
configCleared: 'Настройки очищены',
upload: 'Загрузить',
uploading: 'Загрузка...',
uploadSuccess: 'Загрузка успешна',
uploadFailed: 'Загрузка не удалась',
download: 'Скачать',
downloadSuccess: 'Скачивание успешно',
downloadFailed: 'Скачивание не удалось',
selectFile: 'Выбрать сохранение',
selectFileDesc: 'Выберите файл сохранения для восстановления',
noFiles: 'Файлы сохранений не найдены',
loadFailed: 'Не удалось загрузить список файлов',
confirmDelete: 'Вы уверены, что хотите удалить "{name}"?',
deleteSuccess: 'Файл удален',
deleteFailed: 'Удаление не удалось'
}
},
notifications: {
constructionComplete: 'Строительство завершено',
@@ -1031,8 +1060,8 @@ export default {
officers: 'Офицеры',
modifyResources: 'Изменить ресурсы',
resourcesDesc: 'Быстрое изменение ресурсов планеты',
maxAllResources: '',
maxAllResourcesSuccess: '',
maxAllResources: 'Максимизировать все',
maxAllResourcesSuccess: 'Все ресурсы максимизированы',
modifyBuildings: 'Изменить здания',
buildingsDesc: 'Быстрая установка уровней зданий',
modifyResearch: 'Изменить исследования',
@@ -1074,10 +1103,10 @@ export default {
dangerZoneDesc: 'Следующие операции необратимы',
resetGame: 'Сбросить игру',
resetGameConfirm: 'Вы уверены, что хотите сбросить игру? Все данные будут удалены!',
completeAllQueues: '',
completeAllQueuesDesc: '',
completeQueues: '',
completeQueuesSuccess: ''
completeAllQueues: 'Завершить все очереди',
completeAllQueuesDesc: 'Мгновенно завершить все очереди строительства, исследований, верфи, обороны и миссии флота',
completeQueues: 'Завершить очереди',
completeQueuesSuccess: 'Все очереди успешно завершены'
},
alerts: {
incomingFleets: '{count} вражеских флотов приближается',
@@ -1108,11 +1137,13 @@ export default {
missionType: {
spy: 'Разведка',
attack: 'Атака',
recycle: 'Переработка',
unknown: 'Неизвестно'
},
warning: {
spy: 'Вражеская разведка приближается!',
attack: 'Вражеская атака приближается!',
recycle: 'Враг перерабатывает обломки поблизости!',
unknown: 'Вражеский флот приближается!'
}
},
@@ -1129,13 +1160,11 @@ export default {
noFriendlyNpcs: 'Нет дружественных NPC',
noNeutralNpcs: 'Нет нейтральных NPC',
noHostileNpcs: 'Нет враждебных NPC',
recentEvents: 'Недавние события',
recentEventsDescription: 'Журнал последних дипломатических действий',
ago: 'назад',
notifications: '',
markAllRead: '',
noReports: '',
viewAll: '',
notifications: 'Дипломатические уведомления',
markAllRead: 'Отметить всё прочитанным',
noReports: 'Нет дипломатических событий',
viewAll: 'Показать все',
status: {
friendly: 'Дружественный',
neutral: 'Нейтральный',
@@ -1154,7 +1183,6 @@ export default {
},
note: 'Заметка',
notePlaceholder: 'Введите заметку...',
noteEmpty: 'Нет заметки',
lastEvent: 'Последнее событие',
reportDetails: 'Детали дипломатического отчёта',
eventDescription: 'Описание события',
@@ -1210,6 +1238,19 @@ export default {
npcEliminatedMessage: 'Вы уничтожили все планеты {npcName}! Эта фракция полностью уничтожена.'
},
searchPlaceholder: 'Поиск NPC по имени...',
notificationType: {
tradeOffer: 'Торговое предложение',
intelReport: 'Разведывательный отчёт',
jointAttack: 'Приглашение на совместную атаку'
},
notificationBadge: {
trade: 'Торговля',
intel: 'Разведка',
jointAttack: 'Приглашение'
},
notificationExtra: {
pending: 'Ожидание'
},
viewMode: {
card: 'Карточки',
list: 'Список'
@@ -1226,6 +1267,21 @@ export default {
medium: 'Средняя',
hard: 'Сложная'
},
aiType: 'Тип ИИ',
aiTypes: {
aggressive: 'Агрессивный',
defensive: 'Оборонительный',
trader: 'Торговец',
expansionist: 'Экспансионист',
balanced: 'Сбалансированный'
},
aiTypeDescriptions: {
aggressive: 'Активно шпионит и атакует, сильная месть',
defensive: 'Редко атакует, сильная месть при нападении',
trader: 'Почти не атакует, предпочитает торговлю и подарки',
expansionist: 'Фокус на развитии, менее агрессивен',
balanced: 'Динамически адаптирует стратегию к ситуации'
},
reputation: 'Репутация',
spyProbes: 'Шпионские зонды',
fleetPower: 'Мощь флота',
@@ -1252,27 +1308,12 @@ export default {
attackCooldown: 'Атака на перезарядке ({min}м {sec}с)',
notSpiedYet: 'Ещё не разведан, сначала нужна разведка'
}
},
aiType: 'Тип ИИ',
aiTypes: {
aggressive: 'Агрессивный',
defensive: 'Оборонительный',
trader: 'Торговец',
expansionist: 'Экспансионист',
balanced: 'Сбалансированный'
},
aiTypeDescriptions: {
aggressive: 'Активно шпионит и атакует, сильная месть',
defensive: 'Редко атакует, сильная месть при нападении',
trader: 'Почти не атакует, предпочитает торговлю и подарки',
expansionist: 'Фокус на развитии, менее агрессивен',
balanced: 'Динамически адаптирует стратегию к ситуации'
}
},
pagination: {
previous: 'Предыдущая',
next: 'Следующая',
gotIt: '',
gotIt: 'Понятно',
first: 'Первая',
last: 'Последняя',
page: 'Страница {page}'
@@ -1336,9 +1377,35 @@ export default {
seconds: 'секунд'
},
tutorial: {
progress: 'Прогресс',
previous: 'Назад',
next: 'Далее',
gotIt: 'Понятно',
completeButton: 'Завершить',
skip: 'Пропустить руководство',
welcome: {
title: 'Добро пожаловать в OGame',
content: 'Добро пожаловать, Командир! Давайте начнём с основ и построим вашу космическую империю.'
content:
'Добро пожаловать, Командир! Это руководство познакомит вас с основами построения империи. Нажмите "Далее", чтобы начать своё путешествие.'
},
resources: {
title: 'Обзор ресурсов',
content:
'Это ваши ресурсы: металл, кристалл и дейтерий. Они необходимы для строительства зданий и исследований. Энергия также важна для питания инфраструктуры.'
},
planet: {
title: 'Ваша планета',
content:
'Это ваша родная планета. Здесь вы можете увидеть название планеты, координаты и переключаться между планетами при расширении империи.'
},
navigation: {
title: 'Меню навигации',
content:
'Используйте это меню для перехода между разделами: здания, исследования, флот, галактика и другие. Каждый раздел предоставляет уникальные игровые функции.'
},
gotoBuildings: {
title: 'Перейти к зданиям',
content: 'Давайте начнём с постройки зданий. Нажмите на пункт меню "Здания", чтобы увидеть доступные здания.'
},
buildSolarPlant: {
title: 'Постройте солнечную электростанцию',
@@ -1350,16 +1417,119 @@ export default {
content:
'Ваше здание теперь в очереди строительства. Нажмите на значок очереди в правом верхнем углу, чтобы увидеть все текущие задачи строительства и исследований. Строительство занимает время, но вы можете продолжать работать во время ожидания.'
},
buildMetalMine: {
title: 'Постройте рудник металла',
content:
'Теперь, когда есть энергия, постройте рудник металла. Рудник металла — ваш основной источник металла, который используется почти в каждом здании и корабле.'
},
buildCrystalMine: {
title: 'Постройте рудник кристалла',
content:
'Кристалл более редкий, но важен для продвинутых технологий. Постройте рудник кристалла, чтобы начать сбор этого ценного ресурса.'
},
buildDeuterium: {
title: 'Постройте синтезатор дейтерия',
content:
'Дейтерий необходим для топлива кораблей и продвинутых исследований. Постройте синтезатор дейтерия, чтобы начать производство этого ключевого ресурса.'
},
upgradeMines: {
title: 'Улучшите ресурсные рудники',
content:
'Далее вам нужно улучшить все три ресурсных рудника (металл, кристалл, дейтерий) до уровня 2, чтобы соответствовать требованиям для фабрики роботов. Продолжайте улучшать их, когда накопите достаточно ресурсов.'
},
buildRobotics: {
title: 'Постройте фабрику роботов',
content:
'Фабрика роботов значительно ускоряет строительство. Она требует, чтобы рудники металла, кристалла и дейтерия были на уровне 2. Постройте её для повышения эффективности строительства!'
},
upgradeMinesForLab: {
title: 'Продолжайте улучшать рудники',
content:
'Теперь вам нужно улучшить все три ресурсных рудника до уровня 3, чтобы соответствовать требованиям исследовательской лаборатории. Продолжайте развивать производство ресурсов.'
},
buildResearchLab: {
title: 'Постройте исследовательскую лабораторию',
content:
'Исследовательская лаборатория — основа технологического прогресса. Она требует, чтобы все три ресурсных рудника были на уровне 3. Постройте её, чтобы разблокировать исследования!'
},
gotoResearch: {
title: 'Перейти к исследованиям',
content:
'Теперь, когда у вас есть исследовательская лаборатория, нажмите на пункт меню "Исследования", чтобы увидеть доступные технологии.'
},
researchEnergy: {
title: 'Исследуйте энергетическую технологию',
content:
'Энергетическая технология увеличивает производство энергии и открывает продвинутые здания. Это одна из самых базовых и важных технологий.'
},
shipyardIntro: {
title: 'Флот и верфь',
content:
'Корабли позволяют исследовать галактику, транспортировать ресурсы и защищать вашу империю. Для строительства кораблей вам нужна верфь (требуется фабрика роботов уровня 2).'
},
gotoBuildingsForShipyard: {
title: 'Вернитесь к зданиям',
content: 'Вернитесь на страницу зданий, чтобы построить верфь.'
},
buildShipyard: {
title: 'Постройте верфь',
content: 'Верфь позволяет строить корабли и оборонительные системы. Это критично для флотских операций.'
},
fleetIntro: {
title: 'Флотские операции',
content:
'Когда у вас появятся корабли, вы сможете отправлять их на миссии: транспортировать ресурсы, колонизировать планеты, атаковать врагов или исследовать поля обломков.'
},
galaxyIntro: {
title: 'Исследование галактики',
content:
'Вид галактики показывает другие планеты, поля обломков и возможности для расширения. Используйте его для разведки целей и планирования стратегии.'
},
complete: {
title: 'Руководство завершено!',
content:
'Поздравляем, Командир! Теперь вы знаете основы. Продолжайте строить империю, исследовать технологии и изучать галактику. Помните: сначала развивайте энергию, затем ресурсы, потом фабрики и исследования! Удачи!'
},
// Мобильное руководство
mobile: {
welcome: {
title: 'Добро пожаловать в OGame (Мобильная версия)',
content:
'Добро пожаловать, Командир! Это упрощённое руководство, разработанное для сенсорных экранов. Мы быстро рассмотрим основные функции, чтобы вы могли начать строить свою империю.'
},
resources: {
title: 'Верхняя панель ресурсов',
content:
'Вверху отображаются ваши ресурсы: металл, кристалл и дейтерий. Нажмите, чтобы увидеть подробную информацию о производстве.'
},
menu: {
title: 'Открыть меню навигации',
content:
'Нажмите на этот значок меню, чтобы открыть панель навигации, где можно получить доступ к зданиям, исследованиям, флоту и всем функциям.'
},
gotoBuildings: {
title: 'Перейти к зданиям',
content: 'Меню открыто! Теперь нажмите на опцию "Здания", чтобы начать строительство инфраструктуры.'
},
buildSolarPlant: {
title: 'Постройте солнечную электростанцию',
content:
'Сначала постройте солнечную электростанцию! Прокрутите вниз, чтобы найти её, и нажмите на карточку для строительства. Энергия — основа всего.'
},
waitBuild: {
title: 'Очередь строительства',
content:
'Нажмите на значок очереди в правом верхнем углу, чтобы увидеть прогресс строительства. Вы можете продолжать просматривать другие страницы - строительство происходит в фоновом режиме.'
},
buildMetalMine: {
title: 'Постройте рудник металла',
content:
'После получения энергии постройте рудник металла. Прокрутите вниз, чтобы найти рудник металла, и нажмите для строительства.'
},
complete: {
title: 'Быстрое руководство завершено!',
content:
'Отлично! Вы освоили базовые операции. Продолжайте строить рудники кристалла и синтезатор дейтерия, затем исследуйте другие функции. Помните: сначала энергия, затем ресурсы!'
}
}
},
@@ -1424,11 +1594,21 @@ export default {
title: 'Симулятор боя',
message: 'Симулируйте результаты боя перед атакой. Введите флоты и уровни технологий для прогноза победы, потерь и добычи.'
},
campaign: {
title: 'Режим кампании',
message:
'Исследуйте сюжетную кампанию галактики! Выполняйте миссии, чтобы получить ресурсные награды и открыть новые испытания. Каждый узел имеет уникальные цели и врагов.'
},
achievements: {
title: 'Система достижений',
message:
'Выполняйте игровые цели для разблокировки достижений и получения наград в виде тёмной материи! Достижения имеют несколько уровней - стремитесь к более высоким целям для лучших наград.'
},
ranking: {
title: 'Рейтинг',
message:
'Сравнивайте свой прогресс с другими игроками и NPC. Смотрите рейтинги по очкам за здания, исследования, флот и оборону. Стремитесь к вершине таблицы лидеров!'
},
settings: {
title: 'Настройки',
message: 'Управляйте игровыми данными, уведомлениями, импортом/экспортом сохранений. Регулярно создавайте резервные копии!'
@@ -1554,5 +1734,344 @@ export default {
you: 'Вы',
scoreBreakdown: 'Детали очков',
noData: 'Нет данных рейтинга'
},
// Уведомления о расширенном поведении НПС
npcBehavior: {
tradeOfferReceived: 'Торговое предложение получено',
tradeOfferDesc: '{npcName} отправил вам торговое предложение',
attitudeChanged: 'Изменение отношения НПС',
becameFriendly: '{npcName} стал к вам дружелюбным',
becameHostile: '{npcName} стал к вам враждебным',
intelReceived: 'Разведданные получены',
intelReceivedDesc: '{npcName} поделился информацией о враге',
jointAttackInvite: 'Приглашение к совместной атаке',
jointAttackInviteDesc: '{npcName} приглашает вас совместно атаковать врага',
aidReceived: 'Помощь получена',
aidReceivedDesc: '{npcName} отправил вам {amount} ресурсов',
allyDefense: 'Союзная оборона',
allyDefenseDesc: '{npcName} отправляет флот для защиты вашей планеты',
trade: {
title: 'Торговые предложения',
from: 'От',
offers: 'Предлагает',
requests: 'Запрашивает',
expiresIn: 'Истекает через',
expired: 'Истекло',
accept: 'Принять',
decline: 'Отклонить',
noOffers: 'Нет торговых предложений',
acceptSuccess: 'Сделка завершена!',
acceptFailed: 'Недостаточно ресурсов для сделки',
declined: 'Сделка отклонена',
ratio: 'Курс обмена'
},
intel: {
title: 'Разведывательные отчёты',
from: 'Источник',
target: 'Цель НПС',
type: 'Тип разведки',
types: {
enemyFleet: 'Данные о флоте',
enemyResources: 'Данные о ресурсах',
enemyMovement: 'Данные о передвижениях'
},
fleetInfo: 'Информация о флоте',
resourceInfo: 'Информация о ресурсах',
movementInfo: 'Информация о передвижениях',
noReports: 'Нет разведывательных отчётов',
markAsRead: 'Отметить как прочитанное',
content: 'Содержание разведки',
noFleet: 'Флот не обнаружен',
noData: 'Нет данных',
targetPosition: 'Целевые координаты',
missionType: 'Тип миссии'
},
jointAttack: {
title: 'Приглашения к совместной атаке',
from: 'Инициатор',
target: 'Цель НПС',
targetPlanet: 'Целевая планета',
npcFleet: 'Флот НПС',
lootShare: 'Доля добычи',
expiresIn: 'Истекает через',
expired: 'Истекло',
accept: 'Присоединиться к атаке',
decline: 'Отклонить',
noInvites: 'Нет приглашений к совместной атаке',
acceptSuccess: 'Вы присоединились к совместной атаке!',
declined: 'Приглашение отклонено',
targetInfo: 'Цель атаки',
expectedShare: 'Ожидаемая доля',
remaining: 'Оставшееся время'
},
aid: {
title: 'Ресурсная помощь',
from: 'Источник',
resources: 'Ресурсы помощи',
noAid: 'Нет записей о помощи'
},
attitudeChange: {
title: 'Изменения отношения',
npc: 'НПС',
previous: 'Было',
current: 'Сейчас',
reason: 'Причина',
reasons: {
naturalSwing: 'Естественное изменение',
giftReceived: 'Получен подарок',
attacked: 'Был атакован',
reputationThreshold: 'Порог репутации',
attitude_swing: 'Смена отношения',
gift: 'Получен подарок',
attack: 'Был атакован'
}
},
allyAction: {
title: 'Действия союзников',
defense: 'Оборонная поддержка',
defenseDesc: '{npcName} отправляет флот для защиты {targetPlanet}',
jointAttackStarted: 'Совместная атака началась',
jointAttackStartedDesc: 'Совместная атака на {targetNpc} началась',
reputationBonus: 'Бонус репутации',
reputationBonusDesc: 'Ваш союзник {npcName} хорошо отзывается о вас {targetNpc}'
}
},
campaign: {
name: 'Кампания',
description: 'Исследуйте загадочную галактику и раскройте древние тайны',
totalProgress: 'Общий прогресс',
questsCompleted: 'Квестов выполнено',
chapter: 'Глава',
branch: 'Ветвь',
startQuest: 'Начать квест',
claimRewards: 'Получить награды',
objectives: 'Цели',
objectivesLabel: 'Цели',
rewards: 'Награды',
completed: 'Завершено',
inProgress: 'В процессе',
available: 'Доступно',
locked: 'Заблокировано',
notifications: {
questStarted: 'Квест начат',
questCompleted: 'Квест завершён!',
rewardsClaimed: 'Награды получены',
objectiveCompleted: 'Цель достигнута',
chapterUnlocked: 'Новая глава разблокирована',
reputationUp: 'Репутация с {npcName} повысилась на {value}',
reputationDown: 'Репутация с {npcName} понизилась на {value}',
branchUnlocked: 'Новая сюжетная ветка разблокирована!'
},
dialogue: {
skip: 'Пропустить',
continue: 'Продолжить',
finish: 'Завершить',
player: 'Командир',
npc: 'NPC',
narrator: 'Рассказчик',
mysterious: 'Загадочный сигнал',
unknownSource: 'Неизвестный источник',
choiceEffect: 'Эффект выбора диалога'
},
chapters: {
'1': {
title: 'Начало',
description: 'Постройте свой дом и сделайте первый шаг в космос',
backgroundStory:
'Вы молодой космический командир, только что получивший свою первую планету. В этой необъятной вселенной вы будете строить свой дом, развивать технологии и исследовать глубины галактики...'
},
'2': {
title: 'Исследование',
description: 'Исследуйте вселенную и откройте древние руины',
backgroundStory:
'По мере роста вашей мощи загадочные сигналы из глубокого космоса привлекают ваше внимание. Эти сигналы, кажется, указывают на древнюю тайну, ожидающую смелых исследователей...'
},
'3': {
title: 'Дипломатия',
description: 'Установите связи с другими фракциями',
backgroundStory:
'Вы не одиноки в галактике. Другие цивилизации поднимаются. Вы должны решить, быть ли им врагом или союзником. Дипломатическая мудрость определит, как далеко зайдёт ваша империя...'
},
'4': {
title: 'Восходящая тень',
description: 'Противостойте могущественным врагам и защитите свою территорию',
backgroundStory:
'Опасность таится в тени. Могущественная враждебная сила нацелилась на вашу территорию. Война неизбежна. Вы должны подготовиться к надвигающейся буре...'
},
'5': {
title: 'Древние тайны',
description: 'Раскройте глубочайшие секреты галактики',
backgroundStory:
'Все улики указывают на самый загадочный регион галактики. Там скрыты конечные тайны, оставленные древними цивилизациями. Готовы ли вы раскрыть всё?'
}
},
quests: {
'1_1': { title: 'Строительство дома', description: 'Постройте инфраструктуру, чтобы заложить основу для вашей планеты' },
'1_2': { title: 'Техническое просвещение', description: 'Исследуйте базовые технологии, чтобы начать свой технологический путь' },
'1_3': { title: 'Первый корабль', description: 'Постройте свой первый боевой корабль' },
'1_4': { title: 'Странные соседи', description: 'Разведайте другие фракции в ближайших системах' },
'1_5': { title: 'Первый контакт', description: 'Установите начальный контакт с ближайшими NPC-фракциями' },
'2_1': { title: 'Пионерская колония', description: 'Колонизируйте свою первую новую планету' },
'2_2': { title: 'Экспедиция в глубокий космос', description: 'Отправьте флот в экспедиционные миссии' },
'2_3': { title: 'Загадочный сигнал', description: 'Исследуйте загадочные сигналы из глубокого космоса' },
'2_4': { title: 'Исследование руин', description: 'Исследуйте обнаруженные древние руины' },
'2_5': { title: 'Расшифровка архивов', description: 'Изучите данные, полученные из руин' },
'3_1': { title: 'Миротворец', description: 'Улучшите отношения с NPC через дипломатию' },
'3_2': { title: 'Торговые отношения', description: 'Установите стабильные отношения с дружественными фракциями' },
'3_3': { title: 'Общая угроза', description: 'Обнаружьте потенциальные враждебные силы' },
'3_4': { title: 'Переговоры об альянсе', description: 'Заключите официальный альянс с дружественными NPC' },
'3_5': { title: 'Подготовка к буре', description: 'Постройте оборону для подготовки к вызовам' },
'4_1': { title: 'Атака на форпост', description: 'Отразите первую атаку враждебных сил' },
'4_2': { title: 'Сбор разведданных', description: 'Разведайте военные позиции врага' },
'4_3': { title: 'Контратака', description: 'Начните контратаку против врага' },
'4_4': { title: 'Борьба за ресурсы', description: 'Переработайте обломки поля боя для ресурсов' },
'4_5': { title: 'Канун битвы', description: 'Постройте мощный флот для финальной битвы' },
'5_1': { title: 'Глубины руин', description: 'Исследуйте самые глубокие части руин' },
'5_2': { title: 'Древние технологии', description: 'Разблокируйте технологии древних цивилизаций' },
'5_3': { title: 'Финальное противостояние', description: 'Примите участие в финальной битве с загадочным врагом' },
'5_4': { title: 'Новая эра', description: 'Основывайте новые колонии и начните новую эпоху' },
'5_5': { title: 'Продолжение наследия', description: 'Продолжайте развитие и завоёвывайте больше систем' }
},
objectiveTypes: {
buildBuilding: 'Построить {building} до уровня {level}',
researchTech: 'Исследовать {tech} до уровня {level}',
produceShips: 'Произвести {count} {ship}',
accumulateResources: 'Накопить {amount} {resource}',
defeatNPC: 'Победить {npc}',
winBattles: 'Выиграть {count} сражений',
recycleDebris: 'Переработать {amount} обломков',
reachRelation: 'Достичь отношений {level} с {npc}',
sendGift: 'Отправить {count} подарков {npc}',
formAlliance: 'Заключить альянс с {npc}',
colonize: 'Колонизировать {count} планет',
expedition: 'Завершить {count} экспедиций',
spyTarget: 'Разведать {target}'
},
errors: {
questNotFound: 'Квест не найден',
questNotAvailable: 'Квест недоступен',
questNotActive: 'Квест не активен',
questNotCompleted: 'Квест не завершён',
rewardsAlreadyClaimed: 'Награды уже получены',
prerequisiteNotMet: 'Предварительный квест не завершён'
},
speakers: {
ancientVoice: 'Древний голос',
neighborNPC: 'Соседняя фракция',
mysteriousSignal: 'Загадочный сигнал',
enemyCommander: 'Вражеский командир'
},
objectiveDescriptions: {
buildMetalMine: 'Построить Металлургический завод до уровня 2',
buildCrystalMine: 'Построить Кристаллический завод до уровня 2',
buildSolarPlant: 'Построить Солнечную электростанцию до уровня 2',
buildResearchLab: 'Построить Исследовательскую лабораторию до уровня 1',
researchEnergy: 'Исследовать Энергетическую технологию до уровня 1',
buildShipyard: 'Построить Верфь до уровня 2',
researchCombustion: 'Исследовать Реактивный двигатель до уровня 1',
buildLightFighters: 'Построить 5 Лёгких истребителей',
researchEspionage: 'Исследовать Шпионаж до уровня 2',
buildSpyProbes: 'Построить 3 Шпионских зонда',
spyAnyNPC: 'Разведать планету NPC',
sendGiftToNPC: 'Отправить подарок NPC',
researchAstrophysics: 'Исследовать Астрофизику до уровня 1',
buildColonyShip: 'Построить Колониальный корабль',
colonizeNewPlanet: 'Колонизировать новую планету',
completeExpedition: 'Завершить экспедиционную миссию',
discoverRuins: 'Обнаружить древние руины',
researchComputer: 'Исследовать Компьютерную технологию до уровня 4',
improveRelation: 'Улучшить отношения с NPC',
reachFriendly: 'Достичь дружеского статуса с NPC',
spyHostileNPC: 'Разведать враждебного NPC',
formAlliance: 'Заключить альянс с дружественным NPC',
buildDefenses: 'Построить оборонительные сооружения',
winDefenseBattle: 'Выиграть оборонительное сражение',
spyEnemyPlanet: 'Разведать вражескую планету',
attackEnemy: 'Атаковать врага',
recycleDebris: 'Переработать обломки',
buildBattleships: 'Построить 10 Линкоров',
exploreDeepRuins: 'Исследовать глубокие руины',
researchHyperspace: 'Исследовать Гиперпространственную технологию',
defeatBoss: 'Победить загадочного врага',
colonizeSpecial: 'Колонизировать особое место',
continueDevelopment: 'Продолжить развитие'
},
dialogues: {
'1_1': {
prologue_1:
'Добро пожаловать в галактику, молодой командир. Эта необъятная вселенная ждёт вашего исследования. Сначала построим вашу родную планету.',
prologue_2: 'Я чувствую пробуждение нового сознания... Интересно... Посмотрим, как далеко ты зайдёшь...'
},
'1_2': {
prologue_1:
'Базовая инфраструктура завершена. Теперь время развивать технологии. Постройте Исследовательскую лабораторию и начните свой технологический путь.'
},
'1_3': {
prologue_1:
'С технологической поддержкой вы можете начать строить свой флот. Постройте Верфь и произведите свой первый боевой корабль.'
},
'1_4': {
prologue_1:
'Ваш флот обретает форму. Теперь давайте узнаем о вашем окружении. Отправьте шпионские зонды для разведки ближайших фракций.',
prologue_2: 'Ты не один... В этой галактике существуют другие цивилизации...'
},
'1_5': {
prologue_1: 'Вы обнаружили ближайшие фракции. Дипломатия — это искусство. Попробуйте установить с ними контакт.',
epilogue_1: 'Спасибо за ваш подарок, командир. Надеюсь, мы станем друзьями.',
epilogue_2: 'Хорошо... Установление связей — первый шаг к раскрытию более глубоких тайн...'
},
'2_1': {
prologue_1:
'Ваша сила установлена. Пора расширять территорию. Исследуйте Астрофизику, постройте колониальный корабль и исследуйте новые планеты.',
prologue_2: 'Вселенная бесконечна... Больше планет означает больше возможностей...'
},
'2_2': {
prologue_1: 'Колонизация успешна! Но в космосе ждут более глубокие тайны. Отправьте свой флот в экспедиционные миссии.',
prologue_2: 'Слабые сигналы издалека... Что-то ждёт тебя там...'
},
'2_3': {
prologue_1:
'Ваша экспедиция обнаружила аномальные сигналы. Эти сигналы, похоже, исходят от древней цивилизации... Исследуйте их источник.',
epilogue_1: 'Эти символы... Это руины древней цивилизации! Продолжайте исследование, чтобы раскрыть их тайны.'
},
'2_4': {
prologue_1: 'Вы нашли местоположение древних руин. Отправьте свой флот на исследование и посмотрите, что вы можете обнаружить.'
},
'2_5': { prologue_1: 'В руинах найдены архивы данных. Изучите эти данные, возможно, вы сможете разблокировать новую технологию.' },
'3_1': {
prologue_1: 'Во время исследований не забывайте о дипломатии. Поддержание хороших отношений с окружающими фракциями выгодно.'
},
'3_2': {
prologue_1: 'Некоторые фракции проявили дружелюбие. Продолжайте углублять отношения, возможно, вы получите больше поддержки.'
},
'3_3': {
prologue_1:
'Разведка показывает, что враждебные силы наблюдают за вами из тени. Оставайтесь бдительными и разведайте их перемещения.'
},
'3_4': { prologue_1: 'Заключите официальный альянс с дружественными фракциями для взаимной поддержки против угроз.' },
'3_5': { prologue_1: 'Угрозы приближаются. Постройте оборонительные сооружения и подготовьтесь к возможному конфликту.' },
'4_1': {
prologue_1: 'Враг начал атаку! Защитите свою планету!',
epilogue_1: 'Вы успешно отбили первую волну врага. Но это только начало...'
},
'4_2': { prologue_1: 'Враг отступил, но он вернётся. Разведайте их планеты, чтобы понять их силу.' },
'4_3': { prologue_1: 'Пора контратаковать. Атакуйте вражеские планеты и ослабьте их силы.' },
'4_4': { prologue_1: 'На поле боя осталось много обломков. Переработайте эти ресурсы для подготовки к следующей битве.' },
'4_5': { prologue_1: 'Финальная битва приближается. Постройте мощный флот и приготовьтесь к конечному испытанию.' },
'5_1': {
prologue_1: 'Все улики указывают на самую глубокую часть руин. Ключевые тайны древней цивилизации находятся там.',
prologue_2: 'Ты наконец прибыл... Истина скоро откроется...'
},
'5_2': { prologue_1: 'В глубинах руин вы обнаружили утерянные древние технологии. Исследуйте и разблокируйте их мощь.' },
'5_3': {
prologue_1: 'Появился загадочный враг. Это финальное испытание. Победите его!',
epilogue_1: 'Вы справились! Древний страж повержен. Тайны галактики теперь открыты для вас.'
},
'5_4': { prologue_1: 'Мир наконец наступил. В эту новую эру основывайте новые колонии и расширяйте свою империю.' },
'5_5': {
prologue_1: 'Ваша легенда только началась. Продолжайте исследовать и завоёвывать больше звёздных систем!',
epilogue_1: 'Галактика необъятна и бесконечна, бесчисленные тайны ждут вас...'
}
}
}
}

View File

@@ -11,28 +11,10 @@ export default {
common: {
confirm: '确认',
cancel: '取消',
delete: '删除',
edit: '编辑',
save: '保存',
close: '关闭',
back: '返回',
next: '下一步',
previous: '上一步',
submit: '提交',
reset: '重置',
search: '搜索',
filter: '筛选',
loading: '加载中...',
noData: '暂无数据',
error: '错误',
success: '成功',
warning: '警告',
info: '信息',
resourceType: '资源类型',
playerName: '指挥官',
timeHour: '时',
timeMinute: '分',
timeSecond: '秒',
featureLocked: '功能已锁定',
unlockRequired: '需要解锁前置建筑',
requiredBuilding: '所需建筑',
@@ -40,7 +22,6 @@ export default {
goToBuildings: '前往建筑页面',
locked: '已锁定',
viewRequirements: '查看前置条件',
requirements: '前置条件',
requirementsNotMet: '前置条件未满足',
current: '当前',
level: '等级',
@@ -50,7 +31,8 @@ export default {
viewDetails: '查看详情',
exitConfirmTitle: '退出游戏',
exitConfirmMessage: '确定要退出游戏吗?游戏进度会自动保存。',
points: '积分'
points: '积分',
retry: '重试'
},
errors: {
requirementsNotMet: '不满足前置条件',
@@ -94,7 +76,6 @@ export default {
gm: 'GM'
},
sidebar: {
language: '语言',
lightMode: '日间模式',
darkMode: '夜间模式',
collapse: '收起菜单',
@@ -119,9 +100,6 @@ export default {
},
energy: {
lowWarning: '电力不足,资源生产已停止!',
severeWarning: '电力不足,资源生产已停止!',
criticalWarning: '电力不足,资源生产已停止!',
noProduction: '电力不足,资源生产已停止!',
deficitDetail: '电力缺口: {deficit},请建造更多电站',
buildSolarPlant: '建造电站'
},
@@ -132,27 +110,20 @@ export default {
lowResources: '即将耗尽: {resources}'
},
planet: {
planet: '星球',
moon: '月球',
colony: '殖民地',
position: '位置',
coordinates: '坐标',
switchToMoon: '查看月球',
backToPlanet: '返回母星',
switchPlanet: '切换星球',
currentPlanet: '当前星球',
fields: '场地',
temperature: '温度',
homePlanet: '母星',
planetPrefix: '星球',
moonSuffix: '的月球',
colonyPrefix: '殖民地',
renamePlanet: '重命名星球',
renamePlanetTitle: '重命名星球',
newPlanetName: '新名称',
planetNamePlaceholder: '输入新的星球名称',
rename: '重命名',
renameSuccess: '星球已重命名为 {name}'
rename: '重命名'
},
player: {
points: '总积分'
@@ -179,6 +150,8 @@ export default {
sensorPhalanx: '传感器阵列',
jumpGate: '跳跃门',
planetDestroyerFactory: '行星毁灭者工厂',
geoResearchStation: '地质研究站',
deepDrillingFacility: '深层钻探设施',
buildTime: '建造时间',
production: '产量',
consumption: '消耗',
@@ -225,7 +198,9 @@ export default {
lunarBase: '增加月球可用空间,每级+30空间',
sensorPhalanx: '侦测周围星系的舰队活动',
jumpGate: '瞬间传送舰队到其他月球',
planetDestroyerFactory: '建造能够摧毁行星的终极武器'
planetDestroyerFactory: '建造能够摧毁行星的终极武器',
geoResearchStation: '研究地质结构提高矿脉自然恢复速度。每级增加50%恢复速率',
deepDrillingFacility: '深入地壳钻探解锁更深层的矿脉储量。每级增加20%矿脉上限'
},
ships: {
lightFighter: '轻型战斗机',
@@ -289,9 +264,6 @@ export default {
},
research: {
researchTime: '研究时间',
totalCost: '累积成本',
totalPoints: '累积积分',
levelRange: '等级范围',
attackBonus: '攻击加成',
shieldBonus: '护盾加成',
@@ -300,8 +272,7 @@ export default {
researchQueueBonus: '研究队列',
colonySlots: '殖民地槽位',
forAllPlanets: '(全局)',
speedBonus: '速度加成',
researchSpeedBonus: '研究速度加成'
speedBonus: '速度加成'
},
technologies: {
energyTechnology: '能源技术',
@@ -321,7 +292,8 @@ export default {
hyperspaceDrive: '超空间引擎',
darkMatterTechnology: '暗物质技术',
terraformingTechnology: '地形改造技术',
planetDestructionTech: '行星毁灭技术'
planetDestructionTech: '行星毁灭技术',
miningTechnology: '采矿技术'
},
technologyDescriptions: {
energyTechnology: '提高能源利用效率',
@@ -342,7 +314,8 @@ export default {
hyperspaceDrive: '高级推进技术',
darkMatterTechnology: '研究暗物质的性质和应用',
terraformingTechnology: '研究行星地形改造技术每级为所有行星增加30个可用空间',
planetDestructionTech: '研究如何摧毁整个行星的恐怖技术'
planetDestructionTech: '研究如何摧毁整个行星的恐怖技术',
miningTechnology: '改进采矿方法和设备提升所有星球矿脉储量上限。每级增加15%矿脉上限'
},
officers: {
commander: '指挥官',
@@ -397,14 +370,9 @@ export default {
movedToQueue: '任务已移至正式队列'
},
overview: {
title: '星球总览',
resourceOverview: '资源概览',
fleetInfo: '舰队',
currentShips: '当前星球的舰船数量',
productionSources: '资源获取来源',
productionSourcesDesc: '详细的资源产量及加成信息',
consumptionSources: '电力消耗来源',
consumptionSourcesDesc: '各建筑的电力消耗详情',
totalProduction: '总产量',
totalConsumption: '总消耗',
noConsumption: '当前无电力消耗',
@@ -414,12 +382,8 @@ export default {
},
buildingsView: {
title: '建筑',
usedSpace: '已用空间',
spaceUsage: '占用空间',
level: '等级',
upgradeCost: '升级消耗',
buildTime: '建造时间',
build: '建造',
upgrade: '升级',
maxLevelReached: '等级已满',
requirementsNotMet: '条件不足',
@@ -429,8 +393,7 @@ export default {
demolishRefund: '拆除返还',
demolishFailed: '拆除失败',
demolishFailedMessage: '无法拆除该建筑请检查建造队列是否已满或建筑等级是否为0。',
confirmDemolish: '确认拆除',
confirmDemolishMessage: '确定要拆除'
confirmDemolish: '确认拆除'
},
researchView: {
title: '研究',
@@ -441,24 +404,13 @@ export default {
researchFailedMessage: '请检查资源是否足够、前置条件是否满足,或是否有其他研究任务。'
},
shipyard: {
attack: '攻击力',
shield: '护盾',
armor: '装甲',
speed: '速度',
cargoCapacity: '载货量',
fuelConsumption: '燃料消耗',
buildCost: '建造成本',
buildTime: '建造时间',
perUnit: '每个单位',
batchCalculator: '批量建造计算器',
quantity: '数量',
totalCost: '总成本',
totalTime: '总时间'
fuelConsumption: '燃料消耗'
},
shipyardView: {
title: '船坞',
fleetStorage: '舰队仓储',
owned: '拥有',
attack: '攻击力',
shield: '护盾',
speed: '速度',
@@ -643,7 +595,6 @@ export default {
benefitsBonus: '效果加成',
resourceProduction: '资源产量',
darkMatterProduction: '暗物质产量',
energyProduction: '电量产出',
buildingSpeed: '建筑速度',
researchSpeed: '研究速度',
fleetSpeed: '舰队速度',
@@ -672,14 +623,11 @@ export default {
selectGalaxy: '选择银河系',
system: '星系',
selectSystem: '选择星系',
view: '查看',
myPlanet: '我的星球',
myPlanets: '查看我的星系',
npcPlanets: 'NPC星球',
selectPlanetToView: '选择星球以查看其所在星系',
totalPositions: '共10个星球位置',
mine: '我的',
hostile: '敌对',
emptySlot: '空位 - 可殖民',
scout: '侦察',
attack: '攻击',
@@ -704,9 +652,10 @@ export default {
missileCount: '导弹数量',
availableMissiles: '可用导弹',
missileRange: '射程',
systems: '系',
systems: '系',
distance: '距离',
flightTime: '飞行时间',
outOfRange: '超出射程',
launchMissile: '发射',
missileLaunched: '导弹已发射',
cancel: '取消',
@@ -719,17 +668,13 @@ export default {
phalanxScanTitle: '传感器阵列扫描',
phalanxScanDescription: '扫描星球 [{coordinates}] 的舰队活动',
phalanxNoMoon: '需要有传感器阵列的月球才能扫描',
phalanxOutOfRange: '目标超出扫描范围',
phalanxRange: '扫描范围',
phalanxCost: '扫描消耗',
phalanxNoFleets: '未检测到舰队活动',
phalanxFleetDetected: '检测到 {count} 支舰队',
phalanxMission: '任务',
phalanxOrigin: '出发地',
phalanxDestination: '目的地',
phalanxArrival: '到达时间',
phalanxReturn: '返回时间',
phalanxStatus: '状态',
phalanxStatusOutbound: '前往中',
phalanxStatusReturning: '返回中',
phalanxInsufficientDeuterium: '氘不足',
@@ -767,6 +712,8 @@ export default {
defense: '防御',
buildings: '建筑',
unread: '未读',
pending: '待处理',
invalidData: '无效数据',
targetPlanet: '目标星球',
attackerRemaining: '攻击方剩余',
defenderRemaining: '防守方剩余',
@@ -859,7 +806,11 @@ export default {
clearNPCActivity: 'NPC活动',
clearGiftNotifications: '礼物通知',
clearGiftRejected: '拒绝记录',
clearNow: '立即清空'
clearTradeOffers: '贸易提议',
clearIntelReports: '情报报告',
clearJointAttackInvites: '联合攻击邀请',
clearNow: '立即清空',
clearSuccess: '消息已清空'
},
missionReports: {
transportSuccess: '运输任务成功完成',
@@ -967,7 +918,6 @@ export default {
resume: '恢复',
gamePaused: '游戏已暂停',
gameResumed: '游戏已恢复',
playerName: '玩家名称',
gameSpeed: '资源产出速度',
gameSpeedDesc: '当前资源产出速度倍率',
speedChanged: '资源产出速度已更改为 {speed}x',
@@ -975,17 +925,14 @@ export default {
reset: '重置',
about: '关于',
version: '版本',
latestVersion: '最新版本',
checkUpdate: '检查更新',
checking: '检查中...',
newVersionAvailable: '发现新版本 {version}',
upToDate: '已是最新版本',
checkUpdateCooldown: '请稍后再试5分钟冷却时间',
checkUpdateFailed: '检查更新失败,请检查网络连接',
viewUpdate: '查看更新',
updateAvailable: '有新版本可用。点击查看更新内容。',
download: '下载',
goToDownload: '前往下载',
buildDate: '构建日期',
community: '社区',
github: 'GitHub 仓库',
@@ -1012,13 +959,53 @@ export default {
expandTypes: '展开详细设置',
collapseTypes: '收起详细设置',
// NPC名称更新
npcNameUpdate: 'NPC名称更新',
npcNameUpdateTitle: '检测到旧版NPC名称',
npcNameUpdateMessage: '发现 {count} 个NPC使用旧版名称格式。是否更新为新的本地化名称',
npcNameUpdateConfirm: '更新名称',
npcNameUpdateCancel: '保持原样',
npcNameUpdateSuccess: '已成功更新 {count} 个NPC名称',
npcNameUpdateSkipped: '已跳过NPC名称更新'
npcNameUpdateSkipped: '已跳过NPC名称更新',
// WebDAV
webdav: {
title: '云同步',
desc: '通过WebDAV同步游戏存档',
config: '配置',
configTitle: 'WebDAV 配置',
configDesc: '配置WebDAV服务器以启用云同步',
notConfigured: '请先配置WebDAV服务器',
serverUrl: '服务器地址',
serverUrlPlaceholder: '例如 https://dav.example.com',
serverUrlHint: '输入您的WebDAV服务器地址',
username: '用户名',
usernamePlaceholder: '输入用户名',
password: '密码',
passwordPlaceholder: '输入密码',
passwordHint: '密码仅存储在本地',
basePath: '存档路径',
basePathPlaceholder: '例如 /ogame-saves/',
testConnection: '测试连接',
testing: '测试中...',
testSuccess: '连接成功',
testFailed: '连接失败',
save: '保存',
clearConfig: '清除',
configSaved: '配置已保存',
configCleared: '配置已清除',
upload: '上传',
uploading: '上传中...',
uploadSuccess: '上传成功',
uploadFailed: '上传失败',
download: '下载',
downloadSuccess: '下载成功',
downloadFailed: '下载失败',
selectFile: '选择存档',
selectFileDesc: '选择要恢复的存档文件',
noFiles: '没有找到存档文件',
loadFailed: '加载文件列表失败',
confirmDelete: '确定要删除 "{name}" 吗?',
deleteSuccess: '文件已删除',
deleteFailed: '删除失败'
}
},
notifications: {
constructionComplete: '建造完成',
@@ -1140,8 +1127,6 @@ export default {
noFriendlyNpcs: '暂无友好NPC',
noNeutralNpcs: '暂无中立NPC',
noHostileNpcs: '暂无敌对NPC',
recentEvents: '最近事件',
recentEventsDescription: '最近的外交活动记录',
ago: '前',
notifications: '外交通知',
markAllRead: '全部已读',
@@ -1165,7 +1150,6 @@ export default {
},
note: '备注',
notePlaceholder: '输入备注...',
noteEmpty: '无备注',
lastEvent: '最近活动',
reportDetails: '外交报告详情',
eventDescription: '事件描述',
@@ -1220,10 +1204,23 @@ export default {
npcEliminatedMessage: '你消灭了{npcName}的所有星球!该势力已被彻底摧毁。'
},
searchPlaceholder: '搜索NPC名称...',
viewMode: {
card: '卡片',
list: '列表'
// 通知类型
notificationType: {
tradeOffer: '贸易提议',
intelReport: '情报报告',
jointAttack: '联合攻击邀请'
},
// 通知徽章
notificationBadge: {
trade: '贸易',
intel: '情报',
jointAttack: '邀请'
},
// 通知额外信息
notificationExtra: {
pending: '待处理'
},
diagnostic: {
button: 'NPC状态诊断',
title: 'NPC状态诊断',
@@ -1278,6 +1275,101 @@ export default {
}
}
},
// NPC增强行为通知
npcBehavior: {
// 中立NPC行为
tradeOfferReceived: '收到贸易提议',
tradeOfferDesc: '{npcName}向你发来了贸易提议',
attitudeChanged: 'NPC态度变化',
becameFriendly: '{npcName}对你的态度变得友好了',
becameHostile: '{npcName}对你的态度变得敌对了',
// 友好NPC行为
intelReceived: '收到情报',
intelReceivedDesc: '{npcName}向你分享了敌方情报',
jointAttackInvite: '联合攻击邀请',
jointAttackInviteDesc: '{npcName}邀请你一起攻击敌人',
aidReceived: '收到援助',
aidReceivedDesc: '{npcName}向你赠送了 {amount} 资源',
allyDefense: '盟友协防',
allyDefenseDesc: '{npcName}派遣舰队协助防御你的星球',
// 贸易相关
trade: {
title: '贸易提议',
from: '来自',
offers: '提供',
requests: '请求',
expiresIn: '剩余时间',
expired: '已过期',
accept: '接受',
decline: '拒绝',
noOffers: '暂无贸易提议',
acceptSuccess: '贸易完成!',
acceptFailed: '资源不足,无法完成贸易',
declined: '已拒绝贸易',
ratio: '交换比率'
},
// 情报相关
intel: {
title: '情报报告',
from: '情报来源',
target: '目标NPC',
type: '情报类型',
types: {
enemyFleet: '舰队情报',
enemyResources: '资源情报',
enemyMovement: '动向情报'
},
fleetInfo: '舰队信息',
resourceInfo: '资源信息',
movementInfo: '动向信息',
noReports: '暂无情报报告',
markAsRead: '标记已读',
content: '情报内容',
noFleet: '未探测到舰队',
noData: '暂无数据',
targetPosition: '目标坐标',
missionType: '任务类型'
},
// 联合攻击相关
jointAttack: {
title: '联合攻击邀请',
from: '发起者',
target: '目标NPC',
targetPlanet: '目标星球',
npcFleet: 'NPC出动舰队',
lootShare: '战利品分成',
expiresIn: '剩余时间',
expired: '已过期',
accept: '参与攻击',
decline: '婉拒',
noInvites: '暂无联合攻击邀请',
acceptSuccess: '已加入联合攻击!',
declined: '已婉拒邀请',
targetInfo: '攻击目标',
expectedShare: '预期分成',
remaining: '剩余时间'
},
// 援助相关
aid: {
title: '资源援助',
from: '援助来源',
resources: '援助资源',
noAid: '暂无援助记录'
},
// 态度变化相关
attitudeChange: {
title: '态度变化',
npc: 'NPC',
previous: '之前',
current: '现在',
reason: '原因',
reasons: {
attitude_swing: '态度摇摆',
gift: '收到礼物',
attack: '遭受攻击'
}
}
},
pagination: {
previous: '上一页',
next: '下一页',
@@ -1527,10 +1619,18 @@ export default {
title: '战斗模拟器',
message: '在发动攻击前模拟战斗结果。输入双方舰队和科技等级,预测胜负和损失。'
},
campaign: {
title: '战役模式',
message: '探索银河系的故事战役!完成任务获得资源奖励,解锁新的挑战。每个节点都有独特的目标和敌人。'
},
achievements: {
title: '成就系统',
message: '完成各类游戏目标解锁成就,获得暗物质奖励!成就分为多个等级,挑战更高难度获得更丰厚的奖励。'
},
ranking: {
title: '排行榜',
message: '与其他玩家和NPC比较进度。查看基于建筑、研究、舰队和防御积分的排名。努力攀登排行榜'
},
settings: {
title: '设置',
message: '在这里管理游戏数据、调整通知设置、导出/导入存档。记得定期备份你的进度!'
@@ -1666,7 +1766,6 @@ export default {
startQuest: '开始任务',
claimRewards: '领取奖励',
objectives: '任务目标',
objectivesLabel: '目标',
rewards: '任务奖励',
completed: '已完成',
inProgress: '进行中',
@@ -1677,7 +1776,10 @@ export default {
questCompleted: '任务完成!',
rewardsClaimed: '奖励已领取',
objectiveCompleted: '目标完成',
chapterUnlocked: '新章节已解锁'
chapterUnlocked: '新章节已解锁',
reputationUp: '与 {npcName} 的声望提升了 {value}',
reputationDown: '与 {npcName} 的声望降低了 {value}',
branchUnlocked: '新的故事分支已解锁!'
},
dialogue: {
skip: '跳过',
@@ -1687,18 +1789,21 @@ export default {
npc: 'NPC',
narrator: '旁白',
mysterious: '神秘信号',
unknownSource: '信号来源不明'
unknownSource: '信号来源不明',
choiceEffect: '对话选择效果'
},
chapters: {
'1': {
title: '起源之地',
description: '建设你的家园,迈出星际征途的第一步',
backgroundStory: '你是一位年轻的星际指挥官,刚刚获得了属于自己的第一颗星球。在这片陌生的宇宙中,你将建设家园,发展科技,探索未知的银河系深处...'
backgroundStory:
'你是一位年轻的星际指挥官,刚刚获得了属于自己的第一颗星球。在这片陌生的宇宙中,你将建设家园,发展科技,探索未知的银河系深处...'
},
'2': {
title: '星际探索',
description: '探索宇宙,发现神秘的古代遗迹',
backgroundStory: '随着你的势力不断壮大,来自深空的神秘信号引起了你的注意。这些信号似乎指向一个古老的秘密,等待着勇敢的探索者去揭开...'
backgroundStory:
'随着你的势力不断壮大,来自深空的神秘信号引起了你的注意。这些信号似乎指向一个古老的秘密,等待着勇敢的探索者去揭开...'
},
'3': {
title: '银河外交',

View File

@@ -11,29 +11,12 @@ export default {
common: {
confirm: '確認',
cancel: '取消',
delete: '刪除',
edit: '編輯',
save: '儲存',
close: '關閉',
back: '返回',
next: '下一步',
gotIt: '',
previous: '上一步',
submit: '提交',
reset: '重置',
search: '搜尋',
filter: '篩選',
loading: '載入中...',
noData: '暫無資料',
error: '錯誤',
gotIt: '知道了',
success: '成功',
warning: '警告',
info: '資訊',
resourceType: '資源類型',
playerName: '指揮官',
timeHour: '時',
timeMinute: '分',
timeSecond: '秒',
featureLocked: '功能已鎖定',
unlockRequired: '需要解鎖前置建築',
requiredBuilding: '所需建築',
@@ -41,7 +24,6 @@ export default {
goToBuildings: '前往建築頁面',
locked: '已鎖定',
viewRequirements: '查看前置條件',
requirements: '前置條件',
requirementsNotMet: '前置條件未滿足',
current: '當前',
level: '等級',
@@ -51,7 +33,8 @@ export default {
viewDetails: '查看詳情',
exitConfirmTitle: '退出遊戲',
exitConfirmMessage: '確定要退出遊戲嗎?遊戲進度會自動儲存。',
points: '積分'
points: '積分',
retry: '重試'
},
errors: {
requirementsNotMet: '不滿足前置條件',
@@ -88,6 +71,7 @@ export default {
galaxy: '星系',
diplomacy: '外交',
achievements: '成就',
campaign: '戰役',
ranking: '排行',
messages: '訊息',
settings: '設定',
@@ -95,7 +79,6 @@ export default {
gm: 'GM'
},
sidebar: {
language: '語言',
lightMode: '日間模式',
darkMode: '夜間模式',
collapse: '收起選單',
@@ -120,9 +103,6 @@ export default {
},
energy: {
lowWarning: '電力不足,資源生產已停止!',
severeWarning: '電力不足,資源生產已停止!',
criticalWarning: '電力不足,資源生產已停止!',
noProduction: '電力不足,資源生產已停止!',
deficitDetail: '電力缺口: {deficit},請建造更多電站',
buildSolarPlant: '建造電站'
},
@@ -133,27 +113,20 @@ export default {
lowResources: '即將耗盡: {resources}'
},
planet: {
planet: '星球',
moon: '月球',
colony: '殖民地',
position: '位置',
coordinates: '座標',
switchToMoon: '查看月球',
backToPlanet: '返回母星',
switchPlanet: '切換星球',
currentPlanet: '當前星球',
fields: '場地',
temperature: '溫度',
homePlanet: '母星',
planetPrefix: '星球',
moonSuffix: '的月球',
colonyPrefix: '殖民地',
renamePlanet: '重命名星球',
renamePlanetTitle: '重命名星球',
newPlanetName: '新名稱',
planetNamePlaceholder: '輸入新的星球名稱',
rename: '重命名',
renameSuccess: '星球已重命名為 {name}'
rename: '重命名'
},
player: {
points: '總積分'
@@ -180,8 +153,10 @@ export default {
sensorPhalanx: '傳感器陣列',
jumpGate: '跳躍門',
planetDestroyerFactory: '行星毀滅者工廠',
geoResearchStation: '地質研究站',
deepDrillingFacility: '深層鑽探設施',
buildTime: '建造時間',
build: '',
build: '建造',
production: '產量',
consumption: '消耗',
totalCost: '累積成本',
@@ -229,7 +204,9 @@ export default {
lunarBase: '增加月球可用空間,每級+30空間',
sensorPhalanx: '偵測周圍星系的艦隊活動',
jumpGate: '瞬間傳送艦隊到其他月球',
planetDestroyerFactory: '建造能夠摧毀行星的終極武器'
planetDestroyerFactory: '建造能夠摧毀行星的終極武器',
geoResearchStation: '研究地質結構提高礦脈自然恢復速度。每級增加50%恢復速率',
deepDrillingFacility: ''
},
ships: {
lightFighter: '輕型戰鬥機',
@@ -293,9 +270,6 @@ export default {
},
research: {
researchTime: '研究時間',
totalCost: '累積成本',
totalPoints: '累積積分',
levelRange: '等級範圍',
capacity: '容量/效果',
attackBonus: '攻擊加成',
shieldBonus: '護盾加成',
@@ -304,8 +278,7 @@ export default {
researchQueueBonus: '研究隊列',
colonySlots: '殖民地槽位',
forAllPlanets: '(全局)',
speedBonus: '速度加成',
researchSpeedBonus: '研究速度加成'
speedBonus: '速度加成'
},
technologies: {
energyTechnology: '能源技術',
@@ -325,7 +298,8 @@ export default {
hyperspaceDrive: '超空間引擎',
darkMatterTechnology: '暗物質技術',
terraformingTechnology: '地形改造技術',
planetDestructionTech: '行星毀滅技術'
planetDestructionTech: '行星毀滅技術',
miningTechnology: ''
},
technologyDescriptions: {
energyTechnology: '提高能源利用效率',
@@ -346,7 +320,8 @@ export default {
hyperspaceDrive: '高級推進技術',
darkMatterTechnology: '研究暗物質的性質和應用',
terraformingTechnology: '研究行星地形改造技術每級為所有行星增加30個可用空間',
planetDestructionTech: '研究如何摧毀整個行星的恐怖技術'
planetDestructionTech: '研究如何摧毀整個行星的恐怖技術',
miningTechnology: ''
},
officers: {
commander: '指揮官',
@@ -370,6 +345,9 @@ export default {
queue: {
title: '進行中的任務',
empty: '當前沒有進行中的隊列',
buildQueueBonus: '建造佇列',
spaceBonus: '空間加成',
researchQueueBonus: '研究佇列',
buildQueue: '建造佇列',
researchQueue: '研究佇列',
building: '建造中',
@@ -381,7 +359,8 @@ export default {
cancelResearch: '取消研究',
confirmCancel: '確定要取消嗎將返還50%的資源。',
level: '等級',
gmModeActivated: '',
quantity: '數量',
gmModeActivated: 'GM 模式已啟動!請查看導航選單。',
upgradeToLevel: '升級到等級',
tabs: {
all: '全部',
@@ -400,14 +379,9 @@ export default {
movedToQueue: '任務已移至正式佇列'
},
overview: {
title: '星球總覽',
resourceOverview: '資源概覽',
fleetInfo: '艦隊資訊',
currentShips: '當前星球的艦船數量',
productionSources: '生產來源',
productionSourcesDesc: '詳細資源生產和加成資訊',
consumptionSources: '電力消耗來源',
consumptionSourcesDesc: '各建築的電力消耗詳情',
totalProduction: '總產量',
totalConsumption: '總消耗',
noConsumption: '當前無電力消耗',
@@ -417,12 +391,8 @@ export default {
},
buildingsView: {
title: '建築',
usedSpace: '已用空間',
spaceUsage: '佔用空間',
level: '等級',
gmModeActivated: '',
upgradeCost: '升級消耗',
buildTime: '建造時間',
build: '建造',
upgrade: '升級',
maxLevelReached: '等級已滿',
@@ -433,8 +403,7 @@ export default {
demolishRefund: '拆除返還',
demolishFailed: '拆除失敗',
demolishFailedMessage: '無法拆除該建築請檢查建造隊列是否已滿或建築等級是否為0。',
confirmDemolish: '確認拆除',
confirmDemolishMessage: '確定要拆除以下建築嗎?'
confirmDemolish: '確認拆除'
},
researchView: {
title: '研究',
@@ -445,26 +414,16 @@ export default {
researchFailedMessage: '請檢查資源是否足夠、前置條件是否滿足,或是否有其他研究任務。'
},
shipyard: {
attack: '攻擊力',
missileAttack: '導彈攻擊',
shield: '護盾',
armor: '裝甲',
speed: '速度',
cargoCapacity: '載貨量',
fuelConsumption: '燃料消耗',
buildCost: '建造成本',
buildTime: '建造時間',
build: '',
perUnit: '每個單位',
batchCalculator: '批量建造計算器',
quantity: '數量',
totalCost: '總成本',
totalTime: '總時間'
build: '建造'
},
shipyardView: {
title: '船塢',
fleetStorage: '艦隊倉儲',
owned: '擁有',
attack: '攻擊力',
missileAttack: '導彈攻擊',
shield: '護盾',
@@ -486,7 +445,7 @@ export default {
armor: '裝甲',
buildCost: '建造成本',
buildTime: '建造時間',
build: '',
build: '建造',
perUnit: '每個單位',
batchCalculator: '批量建造計算器',
quantity: '數量',
@@ -539,6 +498,7 @@ export default {
missionInfo: '任務資訊',
fuelConsumption: '燃料消耗',
flightTime: '飛行時間',
outOfRange: '超出射程',
attackMission: '攻擊',
transport: '運輸',
colonize: '殖民',
@@ -569,6 +529,7 @@ export default {
}
},
recycle: '回收',
destroy: '行星毀滅',
transportResources: '運輸資源',
totalCargoCapacity: '總載貨量',
used: '已用',
@@ -580,11 +541,11 @@ export default {
arrivalTime: '到達時間',
returnTime: '返回時間',
recallFleet: '召回艦隊',
abortMission: '',
abortMissionTitle: '',
abortMissionWarning: '',
abortMissionSuccess: '',
abortMissionSuccessMessage: '',
abortMission: '終止任務',
abortMissionTitle: '確認終止任務',
abortMissionWarning: '警告:終止任務將永久損失 {ships} 艘艦船和 {resources} 資源!\n\n此操作不可撤銷艦隊和資源將不會返回。',
abortMissionSuccess: '任務已終止',
abortMissionSuccessMessage: '任務已終止,艦隊和資源已損失。',
sendFailed: '派遣失敗',
sendFailedMessage: '請檢查艦隊數量、燃料是否充足,或載貨量是否超出限制。',
recallFailed: '召回失敗',
@@ -682,14 +643,12 @@ export default {
selectGalaxy: '選擇銀河系',
system: '星系',
selectSystem: '選擇星系',
view: '查看',
myPlanet: '我的星球',
myPlanets: '查看我的星系',
npcPlanets: 'NPC星球',
selectPlanetToView: '選擇星球以查看其所在星系',
totalPositions: '共10個星球位置',
mine: '我的',
hostile: '敵對',
emptySlot: '空位 - 可殖民',
scout: '偵察',
attack: '攻擊',
@@ -714,7 +673,9 @@ export default {
systems: '星系',
distance: '距離',
flightTime: '飛行時間',
outOfRange: '超出射程',
launchMissile: '發射',
missileLaunched: '導彈已發射',
cancel: '取消',
colonizePlanetMessage: '確定要殖民位置 [{coordinates}] 嗎?\n\n請前往艦隊頁面派遣殖民船。',
recyclePlanetMessage: '確定要回收位置 [{coordinates}] 的殘骸嗎?\n\n請前往艦隊頁面派遣回收船。',
@@ -723,6 +684,25 @@ export default {
giftPlanetTitle: '贈送禮物',
giftPlanetMessage: '確定要向星球 [{coordinates}] 贈送資源嗎?\n\n請前往艦隊頁面選擇運輸船並裝載資源。',
npcPlanetName: '{name}的星球',
// 傳感器陣列掃描
phalanxScan: '傳感器掃描',
phalanxScanTitle: '傳感器陣列掃描',
phalanxScanDescription: '掃描星球 [{coordinates}] 的艦隊活動',
phalanxNoMoon: '需要有傳感器陣列的月球才能掃描',
phalanxOutOfRange: '目標超出掃描範圍',
phalanxRange: '掃描範圍',
phalanxCost: '掃描消耗',
phalanxNoFleets: '未檢測到艦隊活動',
phalanxFleetDetected: '檢測到 {count} 支艦隊',
phalanxMission: '任務',
phalanxOrigin: '出發地',
phalanxDestination: '目的地',
phalanxArrival: '到達時間',
phalanxReturn: '返回時間',
phalanxStatus: '狀態',
phalanxStatusOutbound: '前往中',
phalanxStatusReturning: '返回中',
phalanxInsufficientDeuterium: '氘不足',
intercepted: '被攔截',
defenseLosses: '防禦損失'
},
@@ -731,12 +711,12 @@ export default {
battles: '戰鬥',
spy: '偵查',
npc: 'NPC',
diplomacy: '',
diplomacy: '外交',
battleReports: '戰鬥報告',
spyReports: '間諜報告',
noBattleReports: '暫無戰鬥報告',
noSpyReports: '暫無間諜報告',
noDiplomaticReports: '',
noDiplomaticReports: '暫無外交事件',
battleReport: '戰鬥報告',
spyReport: '間諜報告',
victory: '勝利',
@@ -757,6 +737,8 @@ export default {
defense: '防禦',
buildings: '建築',
unread: '未讀',
pending: '待處理',
invalidData: '無效數據',
targetPlanet: '目標星球',
attackerRemaining: '攻擊方剩餘',
defenderRemaining: '防守方剩餘',
@@ -809,36 +791,36 @@ export default {
polite_decline: '對方禮貌地拒絕了'
},
// Spied notification dialog
spiedNotificationDetails: '',
spyDetected: '',
detectionResult: '',
detectionSuccess: '',
spiedNotificationMessage: '',
spiedNotificationTip: '',
viewInGalaxy: '',
spiedNotificationDetails: '被偵查通知詳情',
spyDetected: '偵查被發現',
detectionResult: '檢測結果',
detectionSuccess: '你發現了敵方偵查!',
spiedNotificationMessage: '{npc}試圖偵查你的星球{planet}',
spiedNotificationTip: '若該NPC對你有敵意請考慮增強防禦或反擊',
viewInGalaxy: '在星系中查看',
// Mission report dialog
missionReportDetails: '',
missionSuccess: '',
missionFailed: '',
origin: '',
destination: '',
missionDetails: '',
transportedResources: '',
recycledResources: '',
remainingDebris: '',
newPlanet: '',
missionReportDetails: '任務報告詳情',
missionSuccess: '成功',
missionFailed: '失敗',
origin: '起點',
destination: '終點',
missionDetails: '任務詳情',
transportedResources: '運輸資源',
recycledResources: '回收資源',
remainingDebris: '剩餘殘骸',
newPlanet: '新星球',
// NPC activity dialog
npcActivityDetails: '',
npcActivityDetails: 'NPC活動詳情',
activityType: {
recycle: ''
recycle: '回收殘骸'
},
activityLocation: '',
position: '',
nearPlanet: '',
activityDescription: '',
npcActivityMessage: '',
arrivalTime: '',
npcActivityTip: '',
activityLocation: '活動位置',
position: '位置',
nearPlanet: '附近星球',
activityDescription: '活動描述',
npcActivityMessage: '{npc}正在{position}{activity}',
arrivalTime: '到達時間',
npcActivityTip: 'NPC可能會收集戰鬥產生的殘骸。若想競爭資源可嘗試先到達該位置',
clearMessages: '清空訊息',
clearMessageTypes: '選擇要清空的訊息類型',
clearBattleReports: '戰鬥報告',
@@ -848,7 +830,11 @@ export default {
clearNPCActivity: 'NPC活動',
clearGiftNotifications: '禮物通知',
clearGiftRejected: '拒絕記錄',
clearNow: '立即清空'
clearTradeOffers: '貿易提議',
clearIntelReports: '情報報告',
clearJointAttackInvites: '聯合攻擊邀請',
clearNow: '立即清空',
clearSuccess: '訊息已清空'
},
missionReports: {
transportSuccess: '運輸任務成功完成',
@@ -980,6 +966,10 @@ export default {
github: 'GitHub 儲存庫',
qqGroup: 'QQ 交流群',
privacyPolicy: '隱私協議',
displaySettings: '顯示設定',
displaySettingsDesc: '調整遊戲的視覺效果',
backgroundAnimation: '背景動畫',
backgroundAnimationDesc: '開啟後顯示星空/粒子背景動畫(可能影響效能)',
notifications: '通知設定',
notificationsDesc: '管理遊戲內的通知提醒',
notificationTypes: '通知類型',
@@ -1003,7 +993,48 @@ export default {
npcNameUpdateConfirm: '更新名稱',
npcNameUpdateCancel: '保持原樣',
npcNameUpdateSuccess: '已成功更新 {count} 個NPC名稱',
npcNameUpdateSkipped: '已跳過NPC名稱更新'
npcNameUpdateSkipped: '已跳過NPC名稱更新',
// WebDAV
webdav: {
title: '雲端同步',
desc: '透過WebDAV同步遊戲存檔',
config: '設定',
configTitle: 'WebDAV 設定',
configDesc: '設定WebDAV伺服器以啟用雲端同步',
notConfigured: '請先設定WebDAV伺服器',
serverUrl: '伺服器位址',
serverUrlPlaceholder: '例如 https://dav.example.com',
serverUrlHint: '輸入您的WebDAV伺服器位址',
username: '使用者名稱',
usernamePlaceholder: '輸入使用者名稱',
password: '密碼',
passwordPlaceholder: '輸入密碼',
passwordHint: '密碼僅儲存在本機',
basePath: '存檔路徑',
basePathPlaceholder: '例如 /ogame-saves/',
testConnection: '測試連線',
testing: '測試中...',
testSuccess: '連線成功',
testFailed: '連線失敗',
save: '儲存',
clearConfig: '清除',
configSaved: '設定已儲存',
configCleared: '設定已清除',
upload: '上傳',
uploading: '上傳中...',
uploadSuccess: '上傳成功',
uploadFailed: '上傳失敗',
download: '下載',
downloadSuccess: '下載成功',
downloadFailed: '下載失敗',
selectFile: '選擇存檔',
selectFileDesc: '選擇要還原的存檔檔案',
noFiles: '沒有找到存檔檔案',
loadFailed: '載入檔案清單失敗',
confirmDelete: '確定要刪除 "{name}" 嗎?',
deleteSuccess: '檔案已刪除',
deleteFailed: '刪除失敗'
}
},
notifications: {
constructionComplete: '建造完成',
@@ -1025,8 +1056,8 @@ export default {
officers: '軍官',
modifyResources: '修改資源',
resourcesDesc: '快速修改星球資源數量',
maxAllResources: '',
maxAllResourcesSuccess: '',
maxAllResources: '一鍵拉滿',
maxAllResourcesSuccess: '所有資源已拉滿',
modifyBuildings: '修改建築',
buildingsDesc: '快速設定建築等級',
modifyResearch: '修改科技',
@@ -1067,10 +1098,11 @@ export default {
dangerZoneDesc: '以下操作不可撤銷,請謹慎操作',
resetGame: '重置遊戲',
resetGameConfirm: '確定要重置遊戲嗎?這將刪除所有資料!',
completeAllQueues: '',
completeAllQueuesDesc: '',
completeQueues: '',
completeQueuesSuccess: ''
completeAllQueues: '一鍵完成所有佇列',
completeAllQueuesDesc: '立即完成所有建築、科技、艦船、防禦佇列和飛行任務',
completeQueues: '完成佇列',
completeQueuesSuccess:
'已完成 {buildingCount} 個建築佇列、{researchCount} 個科技佇列、{missionCount} 個飛行任務、{missileCount} 個導彈任務'
},
alerts: {
incomingFleets: '{count}支敵方艦隊來襲',
@@ -1127,10 +1159,10 @@ export default {
recentEvents: '最近事件',
recentEventsDescription: '最近的外交活動記錄',
ago: '前',
notifications: '',
markAllRead: '',
noReports: '',
viewAll: '',
notifications: '外交通知',
markAllRead: '全部已讀',
noReports: '暫無外交事件',
viewAll: '查看全部',
status: {
friendly: '友好',
neutral: '中立',
@@ -1205,6 +1237,19 @@ export default {
npcEliminatedMessage: '你消滅了{npcName}的所有星球!該勢力已被徹底摧毀。'
},
searchPlaceholder: '搜索NPC名稱...',
notificationType: {
tradeOffer: '貿易提議',
intelReport: '情報報告',
jointAttack: '聯合攻擊邀請'
},
notificationBadge: {
trade: '貿易',
intel: '情報',
jointAttack: '邀請'
},
notificationExtra: {
pending: '待處理'
},
viewMode: {
card: '卡片',
list: '列表'
@@ -1220,6 +1265,21 @@ export default {
medium: '普通',
hard: '困難'
},
aiType: 'AI類型',
aiTypes: {
aggressive: '侵略型',
defensive: '防守型',
trader: '商人型',
expansionist: '擴張型',
balanced: '平衡型'
},
aiTypeDescriptions: {
aggressive: '積極偵查和攻擊,反擊猛烈',
defensive: '很少主動攻擊,被攻擊後強烈反擊',
trader: '幾乎不攻擊,更願意交易和送禮',
expansionist: '專注發展,較少攻擊',
balanced: '根據情況動態調整策略'
},
reputation: '好感度',
spyProbes: '偵察機數量',
fleetPower: '艦隊戰力',
@@ -1246,27 +1306,12 @@ export default {
attackCooldown: '攻擊冷卻中({min}分{sec}秒)',
notSpiedYet: '尚未偵查過,需要先進行偵查'
}
},
aiType: 'AI類型',
aiTypes: {
aggressive: '侵略型',
defensive: '防守型',
trader: '商人型',
expansionist: '擴張型',
balanced: '平衡型'
},
aiTypeDescriptions: {
aggressive: '積極偵查和攻擊,反擊猛烈',
defensive: '很少主動攻擊,被攻擊後強烈反擊',
trader: '幾乎不攻擊,更願意交易和送禮',
expansionist: '專注發展,較少攻擊',
balanced: '根據情況動態調整策略'
}
},
pagination: {
previous: '上一頁',
next: '下一頁',
gotIt: '',
gotIt: '知道了',
first: '首頁',
last: '末頁',
page: '第 {page} 頁'
@@ -1329,9 +1374,31 @@ export default {
seconds: '秒'
},
tutorial: {
progress: '進度',
previous: '上一步',
next: '下一步',
gotIt: '我知道了',
completeButton: '完成',
skip: '跳過引導',
welcome: {
title: '歡迎來到 OGame',
content: '歡迎,指揮官!讓我們從基礎開始,建立您的宇宙帝國。'
content: '歡迎,指揮官!本教學將引導您了解建立帝國的基礎知識。點擊「下一步」開始您的征程。'
},
resources: {
title: '資源概覽',
content: '這些是您的資源:金屬、晶體和重氫。它們是建造建築和研究科技的必需品。能量也很重要,用於為您的基礎設施供電。'
},
planet: {
title: '您的星球',
content: '這是您的母星。您可以在這裡查看星球名稱、座標,並在擴張帝國時切換星球。'
},
navigation: {
title: '導航選單',
content: '使用此選單在不同部分之間導航:建築、研究、艦隊、星系等。每個部分都提供獨特的遊戲功能。'
},
gotoBuildings: {
title: '前往建築頁面',
content: '讓我們從建造一些建築開始。點擊「建築」選單項查看可用建築。'
},
buildSolarPlant: {
title: '建造太陽能電站',
@@ -1342,14 +1409,100 @@ export default {
content:
'您的建築現在在建造佇列中。點擊右上角的佇列圖示可以查看所有正在進行的建造和研究任務。建築需要時間完成,但您可以在等待時繼續操作。'
},
buildMetalMine: {
title: '建造金屬礦',
content: '現在有了能量,可以建造金屬礦了。金屬礦是您的主要金屬來源,金屬幾乎用於每個建築和艦船。'
},
buildCrystalMine: {
title: '建造晶體礦',
content: '晶體更稀有但對高級科技至關重要。建造晶體礦開始收集這種寶貴的資源。'
},
buildDeuterium: {
title: '建造重氫合成器',
content: '重氫是艦船燃料和高級研究的必需品。建造重氫合成器開始生產這種關鍵資源。'
},
upgradeMines: {
title: '升級資源礦',
content: '接下來您需要升級三種資源礦金屬、晶體、重氫到2級以滿足建造機器人工廠的要求。資源充足後繼續升級它們。'
},
buildRobotics: {
title: '建造機器人工廠',
content: '機器人工廠可以大幅加快建造速度。它需要金屬礦、晶體礦和重氫合成器各達到2級。建造它來提升建造效率'
},
upgradeMinesForLab: {
title: '繼續升級資源礦',
content: '現在需要將三種資源礦升級到3級以滿足研究實驗室的建造要求。繼續發展您的資源產能。'
},
buildResearchLab: {
title: '建造研究實驗室',
content: '研究實驗室是技術進步的基礎。它需要三種資源礦各達到3級。建造它以解鎖科技研究'
},
gotoResearch: {
title: '前往研究頁面',
content: '既然您有了研究實驗室,點擊「研究」選單查看可用的科技。'
},
researchEnergy: {
title: '研究能量科技',
content: '能量科技可以提高您的能量產出並解鎖高級建築。這是最基礎也是最重要的科技之一。'
},
shipyardIntro: {
title: '艦隊與船塢',
content: '艦船讓您能夠探索星系、運輸資源並保衛您的帝國。要建造艦船您需要船塢需要機器人工廠2級。'
},
gotoBuildingsForShipyard: {
title: '返回建築頁面',
content: '返回建築頁面來建造您的船塢。'
},
buildShipyard: {
title: '建造船塢',
content: '船塢允許您建造艦船和防禦系統。這對艦隊行動至關重要。'
},
fleetIntro: {
title: '艦隊行動',
content: '一旦您擁有艦船,就可以派遣它們執行任務:運輸資源、殖民星球、攻擊敵人或探索廢墟場。'
},
galaxyIntro: {
title: '探索星系',
content: '星系視圖顯示其他星球、廢墟場和擴張機會。使用它來偵察目標並規劃您的戰略。'
},
complete: {
title: '教學完成!',
content:
'恭喜,指揮官!您現在了解了基礎知識。繼續建設您的帝國,研究科技,探索星系。記住:先發展能量,再建資源,然後是工廠和研究!祝您好運!'
},
// 移動端教學
mobile: {
welcome: {
title: '歡迎來到 OGame移動版',
content: '歡迎,指揮官!這是專為觸控螢幕設計的簡化教。我們將快速介紹核心功能,讓您開始建設帝國。'
content: '歡迎,指揮官!這是專為觸控螢幕設計的簡化教。我們將快速介紹核心功能,讓您開始建設帝國。'
},
resources: {
title: '頂部資源欄',
content: '頂部顯示您的資源:金屬、晶體和重氫。點擊可查看詳細生產資訊。'
},
menu: {
title: '打開導航選單',
content: '點擊這個選單圖示打開導航欄,您可以存取建築、研究、艦隊等所有功能。'
},
gotoBuildings: {
title: '前往建築頁面',
content: '選單已打開!現在點擊「建築」選項,開始建造基礎設施。'
},
buildSolarPlant: {
title: '建造太陽能電站',
content: '首先建造太陽能電站!向下捲動找到它,點擊卡片進行建造。能量是一切的基礎。'
},
waitBuild: {
title: '建造佇列',
content: '點擊右上角的佇列圖示可以查看建造進度。您可以繼續瀏覽其他頁面,建造會在背景進行。'
},
buildMetalMine: {
title: '建造金屬礦',
content: '有了能量後,建造金屬礦。向下捲動找到金屬礦,點擊建造。'
},
complete: {
title: '快速教學完成!',
content: '很好!您已經掌握了基礎操作。繼續建造晶體礦和重氫合成器,然後探索其他功能。記住:先能量,再資源!'
}
}
},
@@ -1405,10 +1558,18 @@ export default {
title: '戰鬥模擬器',
message: '在發動攻擊前模擬戰鬥結果。輸入雙方艦隊和科技等級,預測勝負和損失。'
},
campaign: {
title: '戰役模式',
message: '探索銀河系的故事戰役!完成任務獲得資源獎勵,解鎖新的挑戰。每個節點都有獨特的目標和敵人。'
},
achievements: {
title: '成就系統',
message: '完成各類遊戲目標解鎖成就,獲得暗物質獎勵!成就分為多個等級,挑戰更高難度獲得更豐厚的獎勵。'
},
ranking: {
title: '排行榜',
message: '與其他玩家和NPC比較進度。查看基於建築、研究、艦隊和防禦積分的排名。努力攀登排行榜'
},
settings: {
title: '設置',
message: '在這裡管理遊戲數據、調整通知設置、導出/導入存檔。記得定期備份你的進度!'
@@ -1670,5 +1831,427 @@ export default {
you: '你',
scoreBreakdown: '積分詳情',
noData: '暫無排行數據'
},
// NPC增強行為通知
npcBehavior: {
tradeOfferReceived: '收到貿易提議',
tradeOfferDesc: '{npcName}向你發來了貿易提議',
attitudeChanged: 'NPC態度變化',
becameFriendly: '{npcName}對你的態度變得友好了',
becameHostile: '{npcName}對你的態度變得敵對了',
intelReceived: '收到情報',
intelReceivedDesc: '{npcName}向你分享了敵方情報',
jointAttackInvite: '聯合攻擊邀請',
jointAttackInviteDesc: '{npcName}邀請你一起攻擊敵人',
aidReceived: '收到援助',
aidReceivedDesc: '{npcName}向你贈送了 {amount} 資源',
allyDefense: '盟友協防',
allyDefenseDesc: '{npcName}派遣艦隊協助防禦你的星球',
trade: {
title: '貿易提議',
from: '來自',
offers: '提供',
requests: '請求',
expiresIn: '剩餘時間',
expired: '已過期',
accept: '接受',
decline: '拒絕',
noOffers: '暫無貿易提議',
acceptSuccess: '貿易完成!',
acceptFailed: '資源不足,無法完成貿易',
declined: '已拒絕貿易',
ratio: '交換比率'
},
intel: {
title: '情報報告',
from: '情報來源',
target: '目標NPC',
type: '情報類型',
types: {
enemyFleet: '艦隊情報',
enemyResources: '資源情報',
enemyMovement: '動向情報'
},
fleetInfo: '艦隊資訊',
resourceInfo: '資源資訊',
movementInfo: '動向資訊',
noReports: '暫無情報報告',
markAsRead: '標記已讀',
content: '情報內容',
noFleet: '未偵測到艦隊',
noData: '暫無資料',
targetPosition: '目標座標',
missionType: '任務類型'
},
jointAttack: {
title: '聯合攻擊邀請',
from: '發起者',
target: '目標NPC',
targetPlanet: '目標星球',
npcFleet: 'NPC出動艦隊',
lootShare: '戰利品分成',
expiresIn: '剩餘時間',
expired: '已過期',
accept: '參與攻擊',
decline: '婉拒',
noInvites: '暫無聯合攻擊邀請',
acceptSuccess: '已加入聯合攻擊!',
declined: '已婉拒邀請',
targetInfo: '攻擊目標',
expectedShare: '預期分成',
remaining: '剩餘時間'
},
aid: {
title: '資源援助',
from: '援助來源',
resources: '援助資源',
noAid: '暫無援助記錄'
},
attitudeChange: {
title: '態度變化',
npc: 'NPC',
previous: '之前',
current: '現在',
reason: '原因',
reasons: {
attitude_swing: '態度搖擺',
gift: '收到禮物',
attack: '遭受攻擊',
naturalSwing: '自然變動',
giftReceived: '收到禮物',
attacked: '被攻擊',
reputationThreshold: '聲望閾值'
}
},
allyAction: {
title: '盟友行動',
defense: '防禦支援',
defenseDesc: '{npcName}派遣艦隊協助防禦{targetPlanet}',
jointAttackStarted: '聯合攻擊開始',
jointAttackStartedDesc: '對{targetNpc}的聯合攻擊已開始',
reputationBonus: '聲望加成',
reputationBonusDesc: '你的盟友{npcName}向{targetNpc}說了你的好話'
}
},
campaign: {
name: '戰役',
description: '探索神秘的銀河系,揭開古代文明的秘密',
totalProgress: '總進度',
questsCompleted: '任務完成',
chapter: '章節',
branch: '分支',
startQuest: '開始任務',
claimRewards: '領取獎勵',
objectives: '任務目標',
objectivesLabel: '目標',
rewards: '任務獎勵',
completed: '已完成',
inProgress: '進行中',
available: '可接取',
locked: '未解鎖',
notifications: {
questStarted: '任務已開始',
questCompleted: '任務完成!',
rewardsClaimed: '獎勵已領取',
objectiveCompleted: '目標完成',
chapterUnlocked: '新章節已解鎖',
reputationUp: '與 {npcName} 的聲望提升了 {value}',
reputationDown: '與 {npcName} 的聲望降低了 {value}',
branchUnlocked: '新的故事分支已解鎖!'
},
dialogue: {
skip: '跳過',
continue: '繼續',
finish: '完成',
player: '指揮官',
npc: 'NPC',
narrator: '旁白',
mysterious: '神秘信號',
unknownSource: '信號來源不明',
choiceEffect: '對話選擇效果'
},
chapters: {
'1': {
title: '起源之地',
description: '建設你的家園,邁出星際征途的第一步',
backgroundStory:
'你是一位年輕的星際指揮官,剛剛獲得了屬於自己的第一顆星球。在這片陌生的宇宙中,你將建設家園,發展科技,探索未知的銀河系深處...'
},
'2': {
title: '星際探索',
description: '探索宇宙,發現神秘的古代遺跡',
backgroundStory:
'隨著你的勢力不斷壯大,來自深空的神秘信號引起了你的注意。這些信號似乎指向一個古老的秘密,等待著勇敢的探索者去揭開...'
},
'3': {
title: '銀河外交',
description: '與其他勢力建立聯繫,在星際政治中立足',
backgroundStory: '銀河系中並不只有你一方勢力。其他文明正在崛起,你需要決定是與他們為敵還是結盟。外交的智慧將決定你的帝國能走多遠...'
},
'4': {
title: '暗影降臨',
description: '面對強大的敵人,保衛你的領地',
backgroundStory: '暗影中潛伏著危險。一個強大的敵對勢力已經盯上了你的領地。戰爭不可避免,你必須做好準備,迎接即將到來的風暴...'
},
'5': {
title: '古代秘密',
description: '揭開銀河系最深處的秘密',
backgroundStory: '所有的線索都指向銀河系最神秘的區域。在那裡,隱藏著古代文明留下的終極秘密。你準備好揭開這一切了嗎?'
}
},
quests: {
'1_1': {
title: '家園建設',
description: '建造基礎設施,為你的星球奠定發展基礎'
},
'1_2': {
title: '科技啟蒙',
description: '研究基礎科技,開啟科技發展之路'
},
'1_3': {
title: '第一艘船',
description: '建造你的第一艘戰艦'
},
'1_4': {
title: '陌生鄰居',
description: '偵查附近星系中的其他勢力'
},
'1_5': {
title: '初次接觸',
description: '與附近的NPC勢力建立初步聯繫'
},
'2_1': {
title: '殖民先驅',
description: '殖民你的第一顆新星球'
},
'2_2': {
title: '深空探險',
description: '派遣艦隊進行遠征探險'
},
'2_3': {
title: '神秘信號',
description: '調查來自深空的神秘信號'
},
'2_4': {
title: '遺跡調查',
description: '探索發現的古代遺跡'
},
'2_5': {
title: '解密檔案',
description: '研究從遺跡中獲得的數據'
},
'3_1': {
title: '和平使者',
description: '通過外交手段提升與NPC的關係'
},
'3_2': {
title: '利益交換',
description: '與友好勢力建立穩定的關係'
},
'3_3': {
title: '共同威脅',
description: '發現潛在的敵對勢力'
},
'3_4': {
title: '聯盟談判',
description: '與友好NPC建立正式同盟'
},
'3_5': {
title: '備戰風暴',
description: '建設防禦設施,準備迎接挑戰'
},
'4_1': {
title: '前哨遭襲',
description: '抵禦敵對勢力的首次進攻'
},
'4_2': {
title: '情報收集',
description: '偵查敵方的軍事部署'
},
'4_3': {
title: '反擊行動',
description: '對敵方發起反擊'
},
'4_4': {
title: '資源爭奪',
description: '回收戰場殘骸,獲取資源'
},
'4_5': {
title: '決戰前夕',
description: '建造強大的艦隊,準備最終決戰'
},
'5_1': {
title: '遺跡深處',
description: '探索遺跡的最深處'
},
'5_2': {
title: '古代科技',
description: '解鎖古代文明的科技'
},
'5_3': {
title: '最終對決',
description: '與神秘敵人進行最終決戰'
},
'5_4': {
title: '新紀元',
description: '建立新的殖民地,開啟新時代'
},
'5_5': {
title: '傳承延續',
description: '繼續發展,征服更多的星系'
}
},
objectiveTypes: {
buildBuilding: '建造 {building} 到 {level} 級',
researchTech: '研究 {tech} 到 {level} 級',
produceShips: '生產 {count} 艘 {ship}',
accumulateResources: '積累 {amount} {resource}',
defeatNPC: '擊敗 {npc}',
winBattles: '贏得 {count} 場戰鬥',
recycleDebris: '回收 {amount} 殘骸',
reachRelation: '與 {npc} 達到 {level} 關係',
sendGift: '向 {npc} 送禮 {count} 次',
formAlliance: '與 {npc} 結盟',
colonize: '殖民 {count} 顆星球',
expedition: '完成 {count} 次探險',
spyTarget: '偵查 {target}'
},
errors: {
questNotFound: '任務不存在',
questNotAvailable: '任務不可接取',
questNotActive: '任務未激活',
questNotCompleted: '任務未完成',
rewardsAlreadyClaimed: '獎勵已領取',
prerequisiteNotMet: '前置任務未完成'
},
speakers: {
ancientVoice: '古代之聲',
neighborNPC: '鄰近勢力',
mysteriousSignal: '神秘信號',
enemyCommander: '敵方指揮官'
},
objectiveDescriptions: {
buildMetalMine: '建造金屬礦到2級',
buildCrystalMine: '建造晶體礦到2級',
buildSolarPlant: '建造太陽能電站到2級',
buildResearchLab: '建造研究實驗室到1級',
researchEnergy: '研究能量技術到1級',
buildShipyard: '建造船塢到2級',
researchCombustion: '研究燃燒驅動到1級',
buildLightFighters: '建造5艘輕型戰鬥機',
researchEspionage: '研究間諜技術到2級',
buildSpyProbes: '建造3艘間諜探測器',
spyAnyNPC: '偵查任意NPC星球',
sendGiftToNPC: '向任意NPC送禮',
researchAstrophysics: '研究天體物理學到1級',
buildColonyShip: '建造殖民船',
colonizeNewPlanet: '殖民新星球',
completeExpedition: '完成遠征任務',
discoverRuins: '發現古代遺跡',
researchComputer: '研究電腦技術到4級',
improveRelation: '提升與NPC的關係',
reachFriendly: '與NPC達到友好關係',
spyHostileNPC: '偵查敵對NPC',
formAlliance: '與友好NPC結盟',
buildDefenses: '建造防禦設施',
winDefenseBattle: '贏得防禦戰鬥',
spyEnemyPlanet: '偵查敵方星球',
attackEnemy: '攻擊敵方',
recycleDebris: '回收殘骸',
buildBattleships: '建造10艘戰列艦',
exploreDeepRuins: '探索遺跡深處',
researchHyperspace: '研究超空間技術',
defeatBoss: '擊敗神秘敵人',
colonizeSpecial: '殖民特殊位置',
continueDevelopment: '繼續發展'
},
dialogues: {
'1_1': {
prologue_1: '歡迎來到銀河系,年輕的指揮官。這片廣袤的宇宙等待著你的探索。首先,讓我們建設好你的家園星球。',
prologue_2: '我感應到了新的意識覺醒...有趣...讓我們看看你能走多遠...'
},
'1_2': {
prologue_1: '基礎設施已經建設完成。現在,是時候發展科技了。建造研究實驗室,開啟你的科技之路。'
},
'1_3': {
prologue_1: '有了科技的支持,你可以開始建造艦隊了。建造船塢,生產你的第一艘戰艦。'
},
'1_4': {
prologue_1: '你的艦隊已經成型。現在,讓我們了解一下周圍的環境。派出間諜探測器,偵查附近的勢力。',
prologue_2: '你並不孤單...銀河系中還有其他文明存在...'
},
'1_5': {
prologue_1: '你已經發現了鄰近的勢力。外交是一門藝術,嘗試與他們建立聯繫。',
epilogue_1: '感謝你的禮物,指揮官。希望我們能成為朋友。',
epilogue_2: '很好...建立聯繫是探索更深秘密的第一步...'
},
'2_1': {
prologue_1: '你的勢力已經穩固。是時候擴張領地了。研究天體物理學,建造殖民船,探索新的星球。',
prologue_2: '宇宙是無限的...更多的星球意味著更多的可能性...'
},
'2_2': {
prologue_1: '殖民成功!但宇宙深處還有更多秘密等待發現。派遣艦隊進行遠征探險吧。',
prologue_2: '遠方傳來微弱的信號...那裡有什麼在等待著你...'
},
'2_3': {
prologue_1: '你的探險隊發現了異常信號。這些信號似乎來自一個古老的文明...調查它們的來源。',
epilogue_1: '這些符號...是古代文明的遺跡!繼續調查,揭開它們的秘密。'
},
'2_4': {
prologue_1: '你發現了古代遺跡的位置。派遣艦隊前去探索,看看能發現什麼。'
},
'2_5': {
prologue_1: '遺跡中發現了大量數據檔案。研究這些數據,也許能解鎖新的科技。'
},
'3_1': {
prologue_1: '在探索的同時,也不要忘記外交。與周圍的勢力保持良好關係對你有益。'
},
'3_2': {
prologue_1: '有些勢力對你表示了友好。繼續加深關係,也許能獲得更多支持。'
},
'3_3': {
prologue_1: '情報顯示,有敵對勢力正在暗中觀察你。保持警惕,偵查他們的動向。'
},
'3_4': {
prologue_1: '與友好勢力建立正式同盟,在面對威脅時互相支持。'
},
'3_5': {
prologue_1: '威脅正在逼近。建設防禦設施,準備迎接可能的衝突。'
},
'4_1': {
prologue_1: '敵人發動了進攻!保衛你的星球!',
epilogue_1: '你成功擊退了敵人的第一波進攻。但這只是開始...'
},
'4_2': {
prologue_1: '敵人退卻了,但他們還會回來。偵查他們的星球,了解他們的實力。'
},
'4_3': {
prologue_1: '是時候反擊了。進攻敵人的星球,削弱他們的力量。'
},
'4_4': {
prologue_1: '戰場上留下了大量殘骸。回收這些資源,為下一場戰鬥做準備。'
},
'4_5': {
prologue_1: '最終決戰即將來臨。建造強大的艦隊,準備迎接最後的挑戰。'
},
'5_1': {
prologue_1: '所有線索都指向遺跡的最深處。那裡隱藏著古代文明最核心的秘密。',
prologue_2: '你終於來到了這裡...真相即將揭曉...'
},
'5_2': {
prologue_1: '在遺跡深處,你發現了失落的古代科技。研究並解鎖它們的力量。'
},
'5_3': {
prologue_1: '一個神秘的敵人出現了。這是最後的挑戰,擊敗它!',
epilogue_1: '你做到了!古代的守護者已被擊敗。銀河系的秘密向你敞開。'
},
'5_4': {
prologue_1: '和平終於來臨。在這個新紀元中,建立新的殖民地,擴展你的帝國。'
},
'5_5': {
prologue_1: '你的傳奇才剛剛開始。繼續探索,征服更多的星系!',
epilogue_1: '銀河系廣闘無垠,還有無數秘密等待你去發現...'
}
}
}
}

View File

@@ -14,7 +14,7 @@ import {
import { ACHIEVEMENTS, ACHIEVEMENT_MAP, getNextTier, getTierIndex } from '@/config/achievementConfig'
// 初始化空的成就统计数据
export function initializeAchievementStats(): AchievementStats {
export const initializeAchievementStats = (): AchievementStats => {
const emptyShipRecord = Object.values(ShipType).reduce((acc, type) => {
acc[type] = 0
return acc
@@ -100,7 +100,7 @@ export function initializeAchievementStats(): AchievementStats {
}
// 初始化所有成就进度
export function initializeAchievements(): Record<string, AchievementProgress> {
export const initializeAchievements = (): Record<string, AchievementProgress> => {
const achievements: Record<string, AchievementProgress> = {}
for (const config of ACHIEVEMENTS) {
@@ -122,7 +122,7 @@ export function initializeAchievements(): Record<string, AchievementProgress> {
}
// 获取成就的当前值
function getAchievementValue(stats: AchievementStats, statKey: string, checkType: string): number {
const getAchievementValue = (stats: AchievementStats, statKey: string, checkType: string): number => {
// 处理特殊的组合统计键
if (statKey === 'totalResourcesConsumed') {
return stats.totalMetalConsumed + stats.totalCrystalConsumed + stats.totalDeuteriumConsumed + stats.totalDarkMatterConsumed
@@ -157,7 +157,7 @@ export interface AchievementUnlock {
reward: AchievementReward
}
export function checkAchievements(player: Player): AchievementUnlock[] {
export const checkAchievements = (player: Player): AchievementUnlock[] => {
if (!player.achievementStats || !player.achievements) {
return []
}
@@ -213,7 +213,7 @@ export function checkAchievements(player: Player): AchievementUnlock[] {
}
// 应用成就奖励
export function applyAchievementReward(player: Player, reward: AchievementReward): void {
export const applyAchievementReward = (player: Player, reward: AchievementReward): void => {
const firstPlanet = player.planets[0]
if (reward.darkMatter && firstPlanet) {
// 奖励添加到第一个星球的资源中
@@ -228,10 +228,10 @@ export function applyAchievementReward(player: Player, reward: AchievementReward
// ==================== 统计更新函数 ====================
// 更新资源生产统计
export function updateResourceProductionStats(
export const updateResourceProductionStats = (
stats: AchievementStats,
produced: { metal?: number; crystal?: number; deuterium?: number; darkMatter?: number }
): void {
): void => {
if (produced.metal) stats.totalMetalProduced += produced.metal
if (produced.crystal) stats.totalCrystalProduced += produced.crystal
if (produced.deuterium) stats.totalDeuteriumProduced += produced.deuterium
@@ -239,7 +239,7 @@ export function updateResourceProductionStats(
}
// 更新资源消耗统计
export function updateResourceConsumptionStats(stats: AchievementStats, consumed: Partial<Resources>): void {
export const updateResourceConsumptionStats = (stats: AchievementStats, consumed: Partial<Resources>): void => {
if (consumed.metal) stats.totalMetalConsumed += consumed.metal
if (consumed.crystal) stats.totalCrystalConsumed += consumed.crystal
if (consumed.deuterium) stats.totalDeuteriumConsumed += consumed.deuterium
@@ -247,7 +247,7 @@ export function updateResourceConsumptionStats(stats: AchievementStats, consumed
}
// 更新建筑升级统计
export function updateBuildingStats(stats: AchievementStats, buildingType: BuildingType, level: number): void {
export const updateBuildingStats = (stats: AchievementStats, buildingType: BuildingType, level: number): void => {
stats.buildingsUpgraded += 1
// 更新最高等级记录
@@ -257,7 +257,7 @@ export function updateBuildingStats(stats: AchievementStats, buildingType: Build
}
// 更新科技研究统计
export function updateResearchStats(stats: AchievementStats, techType: TechnologyType, level: number): void {
export const updateResearchStats = (stats: AchievementStats, techType: TechnologyType, level: number): void => {
stats.researchCompleted += 1
// 更新最高等级记录
@@ -267,19 +267,19 @@ export function updateResearchStats(stats: AchievementStats, techType: Technolog
}
// 更新舰船生产统计
export function updateShipProductionStats(stats: AchievementStats, shipType: ShipType, quantity: number): void {
export const updateShipProductionStats = (stats: AchievementStats, shipType: ShipType, quantity: number): void => {
stats.shipsProduced[shipType] = (stats.shipsProduced[shipType] || 0) + quantity
stats.totalShipsProduced += quantity
}
// 更新防御建造统计
export function updateDefenseProductionStats(stats: AchievementStats, defenseType: DefenseType, quantity: number): void {
export const updateDefenseProductionStats = (stats: AchievementStats, defenseType: DefenseType, quantity: number): void => {
stats.defensesBuilt[defenseType] = (stats.defensesBuilt[defenseType] || 0) + quantity
stats.totalDefensesBuilt += quantity
}
// 更新攻击统计(玩家作为攻击者)
export function updateAttackStats(stats: AchievementStats, battleResult: BattleResult, won: boolean, debrisValue: number): void {
export const updateAttackStats = (stats: AchievementStats, battleResult: BattleResult, won: boolean, debrisValue: number): void => {
// 攻击也算飞行任务
stats.totalFlightMissions += 1
stats.attacksLaunched += 1
@@ -305,7 +305,7 @@ export function updateAttackStats(stats: AchievementStats, battleResult: BattleR
}
// 更新防御统计(玩家作为防御者)
export function updateDefenseStats(stats: AchievementStats, battleResult: BattleResult, won: boolean, debrisValue: number): void {
export const updateDefenseStats = (stats: AchievementStats, battleResult: BattleResult, won: boolean, debrisValue: number): void => {
if (won) {
stats.defensesSuccessful += 1
} else {
@@ -347,33 +347,33 @@ export function updateDefenseStats(stats: AchievementStats, battleResult: Battle
}
// 更新飞行任务统计
export function updateFlightMissionStats(stats: AchievementStats): void {
export const updateFlightMissionStats = (stats: AchievementStats): void => {
stats.totalFlightMissions += 1
}
// 更新运输任务统计
export function updateTransportStats(stats: AchievementStats, resourcesAmount: number): void {
export const updateTransportStats = (stats: AchievementStats, resourcesAmount: number): void => {
stats.transportMissions += 1
stats.transportedResources += resourcesAmount
}
// 更新殖民统计
export function updateColonizationStats(stats: AchievementStats): void {
export const updateColonizationStats = (stats: AchievementStats): void => {
stats.colonizations += 1
}
// 更新侦查统计
export function updateSpyStats(stats: AchievementStats): void {
export const updateSpyStats = (stats: AchievementStats): void => {
stats.spyMissions += 1
}
// 更新部署统计
export function updateDeploymentStats(stats: AchievementStats): void {
export const updateDeploymentStats = (stats: AchievementStats): void => {
stats.deployments += 1
}
// 更新探险统计
export function updateExpeditionStats(stats: AchievementStats, successful: boolean): void {
export const updateExpeditionStats = (stats: AchievementStats, successful: boolean): void => {
stats.expeditionsTotal += 1
if (successful) {
stats.expeditionsSuccessful += 1
@@ -381,55 +381,55 @@ export function updateExpeditionStats(stats: AchievementStats, successful: boole
}
// 更新回收统计
export function updateRecyclingStats(stats: AchievementStats, resourcesAmount: number): void {
export const updateRecyclingStats = (stats: AchievementStats, resourcesAmount: number): void => {
stats.recyclingMissions += 1
stats.recycledResources += resourcesAmount
}
// 更新行星毁灭统计
export function updatePlanetDestructionStats(stats: AchievementStats): void {
export const updatePlanetDestructionStats = (stats: AchievementStats): void => {
stats.planetDestructions += 1
}
// 更新燃料消耗统计
export function updateFuelConsumptionStats(stats: AchievementStats, fuelAmount: number): void {
export const updateFuelConsumptionStats = (stats: AchievementStats, fuelAmount: number): void => {
stats.fuelConsumed += fuelAmount
}
// 更新友好NPC数量
export function updateFriendlyNPCStats(stats: AchievementStats, count: number): void {
export const updateFriendlyNPCStats = (stats: AchievementStats, count: number): void => {
stats.friendlyNPCCount = count
}
// 更新敌对NPC数量
export function updateHostileNPCStats(stats: AchievementStats, count: number): void {
export const updateHostileNPCStats = (stats: AchievementStats, count: number): void => {
stats.hostileNPCCount = count
}
// 更新送礼统计
export function updateGiftStats(stats: AchievementStats, resourcesAmount: number): void {
export const updateGiftStats = (stats: AchievementStats, resourcesAmount: number): void => {
stats.giftsSent += 1
stats.giftResourcesTotal += resourcesAmount
}
// 更新被NPC攻击统计
export function updateAttackedByNPCStats(stats: AchievementStats): void {
export const updateAttackedByNPCStats = (stats: AchievementStats): void => {
stats.attackedByNPC += 1
}
// 更新被NPC侦查统计
export function updateSpiedByNPCStats(stats: AchievementStats): void {
export const updateSpiedByNPCStats = (stats: AchievementStats): void => {
stats.spiedByNPC += 1
}
// 更新被NPC回收残骸统计
export function updateDebrisRecycledByNPCStats(stats: AchievementStats, resourcesAmount: number): void {
export const updateDebrisRecycledByNPCStats = (stats: AchievementStats, resourcesAmount: number): void => {
stats.debrisRecycledByNPC += 1
stats.debrisResourcesLostToNPC += resourcesAmount
}
// 获取成就的下一个目标
export function getNextTarget(achievementId: string, currentTier: AchievementTier | null): number | null {
export const getNextTarget = (achievementId: string, currentTier: AchievementTier | null): number | null => {
const config = ACHIEVEMENT_MAP[achievementId]
if (!config) return null
@@ -441,7 +441,7 @@ export function getNextTarget(achievementId: string, currentTier: AchievementTie
}
// 获取成就进度百分比
export function getAchievementProgress(achievementId: string, currentValue: number, currentTier: AchievementTier | null): number {
export const getAchievementProgress = (achievementId: string, currentValue: number, currentTier: AchievementTier | null): number => {
const config = ACHIEVEMENT_MAP[achievementId]
if (!config) return 0

View File

@@ -130,7 +130,8 @@ export const completeBuildQueue = (
// 建造完成
if (item.type === 'building') {
const oldLevel = planet.buildings[item.itemType as BuildingType] || 0
const newLevel = item.targetLevel || 0
// 升级完成时,等级+1而不是直接使用targetLevel避免在升级过程中被拆除后跳级
const newLevel = oldLevel + 1
planet.buildings[item.itemType as BuildingType] = newLevel
// 计算并累积积分
@@ -164,7 +165,6 @@ export const completeBuildQueue = (
}
} else if (item.type === 'demolish') {
// 拆除完成,降低建筑等级
// 注意:拆除不会扣除积分,积分只增不减
const buildingType = item.itemType as BuildingType
const currentLevel = planet.buildings[buildingType] || 0
planet.buildings[buildingType] = Math.max(0, currentLevel - 1)

View File

@@ -5,7 +5,7 @@
*/
import { DIPLOMATIC_CONFIG } from '@/config/gameConfig'
import { locales, type Locale } from '@/locales'
import { getLocale, type Locale } from '@/locales'
import * as resourceLogic from './resourceLogic'
import * as officerLogic from './officerLogic'
import type {
@@ -34,7 +34,7 @@ import { RelationStatus as RS, DiplomaticEventType as DET } from '@/types/game'
*/
const t = (key: string, locale: Locale, params?: Record<string, string | number>): string => {
const keys = key.split('.')
let value: any = locales[locale]
let value: any = getLocale(locale)
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
@@ -372,7 +372,7 @@ export const handleAttackReputation = (
attacker,
defender,
DET.Attack,
-reputationLoss,
reputationLoss,
t('diplomacy.reports.youAttackedNpc', locale, { npcName: defender.name })
)
}
@@ -876,3 +876,48 @@ export const checkAndHandleEliminatedNPCs = (allNpcs: NPC[], player: Player, loc
return eliminatedNpcIds
}
/**
* 通知玩家盟友正在协防
* @param npc 派遣协防舰队的NPC
* @param player 玩家
* @param targetPlanet 被协防的星球
* @param mission 协防任务
*/
export const notifyPlayerOfAllyDefense = (
npc: NPC,
player: Player,
targetPlanet: Planet,
mission: FleetMission
): void => {
// 计算舰队规模
const fleetSize = Object.values(mission.fleet).reduce((sum, count) => sum + (count || 0), 0)
// 创建外交报告
const report: DiplomaticReport = {
id: `ally_defense_${Date.now()}_${npc.id}`,
npcId: npc.id,
npcName: npc.name,
eventType: 'ally_defend' as DiplomaticEventType,
timestamp: Date.now(),
details: {
targetPlanetName: targetPlanet.name,
targetPosition: targetPlanet.position,
fleetSize,
arrivalTime: mission.arrivalTime,
stationDuration: mission.returnTime ? mission.returnTime - mission.arrivalTime : 0
},
read: false
}
// 添加到玩家的外交报告列表
if (!player.diplomaticReports) {
player.diplomaticReports = []
}
player.diplomaticReports.unshift(report)
// 限制报告数量
if (player.diplomaticReports.length > 100) {
player.diplomaticReports = player.diplomaticReports.slice(0, 100)
}
}

View File

@@ -63,7 +63,8 @@ export const createFleetMission = (
id: `mission_${now}`,
playerId,
originPlanetId,
targetPosition,
// 深拷贝targetPosition,避免多个任务共享同一个引用
targetPosition: { ...targetPosition },
missionType,
fleet,
cargo,
@@ -766,8 +767,8 @@ export const processExpeditionArrival = (mission: FleetMission): ExpeditionResul
const aliensThreshold = piratesThreshold + probs.aliens
if (random < resourceThreshold) {
// 发现资源
const baseMultiplier = 0.1 + Math.random() * 0.3 // 10%-40% 的货舱容量
// 发现资源 - 大幅提升奖励
const baseMultiplier = 0.3 + Math.random() * 0.5 // 30%-80% 的货舱容量
const resourceMultiplier = baseMultiplier * zoneConfig.resourceMultiplier
const resourceAmount = Math.floor(totalCargoCapacity * resourceMultiplier)
const metalAmount = Math.floor(resourceAmount * 0.5)
@@ -784,8 +785,8 @@ export const processExpeditionArrival = (mission: FleetMission): ExpeditionResul
message: 'expedition.foundResources'
}
} else if (random < darkMatterThreshold) {
// 发现暗物质
const baseDarkMatter = 50 + Math.random() * 150 // 50-200 暗物质
// 发现暗物质 - 大幅提升奖励
const baseDarkMatter = 200 + Math.random() * 500 // 200-700 暗物质
const darkMatterAmount = Math.floor(baseDarkMatter * zoneConfig.darkMatterMultiplier)
mission.cargo.darkMatter += darkMatterAmount
@@ -807,7 +808,7 @@ export const processExpeditionArrival = (mission: FleetMission): ExpeditionResul
const shipTypeIndex = Math.floor(Math.random() * possibleShips.length)
const shipType = possibleShips[shipTypeIndex] ?? ShipType.LightFighter
const baseCount = 1 + Math.random() * 5 // 1-5 艘
const baseCount = 3 + Math.random() * 12 // 3-15 艘
const count = Math.floor(baseCount * zoneConfig.fleetFindMultiplier)
foundFleet[shipType] = count
@@ -1062,11 +1063,11 @@ export const processFleetReturn = (
): void => {
// 舰船返回 - 使用安全添加函数
fleetStorageLogic.addFleetSafely(originPlanet, mission.fleet, technologies)
// 注意:如果舰队仓储溢出,超出部分会丢失(这是合理的惩罚)
// 如果舰队仓储溢出,超出部分会丢失(这是合理的惩罚)
// 资源返回(掠夺物或运输货物)- 使用安全添加函数
resourceLogic.addResourcesSafely(originPlanet, mission.cargo, storageCapacityBonus)
// 注意:如果资源仓储溢出,超出部分会丢失(这是合理的惩罚)
// 如果资源仓储溢出,超出部分会丢失(这是合理的惩罚)
}
/**

View File

@@ -223,7 +223,7 @@ export const processGameUpdate = (
if (onUnlock && player.technologies !== previousTechnologies) {
const mainPlanet = player.planets.find(p => !p.isMoon)
if (mainPlanet) {
// 注意:这里使用完成后的建筑状态,因为我们只关心科技完成带来的解锁
// 这里使用完成后的建筑状态,因为我们只关心科技完成带来的解锁
const techUnlockedItems = unlockLogic.checkAllNewlyUnlocked(
mainPlanet,
player.technologies,

View File

@@ -1,9 +1,102 @@
import type { NPC, Planet, Player, FleetMission, SpyReport, SpiedNotification, IncomingFleetAlert, Fleet, DebrisField } from '@/types/game'
import type {
NPC,
Planet,
Player,
FleetMission,
SpyReport,
SpiedNotification,
IncomingFleetAlert,
Fleet,
DebrisField,
Resources,
TradeOffer,
IntelReport,
JointAttackInvite,
IntelType,
AidNotification,
AllyDefenseNotification,
AttitudeChangeNotification
} from '@/types/game'
import { MissionType, ShipType, TechnologyType, RelationStatus, NPCAIType } from '@/types/game'
// 重新导出类型供外部使用
export type { TradeOffer, IntelReport, JointAttackInvite, IntelType, AidNotification, AllyDefenseNotification, AttitudeChangeNotification }
import * as fleetLogic from './fleetLogic'
import * as diplomaticLogic from './diplomaticLogic'
import * as resourceLogic from './resourceLogic'
import { DIPLOMATIC_CONFIG, SHIPS } from '@/config/gameConfig'
// ========== 敌对NPC增强行为类型定义 ==========
/**
* 骚扰任务类型
*/
export type HarassmentType = 'spy' | 'raid' | 'intimidate'
/**
* 报复等级
*/
export interface RevengeLevel {
attackCount: number
fleetMultiplier: number
attackInterval: number
name: string
}
/**
* 围攻协调信息
*/
export interface SiegeCoordination {
targetPlanetId: string
targetPosition: { galaxy: number; system: number; position: number }
coordinatorNpcId: string
participantNpcIds: string[]
arrivalTime: number
startedAt: number
}
// TradeOffer, IntelReport, JointAttackInvite, IntelType 类型已移至 @/types/game.ts
/**
* NPC之间的关系
*/
export interface NPCRelation {
npcId: string
targetNpcId: string
status: 'ally' | 'neutral' | 'enemy'
strength: number // 关系强度 0-100
lastUpdated: number
}
/**
* NPC联盟
*/
export interface NPCAlliance {
id: string
name: string
leaderNpcId: string
memberNpcIds: string[]
enemyNpcIds: string[]
createdAt: number
}
/**
* 辅助函数:获取舰队中最慢船只的速度
* 用于计算飞行时间
*/
const getFleetMinSpeed = (fleet: Partial<Fleet>): number => {
let minSpeed = Infinity
for (const [shipType, count] of Object.entries(fleet)) {
if ((count || 0) > 0) {
const shipConfig = SHIPS[shipType as ShipType]
if (shipConfig) {
minSpeed = Math.min(minSpeed, shipConfig.speed)
}
}
}
return minSpeed === Infinity ? 10000 : minSpeed // 默认使用基础速度
}
/**
* AI 类型行为修改器
* 根据 NPC 的 AI 类型调整其行为参数
@@ -342,27 +435,133 @@ export const shouldNPCGiftPlayer = (npc: NPC, player: Player, currentTime: numbe
return Math.random() < giftProbability
}
/**
* 计算玩家的总资源产量(每小时)
*/
const calculatePlayerTotalProduction = (player: Player): { metal: number; crystal: number; deuterium: number } => {
let totalMetal = 0
let totalCrystal = 0
let totalDeuterium = 0
// 计算当前激活的军官加成
const bonuses = player.officers
? {
resourceProductionBonus: Object.values(player.officers).reduce((sum, officer) => {
if (officer.active && officer.expiresAt && officer.expiresAt > Date.now()) {
return sum + (officer.bonuses?.resourceProductionBonus || 0)
}
return sum
}, 0),
darkMatterProductionBonus: Object.values(player.officers).reduce((sum, officer) => {
if (officer.active && officer.expiresAt && officer.expiresAt > Date.now()) {
return sum + (officer.bonuses?.darkMatterProductionBonus || 0)
}
return sum
}, 0),
energyProductionBonus: Object.values(player.officers).reduce((sum, officer) => {
if (officer.active && officer.expiresAt && officer.expiresAt > Date.now()) {
return sum + (officer.bonuses?.energyProductionBonus || 0)
}
return sum
}, 0)
}
: { resourceProductionBonus: 0, darkMatterProductionBonus: 0, energyProductionBonus: 0 }
// 遍历所有星球计算总产量
for (const planet of player.planets) {
if (planet.isMoon) continue // 月球没有矿场
const production = resourceLogic.calculateResourceProduction(planet, bonuses)
totalMetal += production.metal || 0
totalCrystal += production.crystal || 0
totalDeuterium += production.deuterium || 0
}
return { metal: totalMetal, crystal: totalCrystal, deuterium: totalDeuterium }
}
/**
* NPC向玩家赠送资源
* 动态计算赠送量基于玩家每小时产量的4-8倍
* 赠送的资源会从NPC主星球扣除
*/
export const giftResourcesToPlayer = (npc: NPC, player: Player): void => {
const { NPC_GIFT_CONFIG } = DIPLOMATIC_CONFIG
const dynamicConfig = NPC_GIFT_CONFIG.DYNAMIC_GIFT
const baseConfig = NPC_GIFT_CONFIG.BASE_GIFT_AMOUNT
// 随机生成赠送资源量
const giftResources = {
metal:
Math.floor(Math.random() * (NPC_GIFT_CONFIG.GIFT_AMOUNT.METAL.max - NPC_GIFT_CONFIG.GIFT_AMOUNT.METAL.min + 1)) +
NPC_GIFT_CONFIG.GIFT_AMOUNT.METAL.min,
crystal:
Math.floor(Math.random() * (NPC_GIFT_CONFIG.GIFT_AMOUNT.CRYSTAL.max - NPC_GIFT_CONFIG.GIFT_AMOUNT.CRYSTAL.min + 1)) +
NPC_GIFT_CONFIG.GIFT_AMOUNT.CRYSTAL.min,
deuterium:
Math.floor(Math.random() * (NPC_GIFT_CONFIG.GIFT_AMOUNT.DEUTERIUM.max - NPC_GIFT_CONFIG.GIFT_AMOUNT.DEUTERIUM.min + 1)) +
NPC_GIFT_CONFIG.GIFT_AMOUNT.DEUTERIUM.min,
// 获取NPC主星球
const npcPlanet = npc.planets[0]
if (!npcPlanet) return
let giftResources = {
metal: 0,
crystal: 0,
deuterium: 0,
darkMatter: 0,
energy: 0
}
if (dynamicConfig?.ENABLED) {
// 动态计算:根据玩家产量
const playerProduction = calculatePlayerTotalProduction(player)
// 随机生成产量倍数4-8倍
const multiplierRange = dynamicConfig.PRODUCTION_MULTIPLIER
const multiplier = multiplierRange.min + Math.random() * (multiplierRange.max - multiplierRange.min)
// 计算赠送量(产量 × 倍数)
let metalGift = Math.floor(playerProduction.metal * multiplier)
let crystalGift = Math.floor(playerProduction.crystal * multiplier)
let deuteriumGift = Math.floor(playerProduction.deuterium * multiplier)
// 应用最小值保底
metalGift = Math.max(metalGift, dynamicConfig.MIN_AMOUNT.METAL)
crystalGift = Math.max(crystalGift, dynamicConfig.MIN_AMOUNT.CRYSTAL)
deuteriumGift = Math.max(deuteriumGift, dynamicConfig.MIN_AMOUNT.DEUTERIUM)
// 应用最大值封顶
metalGift = Math.min(metalGift, dynamicConfig.MAX_AMOUNT.METAL)
crystalGift = Math.min(crystalGift, dynamicConfig.MAX_AMOUNT.CRYSTAL)
deuteriumGift = Math.min(deuteriumGift, dynamicConfig.MAX_AMOUNT.DEUTERIUM)
giftResources.metal = metalGift
giftResources.crystal = crystalGift
giftResources.deuterium = deuteriumGift
} else {
// 使用基础配置(后备方案)
giftResources.metal = Math.floor(Math.random() * (baseConfig.METAL.max - baseConfig.METAL.min + 1)) + baseConfig.METAL.min
giftResources.crystal = Math.floor(Math.random() * (baseConfig.CRYSTAL.max - baseConfig.CRYSTAL.min + 1)) + baseConfig.CRYSTAL.min
giftResources.deuterium =
Math.floor(Math.random() * (baseConfig.DEUTERIUM.max - baseConfig.DEUTERIUM.min + 1)) + baseConfig.DEUTERIUM.min
}
// 根据好感度额外加成:好感度越高,赠送越多
const relation = npc.relations?.[player.id]
if (relation && relation.reputation > 60) {
// 好感度60-100区间额外增加0-40%
const reputationBonus = 1 + (relation.reputation - 60) / 100
giftResources.metal = Math.floor(giftResources.metal * reputationBonus)
giftResources.crystal = Math.floor(giftResources.crystal * reputationBonus)
giftResources.deuterium = Math.floor(giftResources.deuterium * reputationBonus)
}
// 限制赠送量不超过NPC拥有的资源只赠送NPC实际拥有的部分
giftResources.metal = Math.min(giftResources.metal, Math.floor(npcPlanet.resources.metal))
giftResources.crystal = Math.min(giftResources.crystal, Math.floor(npcPlanet.resources.crystal))
giftResources.deuterium = Math.min(giftResources.deuterium, Math.floor(npcPlanet.resources.deuterium))
// 如果NPC资源不足所有资源都为0则不赠送
if (giftResources.metal <= 0 && giftResources.crystal <= 0 && giftResources.deuterium <= 0) {
return
}
// 从NPC星球扣除资源
npcPlanet.resources.metal -= giftResources.metal
npcPlanet.resources.crystal -= giftResources.crystal
npcPlanet.resources.deuterium -= giftResources.deuterium
// 处理赠送
diplomaticLogic.handleNPCGiftToPlayer(npc, player, giftResources)
@@ -370,6 +569,161 @@ export const giftResourcesToPlayer = (npc: NPC, player: Player): void => {
;(npc as any).lastGiftTime = Date.now()
}
/**
* 检查NPC是否应该协防玩家
* 当玩家受到攻击且NPC是友好状态时触发
*/
export const shouldNPCDefendPlayer = (npc: NPC, player: Player, currentTime: number): boolean => {
const { ALLY_DEFENSE_CONFIG } = DIPLOMATIC_CONFIG
// 检查功能是否启用
if (!ALLY_DEFENSE_CONFIG?.ENABLED) {
return false
}
// 检查好感度
const relation = npc.relations?.[player.id]
if (!relation || relation.reputation < ALLY_DEFENSE_CONFIG.MIN_REPUTATION) {
return false
}
// 检查上次协防时间
const lastDefendTime = (npc as any).lastDefendTime || 0
if (currentTime - lastDefendTime < ALLY_DEFENSE_CONFIG.CHECK_INTERVAL * 1000) {
return false
}
// 检查当前协防任务数量
const activeDefenseMissions = npc.fleetMissions?.filter(m => m.missionType === MissionType.Station && m.status === 'outbound').length || 0
if (activeDefenseMissions >= ALLY_DEFENSE_CONFIG.MAX_CONCURRENT_DEFENSES) {
return false
}
// 检查玩家是否有来袭攻击舰队
const incomingAttacks =
player.incomingFleetAlerts?.filter(
alert => alert.missionType === MissionType.Attack && alert.fleetSize >= ALLY_DEFENSE_CONFIG.MIN_INCOMING_FLEET_SIZE
) || []
if (incomingAttacks.length === 0) {
return false
}
// 根据好感度计算协防概率
const reputationBonus = (relation.reputation - ALLY_DEFENSE_CONFIG.MIN_REPUTATION) / 30 // 70-100区间
const defenseProbability = ALLY_DEFENSE_CONFIG.BASE_DEFENSE_PROBABILITY + reputationBonus * 0.3
return Math.random() < defenseProbability
}
/**
* NPC派舰队协防玩家
*/
export const sendDefenseFleet = (npc: NPC, _player: Player, targetPlanet: Planet): FleetMission | null => {
const { ALLY_DEFENSE_CONFIG } = DIPLOMATIC_CONFIG
// 选择最近且有舰队的NPC星球
const sourcePlanet = selectBestNPCPlanet(npc, targetPlanet.position)
if (!sourcePlanet) return null
// 计算派遣舰队战斗舰船的30%-60%
const fleetRatio =
ALLY_DEFENSE_CONFIG.FLEET_RATIO.min + Math.random() * (ALLY_DEFENSE_CONFIG.FLEET_RATIO.max - ALLY_DEFENSE_CONFIG.FLEET_RATIO.min)
const combatShipTypes = [
ShipType.LightFighter,
ShipType.HeavyFighter,
ShipType.Cruiser,
ShipType.Battleship,
ShipType.Battlecruiser,
ShipType.Bomber,
ShipType.Destroyer
]
const defenseFleet: Partial<Fleet> = {}
let hasShips = false
for (const shipType of combatShipTypes) {
const available = sourcePlanet.fleet[shipType] || 0
if (available > 0) {
const toSend = Math.floor(available * fleetRatio)
if (toSend > 0) {
defenseFleet[shipType] = toSend
sourcePlanet.fleet[shipType] = available - toSend
hasShips = true
}
}
}
if (!hasShips) return null
// 计算飞行时间
const distance = fleetLogic.calculateDistance(sourcePlanet.position, targetPlanet.position)
const minSpeed = getFleetMinSpeed(defenseFleet)
const flightTime = fleetLogic.calculateFlightTime(distance, minSpeed)
const currentTime = Date.now()
const arrivalTime = currentTime + flightTime
// 创建协防任务
const mission: FleetMission = {
id: `ally_defend_${currentTime}_${npc.id}`,
playerId: npc.id,
npcId: npc.id,
originPlanetId: sourcePlanet.id,
targetPosition: targetPlanet.position,
targetPlanetId: targetPlanet.id,
fleet: defenseFleet as Fleet,
missionType: MissionType.Station,
departureTime: currentTime,
arrivalTime: arrivalTime,
returnTime: arrivalTime + ALLY_DEFENSE_CONFIG.STATION_DURATION,
status: 'outbound',
cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 },
isGift: false,
isHostile: false // 盟友协防不是敌对任务
}
// 添加到NPC任务列表
if (!npc.fleetMissions) {
npc.fleetMissions = []
}
npc.fleetMissions.push(mission)
// 更新上次协防时间
;(npc as any).lastDefendTime = currentTime
return mission
}
/**
* 检查并执行NPC协防行为
*/
export const checkAndExecuteAllyDefense = (npc: NPC, player: Player, currentTime: number): void => {
if (!shouldNPCDefendPlayer(npc, player, currentTime)) {
return
}
// 找到受攻击的玩家星球
const incomingAttacks = player.incomingFleetAlerts?.filter(alert => alert.missionType === MissionType.Attack) || []
if (incomingAttacks.length === 0) return
// 选择一个受攻击的星球进行协防
const targetAlert = incomingAttacks[Math.floor(Math.random() * incomingAttacks.length)]
if (!targetAlert) return
const targetPlanet = player.planets.find(p => p.id === targetAlert.targetPlanetId)
if (!targetPlanet) return
// 派遣协防舰队
const mission = sendDefenseFleet(npc, player, targetPlanet)
if (mission) {
// 可以在这里添加通知玩家的逻辑
diplomaticLogic.notifyPlayerOfAllyDefense(npc, player, targetPlanet, mission)
}
}
/**
* 选择NPC的最佳攻击来源星球
*/
@@ -795,7 +1149,10 @@ export const updateNPCBehavior = (
giftResourcesToPlayer(npc, player)
}
// 6. 更新即将到来的舰队警告(删除过期的
// 6. 检查是否应该协防玩家仅友好NPC玩家被攻击时
checkAndExecuteAllyDefense(npc, player, currentTime)
// 7. 更新即将到来的舰队警告(删除过期的)
updateIncomingFleetAlerts(player, currentTime)
}
@@ -898,7 +1255,10 @@ export const updateNPCBehaviorWithLimit = (
giftResourcesToPlayer(npc, player)
}
// 6. 更新即将到来的舰队警告(删除过期的
// 6. 检查是否应该协防玩家仅友好NPC玩家被攻击时不受并发限制
checkAndExecuteAllyDefense(npc, player, currentTime)
// 7. 更新即将到来的舰队警告(删除过期的)
updateIncomingFleetAlerts(player, currentTime)
return { spyCreated, attackCreated }
@@ -1552,3 +1912,1875 @@ export const diagnoseNPCBehavior = (npcs: NPC[], player: Player, currentTime: nu
}
})
}
// ========== 敌对NPC增强行为系统 ==========
/**
* 获取NPC的报复等级
* 基于被攻击次数确定报复强度
*/
export const getRevengeLevel = (npc: NPC, playerId: string): RevengeLevel => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const levels = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION.LEVELS
const memoryDuration = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION.MEMORY_DURATION
// 默认等级(用于数组为空或未找到时)
const defaultLevel: RevengeLevel = levels[0] || { attackCount: 1, fleetMultiplier: 1.0, attackInterval: 600, name: 'mild' }
const attackRecord = npc.attackedBy?.[playerId]
if (!attackRecord) {
return defaultLevel // 默认最低等级
}
// 检查记忆是否过期
const now = Date.now()
if (now - attackRecord.lastAttackTime > memoryDuration) {
return defaultLevel // 记忆过期,重置为最低等级
}
// 根据被攻击次数确定报复等级
const attackCount = attackRecord.count
let currentLevel = defaultLevel
for (const level of levels) {
if (attackCount >= level.attackCount) {
currentLevel = level
}
}
return currentLevel
}
/**
* 检查NPC是否应该执行骚扰战术
*/
export const shouldNPCHarass = (npc: NPC, player: Player, currentTime: number): boolean => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const harassConfig = HOSTILE_BEHAVIOR_CONFIG.HARASSMENT
if (!harassConfig.ENABLED) {
return false
}
// 检查外交关系
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Hostile) {
return false
}
// 检查好感度是否足够低
if (relation.reputation > harassConfig.TRIGGER_REPUTATION) {
return false
}
// 检查骚扰冷却时间
const lastHarassTime = (npc as any).lastHarassTime || 0
if (currentTime - lastHarassTime < harassConfig.INTERVAL * 1000) {
return false
}
// 概率判断
return Math.random() < harassConfig.PROBABILITY
}
/**
* 选择骚扰类型
*/
export const selectHarassmentType = (): HarassmentType => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const types = HOSTILE_BEHAVIOR_CONFIG.HARASSMENT.TYPES
const rand = Math.random()
if (rand < types.spy) {
return 'spy'
} else if (rand < types.spy + types.raid) {
return 'raid'
} else {
return 'intimidate'
}
}
/**
* 创建骚扰任务
*/
export const createHarassmentMission = (
npc: NPC,
player: Player,
allPlanets: Planet[],
harassmentType: HarassmentType
): FleetMission | null => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const harassConfig = HOSTILE_BEHAVIOR_CONFIG.HARASSMENT
// 选择随机玩家星球作为目标
const playerPlanets = allPlanets.filter(p => p.ownerId === player.id)
if (playerPlanets.length === 0) return null
const targetPlanet = playerPlanets[Math.floor(Math.random() * playerPlanets.length)]
if (!targetPlanet) return null
// 选择NPC最佳星球
const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position)
if (!npcPlanet) return null
let fleet: Partial<Fleet> = {}
let missionType: MissionType = MissionType.Attack
if (harassmentType === 'spy') {
// 侦查骚扰:派出间谍探测器
const probes = npcPlanet.fleet[ShipType.EspionageProbe] || 0
if (probes < 1) return null
fleet = { [ShipType.EspionageProbe]: Math.min(probes, 3) }
missionType = MissionType.Spy
} else {
// 袭击或威慑:派出小规模战斗舰队
const fleetRatio =
harassConfig.FLEET_SIZE_RATIO.min + Math.random() * (harassConfig.FLEET_SIZE_RATIO.max - harassConfig.FLEET_SIZE_RATIO.min)
const combatShips = [ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser]
let hasShips = false
for (const shipType of combatShips) {
const available = npcPlanet.fleet[shipType] || 0
if (available > 0) {
const sendCount = Math.max(1, Math.floor(available * fleetRatio))
fleet[shipType] = sendCount
hasShips = true
}
}
if (!hasShips) return null
// 威慑任务使用部署类型(不攻击,只是展示武力)
if (harassmentType === 'intimidate') {
missionType = MissionType.Deploy
}
}
// 从NPC星球扣除舰队
for (const [shipType, count] of Object.entries(fleet)) {
npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number)
}
// 计算飞行时间
const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position)
const fleetMinSpeed = getFleetMinSpeed(fleet)
const flightTime = fleetLogic.calculateFlightTime(distance, fleetMinSpeed)
const now = Date.now()
const mission: FleetMission = {
id: `npc-harass-${harassmentType}-${npc.id}-${now}`,
playerId: npc.id,
npcId: npc.id,
isHostile: harassmentType !== 'intimidate', // 威慑不是敌对行动
originPlanetId: npcPlanet.id,
targetPosition: targetPlanet.position,
targetPlanetId: targetPlanet.id,
missionType,
fleet,
cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 },
departureTime: now,
arrivalTime: now + flightTime * 1000,
status: 'outbound'
}
// 更新骚扰时间
;(npc as any).lastHarassTime = now
// 添加到NPC任务列表
if (!npc.fleetMissions) {
npc.fleetMissions = []
}
npc.fleetMissions.push(mission)
return mission
}
/**
* 检查是否可以发起围攻
* 需要多个敌对NPC协调
*/
export const canInitiateSiege = (npcs: NPC[], player: Player, currentTime: number): { canSiege: boolean; eligibleNpcs: NPC[] } => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const siegeConfig = HOSTILE_BEHAVIOR_CONFIG.SIEGE
if (!siegeConfig.ENABLED) {
return { canSiege: false, eligibleNpcs: [] }
}
// 找出所有符合围攻条件的敌对NPC
const eligibleNpcs = npcs.filter(npc => {
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Hostile) {
return false
}
// 检查好感度是否足够低
if (relation.reputation > siegeConfig.TRIGGER_REPUTATION) {
return false
}
// 检查围攻冷却
const lastSiegeTime = (npc as any).lastSiegeTime || 0
if (currentTime - lastSiegeTime < siegeConfig.COOLDOWN) {
return false
}
// 检查是否有足够的舰队
const npcPlanet = npc.planets[0]
if (!npcPlanet) return false
const combatPower = calculateNPCCombatPower(npcPlanet)
return combatPower >= MIN_COMBAT_POWER_FOR_SPY * 10 // 围攻需要较大战力
})
// 检查是否有足够的NPC参与围攻
const canSiege = eligibleNpcs.length >= siegeConfig.MIN_HOSTILE_NPCS
return { canSiege, eligibleNpcs }
}
/**
* 发起围攻
* 协调多个NPC同时攻击玩家
*/
export const initiateSiege = (
eligibleNpcs: NPC[],
player: Player,
allPlanets: Planet[]
): { missions: FleetMission[]; coordination: SiegeCoordination } | null => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const siegeConfig = HOSTILE_BEHAVIOR_CONFIG.SIEGE
if (eligibleNpcs.length < siegeConfig.MIN_HOSTILE_NPCS) {
return null
}
// 选择目标星球(选择资源最多的)
const playerPlanets = allPlanets.filter(p => p.ownerId === player.id)
if (playerPlanets.length === 0) return null
let targetPlanet = playerPlanets[0]
let maxResources = 0
for (const planet of playerPlanets) {
const totalResources = planet.resources.metal + planet.resources.crystal + planet.resources.deuterium
if (totalResources > maxResources) {
maxResources = totalResources
targetPlanet = planet
}
}
if (!targetPlanet) return null
// 计算统一到达时间以最远NPC的飞行时间为基准
let maxFlightTime = 0
for (const npc of eligibleNpcs) {
const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position)
if (npcPlanet) {
const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position)
const flightTime = fleetLogic.calculateFlightTime(distance, 10000) // 使用基准速度
maxFlightTime = Math.max(maxFlightTime, flightTime)
}
}
const now = Date.now()
const coordWindow = siegeConfig.COORDINATION_WINDOW
const baseArrivalTime = now + maxFlightTime * 1000 + coordWindow
const missions: FleetMission[] = []
const participantNpcIds: string[] = []
// 为每个NPC创建攻击任务
for (const npc of eligibleNpcs) {
const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position)
if (!npcPlanet) continue
// 计算围攻舰队
const fleetRatio = siegeConfig.FLEET_RATIO.min + Math.random() * (siegeConfig.FLEET_RATIO.max - siegeConfig.FLEET_RATIO.min)
const combatShips = [
ShipType.LightFighter,
ShipType.HeavyFighter,
ShipType.Cruiser,
ShipType.Battleship,
ShipType.Bomber,
ShipType.Destroyer,
ShipType.Battlecruiser
]
const siegeFleet: Partial<Fleet> = {}
let hasShips = false
for (const shipType of combatShips) {
const available = npcPlanet.fleet[shipType] || 0
if (available > 0) {
const sendCount = Math.max(1, Math.floor(available * fleetRatio))
siegeFleet[shipType] = sendCount
hasShips = true
}
}
if (!hasShips) continue
// 从NPC星球扣除舰队
for (const [shipType, count] of Object.entries(siegeFleet)) {
npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number)
}
// 计算随机偏移的到达时间(在协调窗口内)
const arrivalOffset = Math.random() * coordWindow
const arrivalTime = baseArrivalTime + arrivalOffset
// 反推出发时间
const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position)
const siegeMinSpeed = getFleetMinSpeed(siegeFleet)
const actualFlightTime = fleetLogic.calculateFlightTime(distance, siegeMinSpeed)
const departureTime = arrivalTime - actualFlightTime * 1000
const mission: FleetMission = {
id: `npc-siege-${npc.id}-${now}`,
playerId: npc.id,
npcId: npc.id,
isHostile: true,
originPlanetId: npcPlanet.id,
targetPosition: targetPlanet.position,
targetPlanetId: targetPlanet.id,
missionType: MissionType.Attack,
fleet: siegeFleet,
cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 },
departureTime: Math.max(now, departureTime),
arrivalTime,
status: 'outbound'
}
// 更新NPC的围攻时间
;(npc as any).lastSiegeTime = now
// 添加到NPC任务列表
if (!npc.fleetMissions) {
npc.fleetMissions = []
}
npc.fleetMissions.push(mission)
missions.push(mission)
participantNpcIds.push(npc.id)
}
if (missions.length < siegeConfig.MIN_HOSTILE_NPCS) {
return null // 没有足够的NPC成功发起围攻
}
const coordination: SiegeCoordination = {
targetPlanetId: targetPlanet.id,
targetPosition: targetPlanet.position,
coordinatorNpcId: eligibleNpcs[0]?.id || '',
participantNpcIds,
arrivalTime: baseArrivalTime,
startedAt: now
}
return { missions, coordination }
}
/**
* 执行报复升级攻击
* 根据报复等级派出不同强度的攻击
*/
export const executeEscalatedRevenge = (
npc: NPC,
player: Player,
allPlanets: Planet[],
config: DynamicBehaviorConfig
): FleetMission | null => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const escalationConfig = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION
if (!escalationConfig.ENABLED) {
return null
}
// 获取报复等级
const revengeLevel = getRevengeLevel(npc, player.id)
// 检查攻击间隔
const lastRevengeTime = npc.lastAttackTime || 0
const now = Date.now()
if (now - lastRevengeTime < revengeLevel.attackInterval * 1000) {
return null
}
// 找到攻击者的星球
const attackRecord = npc.attackedBy?.[player.id]
if (!attackRecord?.planetId) {
return null
}
const targetPlanet = allPlanets.find(p => p.id === attackRecord.planetId)
if (!targetPlanet) {
return null
}
// 选择NPC最佳星球
const npcPlanet = selectBestNPCPlanet(npc, targetPlanet.position)
if (!npcPlanet) {
return null
}
// 根据报复等级计算舰队规模
const baseFleetRatio = config.attackFleetSizeRatio
const escalatedRatio = Math.min(1.0, baseFleetRatio * revengeLevel.fleetMultiplier)
const combatShips = [
ShipType.LightFighter,
ShipType.HeavyFighter,
ShipType.Cruiser,
ShipType.Battleship,
ShipType.Bomber,
ShipType.Destroyer,
ShipType.Battlecruiser,
ShipType.Deathstar
]
const revengeFleet: Partial<Fleet> = {}
let hasShips = false
for (const shipType of combatShips) {
const available = npcPlanet.fleet[shipType] || 0
if (available > 0) {
const sendCount = Math.max(1, Math.floor(available * escalatedRatio))
revengeFleet[shipType] = sendCount
hasShips = true
}
}
if (!hasShips) {
return null
}
// 从NPC星球扣除舰队
for (const [shipType, count] of Object.entries(revengeFleet)) {
npcPlanet.fleet[shipType as ShipType] = (npcPlanet.fleet[shipType as ShipType] || 0) - (count as number)
}
// 计算飞行时间
const distance = fleetLogic.calculateDistance(npcPlanet.position, targetPlanet.position)
const revengeMinSpeed = getFleetMinSpeed(revengeFleet)
const flightTime = fleetLogic.calculateFlightTime(distance, revengeMinSpeed)
const mission: FleetMission = {
id: `npc-revenge-escalated-${revengeLevel.name}-${npc.id}-${now}`,
playerId: npc.id,
npcId: npc.id,
isHostile: true,
originPlanetId: npcPlanet.id,
targetPosition: targetPlanet.position,
targetPlanetId: targetPlanet.id,
missionType: MissionType.Attack,
fleet: revengeFleet,
cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 },
departureTime: now,
arrivalTime: now + flightTime * 1000,
status: 'outbound'
}
// 更新攻击时间
npc.lastAttackTime = now
// 添加到NPC任务列表
if (!npc.fleetMissions) {
npc.fleetMissions = []
}
npc.fleetMissions.push(mission)
return mission
}
/**
* 呼叫盟友NPC助战
* 在报复等级较高时,可能呼叫盟友一起攻击
*/
export const callAlliesForRevenge = (
npc: NPC,
allNpcs: NPC[],
player: Player,
allPlanets: Planet[],
_config: DynamicBehaviorConfig
): FleetMission[] => {
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const escalationConfig = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION
if (!escalationConfig.CALL_ALLIES) {
return []
}
// 获取报复等级
const revengeLevel = getRevengeLevel(npc, player.id)
// 计算呼叫盟友概率(报复等级越高概率越大)
const levelIndex = HOSTILE_BEHAVIOR_CONFIG.REVENGE_ESCALATION.LEVELS.findIndex(l => l.name === revengeLevel.name)
const allyCallProbability = escalationConfig.ALLY_CALL_BASE_PROBABILITY * (levelIndex + 1)
if (Math.random() > allyCallProbability) {
return []
}
// 找到NPC的盟友同样敌对玩家的NPC
const allyMissions: FleetMission[] = []
const attackRecord = npc.attackedBy?.[player.id]
if (!attackRecord?.planetId) {
return []
}
const targetPlanet = allPlanets.find(p => p.id === attackRecord.planetId)
if (!targetPlanet) {
return []
}
for (const allyNpc of allNpcs) {
if (allyNpc.id === npc.id) continue // 跳过自己
// 检查是否是盟友(也敌对玩家)
const allyRelation = allyNpc.relations?.[player.id]
if (!allyRelation || allyRelation.status !== RelationStatus.Hostile) {
continue
}
// 检查NPC之间是否有盟友关系
const interNpcRelation = allyNpc.allies?.includes(npc.id) || npc.allies?.includes(allyNpc.id)
if (!interNpcRelation) {
// 即使不是正式盟友敌对同一玩家的NPC也有概率响应
if (Math.random() > 0.3) continue
}
// 选择盟友NPC的最佳星球
const allyPlanet = selectBestNPCPlanet(allyNpc, targetPlanet.position)
if (!allyPlanet) continue
// 派出援军舰队(较小规模)
const combatShips = [ShipType.LightFighter, ShipType.HeavyFighter, ShipType.Cruiser, ShipType.Battleship]
const allyFleet: Partial<Fleet> = {}
let hasShips = false
for (const shipType of combatShips) {
const available = allyPlanet.fleet[shipType] || 0
if (available > 0) {
const sendCount = Math.max(1, Math.floor(available * 0.3)) // 援军派30%
allyFleet[shipType] = sendCount
hasShips = true
}
}
if (!hasShips) continue
// 从盟友星球扣除舰队
for (const [shipType, count] of Object.entries(allyFleet)) {
allyPlanet.fleet[shipType as ShipType] = (allyPlanet.fleet[shipType as ShipType] || 0) - (count as number)
}
// 计算飞行时间
const distance = fleetLogic.calculateDistance(allyPlanet.position, targetPlanet.position)
const allyMinSpeed = getFleetMinSpeed(allyFleet)
const flightTime = fleetLogic.calculateFlightTime(distance, allyMinSpeed)
const now = Date.now()
const mission: FleetMission = {
id: `npc-ally-revenge-${allyNpc.id}-${now}`,
playerId: allyNpc.id,
npcId: allyNpc.id,
isHostile: true,
originPlanetId: allyPlanet.id,
targetPosition: targetPlanet.position,
targetPlanetId: targetPlanet.id,
missionType: MissionType.Attack,
fleet: allyFleet,
cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 },
departureTime: now,
arrivalTime: now + flightTime * 1000,
status: 'outbound'
}
// 添加到盟友NPC任务列表
if (!allyNpc.fleetMissions) {
allyNpc.fleetMissions = []
}
allyNpc.fleetMissions.push(mission)
allyMissions.push(mission)
}
return allyMissions
}
/**
* 增强版NPC行为更新函数
* 包含围攻、骚扰、报复升级等增强行为
*/
export const updateHostileNPCBehavior = (
npc: NPC,
allNpcs: NPC[],
player: Player,
allPlanets: Planet[],
_debrisFields: Record<string, DebrisField>,
currentTime: number,
activeSiege?: SiegeCoordination
): {
harassMission?: FleetMission
siegeMissions?: FleetMission[]
revengeMission?: FleetMission
allyRevengeMissions?: FleetMission[]
newSiegeCoordination?: SiegeCoordination
} => {
const result: {
harassMission?: FleetMission
siegeMissions?: FleetMission[]
revengeMission?: FleetMission
allyRevengeMissions?: FleetMission[]
newSiegeCoordination?: SiegeCoordination
} = {}
const config = calculateDynamicBehavior(player.points)
// 检查是否是敌对NPC
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Hostile) {
return result
}
// 1. 检查报复升级(优先级最高)
if (npc.revengeTarget === player.id) {
const revengeMission = executeEscalatedRevenge(npc, player, allPlanets, config)
if (revengeMission) {
result.revengeMission = revengeMission
// 创建警告
const targetPlanet = allPlanets.find(p => p.id === revengeMission.targetPlanetId)
if (targetPlanet) {
const alert = createIncomingFleetAlert(revengeMission, npc, targetPlanet)
if (!player.incomingFleetAlerts) {
player.incomingFleetAlerts = []
}
player.incomingFleetAlerts.push(alert)
}
// 呼叫盟友助战
const allyMissions = callAlliesForRevenge(npc, allNpcs, player, allPlanets, config)
if (allyMissions.length > 0) {
result.allyRevengeMissions = allyMissions
// 为盟友任务创建警告
for (const allyMission of allyMissions) {
const targetPlanet = allPlanets.find(p => p.id === allyMission.targetPlanetId)
const allyNpc = allNpcs.find(n => n.id === allyMission.npcId)
if (targetPlanet && allyNpc) {
const alert = createIncomingFleetAlert(allyMission, allyNpc, targetPlanet)
player.incomingFleetAlerts.push(alert)
}
}
}
return result // 报复后不执行其他行为
}
}
// 2. 检查围攻(如果没有正在进行的围攻)
if (!activeSiege) {
const { canSiege, eligibleNpcs } = canInitiateSiege(allNpcs, player, currentTime)
if (canSiege && eligibleNpcs.includes(npc)) {
// 概率触发围攻
const { HOSTILE_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
if (Math.random() < HOSTILE_BEHAVIOR_CONFIG.SIEGE.BASE_PROBABILITY) {
const siegeResult = initiateSiege(eligibleNpcs, player, allPlanets)
if (siegeResult) {
result.siegeMissions = siegeResult.missions
result.newSiegeCoordination = siegeResult.coordination
// 为所有围攻任务创建警告
for (const mission of siegeResult.missions) {
const targetPlanet = allPlanets.find(p => p.id === mission.targetPlanetId)
const attackerNpc = allNpcs.find(n => n.id === mission.npcId)
if (targetPlanet && attackerNpc) {
const alert = createIncomingFleetAlert(mission, attackerNpc, targetPlanet)
if (!player.incomingFleetAlerts) {
player.incomingFleetAlerts = []
}
player.incomingFleetAlerts.push(alert)
}
}
return result // 围攻后不执行其他行为
}
}
}
}
// 3. 检查骚扰战术
if (shouldNPCHarass(npc, player, currentTime)) {
const harassmentType = selectHarassmentType()
const harassMission = createHarassmentMission(npc, player, allPlanets, harassmentType)
if (harassMission) {
result.harassMission = harassMission
// 创建警告
const targetPlanet = allPlanets.find(p => p.id === harassMission.targetPlanetId)
if (targetPlanet) {
const alert = createIncomingFleetAlert(harassMission, npc, targetPlanet)
if (!player.incomingFleetAlerts) {
player.incomingFleetAlerts = []
}
player.incomingFleetAlerts.push(alert)
}
}
}
return result
}
// ========== 中立NPC行为系统 ==========
/**
* 检查中立NPC是否应该提议贸易
*/
export const shouldNPCOfferTrade = (npc: NPC, player: Player, currentTime: number): boolean => {
const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const tradeConfig = NEUTRAL_BEHAVIOR_CONFIG.TRADE
if (!tradeConfig.ENABLED) {
return false
}
// 检查玩家当前贸易提议数量是否已达上限
const maxOffers = (tradeConfig as any).MAX_PENDING_OFFERS || 30
if ((player.tradeOffers?.length || 0) >= maxOffers) {
return false
}
// 检查外交关系
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Neutral) {
return false
}
// 检查好感度范围
if (relation.reputation < tradeConfig.MIN_REPUTATION || relation.reputation > tradeConfig.MAX_REPUTATION) {
return false
}
// 检查贸易冷却
const lastTradeTime = (npc as any).lastTradeOfferTime || 0
if (currentTime - lastTradeTime < tradeConfig.CHECK_INTERVAL * 1000) {
return false
}
// 概率判断
return Math.random() < tradeConfig.PROBABILITY
}
/**
* 创建贸易提议
*/
export const createTradeOffer = (npc: NPC, player: Player): TradeOffer | null => {
const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const tradeConfig = NEUTRAL_BEHAVIOR_CONFIG.TRADE
// 获取NPC主星球
const npcPlanet = npc.planets[0]
if (!npcPlanet) return null
// 计算玩家总产量
const playerProduction = calculatePlayerTotalProduction(player)
// 随机决定贸易量
const tradeMultiplier = tradeConfig.TRADE_AMOUNT.min + Math.random() * (tradeConfig.TRADE_AMOUNT.max - tradeConfig.TRADE_AMOUNT.min)
// 随机选择交易类型
const tradeTypes: Array<{ offer: 'metal' | 'crystal' | 'deuterium'; request: 'metal' | 'crystal' | 'deuterium'; rate: number }> = [
{ offer: 'crystal', request: 'metal', rate: tradeConfig.EXCHANGE_RATES.metalToCrystal },
{ offer: 'deuterium', request: 'metal', rate: tradeConfig.EXCHANGE_RATES.metalToDeuterium },
{ offer: 'deuterium', request: 'crystal', rate: tradeConfig.EXCHANGE_RATES.crystalToDeuterium }
]
const selectedTrade = tradeTypes[Math.floor(Math.random() * tradeTypes.length)]
if (!selectedTrade) return null
// 计算交易量基于玩家产量和NPC资源
const baseAmount = playerProduction[selectedTrade.request] * tradeMultiplier
const npcHas = Math.floor(npcPlanet.resources[selectedTrade.offer] || 0)
// 验证基础数值有效性
if (!isFinite(baseAmount) || baseAmount <= 0 || !isFinite(npcHas) || npcHas <= 0) {
return null
}
// NPC提供的资源量
const offerAmount = Math.min(Math.floor(baseAmount / selectedTrade.rate), npcHas)
if (!isFinite(offerAmount) || offerAmount <= 0) return null
// 玩家需要支付的资源量(含手续费)
const requestAmount = Math.floor(offerAmount * selectedTrade.rate * (1 + tradeConfig.FEE_PERCENTAGE / 100))
if (!isFinite(requestAmount) || requestAmount <= 0) return null
const now = Date.now()
const offer: TradeOffer = {
id: `trade-${npc.id}-${now}`,
npcId: npc.id,
npcName: npc.name,
timestamp: now,
offeredResources: {
type: selectedTrade.offer,
amount: offerAmount
},
requestedResources: {
type: selectedTrade.request,
amount: requestAmount
},
expiresAt: now + 30 * 60 * 1000, // 30分钟后过期
status: 'pending'
}
// 更新NPC的贸易时间
;(npc as any).lastTradeOfferTime = now
return offer
}
/**
* 处理贸易接受
*/
export const acceptTradeOffer = (
offer: TradeOffer,
npc: NPC,
player: Player,
playerPlanet: Planet
): { success: boolean; reason?: string } => {
const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const tradeConfig = NEUTRAL_BEHAVIOR_CONFIG.TRADE
// 检查贸易是否过期
if (Date.now() > offer.expiresAt) {
offer.status = 'expired'
return { success: false, reason: 'errors.tradeExpired' }
}
// 检查玩家是否有足够资源
const playerHas = playerPlanet.resources[offer.requestedResources.type]
if (playerHas < offer.requestedResources.amount) {
return { success: false, reason: 'errors.insufficientResources' }
}
// 获取NPC主星球
const npcPlanet = npc.planets[0]
if (!npcPlanet) {
return { success: false, reason: 'errors.npcPlanetNotFound' }
}
// 检查NPC是否还有足够资源
const npcHas = npcPlanet.resources[offer.offeredResources.type]
if (npcHas < offer.offeredResources.amount) {
return { success: false, reason: 'errors.npcInsufficientResources' }
}
// 执行交易
// 从玩家扣除
playerPlanet.resources[offer.requestedResources.type] -= offer.requestedResources.amount
// 给玩家资源
playerPlanet.resources[offer.offeredResources.type] += offer.offeredResources.amount
// 从NPC扣除
npcPlanet.resources[offer.offeredResources.type] -= offer.offeredResources.amount
// 给NPC资源
npcPlanet.resources[offer.requestedResources.type] += offer.requestedResources.amount
// 增加好感度
const relation = npc.relations?.[player.id]
if (relation) {
relation.reputation = Math.min(DIPLOMATIC_CONFIG.MAX_REPUTATION, relation.reputation + tradeConfig.REPUTATION_GAIN)
relation.lastUpdated = Date.now()
}
offer.status = 'accepted'
return { success: true }
}
/**
* 检查中立NPC是否应该摇摆态度
*/
export const shouldNPCSwingAttitude = (
npc: NPC,
player: Player,
currentTime: number
): { shouldSwing: boolean; direction?: 'friendly' | 'hostile' } => {
const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const swingConfig = NEUTRAL_BEHAVIOR_CONFIG.SWING
if (!swingConfig.ENABLED) {
return { shouldSwing: false }
}
// 检查外交关系
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Neutral) {
return { shouldSwing: false }
}
// 检查摇摆冷却
const lastSwingCheck = (npc as any).lastSwingCheck || 0
if (currentTime - lastSwingCheck < swingConfig.CHECK_INTERVAL * 1000) {
return { shouldSwing: false }
}
// 更新检查时间
;(npc as any).lastSwingCheck = currentTime
// 检查是否达到摇摆阈值
if (relation.reputation >= swingConfig.SWING_THRESHOLD.toFriendly) {
// 好感度足够高,可能变友好
const probability = swingConfig.BASE_PROBABILITY * (relation.reputation / 20)
if (Math.random() < probability) {
return { shouldSwing: true, direction: 'friendly' }
}
} else if (relation.reputation <= swingConfig.SWING_THRESHOLD.toHostile) {
// 好感度足够低,可能变敌对
const probability = swingConfig.BASE_PROBABILITY * (Math.abs(relation.reputation) / 20)
if (Math.random() < probability) {
return { shouldSwing: true, direction: 'hostile' }
}
}
return { shouldSwing: false }
}
/**
* 执行NPC态度摇摆
*/
export const executeNPCSwing = (npc: NPC, player: Player, direction: 'friendly' | 'hostile'): void => {
const relation = npc.relations?.[player.id]
if (!relation) return
if (direction === 'friendly') {
relation.status = RelationStatus.Friendly
relation.reputation = Math.max(relation.reputation, DIPLOMATIC_CONFIG.FRIENDLY_THRESHOLD + 5)
} else {
relation.status = RelationStatus.Hostile
relation.reputation = Math.min(relation.reputation, DIPLOMATIC_CONFIG.HOSTILE_THRESHOLD - 5)
}
relation.lastUpdated = Date.now()
}
/**
* 中立NPC观察附近事件并调整好感度
*/
export const observeNearbyEvents = (neutralNpc: NPC, eventNpc: NPC, player: Player, eventType: 'attack' | 'help'): void => {
const { NEUTRAL_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const observeConfig = NEUTRAL_BEHAVIOR_CONFIG.OBSERVE
if (!observeConfig.ENABLED) return
// 检查是否是中立NPC
const relation = neutralNpc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Neutral) return
// 检查是否在同一星系(如果配置要求)
if (observeConfig.SAME_GALAXY_ONLY) {
const neutralPlanet = neutralNpc.planets[0]
const eventPlanet = eventNpc.planets[0]
if (!neutralPlanet || !eventPlanet) return
if (neutralPlanet.position.galaxy !== eventPlanet.position.galaxy) return
}
// 根据事件类型调整好感度
if (eventType === 'attack') {
relation.reputation = Math.max(DIPLOMATIC_CONFIG.MIN_REPUTATION, relation.reputation + observeConfig.ATTACK_NEARBY_PENALTY)
} else if (eventType === 'help') {
relation.reputation = Math.min(DIPLOMATIC_CONFIG.MAX_REPUTATION, relation.reputation + observeConfig.HELP_NEARBY_BONUS)
}
relation.lastUpdated = Date.now()
}
// ========== 友好NPC增强行为系统 ==========
/**
* 检查友好NPC是否应该分享情报
*/
export const shouldNPCShareIntel = (npc: NPC, player: Player, currentTime: number): boolean => {
const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const intelConfig = FRIENDLY_BEHAVIOR_CONFIG.INTEL_SHARING
if (!intelConfig.ENABLED) {
return false
}
// 检查外交关系
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Friendly) {
return false
}
// 检查好感度
if (relation.reputation < intelConfig.MIN_REPUTATION) {
return false
}
// 检查分享冷却
const lastIntelTime = (npc as any).lastIntelShareTime || 0
if (currentTime - lastIntelTime < intelConfig.INTERVAL * 1000) {
return false
}
// 概率判断
return Math.random() < intelConfig.PROBABILITY
}
/**
* 选择情报类型
*/
export const selectIntelType = (): IntelType => {
const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const types = FRIENDLY_BEHAVIOR_CONFIG.INTEL_SHARING.INTEL_TYPES
const rand = Math.random()
if (rand < types.enemyFleet) {
return 'enemyFleet'
} else if (rand < types.enemyFleet + types.enemyResources) {
return 'enemyResources'
} else {
return 'enemyMovement'
}
}
/**
* 创建情报报告
*/
export const createIntelReport = (friendlyNpc: NPC, allNpcs: NPC[], player: Player): IntelReport | null => {
// 找到一个敌对NPC作为情报目标
const hostileNpcs = allNpcs.filter(n => {
const relation = n.relations?.[player.id]
return relation && relation.status === RelationStatus.Hostile
})
if (hostileNpcs.length === 0) return null
const targetNpc = hostileNpcs[Math.floor(Math.random() * hostileNpcs.length)]
if (!targetNpc) return null
const targetPlanet = targetNpc.planets[0]
if (!targetPlanet) return null
const intelType = selectIntelType()
const now = Date.now()
const report: IntelReport = {
id: `intel-${friendlyNpc.id}-${now}`,
fromNpcId: friendlyNpc.id,
fromNpcName: friendlyNpc.name,
timestamp: now,
targetNpcId: targetNpc.id,
targetNpcName: targetNpc.name,
intelType,
data: {},
read: false
}
// 根据情报类型填充数据
switch (intelType) {
case 'enemyFleet':
report.data.fleet = { ...targetPlanet.fleet }
break
case 'enemyResources':
report.data.resources = { ...targetPlanet.resources }
break
case 'enemyMovement':
// 检查NPC是否有正在进行的任务
const activeMission = targetNpc.fleetMissions?.find(m => m.status === 'outbound')
if (activeMission) {
report.data.movement = {
targetPosition: activeMission.targetPosition,
arrivalTime: activeMission.arrivalTime,
missionType: activeMission.missionType
}
} else {
// 没有活动任务,改为分享舰队信息
report.intelType = 'enemyFleet'
report.data.fleet = { ...targetPlanet.fleet }
}
break
}
// 更新NPC的情报分享时间
;(friendlyNpc as any).lastIntelShareTime = now
return report
}
/**
* 检查友好NPC是否应该邀请联合攻击
*/
export const shouldNPCInviteJointAttack = (npc: NPC, player: Player, currentTime: number): boolean => {
const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const jointConfig = FRIENDLY_BEHAVIOR_CONFIG.JOINT_ATTACK
if (!jointConfig.ENABLED) {
return false
}
// 检查外交关系
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Friendly) {
return false
}
// 检查好感度
if (relation.reputation < jointConfig.MIN_REPUTATION) {
return false
}
// 检查邀请冷却
const lastInviteTime = (npc as any).lastJointAttackInvite || 0
if (currentTime - lastInviteTime < jointConfig.INTERVAL * 1000) {
return false
}
// 概率判断
return Math.random() < jointConfig.PROBABILITY
}
/**
* 创建联合攻击邀请
*/
export const createJointAttackInvite = (friendlyNpc: NPC, allNpcs: NPC[], player: Player): JointAttackInvite | null => {
const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const jointConfig = FRIENDLY_BEHAVIOR_CONFIG.JOINT_ATTACK
// 找到合适的攻击目标
let targetNpc: NPC | null = null
if (jointConfig.PREFER_COMMON_ENEMY) {
// 优先选择共同敌人对友好NPC也敌对的NPC
const commonEnemies = allNpcs.filter(n => {
const playerRelation = n.relations?.[player.id]
const npcRelation = n.relations?.[friendlyNpc.id]
return playerRelation?.status === RelationStatus.Hostile && npcRelation?.status === RelationStatus.Hostile
})
if (commonEnemies.length > 0) {
targetNpc = commonEnemies[Math.floor(Math.random() * commonEnemies.length)] || null
}
}
// 如果没有共同敌人选择任意敌对NPC
if (!targetNpc) {
const hostileNpcs = allNpcs.filter(n => {
const relation = n.relations?.[player.id]
return relation && relation.status === RelationStatus.Hostile
})
if (hostileNpcs.length > 0) {
targetNpc = hostileNpcs[Math.floor(Math.random() * hostileNpcs.length)] || null
}
}
if (!targetNpc) return null
const targetPlanet = targetNpc.planets[0]
if (!targetPlanet) return null
// 计算NPC承诺的舰队
const npcPlanet = friendlyNpc.planets[0]
if (!npcPlanet) return null
const fleetRatio = jointConfig.NPC_FLEET_RATIO.min + Math.random() * (jointConfig.NPC_FLEET_RATIO.max - jointConfig.NPC_FLEET_RATIO.min)
const combatShips = [
ShipType.LightFighter,
ShipType.HeavyFighter,
ShipType.Cruiser,
ShipType.Battleship,
ShipType.Bomber,
ShipType.Destroyer
]
const committedFleet: Partial<Fleet> = {}
let hasShips = false
for (const shipType of combatShips) {
const available = npcPlanet.fleet[shipType] || 0
if (available > 0) {
const commitCount = Math.floor(available * fleetRatio)
if (commitCount > 0) {
committedFleet[shipType] = commitCount
hasShips = true
}
}
}
if (!hasShips) return null
const now = Date.now()
const invite: JointAttackInvite = {
id: `joint-attack-${friendlyNpc.id}-${now}`,
fromNpcId: friendlyNpc.id,
fromNpcName: friendlyNpc.name,
timestamp: now,
targetNpcId: targetNpc.id,
targetNpcName: targetNpc.name,
targetPlanetId: targetPlanet.id,
targetPosition: targetPlanet.position,
npcFleetCommitment: committedFleet,
expectedLootRatio: jointConfig.PLAYER_LOOT_RATIO,
expiresAt: now + 2 * 60 * 60 * 1000, // 2小时后过期
status: 'pending'
}
// 更新NPC的邀请时间
;(friendlyNpc as any).lastJointAttackInvite = now
return invite
}
/**
* 检查友好NPC是否应该提供资源援助
*/
export const shouldNPCProvideAid = (npc: NPC, player: Player, currentTime: number): boolean => {
const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const aidConfig = FRIENDLY_BEHAVIOR_CONFIG.RESOURCE_AID
if (!aidConfig.ENABLED) {
return false
}
// 检查外交关系
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Friendly) {
return false
}
// 检查好感度
if (relation.reputation < aidConfig.MIN_REPUTATION) {
return false
}
// 检查援助冷却
const lastAidTime = (npc as any).lastAidTime || 0
if (currentTime - lastAidTime < aidConfig.CHECK_INTERVAL * 1000) {
return false
}
// 检查玩家是否资源紧张
const playerProduction = calculatePlayerTotalProduction(player)
const lowResourceThreshold = {
metal: playerProduction.metal * aidConfig.TRIGGER_LOW_RESOURCES_HOURS,
crystal: playerProduction.crystal * aidConfig.TRIGGER_LOW_RESOURCES_HOURS,
deuterium: playerProduction.deuterium * aidConfig.TRIGGER_LOW_RESOURCES_HOURS
}
// 检查玩家总资源
let totalPlayerResources = { metal: 0, crystal: 0, deuterium: 0 }
for (const planet of player.planets) {
totalPlayerResources.metal += planet.resources.metal
totalPlayerResources.crystal += planet.resources.crystal
totalPlayerResources.deuterium += planet.resources.deuterium
}
// 如果玩家资源充足,不援助
if (
totalPlayerResources.metal > lowResourceThreshold.metal &&
totalPlayerResources.crystal > lowResourceThreshold.crystal &&
totalPlayerResources.deuterium > lowResourceThreshold.deuterium
) {
return false
}
// 概率判断
return Math.random() < aidConfig.PROBABILITY
}
/**
* 执行资源援助
*/
export const executeResourceAid = (npc: NPC, player: Player): Resources | null => {
const { FRIENDLY_BEHAVIOR_CONFIG } = DIPLOMATIC_CONFIG
const aidConfig = FRIENDLY_BEHAVIOR_CONFIG.RESOURCE_AID
// 获取NPC主星球
const npcPlanet = npc.planets[0]
if (!npcPlanet) return null
// 计算援助量
const playerProduction = calculatePlayerTotalProduction(player)
const aidAmount = {
metal: Math.floor(playerProduction.metal * aidConfig.AID_AMOUNT_HOURS),
crystal: Math.floor(playerProduction.crystal * aidConfig.AID_AMOUNT_HOURS),
deuterium: Math.floor(playerProduction.deuterium * aidConfig.AID_AMOUNT_HOURS),
darkMatter: 0,
energy: 0
}
// 限制为NPC实际拥有的资源
aidAmount.metal = Math.min(aidAmount.metal, Math.floor(npcPlanet.resources.metal * 0.5))
aidAmount.crystal = Math.min(aidAmount.crystal, Math.floor(npcPlanet.resources.crystal * 0.5))
aidAmount.deuterium = Math.min(aidAmount.deuterium, Math.floor(npcPlanet.resources.deuterium * 0.5))
// 如果援助量太少,不援助
if (aidAmount.metal <= 0 && aidAmount.crystal <= 0 && aidAmount.deuterium <= 0) {
return null
}
// 从NPC扣除
npcPlanet.resources.metal -= aidAmount.metal
npcPlanet.resources.crystal -= aidAmount.crystal
npcPlanet.resources.deuterium -= aidAmount.deuterium
// 给玩家主星球
const playerMainPlanet = player.planets[0]
if (playerMainPlanet) {
playerMainPlanet.resources.metal += aidAmount.metal
playerMainPlanet.resources.crystal += aidAmount.crystal
playerMainPlanet.resources.deuterium += aidAmount.deuterium
}
// 更新援助时间
;(npc as any).lastAidTime = Date.now()
return aidAmount
}
/**
* 更新中立NPC行为
*/
export const updateNeutralNPCBehavior = (
npc: NPC,
_allNpcs: NPC[],
player: Player,
currentTime: number
): {
tradeOffer?: TradeOffer
swingDirection?: 'friendly' | 'hostile'
} => {
const result: {
tradeOffer?: TradeOffer
swingDirection?: 'friendly' | 'hostile'
} = {}
// 检查是否是中立NPC
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Neutral) {
return result
}
// 1. 检查贸易提议
if (shouldNPCOfferTrade(npc, player, currentTime)) {
const offer = createTradeOffer(npc, player)
if (offer) {
result.tradeOffer = offer
}
}
// 2. 检查态度摇摆
const swingResult = shouldNPCSwingAttitude(npc, player, currentTime)
if (swingResult.shouldSwing && swingResult.direction) {
executeNPCSwing(npc, player, swingResult.direction)
result.swingDirection = swingResult.direction
}
return result
}
/**
* 更新友好NPC行为
*/
export const updateFriendlyNPCBehavior = (
npc: NPC,
allNpcs: NPC[],
player: Player,
currentTime: number
): {
intelReport?: IntelReport
jointAttackInvite?: JointAttackInvite
aidProvided?: Resources
} => {
const result: {
intelReport?: IntelReport
jointAttackInvite?: JointAttackInvite
aidProvided?: Resources
} = {}
// 检查是否是友好NPC
const relation = npc.relations?.[player.id]
if (!relation || relation.status !== RelationStatus.Friendly) {
return result
}
// 1. 检查情报分享
if (shouldNPCShareIntel(npc, player, currentTime)) {
const report = createIntelReport(npc, allNpcs, player)
if (report) {
result.intelReport = report
}
}
// 2. 检查联合攻击邀请
if (shouldNPCInviteJointAttack(npc, player, currentTime)) {
const invite = createJointAttackInvite(npc, allNpcs, player)
if (invite) {
result.jointAttackInvite = invite
}
}
// 3. 检查资源援助
if (shouldNPCProvideAid(npc, player, currentTime)) {
const aid = executeResourceAid(npc, player)
if (aid) {
result.aidProvided = aid
}
}
return result
}
/**
* 综合NPC行为更新函数
* 根据NPC与玩家的关系状态调用相应的行为更新
*/
export const updateAllNPCBehaviors = (
npcs: NPC[],
player: Player,
allPlanets: Planet[],
debrisFields: Record<string, DebrisField>,
currentTime: number,
activeSiege?: SiegeCoordination
): {
hostileResults: Map<string, ReturnType<typeof updateHostileNPCBehavior>>
neutralResults: Map<string, ReturnType<typeof updateNeutralNPCBehavior>>
friendlyResults: Map<string, ReturnType<typeof updateFriendlyNPCBehavior>>
newSiegeCoordination?: SiegeCoordination
} => {
const hostileResults = new Map<string, ReturnType<typeof updateHostileNPCBehavior>>()
const neutralResults = new Map<string, ReturnType<typeof updateNeutralNPCBehavior>>()
const friendlyResults = new Map<string, ReturnType<typeof updateFriendlyNPCBehavior>>()
let newSiegeCoordination: SiegeCoordination | undefined
for (const npc of npcs) {
const relation = npc.relations?.[player.id]
if (relation?.status === RelationStatus.Hostile) {
// 敌对NPC
const result = updateHostileNPCBehavior(npc, npcs, player, allPlanets, debrisFields, currentTime, activeSiege)
hostileResults.set(npc.id, result)
if (result.newSiegeCoordination) {
newSiegeCoordination = result.newSiegeCoordination
}
} else if (relation?.status === RelationStatus.Neutral) {
// 中立NPC
const result = updateNeutralNPCBehavior(npc, npcs, player, currentTime)
neutralResults.set(npc.id, result)
} else if (relation?.status === RelationStatus.Friendly) {
// 友好NPC
const result = updateFriendlyNPCBehavior(npc, npcs, player, currentTime)
friendlyResults.set(npc.id, result)
}
}
return {
hostileResults,
neutralResults,
friendlyResults,
newSiegeCoordination
}
}
// ========== NPC联盟系统 ==========
/**
* 获取NPC之间的AI类型兼容性分数
*/
export const getAITypeCompatibility = (type1?: NPCAIType, type2?: NPCAIType): number => {
const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG
const compatibility = NPC_ALLIANCE_CONFIG.INTER_NPC_RELATIONS.TYPE_COMPATIBILITY
// 默认类型为平衡型
const t1 = type1 || NPCAIType.Balanced
const t2 = type2 || NPCAIType.Balanced
// 商人型与任何类型都友好
if (t1 === NPCAIType.Trader || t2 === NPCAIType.Trader) {
return compatibility.trader_any
}
// 侵略型与防守型互为敌人
if ((t1 === NPCAIType.Aggressive && t2 === NPCAIType.Defensive) || (t1 === NPCAIType.Defensive && t2 === NPCAIType.Aggressive)) {
return compatibility.aggressive_defensive
}
// 扩张型互相竞争
if (t1 === NPCAIType.Expansionist && t2 === NPCAIType.Expansionist) {
return compatibility.expansionist_expansionist
}
// 平衡型保持中立
if (t1 === NPCAIType.Balanced || t2 === NPCAIType.Balanced) {
return compatibility.balanced_any
}
// 默认中立
return 0
}
/**
* 初始化NPC之间的关系
*/
export const initializeInterNPCRelations = (npcs: NPC[]): void => {
const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG
if (!NPC_ALLIANCE_CONFIG.ENABLED) return
const config = NPC_ALLIANCE_CONFIG.INTER_NPC_RELATIONS
for (const npc of npcs) {
if (!npc.allies) npc.allies = []
if (!npc.enemies) npc.enemies = []
const npcPlanet = npc.planets[0]
if (!npcPlanet) continue
for (const otherNpc of npcs) {
if (npc.id === otherNpc.id) continue
const otherPlanet = otherNpc.planets[0]
if (!otherPlanet) continue
// 计算关系分数
let relationScore = 0
// 1. AI类型兼容性
const typeCompatibility = getAITypeCompatibility(npc.aiType, otherNpc.aiType)
relationScore += typeCompatibility * 50 // 将-1到1映射到-50到50
// 2. 同一星系加成
if (npcPlanet.position.galaxy === otherPlanet.position.galaxy) {
relationScore += config.SAME_GALAXY_ALLY_BONUS * 100 // 30点加成
}
// 3. 随机因素如果是mixed模式
if (config.INIT_MODE === 'mixed' || config.INIT_MODE === 'random') {
relationScore += (Math.random() - 0.5) * 40 // -20到20的随机值
}
// 根据分数决定关系
if (relationScore > 30) {
// 盟友
if (!npc.allies.includes(otherNpc.id)) {
npc.allies.push(otherNpc.id)
}
} else if (relationScore < -30) {
// 敌人
if (!npc.enemies.includes(otherNpc.id)) {
npc.enemies.push(otherNpc.id)
}
}
// 否则保持中立
}
}
}
/**
* 传播连坐效应
* 当玩家攻击/帮助NPC时影响其盟友的好感度
*/
export const propagateGuiltByAssociation = (
targetNpc: NPC,
allNpcs: NPC[],
player: Player,
eventType: 'attack' | 'help',
depth: number = 0
): void => {
const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG
const guiltConfig = NPC_ALLIANCE_CONFIG.GUILT_BY_ASSOCIATION
if (!guiltConfig.ENABLED) return
if (depth >= guiltConfig.MAX_PROPAGATION_DEPTH) return
// 获取目标NPC的盟友
const allies = targetNpc.allies || []
for (const allyId of allies) {
const allyNpc = allNpcs.find(n => n.id === allyId)
if (!allyNpc) continue
// 获取盟友与玩家的关系
const relation = allyNpc.relations?.[player.id]
if (!relation) continue
// 计算好感度变化
let reputationChange = eventType === 'attack' ? guiltConfig.ALLY_REPUTATION_PENALTY : guiltConfig.ALLY_REPUTATION_BONUS
// 如果启用强度乘数,根据传播深度减弱效果
if (guiltConfig.STRENGTH_MULTIPLIER) {
reputationChange = Math.floor(reputationChange * Math.pow(0.5, depth))
}
// 应用好感度变化
relation.reputation = Math.max(
DIPLOMATIC_CONFIG.MIN_REPUTATION,
Math.min(DIPLOMATIC_CONFIG.MAX_REPUTATION, relation.reputation + reputationChange)
)
relation.lastUpdated = Date.now()
// 更新关系状态
if (relation.reputation < DIPLOMATIC_CONFIG.HOSTILE_THRESHOLD) {
relation.status = RelationStatus.Hostile
} else if (relation.reputation > DIPLOMATIC_CONFIG.FRIENDLY_THRESHOLD) {
relation.status = RelationStatus.Friendly
} else {
relation.status = RelationStatus.Neutral
}
// 递归传播到盟友的盟友
propagateGuiltByAssociation(allyNpc, allNpcs, player, eventType, depth + 1)
}
}
/**
* NPC盟友互助当NPC被攻击时盟友可能响应
*/
export const checkAllyMutualDefense = (
attackedNpc: NPC,
attackerPlayerId: string,
allNpcs: NPC[],
allPlanets: Planet[],
currentTime: number
): FleetMission[] => {
const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG
const defenseConfig = NPC_ALLIANCE_CONFIG.MUTUAL_DEFENSE
if (!defenseConfig.ENABLED) return []
const defenseMissions: FleetMission[] = []
const allies = attackedNpc.allies || []
// 找到攻击者的星球
const attackerPlanet = allPlanets.find(p => p.ownerId === attackerPlayerId)
if (!attackerPlanet) return []
for (const allyId of allies) {
const allyNpc = allNpcs.find(n => n.id === allyId)
if (!allyNpc) continue
// 检查响应冷却
const lastDefenseTime = (allyNpc as any).lastMutualDefenseTime || 0
if (currentTime - lastDefenseTime < defenseConfig.COOLDOWN * 1000) {
continue
}
// 概率检查
if (Math.random() > defenseConfig.RESPONSE_PROBABILITY) {
continue
}
// 选择盟友最佳星球
const allyPlanet = selectBestNPCPlanet(allyNpc, attackerPlanet.position)
if (!allyPlanet) continue
// 计算响应舰队
const fleetRatio = defenseConfig.FLEET_RATIO.min + Math.random() * (defenseConfig.FLEET_RATIO.max - defenseConfig.FLEET_RATIO.min)
const combatShips = [
ShipType.LightFighter,
ShipType.HeavyFighter,
ShipType.Cruiser,
ShipType.Battleship,
ShipType.Bomber,
ShipType.Destroyer
]
const defenseFleet: Partial<Fleet> = {}
let hasShips = false
for (const shipType of combatShips) {
const available = allyPlanet.fleet[shipType] || 0
if (available > 0) {
const sendCount = Math.max(1, Math.floor(available * fleetRatio))
defenseFleet[shipType] = sendCount
hasShips = true
}
}
if (!hasShips) continue
// 从盟友星球扣除舰队
for (const [shipType, count] of Object.entries(defenseFleet)) {
allyPlanet.fleet[shipType as ShipType] = (allyPlanet.fleet[shipType as ShipType] || 0) - (count as number)
}
// 计算飞行时间
const distance = fleetLogic.calculateDistance(allyPlanet.position, attackerPlanet.position)
const defenseMinSpeed = getFleetMinSpeed(defenseFleet)
const flightTime = fleetLogic.calculateFlightTime(distance, defenseMinSpeed)
const now = Date.now()
const mission: FleetMission = {
id: `npc-mutual-defense-${allyNpc.id}-${now}`,
playerId: allyNpc.id,
npcId: allyNpc.id,
isHostile: true,
originPlanetId: allyPlanet.id,
targetPosition: attackerPlanet.position,
targetPlanetId: attackerPlanet.id,
missionType: MissionType.Attack,
fleet: defenseFleet,
cargo: { metal: 0, crystal: 0, deuterium: 0, darkMatter: 0, energy: 0 },
departureTime: now,
arrivalTime: now + flightTime * 1000,
status: 'outbound'
}
// 更新响应时间
;(allyNpc as any).lastMutualDefenseTime = now
// 添加到盟友NPC任务列表
if (!allyNpc.fleetMissions) {
allyNpc.fleetMissions = []
}
allyNpc.fleetMissions.push(mission)
defenseMissions.push(mission)
}
return defenseMissions
}
/**
* NPC之间的资源共享
*/
export const executeNPCResourceSharing = (npcs: NPC[], currentTime: number): void => {
const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG
const sharingConfig = NPC_ALLIANCE_CONFIG.RESOURCE_SHARING
if (!sharingConfig.ENABLED) return
for (const npc of npcs) {
// 检查共享冷却
const lastShareTime = (npc as any).lastResourceShareTime || 0
if (currentTime - lastShareTime < sharingConfig.INTERVAL * 1000) {
continue
}
const npcPlanet = npc.planets[0]
if (!npcPlanet) continue
const allies = npc.allies || []
if (allies.length === 0) continue
// 计算可共享资源
const shareAmount = {
metal: Math.floor(npcPlanet.resources.metal * sharingConfig.SHARE_RATIO),
crystal: Math.floor(npcPlanet.resources.crystal * sharingConfig.SHARE_RATIO),
deuterium: Math.floor(npcPlanet.resources.deuterium * sharingConfig.SHARE_RATIO)
}
// 平均分配给所有盟友
const perAlly = {
metal: Math.floor(shareAmount.metal / allies.length),
crystal: Math.floor(shareAmount.crystal / allies.length),
deuterium: Math.floor(shareAmount.deuterium / allies.length)
}
if (perAlly.metal <= 0 && perAlly.crystal <= 0 && perAlly.deuterium <= 0) {
continue
}
// 从NPC扣除
npcPlanet.resources.metal -= perAlly.metal * allies.length
npcPlanet.resources.crystal -= perAlly.crystal * allies.length
npcPlanet.resources.deuterium -= perAlly.deuterium * allies.length
// 分配给盟友
for (const allyId of allies) {
const allyNpc = npcs.find(n => n.id === allyId)
if (!allyNpc) continue
const allyPlanet = allyNpc.planets[0]
if (!allyPlanet) continue
allyPlanet.resources.metal += perAlly.metal
allyPlanet.resources.crystal += perAlly.crystal
allyPlanet.resources.deuterium += perAlly.deuterium
}
// 更新共享时间
;(npc as any).lastResourceShareTime = currentTime
}
}
/**
* 获取NPC的所有盟友信息
*/
export const getNPCAllies = (npc: NPC, allNpcs: NPC[]): NPC[] => {
const allies: NPC[] = []
const allyIds = npc.allies || []
for (const allyId of allyIds) {
const allyNpc = allNpcs.find(n => n.id === allyId)
if (allyNpc) {
allies.push(allyNpc)
}
}
return allies
}
/**
* 获取NPC的所有敌人信息
*/
export const getNPCEnemies = (npc: NPC, allNpcs: NPC[]): NPC[] => {
const enemies: NPC[] = []
const enemyIds = npc.enemies || []
for (const enemyId of enemyIds) {
const enemyNpc = allNpcs.find(n => n.id === enemyId)
if (enemyNpc) {
enemies.push(enemyNpc)
}
}
return enemies
}
/**
* 增强版NPC被攻击处理
* 包含连坐效应和盟友互助
*/
export const handleNPCAttackedWithAlliance = (
npc: NPC,
attackerId: string,
attackerPlanetId: string | undefined,
allNpcs: NPC[],
player: Player,
allPlanets: Planet[],
currentTime: number
): FleetMission[] => {
// 基础被攻击处理
handleNPCAttacked(npc, attackerId, attackerPlanetId)
// 传播连坐效应
propagateGuiltByAssociation(npc, allNpcs, player, 'attack')
// 检查盟友互助
const defenseMissions = checkAllyMutualDefense(npc, attackerId, allNpcs, allPlanets, currentTime)
return defenseMissions
}
/**
* 更新NPC联盟系统
* 在游戏循环中调用
*/
export const updateNPCAllianceSystem = (npcs: NPC[], currentTime: number): void => {
const { NPC_ALLIANCE_CONFIG } = DIPLOMATIC_CONFIG
if (!NPC_ALLIANCE_CONFIG.ENABLED) return
// 执行NPC之间的资源共享
executeNPCResourceSharing(npcs, currentTime)
}

View File

@@ -427,11 +427,11 @@ export const generateNPCResources = (npc: NPC, deltaSeconds: number, config: Dyn
const deuteriumLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
const darkMatterLevel = planet.buildings[BuildingType.DarkMatterCollector] || 0
// 简化的资源产量计算(每秒产量)
const metalProduction = 30 * metalMineLevel * Math.pow(1.1, metalMineLevel) * config.resourceGrowthRate
const crystalProduction = 20 * crystalMineLevel * Math.pow(1.1, crystalMineLevel) * config.resourceGrowthRate
const deuteriumProduction = 10 * deuteriumLevel * Math.pow(1.1, deuteriumLevel) * config.resourceGrowthRate
const darkMatterProduction = ((25 * darkMatterLevel * Math.pow(1.5, darkMatterLevel)) / 3600) * config.resourceGrowthRate
// 简化的资源产量计算(每秒产量) - 提升3倍让NPC更富有
const metalProduction = 90 * metalMineLevel * Math.pow(1.1, metalMineLevel) * config.resourceGrowthRate
const crystalProduction = 60 * crystalMineLevel * Math.pow(1.1, crystalMineLevel) * config.resourceGrowthRate
const deuteriumProduction = 30 * deuteriumLevel * Math.pow(1.1, deuteriumLevel) * config.resourceGrowthRate
const darkMatterProduction = ((75 * darkMatterLevel * Math.pow(1.5, darkMatterLevel)) / 3600) * config.resourceGrowthRate
// 应用游戏速度倍率到时间
const effectiveDeltaSeconds = deltaSeconds * gameSpeed
@@ -541,11 +541,11 @@ export const initializeNPCStartingPower = (
npc.technologies[TechnologyType.ArmourTechnology] = Math.floor(targetTechLevel * 0.7)
npc.technologies[TechnologyType.CombustionDrive] = Math.floor(targetTechLevel * 0.6)
// 给予起始资源
planet.resources.metal = 100000 * config.powerRatio
planet.resources.crystal = 50000 * config.powerRatio
planet.resources.deuterium = 20000 * config.powerRatio
planet.resources.darkMatter = 1000 * config.powerRatio
// 给予起始资源 - 大幅提升以让NPC更富有
planet.resources.metal = 500000 * config.powerRatio
planet.resources.crystal = 250000 * config.powerRatio
planet.resources.deuterium = 100000 * config.powerRatio
planet.resources.darkMatter = 5000 * config.powerRatio
// 给予起始舰队确保NPC能够立即侦查和攻击
// 使用平方根函数来平滑舰队数量增长,避免初期过于强大
@@ -586,8 +586,6 @@ export const ensureNPCSpyProbes = (npcs: NPC[]): void => {
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}`)
}
})
}
@@ -761,17 +759,32 @@ export const ensureAllNPCsAIType = (npcs: NPC[]): void => {
* 基于距离难度初始化NPC星球
* 替代旧的 initializeNPCStartingPower
*
* 建筑等级上限30
* 科技等级上限20
* 资源上限:基于仓储建筑等级计算 (10000 * 2^level)
* 舰队数量:基于船坞等级和难度等级合理计算
* 设计原则:
* 1. 只设置行星可建造的建筑(不设置月球专属建筑)
* 2. 建筑空间不超出 maxSpace 限制
* 3. 舰队仓储不超出限制(基于船坞+机库+计算机技术计算)
* 4. 建筑和科技等级合理,不是随机的,与难度成正比
*
* 空间占用(每级):
* - 金属矿/晶体矿: 1, 重氢合成器: 2, 太阳能电站: 2
* - 聚变反应堆: 4, 金属/晶体/重氢仓库: 1
* - 机器人工厂: 4, 造船厂: 5, 研究实验室: 3, 机库: 3
* - 纳米工厂: 8, 导弹发射井: 6, 地形改造器: 5
* - 暗物质收集器: 5, 暗物质储罐: 2, 地质研究站: 6, 深钻设施: 15
*
* 舰队仓储容量 = 1000 + 船坞×1000 + 机库×1500 + 计算机技术×500
*
* 舰船仓储占用:
* - 轻型战斗机: 5, 重型战斗机: 10, 巡洋舰: 15, 战列舰: 25
* - 战列巡洋舰: 20, 轰炸机: 35, 驱逐舰: 12
* - 小型运输: 10, 大型运输: 20, 殖民船: 40, 回收船: 30
* - 间谍探测器: 2, 太阳能卫星: 1, 暗物质收集船: 50, 死星: 100
*/
export const initializeNPCByDistance = (npc: NPC, homeworldPosition: { galaxy: number; system: number; position: number }): void => {
const planet = npc.planets[0]
if (!planet) return
const distance = calculateDistanceToHomeworld(planet.position, homeworldPosition)
const multipliers = calculateDistanceDifficultyMultiplier(distance)
// 保存距离和难度等级到NPC
npc.distanceToHomeworld = distance
@@ -780,88 +793,197 @@ export const initializeNPCByDistance = (npc: NPC, homeworldPosition: { galaxy: n
// 分配随机 AI 类型(如果还没有)
ensureNPCAIType(npc)
// 基础等级 * 倍率,并限制上限
const baseLevel = 5
const MAX_BUILDING_LEVEL = 30
const MAX_TECH_LEVEL = 20
const targetBuildingLevel = Math.min(MAX_BUILDING_LEVEL, Math.max(1, Math.floor(baseLevel * multipliers.buildingMultiplier)))
const targetTechLevel = Math.min(MAX_TECH_LEVEL, Math.max(1, Math.floor(baseLevel * multipliers.techMultiplier)))
// 设置资源建筑上限30
planet.buildings[BuildingType.MetalMine] = Math.min(MAX_BUILDING_LEVEL, targetBuildingLevel)
planet.buildings[BuildingType.CrystalMine] = Math.min(MAX_BUILDING_LEVEL, Math.max(1, Math.floor(targetBuildingLevel * 0.8)))
planet.buildings[BuildingType.DeuteriumSynthesizer] = Math.min(MAX_BUILDING_LEVEL, Math.max(1, Math.floor(targetBuildingLevel * 0.6)))
planet.buildings[BuildingType.SolarPlant] = Math.min(MAX_BUILDING_LEVEL, targetBuildingLevel + 2)
// 设置设施建筑
planet.buildings[BuildingType.RoboticsFactory] = Math.min(15, Math.max(1, Math.floor(targetBuildingLevel * 0.5)))
planet.buildings[BuildingType.Shipyard] = Math.min(12, Math.max(1, Math.floor(targetBuildingLevel * 0.4)))
planet.buildings[BuildingType.ResearchLab] = Math.min(12, Math.max(1, Math.floor(targetBuildingLevel * 0.4)))
// 设置仓储上限10级对应10000*2^10=10,240,000容量
const storageLevel = Math.min(10, Math.max(1, Math.floor(targetBuildingLevel * 0.3)))
planet.buildings[BuildingType.MetalStorage] = storageLevel
planet.buildings[BuildingType.CrystalStorage] = storageLevel
planet.buildings[BuildingType.DeuteriumTank] = storageLevel
// 设置科技上限20
npc.technologies[TechnologyType.EnergyTechnology] = Math.min(MAX_TECH_LEVEL, targetTechLevel)
npc.technologies[TechnologyType.ComputerTechnology] = Math.min(MAX_TECH_LEVEL, Math.max(1, Math.floor(targetTechLevel * 0.8)))
npc.technologies[TechnologyType.WeaponsTechnology] = Math.min(MAX_TECH_LEVEL, Math.max(1, Math.floor(targetTechLevel * 0.7)))
npc.technologies[TechnologyType.ShieldingTechnology] = Math.min(MAX_TECH_LEVEL, Math.max(1, Math.floor(targetTechLevel * 0.7)))
npc.technologies[TechnologyType.ArmourTechnology] = Math.min(MAX_TECH_LEVEL, Math.max(1, Math.floor(targetTechLevel * 0.7)))
npc.technologies[TechnologyType.CombustionDrive] = Math.min(MAX_TECH_LEVEL, Math.max(1, Math.floor(targetTechLevel * 0.6)))
npc.technologies[TechnologyType.EspionageTechnology] = Math.min(MAX_TECH_LEVEL, Math.max(1, Math.floor(targetTechLevel * 0.5)))
// 计算舰队仓储容量(船坞每级+1000
const shipyardLevel = planet.buildings[BuildingType.Shipyard] || 1
const fleetStorageCapacity = shipyardLevel * 1000
// 基于难度等级和船坞容量计算舰队数量
// 难度等级1-7对应不同的舰队规模
// 难度等级 1-7+,有效等级 = 难度等级 + 2
const difficultyLevel = npc.difficultyLevel || 1
const fleetScale = Math.min(1, difficultyLevel / 7) // 0.14 ~ 1.0
const effectiveLevel = difficultyLevel + 2 // 实力提升两档
// 设置舰队(基于船坞容量和难度等级)
// 总舰队数量不超过船坞容量的80%
const maxTotalFleet = Math.floor(fleetStorageCapacity * 0.8)
const baseFleetCount = Math.floor(maxTotalFleet * fleetScale)
// ========== 第一步:设置建筑等级(确保不超出空间限制)==========
// maxSpace 默认为 200需要合理分配
// 分配舰队比例
planet.fleet[ShipType.EspionageProbe] = Math.max(5, Math.floor(baseFleetCount * 0.05))
planet.fleet[ShipType.LightFighter] = Math.floor(baseFleetCount * 0.35)
planet.fleet[ShipType.HeavyFighter] = Math.floor(baseFleetCount * 0.2)
planet.fleet[ShipType.Cruiser] = Math.floor(baseFleetCount * 0.15)
planet.fleet[ShipType.Battleship] = Math.floor(baseFleetCount * 0.05)
planet.fleet[ShipType.SmallCargo] = Math.floor(baseFleetCount * 0.1)
planet.fleet[ShipType.Recycler] = Math.floor(baseFleetCount * 0.1)
// 根据effectiveLevel计算建筑等级上限
// effectiveLevel: 3,4,5,6,7,8,9 对应不同的建筑配置
// 设置防御设施(基于难度等级,合理范围
const defenseScale = difficultyLevel * 5
planet.defense[DefenseType.RocketLauncher] = Math.floor(defenseScale * 2)
planet.defense[DefenseType.LightLaser] = Math.floor(defenseScale * 1.5)
planet.defense[DefenseType.HeavyLaser] = Math.floor(defenseScale * 0.8)
planet.defense[DefenseType.GaussCannon] = Math.floor(defenseScale * 0.3)
planet.defense[DefenseType.IonCannon] = Math.floor(defenseScale * 0.3)
// 高级防御设施只有高难度NPC才有
if (difficultyLevel >= 4) {
planet.defense[DefenseType.PlasmaTurret] = Math.floor(defenseScale * 0.1)
planet.defense[DefenseType.SmallShieldDome] = 1
// 资源建筑(空间占用较低,优先配置
const metalMineLevel = Math.min(25, Math.floor(10 + effectiveLevel * 2)) // 16-28级
const crystalMineLevel = Math.min(23, Math.floor(9 + effectiveLevel * 1.8)) // 14-25级
const deutLevel = Math.min(20, Math.floor(8 + effectiveLevel * 1.5)) // 12-21级
const solarPlantLevel = Math.min(28, Math.floor(12 + effectiveLevel * 2)) // 18-30级
planet.buildings[BuildingType.MetalMine] = metalMineLevel // 空间: 16-28
planet.buildings[BuildingType.CrystalMine] = crystalMineLevel // 空间: 14-25
planet.buildings[BuildingType.DeuteriumSynthesizer] = deutLevel // 空间: 24-42
planet.buildings[BuildingType.SolarPlant] = solarPlantLevel // 空间: 36-60
// 仓储建筑空间占用低每级1
const storageLevel = Math.min(10, Math.floor(4 + effectiveLevel * 0.8)) // 6-11级
planet.buildings[BuildingType.MetalStorage] = storageLevel // 空间: 6-11
planet.buildings[BuildingType.CrystalStorage] = storageLevel // 空间: 6-11
planet.buildings[BuildingType.DeuteriumTank] = storageLevel // 空间: 6-11
// 关键设施建筑(决定舰队仓储容量)
const shipyardLevel = Math.min(16, Math.floor(6 + effectiveLevel * 1.2)) // 10-17级
const hangarLevel = Math.min(20, Math.floor(8 + effectiveLevel * 1.5)) // 12-21级
const roboticsLevel = Math.min(14, Math.floor(5 + effectiveLevel)) // 8-14级
const researchLabLevel = Math.min(14, Math.floor(5 + effectiveLevel)) // 8-14级
planet.buildings[BuildingType.Shipyard] = shipyardLevel // 空间: 50-85
planet.buildings[BuildingType.Hangar] = hangarLevel // 空间: 36-63
planet.buildings[BuildingType.RoboticsFactory] = roboticsLevel // 空间: 32-56
planet.buildings[BuildingType.ResearchLab] = researchLabLevel // 空间: 24-42
// 其他设施
planet.buildings[BuildingType.MissileSilo] = Math.min(8, effectiveLevel) // 3-9级, 空间: 18-54
planet.buildings[BuildingType.FusionReactor] = Math.min(10, effectiveLevel) // 3-9级, 空间: 12-36
// 高级设施根据effectiveLevel逐步解锁
if (effectiveLevel >= 4) {
planet.buildings[BuildingType.NaniteFactory] = Math.min(5, effectiveLevel - 3) // 1-6级, 空间: 8-48
}
if (difficultyLevel >= 6) {
planet.defense[DefenseType.LargeShieldDome] = 1
if (effectiveLevel >= 5) {
planet.buildings[BuildingType.DarkMatterCollector] = Math.min(4, effectiveLevel - 4) // 1-5级, 空间: 5-25
planet.buildings[BuildingType.DarkMatterTank] = Math.min(5, effectiveLevel - 4) // 1-5级, 空间: 2-10
planet.buildings[BuildingType.GeoResearchStation] = Math.min(3, effectiveLevel - 4) // 1-5级, 空间: 6-30
}
if (effectiveLevel >= 7) {
planet.buildings[BuildingType.Terraformer] = Math.min(3, effectiveLevel - 6) // 1-3级, 空间: 5-15
planet.buildings[BuildingType.DeepDrillingFacility] = Math.min(2, effectiveLevel - 6) // 1-3级, 空间: 15-45
}
// 设置资源(基于仓储建筑等级计算容量上限
// 容量公式10000 * 2^level
// 计算并更新maxSpace基于地形改造器
const terraformerLevel = planet.buildings[BuildingType.Terraformer] || 0
planet.maxSpace = 200 + terraformerLevel * 10 // 地形改造器每级+10空间
// ========== 第二步:设置科技等级(基于研究实验室等级)==========
// 科技等级与研究实验室等级相关,不应超过实验室等级太多
const baseTechLevel = Math.min(20, researchLabLevel + effectiveLevel) // 实验室+难度
npc.technologies[TechnologyType.EnergyTechnology] = baseTechLevel
npc.technologies[TechnologyType.ComputerTechnology] = Math.min(10, Math.floor(baseTechLevel * 0.6)) // 计算机技术上限10
npc.technologies[TechnologyType.WeaponsTechnology] = Math.floor(baseTechLevel * 0.9)
npc.technologies[TechnologyType.ShieldingTechnology] = Math.floor(baseTechLevel * 0.85)
npc.technologies[TechnologyType.ArmourTechnology] = Math.floor(baseTechLevel * 0.85)
npc.technologies[TechnologyType.CombustionDrive] = Math.floor(baseTechLevel * 0.8)
npc.technologies[TechnologyType.ImpulseDrive] = Math.floor(baseTechLevel * 0.7)
npc.technologies[TechnologyType.EspionageTechnology] = Math.floor(baseTechLevel * 0.7)
npc.technologies[TechnologyType.LaserTechnology] = Math.floor(baseTechLevel * 0.8)
npc.technologies[TechnologyType.Astrophysics] = Math.floor(baseTechLevel * 0.5)
npc.technologies[TechnologyType.IonTechnology] = Math.min(10, effectiveLevel)
npc.technologies[TechnologyType.HyperspaceTechnology] = Math.min(10, effectiveLevel)
npc.technologies[TechnologyType.HyperspaceDrive] = Math.min(8, Math.max(0, effectiveLevel - 1))
if (effectiveLevel >= 4) {
npc.technologies[TechnologyType.PlasmaTechnology] = Math.min(10, effectiveLevel - 2)
}
if (effectiveLevel >= 5) {
npc.technologies[TechnologyType.DarkMatterTechnology] = Math.min(6, effectiveLevel - 4)
npc.technologies[TechnologyType.TerraformingTechnology] = Math.min(4, effectiveLevel - 4)
}
if (effectiveLevel >= 8) {
npc.technologies[TechnologyType.GravitonTechnology] = 1
}
// ========== 第三步:计算舰队仓储容量 ==========
// 舰队仓储 = 基础1000 + 船坞×1000 + 机库×1500 + 计算机技术×500
const computerTechLevel = npc.technologies[TechnologyType.ComputerTechnology] || 0
const maxFleetStorage = 1000 + shipyardLevel * 1000 + hangarLevel * 1500 + computerTechLevel * 500
// effectiveLevel 3: 1000 + 10×1000 + 12×1500 + 6×500 = 1000+10000+18000+3000 = 32000
// effectiveLevel 9: 1000 + 17×1000 + 21×1500 + 10×500 = 1000+17000+31500+5000 = 54500
// ========== 第四步:设置舰队(确保不超出仓储限制)==========
// 先清空舰队
Object.values(ShipType).forEach(shipType => {
planet.fleet[shipType] = 0
})
// 计算每种舰船的仓储占用
const shipStorage: Record<ShipType, number> = {
[ShipType.LightFighter]: 5,
[ShipType.HeavyFighter]: 10,
[ShipType.Cruiser]: 15,
[ShipType.Battleship]: 25,
[ShipType.Battlecruiser]: 20,
[ShipType.Bomber]: 35,
[ShipType.Destroyer]: 12,
[ShipType.SmallCargo]: 10,
[ShipType.LargeCargo]: 20,
[ShipType.ColonyShip]: 40,
[ShipType.Recycler]: 30,
[ShipType.EspionageProbe]: 2,
[ShipType.SolarSatellite]: 1,
[ShipType.DarkMatterHarvester]: 50,
[ShipType.Deathstar]: 100
}
// 根据 effectiveLevel 计算舰队基数
// effectiveLevel 3: fleetMultiplier = 1, effectiveLevel 9: fleetMultiplier = 4
const fleetMultiplier = Math.pow(effectiveLevel / 3, 2) // 1, 1.78, 2.78, 4, 5.44, 7.11, 9
// 按优先级分配舰队(战斗舰优先)
let usedStorage = 0
// 辅助函数:添加舰船并追踪仓储使用
const addShips = (shipType: ShipType, baseCount: number) => {
const count = Math.floor(baseCount * fleetMultiplier)
const availableStorage = maxFleetStorage - usedStorage
const maxAffordable = Math.floor(availableStorage / shipStorage[shipType])
const actualCount = Math.min(count, maxAffordable)
if (actualCount > 0) {
planet.fleet[shipType] = actualCount
usedStorage += actualCount * shipStorage[shipType]
}
}
// 主力战斗舰队
addShips(ShipType.LightFighter, 800) // 800-7200
addShips(ShipType.HeavyFighter, 400) // 400-3600
addShips(ShipType.Cruiser, 200) // 200-1800
addShips(ShipType.Battleship, 100) // 100-900
addShips(ShipType.Battlecruiser, 80) // 80-720
addShips(ShipType.Destroyer, 60) // 60-540
addShips(ShipType.Bomber, 40) // 40-360
// 辅助舰队
addShips(ShipType.SmallCargo, 150) // 150-1350
addShips(ShipType.LargeCargo, 60) // 60-540
addShips(ShipType.Recycler, 50) // 50-450
addShips(ShipType.EspionageProbe, 100) // 100-900
addShips(ShipType.SolarSatellite, 200) // 200-1800
// 特殊舰船
if (effectiveLevel >= 4) {
addShips(ShipType.ColonyShip, 3) // 3-27
}
if (effectiveLevel >= 5) {
addShips(ShipType.DarkMatterHarvester, 10) // 10-90
}
if (effectiveLevel >= 8) {
addShips(ShipType.Deathstar, 2) // 2-18
}
// ========== 第五步:设置防御设施 ==========
// 防御不受仓储限制,使用 effectiveLevel 的平方增长
const defenseMultiplier = effectiveLevel * effectiveLevel // 9, 16, 25, 36, 49, 64, 81
planet.defense[DefenseType.RocketLauncher] = defenseMultiplier * 50 // 450-4050
planet.defense[DefenseType.LightLaser] = defenseMultiplier * 40 // 360-3240
planet.defense[DefenseType.HeavyLaser] = defenseMultiplier * 20 // 180-1620
planet.defense[DefenseType.GaussCannon] = defenseMultiplier * 8 // 72-648
planet.defense[DefenseType.IonCannon] = defenseMultiplier * 8 // 72-648
planet.defense[DefenseType.PlasmaTurret] = defenseMultiplier * 4 // 36-324
planet.defense[DefenseType.SmallShieldDome] = 1
planet.defense[DefenseType.LargeShieldDome] = effectiveLevel >= 4 ? 1 : 0
planet.defense[DefenseType.AntiBallisticMissile] = defenseMultiplier * 3 // 27-243
planet.defense[DefenseType.InterplanetaryMissile] = effectiveLevel >= 4 ? defenseMultiplier : 0 // 0-81
planet.defense[DefenseType.PlanetaryShield] = effectiveLevel >= 6 ? 1 : 0
// ========== 第六步:设置资源 ==========
// 基于仓储建筑等级计算容量上限资源填充70-100%
const metalCapacity = 10000 * Math.pow(2, planet.buildings[BuildingType.MetalStorage] || 0)
const crystalCapacity = 10000 * Math.pow(2, planet.buildings[BuildingType.CrystalStorage] || 0)
const deuteriumCapacity = 10000 * Math.pow(2, planet.buildings[BuildingType.DeuteriumTank] || 0)
const darkMatterCapacity = 1000 * Math.pow(2, planet.buildings[BuildingType.DarkMatterTank] || 0)
// 资源设置为容量的50%-80%(基于难度等级)
const resourceFillRate = 0.5 + (difficultyLevel / 7) * 0.3
const resourceFillRate = 0.7 + (difficultyLevel / 10) * 0.3
planet.resources.metal = Math.floor(metalCapacity * resourceFillRate)
planet.resources.crystal = Math.floor(crystalCapacity * resourceFillRate)
planet.resources.deuterium = Math.floor(deuteriumCapacity * resourceFillRate)
@@ -984,11 +1106,11 @@ export const generateNPCResourcesByDistance = (
const deuteriumLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
const darkMatterLevel = planet.buildings[BuildingType.DarkMatterCollector] || 0
// 简化的资源产量计算(每秒产量)
const metalProduction = 30 * metalMineLevel * Math.pow(1.1, metalMineLevel) * config.resourceGrowthRate
const crystalProduction = 20 * crystalMineLevel * Math.pow(1.1, crystalMineLevel) * config.resourceGrowthRate
const deuteriumProduction = 10 * deuteriumLevel * Math.pow(1.1, deuteriumLevel) * config.resourceGrowthRate
const darkMatterProduction = ((25 * darkMatterLevel * Math.pow(1.5, darkMatterLevel)) / 3600) * config.resourceGrowthRate
// 简化的资源产量计算(每秒产量) - 提升3倍让NPC更富有
const metalProduction = 90 * metalMineLevel * Math.pow(1.1, metalMineLevel) * config.resourceGrowthRate
const crystalProduction = 60 * crystalMineLevel * Math.pow(1.1, crystalMineLevel) * config.resourceGrowthRate
const deuteriumProduction = 30 * deuteriumLevel * Math.pow(1.1, deuteriumLevel) * config.resourceGrowthRate
const darkMatterProduction = ((75 * darkMatterLevel * Math.pow(1.5, darkMatterLevel)) / 3600) * config.resourceGrowthRate
// 应用游戏速度倍率到时间
const effectiveDeltaSeconds = deltaSeconds * gameSpeed

View File

@@ -7,10 +7,57 @@ import type { Planet, OreDeposits } from '@/types/game'
import { ORE_DEPOSIT_CONFIG } from '@/config/gameConfig'
/**
* 根据星球位置生成初始矿脉储量
* 矿脉上限加成配置
*/
export const generateOreDeposits = (position: { galaxy: number; system: number; position: number }): OreDeposits => {
const { BASE_DEPOSITS, POSITION_MULTIPLIERS, GALAXY_MULTIPLIER, RANDOM_VARIANCE } = ORE_DEPOSIT_CONFIG
export const ORE_CAPACITY_BONUS = {
// 深层钻探设施每级增加20%矿脉上限
DEEP_DRILLING_BONUS_PER_LEVEL: 0.2,
// 采矿技术每级增加15%矿脉上限(全局)
MINING_TECH_BONUS_PER_LEVEL: 0.15
}
/**
* 计算深层钻探设施的矿脉上限加成
* @param deepDrillingLevel 深层钻探设施等级
* @returns 加成倍数1 + 等级 * 0.2
*/
export const calculateDeepDrillingBonus = (deepDrillingLevel: number): number => {
return 1 + deepDrillingLevel * ORE_CAPACITY_BONUS.DEEP_DRILLING_BONUS_PER_LEVEL
}
/**
* 计算采矿技术的矿脉上限加成
* @param miningTechLevel 采矿技术等级
* @returns 加成倍数1 + 等级 * 0.15
*/
export const calculateMiningTechBonus = (miningTechLevel: number): number => {
return 1 + miningTechLevel * ORE_CAPACITY_BONUS.MINING_TECH_BONUS_PER_LEVEL
}
/**
* 根据星球位置动态计算基础储量上限(不含建筑/科技加成)
* 这样修改配置后,所有星球的上限都会自动更新
*/
export const calculateInitialDeposits = (
position:
| {
galaxy: number
system: number
position: number
}
| undefined
): { metal: number; crystal: number; deuterium: number } => {
const { BASE_DEPOSITS, POSITION_MULTIPLIERS, GALAXY_MULTIPLIER } = ORE_DEPOSIT_CONFIG
// 防御性检查如果position无效返回默认值位置8银河系1
if (!position || typeof position.position !== 'number' || typeof position.galaxy !== 'number') {
console.warn('[OreDeposits] Invalid position, using defaults:', position)
return {
metal: BASE_DEPOSITS.metal,
crystal: BASE_DEPOSITS.crystal,
deuterium: BASE_DEPOSITS.deuterium
}
}
// 位置索引 (0-14)
const posIndex = Math.max(0, Math.min(14, position.position - 1))
@@ -18,59 +65,87 @@ export const generateOreDeposits = (position: { galaxy: number; system: number;
// 银河系加成 (银河系1为基础每增加1个银河系增加5%)
const galaxyBonus = 1 + (position.galaxy - 1) * GALAXY_MULTIPLIER
// 计算每种资源的储量
const calculateDeposit = (baseAmount: number, positionMultiplier: number): number => {
// 基础储量 × 位置系数 × 银河系加成
const baseDeposit = baseAmount * positionMultiplier * galaxyBonus
// 计算每种资源的储量上限(不含随机浮动,确保一致性)
const metal = Math.floor(BASE_DEPOSITS.metal * (POSITION_MULTIPLIERS.metal[posIndex] ?? 1) * galaxyBonus)
const crystal = Math.floor(BASE_DEPOSITS.crystal * (POSITION_MULTIPLIERS.crystal[posIndex] ?? 1) * galaxyBonus)
const deuterium = Math.floor(BASE_DEPOSITS.deuterium * (POSITION_MULTIPLIERS.deuterium[posIndex] ?? 1) * galaxyBonus)
// 添加随机浮动 (±RANDOM_VARIANCE)
const variance = 1 + (Math.random() * 2 - 1) * RANDOM_VARIANCE
return Math.floor(baseDeposit * variance)
}
return { metal, crystal, deuterium }
}
const metalDeposit = calculateDeposit(BASE_DEPOSITS.metal, POSITION_MULTIPLIERS.metal[posIndex] ?? 1)
const crystalDeposit = calculateDeposit(BASE_DEPOSITS.crystal, POSITION_MULTIPLIERS.crystal[posIndex] ?? 1)
const deuteriumDeposit = calculateDeposit(BASE_DEPOSITS.deuterium, POSITION_MULTIPLIERS.deuterium[posIndex] ?? 1)
/**
* 计算带有建筑和科技加成的矿脉储量上限
* @param position 星球位置
* @param deepDrillingLevel 深层钻探设施等级(星球级)
* @param miningTechLevel 采矿技术等级(全局)
* @returns 包含加成的储量上限
*/
export const calculateEnhancedDeposits = (
position: { galaxy: number; system: number; position: number } | undefined,
deepDrillingLevel: number = 0,
miningTechLevel: number = 0
): { metal: number; crystal: number; deuterium: number; bonusMultiplier: number } => {
const baseDeposits = calculateInitialDeposits(position)
// 计算总加成倍数
const deepDrillingBonus = calculateDeepDrillingBonus(deepDrillingLevel)
const miningTechBonus = calculateMiningTechBonus(miningTechLevel)
const totalBonus = deepDrillingBonus * miningTechBonus
return {
metal: metalDeposit,
crystal: crystalDeposit,
deuterium: deuteriumDeposit,
initialMetal: metalDeposit,
initialCrystal: crystalDeposit,
initialDeuterium: deuteriumDeposit
metal: Math.floor(baseDeposits.metal * totalBonus),
crystal: Math.floor(baseDeposits.crystal * totalBonus),
deuterium: Math.floor(baseDeposits.deuterium * totalBonus),
bonusMultiplier: totalBonus
}
}
/**
* 根据星球位置生成初始矿脉储量
*/
export const generateOreDeposits = (position: { galaxy: number; system: number; position: number }): OreDeposits => {
const initialDeposits = calculateInitialDeposits(position)
return {
metal: initialDeposits.metal,
crystal: initialDeposits.crystal,
deuterium: initialDeposits.deuterium,
position: { ...position }
}
}
/**
* 计算矿脉储量对产量的效率系数
* 当储量低于衰减阈值时,产量会线性下降
* @returns 0-1 之间的效率系数
* 但即使耗尽也保留最低产量MIN_PRODUCTION_EFFICIENCY
* @returns MIN_PRODUCTION_EFFICIENCY-1 之间的效率系数
*/
export const calculateDepositEfficiency = (deposits: OreDeposits | undefined, resourceType: 'metal' | 'crystal' | 'deuterium'): number => {
if (!deposits) return 1 // 没有储量数据时返回满效率(向后兼容)
const { DECAY_START_THRESHOLD } = ORE_DEPOSIT_CONFIG
const { DECAY_START_THRESHOLD, MIN_PRODUCTION_EFFICIENCY } = ORE_DEPOSIT_CONFIG
const currentDeposit = deposits[resourceType]
const initialDeposit =
resourceType === 'metal' ? deposits.initialMetal : resourceType === 'crystal' ? deposits.initialCrystal : deposits.initialDeuterium
const initialDeposits = calculateInitialDeposits(deposits.position)
const initialDeposit = initialDeposits[resourceType]
// 如果初始储量为0返回0避免除以0
if (initialDeposit <= 0) return 0
// 如果初始储量为0返回最低效率避免除以0
if (initialDeposit <= 0) return MIN_PRODUCTION_EFFICIENCY
// 计算剩余百分比
const remainingPercentage = currentDeposit / initialDeposit
// 如果已耗尽返回0
if (currentDeposit <= 0) return 0
// 如果高于衰减阈值,返回满效率
if (remainingPercentage >= DECAY_START_THRESHOLD) return 1
// 如果已耗尽,返回最低效率(保底产量)
if (currentDeposit <= 0) return MIN_PRODUCTION_EFFICIENCY
// 在衰减阈值以下,线性衰减
// 从 DECAY_START_THRESHOLD 到 0效率从 1 降到 0
return remainingPercentage / DECAY_START_THRESHOLD
// 从 DECAY_START_THRESHOLD 到 0效率从 1 降到 MIN_PRODUCTION_EFFICIENCY
const decayRange = 1 - MIN_PRODUCTION_EFFICIENCY
const decayProgress = remainingPercentage / DECAY_START_THRESHOLD
return MIN_PRODUCTION_EFFICIENCY + decayRange * decayProgress
}
/**
@@ -94,10 +169,12 @@ export const consumeDeposit = (deposits: OreDeposits, resourceType: 'metal' | 'c
*/
export const getDepositPercentage = (deposits: OreDeposits | undefined, resourceType: 'metal' | 'crystal' | 'deuterium'): number => {
if (!deposits) return 100
// 如果没有position信息返回100%(向后兼容旧数据)
if (!deposits.position) return 100
const currentDeposit = deposits[resourceType]
const initialDeposit =
resourceType === 'metal' ? deposits.initialMetal : resourceType === 'crystal' ? deposits.initialCrystal : deposits.initialDeuterium
const initialDeposits = calculateInitialDeposits(deposits.position)
const initialDeposit = initialDeposits[resourceType]
if (initialDeposit <= 0) return 0
@@ -125,6 +202,7 @@ export const isDepositDepleted = (deposits: OreDeposits | undefined, resourceTyp
/**
* 为现有星球迁移/初始化矿脉储量
* 如果星球没有矿脉数据,则生成新的储量
* 如果有矿脉数据但缺少position则补充position信息
*/
export const migrateOreDeposits = (planet: Planet): void => {
// 月球不需要矿脉(没有采矿建筑)
@@ -132,6 +210,9 @@ export const migrateOreDeposits = (planet: Planet): void => {
if (!planet.oreDeposits) {
planet.oreDeposits = generateOreDeposits(planet.position)
} else if (!planet.oreDeposits.position) {
// 旧数据迁移补充position信息
planet.oreDeposits.position = { ...planet.position }
}
}
@@ -181,3 +262,110 @@ export const formatDepletionTime = (hours: number): string => {
return `${remainingHours}h`
}
/**
* 计算地质研究站带来的恢复速率加成
* @param geoStationLevel 地质研究站等级
* @returns 恢复速率倍数1 + 等级 * 0.5
*/
export const calculateGeoStationBonus = (geoStationLevel: number): number => {
// 每级地质研究站增加50%恢复速度
// 0级 = 1倍, 1级 = 1.5倍, 2级 = 2倍, ..., 10级 = 6倍
return 1 + geoStationLevel * 0.5
}
/**
* 恢复矿脉储量
* 矿脉会随时间缓慢恢复,模拟地质活动
* @param deposits 矿脉储量对象
* @param hoursElapsed 经过的小时数
* @param geoStationLevel 地质研究站等级(可选,影响恢复速度)
*/
export const regenerateDeposits = (deposits: OreDeposits, hoursElapsed: number, geoStationLevel: number = 0): void => {
const { REGENERATION } = ORE_DEPOSIT_CONFIG
if (!REGENERATION.ENABLED || hoursElapsed <= 0) return
// 计算地质研究站加成
const geoBonus = calculateGeoStationBonus(geoStationLevel)
// 动态计算初始储量上限
const initialDeposits = calculateInitialDeposits(deposits.position)
// 计算恢复量(基于初始储量的百分比,乘以地质研究站加成)
const regenRate = REGENERATION.RATE_PER_HOUR * hoursElapsed * geoBonus
const maxPercentage = REGENERATION.MAX_PERCENTAGE
// 恢复每种资源
const metalRegen = initialDeposits.metal * regenRate
const crystalRegen = initialDeposits.crystal * regenRate
const deuteriumRegen = initialDeposits.deuterium * regenRate
// 添加恢复量,但不超过初始储量的最大百分比
deposits.metal = Math.min(initialDeposits.metal * maxPercentage, deposits.metal + metalRegen)
deposits.crystal = Math.min(initialDeposits.crystal * maxPercentage, deposits.crystal + crystalRegen)
deposits.deuterium = Math.min(initialDeposits.deuterium * maxPercentage, deposits.deuterium + deuteriumRegen)
}
/**
* 获取矿脉恢复状态信息
* @param deposits 矿脉储量
* @param geoStationLevel 地质研究站等级(可选,影响恢复时间计算)
*/
export const getRegenerationInfo = (
deposits: OreDeposits | undefined,
geoStationLevel: number = 0
): {
metalRecovering: boolean
crystalRecovering: boolean
deuteriumRecovering: boolean
hoursToFullMetal: number
hoursToFullCrystal: number
hoursToFullDeuterium: number
geoStationBonus: number // 地质研究站加成倍数
} => {
const geoBonus = calculateGeoStationBonus(geoStationLevel)
if (!deposits) {
return {
metalRecovering: false,
crystalRecovering: false,
deuteriumRecovering: false,
hoursToFullMetal: 0,
hoursToFullCrystal: 0,
hoursToFullDeuterium: 0,
geoStationBonus: geoBonus
}
}
// 动态计算初始储量上限
const initialDeposits = calculateInitialDeposits(deposits.position)
const { REGENERATION } = ORE_DEPOSIT_CONFIG
const maxPercentage = REGENERATION.MAX_PERCENTAGE
// 实际恢复速率 = 基础速率 * 地质研究站加成
const ratePerHour = REGENERATION.RATE_PER_HOUR * geoBonus
const metalMax = initialDeposits.metal * maxPercentage
const crystalMax = initialDeposits.crystal * maxPercentage
const deuteriumMax = initialDeposits.deuterium * maxPercentage
const metalRecovering = deposits.metal < metalMax
const crystalRecovering = deposits.crystal < crystalMax
const deuteriumRecovering = deposits.deuterium < deuteriumMax
// 计算恢复到满需要的小时数(考虑地质研究站加成)
const hoursToFullMetal = metalRecovering ? (metalMax - deposits.metal) / (initialDeposits.metal * ratePerHour) : 0
const hoursToFullCrystal = crystalRecovering ? (crystalMax - deposits.crystal) / (initialDeposits.crystal * ratePerHour) : 0
const hoursToFullDeuterium = deuteriumRecovering ? (deuteriumMax - deposits.deuterium) / (initialDeposits.deuterium * ratePerHour) : 0
return {
metalRecovering,
crystalRecovering,
deuteriumRecovering,
hoursToFullMetal,
hoursToFullCrystal,
hoursToFullDeuterium,
geoStationBonus: geoBonus
}
}

View File

@@ -131,9 +131,11 @@ export const createNPCPlanet = (
isMoon: false
}
// 随机初始化建筑等级
// 初始化所有建筑等级为0
// 实际的建筑等级会在 initializeNPCByDistance 中根据距离难度系统设置
// 这里只做基础初始化,避免随机设置不合理的等级(如月球专属建筑)
Object.values(BuildingType).forEach(building => {
npcPlanet.buildings[building] = Math.floor(Math.random() * 10)
npcPlanet.buildings[building] = 0
})
// 初始化矿脉储量

View File

@@ -105,7 +105,8 @@ export const completeResearchQueue = (
if (now >= item.endTime) {
// 研究完成
const oldLevel = technologies[item.itemType as TechnologyType] || 0
const newLevel = item.targetLevel || 0
// 研究完成时,等级+1而不是直接使用targetLevel保持一致性
const newLevel = oldLevel + 1
technologies[item.itemType as TechnologyType] = newLevel
// 计算并累积积分

View File

@@ -187,6 +187,12 @@ export const updatePlanetResources = (
): void => {
const timeDiff = (now - planet.lastUpdate) / 1000 // 转换为秒
// 时间回拨保护:如果时间差为负或为零,跳过资源更新但重置时间戳
if (timeDiff <= 0) {
planet.lastUpdate = now
return
}
// 应用游戏速度到时间差(游戏速度影响资源产出速率)
const effectiveTimeDiff = timeDiff * gameSpeed
@@ -224,6 +230,12 @@ export const updatePlanetResources = (
oreDepositLogic.consumeDeposit(planet.oreDeposits, 'metal', metalProduced)
oreDepositLogic.consumeDeposit(planet.oreDeposits, 'crystal', crystalProduced)
oreDepositLogic.consumeDeposit(planet.oreDeposits, 'deuterium', deuteriumProduced)
// 矿脉缓慢恢复(每次更新时恢复一小部分)
// 地质研究站等级影响恢复速度
const hoursElapsed = effectiveTimeDiff / 3600
const geoStationLevel = planet.buildings[BuildingType.GeoResearchStation] || 0
oreDepositLogic.regenerateDeposits(planet.oreDeposits, hoursElapsed, geoStationLevel)
}
// 更新资源(转换为每秒产量,应用游戏速度)

View File

@@ -340,21 +340,40 @@ const tryExecuteBuildingWaitingItem = (
): { success: boolean; queueItem?: BuildQueueItem; message?: string } => {
const buildingType = item.itemType as BuildingType
const currentLevel = planet.buildings[buildingType] || 0
const targetLevel = item.targetLevel || currentLevel + 1
const waitingTargetLevel = item.targetLevel || currentLevel + 1
// 检查目标等级是否仍然正确(可能在等待期间已经升级了)
if (currentLevel >= targetLevel) {
if (currentLevel >= waitingTargetLevel) {
return { success: false, message: 'errors.levelAlreadyReached' }
}
// 验证升级条件(跳过队列已满检查,因为我们已经检查过了)
// 实际执行的目标等级是当前等级+1
const actualTargetLevel = currentLevel + 1
// 验证升级条件
// 跳过以下错误,因为它们不影响下一级升级:
// - buildQueueFull: 我们已经在外层检查了
// - buildingAlreadyInQueue: 同一建筑可能在队列中(等待前一级完成)
// - insufficientResources: 我们会在下面单独检查实际需要的资源
const validation = buildingValidation.validateBuildingUpgrade(planet, buildingType, technologies, officers)
if (!validation.valid && validation.reason !== 'errors.buildQueueFull') {
if (!validation.valid &&
validation.reason !== 'errors.buildQueueFull' &&
validation.reason !== 'errors.buildingAlreadyInQueue' &&
validation.reason !== 'errors.insufficientResources') {
return { success: false, message: validation.reason }
}
// 重新检查资源因为validateBuildingUpgrade可能因资源不足失败
const cost = buildingLogic.calculateBuildingCost(buildingType, targetLevel)
// 检查同一建筑是否已在正式队列中(等待前一级完成
const existingInQueue = planet.buildQueue.find(
q => (q.type === 'building' || q.type === 'demolish') && q.itemType === buildingType
)
if (existingInQueue) {
// 同一建筑已在队列中,暂时不执行,等待完成后再处理
return { success: false, message: 'errors.buildingAlreadyInQueue' }
}
// 检查实际需要的资源(下一级的成本,不是等待队列项的目标等级)
const cost = buildingLogic.calculateBuildingCost(buildingType, actualTargetLevel)
if (!resourceLogic.checkResourcesAvailable(planet.resources, cost)) {
return { success: false, message: 'errors.insufficientResources' }
}
@@ -374,10 +393,10 @@ const tryExecuteDemolishWaitingItem = (
): { success: boolean; queueItem?: BuildQueueItem; message?: string } => {
const buildingType = item.itemType as BuildingType
const currentLevel = planet.buildings[buildingType] || 0
const targetLevel = item.targetLevel || currentLevel - 1
const waitingTargetLevel = item.targetLevel || currentLevel - 1
// 检查等级是否仍然可拆除
if (currentLevel <= 0 || currentLevel <= targetLevel) {
if (currentLevel <= 0 || currentLevel <= waitingTargetLevel) {
return { success: false, message: 'errors.buildingLevelZero' }
}
@@ -387,6 +406,15 @@ const tryExecuteDemolishWaitingItem = (
return { success: false, message: validation.reason }
}
// 检查同一建筑是否已在正式队列中(等待前一次拆除完成)
const existingInQueue = planet.buildQueue.find(
q => (q.type === 'building' || q.type === 'demolish') && q.itemType === buildingType
)
if (existingInQueue) {
// 同一建筑已在队列中,暂时不执行,等待完成后再处理
return { success: false, message: 'errors.buildingAlreadyInQueue' }
}
// 执行拆除
const queueItem = buildingValidation.executeBuildingDemolish(planet, buildingType, officers)
return { success: true, queueItem, message: 'queue.movedToQueue' }
@@ -449,21 +477,38 @@ const tryExecuteResearchWaitingItem = (
): { success: boolean; queueItem?: BuildQueueItem; message?: string } => {
const techType = item.itemType as TechnologyType
const currentLevel = player.technologies[techType] || 0
const targetLevel = item.targetLevel || currentLevel + 1
const waitingTargetLevel = item.targetLevel || currentLevel + 1
// 检查目标等级是否仍然正确
if (currentLevel >= targetLevel) {
if (currentLevel >= waitingTargetLevel) {
return { success: false, message: 'errors.levelAlreadyReached' }
}
// 实际执行的目标等级是当前等级+1
const actualTargetLevel = currentLevel + 1
// 验证研究条件
// 跳过以下错误:
// - researchQueueFull: 我们已经在外层检查了
// - technologyAlreadyInQueue: 同一科技可能在队列中(等待前一级完成)
// - insufficientResources: 我们会在下面单独检查实际需要的资源
const validation = researchValidation.validateTechnologyResearch(planet, techType, player.technologies, player.researchQueue)
if (!validation.valid && validation.reason !== 'errors.researchQueueFull') {
if (!validation.valid &&
validation.reason !== 'errors.researchQueueFull' &&
validation.reason !== 'errors.technologyAlreadyInQueue' &&
validation.reason !== 'errors.insufficientResources') {
return { success: false, message: validation.reason }
}
// 重新检查资源
const cost = researchLogic.calculateTechnologyCost(techType, targetLevel)
// 检查同一科技是否已在研究队列中(等待前一级完成)
const existingInQueue = player.researchQueue.find(q => q.itemType === techType)
if (existingInQueue) {
// 同一科技已在队列中,暂时不执行,等待完成后再处理
return { success: false, message: 'errors.technologyAlreadyInQueue' }
}
// 检查实际需要的资源(下一级的成本,不是等待队列项的目标等级)
const cost = researchLogic.calculateTechnologyCost(techType, actualTargetLevel)
if (!resourceLogic.checkResourcesAvailable(planet.resources, cost)) {
return { success: false, message: 'errors.insufficientResources' }
}

View File

@@ -0,0 +1,313 @@
/**
* WebDAV 同步服务
* 支持将存档上传到 WebDAV 服务器如坚果云、Nextcloud、NAS等
*/
export interface WebDAVConfig {
serverUrl: string // WebDAV 服务器地址,如 https://dav.jianguoyun.com/dav/
username: string // 用户名
password: string // 密码或应用专用密码
basePath: string // 存档存放路径,如 /ogame-saves/
}
export interface WebDAVFile {
name: string
path: string
size: number
lastModified: Date
isDirectory: boolean
}
const STORAGE_KEY = 'ogame-webdav-config'
// 获取保存的 WebDAV 配置
export const getWebDAVConfig = (): WebDAVConfig | null => {
try {
const saved = localStorage.getItem(STORAGE_KEY)
if (saved) {
return JSON.parse(saved)
}
} catch (e) {
console.error('Failed to load WebDAV config:', e)
}
return null
}
// 保存 WebDAV 配置
export const saveWebDAVConfig = (config: WebDAVConfig): void => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(config))
}
// 清除 WebDAV 配置
export const clearWebDAVConfig = (): void => {
localStorage.removeItem(STORAGE_KEY)
}
// 构建 Authorization header
const buildAuthHeader = (config: WebDAVConfig): string => {
const credentials = btoa(`${config.username}:${config.password}`)
return `Basic ${credentials}`
}
// 规范化 URL 路径
const normalizePath = (serverUrl: string, basePath: string, fileName?: string): string => {
let url = serverUrl.replace(/\/+$/, '')
let path = basePath.replace(/^\/+/, '').replace(/\/+$/, '')
if (path) {
url = `${url}/${path}`
}
if (fileName) {
url = `${url}/${fileName}`
}
return url
}
// 测试 WebDAV 连接
export const testWebDAVConnection = async (config: WebDAVConfig): Promise<{ success: boolean; message: string }> => {
try {
const url = normalizePath(config.serverUrl, config.basePath)
const response = await fetch(url, {
method: 'PROPFIND',
headers: {
Authorization: buildAuthHeader(config),
Depth: '0',
'Content-Type': 'application/xml'
}
})
if (response.ok || response.status === 207) {
return { success: true, message: 'WebDAV 连接成功' }
}
if (response.status === 401) {
return { success: false, message: '认证失败,请检查用户名和密码' }
}
if (response.status === 404) {
// 尝试创建目录
const createResult = await createDirectory(config, config.basePath)
if (createResult) {
return { success: true, message: 'WebDAV 连接成功,已创建存档目录' }
}
return { success: false, message: '目录不存在且无法创建' }
}
return { success: false, message: `连接失败: HTTP ${response.status}` }
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
// CORS 错误的处理
if (message.includes('Failed to fetch') || message.includes('NetworkError')) {
return {
success: false,
message: '网络错误,可能是 CORS 限制。建议使用支持 CORS 的 WebDAV 服务或通过代理访问。'
}
}
return { success: false, message: `连接错误: ${message}` }
}
}
// 创建目录
const createDirectory = async (config: WebDAVConfig, path: string): Promise<boolean> => {
try {
const url = normalizePath(config.serverUrl, path)
const response = await fetch(url, {
method: 'MKCOL',
headers: {
Authorization: buildAuthHeader(config)
}
})
return response.ok || response.status === 201
} catch (error) {
console.error('Failed to create directory:', error)
return false
}
}
// 上传存档到 WebDAV
export const uploadToWebDAV = async (
config: WebDAVConfig,
data: string,
fileName?: string
): Promise<{ success: boolean; message: string; fileName?: string }> => {
try {
// 生成带时间戳的文件名
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
const actualFileName = fileName || `ogame-save-${timestamp}.json`
const url = normalizePath(config.serverUrl, config.basePath, actualFileName)
const response = await fetch(url, {
method: 'PUT',
headers: {
Authorization: buildAuthHeader(config),
'Content-Type': 'application/json'
},
body: data
})
if (response.ok || response.status === 201 || response.status === 204) {
return { success: true, message: '上传成功', fileName: actualFileName }
}
if (response.status === 401) {
return { success: false, message: '认证失败' }
}
if (response.status === 403) {
return { success: false, message: '没有写入权限' }
}
if (response.status === 507) {
return { success: false, message: '存储空间不足' }
}
return { success: false, message: `上传失败: HTTP ${response.status}` }
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return { success: false, message: `上传错误: ${message}` }
}
}
// 解析 PROPFIND XML 响应
const parsePropfindResponse = (xml: string, _basePath: string): WebDAVFile[] => {
const parser = new DOMParser()
const doc = parser.parseFromString(xml, 'application/xml')
const responses = doc.getElementsByTagNameNS('DAV:', 'response')
const files: WebDAVFile[] = []
for (let i = 0; i < responses.length; i++) {
const response = responses[i]
if (!response) continue
const href = response.getElementsByTagNameNS('DAV:', 'href')[0]?.textContent || ''
const displayName = response.getElementsByTagNameNS('DAV:', 'displayname')[0]?.textContent
const contentLength = response.getElementsByTagNameNS('DAV:', 'getcontentlength')[0]?.textContent
const lastModified = response.getElementsByTagNameNS('DAV:', 'getlastmodified')[0]?.textContent
const resourceType = response.getElementsByTagNameNS('DAV:', 'resourcetype')[0]
const isCollection = resourceType ? resourceType.getElementsByTagNameNS('DAV:', 'collection').length > 0 : false
// 解码 URL 编码的路径
const decodedHref = decodeURIComponent(href)
const fileName = displayName || decodedHref.split('/').filter(Boolean).pop() || ''
// 跳过目录本身和非 JSON 文件
if (isCollection) continue
if (!fileName.endsWith('.json')) continue
files.push({
name: fileName,
path: decodedHref,
size: parseInt(contentLength || '0', 10),
lastModified: lastModified ? new Date(lastModified) : new Date(),
isDirectory: false
})
}
// 按修改时间降序排序(最新的在前)
return files.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime())
}
// 列出 WebDAV 目录中的存档文件
export const listWebDAVFiles = async (config: WebDAVConfig): Promise<{ success: boolean; files?: WebDAVFile[]; message?: string }> => {
try {
const url = normalizePath(config.serverUrl, config.basePath)
const response = await fetch(url, {
method: 'PROPFIND',
headers: {
Authorization: buildAuthHeader(config),
Depth: '1',
'Content-Type': 'application/xml'
}
})
if (!response.ok && response.status !== 207) {
if (response.status === 401) {
return { success: false, message: '认证失败' }
}
if (response.status === 404) {
return { success: false, message: '目录不存在' }
}
return { success: false, message: `获取文件列表失败: HTTP ${response.status}` }
}
const xml = await response.text()
const files = parsePropfindResponse(xml, config.basePath)
return { success: true, files }
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return { success: false, message: `获取文件列表错误: ${message}` }
}
}
// 从 WebDAV 下载存档
export const downloadFromWebDAV = async (
config: WebDAVConfig,
fileName: string
): Promise<{ success: boolean; data?: string; message?: string }> => {
try {
const url = normalizePath(config.serverUrl, config.basePath, fileName)
const response = await fetch(url, {
method: 'GET',
headers: {
Authorization: buildAuthHeader(config)
}
})
if (!response.ok) {
if (response.status === 401) {
return { success: false, message: '认证失败' }
}
if (response.status === 404) {
return { success: false, message: '文件不存在' }
}
return { success: false, message: `下载失败: HTTP ${response.status}` }
}
const data = await response.text()
return { success: true, data }
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return { success: false, message: `下载错误: ${message}` }
}
}
// 删除 WebDAV 文件
export const deleteFromWebDAV = async (
config: WebDAVConfig,
fileName: string
): Promise<{ success: boolean; message?: string }> => {
try {
const url = normalizePath(config.serverUrl, config.basePath, fileName)
const response = await fetch(url, {
method: 'DELETE',
headers: {
Authorization: buildAuthHeader(config)
}
})
if (response.ok || response.status === 204) {
return { success: true }
}
if (response.status === 401) {
return { success: false, message: '认证失败' }
}
if (response.status === 404) {
return { success: true } // 文件不存在也视为删除成功
}
return { success: false, message: `删除失败: HTTP ${response.status}` }
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return { success: false, message: `删除错误: ${message}` }
}
}

View File

@@ -19,9 +19,9 @@ export interface OreDeposits {
metal: number // 金属矿脉剩余储量
crystal: number // 晶体矿脉剩余储量
deuterium: number // 重氢储量
initialMetal: number // 初始金属储量(用于计算百分比)
initialCrystal: number // 初始晶体储量
initialDeuterium: number // 初始重氢储量
// 存储星球位置,用于动态计算初始储量上限
// 这样修改配置后,所有星球的上限都会自动更新
position: { galaxy: number; system: number; position: number }
}
// 建筑类型
@@ -48,7 +48,9 @@ export const BuildingType = {
SensorPhalanx: 'sensorPhalanx', // 传感器阵列
JumpGate: 'jumpGate', // 跳跃门
// 特殊建筑
PlanetDestroyerFactory: 'planetDestroyerFactory' // 行星毁灭者工厂
PlanetDestroyerFactory: 'planetDestroyerFactory', // 行星毁灭者工厂
GeoResearchStation: 'geoResearchStation', // 地质研究站(影响矿脉恢复速度)
DeepDrillingFacility: 'deepDrillingFacility' // 深层钻探设施(提升矿脉上限)
} as const
export type BuildingType = (typeof BuildingType)[keyof typeof BuildingType]
@@ -95,7 +97,8 @@ export const TechnologyType = {
GravitonTechnology: 'gravitonTechnology', // 引力技术
DarkMatterTechnology: 'darkMatterTechnology', // 暗物质技术
TerraformingTechnology: 'terraformingTechnology', // 地形改造技术
PlanetDestructionTech: 'planetDestructionTech' // 行星毁灭技术
PlanetDestructionTech: 'planetDestructionTech', // 行星毁灭技术
MiningTechnology: 'miningTechnology' // 采矿技术(提升矿脉上限)
} as const
export type TechnologyType = (typeof TechnologyType)[keyof typeof TechnologyType]
@@ -186,6 +189,9 @@ export interface ShipConfig {
fuelConsumption: number
storageUsage: number // 占用舰队仓储
requirements?: Partial<Record<BuildingType | TechnologyType, number>>
// 快速射击:对特定目标有额外攻击次数
// 例如 { lightFighter: 6 } 表示对轻型战斗机有6倍快速射击
rapidFire?: Partial<Record<ShipType | DefenseType, number>>
}
// 舰船实例
@@ -218,7 +224,8 @@ export const MissionType = {
HarvestDarkMatter: 'harvestDarkMatter', // 暗物质采集
Recycle: 'recycle', // 回收残骸
Destroy: 'destroy', // 行星毁灭
MissileAttack: 'missileAttack' // 导弹攻击
MissileAttack: 'missileAttack', // 导弹攻击
Station: 'station' // 驻守协防
} as const
export type MissionType = (typeof MissionType)[keyof typeof MissionType]
@@ -268,7 +275,8 @@ export const DiplomaticEventType = {
Spy: 'spy', // 侦查
StealDebris: 'stealDebris', // 抢夺残骸
AllyAttacked: 'allyAttacked', // 盟友被攻击
DestroyPlanet: 'destroyPlanet' // 摧毁星球
DestroyPlanet: 'destroyPlanet', // 摧毁星球
CampaignChoice: 'campaignChoice' // 战役对话选择
} as const
export type DiplomaticEventType = (typeof DiplomaticEventType)[keyof typeof DiplomaticEventType]
@@ -296,14 +304,22 @@ export interface DiplomaticReport {
npcId: string // NPC ID
npcName: string // NPC名称
eventType: DiplomaticEventType // 事件类型
reputationChange: number // 好感度变化值
newReputation: number // 新的好感度值
oldStatus: RelationStatus // 旧的关系状态
newStatus: RelationStatus // 新的关系状态
message: string // 消息内容(已弃用,保留用于兼容性)
reputationChange?: number // 好感度变化值可选ally_defend 事件没有)
newReputation?: number // 新的好感度值(可选)
oldStatus?: RelationStatus // 旧的关系状态(可选)
newStatus?: RelationStatus // 新的关系状态(可选)
message?: string // 消息内容(已弃用,保留用于兼容性)
messageKey?: string // 翻译键(如 'diplomacy.reports.youDestroyedNpcPlanet'
messageParams?: Record<string, string | number> // 翻译参数(如 { npcName: 'NPC-1', planetName: '星球 1:1:8', reputation: -80 }
read?: boolean // 已读状态
details?: {
// 协防任务详情
targetPlanetName?: string
targetPosition?: Position
fleetSize?: number
arrivalTime?: number
stationDuration?: number
}
}
// 舰队预设
@@ -526,6 +542,108 @@ export interface GiftRejectedNotification {
read?: boolean
}
// NPC贸易提议
export interface TradeOffer {
id: string
npcId: string
npcName: string
timestamp: number
offeredResources: {
type: 'metal' | 'crystal' | 'deuterium'
amount: number
}
requestedResources: {
type: 'metal' | 'crystal' | 'deuterium'
amount: number
}
expiresAt: number
status: 'pending' | 'accepted' | 'declined' | 'expired'
read?: boolean
}
// NPC情报类型
export type IntelType = 'enemyFleet' | 'enemyResources' | 'enemyMovement'
// NPC情报报告
export interface IntelReport {
id: string
fromNpcId: string
fromNpcName: string
timestamp: number
targetNpcId: string
targetNpcName: string
intelType: IntelType
data: {
fleet?: Partial<Fleet>
resources?: Partial<Resources>
movement?: {
targetPosition: { galaxy: number; system: number; position: number }
arrivalTime: number
missionType: string
}
}
read: boolean
}
// NPC联合攻击邀请
export interface JointAttackInvite {
id: string
fromNpcId: string
fromNpcName: string
timestamp: number
targetNpcId: string
targetNpcName: string
targetPlanetId: string
targetPosition: { galaxy: number; system: number; position: number }
npcFleetCommitment: Partial<Fleet>
expectedLootRatio: number // 玩家预期分成比例 (0-1)
expiresAt: number
status: 'pending' | 'accepted' | 'declined' | 'expired'
read?: boolean
}
// NPC资源援助通知
export interface AidNotification {
id: string
timestamp: number
npcId: string
npcName: string
aidResources: Resources
read?: boolean
}
// NPC报复等级
export interface RevengeLevel {
attackCount: number
fleetMultiplier: number
attackInterval: number
name: string
}
// NPC协防通知
export interface AllyDefenseNotification {
id: string
timestamp: number
npcId: string
npcName: string
targetPlanetId: string
targetPlanetName: string
fleetSent: Partial<Fleet>
read?: boolean
}
// NPC态度变化通知
export interface AttitudeChangeNotification {
id: string
timestamp: number
npcId: string
npcName: string
previousStatus: 'hostile' | 'neutral' | 'friendly'
newStatus: 'hostile' | 'neutral' | 'friendly'
reason: string // 变化原因
read?: boolean
}
// 残骸场
export interface DebrisField {
id: string
@@ -629,6 +747,20 @@ export interface Officer {
active: boolean
hiredAt?: number // 招募时间
expiresAt?: number // 到期时间
// 动态加成(与 OfficerConfig.benefits 结构相同)
bonuses?: {
buildingSpeedBonus?: number
researchSpeedBonus?: number
resourceProductionBonus?: number
darkMatterProductionBonus?: number
energyProductionBonus?: number
fleetSpeedBonus?: number
fuelConsumptionReduction?: number
defenseBonus?: number
additionalBuildQueue?: number
additionalFleetSlots?: number
storageCapacityBonus?: number
}
}
// 玩家
@@ -650,6 +782,13 @@ export interface Player {
incomingFleetAlerts: IncomingFleetAlert[] // 即将到来的敌对舰队警告
giftNotifications: GiftNotification[] // 礼物通知(等待接受/拒绝)
giftRejectedNotifications: GiftRejectedNotification[] // 礼物被拒绝通知
// NPC增强行为通知
tradeOffers?: TradeOffer[] // 中立NPC贸易提议
intelReports?: IntelReport[] // 友好NPC情报报告
jointAttackInvites?: JointAttackInvite[] // 友好NPC联合攻击邀请
aidNotifications?: AidNotification[] // 友好NPC资源援助通知
allyDefenseNotifications?: AllyDefenseNotification[] // 友好NPC协防通知
attitudeChangeNotifications?: AttitudeChangeNotification[] // NPC态度变化通知
points: number // 总积分每1000资源=1分
isGMEnabled?: boolean // GM模式开关默认false通过秘籍激活
lastVersionCheckTime?: number // 最后一次自动检查版本的时间戳(被动检测)
@@ -999,6 +1138,9 @@ export interface DialogueChoice {
textKey: string // 翻译键
nextDialogueId?: string
effect?: 'reputation_up' | 'reputation_down' | 'unlock_branch'
npcId?: string // 影响的NPC ID用于声望变化
value?: number // 效果数值如声望变化量默认为10
branchId?: string // 解锁的分支ID用于unlock_branch效果
}
// 战役任务配置
@@ -1069,6 +1211,7 @@ export interface PlayerCampaignProgress {
completedQuests: string[]
unlockedQuests: string[]
branchChoices?: Record<string, string> // 分支选择记录
unlockedBranches?: string[] // 已解锁的分支任务ID列表
}
// 任务通知

View File

@@ -10,6 +10,9 @@ interface CombatUnit {
shield: number
armor: number
rapidFire?: Record<string, number> // 快速射击
// 战斗状态追踪(用于护盾再生和装甲损坏效果)
currentShield?: number // 当前护盾值(每轮开始恢复)
armorDamage?: number // 累积装甲损坏(降低防御效果)
}
// 战斗方
@@ -55,7 +58,8 @@ const prepareCombatUnits = (side: BattleSide, isDefender: boolean = false): Comb
count: count,
attack: applyTechBonus(config.attack, side.weaponTech),
shield: applyTechBonus(config.shield, side.shieldTech),
armor: applyTechBonus(config.armor, side.armorTech)
armor: applyTechBonus(config.armor, side.armorTech),
rapidFire: config.rapidFire as Record<string, number> | undefined
})
}
}
@@ -72,6 +76,7 @@ const prepareCombatUnits = (side: BattleSide, isDefender: boolean = false): Comb
attack: applyTechBonus(config.attack, side.weaponTech),
shield: applyTechBonus(config.shield, side.shieldTech),
armor: applyTechBonus(config.armor, side.armorTech)
// 防御设施没有rapidFire
})
}
}
@@ -81,20 +86,57 @@ const prepareCombatUnits = (side: BattleSide, isDefender: boolean = false): Comb
}
/**
* 计算一个单位对另一个单位造成的伤害
* 战斗效果配置
*/
const calculateDamage = (attacker: CombatUnit, defender: CombatUnit): { destroyed: number; damagedShield: number } => {
const COMBAT_EFFECTS = {
// 护盾弹回阈值:攻击力低于护盾此比例时大概率弹回
SHIELD_BOUNCE_THRESHOLD: 0.01,
// 护盾弹回时仍有此概率穿透
SHIELD_BOUNCE_PENETRATION_CHANCE: 0.01,
// 每轮护盾再生比例(恢复最大护盾的百分比)
SHIELD_REGEN_RATE: 0.7,
// 装甲损坏累积效果:每次受到未被护盾完全吸收的伤害,累积装甲损坏
ARMOR_DAMAGE_RATE: 0.05,
// 装甲损坏上限(最多降低防御效果的百分比)
MAX_ARMOR_DAMAGE: 0.3,
// 重型武器对护盾穿透概率(攻击力>5000的武器
HEAVY_WEAPON_SHIELD_PENETRATION: 0.15,
// 重型武器阈值
HEAVY_WEAPON_THRESHOLD: 5000
}
/**
* 计算一个单位对另一个单位造成的伤害
* 增强版:包含护盾弹回、重型武器穿透、装甲损坏累积
*/
const calculateDamage = (attacker: CombatUnit, defender: CombatUnit): { destroyed: number; damagedShield: number; armorDamageDealt: number } => {
const attackPower = attacker.attack
const defenderShield = defender.shield
const defenderArmor = defender.armor
// 使用当前护盾值(如果有),否则使用最大护盾
const defenderCurrentShield = defender.currentShield ?? defender.shield
// 考虑装甲损坏后的有效装甲
const armorDamageMultiplier = 1 - (defender.armorDamage ?? 0)
const effectiveArmor = defender.armor * armorDamageMultiplier
let destroyed = 0
let damagedShield = 0
let armorDamageDealt = 0
// 如果攻击力小于护盾的1%,有很大概率无法击穿护盾
if (attackPower < defenderShield * 0.01) {
if (Math.random() > 0.01) {
return { destroyed: 0, damagedShield: 0 }
// 重型武器穿透:高攻击力武器有概率部分无视护盾
let shieldPenetration = 0
if (attackPower >= COMBAT_EFFECTS.HEAVY_WEAPON_THRESHOLD) {
if (Math.random() < COMBAT_EFFECTS.HEAVY_WEAPON_SHIELD_PENETRATION) {
// 穿透30%-50%的护盾
shieldPenetration = 0.3 + Math.random() * 0.2
}
}
// 计算实际护盾值(考虑穿透)
const effectiveShield = defenderCurrentShield * (1 - shieldPenetration)
// 如果攻击力小于护盾的1%,有很大概率无法击穿护盾(护盾弹回机制)
if (attackPower < effectiveShield * COMBAT_EFFECTS.SHIELD_BOUNCE_THRESHOLD) {
if (Math.random() > COMBAT_EFFECTS.SHIELD_BOUNCE_PENETRATION_CHANCE) {
return { destroyed: 0, damagedShield: 0, armorDamageDealt: 0 }
}
}
@@ -102,26 +144,96 @@ const calculateDamage = (attacker: CombatUnit, defender: CombatUnit): { destroye
let remainingDamage = attackPower
// 先消耗护盾
if (remainingDamage > defenderShield) {
remainingDamage -= defenderShield
damagedShield = defenderShield
if (remainingDamage > effectiveShield) {
remainingDamage -= effectiveShield
damagedShield = effectiveShield
// 更新单位的当前护盾
defender.currentShield = Math.max(0, defenderCurrentShield - damagedShield)
} else {
damagedShield = remainingDamage
return { destroyed: 0, damagedShield }
defender.currentShield = Math.max(0, defenderCurrentShield - damagedShield)
return { destroyed: 0, damagedShield, armorDamageDealt: 0 }
}
// 穿透护盾后对装甲造成损坏累积
if (remainingDamage > 0) {
armorDamageDealt = Math.min(
COMBAT_EFFECTS.ARMOR_DAMAGE_RATE,
COMBAT_EFFECTS.MAX_ARMOR_DAMAGE - (defender.armorDamage ?? 0)
)
defender.armorDamage = Math.min(
COMBAT_EFFECTS.MAX_ARMOR_DAMAGE,
(defender.armorDamage ?? 0) + armorDamageDealt
)
}
// 再消耗装甲
if (remainingDamage > defenderArmor) {
if (remainingDamage > effectiveArmor) {
destroyed = 1
} else {
// 有概率摧毁
const destroyChance = remainingDamage / defenderArmor
// 有概率摧毁(基于伤害与有效装甲的比例)
const destroyChance = remainingDamage / effectiveArmor
if (Math.random() < destroyChance) {
destroyed = 1
}
}
return { destroyed, damagedShield }
return { destroyed, damagedShield, armorDamageDealt }
}
/**
* 执行单个攻击单位的射击(包含快速射击机制)
* OGame规则如果攻击者对目标有rapidFire值N攻击后有(N-1)/N的概率再次攻击
* @param attacker 攻击单位
* @param targets 目标单位数组
* @param losses 损失记录对象
* @param isShipLoss 是否是舰船损失(用于区分舰船和防御)
*/
const executeAttack = (
attacker: CombatUnit,
targets: CombatUnit[],
shipLosses: Partial<Fleet>,
defenseLosses: Partial<Record<DefenseType, number>>
): void => {
if (targets.length === 0) return
// 随机选择一个目标
const targetIndex = Math.floor(Math.random() * targets.length)
const target = targets[targetIndex]
if (!target) return
const { destroyed } = calculateDamage(attacker, target)
if (destroyed > 0) {
target.count -= destroyed
// 记录损失
if (Object.values(ShipType).includes(target.type as ShipType)) {
const shipType = target.type as ShipType
shipLosses[shipType] = (shipLosses[shipType] || 0) + destroyed
} else {
const defenseType = target.type as DefenseType
defenseLosses[defenseType] = (defenseLosses[defenseType] || 0) + destroyed
}
// 如果目标被全部摧毁,从列表中移除
if (target.count <= 0) {
targets.splice(targetIndex, 1)
}
}
// 快速射击机制如果攻击者对目标类型有rapidFire有概率继续攻击
if (attacker.rapidFire && targets.length > 0) {
const rapidFireValue = attacker.rapidFire[target.type]
if (rapidFireValue && rapidFireValue > 1) {
// 继续攻击的概率 = (N-1)/N
const continueChance = (rapidFireValue - 1) / rapidFireValue
if (Math.random() < continueChance) {
// 递归执行下一次攻击
executeAttack(attacker, targets, shipLosses, defenseLosses)
}
}
}
}
/**
@@ -132,43 +244,17 @@ const executeRound = (attackerUnits: CombatUnit[], defenderUnits: CombatUnit[]):
const defenderShipLosses: Partial<Fleet> = {}
const defenderDefenseLosses: Partial<Record<DefenseType, number>> = {}
// 攻击方向防守方射击
// 攻击方向防守方射击(带快速射击)
for (const attacker of attackerUnits) {
for (let i = 0; i < attacker.count; i++) {
// 随机选择一个目标
if (defenderUnits.length === 0) break
const targetIndex = Math.floor(Math.random() * defenderUnits.length)
const target = defenderUnits[targetIndex]
if (!target) continue
const { destroyed } = calculateDamage(attacker, target)
if (destroyed > 0) {
target.count -= destroyed
// 记录损失
if (Object.values(ShipType).includes(target.type as ShipType)) {
const shipType = target.type as ShipType
defenderShipLosses[shipType] = (defenderShipLosses[shipType] || 0) + destroyed
} else {
const defenseType = target.type as DefenseType
defenderDefenseLosses[defenseType] = (defenderDefenseLosses[defenseType] || 0) + destroyed
}
// 如果目标被全部摧毁,从列表中移除
if (target.count <= 0) {
defenderUnits.splice(targetIndex, 1)
}
}
executeAttack(attacker, defenderUnits, defenderShipLosses, defenderDefenseLosses)
}
}
// 防守方向攻击方射击
// 防守方向攻击方射击防御设施没有rapidFire使用简化逻辑
for (const defender of defenderUnits) {
for (let i = 0; i < defender.count; i++) {
// 随机选择一个目标
if (attackerUnits.length === 0) break
const targetIndex = Math.floor(Math.random() * attackerUnits.length)
@@ -246,6 +332,27 @@ export const simulateBattle = (
let attackerUnits = prepareCombatUnits(attacker, false)
let defenderUnits = prepareCombatUnits(defender, true)
// 初始化战斗状态
const initializeCombatState = (units: CombatUnit[]) => {
for (const unit of units) {
unit.currentShield = unit.shield
unit.armorDamage = 0
}
}
initializeCombatState(attackerUnits)
initializeCombatState(defenderUnits)
// 护盾再生函数:每轮战斗后恢复部分护盾
const regenerateShields = (units: CombatUnit[]) => {
for (const unit of units) {
if (unit.currentShield !== undefined && unit.currentShield < unit.shield) {
// 恢复最大护盾的70%(但不超过最大值)
const regenAmount = unit.shield * COMBAT_EFFECTS.SHIELD_REGEN_RATE
unit.currentShield = Math.min(unit.shield, unit.currentShield + regenAmount)
}
}
}
const totalAttackerLosses: Partial<Fleet> = {}
const totalDefenderShipLosses: Partial<Fleet> = {}
const totalDefenderDefenseLosses: Partial<Record<DefenseType, number>> = {}
@@ -272,6 +379,10 @@ export const simulateBattle = (
const roundResult = executeRound(attackerUnits, defenderUnits)
// 每轮结束后护盾再生
regenerateShields(attackerUnits)
regenerateShields(defenderUnits)
// 保存当前回合详情
roundDetails.push({
round: rounds,

View File

@@ -109,15 +109,27 @@ export const migrateGameData = (): void => {
planet.temperature = generatePlanetTemperature(planet.position.position)
needsSave = true
}
// 迁移矿脉数据:确保所有矿脉都有 position 字段
if (planet.oreDeposits && !planet.isMoon) {
const deposits = planet.oreDeposits as any
// 情况1旧格式有 initialMetal需要删除并添加 position
if (deposits.initialMetal !== undefined) {
delete deposits.initialMetal
delete deposits.initialCrystal
delete deposits.initialDeuterium
needsSave = true
}
// 情况2没有 position 字段,需要添加
if (!deposits.position) {
deposits.position = { ...planet.position }
needsSave = true
}
}
})
if (needsSave) {
console.log('[Migration] Added temperature to player planets')
}
}
// NPC星球
if (oldData.npcs && Array.isArray(oldData.npcs)) {
let npcPlanetMigrated = false
oldData.npcs.forEach((npc: NPC) => {
if (npc.planets && Array.isArray(npc.planets)) {
npc.planets.forEach((planet: Planet) => {
@@ -125,14 +137,26 @@ export const migrateGameData = (): void => {
if (!planet.isMoon && !planet.temperature) {
planet.temperature = generatePlanetTemperature(planet.position.position)
needsSave = true
npcPlanetMigrated = true
}
// 迁移矿脉数据:确保所有矿脉都有 position 字段
if (planet.oreDeposits && !planet.isMoon) {
const deposits = planet.oreDeposits as any
// 情况1旧格式有 initialMetal需要删除
if (deposits.initialMetal !== undefined) {
delete deposits.initialMetal
delete deposits.initialCrystal
delete deposits.initialDeuterium
needsSave = true
}
// 情况2没有 position 字段,需要添加
if (!deposits.position) {
deposits.position = { ...planet.position }
needsSave = true
}
}
})
}
})
if (npcPlanetMigrated) {
console.log('[Migration] Added temperature to NPC planets')
}
}
// 迁移 player.diplomaticRelations 到 npc.relations
@@ -172,7 +196,6 @@ export const migrateGameData = (): void => {
// 删除旧的 diplomaticRelations 字段
delete oldData.player.diplomaticRelations
needsSave = true
console.log('[Migration] Migrated player.diplomaticRelations to npc.relations')
}
// 检查是否需要迁移地图数据
@@ -241,7 +264,6 @@ export const migrateGameData = (): void => {
if (universePlanetMigrated) {
localStorage.setItem(universeStorageKey, encryptData(universeData))
console.log('[Migration] Added temperature to universe planets')
}
} catch (error) {
console.error('[Migration] Failed to migrate universe planets temperature:', error)
@@ -251,7 +273,6 @@ export const migrateGameData = (): void => {
// 如果有任何数据被修改保存gameStore数据
if (needsSave) {
localStorage.setItem(storageKey, encryptData(oldData))
console.log('[Migration] Game data migrated successfully')
}
} catch (error) {
console.error('[Migration] Failed to migrate game data:', error)

View File

@@ -52,7 +52,7 @@
<CardContent>
<div class="space-y-3">
<div class="text-xs sm:text-sm space-y-1.5 sm:space-y-2">
<p class="text-muted-foreground mb-1 sm:mb-2">{{ t('buildingsView.upgradeCost') }}:</p>
<p class="text-muted-foreground mb-1 sm:mb-2">{{ t('buildingsView.upgradeCost') }}</p>
<div class="space-y-1 sm:space-y-1.5">
<div
v-for="resourceType in costResourceTypes"
@@ -118,13 +118,23 @@
<!-- 拆除信息提示 -->
<div v-if="getBuildingLevel(buildingType) > 0" class="text-xs text-muted-foreground">
<p>{{ t('buildingsView.demolishRefund') }}:</p>
<p>{{ t('buildingsView.demolishRefund') }}</p>
<div class="flex gap-2 flex-wrap">
<span>{{ formatNumber(getDemolishRefund(buildingType).metal) }} {{ t('resources.metal') }}</span>
<span>{{ formatNumber(getDemolishRefund(buildingType).crystal) }} {{ t('resources.crystal') }}</span>
<span>{{ formatNumber(getDemolishRefund(buildingType).deuterium) }} {{ t('resources.deuterium') }}</span>
<span v-if="getDemolishRefund(buildingType).darkMatter > 0">
{{ formatNumber(getDemolishRefund(buildingType).darkMatter) }} {{ t('resources.darkMatter') }}
<span class="flex items-center gap-1.5" v-if="getDemolishRefund(buildingType).metal">
<ResourceIcon type="metal" size="sm" />
{{ formatNumber(getDemolishRefund(buildingType).metal) }}
</span>
<span class="flex items-center gap-1.5" v-if="getDemolishRefund(buildingType).crystal">
<ResourceIcon type="crystal" size="sm" />
{{ formatNumber(getDemolishRefund(buildingType).crystal) }}
</span>
<span class="flex items-center gap-1.5" v-if="getDemolishRefund(buildingType).deuterium">
<ResourceIcon type="deuterium" size="sm" />
{{ formatNumber(getDemolishRefund(buildingType).deuterium) }}
</span>
<span class="flex items-center gap-1.5" v-if="getDemolishRefund(buildingType).darkMatter">
<ResourceIcon type="darkMatter" size="sm" />
{{ formatNumber(getDemolishRefund(buildingType).darkMatter) }}
</span>
</div>
</div>
@@ -221,6 +231,10 @@
const alertDialogRequirements = ref<Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }>>([])
const alertDialogShowRequirements = ref(false)
// 防抖状态:防止快速点击
const isProcessing = ref(false)
const DEBOUNCE_DELAY = 300 // 防抖延迟(毫秒)
// 拆除确认对话框状态
const demolishConfirmOpen = ref(false)
const demolishConfirmMessage = ref('')
@@ -276,6 +290,13 @@
// 升级建筑
const handleUpgrade = (buildingType: BuildingType, event: MouseEvent) => {
// 防抖:防止快速点击
if (isProcessing.value) return
isProcessing.value = true
setTimeout(() => {
isProcessing.value = false
}, DEBOUNCE_DELAY)
// 检查前置条件
if (!checkUpgradeRequirements(buildingType)) {
alertDialogTitle.value = t('common.requirementsNotMet')
@@ -448,12 +469,11 @@
const handleDemolish = (buildingType: BuildingType) => {
const refund = getDemolishRefund(buildingType)
demolishConfirmMessage.value = `${t('buildingsView.demolishRefund')}:
${t('resources.metal')}: ${formatNumber(refund.metal)}
${t('resources.crystal')}: ${formatNumber(refund.crystal)}
${t('resources.deuterium')}: ${formatNumber(refund.deuterium)}${
refund.darkMatter > 0 ? `\n${t('resources.darkMatter')}: ${formatNumber(refund.darkMatter)}` : ''
}`
demolishConfirmMessage.value = `${t('buildingsView.demolishRefund')}
${refund.metal ? `${t('resources.metal')}: ${formatNumber(refund.metal)}` : ''}
${refund.crystal ? `${t('resources.crystal')}: ${formatNumber(refund.crystal)}` : ''}
${refund.deuterium ? `${t('resources.deuterium')}: ${formatNumber(refund.deuterium)}` : ''}
${refund.darkMatter ? `${t('resources.darkMatter')}: ${formatNumber(refund.darkMatter)}` : ''}`
pendingDemolishBuilding.value = buildingType
demolishConfirmOpen.value = true

View File

@@ -58,11 +58,7 @@
</Card>
<!-- 任务地图 -->
<QuestMap
:quests="getChapterQuests(chapter.number)"
:progress="campaignProgress"
@select-quest="handleQuestSelect"
/>
<QuestMap :quests="getChapterQuests(chapter.number)" :progress="campaignProgress" @select-quest="handleQuestSelect" />
</TabsContent>
</Tabs>
@@ -71,12 +67,7 @@
<CardHeader>
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div
:class="[
'h-10 w-10 rounded-full flex items-center justify-center',
getQuestStatusClass(selectedQuest.id)
]"
>
<div :class="['h-10 w-10 rounded-full flex items-center justify-center', getQuestStatusClass(selectedQuest.id)]">
<component :is="getQuestStatusIcon(selectedQuest.id)" class="h-5 w-5" />
</div>
<div>
@@ -88,18 +79,11 @@
<CardDescription>{{ t(selectedQuest.descriptionKey) }}</CardDescription>
</div>
</div>
<Button
v-if="canStartQuest(selectedQuest.id)"
@click="handleStartQuest(selectedQuest.id)"
>
<Button v-if="canStartQuest(selectedQuest.id)" @click="handleStartQuest(selectedQuest.id)">
<Play class="h-4 w-4 mr-2" />
{{ t('campaign.startQuest') }}
</Button>
<Button
v-else-if="canClaimRewards(selectedQuest.id)"
@click="handleClaimRewards(selectedQuest.id)"
variant="default"
>
<Button v-else-if="canClaimRewards(selectedQuest.id)" @click="handleClaimRewards(selectedQuest.id)" variant="default">
<Gift class="h-4 w-4 mr-2" />
{{ t('campaign.claimRewards') }}
</Button>
@@ -113,16 +97,12 @@
{{ t('campaign.objectives') }}
</h4>
<div class="space-y-3">
<div
v-for="objective in selectedQuest.objectives"
:key="objective.id"
class="flex items-center gap-3"
>
<div v-for="objective in selectedQuest.objectives" :key="objective.id" class="flex items-center gap-3">
<div
:class="[
'h-6 w-6 rounded-full flex items-center justify-center text-xs',
isObjectiveCompleted(selectedQuest.id, objective.id)
? 'bg-green-500 text-white'
? 'bg-green-500 dark:bg-green-400 text-white'
: 'bg-muted text-muted-foreground'
]"
>
@@ -170,12 +150,7 @@
<Star class="h-3 w-3" />
+{{ formatNumber(selectedQuest.rewards.points) }} {{ t('common.points') }}
</Badge>
<Badge
v-for="(count, shipType) in selectedQuest.rewards.ships"
:key="shipType"
variant="outline"
class="gap-1"
>
<Badge v-for="(count, shipType) in selectedQuest.rewards.ships" :key="shipType" variant="outline" class="gap-1">
<Rocket class="h-3 w-3" />
{{ count }}x {{ getShipName(shipType) }}
</Badge>
@@ -185,270 +160,315 @@
</Card>
<!-- 剧情对话框 -->
<StoryDialog
v-if="showStoryDialog"
:dialogues="currentDialogues"
@close="handleDialogueClose"
@choice="handleDialogueChoice"
/>
<StoryDialog v-if="showStoryDialog" :dialogues="currentDialogues" @close="handleDialogueClose" @choice="handleDialogueChoice" />
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { useI18n } from '@/composables/useI18n'
import { useGameStore } from '@/stores/gameStore'
import { useNPCStore } from '@/stores/npcStore'
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'
import { Badge } from '@/components/ui/badge'
import { Progress } from '@/components/ui/progress'
import ResourceIcon from '@/components/common/ResourceIcon.vue'
import QuestMap from '@/components/campaign/QuestMap.vue'
import StoryDialog from '@/components/campaign/StoryDialog.vue'
import {
Scroll,
Check,
Play,
Gift,
Target,
Star,
Rocket,
Lock,
Circle,
CheckCircle2
} from 'lucide-vue-next'
import { formatNumber } from '@/utils/format'
import { MAIN_CAMPAIGN, getQuestsByChapter, getQuestById, getTotalQuestCount } from '@/config/campaignConfig'
import * as campaignLogic from '@/logic/campaignLogic'
import { QuestStatus, type CampaignQuestConfig, type StoryDialogue } from '@/types/game'
import { SHIPS } from '@/config/gameConfig'
import { toast } from 'vue-sonner'
import { ref, computed, onMounted, watch } from 'vue'
import { useI18n } from '@/composables/useI18n'
import { useGameStore } from '@/stores/gameStore'
import { useNPCStore } from '@/stores/npcStore'
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'
import { Badge } from '@/components/ui/badge'
import { Progress } from '@/components/ui/progress'
import ResourceIcon from '@/components/common/ResourceIcon.vue'
import QuestMap from '@/components/campaign/QuestMap.vue'
import StoryDialog from '@/components/campaign/StoryDialog.vue'
import { Scroll, Check, Play, Gift, Target, Star, Rocket, Lock, Circle, CheckCircle2 } from 'lucide-vue-next'
import { formatNumber } from '@/utils/format'
import { MAIN_CAMPAIGN, getQuestsByChapter, getQuestById, getTotalQuestCount } from '@/config/campaignConfig'
import * as campaignLogic from '@/logic/campaignLogic'
import { QuestStatus, DiplomaticEventType, type CampaignQuestConfig, type StoryDialogue, type DialogueChoice } from '@/types/game'
import { SHIPS } from '@/config/gameConfig'
import { updateReputation, getOrCreateRelation } from '@/logic/diplomaticLogic'
import { toast } from 'vue-sonner'
const { t } = useI18n()
const gameStore = useGameStore()
const npcStore = useNPCStore()
const { t } = useI18n()
const gameStore = useGameStore()
const npcStore = useNPCStore()
// 初始化战役进度
onMounted(() => {
if (!gameStore.player.campaignProgress) {
gameStore.player.campaignProgress = campaignLogic.initializeCampaignProgress(gameStore.player)
}
})
// 响应式状态
const activeChapter = ref('1')
const selectedQuestId = ref<string | null>(null)
const showStoryDialog = ref(false)
const currentDialogues = ref<StoryDialogue[]>([])
const pendingAction = ref<'start' | 'claim' | null>(null)
// 计算属性
const chapters = computed(() => MAIN_CAMPAIGN.chapters)
const campaignProgress = computed(() => gameStore.player.campaignProgress)
const currentChapter = computed(() => campaignProgress.value?.currentChapter || 1)
const totalProgress = computed(() => {
if (!campaignProgress.value) return 0
return campaignLogic.calculateCampaignProgress(campaignProgress.value)
})
const totalQuestCount = computed(() => getTotalQuestCount())
const completedQuestCount = computed(() => campaignProgress.value?.completedQuests.length || 0)
const selectedQuest = computed(() => {
if (!selectedQuestId.value) return null
return getQuestById(selectedQuestId.value)
})
// 获取章节任务
const getChapterQuests = (chapterNumber: number): CampaignQuestConfig[] => {
return getQuestsByChapter(chapterNumber)
}
// 获取章节进度
const getChapterProgress = (chapterNumber: number): number => {
if (!campaignProgress.value) return 0
return campaignLogic.calculateChapterProgress(campaignProgress.value, chapterNumber)
}
// 获取任务状态
const getQuestStatus = (questId: string): QuestStatus => {
if (!campaignProgress.value) return QuestStatus.Locked
return campaignLogic.getQuestStatus(campaignProgress.value, questId)
}
// 获取任务状态样式
const getQuestStatusClass = (questId: string): string => {
const status = getQuestStatus(questId)
switch (status) {
case QuestStatus.Completed:
return 'bg-green-500 text-white'
case QuestStatus.Active:
return 'bg-primary text-primary-foreground'
case QuestStatus.Available:
return 'bg-blue-500 text-white'
default:
return 'bg-muted text-muted-foreground'
}
}
// 获取任务状态图标
const getQuestStatusIcon = (questId: string) => {
const status = getQuestStatus(questId)
switch (status) {
case QuestStatus.Completed:
return CheckCircle2
case QuestStatus.Active:
return Circle
case QuestStatus.Available:
return Circle
default:
return Lock
}
}
// 检查是否可以开始任务
const canStartQuest = (questId: string): boolean => {
const status = getQuestStatus(questId)
return status === QuestStatus.Available
}
// 检查是否可以领取奖励
const canClaimRewards = (questId: string): boolean => {
const status = getQuestStatus(questId)
const progress = campaignProgress.value?.questProgress[questId]
return status === QuestStatus.Completed && !progress?.rewardsClaimed
}
// 检查目标是否完成
const isObjectiveCompleted = (questId: string, objectiveId: string): boolean => {
const progress = campaignProgress.value?.questProgress[questId]
return progress?.objectives[objectiveId]?.completed || false
}
// 获取目标进度
const getObjectiveProgress = (questId: string, objectiveId: string): number => {
const progress = campaignProgress.value?.questProgress[questId]
return progress?.objectives[objectiveId]?.current || 0
}
// 获取舰船名称
const getShipName = (shipType: string): string => {
const ship = SHIPS[shipType as keyof typeof SHIPS]
return ship?.name || shipType
}
// 处理任务选择
const handleQuestSelect = (questId: string) => {
selectedQuestId.value = questId
}
// 处理开始任务
const handleStartQuest = (questId: string) => {
const quest = getQuestById(questId)
// 如果有开场对话,先显示对话
if (quest?.prologueDialogues && quest.prologueDialogues.length > 0) {
currentDialogues.value = quest.prologueDialogues
pendingAction.value = 'start'
showStoryDialog.value = true
return
}
// 直接开始任务
executeStartQuest(questId)
}
// 执行开始任务
const executeStartQuest = (questId: string) => {
const result = campaignLogic.startQuest(gameStore.player, questId)
if (result.success) {
toast.success(t('campaign.notifications.questStarted'))
// 立即检查进度
campaignLogic.checkAllActiveQuestsProgress(gameStore.player, npcStore.npcs)
} else if (result.error) {
toast.error(t(result.error))
}
}
// 处理领取奖励
const handleClaimRewards = (questId: string) => {
const quest = getQuestById(questId)
// 如果有结束对话,先显示对话
if (quest?.epilogueDialogues && quest.epilogueDialogues.length > 0) {
currentDialogues.value = quest.epilogueDialogues
pendingAction.value = 'claim'
showStoryDialog.value = true
return
}
// 直接领取奖励
executeClaimRewards(questId)
}
// 执行领取奖励
const executeClaimRewards = (questId: string) => {
const result = campaignLogic.claimQuestRewards(gameStore.player, questId)
if (result.success) {
toast.success(t('campaign.notifications.rewardsClaimed'))
} else if (result.error) {
toast.error(t(result.error))
}
}
// 处理对话关闭
const handleDialogueClose = () => {
showStoryDialog.value = false
if (pendingAction.value && selectedQuestId.value) {
if (pendingAction.value === 'start') {
executeStartQuest(selectedQuestId.value)
} else if (pendingAction.value === 'claim') {
executeClaimRewards(selectedQuestId.value)
// 初始化战役进度
onMounted(() => {
if (!gameStore.player.campaignProgress) {
gameStore.player.campaignProgress = campaignLogic.initializeCampaignProgress(gameStore.player)
}
}
pendingAction.value = null
currentDialogues.value = []
}
// 处理对话选项选择
const handleDialogueChoice = (choice: { effect?: string }) => {
// TODO: 处理选择效果
console.log('Dialogue choice:', choice)
}
// 监听章节变化,自动选择第一个可用任务
watch(activeChapter, (newChapter) => {
const chapterQuests = getChapterQuests(parseInt(newChapter))
const availableQuest = chapterQuests.find(quest => {
const status = getQuestStatus(quest.id)
return status === QuestStatus.Active || status === QuestStatus.Available
})
if (availableQuest) {
selectedQuestId.value = availableQuest.id
} else {
const firstQuest = chapterQuests[0]
if (firstQuest) {
selectedQuestId.value = firstQuest.id
}
}
})
// 初始选择当前任务
onMounted(() => {
if (campaignProgress.value?.currentQuestId) {
selectedQuestId.value = campaignProgress.value.currentQuestId
const quest = getQuestById(campaignProgress.value.currentQuestId)
if (quest) {
activeChapter.value = quest.chapter.toString()
// 响应式状态
const activeChapter = ref('1')
const selectedQuestId = ref<string | null>(null)
const showStoryDialog = ref(false)
const currentDialogues = ref<StoryDialogue[]>([])
const pendingAction = ref<'start' | 'claim' | null>(null)
// 计算属性
const chapters = computed(() => MAIN_CAMPAIGN.chapters)
const campaignProgress = computed(() => gameStore.player.campaignProgress)
const currentChapter = computed(() => campaignProgress.value?.currentChapter || 1)
const totalProgress = computed(() => {
if (!campaignProgress.value) return 0
return campaignLogic.calculateCampaignProgress(campaignProgress.value)
})
const totalQuestCount = computed(() => getTotalQuestCount())
const completedQuestCount = computed(() => campaignProgress.value?.completedQuests.length || 0)
const selectedQuest = computed(() => {
if (!selectedQuestId.value) return null
return getQuestById(selectedQuestId.value)
})
// 获取章节任务
const getChapterQuests = (chapterNumber: number): CampaignQuestConfig[] => {
return getQuestsByChapter(chapterNumber)
}
// 获取章节进度
const getChapterProgress = (chapterNumber: number): number => {
if (!campaignProgress.value) return 0
return campaignLogic.calculateChapterProgress(campaignProgress.value, chapterNumber)
}
// 获取任务状态
const getQuestStatus = (questId: string): QuestStatus => {
if (!campaignProgress.value) return QuestStatus.Locked
return campaignLogic.getQuestStatus(campaignProgress.value, questId)
}
// 获取任务状态样式
const getQuestStatusClass = (questId: string): string => {
const status = getQuestStatus(questId)
switch (status) {
case QuestStatus.Completed:
return 'bg-green-500 dark:bg-green-400 text-white'
case QuestStatus.Active:
return 'bg-primary text-primary-foreground'
case QuestStatus.Available:
return 'bg-blue-500 dark:bg-blue-400 text-white'
default:
return 'bg-muted text-muted-foreground'
}
}
})
// 获取任务状态图标
const getQuestStatusIcon = (questId: string) => {
const status = getQuestStatus(questId)
switch (status) {
case QuestStatus.Completed:
return CheckCircle2
case QuestStatus.Active:
return Circle
case QuestStatus.Available:
return Circle
default:
return Lock
}
}
// 检查是否可以开始任务
const canStartQuest = (questId: string): boolean => {
const status = getQuestStatus(questId)
return status === QuestStatus.Available
}
// 检查是否可以领取奖励
const canClaimRewards = (questId: string): boolean => {
const status = getQuestStatus(questId)
const progress = campaignProgress.value?.questProgress[questId]
return status === QuestStatus.Completed && !progress?.rewardsClaimed
}
// 检查目标是否完成
const isObjectiveCompleted = (questId: string, objectiveId: string): boolean => {
const progress = campaignProgress.value?.questProgress[questId]
return progress?.objectives[objectiveId]?.completed || false
}
// 获取目标进度
const getObjectiveProgress = (questId: string, objectiveId: string): number => {
const progress = campaignProgress.value?.questProgress[questId]
return progress?.objectives[objectiveId]?.current || 0
}
// 获取舰船名称
const getShipName = (shipType: string): string => {
const ship = SHIPS[shipType as keyof typeof SHIPS]
return ship?.name || shipType
}
// 处理任务选择
const handleQuestSelect = (questId: string) => {
selectedQuestId.value = questId
}
// 处理开始任务
const handleStartQuest = (questId: string) => {
const quest = getQuestById(questId)
// 如果有开场对话,先显示对话
if (quest?.prologueDialogues && quest.prologueDialogues.length > 0) {
currentDialogues.value = quest.prologueDialogues
pendingAction.value = 'start'
showStoryDialog.value = true
return
}
// 直接开始任务
executeStartQuest(questId)
}
// 执行开始任务
const executeStartQuest = (questId: string) => {
const result = campaignLogic.startQuest(gameStore.player, questId)
if (result.success) {
toast.success(t('campaign.notifications.questStarted'))
// 立即检查进度
campaignLogic.checkAllActiveQuestsProgress(gameStore.player, npcStore.npcs)
} else if (result.error) {
toast.error(t(result.error))
}
}
// 处理领取奖励
const handleClaimRewards = (questId: string) => {
const quest = getQuestById(questId)
// 如果有结束对话,先显示对话
if (quest?.epilogueDialogues && quest.epilogueDialogues.length > 0) {
currentDialogues.value = quest.epilogueDialogues
pendingAction.value = 'claim'
showStoryDialog.value = true
return
}
// 直接领取奖励
executeClaimRewards(questId)
}
// 执行领取奖励
const executeClaimRewards = (questId: string) => {
const result = campaignLogic.claimQuestRewards(gameStore.player, questId)
if (result.success) {
toast.success(t('campaign.notifications.rewardsClaimed'))
} else if (result.error) {
toast.error(t(result.error))
}
}
// 处理对话关闭
const handleDialogueClose = () => {
showStoryDialog.value = false
if (pendingAction.value && selectedQuestId.value) {
if (pendingAction.value === 'start') {
executeStartQuest(selectedQuestId.value)
} else if (pendingAction.value === 'claim') {
executeClaimRewards(selectedQuestId.value)
}
}
pendingAction.value = null
currentDialogues.value = []
}
// 处理对话选项选择
const handleDialogueChoice = (choice: DialogueChoice) => {
if (!choice.effect) return
const DEFAULT_REPUTATION_CHANGE = 10
switch (choice.effect) {
case 'reputation_up':
case 'reputation_down': {
// 需要指定NPC ID才能修改声望
if (!choice.npcId) {
return
}
const npc = npcStore.npcs.find(n => n.id === choice.npcId)
if (!npc) {
return
}
// 确保 relations 对象存在
if (!npc.relations) {
npc.relations = {}
}
const change =
choice.effect === 'reputation_up' ? choice.value ?? DEFAULT_REPUTATION_CHANGE : -(choice.value ?? DEFAULT_REPUTATION_CHANGE)
const relation = getOrCreateRelation(npc.relations, npc.id, gameStore.player.id)
npc.relations[gameStore.player.id] = updateReputation(
relation,
change,
DiplomaticEventType.CampaignChoice,
t('campaign.dialogue.choiceEffect')
)
// 显示提示
if (change > 0) {
toast.success(t('campaign.notifications.reputationUp', { npcName: npc.name, value: change }))
} else {
toast.warning(t('campaign.notifications.reputationDown', { npcName: npc.name, value: Math.abs(change) }))
}
break
}
case 'unlock_branch': {
// 解锁分支任务
if (!choice.branchId) {
return
}
// 将分支任务ID添加到已解锁分支列表
if (!gameStore.player.campaignProgress) return
if (!gameStore.player.campaignProgress.unlockedBranches) {
gameStore.player.campaignProgress.unlockedBranches = []
}
if (!gameStore.player.campaignProgress.unlockedBranches.includes(choice.branchId)) {
gameStore.player.campaignProgress.unlockedBranches.push(choice.branchId)
toast.success(t('campaign.notifications.branchUnlocked'))
}
break
}
}
}
// 监听章节变化,自动选择第一个可用任务
watch(activeChapter, newChapter => {
const chapterQuests = getChapterQuests(parseInt(newChapter))
const availableQuest = chapterQuests.find(quest => {
const status = getQuestStatus(quest.id)
return status === QuestStatus.Active || status === QuestStatus.Available
})
if (availableQuest) {
selectedQuestId.value = availableQuest.id
} else {
const firstQuest = chapterQuests[0]
if (firstQuest) {
selectedQuestId.value = firstQuest.id
}
}
})
// 初始选择当前任务
onMounted(() => {
if (campaignProgress.value?.currentQuestId) {
selectedQuestId.value = campaignProgress.value.currentQuestId
const quest = getQuestById(campaignProgress.value.currentQuestId)
if (quest) {
activeChapter.value = quest.chapter.toString()
}
}
})
</script>

View File

@@ -217,6 +217,10 @@
const alertDialogTitle = ref('')
const alertDialogMessage = ref('')
// 防抖状态:防止快速点击
const isProcessing = ref(false)
const DEBOUNCE_DELAY = 300 // 防抖延迟(毫秒)
// 资源类型配置(用于成本显示)
const costResourceTypes = [
{ key: 'metal' as const },
@@ -262,6 +266,13 @@
// 建造防御设施
const handleBuild = (defenseType: DefenseType, event: MouseEvent) => {
// 防抖:防止快速点击
if (isProcessing.value) return
isProcessing.value = true
setTimeout(() => {
isProcessing.value = false
}, DEBOUNCE_DELAY)
const quantity = quantities.value[defenseType]
if (quantity <= 0) {
alertDialogTitle.value = t('defenseView.inputError')

View File

@@ -81,7 +81,10 @@
</div>
<div class="flex items-center gap-2">
<span class="text-muted-foreground">{{ t('diplomacy.diagnostic.aiType') }}:</span>
<span class="font-medium" :title="diagnostic.aiType ? t(`diplomacy.diagnostic.aiTypeDescriptions.${diagnostic.aiType}`) : ''">
<span
class="font-medium"
:title="diagnostic.aiType ? t(`diplomacy.diagnostic.aiTypeDescriptions.${diagnostic.aiType}`) : ''"
>
{{ diagnostic.aiType ? t(`diplomacy.diagnostic.aiTypes.${diagnostic.aiType}`) : '-' }}
</span>
</div>
@@ -178,6 +181,133 @@
</ScrollableDialogContent>
</Dialog>
<!-- NPC互动面板 - 贸易提议情报联合攻击邀请 -->
<div v-if="hasNpcInteractions" class="space-y-4">
<Collapsible v-model:open="interactionPanelOpen" class="border rounded-lg">
<CollapsibleTrigger class="flex items-center justify-between w-full p-4 hover:bg-accent/50 transition-colors">
<div class="flex items-center gap-2">
<Handshake class="h-5 w-5 text-primary" />
<span class="font-semibold">{{ t('npcBehavior.trade.title') }} & {{ t('npcBehavior.intel.title') }}</span>
<Badge variant="destructive" v-if="totalInteractionCount > 0">{{ totalInteractionCount }}</Badge>
</div>
<ChevronDown class="h-4 w-4 transition-transform" :class="{ 'rotate-180': interactionPanelOpen }" />
</CollapsibleTrigger>
<CollapsibleContent class="px-4 pb-4 space-y-4">
<!-- 贸易提议 -->
<div v-if="activeTradeOffers.length > 0">
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
<ArrowLeftRight class="h-4 w-4" />
{{ t('npcBehavior.trade.title') }} ({{ activeTradeOffers.length }})
</h3>
<div class="grid gap-2">
<Card v-for="offer in activeTradeOffers" :key="offer.id" class="p-3">
<div class="flex items-start justify-between gap-4">
<div class="flex-1 space-y-1">
<div class="font-medium">{{ getNpcName(offer.npcId) }}</div>
<div class="text-sm text-muted-foreground">
<span class="text-green-600 dark:text-green-400">{{ t('npcBehavior.trade.offers') }}:</span>
{{ formatResources(offer.offeredResources) }}
</div>
<div class="text-sm text-muted-foreground">
<span class="text-red-600 dark:text-red-400">{{ t('npcBehavior.trade.requests') }}:</span>
{{ formatResources(offer.requestedResources) }}
</div>
<div class="text-xs text-muted-foreground">
{{ t('npcBehavior.trade.expiresIn') }}: {{ formatTimeRemaining(offer.expiresAt) }}
</div>
</div>
<div class="flex gap-2">
<Button size="sm" variant="default" @click="acceptTradeOffer(offer)" :disabled="!canAcceptTrade(offer)">
{{ t('npcBehavior.trade.accept') }}
</Button>
<Button size="sm" variant="outline" @click="declineTradeOffer(offer)">
{{ t('npcBehavior.trade.decline') }}
</Button>
</div>
</div>
</Card>
</div>
</div>
<!-- 情报报告 -->
<div v-if="unreadIntelReports.length > 0">
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
<Eye class="h-4 w-4" />
{{ t('npcBehavior.intel.title') }} ({{ unreadIntelReports.length }})
</h3>
<div class="grid gap-2">
<Card v-for="intel in unreadIntelReports" :key="intel.id" class="p-3">
<div class="flex items-start justify-between gap-4">
<div class="flex-1 space-y-1">
<div class="font-medium">{{ t('npcBehavior.intel.from') }}: {{ getNpcName(intel.fromNpcId) }}</div>
<div class="text-sm text-muted-foreground">
{{ t('npcBehavior.intel.target') }}: {{ getNpcName(intel.targetNpcId) }}
</div>
<div class="text-sm">
<Badge variant="outline">{{ t(`npcBehavior.intel.types.${intel.intelType}`) }}</Badge>
</div>
<div v-if="intel.data?.fleet" class="text-sm text-muted-foreground">
{{ t('npcBehavior.intel.fleetInfo') }}: {{ formatFleetInfo(intel.data.fleet) }}
</div>
<div v-if="intel.data?.resources" class="text-sm text-muted-foreground">
{{ t('npcBehavior.intel.resourceInfo') }}: {{ formatResources(intel.data.resources as Resources) }}
</div>
</div>
<Button size="sm" variant="ghost" @click="markIntelAsRead(intel)">
{{ t('npcBehavior.intel.markAsRead') }}
</Button>
</div>
</Card>
</div>
</div>
<!-- 联合攻击邀请 -->
<div v-if="activeJointAttackInvites.length > 0">
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
<Swords class="h-4 w-4" />
{{ t('npcBehavior.jointAttack.title') }} ({{ activeJointAttackInvites.length }})
</h3>
<div class="grid gap-2">
<Card v-for="invite in activeJointAttackInvites" :key="invite.id" class="p-3">
<div class="flex items-start justify-between gap-4">
<div class="flex-1 space-y-1">
<div class="font-medium">{{ t('npcBehavior.jointAttack.from') }}: {{ getNpcName(invite.fromNpcId) }}</div>
<div class="text-sm text-muted-foreground">
{{ t('npcBehavior.jointAttack.target') }}: {{ getNpcName(invite.targetNpcId) }}
</div>
<div class="text-sm text-muted-foreground">
{{ t('npcBehavior.jointAttack.targetPlanet') }}: [{{ invite.targetPosition.galaxy }}:{{
invite.targetPosition.system
}}:{{ invite.targetPosition.position }}]
</div>
<div class="text-sm text-muted-foreground">
{{ t('npcBehavior.jointAttack.lootShare') }}: {{ (invite.expectedLootRatio * 100).toFixed(0) }}%
</div>
<div class="text-xs text-muted-foreground">
{{ t('npcBehavior.jointAttack.expiresIn') }}: {{ formatTimeRemaining(invite.expiresAt) }}
</div>
</div>
<div class="flex gap-2">
<Button size="sm" variant="default" @click="acceptJointAttack(invite)">
{{ t('npcBehavior.jointAttack.accept') }}
</Button>
<Button size="sm" variant="outline" @click="declineJointAttack(invite)">
{{ t('npcBehavior.jointAttack.decline') }}
</Button>
</div>
</div>
</Card>
</div>
</div>
<!-- 无互动内容提示 -->
<div v-if="!hasActiveInteractions" class="text-center py-4 text-muted-foreground">
{{ t('npcBehavior.trade.noOffers') }}
</div>
</CollapsibleContent>
</Collapsible>
</div>
<!-- 搜索框 -->
<div class="relative">
<Search class="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
@@ -373,11 +503,14 @@
import { useGameStore } from '@/stores/gameStore'
import { useNPCStore } from '@/stores/npcStore'
import { useI18n } from '@/composables/useI18n'
import { toast } from 'vue-sonner'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Dialog, DialogDescription, DialogTitle } from '@/components/ui/dialog'
import ScrollableDialogContent from '@/components/ui/dialog/ScrollableDialogContent.vue'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
import {
FixedPagination,
Pagination,
@@ -390,9 +523,22 @@
import NpcRelationCard from '@/components/npc/NpcRelationCard.vue'
import NpcRelationRow from '@/components/npc/NpcRelationRow.vue'
import { RelationStatus } from '@/types/game'
import type { DiplomaticRelation } from '@/types/game'
import type { DiplomaticRelation, TradeOffer, IntelReport, JointAttackInvite, Resources } from '@/types/game'
import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic'
import { Search, Users, Heart, Minus, Swords, Activity, LayoutGrid, List } from 'lucide-vue-next'
import {
Search,
Users,
Heart,
Minus,
Swords,
Activity,
LayoutGrid,
List,
Handshake,
ChevronDown,
ArrowLeftRight,
Eye
} from 'lucide-vue-next'
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
const route = useRoute()
@@ -402,6 +548,9 @@
const activeTab = ref('all')
// NPC互动面板状态
const interactionPanelOpen = ref(true)
// 视图模式: 'card' | 'list'
const viewMode = ref<'card' | 'list'>('list')
@@ -715,6 +864,188 @@
currentPage.value[activeTab.value] = val
}
})
// ========== NPC互动面板相关 ==========
// 获取当前时间戳
const now = computed(() => Date.now())
// 有效的贸易提议(未过期)
const activeTradeOffers = computed(() => {
return (gameStore.player.tradeOffers || []).filter(offer => offer.expiresAt > now.value)
})
// 未读的情报报告
const unreadIntelReports = computed(() => {
return (gameStore.player.intelReports || []).filter(report => !report.read)
})
// 有效的联合攻击邀请(未过期)
const activeJointAttackInvites = computed(() => {
return (gameStore.player.jointAttackInvites || []).filter(invite => invite.expiresAt > now.value)
})
// 是否有NPC互动数据
const hasNpcInteractions = computed(() => {
return (
(gameStore.player.tradeOffers?.length || 0) > 0 ||
(gameStore.player.intelReports?.length || 0) > 0 ||
(gameStore.player.jointAttackInvites?.length || 0) > 0
)
})
// 是否有有效的互动内容
const hasActiveInteractions = computed(() => {
return activeTradeOffers.value.length > 0 || unreadIntelReports.value.length > 0 || activeJointAttackInvites.value.length > 0
})
// 总互动数量(用于显示徽章)
const totalInteractionCount = computed(() => {
return activeTradeOffers.value.length + unreadIntelReports.value.length + activeJointAttackInvites.value.length
})
// 获取NPC名称
const getNpcName = (npcId: string): string => {
const npc = npcStore.npcs.find(n => n.id === npcId)
return npc?.name || npcId
}
// 格式化资源显示
// 格式化资源(兼容新旧格式)
const formatResources = (resources: Resources | { type: string; amount: number }): string => {
// 新格式:{ type: 'metal', amount: 1000 }
if ('type' in resources && 'amount' in resources) {
const typeLabels: Record<string, string> = {
metal: 'M',
crystal: 'C',
deuterium: 'D'
}
return `${Math.floor(resources.amount).toLocaleString()} ${typeLabels[resources.type] || resources.type}`
}
// 旧格式:{ metal: 1000, crystal: 0, deuterium: 0 }
const parts: string[] = []
if ((resources as Resources).metal > 0) parts.push(`${Math.floor((resources as Resources).metal).toLocaleString()} M`)
if ((resources as Resources).crystal > 0) parts.push(`${Math.floor((resources as Resources).crystal).toLocaleString()} C`)
if ((resources as Resources).deuterium > 0) parts.push(`${Math.floor((resources as Resources).deuterium).toLocaleString()} D`)
return parts.join(' / ') || '-'
}
// 格式化舰队信息
const formatFleetInfo = (fleetInfo: Record<string, number>): string => {
const parts: string[] = []
for (const [shipType, count] of Object.entries(fleetInfo)) {
if (count > 0) {
parts.push(`${shipType}: ${count}`)
}
}
return parts.join(', ') || '-'
}
// 格式化剩余时间
const formatTimeRemaining = (expiresAt: number): string => {
const remaining = expiresAt - now.value
if (remaining <= 0) return t('npcBehavior.trade.expired')
const minutes = Math.floor(remaining / 60000)
const hours = Math.floor(minutes / 60)
const mins = minutes % 60
if (hours > 0) {
return `${hours}h ${mins}m`
}
return `${mins}m`
}
// 检查是否可以接受贸易(兼容新格式 { type, amount }
const canAcceptTrade = (offer: TradeOffer): boolean => {
const planet = gameStore.player.planets[0]
if (!planet) return false
// 新格式:{ type: 'metal', amount: 1000 }
const requestedType = offer.requestedResources.type
const requestedAmount = offer.requestedResources.amount
return planet.resources[requestedType] >= requestedAmount
}
// 接受贸易提议
const acceptTradeOffer = (offer: TradeOffer) => {
if (!canAcceptTrade(offer)) {
toast.error(t('npcBehavior.trade.acceptFailed'))
return
}
const planet = gameStore.player.planets[0]
if (!planet) return
// 新格式:{ type: 'metal', amount: 1000 }
const requestedType = offer.requestedResources.type
const requestedAmount = offer.requestedResources.amount
const offeredType = offer.offeredResources.type
const offeredAmount = offer.offeredResources.amount
// 扣除请求的资源
planet.resources[requestedType] -= requestedAmount
// 添加获得的资源
planet.resources[offeredType] += offeredAmount
// 移除贸易提议
const index = gameStore.player.tradeOffers?.indexOf(offer)
if (index !== undefined && index >= 0) {
gameStore.player.tradeOffers?.splice(index, 1)
}
// 提高与该NPC的好感度使用 npcId 而不是 fromNpcId
const npcRelation = npcStore.npcs.find(n => n.id === offer.npcId)?.relations?.[gameStore.player.id]
if (npcRelation) {
npcRelation.reputation += 10
}
toast.success(t('npcBehavior.trade.acceptSuccess'))
}
// 拒绝贸易提议
const declineTradeOffer = (offer: TradeOffer) => {
const index = gameStore.player.tradeOffers?.indexOf(offer)
if (index !== undefined && index >= 0) {
gameStore.player.tradeOffers?.splice(index, 1)
}
toast.info(t('npcBehavior.trade.declined'))
}
// 标记情报为已读
const markIntelAsRead = (intel: IntelReport) => {
intel.read = true
}
// 接受联合攻击邀请
const acceptJointAttack = (invite: JointAttackInvite) => {
// 这里可以添加联合攻击的逻辑
// 目前只是简单地移除邀请并显示提示
const index = gameStore.player.jointAttackInvites?.indexOf(invite)
if (index !== undefined && index >= 0) {
gameStore.player.jointAttackInvites?.splice(index, 1)
}
// 提高与该NPC的好感度使用 npcStore
const npcRelation = npcStore.npcs.find(n => n.id === invite.fromNpcId)?.relations?.[gameStore.player.id]
if (npcRelation) {
npcRelation.reputation += 15
}
toast.success(t('npcBehavior.jointAttack.acceptSuccess'))
}
// 拒绝联合攻击邀请
const declineJointAttack = (invite: JointAttackInvite) => {
const index = gameStore.player.jointAttackInvites?.indexOf(invite)
if (index !== undefined && index >= 0) {
gameStore.player.jointAttackInvites?.splice(index, 1)
}
toast.info(t('npcBehavior.jointAttack.declined'))
}
</script>
<style>

View File

@@ -60,9 +60,6 @@
<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>
@@ -232,7 +229,7 @@
<!-- 赠送模式切换仅当目标是NPC星球时显示 -->
<div v-if="targetNpc" class="mb-4 p-3 border rounded-lg bg-muted/50">
<div class="flex items-center gap-2 mb-2">
<Checkbox id="gift-mode" :default-value="isGiftMode" />
<Checkbox id="gift-mode" v-model:checked="isGiftMode" />
<Label for="gift-mode" class="flex items-center gap-2 cursor-pointer">
<Gift class="h-4 w-4" />
{{ t('fleetView.giftMode') }}
@@ -874,7 +871,7 @@
const isGiftMode = ref(false)
// 舰队预设相关状态
const MAX_PRESETS = 3
const MAX_PRESETS = 5
const editingPresetId = ref<string | null>(null)
const editingPresetName = ref('')
const showPresetNameDialog = ref(false)
@@ -940,11 +937,7 @@
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
}
@@ -966,10 +959,7 @@
selectedFleet.value[key as ShipType] = preset.fleet[key as ShipType] || 0
})
// 加载目标坐标
if (preset.targetPosition) {
targetPosition.value = { ...preset.targetPosition }
}
// 不再加载坐标,保留用户当前输入的坐标
// 加载任务类型
if (preset.missionType) {
@@ -1028,11 +1018,7 @@
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
}

View File

@@ -738,10 +738,16 @@
<!-- 导弹攻击对话框 -->
<Dialog :open="missileDialogOpen" @update:open="missileDialogOpen = $event">
<DialogContent>
<DialogContent class="max-w-md">
<DialogHeader>
<DialogTitle>{{ t('galaxyView.missileAttackTitle') }}</DialogTitle>
<DialogDescription v-if="missileTargetPlanet">
<DialogTitle class="flex items-center gap-2">
<div class="p-2 rounded-lg bg-destructive/10">
<Rocket class="h-5 w-5 text-destructive" />
</div>
{{ t('galaxyView.missileAttackTitle') }}
</DialogTitle>
<DialogDescription v-if="missileTargetPlanet" class="flex items-center gap-2 pt-1">
<MapPin class="h-4 w-4 text-muted-foreground" />
{{
t('galaxyView.missileAttackMessage').replace(
'{coordinates}',
@@ -751,41 +757,78 @@
</DialogDescription>
</DialogHeader>
<div v-if="gameStore.currentPlanet && missileTargetPlanet" class="space-y-4">
<div v-if="gameStore.currentPlanet && missileTargetPlanet" class="space-y-5 py-2">
<!-- 导弹数量输入 -->
<div class="space-y-2">
<Label>{{ t('galaxyView.missileCount') }}</Label>
<Input
v-model.number="missileCount"
type="number"
min="1"
:max="gameStore.currentPlanet.defense['interplanetaryMissile'] || 0"
/>
<p class="text-sm text-muted-foreground">
{{ t('galaxyView.availableMissiles') }}: {{ gameStore.currentPlanet.defense['interplanetaryMissile'] || 0 }}
</p>
<div class="space-y-3">
<Label class="text-sm font-medium">{{ t('galaxyView.missileCount') }}</Label>
<div class="flex items-center gap-3">
<Input
v-model.number="missileCount"
type="number"
min="1"
:max="gameStore.currentPlanet.defense['interplanetaryMissile'] || 0"
class="flex-1"
/>
<Button variant="outline" size="sm" @click="missileCount = gameStore.currentPlanet?.defense['interplanetaryMissile'] || 0">
{{ t('fleetView.all') }}
</Button>
</div>
<div class="flex items-center gap-2 text-sm text-muted-foreground">
<Crosshair class="h-4 w-4" />
<span>{{ t('galaxyView.availableMissiles') }}:</span>
<span class="font-medium text-foreground">{{ gameStore.currentPlanet.defense['interplanetaryMissile'] || 0 }}</span>
</div>
</div>
<!-- 射程和距离信息 -->
<div class="space-y-2 text-sm">
<div class="flex justify-between">
<span class="text-muted-foreground">{{ t('galaxyView.missileRange') }}:</span>
<span>{{ calculateMissileRange() }} {{ t('galaxyView.systems') }}</span>
<!-- 任务信息卡片 -->
<div class="rounded-lg border bg-muted/30 p-4 space-y-3">
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-muted-foreground">
<Target class="h-4 w-4" />
<span>{{ t('galaxyView.missileRange') }}</span>
</div>
<span class="font-medium">{{ calculateMissileRange() }} {{ t('galaxyView.systems') }}</span>
</div>
<div class="flex justify-between">
<span class="text-muted-foreground">{{ t('galaxyView.distance') }}:</span>
<span>{{ calculateDistance(missileTargetPlanet) }} {{ t('galaxyView.systems') }}</span>
<Separator />
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-muted-foreground">
<Navigation class="h-4 w-4" />
<span>{{ t('galaxyView.distance') }}</span>
</div>
<span class="font-medium">{{ formatDistance(calculateDistance(missileTargetPlanet)) }}</span>
</div>
<div class="flex justify-between">
<span class="text-muted-foreground">{{ t('galaxyView.flightTime') }}:</span>
<span>{{ formatFlightTime(calculateDistance(missileTargetPlanet)) }}</span>
<Separator />
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2 text-muted-foreground">
<Clock class="h-4 w-4" />
<span>{{ t('galaxyView.flightTime') }}</span>
</div>
<span class="font-medium">{{ formatFlightTime(calculateDistance(missileTargetPlanet)) }}</span>
</div>
</div>
<!-- 超出射程警告 -->
<div
v-if="calculateDistance(missileTargetPlanet) > calculateMissileRange()"
class="flex items-center gap-2 p-3 rounded-lg bg-destructive/10 text-destructive text-sm"
>
<AlertTriangle class="h-4 w-4 shrink-0" />
<span>{{ t('galaxyView.outOfRange') }}</span>
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="missileDialogOpen = false">{{ t('galaxyView.cancel') }}</Button>
<Button @click="launchMissileAttack">{{ t('galaxyView.launchMissile') }}</Button>
<DialogFooter class="gap-3">
<Button variant="outline" @click="missileDialogOpen = false">
{{ t('galaxyView.cancel') }}
</Button>
<Button
variant="destructive"
@click="launchMissileAttack"
:disabled="!missileCount || missileCount < 1 || calculateDistance(missileTargetPlanet!) > calculateMissileRange()"
>
<Rocket class="h-4 w-4 mr-2" />
{{ t('galaxyView.launchMissile') }}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -915,6 +958,7 @@
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover'
import { Separator } from '@/components/ui/separator'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import {
AlertDialog,
@@ -927,7 +971,25 @@
AlertDialogTitle
} from '@/components/ui/alert-dialog'
import ResourceIcon from '@/components/common/ResourceIcon.vue'
import { Home, Eye, Sword, Rocket, Recycle, Gift, Globe, Bomb, Moon, Radar, Mountain } from 'lucide-vue-next'
import {
Home,
Eye,
Sword,
Rocket,
Recycle,
Gift,
Globe,
Bomb,
Moon,
Radar,
Mountain,
MapPin,
Crosshair,
Target,
Navigation,
Clock,
AlertTriangle
} from 'lucide-vue-next'
import { useRouter, useRoute } from 'vue-router'
import * as gameLogic from '@/logic/gameLogic'
import * as moonLogic from '@/logic/moonLogic'
@@ -1326,16 +1388,23 @@
}
// 计算到目标的距离
const calculateDistance = (target: Planet) => {
if (!gameStore.currentPlanet) return 0
const calculateDistance = (target: Planet | null): number => {
if (!gameStore.currentPlanet || !target) return 0
const from = gameStore.currentPlanet.position
const to = target.position
if (from.galaxy !== to.galaxy) return Infinity
return Math.abs(from.system - to.system)
}
// 格式化距离显示
const formatDistance = (distance: number): string => {
if (!isFinite(distance)) return t('galaxyView.outOfRange')
return `${distance} ${t('galaxyView.systems')}`
}
// 格式化飞行时间
const formatFlightTime = (distance: number) => {
const formatFlightTime = (distance: number): string => {
if (!isFinite(distance)) return t('galaxyView.outOfRange')
const seconds = 30 + distance * 60
const minutes = Math.floor(seconds / 60)
const secs = seconds % 60

View File

@@ -143,13 +143,16 @@
</Card>
</TabsContent>
<!-- NPC相关消息活动礼物被拒绝 -->
<!-- NPC相关消息活动礼物被拒绝贸易提议情报联合攻击邀请 -->
<TabsContent value="npc" class="mt-4 space-y-2 pb-20">
<Empty
v-if="
sortedNPCActivityNotifications.length === 0 &&
sortedGiftNotifications.length === 0 &&
sortedGiftRejectedNotifications.length === 0
sortedGiftRejectedNotifications.length === 0 &&
sortedTradeOffers.length === 0 &&
sortedIntelReports.length === 0 &&
sortedJointAttackInvites.length === 0
"
class="border rounded-lg"
>
@@ -159,6 +162,143 @@
</EmptyContent>
</Empty>
<!-- 贸易提议 -->
<Card
v-for="offer in sortedTradeOffers"
:key="offer.id"
@click="openTradeOfferDialog(offer)"
class="cursor-pointer hover:shadow-md transition-shadow"
>
<CardHeader class="pb-3">
<div class="flex justify-between items-center gap-2">
<div class="flex items-center gap-2 min-w-0 flex-1">
<ArrowLeftRight class="h-4 w-4 shrink-0 text-amber-500" />
<CardTitle class="text-base sm:text-lg">{{ t('npcBehavior.trade.title') }}</CardTitle>
<Badge variant="default" class="text-xs">{{ t('messagesView.pending') }}</Badge>
<Badge v-if="isOfferExpired(offer)" variant="destructive" class="text-xs">
{{ t('npcBehavior.trade.expired') }}
</Badge>
</div>
<Button @click.stop="deleteTradeOffer(offer.id)" variant="ghost" size="icon" class="h-8 w-8 shrink-0">
<X class="h-4 w-4" />
</Button>
</div>
<CardDescription class="text-xs sm:text-sm">
{{ getNpcNameById(offer.npcId) }} · {{ formatDate(offer.timestamp) }}
</CardDescription>
</CardHeader>
<CardContent>
<div class="space-y-2 text-sm">
<div class="flex gap-4">
<div class="flex-1 flex items-center gap-1">
<span class="text-green-600 dark:text-green-400">{{ t('npcBehavior.trade.offers') }}:</span>
<template v-if="getResourceInfo(offer.offeredResources)">
<ResourceIcon :type="getResourceInfo(offer.offeredResources)!.type" size="sm" />
<NumberWithTooltip :value="getResourceInfo(offer.offeredResources)!.amount" />
</template>
<span v-else>-</span>
</div>
<div class="flex-1 flex items-center gap-1">
<span class="text-red-600 dark:text-red-400">{{ t('npcBehavior.trade.requests') }}:</span>
<template v-if="getResourceInfo(offer.requestedResources)">
<ResourceIcon :type="getResourceInfo(offer.requestedResources)!.type" size="sm" />
<NumberWithTooltip :value="getResourceInfo(offer.requestedResources)!.amount" />
</template>
<span v-else>-</span>
</div>
</div>
<div class="flex gap-2 mt-2">
<Button
@click.stop="acceptTradeOffer(offer)"
variant="default"
size="sm"
class="flex-1"
:disabled="isOfferExpired(offer) || !canAcceptTrade(offer)"
>
{{ t('npcBehavior.trade.accept') }}
</Button>
<Button @click.stop="declineTradeOffer(offer)" variant="outline" size="sm" class="flex-1">
{{ t('npcBehavior.trade.decline') }}
</Button>
</div>
</div>
</CardContent>
</Card>
<!-- 情报报告 -->
<Card
v-for="intel in sortedIntelReports"
:key="intel.id"
@click="openIntelReportDialog(intel)"
class="cursor-pointer hover:shadow-md transition-shadow"
>
<CardHeader class="pb-3">
<div class="flex justify-between items-center gap-2">
<div class="flex items-center gap-2 min-w-0 flex-1">
<FileText class="h-4 w-4 shrink-0 text-blue-500" />
<CardTitle class="text-base sm:text-lg">{{ t('npcBehavior.intel.title') }}</CardTitle>
<Badge v-if="!intel.read" variant="default" class="text-xs">{{ t('messagesView.unread') }}</Badge>
<Badge variant="outline" class="text-xs">{{ t(`npcBehavior.intel.types.${intel.intelType}`) }}</Badge>
</div>
<Button @click.stop="deleteIntelReport(intel.id)" variant="ghost" size="icon" class="h-8 w-8 shrink-0">
<X class="h-4 w-4" />
</Button>
</div>
<CardDescription class="text-xs sm:text-sm">
{{ t('npcBehavior.intel.from') }}: {{ getNpcNameById(intel.fromNpcId) }} → {{ t('npcBehavior.intel.target') }}:
{{ getNpcNameById(intel.targetNpcId) }} ·
{{ formatDate(intel.timestamp) }}
</CardDescription>
</CardHeader>
</Card>
<!-- 联合攻击邀请 -->
<Card
v-for="invite in sortedJointAttackInvites"
:key="invite.id"
@click="openJointAttackDialog(invite)"
class="cursor-pointer hover:shadow-md transition-shadow"
>
<CardHeader class="pb-3">
<div class="flex justify-between items-center gap-2">
<div class="flex items-center gap-2 min-w-0 flex-1">
<Swords class="h-4 w-4 shrink-0 text-red-500" />
<CardTitle class="text-base sm:text-lg">{{ t('npcBehavior.jointAttack.title') }}</CardTitle>
<Badge variant="default" class="text-xs">{{ t('messagesView.pending') }}</Badge>
<Badge v-if="isInviteExpired(invite)" variant="destructive" class="text-xs">
{{ t('npcBehavior.jointAttack.expired') }}
</Badge>
</div>
<Button @click.stop="deleteJointAttackInvite(invite.id)" variant="ghost" size="icon" class="h-8 w-8 shrink-0">
<X class="h-4 w-4" />
</Button>
</div>
<CardDescription class="text-xs sm:text-sm">
{{ getNpcNameById(invite.fromNpcId) }} → {{ getNpcNameById(invite.targetNpcId) }} ({{ invite.targetNpcName }}) ·
{{ formatDate(invite.timestamp) }}
</CardDescription>
</CardHeader>
<CardContent>
<div class="space-y-2 text-sm">
<div>{{ t('npcBehavior.jointAttack.lootShare') }}: {{ (invite.expectedLootRatio * 100).toFixed(0) }}%</div>
<div class="flex gap-2 mt-2">
<Button
@click.stop="acceptJointAttack(invite)"
variant="default"
size="sm"
class="flex-1"
:disabled="isInviteExpired(invite)"
>
{{ t('npcBehavior.jointAttack.accept') }}
</Button>
<Button @click.stop="declineJointAttack(invite)" variant="outline" size="sm" class="flex-1">
{{ t('npcBehavior.jointAttack.decline') }}
</Button>
</div>
</div>
</CardContent>
</Card>
<!-- NPC活动通知 -->
<Card
v-for="notification in sortedNPCActivityNotifications"
@@ -364,8 +504,25 @@
import SpiedNotificationDialog from '@/components/dialogs/SpiedNotificationDialog.vue'
import MissionReportDialog from '@/components/dialogs/MissionReportDialog.vue'
import NPCActivityDialog from '@/components/dialogs/NPCActivityDialog.vue'
import ResourceIcon from '@/components/common/ResourceIcon.vue'
import NumberWithTooltip from '@/components/common/NumberWithTooltip.vue'
import { formatDate } from '@/utils/format'
import { X, Sword, Eye, AlertTriangle, Package, Recycle, Gift, Ban, Check, Users, Trash2 } from 'lucide-vue-next'
import {
X,
Sword,
Eye,
AlertTriangle,
Package,
Recycle,
Gift,
Ban,
Check,
Users,
Trash2,
ArrowLeftRight,
FileText,
Swords
} from 'lucide-vue-next'
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
import type {
BattleResult,
@@ -374,7 +531,10 @@
NPCActivityNotification,
MissionReport,
GiftNotification,
GiftRejectedNotification
GiftRejectedNotification,
TradeOffer,
IntelReport,
JointAttackInvite
} from '@/types/game'
import { MissionType } from '@/types/game'
import { useNPCStore } from '@/stores/npcStore'
@@ -396,6 +556,9 @@
| 'npcActivity'
| 'giftNotifications'
| 'giftRejected'
| 'tradeOffers'
| 'intelReports'
| 'jointAttackInvites'
const clearOptions = ref<Record<ClearOptionKey, boolean>>({
battles: false,
spyReports: false,
@@ -403,7 +566,10 @@
missionReports: false,
npcActivity: false,
giftNotifications: false,
giftRejected: false
giftRejected: false,
tradeOffers: false,
intelReports: false,
jointAttackInvites: false
})
// 清空消息选项配置
@@ -422,7 +588,18 @@
labelKey: 'clearGiftNotifications',
count: gameStore.player.giftNotifications?.length || 0
},
{ key: 'giftRejected' as ClearOptionKey, labelKey: 'clearGiftRejected', count: gameStore.player.giftRejectedNotifications?.length || 0 }
{
key: 'giftRejected' as ClearOptionKey,
labelKey: 'clearGiftRejected',
count: gameStore.player.giftRejectedNotifications?.length || 0
},
{ key: 'tradeOffers' as ClearOptionKey, labelKey: 'clearTradeOffers', count: gameStore.player.tradeOffers?.length || 0 },
{ key: 'intelReports' as ClearOptionKey, labelKey: 'clearIntelReports', count: gameStore.player.intelReports?.length || 0 },
{
key: 'jointAttackInvites' as ClearOptionKey,
labelKey: 'clearJointAttackInvites',
count: gameStore.player.jointAttackInvites?.length || 0
}
])
// 基础资源字段配置(用于显示资源列表)
@@ -495,6 +672,15 @@
if (clearOptions.value.giftRejected) {
gameStore.player.giftRejectedNotifications = []
}
if (clearOptions.value.tradeOffers) {
gameStore.player.tradeOffers = []
}
if (clearOptions.value.intelReports) {
gameStore.player.intelReports = []
}
if (clearOptions.value.jointAttackInvites) {
gameStore.player.jointAttackInvites = []
}
// 重置选项
clearOptions.value = {
@@ -504,7 +690,10 @@
missionReports: false,
npcActivity: false,
giftNotifications: false,
giftRejected: false
giftRejected: false,
tradeOffers: false,
intelReports: false,
jointAttackInvites: false
}
// 关闭popover
@@ -608,12 +797,33 @@
return allMissionReports.value.slice(start, end)
})
// NPC标签页合并数据活动通知 + 礼物通知 + 礼物被拒绝通知)
// 贸易提议数据
const allTradeOffers = computed(() => {
if (!gameStore.player.tradeOffers) return []
return [...gameStore.player.tradeOffers].sort((a, b) => b.timestamp - a.timestamp)
})
// 情报报告数据
const allIntelReports = computed(() => {
if (!gameStore.player.intelReports) return []
return [...gameStore.player.intelReports].sort((a, b) => b.timestamp - a.timestamp)
})
// 联合攻击邀请数据
const allJointAttackInvites = computed(() => {
if (!gameStore.player.jointAttackInvites) return []
return [...gameStore.player.jointAttackInvites].sort((a, b) => b.timestamp - a.timestamp)
})
// NPC标签页合并数据活动通知 + 礼物通知 + 礼物被拒绝通知 + 贸易提议 + 情报报告 + 联合攻击邀请)
const allNPCTabItems = computed(() => {
const activities = allNPCActivityNotifications.value.map(item => ({ ...item, type: 'activity' as const }))
const gifts = allGiftNotifications.value.map(item => ({ ...item, type: 'gift' as const }))
const rejections = allGiftRejectedNotifications.value.map(item => ({ ...item, type: 'rejection' as const }))
return [...activities, ...gifts, ...rejections].sort((a, b) => b.timestamp - a.timestamp)
const trades = allTradeOffers.value.map(item => ({ ...item, type: 'trade' as const }))
const intels = allIntelReports.value.map(item => ({ ...item, type: 'intel' as const }))
const jointAttacks = allJointAttackInvites.value.map(item => ({ ...item, type: 'jointAttack' as const }))
return [...activities, ...gifts, ...rejections, ...trades, ...intels, ...jointAttacks].sort((a, b) => b.timestamp - a.timestamp)
})
const npcTabTotalPages = computed(() => Math.ceil(allNPCTabItems.value.length / ITEMS_PER_PAGE))
@@ -636,6 +846,18 @@
return paginatedNPCTabItems.value.filter(item => item.type === 'rejection')
})
const sortedTradeOffers = computed(() => {
return paginatedNPCTabItems.value.filter(item => item.type === 'trade')
})
const sortedIntelReports = computed(() => {
return paginatedNPCTabItems.value.filter(item => item.type === 'intel')
})
const sortedJointAttackInvites = computed(() => {
return paginatedNPCTabItems.value.filter(item => item.type === 'jointAttack')
})
// 未读战斗报告数量
const unreadBattles = computed(() => {
return gameStore.player.battleReports.filter(r => !r.read).length
@@ -686,14 +908,38 @@
return gameStore.player.giftRejectedNotifications.filter(n => !n.read).length
})
// 待处理贸易提议数量(未过期)
const pendingTradeOffers = computed(() => {
const now = Date.now()
return (gameStore.player.tradeOffers || []).filter(o => o.expiresAt > now).length
})
// 未读情报报告数量
const unreadIntelReports = computed(() => {
return (gameStore.player.intelReports || []).filter(r => !r.read).length
})
// 待处理联合攻击邀请数量(未过期)
const pendingJointAttackInvites = computed(() => {
const now = Date.now()
return (gameStore.player.jointAttackInvites || []).filter(i => i.expiresAt > now).length
})
// 合并:侦查相关未读总数(侦查报告 + 被侦查通知)
const unreadSpyTotal = computed(() => {
return unreadSpyReports.value + unreadSpiedNotifications.value
})
// 合并NPC相关未读总数NPC活动 + 礼物通知 + 礼物被拒绝)
// 合并NPC相关未读总数NPC活动 + 礼物通知 + 礼物被拒绝 + 贸易提议 + 情报 + 联合攻击邀请
const unreadNPCTotal = computed(() => {
return unreadNPCActivity.value + unreadGiftNotifications.value + unreadGiftRejected.value
return (
unreadNPCActivity.value +
unreadGiftNotifications.value +
unreadGiftRejected.value +
pendingTradeOffers.value +
unreadIntelReports.value +
pendingJointAttackInvites.value
)
})
// 标签页配置
@@ -959,4 +1205,185 @@
gameStore.player.giftRejectedNotifications.splice(index, 1)
}
}
// ========== 贸易提议相关 ==========
// 通过 NPC ID 获取名称
const getNpcNameById = (npcId: string): string => {
const npc = npcStore.npcs.find(n => n.id === npcId)
return npc?.name || npcId
}
// 检查贸易提议是否过期
const isOfferExpired = (offer: TradeOffer): boolean => {
const now = Date.now()
return offer.expiresAt <= now
}
// 检查联合攻击邀请是否过期
const isInviteExpired = (invite: JointAttackInvite): boolean => {
const now = Date.now()
return invite.expiresAt <= now
}
// 辅助函数:从资源对象中提取资源信息(兼容新旧格式)
// 用于模板显示和逻辑处理
const getResourceInfo = (resource: any): { type: 'metal' | 'crystal' | 'deuterium'; amount: number } | null => {
if (!resource) return null
// 新格式:{ type: 'metal', amount: 1000 }
if (resource.type && typeof resource.amount === 'number' && !isNaN(resource.amount)) {
return { type: resource.type, amount: resource.amount }
}
// 旧格式:{ metal: 1000, crystal: 0, deuterium: 0 }
if (typeof resource.metal === 'number' && resource.metal > 0) {
return { type: 'metal', amount: resource.metal }
}
if (typeof resource.crystal === 'number' && resource.crystal > 0) {
return { type: 'crystal', amount: resource.crystal }
}
if (typeof resource.deuterium === 'number' && resource.deuterium > 0) {
return { type: 'deuterium', amount: resource.deuterium }
}
return null
}
// 别名,供内部逻辑使用
const extractResourceInfo = getResourceInfo
// 检查是否可以接受贸易
const canAcceptTrade = (offer: TradeOffer): boolean => {
const planet = gameStore.player.planets[0]
if (!planet) return false
const requested = extractResourceInfo(offer.requestedResources)
if (!requested) return false
return planet.resources[requested.type] >= requested.amount
}
// 打开贸易提议详情对话框(目前直接操作,后续可添加对话框)
const openTradeOfferDialog = (_offer: TradeOffer) => {
// 目前贸易提议直接在卡片上操作,不需要单独的对话框
}
// 接受贸易提议
const acceptTradeOffer = (offer: TradeOffer) => {
if (isOfferExpired(offer)) {
toast.error(t('npcBehavior.trade.expired'))
return
}
if (!canAcceptTrade(offer)) {
toast.error(t('npcBehavior.trade.acceptFailed'))
return
}
const planet = gameStore.player.planets[0]
if (!planet) return
const requested = extractResourceInfo(offer.requestedResources)
const offered = extractResourceInfo(offer.offeredResources)
if (!requested || !offered) {
toast.error(t('npcBehavior.trade.acceptFailed'))
return
}
// 扣除请求的资源
planet.resources[requested.type] -= requested.amount
// 添加获得的资源
planet.resources[offered.type] += offered.amount
// 移除贸易提议
deleteTradeOffer(offer.id)
// 提高与该NPC的好感度使用 npcStore
const npcRelation = npcStore.npcs.find(n => n.id === offer.npcId)?.relations?.[gameStore.player.id]
if (npcRelation) {
npcRelation.reputation += 10
}
toast.success(t('npcBehavior.trade.acceptSuccess'))
}
// 拒绝贸易提议
const declineTradeOffer = (offer: TradeOffer) => {
deleteTradeOffer(offer.id)
toast.info(t('npcBehavior.trade.declined'))
}
// 删除贸易提议
const deleteTradeOffer = (offerId: string) => {
if (!gameStore.player.tradeOffers) return
const index = gameStore.player.tradeOffers.findIndex(o => o.id === offerId)
if (index > -1) {
gameStore.player.tradeOffers.splice(index, 1)
}
}
// ========== 情报报告相关 ==========
// 打开情报报告详情对话框
const openIntelReportDialog = (intel: IntelReport) => {
// 标记为已读
const originalIntel = gameStore.player.intelReports?.find(i => i.id === intel.id)
if (originalIntel && !originalIntel.read) {
originalIntel.read = true
}
// 目前情报报告直接显示在卡片上,后续可添加详情对话框
}
// 删除情报报告
const deleteIntelReport = (intelId: string) => {
if (!gameStore.player.intelReports) return
const index = gameStore.player.intelReports.findIndex(i => i.id === intelId)
if (index > -1) {
gameStore.player.intelReports.splice(index, 1)
}
}
// ========== 联合攻击邀请相关 ==========
// 打开联合攻击邀请详情对话框
const openJointAttackDialog = (_invite: JointAttackInvite) => {
// 目前联合攻击邀请直接在卡片上操作,后续可添加详情对话框
}
// 接受联合攻击邀请
const acceptJointAttack = (invite: JointAttackInvite) => {
if (isInviteExpired(invite)) {
toast.error(t('npcBehavior.jointAttack.expired'))
return
}
// 移除邀请
deleteJointAttackInvite(invite.id)
// 提高与该NPC的好感度使用 npcStore
const npcRelation = npcStore.npcs.find(n => n.id === invite.fromNpcId)?.relations?.[gameStore.player.id]
if (npcRelation) {
npcRelation.reputation += 15
}
toast.success(t('npcBehavior.jointAttack.acceptSuccess'))
// 后续可以添加实际的联合攻击逻辑
}
// 拒绝联合攻击邀请
const declineJointAttack = (invite: JointAttackInvite) => {
deleteJointAttackInvite(invite.id)
toast.info(t('npcBehavior.jointAttack.declined'))
}
// 删除联合攻击邀请
const deleteJointAttackInvite = (inviteId: string) => {
if (!gameStore.player.jointAttackInvites) return
const index = gameStore.player.jointAttackInvites.findIndex(i => i.id === inviteId)
if (index > -1) {
gameStore.player.jointAttackInvites.splice(index, 1)
}
}
</script>

View File

@@ -143,6 +143,10 @@
const alertDialogRequirements = ref<Array<{ name: string; requiredLevel: number; currentLevel: number; met: boolean }>>([])
const alertDialogShowRequirements = ref(false)
// 防抖状态:防止快速点击
const isProcessing = ref(false)
const DEBOUNCE_DELAY = 300 // 防抖延迟(毫秒)
// 资源类型配置(用于成本显示)
const costResourceTypes = [
{ key: 'metal' as const },
@@ -252,6 +256,13 @@
// 研究科技
const handleResearch = (techType: TechnologyType, event: MouseEvent) => {
// 防抖:防止快速点击
if (isProcessing.value) return
isProcessing.value = true
setTimeout(() => {
isProcessing.value = false
}, DEBOUNCE_DELAY)
// 检查前置条件
if (!checkUpgradeRequirements(techType)) {
alertDialogTitle.value = t('common.requirementsNotMet')

View File

@@ -38,6 +38,34 @@
</div>
</div>
<!-- WebDAV 云同步 -->
<div class="flex flex-col gap-3 p-4 border rounded-lg border-blue-500/30 bg-blue-500/5">
<div class="flex items-center justify-between">
<div class="space-y-1">
<h3 class="font-medium flex items-center gap-2">
<Cloud class="h-4 w-4 text-blue-500" />
{{ t('settings.webdav.title') }}
</h3>
<p class="text-sm text-muted-foreground">{{ t('settings.webdav.desc') }}</p>
</div>
<Button @click="showWebDAVConfig = true" variant="outline" size="sm">
<Settings2 class="mr-2 h-4 w-4" />
{{ t('settings.webdav.config') }}
</Button>
</div>
<div v-if="webdavConfig" class="flex gap-2 pt-2 border-t">
<Button @click="handleWebDAVUpload" :disabled="isWebDAVUploading" class="flex-1" variant="outline">
<CloudUpload class="mr-2 h-4 w-4" />
{{ isWebDAVUploading ? t('settings.webdav.uploading') : t('settings.webdav.upload') }}
</Button>
<Button @click="showWebDAVFiles = true" class="flex-1" variant="outline">
<CloudDownload class="mr-2 h-4 w-4" />
{{ t('settings.webdav.download') }}
</Button>
</div>
<p v-else class="text-xs text-muted-foreground">{{ t('settings.webdav.notConfigured') }}</p>
</div>
<!-- 清除数据 -->
<div class="flex items-center justify-between p-4 border rounded-lg border-destructive/50">
<div class="space-y-1">
@@ -97,7 +125,7 @@
</CardHeader>
<CardContent class="space-y-4">
<!-- 浏览器通知 -->
<div class="flex flex-col gap-4 p-4 border rounded-lg">
<div class="flex flex-col gap-4 p-4 border rounded-lg" v-if="!Capacitor.isNativePlatform()">
<div class="flex items-center justify-between">
<div class="space-y-1">
<h3 class="font-medium">{{ t('settings.browserNotifications') }}</h3>
@@ -304,6 +332,12 @@
<!-- 隐私协议弹窗 -->
<PrivacyDialog v-model:open="showPrivacyDialog" />
<!-- WebDAV 配置对话框 -->
<WebDAVConfigDialog v-model:open="showWebDAVConfig" @saved="onWebDAVConfigSaved" />
<!-- WebDAV 文件列表对话框 -->
<WebDAVFileListDialog v-model:open="showWebDAVFiles" :config="webdavConfig" @select="handleWebDAVDownload" />
</div>
</template>
@@ -337,7 +371,11 @@
ChevronDown,
ChevronUp,
RotateCcw,
Shield
Shield,
Cloud,
CloudUpload,
CloudDownload,
Settings2
} from 'lucide-vue-next'
import { saveAs } from 'file-saver'
import { toast } from 'vue-sonner'
@@ -348,7 +386,15 @@
import type { VersionInfo } from '@/utils/versionCheck'
import UpdateDialog from '@/components/dialogs/UpdateDialog.vue'
import PrivacyDialog from '@/components/dialogs/PrivacyDialog.vue'
import WebDAVConfigDialog from '@/components/settings/WebDAVConfigDialog.vue'
import WebDAVFileListDialog from '@/components/settings/WebDAVFileListDialog.vue'
import { useHints } from '@/composables/useHints'
import {
type WebDAVConfig,
getWebDAVConfig,
uploadToWebDAV,
downloadFromWebDAV
} from '@/services/webdavService'
const { t } = useI18n()
const { hintsEnabled, setHintsEnabled, resetHints } = useHints()
@@ -366,6 +412,12 @@
const isTypesExpanded = ref(false)
// WebDAV 相关状态
const showWebDAVConfig = ref(false)
const showWebDAVFiles = ref(false)
const webdavConfig = ref<WebDAVConfig | null>(getWebDAVConfig())
const isWebDAVUploading = ref(false)
// 确保通知设置存在
if (!gameStore.notificationSettings) {
gameStore.notificationSettings = {
@@ -705,4 +757,98 @@
const updateBackgroundSetting = (val: boolean) => {
gameStore.player.backgroundEnabled = val
}
// WebDAV 配置保存回调
const onWebDAVConfigSaved = () => {
webdavConfig.value = getWebDAVConfig()
}
// WebDAV 上传
const handleWebDAVUpload = async () => {
if (!webdavConfig.value) return
isWebDAVUploading.value = true
try {
// 获取游戏数据
const gameData = localStorage.getItem(pkg.name)
const universeData = localStorage.getItem(`${pkg.name}-universe`)
const npcData = localStorage.getItem(`${pkg.name}-npcs`)
if (!gameData) {
toast.error(t('settings.exportFailed'))
return
}
// 合并数据
const exportData = {
game: gameData,
npcs: npcData,
universe: universeData || null
}
const jsonString = JSON.stringify(exportData, null, 2)
const result = await uploadToWebDAV(webdavConfig.value, jsonString)
if (result.success) {
toast.success(t('settings.webdav.uploadSuccess'))
} else {
toast.error(result.message || t('settings.webdav.uploadFailed'))
}
} catch (error) {
console.error('WebDAV upload failed:', error)
toast.error(t('settings.webdav.uploadFailed'))
} finally {
isWebDAVUploading.value = false
}
}
// WebDAV 下载
const handleWebDAVDownload = async (fileName: string) => {
if (!webdavConfig.value) return
try {
const result = await downloadFromWebDAV(webdavConfig.value, fileName)
if (!result.success || !result.data) {
toast.error(result.message || t('settings.webdav.downloadFailed'))
return
}
// 确认导入
confirmTitle.value = t('settings.importConfirmTitle')
confirmMessage.value = t('settings.importConfirmMessage')
showConfirmDialog.value = true
gameStore.isPaused = true
confirmCallback = () => {
try {
const importData = JSON.parse(result.data!)
// 兼容旧版本格式
if (typeof importData === 'string' || !importData.game) {
localStorage.setItem(pkg.name, result.data!)
} else {
if (importData.game) {
localStorage.setItem(pkg.name, importData.game)
}
if (importData.universe) {
localStorage.setItem(`${pkg.name}-universe`, importData.universe)
}
if (importData.npcs) {
localStorage.setItem(`${pkg.name}-npcs`, importData.npcs)
}
}
toast.success(t('settings.importSuccess'))
setTimeout(() => window.location.reload(), 1000)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
toast.error(t('settings.importFailed') + ': ' + message)
}
}
} catch (error) {
console.error('WebDAV download failed:', error)
toast.error(t('settings.webdav.downloadFailed'))
}
}
</script>

View File

@@ -200,6 +200,10 @@
const alertDialogTitle = ref('')
const alertDialogMessage = ref('')
// 防抖状态:防止快速点击
const isProcessing = ref(false)
const DEBOUNCE_DELAY = 300 // 防抖延迟(毫秒)
// 资源类型配置(用于成本显示)
const costResourceTypes = [
{ key: 'metal' as const },
@@ -255,6 +259,13 @@
// 建造舰船
const handleBuild = (shipType: ShipType, event: MouseEvent) => {
// 防抖:防止快速点击
if (isProcessing.value) return
isProcessing.value = true
setTimeout(() => {
isProcessing.value = false
}, DEBOUNCE_DELAY)
const quantity = quantities.value[shipType]
if (quantity <= 0) {
alertDialogTitle.value = t('shipyardView.inputError')

View File

@@ -4,6 +4,7 @@ import tailwindcss from '@tailwindcss/vite'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { VitePWA } from 'vite-plugin-pwa'
import autoprefixer from 'autoprefixer'
import pkg from './package.json'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
@@ -93,8 +94,9 @@ export default defineConfig(async () => {
if (id.includes('/src/logic/')) return 'game-logic'
// 配置和类型
if (id.includes('/src/config/') || id.includes('/src/types/')) return 'game-config'
// 本地化
if (id.includes('/src/locales/')) return 'game-i18n'
// 本地化 - 只打包默认语言和index其他语言通过动态导入自动分割
if (id.includes('/src/locales/index') || id.includes('/src/locales/zh-CN')) return 'locale-default'
// 其他语言文件不指定chunk让Vite根据动态导入自动分割
// 其他 node_modules 依赖
if (id.includes('node_modules/')) return 'vendor-others'
},
@@ -107,6 +109,15 @@ export default defineConfig(async () => {
},
plugins,
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
css: {
postcss: {
plugins: [
autoprefixer({
overrideBrowserslist: ['Android >= 4.1', 'iOS >= 7.1', 'Chrome >= 31', 'Firefox >= 31', 'ie >= 8']
})
]
}
},
// 优化依赖预构建
optimizeDeps: { include: ['vue', 'vue-router', 'pinia', 'reka-ui', '@vueuse/core', 'lucide-vue-next', 'crypto-js', 'file-saver'] }
}