mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 07:55:11 +08:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec96d2541e | ||
|
|
9e7560cc4b | ||
|
|
1ad051cd6d | ||
|
|
fda15646eb | ||
|
|
6a9846c6df | ||
|
|
49753566c3 | ||
|
|
66783f896c | ||
|
|
7cc885c62a | ||
|
|
5c6404d86a | ||
|
|
010ea137ac | ||
|
|
6dbca76252 | ||
|
|
c047ffb88e | ||
|
|
6f8adfa586 | ||
|
|
94fa2ad57a | ||
|
|
7ed508945a | ||
|
|
fe2e5bfad9 | ||
|
|
7f36b6693f | ||
|
|
27d60ae71a | ||
|
|
ca1aed1e9b | ||
|
|
04ee72a33d | ||
|
|
d95dffcfcd | ||
|
|
b6bcae3294 | ||
|
|
ebed10b82f | ||
|
|
f4f5a719f5 | ||
|
|
1686622013 |
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@@ -72,16 +72,11 @@ jobs:
|
|||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
# 缓存 Gradle 依赖 (可节省 3-5 分钟)
|
# 使用官方 Gradle Action(内置智能缓存)
|
||||||
- name: Cache Gradle
|
- name: Setup Gradle
|
||||||
uses: actions/cache@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
cache-read-only: false
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: gradle-${{ runner.os }}-${{ hashFiles('android/**/*.gradle*', 'android/**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
gradle-${{ runner.os }}-
|
|
||||||
|
|
||||||
- name: Build Frontend
|
- name: Build Frontend
|
||||||
run: |
|
run: |
|
||||||
@@ -118,13 +113,13 @@ jobs:
|
|||||||
working-directory: android
|
working-directory: android
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./gradlew
|
chmod +x ./gradlew
|
||||||
./gradlew assembleRelease --no-daemon
|
./gradlew assembleRelease --build-cache
|
||||||
|
|
||||||
- name: Upload APK Artifacts
|
- name: Upload APK Artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: android-apk
|
name: android-apk
|
||||||
path: android/app/build/outputs/apk/release/*.APK
|
path: android/app/build/outputs/apk/release/*.apk
|
||||||
|
|
||||||
# 3. 构建 Electron 客户端
|
# 3. 构建 Electron 客户端
|
||||||
build-electron:
|
build-electron:
|
||||||
@@ -180,7 +175,7 @@ jobs:
|
|||||||
|
|
||||||
# 4. 发布 Release
|
# 4. 发布 Release
|
||||||
release:
|
release:
|
||||||
needs: [ build-server, build-android, build-electron ]
|
needs: [build-server, build-android, build-electron]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -201,18 +196,18 @@ jobs:
|
|||||||
- name: Flatten and Rename Assets
|
- name: Flatten and Rename Assets
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./final-release
|
mkdir -p ./final-release
|
||||||
|
|
||||||
# 移动 Server 文件并确保名字唯一
|
# 移动 Server 文件并确保名字唯一
|
||||||
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-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-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
|
cp ./raw-assets/server-linux-arm64/OGame-Vue-Ts-server-linux-arm64 ./final-release/OGame-Vue-Ts-server-linux-arm64 || true
|
||||||
|
|
||||||
# 移动 Electron 安装包 (排除 unpacked 目录)
|
# 移动 Electron 安装包 (排除 unpacked 目录)
|
||||||
find ./raw-assets/electron-* -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.zip" \) -exec cp {} ./final-release/ \;
|
find ./raw-assets/electron-* -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.zip" \) -exec cp {} ./final-release/ \;
|
||||||
|
|
||||||
# 移动 Android APK
|
# 移动 Android APK
|
||||||
find ./raw-assets/android-apk -type f -name "*.APK" -exec cp {} ./final-release/ \; || true
|
find ./raw-assets/android-apk -type f -name "*.apk" -exec cp {} ./final-release/ \; || true
|
||||||
|
|
||||||
# 检查结果
|
# 检查结果
|
||||||
echo "Final assets to upload:"
|
echo "Final assets to upload:"
|
||||||
ls -R ./final-release
|
ls -R ./final-release
|
||||||
@@ -222,7 +217,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||||
# 获取 release 中的现有 assets 并删除 APK 文件
|
# 获取 release 中的现有 assets 并删除 APK 文件
|
||||||
gh release view "$VERSION" --json assets -q '.assets[].name' 2>/dev/null | grep -i '\.APK$' | while read asset; do
|
gh release view "$VERSION" --json assets -q '.assets[].name' 2>/dev/null | grep -i '\.apk$' | while read asset; do
|
||||||
echo "Deleting existing asset: $asset"
|
echo "Deleting existing asset: $asset"
|
||||||
gh release delete-asset "$VERSION" "$asset" -y || true
|
gh release delete-asset "$VERSION" "$asset" -y || true
|
||||||
done
|
done
|
||||||
@@ -239,4 +234,4 @@ jobs:
|
|||||||
files: ./final-release/*
|
files: ./final-release/*
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
4
.github/workflows/github-pages.yml
vendored
4
.github/workflows/github-pages.yml
vendored
@@ -2,7 +2,7 @@ name: 构建 Github Pages
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ] # 如果你的主分支叫 master,请改为 master
|
branches: [main] # 如果你的主分支叫 master,请改为 master
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -44,4 +44,4 @@ jobs:
|
|||||||
path: './docs'
|
path: './docs'
|
||||||
|
|
||||||
- name: 部署到 GitHub Pages
|
- name: 部署到 GitHub Pages
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
|
|||||||
8
.github/workflows/ogame-vue-ts.yml
vendored
8
.github/workflows/ogame-vue-ts.yml
vendored
@@ -2,8 +2,8 @@ name: Docker 多架构构建并发布
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
tags: [ 'v*.*.*' ] # 打 tag 时也触发
|
tags: ['v*.*.*'] # 打 tag 时也触发
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
|
|
||||||
# 登录 Docker Hub(只在用户名存在时执行)
|
# 登录 Docker Hub(只在用户名存在时执行)
|
||||||
- name: 登录 Docker Hub
|
- name: 登录 Docker Hub
|
||||||
if: vars.DOCKERHUB_USERNAME != '' # 只检查 vars,忽略 secrets
|
if: vars.DOCKERHUB_USERNAME != '' # 只检查 vars,忽略 secrets
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||||
@@ -57,4 +57,4 @@ jobs:
|
|||||||
${{ vars.DOCKERHUB_USERNAME != '' && format('docker.io/{0}/ogame-vue-ts:{1}', vars.DOCKERHUB_USERNAME, github.sha) || '' }}
|
${{ vars.DOCKERHUB_USERNAME != '' && format('docker.io/{0}/ogame-vue-ts:{1}', vars.DOCKERHUB_USERNAME, github.sha) || '' }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=OGame Vue Ts
|
outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=OGame Vue Ts
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
// 从 package.json 读取版本号
|
||||||
|
def packageJsonFile = file('../../package.json')
|
||||||
|
def packageJsonText = packageJsonFile.text
|
||||||
|
// 使用正则提取版本号
|
||||||
|
def versionMatcher = packageJsonText =~ /"version"\s*:\s*"([^"]+)"/
|
||||||
|
def appVersionName = versionMatcher ? versionMatcher[0][1] : "1.0.0"
|
||||||
|
// 将版本号转换为 versionCode,例如 "1.5.5" -> 1*10000 + 5*100 + 5 = 10505
|
||||||
|
def versionParts = appVersionName.split('\\.')
|
||||||
|
def appVersionCode = versionParts[0].toInteger() * 10000 + versionParts[1].toInteger() * 100 + versionParts[2].toInteger()
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "games.wenzi.ogame"
|
namespace = "games.wenzi.ogame"
|
||||||
compileSdk = rootProject.ext.compileSdkVersion
|
compileSdk = rootProject.ext.compileSdkVersion
|
||||||
@@ -7,8 +17,8 @@ android {
|
|||||||
applicationId "games.wenzi.ogame"
|
applicationId "games.wenzi.ogame"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 15
|
versionCode appVersionCode
|
||||||
versionName "1.5.0"
|
versionName appVersionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
@@ -56,7 +66,7 @@ android {
|
|||||||
def abi = output.getFilter(com.android.build.OutputFile.ABI)
|
def abi = output.getFilter(com.android.build.OutputFile.ABI)
|
||||||
if (abi != null) {
|
if (abi != null) {
|
||||||
output.versionCodeOverride = abiVersionCode[abi] * 1000 + defaultConfig.versionCode
|
output.versionCodeOverride = abiVersionCode[abi] * 1000 + defaultConfig.versionCode
|
||||||
output.outputFileName = "OGame-Vue-Ts-${abi}.APK"
|
output.outputFileName = "OGame-Vue-Ts-${abi}.apk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ android {
|
|||||||
|
|
||||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(':capacitor-app')
|
||||||
|
implementation project(':capacitor-filesystem')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,46 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:hardwareAccelerated="true"
|
||||||
android:theme="@style/AppTheme">
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/title_activity_main"
|
android:label="@string/title_activity_main"
|
||||||
android:theme="@style/AppTheme.NoActionBarLaunch"
|
android:theme="@style/AppTheme.NoActionBarLaunch"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.fileprovider"
|
android:authorities="${applicationId}.fileprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths"></meta-data>
|
android:resource="@xml/file_paths"></meta-data>
|
||||||
</provider>
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,18 +1,100 @@
|
|||||||
package games.wenzi.ogame;
|
package games.wenzi.ogame;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import androidx.core.content.ContextCompat;
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
|
import android.webkit.WebSettings;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import androidx.core.splashscreen.SplashScreen;
|
||||||
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat;
|
||||||
import com.getcapacitor.BridgeActivity;
|
import com.getcapacitor.BridgeActivity;
|
||||||
|
|
||||||
public class MainActivity extends BridgeActivity {
|
public class MainActivity extends BridgeActivity {
|
||||||
|
private boolean isWebViewReady = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
// 安装 SplashScreen,必须在 super.onCreate 之前调用
|
||||||
|
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
||||||
|
|
||||||
|
// 保持 SplashScreen 直到 WebView 加载完成
|
||||||
|
splashScreen.setKeepOnScreenCondition(() -> !isWebViewReady);
|
||||||
|
|
||||||
|
// 设置淡出退出动画
|
||||||
|
splashScreen.setOnExitAnimationListener(splashScreenView -> {
|
||||||
|
// 创建淡出动画
|
||||||
|
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(
|
||||||
|
splashScreenView.getView(),
|
||||||
|
View.ALPHA,
|
||||||
|
1f,
|
||||||
|
0f
|
||||||
|
);
|
||||||
|
fadeOut.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||||
|
fadeOut.setDuration(300);
|
||||||
|
|
||||||
|
// 动画结束后移除 SplashScreen
|
||||||
|
fadeOut.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
splashScreenView.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fadeOut.start();
|
||||||
|
});
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// 设置状态栏颜色,防止 Capacitor 强制透明
|
|
||||||
Window window = getWindow();
|
Window window = getWindow();
|
||||||
window.setStatusBarColor(ContextCompat.getColor(this, R.color.status_bar_color));
|
|
||||||
window.setNavigationBarColor(ContextCompat.getColor(this, R.color.status_bar_color));
|
// 启用边到边显示(Edge-to-Edge)
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(window, false);
|
||||||
|
|
||||||
|
// 设置透明状态栏和导航栏
|
||||||
|
window.setStatusBarColor(Color.TRANSPARENT);
|
||||||
|
window.setNavigationBarColor(Color.TRANSPARENT);
|
||||||
|
|
||||||
|
// 设置状态栏图标为浅色(因为背景是深色)
|
||||||
|
WindowInsetsControllerCompat controller = WindowCompat.getInsetsController(window, window.getDecorView());
|
||||||
|
if (controller != null) {
|
||||||
|
controller.setAppearanceLightStatusBars(false);
|
||||||
|
controller.setAppearanceLightNavigationBars(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
WebView webView = getBridge().getWebView();
|
||||||
|
if (webView != null) {
|
||||||
|
WebSettings settings = webView.getSettings();
|
||||||
|
// 禁用 WebView 文本缩放,防止系统字体大小设置影响布局
|
||||||
|
settings.setTextZoom(100);
|
||||||
|
// 优化 WebView 性能
|
||||||
|
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
|
||||||
|
settings.setDomStorageEnabled(true);
|
||||||
|
settings.setDatabaseEnabled(true);
|
||||||
|
// 启用硬件加速渲染
|
||||||
|
webView.setLayerType(android.view.View.LAYER_TYPE_HARDWARE, null);
|
||||||
|
|
||||||
|
// 监听页面加载进度,加载完成后隐藏 SplashScreen
|
||||||
|
webView.setWebChromeClient(new WebChromeClient() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(WebView view, int newProgress) {
|
||||||
|
super.onProgressChanged(view, newProgress);
|
||||||
|
// 当页面加载达到 80% 时认为可以显示
|
||||||
|
if (newProgress >= 80) {
|
||||||
|
isWebViewReady = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
android/app/src/main/res/drawable/splash_icon.xml
Normal file
9
android/app/src/main/res/drawable/splash_icon.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 启动画面专用图标,使用较小的尺寸避免模糊 -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:width="96dp"
|
||||||
|
android:height="96dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:drawable="@mipmap/ic_launcher_foreground" />
|
||||||
|
</layer-list>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="status_bar_color">#1a1a2e</color>
|
<color name="status_bar_color">#1a1a2e</color>
|
||||||
</resources>
|
<color name="splash_background">#0f0f1a</color>
|
||||||
|
</resources>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#FFFFFF</color>
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">OGame Vue Ts</string>
|
<string name="app_name">OGame Vue Ts</string>
|
||||||
<string name="title_activity_main">OGame Vue Ts</string>
|
<string name="title_activity_main">OGame Vue Ts</string>
|
||||||
<string name="package_name">games.wenzi.ogame</string>
|
<string name="package_name">games.wenzi.ogame</string>
|
||||||
<string name="custom_url_scheme">games.wenzi.ogame</string>
|
<string name="custom_url_scheme">games.wenzi.ogame</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,24 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="colorPrimary">#1a1a2e</item>
|
<item name="colorPrimary">#1a1a2e</item>
|
||||||
<item name="colorPrimaryDark">#0f0f1a</item>
|
<item name="colorPrimaryDark">#0f0f1a</item>
|
||||||
<item name="colorAccent">#6366f1</item>
|
<item name="colorAccent">#6366f1</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
|
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
<item name="android:background">@null</item>
|
<item name="android:background">@null</item>
|
||||||
<item name="android:statusBarColor">#1a1a2e</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="android:navigationBarColor">#1a1a2e</item>
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
</style>
|
<item name="android:windowTranslucentStatus">false</item>
|
||||||
|
<item name="android:windowTranslucentNavigation">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- 启动画面主题 - 延长显示直到 WebView 加载完成 -->
|
||||||
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
||||||
<item name="android:background">@drawable/splash</item>
|
<item name="windowSplashScreenBackground">@color/splash_background</item>
|
||||||
</style>
|
<!-- 使用较小的自定义图标避免模糊 -->
|
||||||
|
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
|
||||||
|
<!-- 图标大小限制 -->
|
||||||
|
<item name="windowSplashScreenIconBackgroundColor">@color/splash_background</item>
|
||||||
|
<!-- 退出动画时长 -->
|
||||||
|
<item name="android:windowSplashScreenAnimationDuration">500</item>
|
||||||
|
<item name="postSplashScreenTheme">@style/AppTheme.NoActionBar</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<external-path name="my_images" path="." />
|
<external-path name="my_images" path="." />
|
||||||
<cache-path name="my_cache_images" path="." />
|
<cache-path name="my_cache_images" path="." />
|
||||||
</paths>
|
</paths>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<network-security-config>
|
<network-security-config>
|
||||||
<base-config cleartextTrafficPermitted="true">
|
<base-config cleartextTrafficPermitted="true">
|
||||||
<trust-anchors>
|
<trust-anchors>
|
||||||
<certificates src="system" />
|
<certificates src="system" />
|
||||||
</trust-anchors>
|
</trust-anchors>
|
||||||
</base-config>
|
</base-config>
|
||||||
</network-security-config>
|
</network-security-config>
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||||
include ':capacitor-android'
|
include ':capacitor-android'
|
||||||
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/android/capacitor')
|
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/android/capacitor')
|
||||||
|
|
||||||
|
include ':capacitor-app'
|
||||||
|
project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/app/android')
|
||||||
|
|
||||||
|
include ':capacitor-filesystem'
|
||||||
|
project(':capacitor-filesystem').projectDir = new File('../node_modules/.pnpm/@capacitor+filesystem@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/filesystem/android')
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ const config: CapacitorConfig = {
|
|||||||
appId: 'games.wenzi.ogame',
|
appId: 'games.wenzi.ogame',
|
||||||
appName: 'OGame Vue Ts',
|
appName: 'OGame Vue Ts',
|
||||||
webDir: 'docs',
|
webDir: 'docs',
|
||||||
server: {
|
server: { androidScheme: 'https', cacheControl: 'max-age=31536000' },
|
||||||
androidScheme: 'https'
|
|
||||||
},
|
|
||||||
android: {
|
android: {
|
||||||
buildOptions: {
|
buildOptions: { keystorePath: undefined, keystoreAlias: undefined },
|
||||||
keystorePath: undefined,
|
webContentsDebuggingEnabled: false,
|
||||||
keystoreAlias: undefined
|
allowMixedContent: false,
|
||||||
}
|
hardwareAcceleration: true
|
||||||
}
|
},
|
||||||
|
// 禁用键盘自动调整视口
|
||||||
|
plugins: { Keyboard: { resize: 'none' } }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
|
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||||
|
<link rel="apple-touch-icon" href="/logo.svg" />
|
||||||
<title>OGame-Vue-Ts</title>
|
<title>OGame-Vue-Ts</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -13,8 +14,9 @@
|
|||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
<!-- 统计勿删 -->
|
<!-- 统计勿删 -->
|
||||||
<script charset="UTF-8" id="LA_COLLECT" src="https://sdk.51.la/js-sdk-pro.min.js"></script>
|
<script>
|
||||||
<script>LA.init({ id: "L298GYqn6JhAO0VU", ck: "L298GYqn6JhAO0VU", autoTrack: true, hashMode: true })</script>
|
!function (p) { "use strict"; !function (t) { var s = window, e = document, i = p, c = "".concat("https:" === e.location.protocol ? "https://" : "http://", "sdk.51.la/js-sdk-pro.min.js"), n = e.createElement("script"), r = e.getElementsByTagName("script")[0]; n.type = "text/javascript", n.setAttribute("charset", "UTF-8"), n.async = !0, n.src = c, n.id = "LA_COLLECT", i.d = n; var o = function () { s.LA.ids.push(i) }; s.LA ? s.LA.ids && o() : (s.LA = p, s.LA.ids = [], o()), r.parentNode.insertBefore(n, r) }() }({ id: "L298GYqn6JhAO0VU", ck: "L298GYqn6JhAO0VU", autoTrack: true, hashMode: true });
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
75
main.go
75
main.go
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"embed"
|
"embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -20,10 +22,16 @@ var content embed.FS
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// --- 1. 命令行参数配置 ---
|
// --- 1. 命令行参数配置 ---
|
||||||
// 定义 -port 参数,默认为 0(自动分配)
|
// 定义 -port 参数,默认为 -1(表示未指定,需要交互选择)
|
||||||
portPtr := flag.Int("port", 0, "指定运行端口 (例如: 8080),不指定则自动分配可用端口")
|
portPtr := flag.Int("port", -1, "指定运行端口 (例如: 8080),不指定则显示交互菜单")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
// 如果没有通过命令行指定端口,显示交互式菜单
|
||||||
|
port := *portPtr
|
||||||
|
if port == -1 {
|
||||||
|
port = showPortMenu()
|
||||||
|
}
|
||||||
|
|
||||||
// --- 2. 静态资源处理 ---
|
// --- 2. 静态资源处理 ---
|
||||||
// 获取 docs 子目录的文件系统句柄
|
// 获取 docs 子目录的文件系统句柄
|
||||||
distFS, err := fs.Sub(content, "docs")
|
distFS, err := fs.Sub(content, "docs")
|
||||||
@@ -60,10 +68,10 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// --- 3. 端口监听逻辑 ---
|
// --- 3. 端口监听逻辑 ---
|
||||||
addr := fmt.Sprintf("0.0.0.0:%d", *portPtr)
|
addr := fmt.Sprintf("0.0.0.0:%d", port)
|
||||||
listener, err := net.Listen("tcp", addr)
|
listener, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("错误: 端口 %d 已被占用或监听失败: %v\n", *portPtr, err)
|
fmt.Printf("错误: 端口 %d 已被占用或监听失败: %v\n", port, err)
|
||||||
// 停留 5 秒让用户看到错误信息
|
// 停留 5 秒让用户看到错误信息
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -79,8 +87,8 @@ func main() {
|
|||||||
fmt.Printf("启动时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
|
fmt.Printf("启动时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
fmt.Printf("本地访问: %s\n", localUrl)
|
fmt.Printf("本地访问: %s\n", localUrl)
|
||||||
fmt.Printf("局域网访问: %s\n", lanUrl)
|
fmt.Printf("局域网访问: %s\n", lanUrl)
|
||||||
if *portPtr != 0 {
|
if port != 0 {
|
||||||
fmt.Printf("运行模式: 固定端口 (%d)\n", *portPtr)
|
fmt.Printf("运行模式: 固定端口 (%d)\n", port)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("运行模式: 自动分配端口\n")
|
fmt.Printf("运行模式: 自动分配端口\n")
|
||||||
}
|
}
|
||||||
@@ -131,4 +139,59 @@ func openBrowser(url string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = exec.Command(cmd, args...).Start()
|
_ = exec.Command(cmd, args...).Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示端口选择菜单
|
||||||
|
func showPortMenu() int {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
|
fmt.Println("=======================================")
|
||||||
|
fmt.Println(" OGame Vue Ts 服务器启动")
|
||||||
|
fmt.Println("=======================================")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("请选择端口模式:")
|
||||||
|
fmt.Println(" [1] 随机端口 (自动分配可用端口)")
|
||||||
|
fmt.Println(" [2] 自定义端口 (指定固定端口)")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Print("请输入选项 (1/2): ")
|
||||||
|
|
||||||
|
for {
|
||||||
|
input, _ := reader.ReadString('\n')
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
|
||||||
|
switch input {
|
||||||
|
case "1", "":
|
||||||
|
fmt.Println("\n已选择: 随机端口模式")
|
||||||
|
return 0
|
||||||
|
case "2":
|
||||||
|
return inputCustomPort(reader)
|
||||||
|
default:
|
||||||
|
fmt.Print("无效输入,请输入 1 或 2: ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入自定义端口
|
||||||
|
func inputCustomPort(reader *bufio.Reader) int {
|
||||||
|
fmt.Print("请输入端口号 (1-65535,推荐: 8080): ")
|
||||||
|
|
||||||
|
for {
|
||||||
|
input, _ := reader.ReadString('\n')
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
|
||||||
|
// 如果直接回车,使用默认端口 8080
|
||||||
|
if input == "" {
|
||||||
|
fmt.Println("\n已选择: 固定端口 8080")
|
||||||
|
return 8080
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := strconv.Atoi(input)
|
||||||
|
if err != nil || port < 1 || port > 65535 {
|
||||||
|
fmt.Print("无效端口号,请输入 1-65535 之间的数字: ")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n已选择: 固定端口 %d\n", port)
|
||||||
|
return port
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
"email": "1962257451@qq.com"
|
"email": "1962257451@qq.com"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.5.5",
|
"version": "1.6.0",
|
||||||
"buildDate": "2025/12/25 18:23:43",
|
"buildDate": "2026/1/6 03:05:21",
|
||||||
"main": "dist-electron/main.js",
|
"main": "dist-electron/main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"finalhandler": "^2.1.1",
|
"finalhandler": "^2.1.1",
|
||||||
|
"lightningcss": "^1.30.2",
|
||||||
"lucide-vue-next": "^0.556.0",
|
"lucide-vue-next": "^0.556.0",
|
||||||
"marked": "^17.0.1",
|
"marked": "^17.0.1",
|
||||||
"motion-v": "^1.7.4",
|
"motion-v": "^1.7.4",
|
||||||
@@ -47,12 +48,12 @@
|
|||||||
"vue-sonner": "^2.0.9"
|
"vue-sonner": "^2.0.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@csstools/postcss-cascade-layers": "^5.0.2",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/node": "^24.10.2",
|
"@types/node": "^24.10.2",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"@vue/tsconfig": "^0.8.1",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
"autoprefixer": "^10.4.23",
|
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
|
|||||||
69
pnpm-lock.yaml
generated
69
pnpm-lock.yaml
generated
@@ -44,6 +44,9 @@ importers:
|
|||||||
finalhandler:
|
finalhandler:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
lightningcss:
|
||||||
|
specifier: ^1.30.2
|
||||||
|
version: 1.30.2
|
||||||
lucide-vue-next:
|
lucide-vue-next:
|
||||||
specifier: ^0.556.0
|
specifier: ^0.556.0
|
||||||
version: 0.556.0(vue@3.5.25(typescript@5.9.3))
|
version: 0.556.0(vue@3.5.25(typescript@5.9.3))
|
||||||
@@ -78,6 +81,9 @@ importers:
|
|||||||
specifier: ^2.0.9
|
specifier: ^2.0.9
|
||||||
version: 2.0.9
|
version: 2.0.9
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@csstools/postcss-cascade-layers':
|
||||||
|
specifier: ^5.0.2
|
||||||
|
version: 5.0.2(postcss@8.5.6)
|
||||||
'@types/crypto-js':
|
'@types/crypto-js':
|
||||||
specifier: ^4.2.2
|
specifier: ^4.2.2
|
||||||
version: 4.2.2
|
version: 4.2.2
|
||||||
@@ -93,9 +99,6 @@ importers:
|
|||||||
'@vue/tsconfig':
|
'@vue/tsconfig':
|
||||||
specifier: ^0.8.1
|
specifier: ^0.8.1
|
||||||
version: 0.8.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
|
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:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -674,6 +677,18 @@ packages:
|
|||||||
'@capacitor/synapse@1.0.4':
|
'@capacitor/synapse@1.0.4':
|
||||||
resolution: {integrity: sha512-/C1FUo8/OkKuAT4nCIu/34ny9siNHr9qtFezu4kxm6GY1wNFxrCFWjfYx5C1tUhVGz3fxBABegupkpjXvjCHrw==}
|
resolution: {integrity: sha512-/C1FUo8/OkKuAT4nCIu/34ny9siNHr9qtFezu4kxm6GY1wNFxrCFWjfYx5C1tUhVGz3fxBABegupkpjXvjCHrw==}
|
||||||
|
|
||||||
|
'@csstools/postcss-cascade-layers@5.0.2':
|
||||||
|
resolution: {integrity: sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
postcss: ^8.4
|
||||||
|
|
||||||
|
'@csstools/selector-specificity@5.0.0':
|
||||||
|
resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
postcss-selector-parser: ^7.0.0
|
||||||
|
|
||||||
'@develar/schema-utils@2.6.5':
|
'@develar/schema-utils@2.6.5':
|
||||||
resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==}
|
resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==}
|
||||||
engines: {node: '>= 8.9.0'}
|
engines: {node: '>= 8.9.0'}
|
||||||
@@ -1672,13 +1687,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
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:
|
available-typed-arrays@1.0.7:
|
||||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1922,6 +1930,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
|
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
cssesc@3.0.0:
|
||||||
|
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
csstype@3.2.3:
|
csstype@3.2.3:
|
||||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||||
|
|
||||||
@@ -2215,9 +2228,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
fraction.js@5.3.4:
|
|
||||||
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
|
|
||||||
|
|
||||||
framer-motion@12.23.12:
|
framer-motion@12.23.12:
|
||||||
resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==}
|
resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3114,8 +3124,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
postcss-value-parser@4.2.0:
|
postcss-selector-parser@7.1.1:
|
||||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
postcss@8.5.6:
|
postcss@8.5.6:
|
||||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||||
@@ -4659,6 +4670,16 @@ snapshots:
|
|||||||
|
|
||||||
'@capacitor/synapse@1.0.4': {}
|
'@capacitor/synapse@1.0.4': {}
|
||||||
|
|
||||||
|
'@csstools/postcss-cascade-layers@5.0.2(postcss@8.5.6)':
|
||||||
|
dependencies:
|
||||||
|
'@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.1)
|
||||||
|
postcss: 8.5.6
|
||||||
|
postcss-selector-parser: 7.1.1
|
||||||
|
|
||||||
|
'@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.1)':
|
||||||
|
dependencies:
|
||||||
|
postcss-selector-parser: 7.1.1
|
||||||
|
|
||||||
'@develar/schema-utils@2.6.5':
|
'@develar/schema-utils@2.6.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
@@ -5680,15 +5701,6 @@ snapshots:
|
|||||||
|
|
||||||
at-least-node@1.0.0: {}
|
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:
|
available-typed-arrays@1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
@@ -5977,6 +5989,8 @@ snapshots:
|
|||||||
|
|
||||||
crypto-random-string@2.0.0: {}
|
crypto-random-string@2.0.0: {}
|
||||||
|
|
||||||
|
cssesc@3.0.0: {}
|
||||||
|
|
||||||
csstype@3.2.3: {}
|
csstype@3.2.3: {}
|
||||||
|
|
||||||
data-view-buffer@1.0.2:
|
data-view-buffer@1.0.2:
|
||||||
@@ -6395,8 +6409,6 @@ snapshots:
|
|||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
fraction.js@5.3.4: {}
|
|
||||||
|
|
||||||
framer-motion@12.23.12:
|
framer-motion@12.23.12:
|
||||||
dependencies:
|
dependencies:
|
||||||
motion-dom: 12.23.12
|
motion-dom: 12.23.12
|
||||||
@@ -7267,7 +7279,10 @@ snapshots:
|
|||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|
||||||
postcss-value-parser@4.2.0: {}
|
postcss-selector-parser@7.1.1:
|
||||||
|
dependencies:
|
||||||
|
cssesc: 3.0.0
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
postcss@8.5.6:
|
postcss@8.5.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
103
src/App.vue
103
src/App.vue
@@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 首页:无侧边栏/头部 -->
|
<!-- 首页:无侧边栏/头部 -->
|
||||||
<template v-if="isHomePage">
|
<RouterView v-if="isHomePage" />
|
||||||
<RouterView />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 其他页面:完整布局(含侧边栏) -->
|
<!-- 其他页面:完整布局(含侧边栏) -->
|
||||||
<SidebarProvider v-else :open="sidebarOpen" @update:open="handleSidebarOpenChange">
|
<SidebarProvider v-else :open="sidebarOpen" @update:open="handleSidebarOpenChange">
|
||||||
@@ -47,7 +45,7 @@
|
|||||||
<ChevronsUpDown class="h-4 w-4 shrink-0 text-muted-foreground ml-2" />
|
<ChevronsUpDown class="h-4 w-4 shrink-0 text-muted-foreground ml-2" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent class="w-72 p-0" side="bottom" align="start">
|
<PopoverContent class="w-70 p-0" side="bottom" align="start">
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<div class="px-2 py-1.5 mb-1 text-xs font-semibold text-muted-foreground">
|
<div class="px-2 py-1.5 mb-1 text-xs font-semibold text-muted-foreground">
|
||||||
{{ t('planet.switchPlanet') }}
|
{{ t('planet.switchPlanet') }}
|
||||||
@@ -133,13 +131,6 @@
|
|||||||
<SidebarMenuBadge v-if="item.path === '/fleet' && activeFleetMissionsCount > 0" class="bg-primary text-primary-foreground">
|
<SidebarMenuBadge v-if="item.path === '/fleet' && activeFleetMissionsCount > 0" class="bg-primary text-primary-foreground">
|
||||||
{{ activeFleetMissionsCount }}
|
{{ activeFleetMissionsCount }}
|
||||||
</SidebarMenuBadge>
|
</SidebarMenuBadge>
|
||||||
<!-- 未读外交报告数量 -->
|
|
||||||
<SidebarMenuBadge
|
|
||||||
v-if="item.path === '/diplomacy' && unreadDiplomaticReportsCount > 0"
|
|
||||||
class="bg-destructive text-destructive-foreground"
|
|
||||||
>
|
|
||||||
{{ unreadDiplomaticReportsCount }}
|
|
||||||
</SidebarMenuBadge>
|
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
@@ -387,7 +378,7 @@
|
|||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
|
|
||||||
<!-- 右下角固定通知按钮 -->
|
<!-- 右下角固定通知按钮 -->
|
||||||
<div class="fixed bottom-4 right-4 z-50 flex flex-col gap-2">
|
<div class="fixed bottom-4 right-4 z-50 flex flex-col gap-2" :class="{ 'bottom-15': Capacitor.isNativePlatform() }">
|
||||||
<!-- 返回顶部 -->
|
<!-- 返回顶部 -->
|
||||||
<BackToTop />
|
<BackToTop />
|
||||||
<!-- 队列通知 -->
|
<!-- 队列通知 -->
|
||||||
@@ -527,7 +518,7 @@
|
|||||||
import HintToast from '@/components/notifications/HintToast.vue'
|
import HintToast from '@/components/notifications/HintToast.vue'
|
||||||
import BackToTop from '@/components/common/BackToTop.vue'
|
import BackToTop from '@/components/common/BackToTop.vue'
|
||||||
import Sonner from '@/components/ui/sonner/Sonner.vue'
|
import Sonner from '@/components/ui/sonner/Sonner.vue'
|
||||||
import { MissionType, BuildingType, TechnologyType, DiplomaticEventType } from '@/types/game'
|
import { MissionType, BuildingType, TechnologyType, DiplomaticEventType, ShipType } from '@/types/game'
|
||||||
import type { FleetMission, NPC, MissileAttack } from '@/types/game'
|
import type { FleetMission, NPC, MissileAttack } from '@/types/game'
|
||||||
import { DIPLOMATIC_CONFIG } from '@/config/gameConfig'
|
import { DIPLOMATIC_CONFIG } from '@/config/gameConfig'
|
||||||
import type { VersionInfo } from '@/utils/versionCheck'
|
import type { VersionInfo } from '@/utils/versionCheck'
|
||||||
@@ -738,11 +729,6 @@
|
|||||||
return fleetMissions + flyingMissiles
|
return fleetMissions + flyingMissiles
|
||||||
})
|
})
|
||||||
|
|
||||||
// 未读外交报告数量
|
|
||||||
const unreadDiplomaticReportsCount = computed(() => {
|
|
||||||
return (gameStore.player.diplomaticReports || []).filter(r => !r.read).length
|
|
||||||
})
|
|
||||||
|
|
||||||
// 月球相关
|
// 月球相关
|
||||||
const moon = computed(() => {
|
const moon = computed(() => {
|
||||||
if (!planet.value || planet.value.isMoon) return null
|
if (!planet.value || planet.value.isMoon) return null
|
||||||
@@ -852,8 +838,14 @@
|
|||||||
|
|
||||||
// 计算离线收益(直接同步计算,应用游戏速度)
|
// 计算离线收益(直接同步计算,应用游戏速度)
|
||||||
const bonuses = officerLogic.calculateActiveBonuses(gameStore.player.officers, now)
|
const bonuses = officerLogic.calculateActiveBonuses(gameStore.player.officers, now)
|
||||||
|
const miningTechLevel = gameStore.player.technologies[TechnologyType.MiningTechnology] || 0
|
||||||
|
const techBonuses = {
|
||||||
|
mineralResearchLevel: gameStore.player.technologies[TechnologyType.MineralResearch] || 0,
|
||||||
|
crystalResearchLevel: gameStore.player.technologies[TechnologyType.CrystalResearch] || 0,
|
||||||
|
fuelResearchLevel: gameStore.player.technologies[TechnologyType.FuelResearch] || 0
|
||||||
|
}
|
||||||
gameStore.player.planets.forEach(planet => {
|
gameStore.player.planets.forEach(planet => {
|
||||||
resourceLogic.updatePlanetResources(planet, now, bonuses, gameStore.gameSpeed)
|
resourceLogic.updatePlanetResources(planet, now, bonuses, gameStore.gameSpeed, miningTechLevel, techBonuses)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 只在没有NPC星球时才生成(首次加载已有玩家数据时)
|
// 只在没有NPC星球时才生成(首次加载已有玩家数据时)
|
||||||
@@ -1079,6 +1071,19 @@
|
|||||||
const targetNpc = npcStore.npcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
|
const targetNpc = npcStore.npcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
|
||||||
if (targetNpc) {
|
if (targetNpc) {
|
||||||
diplomaticLogic.handleAttackReputation(gameStore.player, targetNpc, attackResult.battleResult, npcStore.npcs, gameStore.locale)
|
diplomaticLogic.handleAttackReputation(gameStore.player, targetNpc, attackResult.battleResult, npcStore.npcs, gameStore.locale)
|
||||||
|
|
||||||
|
// 同步战斗损失到NPC的实际星球数据
|
||||||
|
const npcPlanet = targetNpc.planets.find(p => p.id === targetPlanet.id)
|
||||||
|
if (npcPlanet) {
|
||||||
|
// 同步舰队损失
|
||||||
|
Object.entries(attackResult.battleResult.defenderLosses.fleet).forEach(([shipType, lost]) => {
|
||||||
|
npcPlanet.fleet[shipType as ShipType] = Math.max(0, (npcPlanet.fleet[shipType as ShipType] || 0) - lost)
|
||||||
|
})
|
||||||
|
// 同步防御损失(修复后的数据已在targetPlanet中)
|
||||||
|
npcPlanet.defense = { ...targetPlanet.defense }
|
||||||
|
// 同步资源(被掠夺后的)
|
||||||
|
npcPlanet.resources = { ...targetPlanet.resources }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,8 +1281,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mission.missionType === MissionType.Destroy) {
|
} else if (mission.missionType === MissionType.Destroy) {
|
||||||
// 处理行星毁灭任务
|
// 处理行星毁灭任务(需要先战斗,再计算毁灭概率)
|
||||||
const destroyResult = fleetLogic.processDestroyArrival(mission, targetPlanet, gameStore.player)
|
const destroyResult = await fleetLogic.processDestroyArrival(mission, targetPlanet, gameStore.player, null, gameStore.player.planets)
|
||||||
|
|
||||||
|
// 处理战斗报告(如果发生了战斗)
|
||||||
|
if (destroyResult.battleResult) {
|
||||||
|
gameStore.player.battleReports.push(destroyResult.battleResult)
|
||||||
|
|
||||||
|
// 处理战斗对NPC的影响
|
||||||
|
if (targetPlanet) {
|
||||||
|
const targetNpc = npcStore.npcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
|
||||||
|
if (targetNpc) {
|
||||||
|
diplomaticLogic.handleAttackReputation(gameStore.player, targetNpc, destroyResult.battleResult, npcStore.npcs, gameStore.locale)
|
||||||
|
|
||||||
|
// 同步战斗损失到NPC的实际星球数据
|
||||||
|
const npcPlanet = targetNpc.planets.find(p => p.id === targetPlanet.id)
|
||||||
|
if (npcPlanet) {
|
||||||
|
Object.entries(destroyResult.battleResult.defenderLosses.fleet).forEach(([shipType, lost]) => {
|
||||||
|
npcPlanet.fleet[shipType as ShipType] = Math.max(0, (npcPlanet.fleet[shipType as ShipType] || 0) - lost)
|
||||||
|
})
|
||||||
|
npcPlanet.defense = { ...targetPlanet.defense }
|
||||||
|
npcPlanet.resources = { ...targetPlanet.resources }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理新生成的月球
|
||||||
|
if (destroyResult.moon) {
|
||||||
|
gameStore.player.planets.push(destroyResult.moon)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理残骸场
|
||||||
|
if (destroyResult.debrisField) {
|
||||||
|
universeStore.debrisFields[destroyResult.debrisField.id] = destroyResult.debrisField
|
||||||
|
}
|
||||||
|
|
||||||
// 更新成就统计 - 行星毁灭
|
// 更新成就统计 - 行星毁灭
|
||||||
if (destroyResult.success) {
|
if (destroyResult.success) {
|
||||||
@@ -1317,12 +1355,14 @@
|
|||||||
? {
|
? {
|
||||||
destroyedPlanetName:
|
destroyedPlanetName:
|
||||||
targetPlanet?.name ||
|
targetPlanet?.name ||
|
||||||
`[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`
|
`[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`,
|
||||||
|
hadBattle: !!destroyResult.battleResult
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
failReason: destroyResult.failReason,
|
failReason: destroyResult.failReason,
|
||||||
destructionChance: destroyResult.destructionChance,
|
destructionChance: destroyResult.destructionChance,
|
||||||
deathstarsLost: destroyResult.deathstarsLost
|
deathstarsLost: destroyResult.deathstarsLost,
|
||||||
|
hadBattle: !!destroyResult.battleResult
|
||||||
},
|
},
|
||||||
read: false
|
read: false
|
||||||
})
|
})
|
||||||
@@ -1401,7 +1441,7 @@
|
|||||||
delete universeStore.debrisFields[destroyedDebrisId]
|
delete universeStore.debrisFields[destroyedDebrisId]
|
||||||
}
|
}
|
||||||
} else if (mission.missionType === MissionType.Expedition) {
|
} else if (mission.missionType === MissionType.Expedition) {
|
||||||
// 处理远征任务
|
// 处理探险任务
|
||||||
const expeditionResult = fleetLogic.processExpeditionArrival(mission)
|
const expeditionResult = fleetLogic.processExpeditionArrival(mission)
|
||||||
|
|
||||||
// 确保返回时间正确设置(兼容旧版本任务数据)
|
// 确保返回时间正确设置(兼容旧版本任务数据)
|
||||||
@@ -1413,12 +1453,12 @@
|
|||||||
mission.returnTime = now + flightDuration
|
mission.returnTime = now + flightDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新成就统计 - 远征
|
// 更新成就统计 - 探险
|
||||||
const isSuccessful =
|
const isSuccessful =
|
||||||
expeditionResult.eventType === 'resources' || expeditionResult.eventType === 'darkMatter' || expeditionResult.eventType === 'fleet'
|
expeditionResult.eventType === 'resources' || expeditionResult.eventType === 'darkMatter' || expeditionResult.eventType === 'fleet'
|
||||||
gameLogic.trackMissionStats(gameStore.player, 'expedition', { successful: isSuccessful })
|
gameLogic.trackMissionStats(gameStore.player, 'expedition', { successful: isSuccessful })
|
||||||
|
|
||||||
// 生成远征任务报告
|
// 生成探险任务报告
|
||||||
if (!gameStore.player.missionReports) {
|
if (!gameStore.player.missionReports) {
|
||||||
gameStore.player.missionReports = []
|
gameStore.player.missionReports = []
|
||||||
}
|
}
|
||||||
@@ -1690,10 +1730,16 @@
|
|||||||
// 应用损失到目标星球
|
// 应用损失到目标星球
|
||||||
missileLogic.applyMissileAttackResult(targetPlanet, impactResult.defenseLosses)
|
missileLogic.applyMissileAttackResult(targetPlanet, impactResult.defenseLosses)
|
||||||
|
|
||||||
// 如果目标是NPC的星球,扣除外交好感度
|
// 如果目标是NPC的星球,同步损失到NPC实际数据并扣除外交好感度
|
||||||
if (targetPlanet.ownerId && targetPlanet.ownerId !== gameStore.player.id) {
|
if (targetPlanet.ownerId && targetPlanet.ownerId !== gameStore.player.id) {
|
||||||
const targetNpc = npcStore.npcs.find(npc => npc.id === targetPlanet.ownerId)
|
const targetNpc = npcStore.npcs.find(npc => npc.id === targetPlanet.ownerId)
|
||||||
if (targetNpc) {
|
if (targetNpc) {
|
||||||
|
// 同步防御损失到NPC的实际星球数据
|
||||||
|
const npcPlanet = targetNpc.planets.find(p => p.id === targetPlanet.id)
|
||||||
|
if (npcPlanet) {
|
||||||
|
missileLogic.applyMissileAttackResult(npcPlanet, impactResult.defenseLosses)
|
||||||
|
}
|
||||||
|
|
||||||
// 导弹攻击扣除好感度
|
// 导弹攻击扣除好感度
|
||||||
const { REPUTATION_CHANGES } = DIPLOMATIC_CONFIG
|
const { REPUTATION_CHANGES } = DIPLOMATIC_CONFIG
|
||||||
const reputationLoss = REPUTATION_CHANGES.ATTACK / 2 // 导弹攻击的好感度惩罚是普通攻击的一半
|
const reputationLoss = REPUTATION_CHANGES.ATTACK / 2 // 导弹攻击的好感度惩罚是普通攻击的一半
|
||||||
@@ -2234,6 +2280,7 @@
|
|||||||
const switchToMoon = () => {
|
const switchToMoon = () => {
|
||||||
if (moon.value) {
|
if (moon.value) {
|
||||||
gameStore.currentPlanetId = moon.value.id
|
gameStore.currentPlanetId = moon.value.id
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2241,12 +2288,14 @@
|
|||||||
const switchToParentPlanet = () => {
|
const switchToParentPlanet = () => {
|
||||||
if (planet.value?.parentPlanetId) {
|
if (planet.value?.parentPlanetId) {
|
||||||
gameStore.currentPlanetId = planet.value.parentPlanetId
|
gameStore.currentPlanetId = planet.value.parentPlanetId
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换到指定星球
|
// 切换到指定星球
|
||||||
const switchToPlanet = (planetId: string) => {
|
const switchToPlanet = (planetId: string) => {
|
||||||
gameStore.currentPlanetId = planetId
|
gameStore.currentPlanetId = planetId
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换侧边栏
|
// 切换侧边栏
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ html.light {
|
|||||||
/* 队列添加动画 - 脉冲效果 */
|
/* 队列添加动画 - 脉冲效果 */
|
||||||
@keyframes queue-pulse-animation {
|
@keyframes queue-pulse-animation {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(1);
|
transform: scale3d(1, 1, 1);
|
||||||
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
|
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: scale(1.1);
|
transform: scale3d(1.1, 1.1, 1);
|
||||||
box-shadow: 0 0 0 8px rgba(34, 197, 94, 0);
|
box-shadow: 0 0 0 8px rgba(34, 197, 94, 0);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1);
|
transform: scale3d(1, 1, 1);
|
||||||
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
|
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<!-- 可缩放内容包装器 -->
|
<!-- 可缩放内容包装器 -->
|
||||||
<div
|
<div
|
||||||
class="map-content origin-top-left transition-transform duration-200"
|
class="map-content origin-top-left transition-transform duration-200"
|
||||||
:style="{ transform: `scale(${zoomLevel})`, minWidth: calculatedMapWidth + 'px', minHeight: calculatedMapHeight + 'px' }"
|
:style="{ transform: `scale3d(${zoomLevel}, ${zoomLevel}, 1)`, minWidth: calculatedMapWidth + 'px', minHeight: calculatedMapHeight + 'px' }"
|
||||||
>
|
>
|
||||||
<!-- SVG连接线 - 位置与节点容器对齐 -->
|
<!-- SVG连接线 - 位置与节点容器对齐 -->
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -176,7 +176,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.quest-available .quest-node:hover {
|
.quest-available .quest-node:hover {
|
||||||
transform: scale(1.05);
|
transform: scale3d(1.05, 1.05, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse-glow {
|
@keyframes pulse-glow {
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog :open="true" @update:open="handleClose">
|
<Dialog :open="true" @update:open="handleClose">
|
||||||
<DialogContent class="max-w-2xl p-0 overflow-hidden bg-gradient-to-b from-background to-background/95">
|
<DialogContent class="max-w-2xl p-0 overflow-hidden bg-gradient-to-b from-background to-background/95">
|
||||||
|
<!-- 可访问性标题(隐藏) -->
|
||||||
|
<VisuallyHidden>
|
||||||
|
<DialogTitle>{{ t('campaign.dialogue.title') }}</DialogTitle>
|
||||||
|
<DialogDescription>{{ t('campaign.dialogue.description') }}</DialogDescription>
|
||||||
|
</VisuallyHidden>
|
||||||
|
|
||||||
<!-- 对话框头部 - 星空效果 -->
|
<!-- 对话框头部 - 星空效果 -->
|
||||||
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
||||||
<div class="stars-bg" />
|
<div class="stars-bg" />
|
||||||
@@ -80,7 +86,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
||||||
|
import { VisuallyHidden } from 'reka-ui'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import type { StoryDialogue, DialogueChoice } from '@/types/game'
|
import type { StoryDialogue, DialogueChoice } from '@/types/game'
|
||||||
import { User, Bot, HelpCircle, MessageCircle, ChevronRight } from 'lucide-vue-next'
|
import { User, Bot, HelpCircle, MessageCircle, ChevronRight } from 'lucide-vue-next'
|
||||||
|
|||||||
@@ -491,9 +491,20 @@
|
|||||||
return currentPlanet.value.buildings['naniteFactory'] || 0
|
return currentPlanet.value.buildings['naniteFactory'] || 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取研究所等级(用于研究时间计算)
|
// 获取有效研究所等级(考虑星际研究网络)
|
||||||
const researchLabLevel = computed(() => {
|
const researchLabLevel = computed(() => {
|
||||||
if (!currentPlanet.value) return 0
|
if (!currentPlanet.value) return 0
|
||||||
|
const intergalacticResearchNetworkLevel = gameStore.player.technologies[TechnologyType.IntergalacticResearchNetwork] || 0
|
||||||
|
|
||||||
|
// 如果有星际研究网络,计算有效实验室等级
|
||||||
|
if (intergalacticResearchNetworkLevel > 0) {
|
||||||
|
return researchLogic.calculateEffectiveLabLevel(
|
||||||
|
gameStore.player.planets,
|
||||||
|
currentPlanet.value.id,
|
||||||
|
intergalacticResearchNetworkLevel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return currentPlanet.value.buildings['researchLab'] || 0
|
return currentPlanet.value.buildings['researchLab'] || 0
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -502,6 +513,12 @@
|
|||||||
return gameStore.player.technologies['energyTechnology'] || 0
|
return gameStore.player.technologies['energyTechnology'] || 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 获取大学等级(用于研究时间计算)
|
||||||
|
const universityLevel = computed(() => {
|
||||||
|
if (!currentPlanet.value) return 0
|
||||||
|
return currentPlanet.value.buildings['university'] || 0
|
||||||
|
})
|
||||||
|
|
||||||
// 翻译键(转换为复数形式)
|
// 翻译键(转换为复数形式)
|
||||||
const typeKey = computed(() => {
|
const typeKey = computed(() => {
|
||||||
const typeMap = {
|
const typeMap = {
|
||||||
@@ -931,7 +948,9 @@
|
|||||||
level - 1,
|
level - 1,
|
||||||
activeBonuses.value.researchSpeedBonus,
|
activeBonuses.value.researchSpeedBonus,
|
||||||
researchLabLevel.value,
|
researchLabLevel.value,
|
||||||
energyTechLevel.value
|
energyTechLevel.value,
|
||||||
|
1,
|
||||||
|
universityLevel.value
|
||||||
)
|
)
|
||||||
|
|
||||||
let researchSpeedBonus = 0
|
let researchSpeedBonus = 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="[colors[type], sizes[size], 'rounded shadow-sm']" />
|
<span :class="[colors[type], sizes[size], 'inline-block rounded shrink-0']" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -8,21 +8,22 @@
|
|||||||
size?: 'sm' | 'md' | 'lg'
|
size?: 'sm' | 'md' | 'lg'
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
withDefaults(defineProps<Props>(), {
|
||||||
size: 'md'
|
size: 'md'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 使用纯色背景,在 Android WebView 中更可靠
|
||||||
const colors = {
|
const colors = {
|
||||||
metal: 'bg-gradient-to-br from-slate-400 to-slate-600',
|
metal: 'bg-slate-500',
|
||||||
crystal: 'bg-gradient-to-br from-cyan-400 to-blue-600',
|
crystal: 'bg-cyan-500',
|
||||||
deuterium: 'bg-gradient-to-br from-green-400 to-emerald-600',
|
deuterium: 'bg-green-500',
|
||||||
darkMatter: 'bg-gradient-to-br from-purple-600 to-indigo-900',
|
darkMatter: 'bg-purple-700',
|
||||||
energy: 'bg-gradient-to-br from-yellow-400 to-orange-500'
|
energy: 'bg-yellow-500'
|
||||||
}
|
}
|
||||||
|
|
||||||
const sizes = {
|
const sizes = {
|
||||||
sm: 'w-3 h-3',
|
sm: 'w-3 h-3 min-w-3 min-h-3',
|
||||||
md: 'w-4 h-4',
|
md: 'w-4 h-4 min-w-4 min-h-4',
|
||||||
lg: 'w-5 h-5'
|
lg: 'w-5 h-5 min-w-5 min-h-5'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -178,6 +178,16 @@
|
|||||||
type: 'attacker-loss' | 'defender-loss' | 'info'
|
type: 'attacker-loss' | 'defender-loss' | 'info'
|
||||||
}
|
}
|
||||||
const battleLogs = ref<BattleLog[]>([])
|
const battleLogs = ref<BattleLog[]>([])
|
||||||
|
const MAX_LOGS = 100 // 限制日志数量,防止100回合战斗导致性能问题
|
||||||
|
|
||||||
|
// 添加日志的辅助函数,自动限制数量
|
||||||
|
const addBattleLog = (log: BattleLog) => {
|
||||||
|
battleLogs.value.push(log)
|
||||||
|
// 如果超过最大数量,删除最旧的日志
|
||||||
|
if (battleLogs.value.length > MAX_LOGS) {
|
||||||
|
battleLogs.value = battleLogs.value.slice(-MAX_LOGS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const totalRounds = computed(() => props.report.roundDetails?.length || props.report.rounds || 1)
|
const totalRounds = computed(() => props.report.roundDetails?.length || props.report.rounds || 1)
|
||||||
@@ -367,97 +377,101 @@
|
|||||||
if (currentRoundIndex.value >= totalRounds.value) return
|
if (currentRoundIndex.value >= totalRounds.value) return
|
||||||
|
|
||||||
isPlayingRound = true
|
isPlayingRound = true
|
||||||
const speed = parseFloat(speedMultiplier.value)
|
try {
|
||||||
const roundIndex = currentRoundIndex.value
|
const speed = parseFloat(speedMultiplier.value) || 1
|
||||||
const roundData = props.report.roundDetails?.[roundIndex]
|
const roundIndex = currentRoundIndex.value
|
||||||
|
const roundData = props.report.roundDetails?.[roundIndex]
|
||||||
|
|
||||||
// 攻击动画阶段
|
// 攻击动画阶段
|
||||||
attackAnimationPhase.value = 'attack'
|
attackAnimationPhase.value = 'attack'
|
||||||
|
|
||||||
// 添加日志
|
// 添加日志
|
||||||
battleLogs.value.push({
|
addBattleLog({
|
||||||
round: roundIndex + 1,
|
round: roundIndex + 1,
|
||||||
message: t('messagesView.roundStarted').replace('{round}', String(roundIndex + 1)),
|
message: t('messagesView.roundStarted').replace('{round}', String(roundIndex + 1)),
|
||||||
type: 'info'
|
type: 'info'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 等待攻击动画
|
// 等待攻击动画
|
||||||
await sleep(400 / speed)
|
await sleep(400 / speed)
|
||||||
|
|
||||||
// 伤害阶段
|
// 伤害阶段
|
||||||
attackAnimationPhase.value = 'damage'
|
attackAnimationPhase.value = 'damage'
|
||||||
|
|
||||||
// 计算当前回合的损失数字
|
// 计算当前回合的损失数字
|
||||||
if (roundData) {
|
if (roundData) {
|
||||||
const attackerLoss = Object.values(roundData.attackerLosses).reduce((sum, count) => sum + count, 0)
|
const attackerLoss = Object.values(roundData.attackerLosses || {}).reduce((sum, count) => sum + count, 0)
|
||||||
const defenderLoss =
|
const defenderLoss =
|
||||||
Object.values(roundData.defenderLosses.fleet || {}).reduce((sum, count) => sum + count, 0) +
|
Object.values(roundData.defenderLosses?.fleet || {}).reduce((sum, count) => sum + count, 0) +
|
||||||
Object.values(roundData.defenderLosses.defense || {}).reduce((sum, count) => sum + count, 0)
|
Object.values(roundData.defenderLosses?.defense || {}).reduce((sum, count) => sum + count, 0)
|
||||||
displayedLosses.value = { attacker: attackerLoss, defender: defenderLoss }
|
displayedLosses.value = { attacker: attackerLoss, defender: defenderLoss }
|
||||||
} else {
|
} else {
|
||||||
displayedLosses.value = { attacker: 0, defender: 0 }
|
displayedLosses.value = { attacker: 0, defender: 0 }
|
||||||
|
}
|
||||||
|
showDamageNumbers.value = true
|
||||||
|
|
||||||
|
if (roundData) {
|
||||||
|
// 记录攻击方损失
|
||||||
|
for (const [shipType, count] of Object.entries(roundData.attackerLosses || {})) {
|
||||||
|
if (count > 0) {
|
||||||
|
explodingShips.value.push({ side: 'attacker', type: shipType })
|
||||||
|
addBattleLog({
|
||||||
|
round: roundIndex + 1,
|
||||||
|
message: t('messagesView.shipDestroyed')
|
||||||
|
.replace('{count}', String(count))
|
||||||
|
.replace('{ship}', SHIPS.value[shipType as ShipType]?.name || shipType),
|
||||||
|
type: 'attacker-loss'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录防守方损失
|
||||||
|
for (const [shipType, count] of Object.entries(roundData.defenderLosses?.fleet || {})) {
|
||||||
|
if (count > 0) {
|
||||||
|
explodingShips.value.push({ side: 'defender', type: shipType })
|
||||||
|
addBattleLog({
|
||||||
|
round: roundIndex + 1,
|
||||||
|
message: t('messagesView.shipDestroyed')
|
||||||
|
.replace('{count}', String(count))
|
||||||
|
.replace('{ship}', SHIPS.value[shipType as ShipType]?.name || shipType),
|
||||||
|
type: 'defender-loss'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [defType, count] of Object.entries(roundData.defenderLosses?.defense || {})) {
|
||||||
|
if (count > 0) {
|
||||||
|
explodingShips.value.push({ side: 'defender', type: defType })
|
||||||
|
addBattleLog({
|
||||||
|
round: roundIndex + 1,
|
||||||
|
message: t('messagesView.defenseDestroyed')
|
||||||
|
.replace('{count}', String(count))
|
||||||
|
.replace('{defense}', DEFENSES.value[defType as DefenseType]?.name || defType),
|
||||||
|
type: 'defender-loss'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待伤害显示
|
||||||
|
await sleep(600 / speed)
|
||||||
|
|
||||||
|
// 清理状态
|
||||||
|
attackAnimationPhase.value = 'idle'
|
||||||
|
showDamageNumbers.value = false
|
||||||
|
explodingShips.value = []
|
||||||
|
|
||||||
|
currentRoundIndex.value++
|
||||||
|
} finally {
|
||||||
|
// 确保锁始终被释放,即使发生错误
|
||||||
|
isPlayingRound = false
|
||||||
}
|
}
|
||||||
showDamageNumbers.value = true
|
|
||||||
|
|
||||||
if (roundData) {
|
|
||||||
// 记录攻击方损失
|
|
||||||
for (const [shipType, count] of Object.entries(roundData.attackerLosses)) {
|
|
||||||
if (count > 0) {
|
|
||||||
explodingShips.value.push({ side: 'attacker', type: shipType })
|
|
||||||
battleLogs.value.push({
|
|
||||||
round: roundIndex + 1,
|
|
||||||
message: t('messagesView.shipDestroyed')
|
|
||||||
.replace('{count}', String(count))
|
|
||||||
.replace('{ship}', SHIPS.value[shipType as ShipType]?.name || shipType),
|
|
||||||
type: 'attacker-loss'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录防守方损失
|
|
||||||
for (const [shipType, count] of Object.entries(roundData.defenderLosses.fleet || {})) {
|
|
||||||
if (count > 0) {
|
|
||||||
explodingShips.value.push({ side: 'defender', type: shipType })
|
|
||||||
battleLogs.value.push({
|
|
||||||
round: roundIndex + 1,
|
|
||||||
message: t('messagesView.shipDestroyed')
|
|
||||||
.replace('{count}', String(count))
|
|
||||||
.replace('{ship}', SHIPS.value[shipType as ShipType]?.name || shipType),
|
|
||||||
type: 'defender-loss'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [defType, count] of Object.entries(roundData.defenderLosses.defense || {})) {
|
|
||||||
if (count > 0) {
|
|
||||||
explodingShips.value.push({ side: 'defender', type: defType })
|
|
||||||
battleLogs.value.push({
|
|
||||||
round: roundIndex + 1,
|
|
||||||
message: t('messagesView.defenseDestroyed')
|
|
||||||
.replace('{count}', String(count))
|
|
||||||
.replace('{defense}', DEFENSES.value[defType as DefenseType]?.name || defType),
|
|
||||||
type: 'defender-loss'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待伤害显示
|
|
||||||
await sleep(600 / speed)
|
|
||||||
|
|
||||||
// 清理状态
|
|
||||||
attackAnimationPhase.value = 'idle'
|
|
||||||
showDamageNumbers.value = false
|
|
||||||
explodingShips.value = []
|
|
||||||
|
|
||||||
currentRoundIndex.value++
|
|
||||||
isPlayingRound = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextRound = () => {
|
const nextRound = async () => {
|
||||||
if (currentRoundIndex.value < totalRounds.value) {
|
if (currentRoundIndex.value < totalRounds.value) {
|
||||||
pause()
|
pause()
|
||||||
playRound()
|
await playRound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
{{ t('diplomacy.markAllRead') }}
|
{{ t('diplomacy.markAllRead') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ScrollArea class="h-96">
|
<ScrollArea class="h-auto max-h-96 overflow-y-auto">
|
||||||
<Empty v-if="allNotifications.length === 0" class="border-0">
|
<Empty v-if="allNotifications.length === 0" class="border-0">
|
||||||
<EmptyContent>
|
<EmptyContent>
|
||||||
<ScrollText class="h-10 w-10 text-muted-foreground" />
|
<ScrollText class="h-10 w-10 text-muted-foreground" />
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
{{ t('enemyAlert.markAllRead') }}
|
{{ t('enemyAlert.markAllRead') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ScrollArea class="h-96">
|
<ScrollArea class="h-auto max-h-96 overflow-y-auto">
|
||||||
<Empty v-if="activeAlerts.length === 0" class="border-0">
|
<Empty v-if="activeAlerts.length === 0" class="border-0">
|
||||||
<EmptyContent>
|
<EmptyContent>
|
||||||
<Shield class="h-10 w-10 text-muted-foreground" />
|
<Shield class="h-10 w-10 text-muted-foreground" />
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<ScrollArea class="h-[420px]">
|
<ScrollArea class="h-auto max-h-96 overflow-y-auto">
|
||||||
<TabsContent v-for="tab in tabConfig" :key="tab.value" :value="tab.value" class="mt-0">
|
<TabsContent v-for="tab in tabConfig" :key="tab.value" :value="tab.value" class="mt-0">
|
||||||
<Empty v-if="tab.items.length === 0" class="border-0">
|
<Empty v-if="tab.items.length === 0" class="border-0">
|
||||||
<EmptyContent>
|
<EmptyContent>
|
||||||
@@ -63,7 +63,9 @@
|
|||||||
class="text-[10px] sm:text-xs whitespace-nowrap"
|
class="text-[10px] sm:text-xs whitespace-nowrap"
|
||||||
:class="isWaitingItemResourcesReady(item as WaitingQueueItem) ? 'text-green-500' : 'text-yellow-500'"
|
:class="isWaitingItemResourcesReady(item as WaitingQueueItem) ? 'text-green-500' : 'text-yellow-500'"
|
||||||
>
|
>
|
||||||
{{ isWaitingItemResourcesReady(item as WaitingQueueItem) ? t('queue.resourcesReady') : t('queue.waitingResources') }}
|
{{
|
||||||
|
isWaitingItemResourcesReady(item as WaitingQueueItem) ? t('queue.resourcesReady') : t('queue.waitingResources')
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -146,8 +148,8 @@
|
|||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { formatTime, formatNumber } from '@/utils/format'
|
import { formatTime, formatNumber } from '@/utils/format'
|
||||||
import type { BuildQueueItem, WaitingQueueItem, BuildingType, ShipType, DefenseType, TechnologyType, Resources } from '@/types/game'
|
import type { BuildQueueItem, WaitingQueueItem, BuildingType, ShipType, DefenseType, TechnologyType, Resources } from '@/types/game'
|
||||||
import * as waitingQueueLogic from '@/logic/waitingQueueLogic'
|
import * as waitingQueueLogic from '@/logic/waitingQueueLogic'
|
||||||
import * as resourceLogic from '@/logic/resourceLogic'
|
import * as resourceLogic from '@/logic/resourceLogic'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -95,13 +96,11 @@ import { Label } from '@/components/ui/label'
|
|||||||
import { Loader2 } from 'lucide-vue-next'
|
import { Loader2 } from 'lucide-vue-next'
|
||||||
import {
|
import {
|
||||||
type WebDAVConfig,
|
type WebDAVConfig,
|
||||||
getWebDAVConfig,
|
|
||||||
saveWebDAVConfig,
|
|
||||||
clearWebDAVConfig,
|
|
||||||
testWebDAVConnection
|
testWebDAVConnection
|
||||||
} from '@/services/webdavService'
|
} from '@/services/webdavService'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
const gameStore = useGameStore()
|
||||||
|
|
||||||
const isOpen = defineModel<boolean>('open', { default: false })
|
const isOpen = defineModel<boolean>('open', { default: false })
|
||||||
|
|
||||||
@@ -130,7 +129,7 @@ const isConfigValid = computed(() => {
|
|||||||
// 加载已保存的配置
|
// 加载已保存的配置
|
||||||
watch(isOpen, (open) => {
|
watch(isOpen, (open) => {
|
||||||
if (open) {
|
if (open) {
|
||||||
const saved = getWebDAVConfig()
|
const saved = gameStore.webdavConfig
|
||||||
if (saved) {
|
if (saved) {
|
||||||
config.value = { ...saved }
|
config.value = { ...saved }
|
||||||
hasExistingConfig.value = true
|
hasExistingConfig.value = true
|
||||||
@@ -152,21 +151,25 @@ const handleTest = async () => {
|
|||||||
testResult.value = null
|
testResult.value = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
testResult.value = await testWebDAVConnection(config.value)
|
const result = await testWebDAVConnection(config.value)
|
||||||
|
testResult.value = {
|
||||||
|
success: result.success,
|
||||||
|
message: t(result.messageKey)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isTesting.value = false
|
isTesting.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
saveWebDAVConfig(config.value)
|
gameStore.webdavConfig = { ...config.value }
|
||||||
hasExistingConfig.value = true
|
hasExistingConfig.value = true
|
||||||
emit('saved', config.value)
|
emit('saved', config.value)
|
||||||
isOpen.value = false
|
isOpen.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
clearWebDAVConfig()
|
gameStore.webdavConfig = null
|
||||||
hasExistingConfig.value = false
|
hasExistingConfig.value = false
|
||||||
config.value = {
|
config.value = {
|
||||||
serverUrl: '',
|
serverUrl: '',
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ const loadFiles = async () => {
|
|||||||
if (result.success && result.files) {
|
if (result.success && result.files) {
|
||||||
files.value = result.files
|
files.value = result.files
|
||||||
} else {
|
} else {
|
||||||
error.value = result.message || t('settings.webdav.loadFailed')
|
error.value = t(result.messageKey) || t('settings.webdav.loadFailed')
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e instanceof Error ? e.message : String(e)
|
error.value = e instanceof Error ? e.message : String(e)
|
||||||
@@ -174,7 +174,7 @@ const handleDelete = async (fileName: string) => {
|
|||||||
selectedFile.value = null
|
selectedFile.value = null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || t('settings.webdav.deleteFailed'))
|
toast.error(t(result.messageKey) || t('settings.webdav.deleteFailed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,20 +2,21 @@
|
|||||||
<AlertDialogPortal>
|
<AlertDialogPortal>
|
||||||
<AlertDialogOverlay
|
<AlertDialogOverlay
|
||||||
data-slot="alert-dialog-overlay"
|
data-slot="alert-dialog-overlay"
|
||||||
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80"
|
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4"
|
||||||
/>
|
|
||||||
<AlertDialogContent
|
|
||||||
data-slot="alert-dialog-content"
|
|
||||||
v-bind="{ ...$attrs, ...forwarded }"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
|
||||||
props.class
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<slot />
|
<AlertDialogContent
|
||||||
</AlertDialogContent>
|
data-slot="alert-dialog-content"
|
||||||
|
v-bind="{ ...$attrs, ...forwarded }"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 grid w-full max-w-lg gap-4 rounded-lg border p-6 shadow-lg duration-200',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialogOverlay>
|
||||||
</AlertDialogPortal>
|
</AlertDialogPortal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -71,11 +71,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SpringOptions, Transition } from 'motion-v'
|
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { motion, useMotionValue, useSpring } from 'motion-v'
|
import { motion, useMotionValue, useSpring } from 'motion-v'
|
||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
// 本地定义类型,因为 motion-v 不导出这些类型
|
||||||
|
interface SpringOptions {
|
||||||
|
stiffness?: number
|
||||||
|
damping?: number
|
||||||
|
mass?: number
|
||||||
|
}
|
||||||
|
|
||||||
interface StarsBackgroundProps {
|
interface StarsBackgroundProps {
|
||||||
factor?: number
|
factor?: number
|
||||||
speed?: number
|
speed?: number
|
||||||
@@ -139,19 +145,19 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const starLayer1Transition = computed<Transition>(() => ({
|
const starLayer1Transition = computed(() => ({
|
||||||
repeat: Infinity,
|
repeat: Infinity,
|
||||||
duration: props.speed,
|
duration: props.speed,
|
||||||
ease: 'linear' as const
|
ease: 'linear' as const
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const starLayer2Transition = computed<Transition>(() => ({
|
const starLayer2Transition = computed(() => ({
|
||||||
repeat: Infinity,
|
repeat: Infinity,
|
||||||
duration: props.speed * 2,
|
duration: props.speed * 2,
|
||||||
ease: 'linear' as const
|
ease: 'linear' as const
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const starLayer3Transition = computed<Transition>(() => ({
|
const starLayer3Transition = computed(() => ({
|
||||||
repeat: Infinity,
|
repeat: Infinity,
|
||||||
duration: props.speed * 3,
|
duration: props.speed * 3,
|
||||||
ease: 'linear' as const
|
ease: 'linear' as const
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay />
|
<DialogOverlay
|
||||||
<DialogContent
|
data-slot="dialog-overlay"
|
||||||
data-slot="dialog-content"
|
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80 flex items-center justify-center p-4"
|
||||||
v-bind="{ ...$attrs, ...forwarded }"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-60 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-2xl',
|
|
||||||
props.class
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<slot />
|
<DialogContent
|
||||||
|
data-slot="dialog-content"
|
||||||
<DialogClose
|
v-bind="{ ...$attrs, ...forwarded }"
|
||||||
v-if="showCloseButton"
|
:class="
|
||||||
data-slot="dialog-close"
|
cn(
|
||||||
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-60 grid w-full max-w-2xl gap-4 rounded-lg border p-6 shadow-lg duration-200 relative',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<X />
|
<slot />
|
||||||
<span class="sr-only">Close</span>
|
|
||||||
</DialogClose>
|
<DialogClose
|
||||||
</DialogContent>
|
v-if="showCloseButton"
|
||||||
|
data-slot="dialog-close"
|
||||||
|
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogContent>
|
||||||
|
</DialogOverlay>
|
||||||
</DialogPortal>
|
</DialogPortal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -30,9 +34,8 @@
|
|||||||
import type { HTMLAttributes } from 'vue'
|
import type { HTMLAttributes } from 'vue'
|
||||||
import { reactiveOmit } from '@vueuse/core'
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { X } from 'lucide-vue-next'
|
import { X } from 'lucide-vue-next'
|
||||||
import { DialogClose, DialogContent, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
import { DialogClose, DialogContent, DialogOverlay, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import DialogOverlay from './DialogOverlay.vue'
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
inheritAttrs: false
|
inheritAttrs: false
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
v-bind="delegatedProps"
|
v-bind="delegatedProps"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80',
|
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80 flex items-center justify-center p-4',
|
||||||
props.class
|
props.class
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay />
|
<DialogOverlay
|
||||||
<DialogContent
|
data-slot="dialog-overlay"
|
||||||
data-slot="scrollable-dialog-content"
|
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80 flex items-center justify-center p-4"
|
||||||
v-bind="{ ...$attrs, ...forwarded }"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-60 w-[calc(100vw-3rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg border shadow-lg duration-200 sm:w-auto sm:min-w-[764px] flex flex-col p-0',
|
|
||||||
containerClass
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
|
<DialogContent
|
||||||
|
data-slot="scrollable-dialog-content"
|
||||||
|
v-bind="{ ...$attrs, ...forwarded }"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-60 w-full max-w-3xl rounded-lg border shadow-lg duration-200 flex flex-col p-0 relative',
|
||||||
|
containerClass
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
<!-- 固定的头部 -->
|
<!-- 固定的头部 -->
|
||||||
<div class="shrink-0 px-4 pt-4 pb-3 sm:px-6 sm:pt-6 sm:pb-4 border-b">
|
<div class="shrink-0 px-4 pt-4 pb-3 sm:px-6 sm:pt-6 sm:pb-4 border-b">
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
@@ -35,7 +38,8 @@
|
|||||||
<X />
|
<X />
|
||||||
<span class="sr-only">Close</span>
|
<span class="sr-only">Close</span>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
</DialogOverlay>
|
||||||
</DialogPortal>
|
</DialogPortal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -44,9 +48,8 @@
|
|||||||
import type { HTMLAttributes } from 'vue'
|
import type { HTMLAttributes } from 'vue'
|
||||||
import { reactiveOmit } from '@vueuse/core'
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { X } from 'lucide-vue-next'
|
import { X } from 'lucide-vue-next'
|
||||||
import { DialogClose, DialogContent, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
import { DialogClose, DialogContent, DialogOverlay, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import DialogOverlay from './DialogOverlay.vue'
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
inheritAttrs: false
|
inheritAttrs: false
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<ProgressIndicator
|
<ProgressIndicator
|
||||||
data-slot="progress-indicator"
|
data-slot="progress-indicator"
|
||||||
class="bg-primary h-full w-full flex-1 transition-all"
|
class="bg-primary h-full w-full flex-1 transition-all"
|
||||||
:style="`transform: translateX(-${100 - (props.modelValue ?? 0)}%);`"
|
:style="`transform: translate3d(-${100 - (props.modelValue ?? 0)}%, 0, 0);`"
|
||||||
/>
|
/>
|
||||||
</ProgressRoot>
|
</ProgressRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ export const useGameConfig = () => {
|
|||||||
[BuildingType.JumpGate]: 'jumpGate',
|
[BuildingType.JumpGate]: 'jumpGate',
|
||||||
[BuildingType.PlanetDestroyerFactory]: 'planetDestroyerFactory',
|
[BuildingType.PlanetDestroyerFactory]: 'planetDestroyerFactory',
|
||||||
[BuildingType.GeoResearchStation]: 'geoResearchStation',
|
[BuildingType.GeoResearchStation]: 'geoResearchStation',
|
||||||
[BuildingType.DeepDrillingFacility]: 'deepDrillingFacility'
|
[BuildingType.DeepDrillingFacility]: 'deepDrillingFacility',
|
||||||
|
[BuildingType.University]: 'university'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 舰船类型枚举值到翻译键的映射
|
// 舰船类型枚举值到翻译键的映射
|
||||||
@@ -100,7 +101,11 @@ export const useGameConfig = () => {
|
|||||||
[TechnologyType.DarkMatterTechnology]: 'darkMatterTechnology',
|
[TechnologyType.DarkMatterTechnology]: 'darkMatterTechnology',
|
||||||
[TechnologyType.TerraformingTechnology]: 'terraformingTechnology',
|
[TechnologyType.TerraformingTechnology]: 'terraformingTechnology',
|
||||||
[TechnologyType.PlanetDestructionTech]: 'planetDestructionTech',
|
[TechnologyType.PlanetDestructionTech]: 'planetDestructionTech',
|
||||||
[TechnologyType.MiningTechnology]: 'miningTechnology'
|
[TechnologyType.MiningTechnology]: 'miningTechnology',
|
||||||
|
[TechnologyType.IntergalacticResearchNetwork]: 'intergalacticResearchNetwork',
|
||||||
|
[TechnologyType.MineralResearch]: 'mineralResearch',
|
||||||
|
[TechnologyType.CrystalResearch]: 'crystalResearch',
|
||||||
|
[TechnologyType.FuelResearch]: 'fuelResearch'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 军官类型枚举值到翻译键的映射
|
// 军官类型枚举值到翻译键的映射
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const triggerQueueAnimation = (event: MouseEvent, type: 'building' | 'tec
|
|||||||
|
|
||||||
el.style.left = `${x}px`
|
el.style.left = `${x}px`
|
||||||
el.style.top = `${y}px`
|
el.style.top = `${y}px`
|
||||||
el.style.transform = `translate(-50%, -50%) scale(${scale})`
|
el.style.transform = `translate3d(-50%, -50%, 0) scale3d(${scale}, ${scale}, 1)`
|
||||||
el.style.opacity = `${opacity}`
|
el.style.opacity = `${opacity}`
|
||||||
|
|
||||||
if (progress < 1) {
|
if (progress < 1) {
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ export const BUILDINGS: Record<BuildingType, BuildingConfig> = {
|
|||||||
id: BuildingType.LunarBase,
|
id: BuildingType.LunarBase,
|
||||||
name: '月球基地',
|
name: '月球基地',
|
||||||
description: '增加月球可用空间',
|
description: '增加月球可用空间',
|
||||||
baseCost: { metal: 20000, crystal: 40000, deuterium: 20000, darkMatter: 0, energy: 0 },
|
baseCost: { metal: 8000, crystal: 8000, deuterium: 4000, darkMatter: 0, energy: 0 },
|
||||||
baseTime: 45, // 减少建造时间:60→45秒
|
baseTime: 45, // 减少建造时间:60→45秒
|
||||||
costMultiplier: 2,
|
costMultiplier: 2,
|
||||||
spaceUsage: 0, // 月球基地本身不占用空间,反而增加空间
|
spaceUsage: 0, // 月球基地本身不占用空间,反而增加空间
|
||||||
@@ -357,7 +357,7 @@ export const BUILDINGS: Record<BuildingType, BuildingConfig> = {
|
|||||||
[BuildingType.GeoResearchStation]: {
|
[BuildingType.GeoResearchStation]: {
|
||||||
id: BuildingType.GeoResearchStation,
|
id: BuildingType.GeoResearchStation,
|
||||||
name: '地质研究站',
|
name: '地质研究站',
|
||||||
description: '研究地质结构,提高矿脉自然恢复速度。每级增加50%恢复速率',
|
description: '研究地质结构,提高矿脉自然恢复速度。每级增加10%恢复速率',
|
||||||
baseCost: { metal: 50000, crystal: 30000, deuterium: 20000, darkMatter: 0, energy: 0 },
|
baseCost: { metal: 50000, crystal: 30000, deuterium: 20000, darkMatter: 0, energy: 0 },
|
||||||
baseTime: 60,
|
baseTime: 60,
|
||||||
costMultiplier: 1.8,
|
costMultiplier: 1.8,
|
||||||
@@ -416,6 +416,34 @@ export const BUILDINGS: Record<BuildingType, BuildingConfig> = {
|
|||||||
[TechnologyType.PlasmaTechnology]: 5
|
[TechnologyType.PlasmaTechnology]: 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// 2moons新增建筑
|
||||||
|
[BuildingType.University]: {
|
||||||
|
id: BuildingType.University,
|
||||||
|
name: '大学',
|
||||||
|
description: '培养科研人员,加快研究速度。每级减少研究时间8%(与星际研究网络叠加)',
|
||||||
|
baseCost: { metal: 200000, crystal: 100000, deuterium: 50000, darkMatter: 0, energy: 0 },
|
||||||
|
baseTime: 120,
|
||||||
|
costMultiplier: 2,
|
||||||
|
spaceUsage: 8,
|
||||||
|
planetOnly: true,
|
||||||
|
maxLevel: 10, // 最多10级(最高约57%研究时间减少)
|
||||||
|
requirements: {
|
||||||
|
[BuildingType.ResearchLab]: 8,
|
||||||
|
[BuildingType.RoboticsFactory]: 6
|
||||||
|
},
|
||||||
|
levelRequirements: {
|
||||||
|
5: {
|
||||||
|
[BuildingType.ResearchLab]: 10,
|
||||||
|
[BuildingType.RoboticsFactory]: 8,
|
||||||
|
[TechnologyType.EnergyTechnology]: 8
|
||||||
|
},
|
||||||
|
8: {
|
||||||
|
[BuildingType.ResearchLab]: 12,
|
||||||
|
[BuildingType.NaniteFactory]: 2,
|
||||||
|
[TechnologyType.ComputerTechnology]: 8
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,6 +754,111 @@ export const TECHNOLOGIES: Record<TechnologyType, TechnologyConfig> = {
|
|||||||
[TechnologyType.PlasmaTechnology]: 3
|
[TechnologyType.PlasmaTechnology]: 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// 2moons新增科技
|
||||||
|
[TechnologyType.IntergalacticResearchNetwork]: {
|
||||||
|
id: TechnologyType.IntergalacticResearchNetwork,
|
||||||
|
name: '星际研究网络',
|
||||||
|
description: '连接多个星球的研究实验室,共享研究资源。每级可连接1个额外的研究实验室(取等级最高的N个实验室)',
|
||||||
|
baseCost: { metal: 240000, crystal: 400000, deuterium: 160000, darkMatter: 0, energy: 0 },
|
||||||
|
baseTime: 180,
|
||||||
|
costMultiplier: 2,
|
||||||
|
maxLevel: 10,
|
||||||
|
requirements: {
|
||||||
|
[BuildingType.ResearchLab]: 10,
|
||||||
|
[TechnologyType.ComputerTechnology]: 8,
|
||||||
|
[TechnologyType.HyperspaceTechnology]: 8
|
||||||
|
},
|
||||||
|
levelRequirements: {
|
||||||
|
5: {
|
||||||
|
[BuildingType.ResearchLab]: 12,
|
||||||
|
[TechnologyType.ComputerTechnology]: 10,
|
||||||
|
[TechnologyType.HyperspaceTechnology]: 10
|
||||||
|
},
|
||||||
|
8: {
|
||||||
|
[BuildingType.ResearchLab]: 14,
|
||||||
|
[TechnologyType.ComputerTechnology]: 12,
|
||||||
|
[BuildingType.NaniteFactory]: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[TechnologyType.MineralResearch]: {
|
||||||
|
id: TechnologyType.MineralResearch,
|
||||||
|
name: '矿物研究',
|
||||||
|
description: '研究更高效的金属提取技术,提升金属矿产量。每级增加金属产量2%',
|
||||||
|
baseCost: { metal: 60000, crystal: 30000, deuterium: 0, darkMatter: 0, energy: 0 },
|
||||||
|
baseTime: 60,
|
||||||
|
costMultiplier: 1.75,
|
||||||
|
maxLevel: 20,
|
||||||
|
requirements: {
|
||||||
|
[BuildingType.ResearchLab]: 5,
|
||||||
|
[BuildingType.MetalMine]: 10,
|
||||||
|
[TechnologyType.EnergyTechnology]: 3
|
||||||
|
},
|
||||||
|
levelRequirements: {
|
||||||
|
10: {
|
||||||
|
[BuildingType.ResearchLab]: 8,
|
||||||
|
[BuildingType.MetalMine]: 20,
|
||||||
|
[TechnologyType.EnergyTechnology]: 8
|
||||||
|
},
|
||||||
|
15: {
|
||||||
|
[BuildingType.ResearchLab]: 12,
|
||||||
|
[BuildingType.NaniteFactory]: 2,
|
||||||
|
[TechnologyType.PlasmaTechnology]: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[TechnologyType.CrystalResearch]: {
|
||||||
|
id: TechnologyType.CrystalResearch,
|
||||||
|
name: '晶体研究',
|
||||||
|
description: '研究更高效的晶体提炼技术,提升晶体矿产量。每级增加晶体产量2%',
|
||||||
|
baseCost: { metal: 40000, crystal: 60000, deuterium: 0, darkMatter: 0, energy: 0 },
|
||||||
|
baseTime: 60,
|
||||||
|
costMultiplier: 1.75,
|
||||||
|
maxLevel: 20,
|
||||||
|
requirements: {
|
||||||
|
[BuildingType.ResearchLab]: 5,
|
||||||
|
[BuildingType.CrystalMine]: 10,
|
||||||
|
[TechnologyType.EnergyTechnology]: 3
|
||||||
|
},
|
||||||
|
levelRequirements: {
|
||||||
|
10: {
|
||||||
|
[BuildingType.ResearchLab]: 8,
|
||||||
|
[BuildingType.CrystalMine]: 20,
|
||||||
|
[TechnologyType.EnergyTechnology]: 8
|
||||||
|
},
|
||||||
|
15: {
|
||||||
|
[BuildingType.ResearchLab]: 12,
|
||||||
|
[BuildingType.NaniteFactory]: 2,
|
||||||
|
[TechnologyType.PlasmaTechnology]: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[TechnologyType.FuelResearch]: {
|
||||||
|
id: TechnologyType.FuelResearch,
|
||||||
|
name: '燃料研究',
|
||||||
|
description: '研究更高效的重氢合成技术,提升重氢产量。每级增加重氢产量2%',
|
||||||
|
baseCost: { metal: 0, crystal: 50000, deuterium: 50000, darkMatter: 0, energy: 0 },
|
||||||
|
baseTime: 60,
|
||||||
|
costMultiplier: 1.75,
|
||||||
|
maxLevel: 20,
|
||||||
|
requirements: {
|
||||||
|
[BuildingType.ResearchLab]: 5,
|
||||||
|
[BuildingType.DeuteriumSynthesizer]: 10,
|
||||||
|
[TechnologyType.EnergyTechnology]: 3
|
||||||
|
},
|
||||||
|
levelRequirements: {
|
||||||
|
10: {
|
||||||
|
[BuildingType.ResearchLab]: 8,
|
||||||
|
[BuildingType.DeuteriumSynthesizer]: 20,
|
||||||
|
[TechnologyType.EnergyTechnology]: 8
|
||||||
|
},
|
||||||
|
15: {
|
||||||
|
[BuildingType.ResearchLab]: 12,
|
||||||
|
[BuildingType.NaniteFactory]: 2,
|
||||||
|
[TechnologyType.PlasmaTechnology]: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,7 +1140,7 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
|
|||||||
id: ShipType.Deathstar,
|
id: ShipType.Deathstar,
|
||||||
name: '死星',
|
name: '死星',
|
||||||
description: '终极武器,能够摧毁整个行星',
|
description: '终极武器,能够摧毁整个行星',
|
||||||
cost: { metal: 5000000, crystal: 4000000, deuterium: 1000000, darkMatter: 20000, energy: 0 },
|
cost: { metal: 50000000, crystal: 40000000, deuterium: 10000000, darkMatter: 20000, energy: 0 },
|
||||||
buildTime: 600,
|
buildTime: 600,
|
||||||
cargoCapacity: 1000000,
|
cargoCapacity: 1000000,
|
||||||
attack: 200000,
|
attack: 200000,
|
||||||
@@ -1467,9 +1600,9 @@ export const DIPLOMATIC_CONFIG = {
|
|||||||
// 贸易检查间隔(秒)
|
// 贸易检查间隔(秒)
|
||||||
CHECK_INTERVAL: 1800, // 30分钟
|
CHECK_INTERVAL: 1800, // 30分钟
|
||||||
// 贸易概率
|
// 贸易概率
|
||||||
PROBABILITY: 0.1,
|
PROBABILITY: 0.05,
|
||||||
// 玩家最多同时保留的贸易提议数量
|
// 玩家最多同时保留的贸易提议数量
|
||||||
MAX_PENDING_OFFERS: 30,
|
MAX_PENDING_OFFERS: 5,
|
||||||
// 汇率配置:NPC出售资源的汇率
|
// 汇率配置:NPC出售资源的汇率
|
||||||
EXCHANGE_RATES: {
|
EXCHANGE_RATES: {
|
||||||
// 金属:晶体:重氢 基准比例 3:2:1
|
// 金属:晶体:重氢 基准比例 3:2:1
|
||||||
@@ -1658,9 +1791,9 @@ export const ORE_DEPOSIT_CONFIG = {
|
|||||||
// 矿脉恢复配置
|
// 矿脉恢复配置
|
||||||
REGENERATION: {
|
REGENERATION: {
|
||||||
ENABLED: true,
|
ENABLED: true,
|
||||||
// 每小时恢复初始储量的百分比(0.1% = 每小时恢复0.1%,约42天完全恢复)
|
// 每小时恢复初始储量的百分比(1% = 每小时恢复1%,约4.2天完全恢复)
|
||||||
RATE_PER_HOUR: 0.001,
|
RATE_PER_HOUR: 0.01,
|
||||||
// 最大恢复到初始储量的百分比(100%表示可以完全恢复)
|
// 最大恢复到初始储量的百分比(100表示可以完全恢复)%
|
||||||
MAX_PERCENTAGE: 1.0
|
MAX_PERCENTAGE: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,10 +153,15 @@ export default {
|
|||||||
planetDestroyerFactory: 'Planetenzerstörer-Fabrik',
|
planetDestroyerFactory: 'Planetenzerstörer-Fabrik',
|
||||||
geoResearchStation: 'Geologische Forschungsstation',
|
geoResearchStation: 'Geologische Forschungsstation',
|
||||||
deepDrillingFacility: 'Tiefbohranlage',
|
deepDrillingFacility: 'Tiefbohranlage',
|
||||||
|
university: 'Universität',
|
||||||
buildTime: 'Bauzeit',
|
buildTime: 'Bauzeit',
|
||||||
build: 'Bauen',
|
build: 'Bauen',
|
||||||
production: 'Produktion',
|
production: 'Produktion',
|
||||||
consumption: 'Verbrauch',
|
consumption: 'Verbrauch',
|
||||||
|
levelRange: 'Stufenbereich',
|
||||||
|
totalCost: 'Gesamtkosten',
|
||||||
|
totalPoints: 'Gesamtpunkte',
|
||||||
|
researchSpeedBonus: 'Forschungsgeschwindigkeitsbonus',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -200,8 +205,9 @@ export default {
|
|||||||
jumpGate: 'Überträgt Flotten sofort zu anderen Monden',
|
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:
|
geoResearchStation:
|
||||||
'Erforscht geologische Strukturen und erhöht die natürliche Erzvorkommen-Regenerationsrate. +50% Regeneration pro Stufe',
|
'Erforscht geologische Strukturen und erhöht die natürliche Erzvorkommen-Regenerationsrate. +10% Regeneration pro Stufe',
|
||||||
deepDrillingFacility: ''
|
deepDrillingFacility: '',
|
||||||
|
university: 'Bildet Forscher aus, um die Forschungsgeschwindigkeit zu beschleunigen. -8% Forschungszeit pro Stufe'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: 'Leichter Jäger',
|
lightFighter: 'Leichter Jäger',
|
||||||
@@ -265,6 +271,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: 'Forschungszeit',
|
researchTime: 'Forschungszeit',
|
||||||
|
levelRange: 'Stufenbereich',
|
||||||
|
totalCost: 'Gesamtkosten',
|
||||||
|
totalPoints: 'Gesamtpunkte',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -280,7 +289,11 @@ export default {
|
|||||||
researchQueueBonus: 'Forschungswarteschlange',
|
researchQueueBonus: 'Forschungswarteschlange',
|
||||||
colonySlots: 'Kolonieplätze',
|
colonySlots: 'Kolonieplätze',
|
||||||
forAllPlanets: '(Global)',
|
forAllPlanets: '(Global)',
|
||||||
speedBonus: 'Geschwindigkeitsbonus'
|
speedBonus: 'Geschwindigkeitsbonus',
|
||||||
|
// Ressourcenproduktionsbonus-Anzeige
|
||||||
|
mineralResearch: 'Mineralforschung',
|
||||||
|
crystalResearch: 'Kristallforschung',
|
||||||
|
fuelResearch: 'Brennstoffforschung'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: 'Energietechnik',
|
energyTechnology: 'Energietechnik',
|
||||||
@@ -301,7 +314,11 @@ export default {
|
|||||||
darkMatterTechnology: 'Dunkle-Materie-Technologie',
|
darkMatterTechnology: 'Dunkle-Materie-Technologie',
|
||||||
terraformingTechnology: 'Terraforming-Technologie',
|
terraformingTechnology: 'Terraforming-Technologie',
|
||||||
planetDestructionTech: 'Planetenzerstörungstechnologie',
|
planetDestructionTech: 'Planetenzerstörungstechnologie',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: 'Intergalaktisches Forschungsnetzwerk',
|
||||||
|
mineralResearch: 'Mineralforschung',
|
||||||
|
crystalResearch: 'Kristallforschung',
|
||||||
|
fuelResearch: 'Treibstoffforschung'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: 'Verbessert Energieeffizienz',
|
energyTechnology: 'Verbessert Energieeffizienz',
|
||||||
@@ -323,7 +340,11 @@ export default {
|
|||||||
darkMatterTechnology: 'Forschung zu Eigenschaften und Anwendungen von Dunkler Materie',
|
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',
|
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: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: 'Verbindet mehrere Forschungslabore über Planeten hinweg. Jede Stufe verbindet 1 zusätzliches Labor',
|
||||||
|
mineralResearch: 'Erforscht effizientere Metallextraktionstechniken. +2% Metallproduktion pro Stufe',
|
||||||
|
crystalResearch: 'Erforscht effizientere Kristallveredelungstechniken. +2% Kristallproduktion pro Stufe',
|
||||||
|
fuelResearch: 'Erforscht effizientere Deuterium-Synthesetechniken. +2% Deuteriumproduktion pro Stufe'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: 'Kommandant',
|
commander: 'Kommandant',
|
||||||
@@ -392,8 +413,10 @@ export default {
|
|||||||
tabConsumption: 'Verbrauchsdetails'
|
tabConsumption: 'Verbrauchsdetails'
|
||||||
},
|
},
|
||||||
buildingsView: {
|
buildingsView: {
|
||||||
|
title: 'Gebäude',
|
||||||
spaceUsage: 'Platzbedarf',
|
spaceUsage: 'Platzbedarf',
|
||||||
upgradeCost: 'Ausbaukosten',
|
upgradeCost: 'Ausbaukosten',
|
||||||
|
build: 'Bauen',
|
||||||
upgrade: 'Ausbauen',
|
upgrade: 'Ausbauen',
|
||||||
maxLevelReached: 'Maximale Stufe erreicht',
|
maxLevelReached: 'Maximale Stufe erreicht',
|
||||||
requirementsNotMet: 'Anforderungen nicht erfüllt',
|
requirementsNotMet: 'Anforderungen nicht erfüllt',
|
||||||
@@ -415,10 +438,20 @@ export default {
|
|||||||
'Bitte überprüfen Sie, ob Sie genügend Ressourcen haben, die Voraussetzungen erfüllt sind oder keine anderen Forschungsaufträge vorhanden sind.'
|
'Bitte überprüfen Sie, ob Sie genügend Ressourcen haben, die Voraussetzungen erfüllt sind oder keine anderen Forschungsaufträge vorhanden sind.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Angriff',
|
||||||
|
shield: 'Schild',
|
||||||
|
armor: 'Panzerung',
|
||||||
missileAttack: 'Raketenangriff',
|
missileAttack: 'Raketenangriff',
|
||||||
speed: 'Geschwindigkeit',
|
speed: 'Geschwindigkeit',
|
||||||
cargoCapacity: 'Ladekapazität',
|
cargoCapacity: 'Ladekapazität',
|
||||||
fuelConsumption: 'Treibstoffverbrauch'
|
fuelConsumption: 'Treibstoffverbrauch',
|
||||||
|
buildCost: 'Baukosten',
|
||||||
|
buildTime: 'Bauzeit',
|
||||||
|
perUnit: 'Pro Einheit',
|
||||||
|
batchCalculator: 'Stapelrechner',
|
||||||
|
quantity: 'Menge',
|
||||||
|
totalCost: 'Gesamtkosten',
|
||||||
|
totalTime: 'Gesamtzeit'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Raumschiffwerft',
|
title: 'Raumschiffwerft',
|
||||||
@@ -530,6 +563,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
recycle: 'Recyceln',
|
recycle: 'Recyceln',
|
||||||
|
harvestDarkMatter: 'Dunkle Materie ernten',
|
||||||
|
station: 'Stationieren',
|
||||||
transportResources: 'Ressourcen transportieren',
|
transportResources: 'Ressourcen transportieren',
|
||||||
totalCargoCapacity: 'Gesamtladekapazität',
|
totalCargoCapacity: 'Gesamtladekapazität',
|
||||||
used: 'Verwendet',
|
used: 'Verwendet',
|
||||||
@@ -911,7 +946,10 @@ export default {
|
|||||||
hideRoundDetails: 'Rundendetails ausblenden',
|
hideRoundDetails: 'Rundendetails ausblenden',
|
||||||
round: 'Runde {round}',
|
round: 'Runde {round}',
|
||||||
attackerRemainingPower: 'Verbleibende Angreiferkraft',
|
attackerRemainingPower: 'Verbleibende Angreiferkraft',
|
||||||
defenderRemainingPower: 'Verbleibende Verteidigerkraft'
|
defenderRemainingPower: 'Verbleibende Verteidigerkraft',
|
||||||
|
importFromSpyReport: 'Aus Spionagebericht importieren',
|
||||||
|
selectSpyReport: 'Spionagebericht auswählen',
|
||||||
|
noSpyReports: 'Keine Spionageberichte verfügbar'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
dataManagement: 'Datenverwaltung',
|
dataManagement: 'Datenverwaltung',
|
||||||
@@ -922,6 +960,7 @@ export default {
|
|||||||
exporting: 'Exportieren...',
|
exporting: 'Exportieren...',
|
||||||
exportSuccess: 'Export erfolgreich',
|
exportSuccess: 'Export erfolgreich',
|
||||||
exportSuccessWithPath: 'Export erfolgreich, Datei gespeichert unter: {path}',
|
exportSuccessWithPath: 'Export erfolgreich, Datei gespeichert unter: {path}',
|
||||||
|
storagePermissionDenied: 'Speicherberechtigung verweigert, Datei kann nicht exportiert werden',
|
||||||
exportFailed: 'Export fehlgeschlagen, bitte erneut versuchen',
|
exportFailed: 'Export fehlgeschlagen, bitte erneut versuchen',
|
||||||
importData: 'Daten importieren',
|
importData: 'Daten importieren',
|
||||||
importDataDesc: 'Spielfortschritt aus JSON-Datei wiederherstellen',
|
importDataDesc: 'Spielfortschritt aus JSON-Datei wiederherstellen',
|
||||||
@@ -941,6 +980,8 @@ export default {
|
|||||||
gameSettingsDesc: 'Spielparameter und Einstellungen anpassen',
|
gameSettingsDesc: 'Spielparameter und Einstellungen anpassen',
|
||||||
gamePause: 'Spielpause',
|
gamePause: 'Spielpause',
|
||||||
gamePauseDesc: 'Spielzeit und Ressourcenproduktion pausieren oder fortsetzen',
|
gamePauseDesc: 'Spielzeit und Ressourcenproduktion pausieren oder fortsetzen',
|
||||||
|
battleMode: 'Bis zum Ende kämpfen',
|
||||||
|
battleModeDesc: 'Wenn aktiviert, dauern Kämpfe bis zu 100 Runden bis ein Sieger feststeht. Wenn deaktiviert, wird der klassische 6-Runden-Modus verwendet',
|
||||||
pause: 'Pausieren',
|
pause: 'Pausieren',
|
||||||
resume: 'Fortsetzen',
|
resume: 'Fortsetzen',
|
||||||
gamePaused: 'Spiel pausiert',
|
gamePaused: 'Spiel pausiert',
|
||||||
@@ -1379,19 +1420,23 @@ export default {
|
|||||||
skip: 'Anleitung überspringen',
|
skip: 'Anleitung überspringen',
|
||||||
welcome: {
|
welcome: {
|
||||||
title: 'Willkommen bei OGame',
|
title: 'Willkommen bei OGame',
|
||||||
content: 'Willkommen, Kommandant! Diese Anleitung führt Sie durch die Grundlagen des Imperiumsaufbaus. Klicken Sie auf "Weiter", um Ihre Reise zu beginnen.'
|
content:
|
||||||
|
'Willkommen, Kommandant! Diese Anleitung führt Sie durch die Grundlagen des Imperiumsaufbaus. Klicken Sie auf "Weiter", um Ihre Reise zu beginnen.'
|
||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
title: 'Ressourcenübersicht',
|
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.'
|
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: {
|
planet: {
|
||||||
title: 'Ihr 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.'
|
content:
|
||||||
|
'Dies ist Ihr Heimatplanet. Hier sehen Sie den Planetennamen, die Koordinaten und können zwischen Planeten wechseln, wenn Sie Ihr Imperium erweitern.'
|
||||||
},
|
},
|
||||||
navigation: {
|
navigation: {
|
||||||
title: 'Navigationsmenü',
|
title: 'Navigationsmenü',
|
||||||
content: 'Verwenden Sie dieses Menü, um zwischen verschiedenen Bereichen zu navigieren: Gebäude, Forschung, Flotte, Galaxie usw. Jeder Bereich bietet einzigartige Spielfunktionen.'
|
content:
|
||||||
|
'Verwenden Sie dieses Menü, um zwischen verschiedenen Bereichen zu navigieren: Gebäude, Forschung, Flotte, Galaxie usw. Jeder Bereich bietet einzigartige Spielfunktionen.'
|
||||||
},
|
},
|
||||||
gotoBuildings: {
|
gotoBuildings: {
|
||||||
title: 'Zu Gebäuden gehen',
|
title: 'Zu Gebäuden gehen',
|
||||||
@@ -1399,39 +1444,48 @@ export default {
|
|||||||
},
|
},
|
||||||
buildSolarPlant: {
|
buildSolarPlant: {
|
||||||
title: 'Solarkraftwerk bauen',
|
title: 'Solarkraftwerk bauen',
|
||||||
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.'
|
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: {
|
waitBuild: {
|
||||||
title: 'Bauauftrag',
|
title: 'Bauauftrag',
|
||||||
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.'
|
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: {
|
buildMetalMine: {
|
||||||
title: 'Metallmine bauen',
|
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.'
|
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: {
|
buildCrystalMine: {
|
||||||
title: 'Kristallmine bauen',
|
title: 'Kristallmine bauen',
|
||||||
content: 'Kristall ist seltener, aber für fortgeschrittene Technologien entscheidend. Bauen Sie eine Kristallmine, um diese wertvolle Ressource zu sammeln.'
|
content:
|
||||||
|
'Kristall ist seltener, aber für fortgeschrittene Technologien entscheidend. Bauen Sie eine Kristallmine, um diese wertvolle Ressource zu sammeln.'
|
||||||
},
|
},
|
||||||
buildDeuterium: {
|
buildDeuterium: {
|
||||||
title: 'Deuteriumsynthesizer bauen',
|
title: 'Deuteriumsynthesizer bauen',
|
||||||
content: 'Deuterium ist für Schiffstreibstoff und fortgeschrittene Forschung unerlässlich. Bauen Sie einen Deuteriumsynthesizer, um diese wichtige Ressource zu produzieren.'
|
content:
|
||||||
|
'Deuterium ist für Schiffstreibstoff und fortgeschrittene Forschung unerlässlich. Bauen Sie einen Deuteriumsynthesizer, um diese wichtige Ressource zu produzieren.'
|
||||||
},
|
},
|
||||||
upgradeMines: {
|
upgradeMines: {
|
||||||
title: 'Ressourcenminen upgraden',
|
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.'
|
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: {
|
buildRobotics: {
|
||||||
title: 'Roboterfabrik bauen',
|
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!'
|
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: {
|
upgradeMinesForLab: {
|
||||||
title: 'Ressourcenminen weiter upgraden',
|
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.'
|
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: {
|
buildResearchLab: {
|
||||||
title: 'Forschungslabor bauen',
|
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!'
|
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: {
|
gotoResearch: {
|
||||||
title: 'Zur Forschung gehen',
|
title: 'Zur Forschung gehen',
|
||||||
@@ -1439,11 +1493,13 @@ export default {
|
|||||||
},
|
},
|
||||||
researchEnergy: {
|
researchEnergy: {
|
||||||
title: 'Energietechnik erforschen',
|
title: 'Energietechnik erforschen',
|
||||||
content: 'Die Energietechnik kann Ihre Energieproduktion verbessern und fortgeschrittene Gebäude freischalten. Dies ist eine der grundlegendsten und wichtigsten Technologien.'
|
content:
|
||||||
|
'Die Energietechnik kann Ihre Energieproduktion verbessern und fortgeschrittene Gebäude freischalten. Dies ist eine der grundlegendsten und wichtigsten Technologien.'
|
||||||
},
|
},
|
||||||
shipyardIntro: {
|
shipyardIntro: {
|
||||||
title: 'Flotte & Werft',
|
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).'
|
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: {
|
gotoBuildingsForShipyard: {
|
||||||
title: 'Zurück zur Gebäudeseite',
|
title: 'Zurück zur Gebäudeseite',
|
||||||
@@ -1451,32 +1507,39 @@ export default {
|
|||||||
},
|
},
|
||||||
buildShipyard: {
|
buildShipyard: {
|
||||||
title: 'Raumschiffwerft bauen',
|
title: 'Raumschiffwerft bauen',
|
||||||
content: 'Die Raumschiffwerft ermöglicht es Ihnen, Schiffe und Verteidigungssysteme zu bauen. Dies ist für Flottenoperationen unerlässlich.'
|
content:
|
||||||
|
'Die Raumschiffwerft ermöglicht es Ihnen, Schiffe und Verteidigungssysteme zu bauen. Dies ist für Flottenoperationen unerlässlich.'
|
||||||
},
|
},
|
||||||
fleetIntro: {
|
fleetIntro: {
|
||||||
title: 'Flottenoperationen',
|
title: 'Flottenoperationen',
|
||||||
content: 'Sobald Sie Schiffe haben, können Sie sie auf Missionen schicken: Ressourcen transportieren, Planeten kolonisieren, Feinde angreifen oder Trümmerfelder erkunden.'
|
content:
|
||||||
|
'Sobald Sie Schiffe haben, können Sie sie auf Missionen schicken: Ressourcen transportieren, Planeten kolonisieren, Feinde angreifen oder Trümmerfelder erkunden.'
|
||||||
},
|
},
|
||||||
galaxyIntro: {
|
galaxyIntro: {
|
||||||
title: 'Galaxie erkunden',
|
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.'
|
content:
|
||||||
|
'Die Galaxieansicht zeigt andere Planeten, Trümmerfelder und Expansionsmöglichkeiten. Verwenden Sie sie, um Ziele auszukundschaften und Ihre Strategie zu planen.'
|
||||||
},
|
},
|
||||||
complete: {
|
complete: {
|
||||||
title: 'Anleitung abgeschlossen!',
|
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!'
|
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: {
|
mobile: {
|
||||||
welcome: {
|
welcome: {
|
||||||
title: 'Willkommen bei OGame (Mobil)',
|
title: 'Willkommen bei OGame (Mobil)',
|
||||||
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.'
|
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: {
|
resources: {
|
||||||
title: 'Obere Ressourcenleiste',
|
title: 'Obere Ressourcenleiste',
|
||||||
content: 'Oben werden Ihre Ressourcen angezeigt: Metall, Kristall und Deuterium. Tippen Sie, um detaillierte Produktionsinformationen anzuzeigen.'
|
content:
|
||||||
|
'Oben werden Ihre Ressourcen angezeigt: Metall, Kristall und Deuterium. Tippen Sie, um detaillierte Produktionsinformationen anzuzeigen.'
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
title: 'Navigationsmenü öffnen',
|
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.'
|
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: {
|
gotoBuildings: {
|
||||||
title: 'Zur Gebäudeseite gehen',
|
title: 'Zur Gebäudeseite gehen',
|
||||||
@@ -1484,19 +1547,23 @@ export default {
|
|||||||
},
|
},
|
||||||
buildSolarPlant: {
|
buildSolarPlant: {
|
||||||
title: 'Solarkraftwerk bauen',
|
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.'
|
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: {
|
waitBuild: {
|
||||||
title: 'Bauauftrag',
|
title: 'Bauauftrag',
|
||||||
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.'
|
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: {
|
buildMetalMine: {
|
||||||
title: 'Metallmine bauen',
|
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.'
|
content:
|
||||||
|
'Nachdem Sie Energie haben, bauen Sie eine Metallmine. Scrollen Sie nach unten, um die Metallmine zu finden, und tippen Sie zum Bauen.'
|
||||||
},
|
},
|
||||||
complete: {
|
complete: {
|
||||||
title: 'Schnellanleitung abgeschlossen!',
|
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!'
|
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!'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1565,7 +1632,8 @@ export default {
|
|||||||
},
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
title: 'Kampagnenmodus',
|
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.'
|
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: {
|
achievements: {
|
||||||
title: 'Erfolgssystem',
|
title: 'Erfolgssystem',
|
||||||
@@ -1804,6 +1872,27 @@ export default {
|
|||||||
reputationBonusDesc: 'Dein Verbündeter {npcName} spricht gut von dir zu {targetNpc}'
|
reputationBonusDesc: 'Dein Verbündeter {npcName} spricht gut von dir zu {targetNpc}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV-Verbindung erfolgreich',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV-Verbindung erfolgreich, Speicherverzeichnis erstellt',
|
||||||
|
authFailed: 'Authentifizierung fehlgeschlagen, bitte Benutzername und Passwort überprüfen',
|
||||||
|
directoryNotExist: 'Verzeichnis existiert nicht und konnte nicht erstellt werden',
|
||||||
|
networkError: 'Netzwerkfehler, bitte Serveradresse und Netzwerk überprüfen',
|
||||||
|
unknownError: 'Unbekannter Fehler',
|
||||||
|
uploadSuccess: 'Spielstand erfolgreich hochgeladen',
|
||||||
|
uploadFailed: 'Hochladen fehlgeschlagen',
|
||||||
|
downloadSuccess: 'Spielstand erfolgreich heruntergeladen',
|
||||||
|
downloadFailed: 'Herunterladen fehlgeschlagen',
|
||||||
|
noSaveFiles: 'Keine Spielstände auf dem Server',
|
||||||
|
fileListSuccess: 'Spielstandliste erfolgreich abgerufen',
|
||||||
|
fileListFailed: 'Abrufen der Spielstandliste fehlgeschlagen',
|
||||||
|
deleteSuccess: 'Spielstand erfolgreich gelöscht',
|
||||||
|
deleteFailed: 'Löschen fehlgeschlagen',
|
||||||
|
serverError: 'Serverfehler',
|
||||||
|
notConfigured: 'WebDAV nicht konfiguriert',
|
||||||
|
invalidUrl: 'Ungültige WebDAV-URL',
|
||||||
|
timeout: 'Verbindungszeitüberschreitung'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: 'Kampagne',
|
name: 'Kampagne',
|
||||||
description: 'Erkunde die mysteriöse Galaxie und entdecke antike Geheimnisse',
|
description: 'Erkunde die mysteriöse Galaxie und entdecke antike Geheimnisse',
|
||||||
@@ -1830,6 +1919,8 @@ export default {
|
|||||||
branchUnlocked: 'Neuer Storyzweig freigeschaltet!'
|
branchUnlocked: 'Neuer Storyzweig freigeschaltet!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Story-Dialog',
|
||||||
|
description: 'Kampagnen-Story-Dialoginhalt',
|
||||||
skip: 'Überspringen',
|
skip: 'Überspringen',
|
||||||
continue: 'Weiter',
|
continue: 'Weiter',
|
||||||
finish: 'Beenden',
|
finish: 'Beenden',
|
||||||
@@ -1920,13 +2011,19 @@ export default {
|
|||||||
questNotActive: 'Quest nicht aktiv',
|
questNotActive: 'Quest nicht aktiv',
|
||||||
questNotCompleted: 'Quest nicht abgeschlossen',
|
questNotCompleted: 'Quest nicht abgeschlossen',
|
||||||
rewardsAlreadyClaimed: 'Belohnungen bereits erhalten',
|
rewardsAlreadyClaimed: 'Belohnungen bereits erhalten',
|
||||||
prerequisiteNotMet: 'Voraussetzungs-Quest nicht abgeschlossen'
|
prerequisiteNotMet: 'Voraussetzungs-Quest nicht abgeschlossen',
|
||||||
|
questLocked: 'Quest ist gesperrt',
|
||||||
|
notInitialized: 'Kampagne nicht initialisiert',
|
||||||
|
questAlreadyCompleted: 'Quest bereits abgeschlossen'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: 'Antike Stimme',
|
ancientVoice: 'Antike Stimme',
|
||||||
neighborNPC: 'Nachbar-Fraktion',
|
neighborNPC: 'Nachbar-Fraktion',
|
||||||
mysteriousSignal: 'Mysteriöses Signal',
|
mysteriousSignal: 'Mysteriöses Signal',
|
||||||
enemyCommander: 'Feindlicher Kommandant'
|
enemyCommander: 'Feindlicher Kommandant',
|
||||||
|
shadowVoice: 'Schattenstimme',
|
||||||
|
allyNPC: 'Verbündete Fraktion',
|
||||||
|
ancientGuardian: 'Antiker Wächter'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: 'Baue Metallmine auf Stufe 2',
|
buildMetalMine: 'Baue Metallmine auf Stufe 2',
|
||||||
@@ -1942,25 +2039,42 @@ export default {
|
|||||||
spyAnyNPC: 'Spioniere einen NPC-Planeten aus',
|
spyAnyNPC: 'Spioniere einen NPC-Planeten aus',
|
||||||
sendGiftToNPC: 'Sende ein Geschenk an einen NPC',
|
sendGiftToNPC: 'Sende ein Geschenk an einen NPC',
|
||||||
researchAstrophysics: 'Erforsche Astrophysik auf Stufe 1',
|
researchAstrophysics: 'Erforsche Astrophysik auf Stufe 1',
|
||||||
|
researchAstrophysicsHigher: 'Erforsche Astrophysik auf Stufe 3',
|
||||||
buildColonyShip: 'Baue ein Kolonieschiff',
|
buildColonyShip: 'Baue ein Kolonieschiff',
|
||||||
colonizeNewPlanet: 'Kolonisiere einen neuen Planeten',
|
colonizeNewPlanet: 'Kolonisiere einen neuen Planeten',
|
||||||
completeExpedition: 'Schließe eine Expeditionsmission ab',
|
colonizeMultiple: 'Kolonisiere 5 Planeten',
|
||||||
|
completeExpedition: 'Schließe 3 Expeditionsmissionen ab',
|
||||||
|
expeditionDeepSpace: 'Schließe 2 Tiefenraum-Expeditionen ab',
|
||||||
|
expeditionUncharted: 'Erkunde 1 unbekannte Region',
|
||||||
|
expeditionDangerous: 'Schließe 3 gefährliche Nebel-Expeditionen ab',
|
||||||
discoverRuins: 'Entdecke antike Ruinen',
|
discoverRuins: 'Entdecke antike Ruinen',
|
||||||
researchComputer: 'Erforsche Computertechnik auf Stufe 4',
|
researchComputer: 'Erforsche Computertechnik auf Stufe 4',
|
||||||
|
researchImpulse: 'Erforsche Impulsantrieb auf Stufe 3',
|
||||||
|
researchLaser: 'Erforsche Lasertechnik auf Stufe 5',
|
||||||
|
researchIntergalactic: 'Erforsche Computertechnik auf Stufe 10',
|
||||||
|
researchGraviton: 'Erforsche Gravitontechnik auf Stufe 1',
|
||||||
improveRelation: 'Verbessere Beziehungen zu einem NPC',
|
improveRelation: 'Verbessere Beziehungen zu einem NPC',
|
||||||
reachFriendly: 'Erreiche freundlichen Status mit einem NPC',
|
reachFriendly: 'Erreiche freundlichen Status mit einem NPC',
|
||||||
spyHostileNPC: 'Spioniere einen feindlichen NPC aus',
|
reachFriendlyRelation: 'Erreiche freundlichen Status mit einem beliebigen NPC',
|
||||||
|
sendMultipleGifts: 'Sende 3 Geschenke an NPCs',
|
||||||
|
spyHostileNPC: 'Spioniere 2 feindliche NPCs aus',
|
||||||
formAlliance: 'Schließe Allianz mit einem freundlichen NPC',
|
formAlliance: 'Schließe Allianz mit einem freundlichen NPC',
|
||||||
buildDefenses: 'Baue Verteidigungsanlagen',
|
buildDefenses: 'Baue Verteidigungsanlagen',
|
||||||
|
buildMissileSilo: 'Baue Raketensilo auf Stufe 2',
|
||||||
|
buildCruisers: 'Baue 10 Kreuzer',
|
||||||
winDefenseBattle: 'Gewinne eine Verteidigungsschlacht',
|
winDefenseBattle: 'Gewinne eine Verteidigungsschlacht',
|
||||||
|
defendAgainstAttack: 'Verteidige erfolgreich gegen 1 Angriff',
|
||||||
spyEnemyPlanet: 'Spioniere Feindplaneten aus',
|
spyEnemyPlanet: 'Spioniere Feindplaneten aus',
|
||||||
|
spyEnemyPlanets: 'Spioniere 5 Feindplaneten aus',
|
||||||
|
winAttackBattles: 'Gewinne 3 Angriffsschlachten',
|
||||||
attackEnemy: 'Greife den Feind an',
|
attackEnemy: 'Greife den Feind an',
|
||||||
recycleDebris: 'Recycel Trümmer',
|
recycleDebris: 'Recycel 5 Mal Trümmer',
|
||||||
buildBattleships: 'Baue 10 Schlachtschiffe',
|
buildBattleships: 'Baue 20 Schlachtschiffe',
|
||||||
exploreDeepRuins: 'Erkunde tiefe Ruinen',
|
exploreDeepRuins: 'Erkunde tiefe Ruinen',
|
||||||
researchHyperspace: 'Erforsche Hyperraumtechnologie',
|
researchHyperspace: 'Erforsche Hyperraumantrieb auf Stufe 3',
|
||||||
defeatBoss: 'Besiege den mysteriösen Feind',
|
defeatBoss: 'Besiege den Antiken Wächter',
|
||||||
colonizeSpecial: 'Kolonisiere besonderen Standort',
|
colonizeSpecial: 'Kolonisiere besonderen Standort',
|
||||||
|
accumulateWealth: 'Sammle 1 Million Gesamtressourcen',
|
||||||
continueDevelopment: 'Setze Entwicklung fort'
|
continueDevelopment: 'Setze Entwicklung fort'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -1999,13 +2113,19 @@ export default {
|
|||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
'Deine Expedition entdeckte anomale Signale. Diese Signale scheinen von einer antiken Zivilisation zu stammen... Untersuche ihre Quelle.',
|
'Deine Expedition entdeckte anomale Signale. Diese Signale scheinen von einer antiken Zivilisation zu stammen... Untersuche ihre Quelle.',
|
||||||
|
prologue_2:
|
||||||
|
'Diese Signale... Sie tragen Echos einer längst verlorenen Zivilisation. Ihre Geheimnisse warten darauf, entdeckt zu werden...',
|
||||||
epilogue_1: 'Diese Symbole... Sie sind Ruinen einer antiken Zivilisation! Untersuche weiter, um ihre Geheimnisse zu enthüllen.'
|
epilogue_1: 'Diese Symbole... Sie sind Ruinen einer antiken Zivilisation! Untersuche weiter, um ihre Geheimnisse zu enthüllen.'
|
||||||
},
|
},
|
||||||
'2_4': {
|
'2_4': {
|
||||||
prologue_1: 'Du hast den Standort antiker Ruinen gefunden. Sende deine Flotte zur Erkundung und sieh, was du entdecken kannst.'
|
prologue_1: 'Du hast den Standort antiker Ruinen gefunden. Sende deine Flotte zur Erkundung und sieh, was du entdecken kannst.',
|
||||||
|
prologue_2: 'Die Ruinen bergen viele Geheimnisse... Wähle deinen Weg weise...',
|
||||||
|
choice_1: 'Vorsichtig erkunden - Sicherheit priorisieren',
|
||||||
|
choice_2: 'Aggressiv erkunden - Entdeckung priorisieren'
|
||||||
},
|
},
|
||||||
'2_5': {
|
'2_5': {
|
||||||
prologue_1: 'Datenarchive wurden in den Ruinen gefunden. Studiere diese Daten, vielleicht kannst du neue Technologie freischalten.'
|
prologue_1: 'Datenarchive wurden in den Ruinen gefunden. Studiere diese Daten, vielleicht kannst du neue Technologie freischalten.',
|
||||||
|
epilogue_1: 'Die antiken Daten wurden entschlüsselt! Neue technologische Erkenntnisse wurden gewonnen.'
|
||||||
},
|
},
|
||||||
'3_1': { prologue_1: 'Beim Erkunden vergiss nicht die Diplomatie. Gute Beziehungen zu umliegenden Fraktionen zu pflegen nützt dir.' },
|
'3_1': { prologue_1: 'Beim Erkunden vergiss nicht die Diplomatie. Gute Beziehungen zu umliegenden Fraktionen zu pflegen nützt dir.' },
|
||||||
'3_2': {
|
'3_2': {
|
||||||
@@ -2014,14 +2134,19 @@ export default {
|
|||||||
},
|
},
|
||||||
'3_3': {
|
'3_3': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
'Geheimdienstberichte deuten darauf hin, dass feindliche Kräfte dich aus den Schatten beobachten. Bleibe wachsam und erkunde ihre Bewegungen.'
|
'Geheimdienstberichte deuten darauf hin, dass feindliche Kräfte dich aus den Schatten beobachten. Bleibe wachsam und erkunde ihre Bewegungen.',
|
||||||
|
prologue_2: 'Dunkle Mächte regen sich in der Leere... Sie haben deine wachsende Macht bemerkt...'
|
||||||
},
|
},
|
||||||
'3_4': {
|
'3_4': {
|
||||||
prologue_1: 'Schließe eine formelle Allianz mit freundlichen Fraktionen, um euch gegenseitig gegen Bedrohungen zu unterstützen.'
|
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.' },
|
'3_5': {
|
||||||
|
prologue_1: 'Bedrohungen nähern sich. Baue Verteidigungsanlagen und bereite dich auf mögliche Konflikte vor.',
|
||||||
|
epilogue_1: 'Deine Verteidigung ist bereit. Der Sturm naht, aber du bist vorbereitet.'
|
||||||
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: 'Der Feind hat einen Angriff gestartet! Verteidige deinen Planeten!',
|
prologue_1: 'Der Feind hat einen Angriff gestartet! Verteidige deinen Planeten!',
|
||||||
|
prologue_2: 'Die Schattenflotte nähert sich... Deine Stunde der Prüfung ist gekommen...',
|
||||||
epilogue_1: 'Du hast die erste Welle des Feindes erfolgreich abgewehrt. Aber das ist erst der Anfang...'
|
epilogue_1: 'Du hast die erste Welle des Feindes erfolgreich abgewehrt. Aber das ist erst der Anfang...'
|
||||||
},
|
},
|
||||||
'4_2': {
|
'4_2': {
|
||||||
@@ -2031,20 +2156,28 @@ export default {
|
|||||||
'4_4': {
|
'4_4': {
|
||||||
prologue_1: 'Viele Trümmer bleiben auf dem Schlachtfeld. Recycel diese Ressourcen, um dich auf die nächste Schlacht vorzubereiten.'
|
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.' },
|
'4_5': {
|
||||||
|
prologue_1: 'Die letzte Schlacht naht. Baue eine mächtige Flotte und bereite dich auf die ultimative Herausforderung vor.',
|
||||||
|
epilogue_1: 'Deine Flotte ist versammelt. Der entscheidende Moment naht...'
|
||||||
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1: 'Alle Hinweise deuten auf den tiefsten Teil der Ruinen. Die Kerngeheimnisse der antiken Zivilisation liegen dort.',
|
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...'
|
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_2': {
|
||||||
|
prologue_1: 'In den Tiefen der Ruinen entdecktest du verlorene antike Technologie. Erforsche und schalte ihre Macht frei.',
|
||||||
|
prologue_2: 'Diese Technologie... Sie ist älter als alle bekannten Zivilisationen. Gehe vorsichtig damit um...'
|
||||||
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: 'Ein mysteriöser Feind ist aufgetaucht. Dies ist die letzte Herausforderung. Besiege ihn!',
|
prologue_1: 'Ein mysteriöser Feind ist aufgetaucht. Dies ist die letzte Herausforderung. Besiege ihn!',
|
||||||
|
prologue_2: 'Ich bin der Wächter dieser Geheimnisse. Beweise deinen Wert oder werde vernichtet!',
|
||||||
epilogue_1: 'Du hast es geschafft! Der antike Wächter wurde besiegt. Die Geheimnisse der Galaxie stehen dir nun offen.'
|
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_4': { prologue_1: 'Frieden ist endlich eingekehrt. In dieser neuen Ära gründe neue Kolonien und erweitere dein Imperium.' },
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: 'Deine Legende hat gerade erst begonnen. Erkunde weiter und erobere mehr Sternensysteme!',
|
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...'
|
epilogue_1: 'Die Galaxie ist weit und grenzenlos, mit zahllosen Geheimnissen, die auf dich warten...',
|
||||||
|
epilogue_2: 'Deine Reise geht weiter... Neue Abenteuer warten jenseits der Sterne...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ export default {
|
|||||||
cannotAttackOwnPlanet: 'Cannot attack your own planet',
|
cannotAttackOwnPlanet: 'Cannot attack your own planet',
|
||||||
fleetMissionsFull: 'Fleet mission slots full',
|
fleetMissionsFull: 'Fleet mission slots full',
|
||||||
insufficientFleet: 'Insufficient fleet',
|
insufficientFleet: 'Insufficient fleet',
|
||||||
|
insufficientShips: 'Insufficient ships',
|
||||||
|
invalidQuantity: 'Invalid quantity',
|
||||||
insufficientFuel: 'Insufficient fuel',
|
insufficientFuel: 'Insufficient fuel',
|
||||||
planetOnly: 'This building can only be built on planets',
|
planetOnly: 'This building can only be built on planets',
|
||||||
moonOnly: 'This building can only be built on moons',
|
moonOnly: 'This building can only be built on moons',
|
||||||
@@ -55,7 +57,12 @@ export default {
|
|||||||
researchQueueFull: 'Research queue full',
|
researchQueueFull: 'Research queue full',
|
||||||
moonExists: 'Moon already exists',
|
moonExists: 'Moon already exists',
|
||||||
insufficientDebris: 'Insufficient debris field',
|
insufficientDebris: 'Insufficient debris field',
|
||||||
launchFailed: 'Launch failed'
|
launchFailed: 'Launch failed',
|
||||||
|
planetNotFound: 'Planet not found',
|
||||||
|
cannotAbandonHomePlanet: 'Cannot abandon home planet',
|
||||||
|
hasBuildQueue: 'Please wait for build queue to complete',
|
||||||
|
hasFleetOnPlanet: 'Please transfer or scrap all fleet first',
|
||||||
|
hasDefenseOnPlanet: 'Please demolish all defense first'
|
||||||
},
|
},
|
||||||
nav: {
|
nav: {
|
||||||
overview: 'Overview',
|
overview: 'Overview',
|
||||||
@@ -123,7 +130,12 @@ export default {
|
|||||||
renamePlanet: 'Rename Planet',
|
renamePlanet: 'Rename Planet',
|
||||||
renamePlanetTitle: 'Rename Planet',
|
renamePlanetTitle: 'Rename Planet',
|
||||||
planetNamePlaceholder: 'Enter new planet name',
|
planetNamePlaceholder: 'Enter new planet name',
|
||||||
rename: 'Rename'
|
rename: 'Rename',
|
||||||
|
abandonColony: 'Abandon Colony',
|
||||||
|
confirmAbandon: 'Confirm Abandon Colony',
|
||||||
|
abandonWarning: 'Are you sure you want to abandon "{name}"?\n\nThis action cannot be undone!\nAll buildings, resources and the moon (if any) will be lost.',
|
||||||
|
confirmAbandonButton: 'Abandon',
|
||||||
|
abandonFailed: 'Abandon Failed'
|
||||||
},
|
},
|
||||||
player: {
|
player: {
|
||||||
points: 'Total Points'
|
points: 'Total Points'
|
||||||
@@ -152,9 +164,14 @@ export default {
|
|||||||
planetDestroyerFactory: 'Planet Destroyer Factory',
|
planetDestroyerFactory: 'Planet Destroyer Factory',
|
||||||
geoResearchStation: 'Geological Research Station',
|
geoResearchStation: 'Geological Research Station',
|
||||||
deepDrillingFacility: 'Deep Drilling Facility',
|
deepDrillingFacility: 'Deep Drilling Facility',
|
||||||
|
university: 'University',
|
||||||
buildTime: 'Build Time',
|
buildTime: 'Build Time',
|
||||||
production: 'Production',
|
production: 'Production',
|
||||||
consumption: 'Consumption',
|
consumption: 'Consumption',
|
||||||
|
levelRange: 'Level Range',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalPoints: 'Total Points',
|
||||||
|
researchSpeedBonus: 'Research Speed Bonus',
|
||||||
|
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -195,8 +212,9 @@ export default {
|
|||||||
sensorPhalanx: 'Detects fleet activities in surrounding systems',
|
sensorPhalanx: 'Detects fleet activities in surrounding systems',
|
||||||
jumpGate: 'Instantly transfers fleets to other moons',
|
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',
|
geoResearchStation: 'Researches geological structures and increases ore deposit regeneration rate. +10% regeneration per level',
|
||||||
deepDrillingFacility: 'Drills deep into the crust to access deeper ore veins. +20% ore deposit capacity per level'
|
deepDrillingFacility: 'Drills deep into the crust to access deeper ore veins. +20% ore deposit capacity per level',
|
||||||
|
university: 'Trains researchers to accelerate research speed. -8% research time per level (stacks with Intergalactic Research Network)'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: 'Light Fighter',
|
lightFighter: 'Light Fighter',
|
||||||
@@ -260,6 +278,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: 'Research Time',
|
researchTime: 'Research Time',
|
||||||
|
levelRange: 'Level Range',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalPoints: 'Total Points',
|
||||||
|
|
||||||
attackBonus: 'Attack Bonus',
|
attackBonus: 'Attack Bonus',
|
||||||
shieldBonus: 'Shield Bonus',
|
shieldBonus: 'Shield Bonus',
|
||||||
@@ -268,7 +289,11 @@ export default {
|
|||||||
researchQueueBonus: 'Research Queue',
|
researchQueueBonus: 'Research Queue',
|
||||||
colonySlots: 'Colony Slots',
|
colonySlots: 'Colony Slots',
|
||||||
forAllPlanets: '(Global)',
|
forAllPlanets: '(Global)',
|
||||||
speedBonus: 'Speed Bonus'
|
speedBonus: 'Speed Bonus',
|
||||||
|
// 资源产量加成显示
|
||||||
|
mineralResearch: 'Mineral Research',
|
||||||
|
crystalResearch: 'Crystal Research',
|
||||||
|
fuelResearch: 'Fuel Research'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: 'Energy Technology',
|
energyTechnology: 'Energy Technology',
|
||||||
@@ -289,7 +314,11 @@ export default {
|
|||||||
darkMatterTechnology: 'Dark Matter Technology',
|
darkMatterTechnology: 'Dark Matter Technology',
|
||||||
terraformingTechnology: 'Terraforming Technology',
|
terraformingTechnology: 'Terraforming Technology',
|
||||||
planetDestructionTech: 'Planet Destruction Technology',
|
planetDestructionTech: 'Planet Destruction Technology',
|
||||||
miningTechnology: 'Mining Technology'
|
miningTechnology: 'Mining Technology',
|
||||||
|
intergalacticResearchNetwork: 'Intergalactic Research Network',
|
||||||
|
mineralResearch: 'Mineral Research',
|
||||||
|
crystalResearch: 'Crystal Research',
|
||||||
|
fuelResearch: 'Fuel Research'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: 'Improves energy efficiency',
|
energyTechnology: 'Improves energy efficiency',
|
||||||
@@ -311,7 +340,11 @@ export default {
|
|||||||
darkMatterTechnology: 'Research into dark matter properties and applications',
|
darkMatterTechnology: 'Research into dark matter properties and applications',
|
||||||
terraformingTechnology: 'Research planet terraforming technology, adds 30 available space to all planets per level',
|
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'
|
miningTechnology: 'Improves mining methods and equipment, increases ore deposit capacity on all planets. +15% capacity per level',
|
||||||
|
intergalacticResearchNetwork: 'Links multiple research labs across planets. Each level connects 1 additional lab (highest level labs are used)',
|
||||||
|
mineralResearch: 'Research more efficient metal extraction techniques. +2% metal production per level',
|
||||||
|
crystalResearch: 'Research more efficient crystal refinement techniques. +2% crystal production per level',
|
||||||
|
fuelResearch: 'Research more efficient deuterium synthesis techniques. +2% deuterium production per level'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: 'Commander',
|
commander: 'Commander',
|
||||||
@@ -382,6 +415,7 @@ export default {
|
|||||||
title: 'Buildings',
|
title: 'Buildings',
|
||||||
spaceUsage: 'Space Usage',
|
spaceUsage: 'Space Usage',
|
||||||
upgradeCost: 'Upgrade Cost',
|
upgradeCost: 'Upgrade Cost',
|
||||||
|
build: 'Build',
|
||||||
upgrade: 'Upgrade',
|
upgrade: 'Upgrade',
|
||||||
maxLevelReached: 'Max Level Reached',
|
maxLevelReached: 'Max Level Reached',
|
||||||
requirementsNotMet: 'Requirements Not Met',
|
requirementsNotMet: 'Requirements Not Met',
|
||||||
@@ -402,10 +436,20 @@ export default {
|
|||||||
researchFailedMessage: 'Please check if you have enough resources, prerequisites are met, or if there are other research tasks.'
|
researchFailedMessage: 'Please check if you have enough resources, prerequisites are met, or if there are other research tasks.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Attack',
|
||||||
|
shield: 'Shield',
|
||||||
|
armor: 'Armor',
|
||||||
missileAttack: 'Missile Attack',
|
missileAttack: 'Missile Attack',
|
||||||
speed: 'Speed',
|
speed: 'Speed',
|
||||||
cargoCapacity: 'Cargo Capacity',
|
cargoCapacity: 'Cargo Capacity',
|
||||||
fuelConsumption: 'Fuel Consumption'
|
fuelConsumption: 'Fuel Consumption',
|
||||||
|
buildCost: 'Build Cost',
|
||||||
|
buildTime: 'Build Time',
|
||||||
|
perUnit: 'Per Unit',
|
||||||
|
batchCalculator: 'Batch Calculator',
|
||||||
|
quantity: 'Quantity',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalTime: 'Total Time'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Shipyard',
|
title: 'Shipyard',
|
||||||
@@ -422,10 +466,25 @@ export default {
|
|||||||
inputError: 'Input Error',
|
inputError: 'Input Error',
|
||||||
inputErrorMessage: 'Please enter build quantity!',
|
inputErrorMessage: 'Please enter build quantity!',
|
||||||
buildFailed: 'Build Failed',
|
buildFailed: 'Build Failed',
|
||||||
buildFailedMessage: 'Please check if you have enough resources or if prerequisites are met.'
|
buildFailedMessage: 'Please check if you have enough resources or if prerequisites are met.',
|
||||||
|
scrapQuantity: 'Scrap Quantity',
|
||||||
|
scrapRefund: 'Scrap Refund (50%)',
|
||||||
|
scrap: 'Scrap',
|
||||||
|
scrapFailed: 'Scrap Failed',
|
||||||
|
scrapFailedMessage: 'Please check if you have enough ships.'
|
||||||
},
|
},
|
||||||
defense: {
|
defense: {
|
||||||
missileAttack: 'Missile Attack'
|
attack: 'Attack',
|
||||||
|
shield: 'Shield',
|
||||||
|
armor: 'Armor',
|
||||||
|
missileAttack: 'Missile Attack',
|
||||||
|
buildCost: 'Build Cost',
|
||||||
|
buildTime: 'Build Time',
|
||||||
|
perUnit: 'Per Unit',
|
||||||
|
batchCalculator: 'Batch Calculator',
|
||||||
|
quantity: 'Quantity',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalTime: 'Total Time'
|
||||||
},
|
},
|
||||||
defenseView: {
|
defenseView: {
|
||||||
title: 'Defense',
|
title: 'Defense',
|
||||||
@@ -504,6 +563,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: 'Recycle',
|
recycle: 'Recycle',
|
||||||
destroy: 'Planet Destruction',
|
destroy: 'Planet Destruction',
|
||||||
|
harvestDarkMatter: 'Harvest Dark Matter',
|
||||||
|
station: 'Station',
|
||||||
transportResources: 'Transport Resources',
|
transportResources: 'Transport Resources',
|
||||||
totalCargoCapacity: 'Total Cargo Capacity',
|
totalCargoCapacity: 'Total Cargo Capacity',
|
||||||
used: 'Used',
|
used: 'Used',
|
||||||
@@ -887,6 +948,9 @@ export default {
|
|||||||
round: 'Round {round}',
|
round: 'Round {round}',
|
||||||
attackerRemainingPower: 'Attacker remaining power',
|
attackerRemainingPower: 'Attacker remaining power',
|
||||||
defenderRemainingPower: 'Defender remaining power',
|
defenderRemainingPower: 'Defender remaining power',
|
||||||
|
importFromSpyReport: 'Import from Spy Report',
|
||||||
|
selectSpyReport: 'Select Spy Report',
|
||||||
|
noSpyReports: 'No spy reports available',
|
||||||
// Battle animation
|
// Battle animation
|
||||||
playAnimation: 'Play Animation',
|
playAnimation: 'Play Animation',
|
||||||
showDetails: 'Show Details',
|
showDetails: 'Show Details',
|
||||||
@@ -914,6 +978,7 @@ export default {
|
|||||||
exporting: 'Exporting...',
|
exporting: 'Exporting...',
|
||||||
exportSuccess: 'Export successful',
|
exportSuccess: 'Export successful',
|
||||||
exportSuccessWithPath: 'Export successful, file saved to: {path}',
|
exportSuccessWithPath: 'Export successful, file saved to: {path}',
|
||||||
|
storagePermissionDenied: 'Storage permission denied, cannot export file',
|
||||||
exportFailed: 'Export failed, please try again',
|
exportFailed: 'Export failed, please try again',
|
||||||
importData: 'Import Data',
|
importData: 'Import Data',
|
||||||
importDataDesc: 'Restore game progress from JSON file',
|
importDataDesc: 'Restore game progress from JSON file',
|
||||||
@@ -931,6 +996,8 @@ export default {
|
|||||||
gameSettingsDesc: 'Adjust game parameters and preferences',
|
gameSettingsDesc: 'Adjust game parameters and preferences',
|
||||||
gamePause: 'Game Pause',
|
gamePause: 'Game Pause',
|
||||||
gamePauseDesc: 'Pause or resume game time and resource production',
|
gamePauseDesc: 'Pause or resume game time and resource production',
|
||||||
|
battleMode: 'Fight to Finish Mode',
|
||||||
|
battleModeDesc: 'When enabled, battles last up to 100 rounds until a winner is decided. When disabled, uses classic 6-round mode',
|
||||||
pause: 'Pause',
|
pause: 'Pause',
|
||||||
resume: 'Resume',
|
resume: 'Resume',
|
||||||
gamePaused: 'Game paused',
|
gamePaused: 'Game paused',
|
||||||
@@ -1753,6 +1820,8 @@ export default {
|
|||||||
branchUnlocked: 'New story branch unlocked!'
|
branchUnlocked: 'New story branch unlocked!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Story Dialogue',
|
||||||
|
description: 'Campaign story dialogue content',
|
||||||
skip: 'Skip',
|
skip: 'Skip',
|
||||||
continue: 'Continue',
|
continue: 'Continue',
|
||||||
finish: 'Finish',
|
finish: 'Finish',
|
||||||
@@ -1918,13 +1987,19 @@ export default {
|
|||||||
questNotActive: 'Quest not active',
|
questNotActive: 'Quest not active',
|
||||||
questNotCompleted: 'Quest not completed',
|
questNotCompleted: 'Quest not completed',
|
||||||
rewardsAlreadyClaimed: 'Rewards already claimed',
|
rewardsAlreadyClaimed: 'Rewards already claimed',
|
||||||
prerequisiteNotMet: 'Prerequisite quest not completed'
|
prerequisiteNotMet: 'Prerequisite quest not completed',
|
||||||
|
questLocked: 'Quest is locked',
|
||||||
|
notInitialized: 'Campaign not initialized',
|
||||||
|
questAlreadyCompleted: 'Quest already completed'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: 'Ancient Voice',
|
ancientVoice: 'Ancient Voice',
|
||||||
neighborNPC: 'Neighbor Faction',
|
neighborNPC: 'Neighbor Faction',
|
||||||
mysteriousSignal: 'Mysterious Signal',
|
mysteriousSignal: 'Mysterious Signal',
|
||||||
enemyCommander: 'Enemy Commander'
|
enemyCommander: 'Enemy Commander',
|
||||||
|
shadowVoice: 'Shadow Voice',
|
||||||
|
allyNPC: 'Allied Faction',
|
||||||
|
ancientGuardian: 'Ancient Guardian'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: 'Build Metal Mine to level 2',
|
buildMetalMine: 'Build Metal Mine to level 2',
|
||||||
@@ -1940,25 +2015,42 @@ export default {
|
|||||||
spyAnyNPC: 'Spy on any NPC planet',
|
spyAnyNPC: 'Spy on any NPC planet',
|
||||||
sendGiftToNPC: 'Send a gift to any NPC',
|
sendGiftToNPC: 'Send a gift to any NPC',
|
||||||
researchAstrophysics: 'Research Astrophysics to level 1',
|
researchAstrophysics: 'Research Astrophysics to level 1',
|
||||||
|
researchAstrophysicsHigher: 'Research Astrophysics to level 3',
|
||||||
buildColonyShip: 'Build a Colony Ship',
|
buildColonyShip: 'Build a Colony Ship',
|
||||||
colonizeNewPlanet: 'Colonize a new planet',
|
colonizeNewPlanet: 'Colonize a new planet',
|
||||||
completeExpedition: 'Complete an expedition mission',
|
colonizeMultiple: 'Colonize 5 planets',
|
||||||
|
completeExpedition: 'Complete 3 expedition missions',
|
||||||
|
expeditionDeepSpace: 'Complete 2 deep space expeditions',
|
||||||
|
expeditionUncharted: 'Explore 1 uncharted region',
|
||||||
|
expeditionDangerous: 'Complete 3 dangerous nebula expeditions',
|
||||||
discoverRuins: 'Discover ancient ruins',
|
discoverRuins: 'Discover ancient ruins',
|
||||||
researchComputer: 'Research Computer Technology to level 4',
|
researchComputer: 'Research Computer Technology to level 4',
|
||||||
|
researchImpulse: 'Research Impulse Drive to level 3',
|
||||||
|
researchLaser: 'Research Laser Technology to level 5',
|
||||||
|
researchIntergalactic: 'Research Computer Technology to level 10',
|
||||||
|
researchGraviton: 'Research Graviton Technology to level 1',
|
||||||
improveRelation: 'Improve relations with an NPC',
|
improveRelation: 'Improve relations with an NPC',
|
||||||
reachFriendly: 'Reach friendly status with an NPC',
|
reachFriendly: 'Reach friendly status with an NPC',
|
||||||
spyHostileNPC: 'Spy on a hostile NPC',
|
reachFriendlyRelation: 'Reach friendly status with any NPC',
|
||||||
|
sendMultipleGifts: 'Send 3 gifts to NPCs',
|
||||||
|
spyHostileNPC: 'Spy on 2 hostile NPCs',
|
||||||
formAlliance: 'Form alliance with a friendly NPC',
|
formAlliance: 'Form alliance with a friendly NPC',
|
||||||
buildDefenses: 'Build defense facilities',
|
buildDefenses: 'Build defense facilities',
|
||||||
|
buildMissileSilo: 'Build Missile Silo to level 2',
|
||||||
|
buildCruisers: 'Build 10 Cruisers',
|
||||||
winDefenseBattle: 'Win a defensive battle',
|
winDefenseBattle: 'Win a defensive battle',
|
||||||
|
defendAgainstAttack: 'Successfully defend against 1 attack',
|
||||||
spyEnemyPlanet: 'Spy on enemy planet',
|
spyEnemyPlanet: 'Spy on enemy planet',
|
||||||
|
spyEnemyPlanets: 'Spy on 5 enemy planets',
|
||||||
|
winAttackBattles: 'Win 3 attack battles',
|
||||||
attackEnemy: 'Attack the enemy',
|
attackEnemy: 'Attack the enemy',
|
||||||
recycleDebris: 'Recycle debris',
|
recycleDebris: 'Recycle debris 5 times',
|
||||||
buildBattleships: 'Build 10 Battleships',
|
buildBattleships: 'Build 20 Battleships',
|
||||||
exploreDeepRuins: 'Explore deep ruins',
|
exploreDeepRuins: 'Explore deep ruins',
|
||||||
researchHyperspace: 'Research Hyperspace Technology',
|
researchHyperspace: 'Research Hyperspace Drive to level 3',
|
||||||
defeatBoss: 'Defeat the mysterious enemy',
|
defeatBoss: 'Defeat the Ancient Guardian',
|
||||||
colonizeSpecial: 'Colonize special location',
|
colonizeSpecial: 'Colonize special location',
|
||||||
|
accumulateWealth: 'Accumulate 1 million total resources',
|
||||||
continueDevelopment: 'Continue development'
|
continueDevelopment: 'Continue development'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -1995,13 +2087,18 @@ export default {
|
|||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
'Your expedition discovered anomalous signals. These signals seem to come from an ancient civilization... Investigate their source.',
|
'Your expedition discovered anomalous signals. These signals seem to come from an ancient civilization... Investigate their source.',
|
||||||
|
prologue_2: 'These signals... they carry echoes of a long-lost civilization. Their secrets await discovery...',
|
||||||
epilogue_1: 'These symbols... They are ruins of an ancient civilization! Continue investigating to uncover their secrets.'
|
epilogue_1: 'These symbols... They are ruins of an ancient civilization! Continue investigating to uncover their secrets.'
|
||||||
},
|
},
|
||||||
'2_4': {
|
'2_4': {
|
||||||
prologue_1: 'You have found the location of ancient ruins. Send your fleet to explore and see what you can discover.'
|
prologue_1: 'You have found the location of ancient ruins. Send your fleet to explore and see what you can discover.',
|
||||||
|
prologue_2: 'The ruins hold many secrets... Choose your path wisely...',
|
||||||
|
choice_1: 'Explore cautiously - prioritize safety',
|
||||||
|
choice_2: 'Explore aggressively - prioritize discovery'
|
||||||
},
|
},
|
||||||
'2_5': {
|
'2_5': {
|
||||||
prologue_1: 'Data archives were found in the ruins. Study this data, perhaps you can unlock new technology.'
|
prologue_1: 'Data archives were found in the ruins. Study this data, perhaps you can unlock new technology.',
|
||||||
|
epilogue_1: 'The ancient data has been decrypted! New technological insights have been gained.'
|
||||||
},
|
},
|
||||||
'3_1': {
|
'3_1': {
|
||||||
prologue_1: 'While exploring, do not forget about diplomacy. Maintaining good relations with surrounding factions benefits you.'
|
prologue_1: 'While exploring, do not forget about diplomacy. Maintaining good relations with surrounding factions benefits you.'
|
||||||
@@ -2010,16 +2107,19 @@ export default {
|
|||||||
prologue_1: 'Some factions have shown friendliness. Continue deepening relations, perhaps you can gain more support.'
|
prologue_1: 'Some factions have shown friendliness. Continue deepening relations, perhaps you can gain more support.'
|
||||||
},
|
},
|
||||||
'3_3': {
|
'3_3': {
|
||||||
prologue_1: 'Intelligence indicates hostile forces are watching you from the shadows. Stay vigilant and scout their movements.'
|
prologue_1: 'Intelligence indicates hostile forces are watching you from the shadows. Stay vigilant and scout their movements.',
|
||||||
|
prologue_2: 'Dark forces stir in the void... They have noticed your growing power...'
|
||||||
},
|
},
|
||||||
'3_4': {
|
'3_4': {
|
||||||
prologue_1: 'Establish a formal alliance with friendly factions to support each other against threats.'
|
prologue_1: 'Establish a formal alliance with friendly factions to support each other against threats.'
|
||||||
},
|
},
|
||||||
'3_5': {
|
'3_5': {
|
||||||
prologue_1: 'Threats are approaching. Build defense facilities and prepare for possible conflict.'
|
prologue_1: 'Threats are approaching. Build defense facilities and prepare for possible conflict.',
|
||||||
|
epilogue_1: 'Your defenses are ready. The storm is coming, but you are prepared.'
|
||||||
},
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: 'The enemy has launched an attack! Defend your planet!',
|
prologue_1: 'The enemy has launched an attack! Defend your planet!',
|
||||||
|
prologue_2: 'The shadow fleet approaches... Your moment of trial has come...',
|
||||||
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': {
|
'4_2': {
|
||||||
@@ -2032,17 +2132,20 @@ export default {
|
|||||||
prologue_1: 'Much debris remains on the battlefield. Recycle these resources to prepare for the next battle.'
|
prologue_1: 'Much debris remains on the battlefield. Recycle these resources to prepare for the next battle.'
|
||||||
},
|
},
|
||||||
'4_5': {
|
'4_5': {
|
||||||
prologue_1: 'The final battle approaches. Build a powerful fleet and prepare for the ultimate challenge.'
|
prologue_1: 'The final battle approaches. Build a powerful fleet and prepare for the ultimate challenge.',
|
||||||
|
epilogue_1: 'Your fleet is assembled. The decisive moment draws near...'
|
||||||
},
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1: 'All clues point to the deepest part of the ruins. The core secrets of the ancient civilization lie there.',
|
prologue_1: 'All clues point to the deepest part of the ruins. The core secrets of the ancient civilization lie there.',
|
||||||
prologue_2: 'You have finally arrived... The truth will soon be revealed...'
|
prologue_2: 'You have finally arrived... The truth will soon be revealed...'
|
||||||
},
|
},
|
||||||
'5_2': {
|
'5_2': {
|
||||||
prologue_1: 'In the depths of the ruins, you discovered lost ancient technology. Research and unlock their power.'
|
prologue_1: 'In the depths of the ruins, you discovered lost ancient technology. Research and unlock their power.',
|
||||||
|
prologue_2: 'This technology... it predates all known civilizations. Handle it with care...'
|
||||||
},
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: 'A mysterious enemy has appeared. This is the final challenge. Defeat it!',
|
prologue_1: 'A mysterious enemy has appeared. This is the final challenge. Defeat it!',
|
||||||
|
prologue_2: 'I am the guardian of these secrets. Prove your worth, or be destroyed!',
|
||||||
epilogue_1: 'You did it! The ancient guardian has been defeated. The secrets of the galaxy are now open to you.'
|
epilogue_1: 'You did it! The ancient guardian has been defeated. The secrets of the galaxy are now open to you.'
|
||||||
},
|
},
|
||||||
'5_4': {
|
'5_4': {
|
||||||
@@ -2050,7 +2153,8 @@ export default {
|
|||||||
},
|
},
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: 'Your legend has just begun. Continue exploring and conquering more star systems!',
|
prologue_1: 'Your legend has just begun. Continue exploring and conquering more star systems!',
|
||||||
epilogue_1: 'The galaxy is vast and boundless, with countless secrets waiting for you to discover...'
|
epilogue_1: 'The galaxy is vast and boundless, with countless secrets waiting for you to discover...',
|
||||||
|
epilogue_2: 'Your journey continues... New adventures await beyond the stars...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2162,5 +2266,31 @@ export default {
|
|||||||
reputationBonus: 'Reputation Bonus',
|
reputationBonus: 'Reputation Bonus',
|
||||||
reputationBonusDesc: 'Your ally {npcName} speaks well of you to {targetNpc}'
|
reputationBonusDesc: 'Your ally {npcName} speaks well of you to {targetNpc}'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
webdav: {
|
||||||
|
// Connection
|
||||||
|
connectionSuccess: 'WebDAV connection successful',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV connection successful, save directory created',
|
||||||
|
authFailed: 'Authentication failed, please check username and password',
|
||||||
|
directoryNotExist: 'Directory does not exist and cannot be created',
|
||||||
|
connectionFailedHttp: 'Connection failed: HTTP {status}',
|
||||||
|
networkError: 'Network error, possibly CORS restriction. Try using a CORS-enabled WebDAV service or proxy.',
|
||||||
|
connectionError: 'Connection error: {error}',
|
||||||
|
// Upload
|
||||||
|
uploadSuccess: 'Upload successful',
|
||||||
|
noWritePermission: 'No write permission',
|
||||||
|
insufficientStorage: 'Insufficient storage space',
|
||||||
|
uploadFailedHttp: 'Upload failed: HTTP {status}',
|
||||||
|
uploadError: 'Upload error: {error}',
|
||||||
|
// Download
|
||||||
|
fileNotExist: 'File does not exist',
|
||||||
|
downloadFailedHttp: 'Download failed: HTTP {status}',
|
||||||
|
downloadError: 'Download error: {error}',
|
||||||
|
// List
|
||||||
|
listFailedHttp: 'Failed to get file list: HTTP {status}',
|
||||||
|
listError: 'Error getting file list: {error}',
|
||||||
|
// Delete
|
||||||
|
deleteFailedHttp: 'Delete failed: HTTP {status}',
|
||||||
|
deleteError: 'Delete error: {error}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,9 +152,14 @@ export default {
|
|||||||
planetDestroyerFactory: 'Fábrica de Destructores de Planetas',
|
planetDestroyerFactory: 'Fábrica de Destructores de Planetas',
|
||||||
geoResearchStation: 'Estación de Investigación Geológica',
|
geoResearchStation: 'Estación de Investigación Geológica',
|
||||||
deepDrillingFacility: 'Instalación de Perforación Profunda',
|
deepDrillingFacility: 'Instalación de Perforación Profunda',
|
||||||
|
university: 'Universidad',
|
||||||
buildTime: 'Tiempo de Construcción',
|
buildTime: 'Tiempo de Construcción',
|
||||||
production: 'Producción',
|
production: 'Producción',
|
||||||
consumption: 'Consumo',
|
consumption: 'Consumo',
|
||||||
|
levelRange: 'Rango de Nivel',
|
||||||
|
totalCost: 'Costo Total',
|
||||||
|
totalPoints: 'Puntos Totales',
|
||||||
|
researchSpeedBonus: 'Bono de Velocidad de Investigación',
|
||||||
|
|
||||||
storageCapacity: 'Capacidad',
|
storageCapacity: 'Capacidad',
|
||||||
energyProduction: 'Producción de Energía',
|
energyProduction: 'Producción de Energía',
|
||||||
@@ -196,9 +201,10 @@ export default {
|
|||||||
jumpGate: 'Transfiere flotas instantáneamente a otras lunas',
|
jumpGate: 'Transfiere flotas instantáneamente a otras lunas',
|
||||||
planetDestroyerFactory: 'Construye armas definitivas capaces de destruir planetas',
|
planetDestroyerFactory: 'Construye armas definitivas capaces de destruir planetas',
|
||||||
geoResearchStation:
|
geoResearchStation:
|
||||||
'Investiga estructuras geológicas y aumenta la tasa de regeneración de depósitos de mineral. +50% de regeneración por nivel',
|
'Investiga estructuras geológicas y aumenta la tasa de regeneración de depósitos de mineral. +10% de regeneración por nivel',
|
||||||
deepDrillingFacility:
|
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'
|
'Perfora profundamente en la corteza para acceder a vetas de mineral más profundas. +20% de capacidad de depósito de mineral por nivel',
|
||||||
|
university: 'Entrena investigadores para acelerar la velocidad de investigación. -8% de tiempo de investigación por nivel'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: 'Caza Ligero',
|
lightFighter: 'Caza Ligero',
|
||||||
@@ -274,7 +280,11 @@ export default {
|
|||||||
colonySlots: 'Espacios de Colonia',
|
colonySlots: 'Espacios de Colonia',
|
||||||
forAllPlanets: '(Global)',
|
forAllPlanets: '(Global)',
|
||||||
speedBonus: 'Bono de Velocidad',
|
speedBonus: 'Bono de Velocidad',
|
||||||
researchSpeedBonus: 'Bono de Velocidad de Investigación'
|
researchSpeedBonus: 'Bono de Velocidad de Investigación',
|
||||||
|
// Visualización de bono de producción de recursos
|
||||||
|
mineralResearch: 'Investigación Mineral',
|
||||||
|
crystalResearch: 'Investigación de Cristal',
|
||||||
|
fuelResearch: 'Investigación de Combustible'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: 'Tecnología de Energía',
|
energyTechnology: 'Tecnología de Energía',
|
||||||
@@ -295,7 +305,11 @@ export default {
|
|||||||
darkMatterTechnology: 'Tecnología de Materia Oscura',
|
darkMatterTechnology: 'Tecnología de Materia Oscura',
|
||||||
terraformingTechnology: 'Tecnología de Terraformación',
|
terraformingTechnology: 'Tecnología de Terraformación',
|
||||||
planetDestructionTech: 'Tecnología de Destrucción Planetaria',
|
planetDestructionTech: 'Tecnología de Destrucción Planetaria',
|
||||||
miningTechnology: 'Tecnología de Minería'
|
miningTechnology: 'Tecnología de Minería',
|
||||||
|
intergalacticResearchNetwork: 'Red de Investigación Intergaláctica',
|
||||||
|
mineralResearch: 'Investigación Mineral',
|
||||||
|
crystalResearch: 'Investigación de Cristal',
|
||||||
|
fuelResearch: 'Investigación de Combustible'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: 'Mejora la eficiencia energética',
|
energyTechnology: 'Mejora la eficiencia energética',
|
||||||
@@ -319,7 +333,11 @@ export default {
|
|||||||
'Investigación de tecnología de terraformación planetaria, añade 30 espacios disponibles a todos los planetas por nivel',
|
'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',
|
planetDestructionTech: 'Tecnología aterradora para destruir planetas enteros',
|
||||||
miningTechnology:
|
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'
|
'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',
|
||||||
|
intergalacticResearchNetwork: 'Conecta múltiples laboratorios de investigación entre planetas. Cada nivel conecta 1 laboratorio adicional',
|
||||||
|
mineralResearch: 'Investiga técnicas más eficientes de extracción de metal. +2% de producción de metal por nivel',
|
||||||
|
crystalResearch: 'Investiga técnicas más eficientes de refinamiento de cristal. +2% de producción de cristal por nivel',
|
||||||
|
fuelResearch: 'Investiga técnicas más eficientes de síntesis de deuterio. +2% de producción de deuterio por nivel'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: 'Comandante',
|
commander: 'Comandante',
|
||||||
@@ -411,10 +429,20 @@ export default {
|
|||||||
'Por favor verifica si tienes suficientes recursos, si se cumplen los prerrequisitos, o si hay otras tareas de investigación.'
|
'Por favor verifica si tienes suficientes recursos, si se cumplen los prerrequisitos, o si hay otras tareas de investigación.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Ataque',
|
||||||
|
shield: 'Escudo',
|
||||||
|
armor: 'Blindaje',
|
||||||
missileAttack: 'Ataque de Misil',
|
missileAttack: 'Ataque de Misil',
|
||||||
speed: 'Velocidad',
|
speed: 'Velocidad',
|
||||||
cargoCapacity: 'Capacidad de Carga',
|
cargoCapacity: 'Capacidad de Carga',
|
||||||
fuelConsumption: 'Consumo de Combustible'
|
fuelConsumption: 'Consumo de Combustible',
|
||||||
|
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'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Astillero',
|
title: 'Astillero',
|
||||||
@@ -524,6 +552,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: 'Reciclar',
|
recycle: 'Reciclar',
|
||||||
destroy: 'Destrucción Planetaria',
|
destroy: 'Destrucción Planetaria',
|
||||||
|
harvestDarkMatter: 'Recolectar Materia Oscura',
|
||||||
|
station: 'Estacionar',
|
||||||
transportResources: 'Transportar Recursos',
|
transportResources: 'Transportar Recursos',
|
||||||
totalCargoCapacity: 'Capacidad de Carga Total',
|
totalCargoCapacity: 'Capacidad de Carga Total',
|
||||||
used: 'Usado',
|
used: 'Usado',
|
||||||
@@ -909,6 +939,9 @@ export default {
|
|||||||
round: 'Ronda {round}',
|
round: 'Ronda {round}',
|
||||||
attackerRemainingPower: 'Poder restante del atacante',
|
attackerRemainingPower: 'Poder restante del atacante',
|
||||||
defenderRemainingPower: 'Poder restante del defensor',
|
defenderRemainingPower: 'Poder restante del defensor',
|
||||||
|
importFromSpyReport: 'Importar desde informe de espionaje',
|
||||||
|
selectSpyReport: 'Seleccionar informe de espionaje',
|
||||||
|
noSpyReports: 'No hay informes de espionaje disponibles',
|
||||||
// Battle animation
|
// Battle animation
|
||||||
playAnimation: 'Reproducir Animación',
|
playAnimation: 'Reproducir Animación',
|
||||||
showDetails: 'Mostrar Detalles',
|
showDetails: 'Mostrar Detalles',
|
||||||
@@ -936,6 +969,7 @@ export default {
|
|||||||
exporting: 'Exportando...',
|
exporting: 'Exportando...',
|
||||||
exportSuccess: 'Exportación exitosa',
|
exportSuccess: 'Exportación exitosa',
|
||||||
exportSuccessWithPath: 'Exportación exitosa, archivo guardado en: {path}',
|
exportSuccessWithPath: 'Exportación exitosa, archivo guardado en: {path}',
|
||||||
|
storagePermissionDenied: 'Permiso de almacenamiento denegado, no se puede exportar el archivo',
|
||||||
exportFailed: 'Exportación fallida, por favor intenta de nuevo',
|
exportFailed: 'Exportación fallida, por favor intenta de nuevo',
|
||||||
importData: 'Importar Datos',
|
importData: 'Importar Datos',
|
||||||
importDataDesc: 'Restaurar progreso del juego desde archivo JSON',
|
importDataDesc: 'Restaurar progreso del juego desde archivo JSON',
|
||||||
@@ -953,6 +987,8 @@ export default {
|
|||||||
gameSettingsDesc: 'Ajustar parámetros y preferencias del juego',
|
gameSettingsDesc: 'Ajustar parámetros y preferencias del juego',
|
||||||
gamePause: 'Pausa del Juego',
|
gamePause: 'Pausa del Juego',
|
||||||
gamePauseDesc: 'Pausar o reanudar el tiempo del juego y la producción de recursos',
|
gamePauseDesc: 'Pausar o reanudar el tiempo del juego y la producción de recursos',
|
||||||
|
battleMode: 'Modo de combate hasta el final',
|
||||||
|
battleModeDesc: 'Cuando está activado, las batallas duran hasta 100 rondas hasta que se decide un ganador. Cuando está desactivado, se usa el modo clásico de 6 rondas',
|
||||||
pause: 'Pausar',
|
pause: 'Pausar',
|
||||||
resume: 'Reanudar',
|
resume: 'Reanudar',
|
||||||
gamePaused: 'Juego pausado',
|
gamePaused: 'Juego pausado',
|
||||||
@@ -1771,6 +1807,8 @@ export default {
|
|||||||
branchUnlocked: '¡Nueva rama de historia desbloqueada!'
|
branchUnlocked: '¡Nueva rama de historia desbloqueada!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Diálogo de historia',
|
||||||
|
description: 'Contenido del diálogo de la campaña',
|
||||||
skip: 'Saltar',
|
skip: 'Saltar',
|
||||||
continue: 'Continuar',
|
continue: 'Continuar',
|
||||||
finish: 'Finalizar',
|
finish: 'Finalizar',
|
||||||
@@ -1936,13 +1974,19 @@ export default {
|
|||||||
questNotActive: 'Misión no activa',
|
questNotActive: 'Misión no activa',
|
||||||
questNotCompleted: 'Misión no completada',
|
questNotCompleted: 'Misión no completada',
|
||||||
rewardsAlreadyClaimed: 'Recompensas ya reclamadas',
|
rewardsAlreadyClaimed: 'Recompensas ya reclamadas',
|
||||||
prerequisiteNotMet: 'Misión prerequisito no completada'
|
prerequisiteNotMet: 'Misión prerequisito no completada',
|
||||||
|
questLocked: 'Misión bloqueada',
|
||||||
|
notInitialized: 'Campaña no inicializada',
|
||||||
|
questAlreadyCompleted: 'Misión ya completada'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: 'Voz Antigua',
|
ancientVoice: 'Voz Antigua',
|
||||||
neighborNPC: 'Facción Vecina',
|
neighborNPC: 'Facción Vecina',
|
||||||
mysteriousSignal: 'Señal Misteriosa',
|
mysteriousSignal: 'Señal Misteriosa',
|
||||||
enemyCommander: 'Comandante Enemigo'
|
enemyCommander: 'Comandante Enemigo',
|
||||||
|
shadowVoice: 'Voz de la Sombra',
|
||||||
|
allyNPC: 'Facción Aliada',
|
||||||
|
ancientGuardian: 'Guardián Antiguo'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: 'Construir Mina de Metal al nivel 2',
|
buildMetalMine: 'Construir Mina de Metal al nivel 2',
|
||||||
@@ -1958,25 +2002,42 @@ export default {
|
|||||||
spyAnyNPC: 'Espiar cualquier planeta NPC',
|
spyAnyNPC: 'Espiar cualquier planeta NPC',
|
||||||
sendGiftToNPC: 'Enviar un regalo a cualquier NPC',
|
sendGiftToNPC: 'Enviar un regalo a cualquier NPC',
|
||||||
researchAstrophysics: 'Investigar Astrofísica al nivel 1',
|
researchAstrophysics: 'Investigar Astrofísica al nivel 1',
|
||||||
|
researchAstrophysicsHigher: 'Investigar Astrofísica al nivel 3',
|
||||||
buildColonyShip: 'Construir una Nave Colonial',
|
buildColonyShip: 'Construir una Nave Colonial',
|
||||||
colonizeNewPlanet: 'Colonizar un nuevo planeta',
|
colonizeNewPlanet: 'Colonizar un nuevo planeta',
|
||||||
completeExpedition: 'Completar una misión de expedición',
|
colonizeMultiple: 'Colonizar 5 planetas',
|
||||||
|
completeExpedition: 'Completar 3 misiones de expedición',
|
||||||
|
expeditionDeepSpace: 'Completar 2 expediciones al espacio profundo',
|
||||||
|
expeditionUncharted: 'Explorar 1 región desconocida',
|
||||||
|
expeditionDangerous: 'Completar 3 expediciones a nebulosas peligrosas',
|
||||||
discoverRuins: 'Descubrir ruinas antiguas',
|
discoverRuins: 'Descubrir ruinas antiguas',
|
||||||
researchComputer: 'Investigar Tecnología de Computación al nivel 4',
|
researchComputer: 'Investigar Tecnología de Computación al nivel 4',
|
||||||
|
researchImpulse: 'Investigar Motor de Impulso al nivel 3',
|
||||||
|
researchLaser: 'Investigar Tecnología Láser al nivel 5',
|
||||||
|
researchIntergalactic: 'Investigar Tecnología de Computación al nivel 10',
|
||||||
|
researchGraviton: 'Investigar Tecnología Gravitón al nivel 1',
|
||||||
improveRelation: 'Mejorar relaciones con un NPC',
|
improveRelation: 'Mejorar relaciones con un NPC',
|
||||||
reachFriendly: 'Alcanzar estatus amigable con un NPC',
|
reachFriendly: 'Alcanzar estatus amigable con un NPC',
|
||||||
spyHostileNPC: 'Espiar un NPC hostil',
|
reachFriendlyRelation: 'Alcanzar estatus amigable con cualquier NPC',
|
||||||
|
sendMultipleGifts: 'Enviar 3 regalos a NPCs',
|
||||||
|
spyHostileNPC: 'Espiar 2 NPCs hostiles',
|
||||||
formAlliance: 'Formar alianza con un NPC amigable',
|
formAlliance: 'Formar alianza con un NPC amigable',
|
||||||
buildDefenses: 'Construir instalaciones de defensa',
|
buildDefenses: 'Construir instalaciones de defensa',
|
||||||
|
buildMissileSilo: 'Construir Silo de Misiles al nivel 2',
|
||||||
|
buildCruisers: 'Construir 10 Cruceros',
|
||||||
winDefenseBattle: 'Ganar una batalla defensiva',
|
winDefenseBattle: 'Ganar una batalla defensiva',
|
||||||
|
defendAgainstAttack: 'Defender exitosamente contra 1 ataque',
|
||||||
spyEnemyPlanet: 'Espiar planeta enemigo',
|
spyEnemyPlanet: 'Espiar planeta enemigo',
|
||||||
|
spyEnemyPlanets: 'Espiar 5 planetas enemigos',
|
||||||
|
winAttackBattles: 'Ganar 3 batallas de ataque',
|
||||||
attackEnemy: 'Atacar al enemigo',
|
attackEnemy: 'Atacar al enemigo',
|
||||||
recycleDebris: 'Reciclar escombros',
|
recycleDebris: 'Reciclar escombros 5 veces',
|
||||||
buildBattleships: 'Construir 10 Naves de Batalla',
|
buildBattleships: 'Construir 20 Naves de Batalla',
|
||||||
exploreDeepRuins: 'Explorar ruinas profundas',
|
exploreDeepRuins: 'Explorar ruinas profundas',
|
||||||
researchHyperspace: 'Investigar Tecnología de Hiperespacio',
|
researchHyperspace: 'Investigar Motor de Hiperespacio al nivel 3',
|
||||||
defeatBoss: 'Derrotar al enemigo misterioso',
|
defeatBoss: 'Derrotar al Guardián Antiguo',
|
||||||
colonizeSpecial: 'Colonizar ubicación especial',
|
colonizeSpecial: 'Colonizar ubicación especial',
|
||||||
|
accumulateWealth: 'Acumular 1 millón de recursos totales',
|
||||||
continueDevelopment: 'Continuar desarrollo'
|
continueDevelopment: 'Continuar desarrollo'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2015,13 +2076,18 @@ export default {
|
|||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
'Tu expedición descubrió señales anómalas. Estas señales parecen venir de una civilización antigua... Investiga su origen.',
|
'Tu expedición descubrió señales anómalas. Estas señales parecen venir de una civilización antigua... Investiga su origen.',
|
||||||
|
prologue_2: 'Estas señales... llevan ecos de una civilización perdida hace tiempo. Sus secretos esperan ser descubiertos...',
|
||||||
epilogue_1: 'Estos símbolos... ¡Son ruinas de una civilización antigua! Continúa investigando para descubrir sus secretos.'
|
epilogue_1: 'Estos símbolos... ¡Son ruinas de una civilización antigua! Continúa investigando para descubrir sus secretos.'
|
||||||
},
|
},
|
||||||
'2_4': {
|
'2_4': {
|
||||||
prologue_1: 'Has encontrado la ubicación de ruinas antiguas. Envía tu flota a explorar y ver qué puedes descubrir.'
|
prologue_1: 'Has encontrado la ubicación de ruinas antiguas. Envía tu flota a explorar y ver qué puedes descubrir.',
|
||||||
|
prologue_2: 'Las ruinas guardan muchos secretos... Elige tu camino sabiamente...',
|
||||||
|
choice_1: 'Explorar con cautela - priorizar seguridad',
|
||||||
|
choice_2: 'Explorar agresivamente - priorizar descubrimiento'
|
||||||
},
|
},
|
||||||
'2_5': {
|
'2_5': {
|
||||||
prologue_1: 'Se encontraron archivos de datos en las ruinas. Estudia estos datos, quizás puedas desbloquear nueva tecnología.'
|
prologue_1: 'Se encontraron archivos de datos en las ruinas. Estudia estos datos, quizás puedas desbloquear nueva tecnología.',
|
||||||
|
epilogue_1: '¡Los datos antiguos han sido descifrados! Se han obtenido nuevos conocimientos tecnológicos.'
|
||||||
},
|
},
|
||||||
'3_1': {
|
'3_1': {
|
||||||
prologue_1: 'Mientras exploras, no olvides la diplomacia. Mantener buenas relaciones con las facciones circundantes te beneficia.'
|
prologue_1: 'Mientras exploras, no olvides la diplomacia. Mantener buenas relaciones con las facciones circundantes te beneficia.'
|
||||||
@@ -2031,16 +2097,19 @@ export default {
|
|||||||
},
|
},
|
||||||
'3_3': {
|
'3_3': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
'La inteligencia indica que fuerzas hostiles te están vigilando desde las sombras. Mantente alerta y explora sus movimientos.'
|
'La inteligencia indica que fuerzas hostiles te están vigilando desde las sombras. Mantente alerta y explora sus movimientos.',
|
||||||
|
prologue_2: 'Fuerzas oscuras se agitan en el vacío... Han notado tu poder creciente...'
|
||||||
},
|
},
|
||||||
'3_4': {
|
'3_4': {
|
||||||
prologue_1: 'Establece una alianza formal con facciones amigables para apoyarse mutuamente contra las amenazas.'
|
prologue_1: 'Establece una alianza formal con facciones amigables para apoyarse mutuamente contra las amenazas.'
|
||||||
},
|
},
|
||||||
'3_5': {
|
'3_5': {
|
||||||
prologue_1: 'Las amenazas se acercan. Construye instalaciones de defensa y prepárate para posibles conflictos.'
|
prologue_1: 'Las amenazas se acercan. Construye instalaciones de defensa y prepárate para posibles conflictos.',
|
||||||
|
epilogue_1: 'Tus defensas están listas. La tormenta se acerca, pero estás preparado.'
|
||||||
},
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: '¡El enemigo ha lanzado un ataque! ¡Defiende tu planeta!',
|
prologue_1: '¡El enemigo ha lanzado un ataque! ¡Defiende tu planeta!',
|
||||||
|
prologue_2: 'La flota de las sombras se acerca... Ha llegado tu momento de prueba...',
|
||||||
epilogue_1: 'Repeliste exitosamente la primera ola del enemigo. Pero esto es solo el comienzo...'
|
epilogue_1: 'Repeliste exitosamente la primera ola del enemigo. Pero esto es solo el comienzo...'
|
||||||
},
|
},
|
||||||
'4_2': {
|
'4_2': {
|
||||||
@@ -2053,7 +2122,8 @@ export default {
|
|||||||
prologue_1: 'Muchos escombros permanecen en el campo de batalla. Recicla estos recursos para prepararte para la próxima batalla.'
|
prologue_1: 'Muchos escombros permanecen en el campo de batalla. Recicla estos recursos para prepararte para la próxima batalla.'
|
||||||
},
|
},
|
||||||
'4_5': {
|
'4_5': {
|
||||||
prologue_1: 'La batalla final se acerca. Construye una flota poderosa y prepárate para el desafío definitivo.'
|
prologue_1: 'La batalla final se acerca. Construye una flota poderosa y prepárate para el desafío definitivo.',
|
||||||
|
epilogue_1: 'Tu flota está reunida. El momento decisivo se acerca...'
|
||||||
},
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
@@ -2061,10 +2131,12 @@ export default {
|
|||||||
prologue_2: 'Finalmente has llegado... La verdad pronto será revelada...'
|
prologue_2: 'Finalmente has llegado... La verdad pronto será revelada...'
|
||||||
},
|
},
|
||||||
'5_2': {
|
'5_2': {
|
||||||
prologue_1: 'En las profundidades de las ruinas, descubriste tecnología antigua perdida. Investiga y desbloquea su poder.'
|
prologue_1: 'En las profundidades de las ruinas, descubriste tecnología antigua perdida. Investiga y desbloquea su poder.',
|
||||||
|
prologue_2: 'Esta tecnología... es más antigua que todas las civilizaciones conocidas. Manéjala con cuidado...'
|
||||||
},
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: 'Ha aparecido un enemigo misterioso. Este es el desafío final. ¡Derrótalo!',
|
prologue_1: 'Ha aparecido un enemigo misterioso. Este es el desafío final. ¡Derrótalo!',
|
||||||
|
prologue_2: '¡Soy el guardián de estos secretos. Demuestra tu valor o serás destruido!',
|
||||||
epilogue_1: '¡Lo lograste! El guardián antiguo ha sido derrotado. Los secretos de la galaxia ahora están abiertos para ti.'
|
epilogue_1: '¡Lo lograste! El guardián antiguo ha sido derrotado. Los secretos de la galaxia ahora están abiertos para ti.'
|
||||||
},
|
},
|
||||||
'5_4': {
|
'5_4': {
|
||||||
@@ -2072,7 +2144,8 @@ export default {
|
|||||||
},
|
},
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: 'Tu leyenda acaba de comenzar. ¡Continúa explorando y conquistando más sistemas estelares!',
|
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...'
|
epilogue_1: 'La galaxia es vasta e infinita, con innumerables secretos esperando que los descubras...',
|
||||||
|
epilogue_2: 'Tu viaje continúa... Nuevas aventuras esperan más allá de las estrellas...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2184,5 +2257,26 @@ export default {
|
|||||||
reputationBonus: 'Bonificación de Reputación',
|
reputationBonus: 'Bonificación de Reputación',
|
||||||
reputationBonusDesc: 'Tu aliado {npcName} habla bien de ti ante {targetNpc}'
|
reputationBonusDesc: 'Tu aliado {npcName} habla bien de ti ante {targetNpc}'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'Conexión WebDAV exitosa',
|
||||||
|
connectionSuccessDirectoryCreated: 'Conexión WebDAV exitosa, directorio de guardado creado',
|
||||||
|
authFailed: 'Autenticación fallida, verifica el nombre de usuario y contraseña',
|
||||||
|
directoryNotExist: 'El directorio no existe y no se puede crear',
|
||||||
|
networkError: 'Error de red, verifica la dirección del servidor y la red',
|
||||||
|
unknownError: 'Error desconocido',
|
||||||
|
uploadSuccess: 'Guardado subido exitosamente',
|
||||||
|
uploadFailed: 'Error al subir',
|
||||||
|
downloadSuccess: 'Guardado descargado exitosamente',
|
||||||
|
downloadFailed: 'Error al descargar',
|
||||||
|
noSaveFiles: 'No hay archivos de guardado en el servidor',
|
||||||
|
fileListSuccess: 'Lista de guardados obtenida exitosamente',
|
||||||
|
fileListFailed: 'Error al obtener la lista de guardados',
|
||||||
|
deleteSuccess: 'Guardado eliminado exitosamente',
|
||||||
|
deleteFailed: 'Error al eliminar',
|
||||||
|
serverError: 'Error del servidor',
|
||||||
|
notConfigured: 'WebDAV no configurado',
|
||||||
|
invalidUrl: 'URL de WebDAV inválida',
|
||||||
|
timeout: 'Tiempo de conexión agotado'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ export default {
|
|||||||
planetDestroyerFactory: '惑星破壊工場',
|
planetDestroyerFactory: '惑星破壊工場',
|
||||||
geoResearchStation: '地質研究所',
|
geoResearchStation: '地質研究所',
|
||||||
deepDrillingFacility: '深部掘削施設',
|
deepDrillingFacility: '深部掘削施設',
|
||||||
|
university: '大学',
|
||||||
buildTime: '建設時間',
|
buildTime: '建設時間',
|
||||||
build: '建設',
|
build: '建設',
|
||||||
production: '生産量',
|
production: '生産量',
|
||||||
@@ -215,8 +216,9 @@ export default {
|
|||||||
sensorPhalanx: '周辺星系の艦隊活動を探知',
|
sensorPhalanx: '周辺星系の艦隊活動を探知',
|
||||||
jumpGate: '他の月へ艦隊を瞬間移動',
|
jumpGate: '他の月へ艦隊を瞬間移動',
|
||||||
planetDestroyerFactory: '惑星を破壊できる究極兵器を建造',
|
planetDestroyerFactory: '惑星を破壊できる究極兵器を建造',
|
||||||
geoResearchStation: '地質構造を研究し、鉱脈の自然回復速度を向上。レベル毎に回復速度50%増加',
|
geoResearchStation: '地質構造を研究し、鉱脈の自然回復速度を向上。レベル毎に回復速度10%増加',
|
||||||
deepDrillingFacility: ''
|
deepDrillingFacility: '',
|
||||||
|
university: '研究者を育成し、研究速度を加速。レベル毎に研究時間-8%'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: '軽戦闘機',
|
lightFighter: '軽戦闘機',
|
||||||
@@ -299,7 +301,11 @@ export default {
|
|||||||
colonySlots: '植民地スロット',
|
colonySlots: '植民地スロット',
|
||||||
forAllPlanets: '(全惑星)',
|
forAllPlanets: '(全惑星)',
|
||||||
speedBonus: '速度ボーナス',
|
speedBonus: '速度ボーナス',
|
||||||
researchSpeedBonus: '研究速度ボーナス'
|
researchSpeedBonus: '研究速度ボーナス',
|
||||||
|
// 資源生産ボーナス表示
|
||||||
|
mineralResearch: '鉱物研究',
|
||||||
|
crystalResearch: 'クリスタル研究',
|
||||||
|
fuelResearch: '燃料研究'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: 'エネルギー技術',
|
energyTechnology: 'エネルギー技術',
|
||||||
@@ -320,7 +326,11 @@ export default {
|
|||||||
darkMatterTechnology: 'ダークマター技術',
|
darkMatterTechnology: 'ダークマター技術',
|
||||||
terraformingTechnology: 'テラフォーミング技術',
|
terraformingTechnology: 'テラフォーミング技術',
|
||||||
planetDestructionTech: '惑星破壊技術',
|
planetDestructionTech: '惑星破壊技術',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: '銀河間研究ネットワーク',
|
||||||
|
mineralResearch: '鉱物研究',
|
||||||
|
crystalResearch: 'クリスタル研究',
|
||||||
|
fuelResearch: '燃料研究'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: 'エネルギー利用効率を向上',
|
energyTechnology: 'エネルギー利用効率を向上',
|
||||||
@@ -342,7 +352,11 @@ export default {
|
|||||||
darkMatterTechnology: 'ダークマターの性質と応用を研究',
|
darkMatterTechnology: 'ダークマターの性質と応用を研究',
|
||||||
terraformingTechnology: '惑星地形改造技術を研究、レベル毎に全惑星の利用可能スペース30増加',
|
terraformingTechnology: '惑星地形改造技術を研究、レベル毎に全惑星の利用可能スペース30増加',
|
||||||
planetDestructionTech: '惑星全体を破壊する恐怖の技術を研究',
|
planetDestructionTech: '惑星全体を破壊する恐怖の技術を研究',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: '複数の惑星の研究所を連結。レベル毎に追加1研究所を接続',
|
||||||
|
mineralResearch: 'より効率的な金属抽出技術を研究。レベル毎に金属生産+2%',
|
||||||
|
crystalResearch: 'より効率的なクリスタル精製技術を研究。レベル毎にクリスタル生産+2%',
|
||||||
|
fuelResearch: 'より効率的な重水素合成技術を研究。レベル毎に重水素生産+2%'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: '司令官',
|
commander: '司令官',
|
||||||
@@ -641,7 +655,9 @@ export default {
|
|||||||
jumpGateSuccessMessage: '艦隊は{target}へ瞬時に転送されました',
|
jumpGateSuccessMessage: '艦隊は{target}へ瞬時に転送されました',
|
||||||
jumpGateFailed: 'ジャンプゲート転送失敗',
|
jumpGateFailed: 'ジャンプゲート転送失敗',
|
||||||
jumpGateFailedMessage: 'ジャンプゲートの状態と艦隊構成を確認してください',
|
jumpGateFailedMessage: 'ジャンプゲートの状態と艦隊構成を確認してください',
|
||||||
destroy: '破壊'
|
destroy: '破壊',
|
||||||
|
harvestDarkMatter: 'ダークマター採取',
|
||||||
|
station: '駐留'
|
||||||
},
|
},
|
||||||
officersView: {
|
officersView: {
|
||||||
title: '士官',
|
title: '士官',
|
||||||
@@ -953,7 +969,10 @@ export default {
|
|||||||
hideRoundDetails: 'ラウンド詳細非表示',
|
hideRoundDetails: 'ラウンド詳細非表示',
|
||||||
round: '第{round}ラウンド',
|
round: '第{round}ラウンド',
|
||||||
attackerRemainingPower: '攻撃側残存火力',
|
attackerRemainingPower: '攻撃側残存火力',
|
||||||
defenderRemainingPower: '防御側残存火力'
|
defenderRemainingPower: '防御側残存火力',
|
||||||
|
importFromSpyReport: 'スパイレポートからインポート',
|
||||||
|
selectSpyReport: 'スパイレポートを選択',
|
||||||
|
noSpyReports: 'スパイレポートがありません'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
dataManagement: 'データ管理',
|
dataManagement: 'データ管理',
|
||||||
@@ -964,6 +983,7 @@ export default {
|
|||||||
exporting: 'エクスポート中...',
|
exporting: 'エクスポート中...',
|
||||||
exportSuccess: 'エクスポート成功',
|
exportSuccess: 'エクスポート成功',
|
||||||
exportSuccessWithPath: 'エクスポート成功、ファイルの保存先:{path}',
|
exportSuccessWithPath: 'エクスポート成功、ファイルの保存先:{path}',
|
||||||
|
storagePermissionDenied: 'ストレージ権限が拒否されました。ファイルをエクスポートできません',
|
||||||
exportFailed: 'エクスポートに失敗しました。もう一度お試しください',
|
exportFailed: 'エクスポートに失敗しました。もう一度お試しください',
|
||||||
importData: 'データインポート',
|
importData: 'データインポート',
|
||||||
importDataDesc: 'JSONファイルからゲームの進行状況を復元',
|
importDataDesc: 'JSONファイルからゲームの進行状況を復元',
|
||||||
@@ -981,6 +1001,8 @@ export default {
|
|||||||
gameSettingsDesc: 'ゲームパラメータと設定を調整',
|
gameSettingsDesc: 'ゲームパラメータと設定を調整',
|
||||||
gamePause: 'ゲーム一時停止',
|
gamePause: 'ゲーム一時停止',
|
||||||
gamePauseDesc: 'ゲーム時間と資源生産を一時停止または再開',
|
gamePauseDesc: 'ゲーム時間と資源生産を一時停止または再開',
|
||||||
|
battleMode: '最後まで戦うモード',
|
||||||
|
battleModeDesc: '有効にすると、勝者が決まるまで最大100ラウンドの戦闘が行われます。無効の場合は、クラシックな6ラウンドモードが使用されます',
|
||||||
pause: '一時停止',
|
pause: '一時停止',
|
||||||
resume: '再開',
|
resume: '再開',
|
||||||
gamePaused: 'ゲームを一時停止しました',
|
gamePaused: 'ゲームを一時停止しました',
|
||||||
@@ -1854,6 +1876,27 @@ export default {
|
|||||||
reputationBonusDesc: '同盟の{npcName}が{targetNpc}にあなたのことを良く言っています'
|
reputationBonusDesc: '同盟の{npcName}が{targetNpc}にあなたのことを良く言っています'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV接続成功',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV接続成功、保存ディレクトリを作成しました',
|
||||||
|
authFailed: '認証失敗、ユーザー名とパスワードを確認してください',
|
||||||
|
directoryNotExist: 'ディレクトリが存在せず、作成できませんでした',
|
||||||
|
networkError: 'ネットワークエラー、サーバーアドレスとネットワークを確認してください',
|
||||||
|
unknownError: '不明なエラー',
|
||||||
|
uploadSuccess: 'セーブデータのアップロード成功',
|
||||||
|
uploadFailed: 'アップロード失敗',
|
||||||
|
downloadSuccess: 'セーブデータのダウンロード成功',
|
||||||
|
downloadFailed: 'ダウンロード失敗',
|
||||||
|
noSaveFiles: 'サーバーにセーブデータがありません',
|
||||||
|
fileListSuccess: 'セーブデータリストの取得成功',
|
||||||
|
fileListFailed: 'セーブデータリストの取得失敗',
|
||||||
|
deleteSuccess: 'セーブデータの削除成功',
|
||||||
|
deleteFailed: '削除失敗',
|
||||||
|
serverError: 'サーバーエラー',
|
||||||
|
notConfigured: 'WebDAVが設定されていません',
|
||||||
|
invalidUrl: '無効なWebDAV URL',
|
||||||
|
timeout: '接続タイムアウト'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: 'キャンペーン',
|
name: 'キャンペーン',
|
||||||
description: '神秘的な銀河を探索し、古代の秘密を解き明かす',
|
description: '神秘的な銀河を探索し、古代の秘密を解き明かす',
|
||||||
@@ -1881,6 +1924,8 @@ export default {
|
|||||||
branchUnlocked: '新しいストーリー分岐が解放されました!'
|
branchUnlocked: '新しいストーリー分岐が解放されました!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'ストーリー対話',
|
||||||
|
description: 'キャンペーンストーリーの対話内容',
|
||||||
skip: 'スキップ',
|
skip: 'スキップ',
|
||||||
continue: '続ける',
|
continue: '続ける',
|
||||||
finish: '完了',
|
finish: '完了',
|
||||||
@@ -1971,13 +2016,19 @@ export default {
|
|||||||
questNotActive: 'クエストはアクティブではありません',
|
questNotActive: 'クエストはアクティブではありません',
|
||||||
questNotCompleted: 'クエスト未完了',
|
questNotCompleted: 'クエスト未完了',
|
||||||
rewardsAlreadyClaimed: '報酬は既に受け取り済み',
|
rewardsAlreadyClaimed: '報酬は既に受け取り済み',
|
||||||
prerequisiteNotMet: '前提クエスト未完了'
|
prerequisiteNotMet: '前提クエスト未完了',
|
||||||
|
questLocked: 'クエストはロックされています',
|
||||||
|
notInitialized: 'キャンペーンが初期化されていません',
|
||||||
|
questAlreadyCompleted: 'クエストは既に完了しています'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: '古代の声',
|
ancientVoice: '古代の声',
|
||||||
neighborNPC: '隣接勢力',
|
neighborNPC: '隣接勢力',
|
||||||
mysteriousSignal: '謎の信号',
|
mysteriousSignal: '謎の信号',
|
||||||
enemyCommander: '敵司令官'
|
enemyCommander: '敵司令官',
|
||||||
|
shadowVoice: '影の声',
|
||||||
|
allyNPC: '同盟勢力',
|
||||||
|
ancientGuardian: '古代の守護者'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: '金属鉱山をレベル2に建設',
|
buildMetalMine: '金属鉱山をレベル2に建設',
|
||||||
@@ -1993,25 +2044,42 @@ export default {
|
|||||||
spyAnyNPC: 'NPC惑星をスパイ',
|
spyAnyNPC: 'NPC惑星をスパイ',
|
||||||
sendGiftToNPC: 'NPCに贈り物を送る',
|
sendGiftToNPC: 'NPCに贈り物を送る',
|
||||||
researchAstrophysics: '宇宙物理学をレベル1まで研究',
|
researchAstrophysics: '宇宙物理学をレベル1まで研究',
|
||||||
|
researchAstrophysicsHigher: '宇宙物理学をレベル3まで研究',
|
||||||
buildColonyShip: 'コロニーシップを建造',
|
buildColonyShip: 'コロニーシップを建造',
|
||||||
colonizeNewPlanet: '新惑星を植民地化',
|
colonizeNewPlanet: '新惑星を植民地化',
|
||||||
completeExpedition: '遠征ミッションを完了',
|
colonizeMultiple: '5つの惑星を植民地化',
|
||||||
|
completeExpedition: '3回の遠征ミッションを完了',
|
||||||
|
expeditionDeepSpace: '2回の深宇宙遠征を完了',
|
||||||
|
expeditionUncharted: '1回の未知領域探索',
|
||||||
|
expeditionDangerous: '3回の危険な星雲遠征を完了',
|
||||||
discoverRuins: '古代遺跡を発見',
|
discoverRuins: '古代遺跡を発見',
|
||||||
researchComputer: 'コンピュータ技術をレベル4まで研究',
|
researchComputer: 'コンピュータ技術をレベル4まで研究',
|
||||||
|
researchImpulse: 'インパルスドライブをレベル3まで研究',
|
||||||
|
researchLaser: 'レーザー技術をレベル5まで研究',
|
||||||
|
researchIntergalactic: 'コンピュータ技術をレベル10まで研究',
|
||||||
|
researchGraviton: 'グラビトン技術をレベル1まで研究',
|
||||||
improveRelation: 'NPCとの関係を改善',
|
improveRelation: 'NPCとの関係を改善',
|
||||||
reachFriendly: 'NPCと友好ステータスに到達',
|
reachFriendly: 'NPCと友好ステータスに到達',
|
||||||
spyHostileNPC: '敵対NPCをスパイ',
|
reachFriendlyRelation: '任意のNPCと友好ステータスに到達',
|
||||||
|
sendMultipleGifts: 'NPCに3回贈り物を送る',
|
||||||
|
spyHostileNPC: '2つの敵対NPCをスパイ',
|
||||||
formAlliance: '友好NPCと同盟を結ぶ',
|
formAlliance: '友好NPCと同盟を結ぶ',
|
||||||
buildDefenses: '防衛施設を建設',
|
buildDefenses: '防衛施設を建設',
|
||||||
|
buildMissileSilo: 'ミサイルサイロをレベル2に建設',
|
||||||
|
buildCruisers: 'クルーザー10隻を建造',
|
||||||
winDefenseBattle: '防衛戦に勝利',
|
winDefenseBattle: '防衛戦に勝利',
|
||||||
|
defendAgainstAttack: '1回の攻撃を防御成功',
|
||||||
spyEnemyPlanet: '敵惑星をスパイ',
|
spyEnemyPlanet: '敵惑星をスパイ',
|
||||||
|
spyEnemyPlanets: '5つの敵惑星をスパイ',
|
||||||
|
winAttackBattles: '3回の攻撃戦に勝利',
|
||||||
attackEnemy: '敵を攻撃',
|
attackEnemy: '敵を攻撃',
|
||||||
recycleDebris: 'デブリをリサイクル',
|
recycleDebris: '5回デブリをリサイクル',
|
||||||
buildBattleships: 'バトルシップ10隻を建造',
|
buildBattleships: 'バトルシップ20隻を建造',
|
||||||
exploreDeepRuins: '深部遺跡を探索',
|
exploreDeepRuins: '深部遺跡を探索',
|
||||||
researchHyperspace: 'ハイパースペース技術を研究',
|
researchHyperspace: 'ハイパースペースドライブをレベル3まで研究',
|
||||||
defeatBoss: '謎の敵を撃破',
|
defeatBoss: '古代の守護者を撃破',
|
||||||
colonizeSpecial: '特別な場所を植民地化',
|
colonizeSpecial: '特別な場所を植民地化',
|
||||||
|
accumulateWealth: '総資源100万を蓄積',
|
||||||
continueDevelopment: '発展を継続'
|
continueDevelopment: '発展を継続'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2040,36 +2108,60 @@ export default {
|
|||||||
},
|
},
|
||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1: '遠征で異常な信号を発見しました。これらの信号は古代文明からのようです...発信源を調査してください。',
|
prologue_1: '遠征で異常な信号を発見しました。これらの信号は古代文明からのようです...発信源を調査してください。',
|
||||||
|
prologue_2: 'これらの信号...失われた文明の残響を運んでいる。その秘密が発見を待っている...',
|
||||||
epilogue_1: 'これらの記号は...古代文明の遺跡だ!さらに調査して秘密を解き明かそう。'
|
epilogue_1: 'これらの記号は...古代文明の遺跡だ!さらに調査して秘密を解き明かそう。'
|
||||||
},
|
},
|
||||||
'2_4': { prologue_1: '古代遺跡の場所を発見しました。艦隊を送り、何が見つかるか探索してください。' },
|
'2_4': {
|
||||||
'2_5': { prologue_1: '遺跡でデータアーカイブが見つかりました。このデータを研究すれば、新しい技術が解放できるかもしれません。' },
|
prologue_1: '古代遺跡の場所を発見しました。艦隊を送り、何が見つかるか探索してください。',
|
||||||
|
prologue_2: '遺跡には多くの秘密がある...賢く道を選べ...',
|
||||||
|
choice_1: '慎重に探索する - 安全を優先',
|
||||||
|
choice_2: '積極的に探索する - 発見を優先'
|
||||||
|
},
|
||||||
|
'2_5': {
|
||||||
|
prologue_1: '遺跡でデータアーカイブが見つかりました。このデータを研究すれば、新しい技術が解放できるかもしれません。',
|
||||||
|
epilogue_1: '古代のデータが解読されました!新しい技術的洞察を得ました。'
|
||||||
|
},
|
||||||
'3_1': { prologue_1: '探索中も外交を忘れないでください。周囲の勢力と良好な関係を維持することは有益です。' },
|
'3_1': { prologue_1: '探索中も外交を忘れないでください。周囲の勢力と良好な関係を維持することは有益です。' },
|
||||||
'3_2': { prologue_1: 'いくつかの勢力が友好を示しています。関係を深め続ければ、より多くのサポートを得られるかもしれません。' },
|
'3_2': { prologue_1: 'いくつかの勢力が友好を示しています。関係を深め続ければ、より多くのサポートを得られるかもしれません。' },
|
||||||
'3_3': { prologue_1: '情報によると敵対勢力が影からあなたを監視しています。警戒を怠らず、彼らの動きを偵察してください。' },
|
'3_3': {
|
||||||
|
prologue_1: '情報によると敵対勢力が影からあなたを監視しています。警戒を怠らず、彼らの動きを偵察してください。',
|
||||||
|
prologue_2: '闇の力が虚空で蠢いている...彼らはあなたの成長する力に気づいた...'
|
||||||
|
},
|
||||||
'3_4': { prologue_1: '友好勢力と正式な同盟を結び、脅威に対してお互いをサポートしましょう。' },
|
'3_4': { prologue_1: '友好勢力と正式な同盟を結び、脅威に対してお互いをサポートしましょう。' },
|
||||||
'3_5': { prologue_1: '脅威が迫っています。防衛施設を建設し、可能な紛争に備えてください。' },
|
'3_5': {
|
||||||
|
prologue_1: '脅威が迫っています。防衛施設を建設し、可能な紛争に備えてください。',
|
||||||
|
epilogue_1: '防衛準備が整いました。嵐が来るが、あなたは準備ができている。'
|
||||||
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: '敵が攻撃を開始しました!惑星を守ってください!',
|
prologue_1: '敵が攻撃を開始しました!惑星を守ってください!',
|
||||||
|
prologue_2: '影の艦隊が迫っている...あなたの試練の時が来た...',
|
||||||
epilogue_1: '敵の第一波を撃退しました。しかしこれは始まりに過ぎません...'
|
epilogue_1: '敵の第一波を撃退しました。しかしこれは始まりに過ぎません...'
|
||||||
},
|
},
|
||||||
'4_2': { prologue_1: '敵は撤退しましたが、戻ってくるでしょう。彼らの惑星を偵察して戦力を把握してください。' },
|
'4_2': { prologue_1: '敵は撤退しましたが、戻ってくるでしょう。彼らの惑星を偵察して戦力を把握してください。' },
|
||||||
'4_3': { prologue_1: '反撃の時です。敵の惑星を攻撃し、彼らの戦力を弱めてください。' },
|
'4_3': { prologue_1: '反撃の時です。敵の惑星を攻撃し、彼らの戦力を弱めてください。' },
|
||||||
'4_4': { prologue_1: '戦場に多くのデブリが残っています。これらの資源をリサイクルして次の戦闘に備えてください。' },
|
'4_4': { prologue_1: '戦場に多くのデブリが残っています。これらの資源をリサイクルして次の戦闘に備えてください。' },
|
||||||
'4_5': { prologue_1: '最終決戦が近づいています。強力な艦隊を建造し、究極の挑戦に備えてください。' },
|
'4_5': {
|
||||||
|
prologue_1: '最終決戦が近づいています。強力な艦隊を建造し、究極の挑戦に備えてください。',
|
||||||
|
epilogue_1: '艦隊が集結しました。決戦の時が近づいている...'
|
||||||
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1: 'すべての手がかりは遺跡の最深部を指しています。古代文明の核心的な秘密がそこにあります。',
|
prologue_1: 'すべての手がかりは遺跡の最深部を指しています。古代文明の核心的な秘密がそこにあります。',
|
||||||
prologue_2: 'ついに到着した...真実がまもなく明かされる...'
|
prologue_2: 'ついに到着した...真実がまもなく明かされる...'
|
||||||
},
|
},
|
||||||
'5_2': { prologue_1: '遺跡の深部で失われた古代技術を発見しました。研究してその力を解放してください。' },
|
'5_2': {
|
||||||
|
prologue_1: '遺跡の深部で失われた古代技術を発見しました。研究してその力を解放してください。',
|
||||||
|
prologue_2: 'この技術は...既知のすべての文明より古い。慎重に扱え...'
|
||||||
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: '謎の敵が現れました。これが最後の挑戦です。撃破してください!',
|
prologue_1: '謎の敵が現れました。これが最後の挑戦です。撃破してください!',
|
||||||
|
prologue_2: '私はこれらの秘密の守護者だ。お前の価値を証明せよ、さもなくば滅びよ!',
|
||||||
epilogue_1: 'やりました!古代の守護者は倒されました。銀河の秘密は今やあなたに開かれています。'
|
epilogue_1: 'やりました!古代の守護者は倒されました。銀河の秘密は今やあなたに開かれています。'
|
||||||
},
|
},
|
||||||
'5_4': { prologue_1: 'ついに平和が訪れました。この新時代に新しい植民地を築き、帝国を拡大してください。' },
|
'5_4': { prologue_1: 'ついに平和が訪れました。この新時代に新しい植民地を築き、帝国を拡大してください。' },
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: 'あなたの伝説は始まったばかりです。さらに探索を続け、より多くの星系を征服してください!',
|
prologue_1: 'あなたの伝説は始まったばかりです。さらに探索を続け、より多くの星系を征服してください!',
|
||||||
epilogue_1: '銀河は広大で果てしなく、数え切れない秘密があなたを待っています...'
|
epilogue_1: '銀河は広大で果てしなく、数え切れない秘密があなたを待っています...',
|
||||||
|
epilogue_2: 'あなたの旅は続く...新たな冒険が星の彼方で待っている...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ export default {
|
|||||||
planetDestroyerFactory: '행성 파괴 공장',
|
planetDestroyerFactory: '행성 파괴 공장',
|
||||||
geoResearchStation: '지질 연구소',
|
geoResearchStation: '지질 연구소',
|
||||||
deepDrillingFacility: '심층 시추 시설',
|
deepDrillingFacility: '심층 시추 시설',
|
||||||
|
university: '대학',
|
||||||
buildTime: '건설 시간',
|
buildTime: '건설 시간',
|
||||||
build: '건설',
|
build: '건설',
|
||||||
production: '생산량',
|
production: '생산량',
|
||||||
@@ -199,8 +200,9 @@ export default {
|
|||||||
sensorPhalanx: '주변 행성계의 함대 활동 감지',
|
sensorPhalanx: '주변 행성계의 함대 활동 감지',
|
||||||
jumpGate: '다른 위성으로 함대 순간 이동',
|
jumpGate: '다른 위성으로 함대 순간 이동',
|
||||||
planetDestroyerFactory: '행성을 파괴할 수 있는 궁극 병기 건조',
|
planetDestroyerFactory: '행성을 파괴할 수 있는 궁극 병기 건조',
|
||||||
geoResearchStation: '지질 구조를 연구하여 광맥 자연 회복 속도를 높입니다. 레벨당 회복 속도 50% 증가',
|
geoResearchStation: '지질 구조를 연구하여 광맥 자연 회복 속도를 높입니다. 레벨당 회복 속도 10% 증가',
|
||||||
deepDrillingFacility: ''
|
deepDrillingFacility: '',
|
||||||
|
university: '연구원을 양성하여 연구 속도를 가속합니다. 레벨당 연구 시간 -8%'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: '경전투기',
|
lightFighter: '경전투기',
|
||||||
@@ -276,7 +278,11 @@ export default {
|
|||||||
colonySlots: '식민지 슬롯',
|
colonySlots: '식민지 슬롯',
|
||||||
forAllPlanets: '(전역)',
|
forAllPlanets: '(전역)',
|
||||||
speedBonus: '속도 보너스',
|
speedBonus: '속도 보너스',
|
||||||
researchSpeedBonus: '연구 속도 보너스'
|
researchSpeedBonus: '연구 속도 보너스',
|
||||||
|
// 자원 생산 보너스 표시
|
||||||
|
mineralResearch: '광물 연구',
|
||||||
|
crystalResearch: '크리스탈 연구',
|
||||||
|
fuelResearch: '연료 연구'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: '에너지 기술',
|
energyTechnology: '에너지 기술',
|
||||||
@@ -297,7 +303,11 @@ export default {
|
|||||||
darkMatterTechnology: '암흑 물질 기술',
|
darkMatterTechnology: '암흑 물질 기술',
|
||||||
terraformingTechnology: '지형 변환 기술',
|
terraformingTechnology: '지형 변환 기술',
|
||||||
planetDestructionTech: '행성 파괴 기술',
|
planetDestructionTech: '행성 파괴 기술',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: '은하간 연구 네트워크',
|
||||||
|
mineralResearch: '광물 연구',
|
||||||
|
crystalResearch: '크리스탈 연구',
|
||||||
|
fuelResearch: '연료 연구'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: '에너지 이용 효율 향상',
|
energyTechnology: '에너지 이용 효율 향상',
|
||||||
@@ -319,7 +329,11 @@ export default {
|
|||||||
darkMatterTechnology: '암흑 물질의 성질과 응용 연구',
|
darkMatterTechnology: '암흑 물질의 성질과 응용 연구',
|
||||||
terraformingTechnology: '행성 지형 개조 기술 연구, 레벨당 모든 행성의 가용 공간 30 증가',
|
terraformingTechnology: '행성 지형 개조 기술 연구, 레벨당 모든 행성의 가용 공간 30 증가',
|
||||||
planetDestructionTech: '행성 전체를 파괴하는 공포의 기술 연구',
|
planetDestructionTech: '행성 전체를 파괴하는 공포의 기술 연구',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: '여러 행성의 연구소를 연결합니다. 레벨당 1개 추가 연구소 연결',
|
||||||
|
mineralResearch: '더 효율적인 금속 추출 기술 연구. 레벨당 금속 생산 +2%',
|
||||||
|
crystalResearch: '더 효율적인 크리스탈 정제 기술 연구. 레벨당 크리스탈 생산 +2%',
|
||||||
|
fuelResearch: '더 효율적인 중수소 합성 기술 연구. 레벨당 중수소 생산 +2%'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: '사령관',
|
commander: '사령관',
|
||||||
@@ -388,6 +402,7 @@ export default {
|
|||||||
title: '건물',
|
title: '건물',
|
||||||
spaceUsage: '공간 사용',
|
spaceUsage: '공간 사용',
|
||||||
upgradeCost: '업그레이드 비용',
|
upgradeCost: '업그레이드 비용',
|
||||||
|
build: '건설',
|
||||||
upgrade: '업그레이드',
|
upgrade: '업그레이드',
|
||||||
maxLevelReached: '최대 레벨 도달',
|
maxLevelReached: '최대 레벨 도달',
|
||||||
requirementsNotMet: '요구 사항 미충족',
|
requirementsNotMet: '요구 사항 미충족',
|
||||||
@@ -408,9 +423,19 @@ export default {
|
|||||||
researchFailedMessage: '자원이 충분한지, 전제 조건이 충족되었는지, 또는 다른 연구 작업이 있는지 확인하세요.'
|
researchFailedMessage: '자원이 충분한지, 전제 조건이 충족되었는지, 또는 다른 연구 작업이 있는지 확인하세요.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: '공격력',
|
||||||
|
shield: '실드',
|
||||||
|
armor: '장갑',
|
||||||
speed: '속도',
|
speed: '속도',
|
||||||
cargoCapacity: '화물 용량',
|
cargoCapacity: '화물 용량',
|
||||||
fuelConsumption: '연료 소비'
|
fuelConsumption: '연료 소비',
|
||||||
|
buildCost: '건조 비용',
|
||||||
|
buildTime: '건조 시간',
|
||||||
|
perUnit: '단위당',
|
||||||
|
batchCalculator: '일괄 계산기',
|
||||||
|
quantity: '수량',
|
||||||
|
totalCost: '총 비용',
|
||||||
|
totalTime: '총 시간'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: '조선소',
|
title: '조선소',
|
||||||
@@ -521,6 +546,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: '회수',
|
recycle: '회수',
|
||||||
destroy: '행성 파괴',
|
destroy: '행성 파괴',
|
||||||
|
harvestDarkMatter: '암흑 물질 수확',
|
||||||
|
station: '주둔',
|
||||||
transportResources: '자원 수송',
|
transportResources: '자원 수송',
|
||||||
totalCargoCapacity: '총 적재량',
|
totalCargoCapacity: '총 적재량',
|
||||||
used: '사용됨',
|
used: '사용됨',
|
||||||
@@ -897,7 +924,10 @@ export default {
|
|||||||
hideRoundDetails: '라운드 상세 숨기기',
|
hideRoundDetails: '라운드 상세 숨기기',
|
||||||
round: '제 {round} 라운드',
|
round: '제 {round} 라운드',
|
||||||
attackerRemainingPower: '공격자 잔여 화력',
|
attackerRemainingPower: '공격자 잔여 화력',
|
||||||
defenderRemainingPower: '방어자 잔여 화력'
|
defenderRemainingPower: '방어자 잔여 화력',
|
||||||
|
importFromSpyReport: '정찰 보고서에서 가져오기',
|
||||||
|
selectSpyReport: '정찰 보고서 선택',
|
||||||
|
noSpyReports: '정찰 보고서가 없습니다'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
dataManagement: '데이터 관리',
|
dataManagement: '데이터 관리',
|
||||||
@@ -908,6 +938,7 @@ export default {
|
|||||||
exporting: '내보내는 중...',
|
exporting: '내보내는 중...',
|
||||||
exportSuccess: '내보내기 성공',
|
exportSuccess: '내보내기 성공',
|
||||||
exportSuccessWithPath: '내보내기 성공, 파일 저장 위치: {path}',
|
exportSuccessWithPath: '내보내기 성공, 파일 저장 위치: {path}',
|
||||||
|
storagePermissionDenied: '저장소 권한이 거부되어 파일을 내보낼 수 없습니다',
|
||||||
exportFailed: '내보내기 실패, 다시 시도해주세요',
|
exportFailed: '내보내기 실패, 다시 시도해주세요',
|
||||||
importData: '데이터 가져오기',
|
importData: '데이터 가져오기',
|
||||||
importDataDesc: 'JSON 파일에서 게임 진행 상황 복원',
|
importDataDesc: 'JSON 파일에서 게임 진행 상황 복원',
|
||||||
@@ -925,6 +956,8 @@ export default {
|
|||||||
gameSettingsDesc: '게임 매개변수 및 설정 조정',
|
gameSettingsDesc: '게임 매개변수 및 설정 조정',
|
||||||
gamePause: '게임 일시정지',
|
gamePause: '게임 일시정지',
|
||||||
gamePauseDesc: '게임 시간 및 자원 생산 일시정지 또는 재개',
|
gamePauseDesc: '게임 시간 및 자원 생산 일시정지 또는 재개',
|
||||||
|
battleMode: '끝까지 싸우기 모드',
|
||||||
|
battleModeDesc: '활성화하면 승자가 결정될 때까지 최대 100라운드까지 전투가 진행됩니다. 비활성화하면 클래식 6라운드 모드가 사용됩니다',
|
||||||
pause: '일시정지',
|
pause: '일시정지',
|
||||||
resume: '재개',
|
resume: '재개',
|
||||||
gamePaused: '게임이 일시정지되었습니다',
|
gamePaused: '게임이 일시정지되었습니다',
|
||||||
@@ -1784,6 +1817,27 @@ export default {
|
|||||||
reputationBonusDesc: '동맹 {npcName}이(가) {targetNpc}에게 당신을 좋게 말하고 있습니다'
|
reputationBonusDesc: '동맹 {npcName}이(가) {targetNpc}에게 당신을 좋게 말하고 있습니다'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV 연결 성공',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV 연결 성공, 저장 디렉토리 생성됨',
|
||||||
|
authFailed: '인증 실패, 사용자 이름과 비밀번호를 확인하세요',
|
||||||
|
directoryNotExist: '디렉토리가 존재하지 않으며 생성할 수 없습니다',
|
||||||
|
networkError: '네트워크 오류, 서버 주소와 네트워크를 확인하세요',
|
||||||
|
unknownError: '알 수 없는 오류',
|
||||||
|
uploadSuccess: '저장 파일 업로드 성공',
|
||||||
|
uploadFailed: '업로드 실패',
|
||||||
|
downloadSuccess: '저장 파일 다운로드 성공',
|
||||||
|
downloadFailed: '다운로드 실패',
|
||||||
|
noSaveFiles: '서버에 저장 파일이 없습니다',
|
||||||
|
fileListSuccess: '저장 파일 목록 가져오기 성공',
|
||||||
|
fileListFailed: '저장 파일 목록 가져오기 실패',
|
||||||
|
deleteSuccess: '저장 파일 삭제 성공',
|
||||||
|
deleteFailed: '삭제 실패',
|
||||||
|
serverError: '서버 오류',
|
||||||
|
notConfigured: 'WebDAV가 구성되지 않았습니다',
|
||||||
|
invalidUrl: '잘못된 WebDAV URL',
|
||||||
|
timeout: '연결 시간 초과'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: '캠페인',
|
name: '캠페인',
|
||||||
description: '신비로운 은하를 탐험하고 고대의 비밀을 밝혀내세요',
|
description: '신비로운 은하를 탐험하고 고대의 비밀을 밝혀내세요',
|
||||||
@@ -1810,6 +1864,8 @@ export default {
|
|||||||
branchUnlocked: '새로운 스토리 분기가 해금되었습니다!'
|
branchUnlocked: '새로운 스토리 분기가 해금되었습니다!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: '스토리 대화',
|
||||||
|
description: '캠페인 스토리 대화 내용',
|
||||||
skip: '건너뛰기',
|
skip: '건너뛰기',
|
||||||
continue: '계속',
|
continue: '계속',
|
||||||
finish: '완료',
|
finish: '완료',
|
||||||
@@ -1900,13 +1956,19 @@ export default {
|
|||||||
questNotActive: '퀘스트가 활성화되지 않았습니다',
|
questNotActive: '퀘스트가 활성화되지 않았습니다',
|
||||||
questNotCompleted: '퀘스트 미완료',
|
questNotCompleted: '퀘스트 미완료',
|
||||||
rewardsAlreadyClaimed: '이미 보상을 받았습니다',
|
rewardsAlreadyClaimed: '이미 보상을 받았습니다',
|
||||||
prerequisiteNotMet: '선행 퀘스트 미완료'
|
prerequisiteNotMet: '선행 퀘스트 미완료',
|
||||||
|
questLocked: '퀘스트가 잠겨 있습니다',
|
||||||
|
notInitialized: '캠페인이 초기화되지 않았습니다',
|
||||||
|
questAlreadyCompleted: '퀘스트가 이미 완료되었습니다'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: '고대의 목소리',
|
ancientVoice: '고대의 목소리',
|
||||||
neighborNPC: '인접 세력',
|
neighborNPC: '인접 세력',
|
||||||
mysteriousSignal: '신비한 신호',
|
mysteriousSignal: '신비한 신호',
|
||||||
enemyCommander: '적 사령관'
|
enemyCommander: '적 사령관',
|
||||||
|
shadowVoice: '그림자의 목소리',
|
||||||
|
allyNPC: '동맹 세력',
|
||||||
|
ancientGuardian: '고대의 수호자'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: '금속 광산을 레벨 2로 건설',
|
buildMetalMine: '금속 광산을 레벨 2로 건설',
|
||||||
@@ -1922,25 +1984,42 @@ export default {
|
|||||||
spyAnyNPC: 'NPC 행성 정찰',
|
spyAnyNPC: 'NPC 행성 정찰',
|
||||||
sendGiftToNPC: 'NPC에게 선물 보내기',
|
sendGiftToNPC: 'NPC에게 선물 보내기',
|
||||||
researchAstrophysics: '천체물리학을 레벨 1까지 연구',
|
researchAstrophysics: '천체물리학을 레벨 1까지 연구',
|
||||||
|
researchAstrophysicsHigher: '천체물리학을 레벨 3까지 연구',
|
||||||
buildColonyShip: '식민선 건조',
|
buildColonyShip: '식민선 건조',
|
||||||
colonizeNewPlanet: '새 행성 식민지화',
|
colonizeNewPlanet: '새 행성 식민지화',
|
||||||
completeExpedition: '탐험 임무 완료',
|
colonizeMultiple: '5개 행성 식민지화',
|
||||||
|
completeExpedition: '3회 탐험 임무 완료',
|
||||||
|
expeditionDeepSpace: '2회 심우주 탐험 완료',
|
||||||
|
expeditionUncharted: '1회 미지 영역 탐험',
|
||||||
|
expeditionDangerous: '3회 위험 성운 탐험 완료',
|
||||||
discoverRuins: '고대 유적 발견',
|
discoverRuins: '고대 유적 발견',
|
||||||
researchComputer: '컴퓨터 기술을 레벨 4까지 연구',
|
researchComputer: '컴퓨터 기술을 레벨 4까지 연구',
|
||||||
|
researchImpulse: '임펄스 드라이브를 레벨 3까지 연구',
|
||||||
|
researchLaser: '레이저 기술을 레벨 5까지 연구',
|
||||||
|
researchIntergalactic: '컴퓨터 기술을 레벨 10까지 연구',
|
||||||
|
researchGraviton: '그래비톤 기술을 레벨 1까지 연구',
|
||||||
improveRelation: 'NPC와의 관계 개선',
|
improveRelation: 'NPC와의 관계 개선',
|
||||||
reachFriendly: 'NPC와 우호 상태 도달',
|
reachFriendly: 'NPC와 우호 상태 도달',
|
||||||
spyHostileNPC: '적대 NPC 정찰',
|
reachFriendlyRelation: '임의의 NPC와 우호 상태 도달',
|
||||||
|
sendMultipleGifts: 'NPC에게 3회 선물 보내기',
|
||||||
|
spyHostileNPC: '적대 NPC 2곳 정찰',
|
||||||
formAlliance: '우호 NPC와 동맹 체결',
|
formAlliance: '우호 NPC와 동맹 체결',
|
||||||
buildDefenses: '방어 시설 건설',
|
buildDefenses: '방어 시설 건설',
|
||||||
|
buildMissileSilo: '미사일 사일로를 레벨 2로 건설',
|
||||||
|
buildCruisers: '순양함 10대 건조',
|
||||||
winDefenseBattle: '방어전 승리',
|
winDefenseBattle: '방어전 승리',
|
||||||
|
defendAgainstAttack: '1회 공격 방어 성공',
|
||||||
spyEnemyPlanet: '적 행성 정찰',
|
spyEnemyPlanet: '적 행성 정찰',
|
||||||
|
spyEnemyPlanets: '적 행성 5곳 정찰',
|
||||||
|
winAttackBattles: '3회 공격전 승리',
|
||||||
attackEnemy: '적 공격',
|
attackEnemy: '적 공격',
|
||||||
recycleDebris: '잔해 재활용',
|
recycleDebris: '5회 잔해 재활용',
|
||||||
buildBattleships: '전함 10대 건조',
|
buildBattleships: '전함 20대 건조',
|
||||||
exploreDeepRuins: '깊은 유적 탐험',
|
exploreDeepRuins: '깊은 유적 탐험',
|
||||||
researchHyperspace: '하이퍼스페이스 기술 연구',
|
researchHyperspace: '하이퍼스페이스 드라이브를 레벨 3까지 연구',
|
||||||
defeatBoss: '신비한 적 격파',
|
defeatBoss: '고대 수호자 격파',
|
||||||
colonizeSpecial: '특별한 장소 식민지화',
|
colonizeSpecial: '특별한 장소 식민지화',
|
||||||
|
accumulateWealth: '총 자원 100만 축적',
|
||||||
continueDevelopment: '개발 계속'
|
continueDevelopment: '개발 계속'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -1970,38 +2049,60 @@ export default {
|
|||||||
},
|
},
|
||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1: '탐험에서 이상한 신호를 발견했습니다. 이 신호들은 고대 문명에서 온 것 같습니다... 출처를 조사하세요.',
|
prologue_1: '탐험에서 이상한 신호를 발견했습니다. 이 신호들은 고대 문명에서 온 것 같습니다... 출처를 조사하세요.',
|
||||||
|
prologue_2: '이 신호들... 잃어버린 문명의 메아리를 담고 있다. 그들의 비밀이 발견을 기다리고 있다...',
|
||||||
epilogue_1: '이 기호들은... 고대 문명의 유적이다! 계속 조사하여 비밀을 밝히세요.'
|
epilogue_1: '이 기호들은... 고대 문명의 유적이다! 계속 조사하여 비밀을 밝히세요.'
|
||||||
},
|
},
|
||||||
'2_4': { prologue_1: '고대 유적의 위치를 발견했습니다. 함대를 보내 무엇을 발견할 수 있는지 탐험하세요.' },
|
'2_4': {
|
||||||
'2_5': { prologue_1: '유적에서 데이터 아카이브가 발견되었습니다. 이 데이터를 연구하면 새 기술을 해금할 수 있을지도 모릅니다.' },
|
prologue_1: '고대 유적의 위치를 발견했습니다. 함대를 보내 무엇을 발견할 수 있는지 탐험하세요.',
|
||||||
|
prologue_2: '유적에는 많은 비밀이 있다... 현명하게 길을 선택하라...',
|
||||||
|
choice_1: '신중하게 탐험 - 안전 우선',
|
||||||
|
choice_2: '적극적으로 탐험 - 발견 우선'
|
||||||
|
},
|
||||||
|
'2_5': {
|
||||||
|
prologue_1: '유적에서 데이터 아카이브가 발견되었습니다. 이 데이터를 연구하면 새 기술을 해금할 수 있을지도 모릅니다.',
|
||||||
|
epilogue_1: '고대 데이터가 해독되었습니다! 새로운 기술적 통찰을 얻었습니다.'
|
||||||
|
},
|
||||||
'3_1': { prologue_1: '탐험 중에도 외교를 잊지 마세요. 주변 세력과 좋은 관계를 유지하는 것이 유익합니다.' },
|
'3_1': { prologue_1: '탐험 중에도 외교를 잊지 마세요. 주변 세력과 좋은 관계를 유지하는 것이 유익합니다.' },
|
||||||
'3_2': { prologue_1: '일부 세력이 우호를 보이고 있습니다. 관계를 계속 깊게 하면 더 많은 지원을 받을 수 있을지도 모릅니다.' },
|
'3_2': { prologue_1: '일부 세력이 우호를 보이고 있습니다. 관계를 계속 깊게 하면 더 많은 지원을 받을 수 있을지도 모릅니다.' },
|
||||||
'3_3': {
|
'3_3': {
|
||||||
prologue_1: '정보에 따르면 적대 세력이 그림자에서 당신을 감시하고 있습니다. 경계를 늦추지 말고 그들의 움직임을 정찰하세요.'
|
prologue_1: '정보에 따르면 적대 세력이 그림자에서 당신을 감시하고 있습니다. 경계를 늦추지 말고 그들의 움직임을 정찰하세요.',
|
||||||
|
prologue_2: '어둠의 힘이 허공에서 움직이고 있다... 그들은 당신의 성장하는 힘을 알아챘다...'
|
||||||
},
|
},
|
||||||
'3_4': { prologue_1: '우호 세력과 공식 동맹을 맺어 위협에 대해 서로를 지원하세요.' },
|
'3_4': { prologue_1: '우호 세력과 공식 동맹을 맺어 위협에 대해 서로를 지원하세요.' },
|
||||||
'3_5': { prologue_1: '위협이 다가오고 있습니다. 방어 시설을 건설하고 가능한 충돌에 대비하세요.' },
|
'3_5': {
|
||||||
|
prologue_1: '위협이 다가오고 있습니다. 방어 시설을 건설하고 가능한 충돌에 대비하세요.',
|
||||||
|
epilogue_1: '방어 준비가 완료되었습니다. 폭풍이 오지만, 당신은 준비가 되어 있습니다.'
|
||||||
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: '적이 공격을 시작했습니다! 행성을 방어하세요!',
|
prologue_1: '적이 공격을 시작했습니다! 행성을 방어하세요!',
|
||||||
|
prologue_2: '그림자 함대가 다가온다... 당신의 시련의 시간이 왔다...',
|
||||||
epilogue_1: '적의 첫 번째 파도를 성공적으로 격퇴했습니다. 하지만 이것은 시작에 불과합니다...'
|
epilogue_1: '적의 첫 번째 파도를 성공적으로 격퇴했습니다. 하지만 이것은 시작에 불과합니다...'
|
||||||
},
|
},
|
||||||
'4_2': { prologue_1: '적이 후퇴했지만 돌아올 것입니다. 그들의 행성을 정찰하여 전력을 파악하세요.' },
|
'4_2': { prologue_1: '적이 후퇴했지만 돌아올 것입니다. 그들의 행성을 정찰하여 전력을 파악하세요.' },
|
||||||
'4_3': { prologue_1: '반격할 시간입니다. 적 행성을 공격하여 그들의 전력을 약화시키세요.' },
|
'4_3': { prologue_1: '반격할 시간입니다. 적 행성을 공격하여 그들의 전력을 약화시키세요.' },
|
||||||
'4_4': { prologue_1: '전장에 많은 잔해가 남아 있습니다. 이 자원을 재활용하여 다음 전투에 대비하세요.' },
|
'4_4': { prologue_1: '전장에 많은 잔해가 남아 있습니다. 이 자원을 재활용하여 다음 전투에 대비하세요.' },
|
||||||
'4_5': { prologue_1: '최종 전투가 다가오고 있습니다. 강력한 함대를 건조하고 궁극의 도전에 대비하세요.' },
|
'4_5': {
|
||||||
|
prologue_1: '최종 전투가 다가오고 있습니다. 강력한 함대를 건조하고 궁극의 도전에 대비하세요.',
|
||||||
|
epilogue_1: '함대가 집결했습니다. 결전의 시간이 다가온다...'
|
||||||
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1: '모든 단서가 유적의 가장 깊은 곳을 가리킵니다. 고대 문명의 핵심 비밀이 그곳에 있습니다.',
|
prologue_1: '모든 단서가 유적의 가장 깊은 곳을 가리킵니다. 고대 문명의 핵심 비밀이 그곳에 있습니다.',
|
||||||
prologue_2: '드디어 도착했다... 진실이 곧 밝혀질 것이다...'
|
prologue_2: '드디어 도착했다... 진실이 곧 밝혀질 것이다...'
|
||||||
},
|
},
|
||||||
'5_2': { prologue_1: '유적 깊은 곳에서 잃어버린 고대 기술을 발견했습니다. 연구하여 그 힘을 해방하세요.' },
|
'5_2': {
|
||||||
|
prologue_1: '유적 깊은 곳에서 잃어버린 고대 기술을 발견했습니다. 연구하여 그 힘을 해방하세요.',
|
||||||
|
prologue_2: '이 기술은... 알려진 모든 문명보다 오래되었다. 신중하게 다루어라...'
|
||||||
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: '신비한 적이 나타났습니다. 이것이 마지막 도전입니다. 격파하세요!',
|
prologue_1: '신비한 적이 나타났습니다. 이것이 마지막 도전입니다. 격파하세요!',
|
||||||
|
prologue_2: '나는 이 비밀들의 수호자다. 네 가치를 증명하라, 그렇지 않으면 멸망하라!',
|
||||||
epilogue_1: '해냈습니다! 고대의 수호자가 쓰러졌습니다. 은하의 비밀이 이제 당신에게 열려 있습니다.'
|
epilogue_1: '해냈습니다! 고대의 수호자가 쓰러졌습니다. 은하의 비밀이 이제 당신에게 열려 있습니다.'
|
||||||
},
|
},
|
||||||
'5_4': { prologue_1: '마침내 평화가 찾아왔습니다. 이 새 시대에 새 식민지를 세우고 제국을 확장하세요.' },
|
'5_4': { prologue_1: '마침내 평화가 찾아왔습니다. 이 새 시대에 새 식민지를 세우고 제국을 확장하세요.' },
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: '당신의 전설은 이제 막 시작되었습니다. 계속 탐험하고 더 많은 성계를 정복하세요!',
|
prologue_1: '당신의 전설은 이제 막 시작되었습니다. 계속 탐험하고 더 많은 성계를 정복하세요!',
|
||||||
epilogue_1: '은하는 광대하고 끝이 없으며, 셀 수 없는 비밀이 당신을 기다리고 있습니다...'
|
epilogue_1: '은하는 광대하고 끝이 없으며, 셀 수 없는 비밀이 당신을 기다리고 있습니다...',
|
||||||
|
epilogue_2: '당신의 여정은 계속된다... 새로운 모험이 별 너머에서 기다리고 있다...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ export default {
|
|||||||
planetDestroyerFactory: 'Фабрика разрушителей планет',
|
planetDestroyerFactory: 'Фабрика разрушителей планет',
|
||||||
geoResearchStation: 'Геологическая станция',
|
geoResearchStation: 'Геологическая станция',
|
||||||
deepDrillingFacility: 'Глубинная буровая установка',
|
deepDrillingFacility: 'Глубинная буровая установка',
|
||||||
|
university: 'Университет',
|
||||||
buildTime: 'Время строительства',
|
buildTime: 'Время строительства',
|
||||||
build: 'Построить',
|
build: 'Построить',
|
||||||
production: 'Производство',
|
production: 'Производство',
|
||||||
@@ -161,6 +162,7 @@ export default {
|
|||||||
totalCost: 'Общая стоимость',
|
totalCost: 'Общая стоимость',
|
||||||
totalPoints: 'Общие очки',
|
totalPoints: 'Общие очки',
|
||||||
levelRange: 'Диапазон уровней',
|
levelRange: 'Диапазон уровней',
|
||||||
|
researchSpeedBonus: 'Бонус скорости исследований',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -204,8 +206,9 @@ export default {
|
|||||||
jumpGate: 'Мгновенно переносит флоты на другие луны',
|
jumpGate: 'Мгновенно переносит флоты на другие луны',
|
||||||
planetDestroyerFactory: 'Производит абсолютное оружие, способное уничтожать планеты',
|
planetDestroyerFactory: 'Производит абсолютное оружие, способное уничтожать планеты',
|
||||||
geoResearchStation:
|
geoResearchStation:
|
||||||
'Исследует геологическую структуру и повышает скорость восстановления рудных месторождений. +50% скорости восстановления за уровень',
|
'Исследует геологическую структуру и повышает скорость восстановления рудных месторождений. +10% скорости восстановления за уровень',
|
||||||
deepDrillingFacility: ''
|
deepDrillingFacility: '',
|
||||||
|
university: 'Обучает исследователей для ускорения исследований. -8% времени исследования за уровень'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: 'Лёгкий истребитель',
|
lightFighter: 'Лёгкий истребитель',
|
||||||
@@ -269,6 +272,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: 'Время исследования',
|
researchTime: 'Время исследования',
|
||||||
|
levelRange: 'Диапазон уровней',
|
||||||
|
totalCost: 'Общая стоимость',
|
||||||
|
totalPoints: 'Всего очков',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -284,7 +290,11 @@ export default {
|
|||||||
researchQueueBonus: 'Очередь исследований',
|
researchQueueBonus: 'Очередь исследований',
|
||||||
colonySlots: 'Слоты колоний',
|
colonySlots: 'Слоты колоний',
|
||||||
forAllPlanets: '(Глобально)',
|
forAllPlanets: '(Глобально)',
|
||||||
speedBonus: 'Бонус скорости'
|
speedBonus: 'Бонус скорости',
|
||||||
|
// Отображение бонуса производства ресурсов
|
||||||
|
mineralResearch: 'Минеральные исследования',
|
||||||
|
crystalResearch: 'Кристаллические исследования',
|
||||||
|
fuelResearch: 'Топливные исследования'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: 'Энергетическая технология',
|
energyTechnology: 'Энергетическая технология',
|
||||||
@@ -305,7 +315,11 @@ export default {
|
|||||||
darkMatterTechnology: 'Технология тёмной материи',
|
darkMatterTechnology: 'Технология тёмной материи',
|
||||||
terraformingTechnology: 'Технология терраформирования',
|
terraformingTechnology: 'Технология терраформирования',
|
||||||
planetDestructionTech: 'Технология уничтожения планет',
|
planetDestructionTech: 'Технология уничтожения планет',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: 'Межгалактическая исследовательская сеть',
|
||||||
|
mineralResearch: 'Исследование минералов',
|
||||||
|
crystalResearch: 'Исследование кристаллов',
|
||||||
|
fuelResearch: 'Исследование топлива'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: 'Улучшает энергоэффективность',
|
energyTechnology: 'Улучшает энергоэффективность',
|
||||||
@@ -328,7 +342,11 @@ export default {
|
|||||||
terraformingTechnology:
|
terraformingTechnology:
|
||||||
'Исследование технологии терраформирования планет, увеличивает доступное пространство всех планет на 30 за уровень',
|
'Исследование технологии терраформирования планет, увеличивает доступное пространство всех планет на 30 за уровень',
|
||||||
planetDestructionTech: 'Исследование ужасающей технологии уничтожения целых планет',
|
planetDestructionTech: 'Исследование ужасающей технологии уничтожения целых планет',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: 'Связывает несколько исследовательских лабораторий на разных планетах. Каждый уровень подключает 1 дополнительную лабораторию',
|
||||||
|
mineralResearch: 'Исследование более эффективных методов добычи металла. +2% производства металла за уровень',
|
||||||
|
crystalResearch: 'Исследование более эффективных методов обработки кристаллов. +2% производства кристаллов за уровень',
|
||||||
|
fuelResearch: 'Исследование более эффективных методов синтеза дейтерия. +2% производства дейтерия за уровень'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: 'Командир',
|
commander: 'Командир',
|
||||||
@@ -402,6 +420,7 @@ export default {
|
|||||||
spaceUsage: 'Использование полей',
|
spaceUsage: 'Использование полей',
|
||||||
level: 'Уровень',
|
level: 'Уровень',
|
||||||
upgradeCost: 'Стоимость улучшения',
|
upgradeCost: 'Стоимость улучшения',
|
||||||
|
build: 'Построить',
|
||||||
upgrade: 'Улучшить',
|
upgrade: 'Улучшить',
|
||||||
maxLevelReached: 'Достигнут максимальный уровень',
|
maxLevelReached: 'Достигнут максимальный уровень',
|
||||||
requirementsNotMet: 'Требования не выполнены',
|
requirementsNotMet: 'Требования не выполнены',
|
||||||
@@ -423,9 +442,19 @@ export default {
|
|||||||
'Пожалуйста, проверьте, достаточно ли у вас ресурсов, выполнены ли предварительные условия или нет других исследовательских задач.'
|
'Пожалуйста, проверьте, достаточно ли у вас ресурсов, выполнены ли предварительные условия или нет других исследовательских задач.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Атака',
|
||||||
|
shield: 'Щит',
|
||||||
|
armor: 'Броня',
|
||||||
speed: 'Скорость',
|
speed: 'Скорость',
|
||||||
cargoCapacity: 'Грузоподъёмность',
|
cargoCapacity: 'Грузоподъёмность',
|
||||||
fuelConsumption: 'Расход топлива'
|
fuelConsumption: 'Расход топлива',
|
||||||
|
buildCost: 'Стоимость постройки',
|
||||||
|
buildTime: 'Время постройки',
|
||||||
|
perUnit: 'За единицу',
|
||||||
|
batchCalculator: 'Калькулятор партии',
|
||||||
|
quantity: 'Количество',
|
||||||
|
totalCost: 'Общая стоимость',
|
||||||
|
totalTime: 'Общее время'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Верфь',
|
title: 'Верфь',
|
||||||
@@ -607,7 +636,9 @@ export default {
|
|||||||
jumpGateSuccessMessage: 'Флот мгновенно переброшен к {target}',
|
jumpGateSuccessMessage: 'Флот мгновенно переброшен к {target}',
|
||||||
jumpGateFailed: 'Переброска через ворота не удалась',
|
jumpGateFailed: 'Переброска через ворота не удалась',
|
||||||
jumpGateFailedMessage: 'Проверьте состояние ворот и конфигурацию флота',
|
jumpGateFailedMessage: 'Проверьте состояние ворот и конфигурацию флота',
|
||||||
destroy: 'Уничтожение'
|
destroy: 'Уничтожение',
|
||||||
|
harvestDarkMatter: 'Сбор тёмной материи',
|
||||||
|
station: 'Расположение'
|
||||||
},
|
},
|
||||||
officersView: {
|
officersView: {
|
||||||
title: 'Офицеры',
|
title: 'Офицеры',
|
||||||
@@ -916,7 +947,10 @@ export default {
|
|||||||
hideRoundDetails: 'Скрыть детали раундов',
|
hideRoundDetails: 'Скрыть детали раундов',
|
||||||
round: 'Раунд {round}',
|
round: 'Раунд {round}',
|
||||||
attackerRemainingPower: 'Оставшаяся мощь нападающего',
|
attackerRemainingPower: 'Оставшаяся мощь нападающего',
|
||||||
defenderRemainingPower: 'Оставшаяся мощь защитника'
|
defenderRemainingPower: 'Оставшаяся мощь защитника',
|
||||||
|
importFromSpyReport: 'Импорт из шпионского отчета',
|
||||||
|
selectSpyReport: 'Выбрать шпионский отчет',
|
||||||
|
noSpyReports: 'Нет шпионских отчетов'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
dataManagement: 'Управление данными',
|
dataManagement: 'Управление данными',
|
||||||
@@ -927,6 +961,7 @@ export default {
|
|||||||
exporting: 'Экспорт...',
|
exporting: 'Экспорт...',
|
||||||
exportSuccess: 'Экспорт успешен',
|
exportSuccess: 'Экспорт успешен',
|
||||||
exportSuccessWithPath: 'Экспорт успешен, файл сохранен в: {path}',
|
exportSuccessWithPath: 'Экспорт успешен, файл сохранен в: {path}',
|
||||||
|
storagePermissionDenied: 'Разрешение на хранение отклонено, невозможно экспортировать файл',
|
||||||
exportFailed: 'Экспорт не удался, попробуйте еще раз',
|
exportFailed: 'Экспорт не удался, попробуйте еще раз',
|
||||||
importData: 'Импорт данных',
|
importData: 'Импорт данных',
|
||||||
importDataDesc: 'Восстановить прогресс игры из JSON файла',
|
importDataDesc: 'Восстановить прогресс игры из JSON файла',
|
||||||
@@ -944,6 +979,8 @@ export default {
|
|||||||
gameSettingsDesc: 'Настроить параметры и предпочтения игры',
|
gameSettingsDesc: 'Настроить параметры и предпочтения игры',
|
||||||
gamePause: 'Пауза игры',
|
gamePause: 'Пауза игры',
|
||||||
gamePauseDesc: 'Приостановить или возобновить игровое время и производство ресурсов',
|
gamePauseDesc: 'Приостановить или возобновить игровое время и производство ресурсов',
|
||||||
|
battleMode: 'Режим боя до конца',
|
||||||
|
battleModeDesc: 'При включении бои длятся до 100 раундов до определения победителя. При выключении используется классический режим 6 раундов',
|
||||||
pause: 'Пауза',
|
pause: 'Пауза',
|
||||||
resume: 'Возобновить',
|
resume: 'Возобновить',
|
||||||
gamePaused: 'Игра приостановлена',
|
gamePaused: 'Игра приостановлена',
|
||||||
@@ -1836,6 +1873,27 @@ export default {
|
|||||||
reputationBonusDesc: 'Ваш союзник {npcName} хорошо отзывается о вас {targetNpc}'
|
reputationBonusDesc: 'Ваш союзник {npcName} хорошо отзывается о вас {targetNpc}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'Подключение WebDAV успешно',
|
||||||
|
connectionSuccessDirectoryCreated: 'Подключение WebDAV успешно, каталог сохранений создан',
|
||||||
|
authFailed: 'Ошибка аутентификации, проверьте имя пользователя и пароль',
|
||||||
|
directoryNotExist: 'Каталог не существует и не может быть создан',
|
||||||
|
networkError: 'Ошибка сети, проверьте адрес сервера и сеть',
|
||||||
|
unknownError: 'Неизвестная ошибка',
|
||||||
|
uploadSuccess: 'Сохранение успешно загружено',
|
||||||
|
uploadFailed: 'Ошибка загрузки',
|
||||||
|
downloadSuccess: 'Сохранение успешно скачано',
|
||||||
|
downloadFailed: 'Ошибка скачивания',
|
||||||
|
noSaveFiles: 'На сервере нет сохранений',
|
||||||
|
fileListSuccess: 'Список сохранений получен',
|
||||||
|
fileListFailed: 'Ошибка получения списка сохранений',
|
||||||
|
deleteSuccess: 'Сохранение успешно удалено',
|
||||||
|
deleteFailed: 'Ошибка удаления',
|
||||||
|
serverError: 'Ошибка сервера',
|
||||||
|
notConfigured: 'WebDAV не настроен',
|
||||||
|
invalidUrl: 'Недействительный URL WebDAV',
|
||||||
|
timeout: 'Превышено время ожидания'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: 'Кампания',
|
name: 'Кампания',
|
||||||
description: 'Исследуйте загадочную галактику и раскройте древние тайны',
|
description: 'Исследуйте загадочную галактику и раскройте древние тайны',
|
||||||
@@ -1863,6 +1921,8 @@ export default {
|
|||||||
branchUnlocked: 'Новая сюжетная ветка разблокирована!'
|
branchUnlocked: 'Новая сюжетная ветка разблокирована!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Сюжетный диалог',
|
||||||
|
description: 'Содержание сюжетного диалога кампании',
|
||||||
skip: 'Пропустить',
|
skip: 'Пропустить',
|
||||||
continue: 'Продолжить',
|
continue: 'Продолжить',
|
||||||
finish: 'Завершить',
|
finish: 'Завершить',
|
||||||
@@ -1953,13 +2013,19 @@ export default {
|
|||||||
questNotActive: 'Квест не активен',
|
questNotActive: 'Квест не активен',
|
||||||
questNotCompleted: 'Квест не завершён',
|
questNotCompleted: 'Квест не завершён',
|
||||||
rewardsAlreadyClaimed: 'Награды уже получены',
|
rewardsAlreadyClaimed: 'Награды уже получены',
|
||||||
prerequisiteNotMet: 'Предварительный квест не завершён'
|
prerequisiteNotMet: 'Предварительный квест не завершён',
|
||||||
|
questLocked: 'Квест заблокирован',
|
||||||
|
notInitialized: 'Кампания не инициализирована',
|
||||||
|
questAlreadyCompleted: 'Квест уже завершён'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: 'Древний голос',
|
ancientVoice: 'Древний голос',
|
||||||
neighborNPC: 'Соседняя фракция',
|
neighborNPC: 'Соседняя фракция',
|
||||||
mysteriousSignal: 'Загадочный сигнал',
|
mysteriousSignal: 'Загадочный сигнал',
|
||||||
enemyCommander: 'Вражеский командир'
|
enemyCommander: 'Вражеский командир',
|
||||||
|
shadowVoice: 'Голос тени',
|
||||||
|
allyNPC: 'Союзная фракция',
|
||||||
|
ancientGuardian: 'Древний страж'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: 'Построить Металлургический завод до уровня 2',
|
buildMetalMine: 'Построить Металлургический завод до уровня 2',
|
||||||
@@ -1975,25 +2041,42 @@ export default {
|
|||||||
spyAnyNPC: 'Разведать планету NPC',
|
spyAnyNPC: 'Разведать планету NPC',
|
||||||
sendGiftToNPC: 'Отправить подарок NPC',
|
sendGiftToNPC: 'Отправить подарок NPC',
|
||||||
researchAstrophysics: 'Исследовать Астрофизику до уровня 1',
|
researchAstrophysics: 'Исследовать Астрофизику до уровня 1',
|
||||||
|
researchAstrophysicsHigher: 'Исследовать Астрофизику до уровня 3',
|
||||||
buildColonyShip: 'Построить Колониальный корабль',
|
buildColonyShip: 'Построить Колониальный корабль',
|
||||||
colonizeNewPlanet: 'Колонизировать новую планету',
|
colonizeNewPlanet: 'Колонизировать новую планету',
|
||||||
completeExpedition: 'Завершить экспедиционную миссию',
|
colonizeMultiple: 'Колонизировать 5 планет',
|
||||||
|
completeExpedition: 'Завершить 3 экспедиционные миссии',
|
||||||
|
expeditionDeepSpace: 'Завершить 2 экспедиции в глубокий космос',
|
||||||
|
expeditionUncharted: 'Исследовать 1 неизвестный регион',
|
||||||
|
expeditionDangerous: 'Завершить 3 экспедиции в опасные туманности',
|
||||||
discoverRuins: 'Обнаружить древние руины',
|
discoverRuins: 'Обнаружить древние руины',
|
||||||
researchComputer: 'Исследовать Компьютерную технологию до уровня 4',
|
researchComputer: 'Исследовать Компьютерную технологию до уровня 4',
|
||||||
|
researchImpulse: 'Исследовать Импульсный двигатель до уровня 3',
|
||||||
|
researchLaser: 'Исследовать Лазерную технологию до уровня 5',
|
||||||
|
researchIntergalactic: 'Исследовать Компьютерную технологию до уровня 10',
|
||||||
|
researchGraviton: 'Исследовать Гравитонную технологию до уровня 1',
|
||||||
improveRelation: 'Улучшить отношения с NPC',
|
improveRelation: 'Улучшить отношения с NPC',
|
||||||
reachFriendly: 'Достичь дружеского статуса с NPC',
|
reachFriendly: 'Достичь дружеского статуса с NPC',
|
||||||
spyHostileNPC: 'Разведать враждебного NPC',
|
reachFriendlyRelation: 'Достичь дружеского статуса с любым NPC',
|
||||||
|
sendMultipleGifts: 'Отправить 3 подарка NPC',
|
||||||
|
spyHostileNPC: 'Разведать 2 враждебных NPC',
|
||||||
formAlliance: 'Заключить альянс с дружественным NPC',
|
formAlliance: 'Заключить альянс с дружественным NPC',
|
||||||
buildDefenses: 'Построить оборонительные сооружения',
|
buildDefenses: 'Построить оборонительные сооружения',
|
||||||
|
buildMissileSilo: 'Построить Ракетную шахту до уровня 2',
|
||||||
|
buildCruisers: 'Построить 10 Крейсеров',
|
||||||
winDefenseBattle: 'Выиграть оборонительное сражение',
|
winDefenseBattle: 'Выиграть оборонительное сражение',
|
||||||
|
defendAgainstAttack: 'Успешно отразить 1 атаку',
|
||||||
spyEnemyPlanet: 'Разведать вражескую планету',
|
spyEnemyPlanet: 'Разведать вражескую планету',
|
||||||
|
spyEnemyPlanets: 'Разведать 5 вражеских планет',
|
||||||
|
winAttackBattles: 'Выиграть 3 атакующих сражения',
|
||||||
attackEnemy: 'Атаковать врага',
|
attackEnemy: 'Атаковать врага',
|
||||||
recycleDebris: 'Переработать обломки',
|
recycleDebris: 'Переработать обломки 5 раз',
|
||||||
buildBattleships: 'Построить 10 Линкоров',
|
buildBattleships: 'Построить 20 Линкоров',
|
||||||
exploreDeepRuins: 'Исследовать глубокие руины',
|
exploreDeepRuins: 'Исследовать глубокие руины',
|
||||||
researchHyperspace: 'Исследовать Гиперпространственную технологию',
|
researchHyperspace: 'Исследовать Гиперпространственный двигатель до уровня 3',
|
||||||
defeatBoss: 'Победить загадочного врага',
|
defeatBoss: 'Победить Древнего Стража',
|
||||||
colonizeSpecial: 'Колонизировать особое место',
|
colonizeSpecial: 'Колонизировать особое место',
|
||||||
|
accumulateWealth: 'Накопить 1 миллион ресурсов',
|
||||||
continueDevelopment: 'Продолжить развитие'
|
continueDevelopment: 'Продолжить развитие'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2032,12 +2115,19 @@ export default {
|
|||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
'Ваша экспедиция обнаружила аномальные сигналы. Эти сигналы, похоже, исходят от древней цивилизации... Исследуйте их источник.',
|
'Ваша экспедиция обнаружила аномальные сигналы. Эти сигналы, похоже, исходят от древней цивилизации... Исследуйте их источник.',
|
||||||
|
prologue_2: 'Эти сигналы... Они несут отголоски исчезнувшей цивилизации. Их тайны ждут открытия...',
|
||||||
epilogue_1: 'Эти символы... Это руины древней цивилизации! Продолжайте исследование, чтобы раскрыть их тайны.'
|
epilogue_1: 'Эти символы... Это руины древней цивилизации! Продолжайте исследование, чтобы раскрыть их тайны.'
|
||||||
},
|
},
|
||||||
'2_4': {
|
'2_4': {
|
||||||
prologue_1: 'Вы нашли местоположение древних руин. Отправьте свой флот на исследование и посмотрите, что вы можете обнаружить.'
|
prologue_1: 'Вы нашли местоположение древних руин. Отправьте свой флот на исследование и посмотрите, что вы можете обнаружить.',
|
||||||
|
prologue_2: 'В руинах скрыто много тайн... Выбирайте путь мудро...',
|
||||||
|
choice_1: 'Исследовать осторожно - приоритет безопасности',
|
||||||
|
choice_2: 'Исследовать агрессивно - приоритет открытий'
|
||||||
|
},
|
||||||
|
'2_5': {
|
||||||
|
prologue_1: 'В руинах найдены архивы данных. Изучите эти данные, возможно, вы сможете разблокировать новую технологию.',
|
||||||
|
epilogue_1: 'Древние данные расшифрованы! Получены новые технологические знания.'
|
||||||
},
|
},
|
||||||
'2_5': { prologue_1: 'В руинах найдены архивы данных. Изучите эти данные, возможно, вы сможете разблокировать новую технологию.' },
|
|
||||||
'3_1': {
|
'3_1': {
|
||||||
prologue_1: 'Во время исследований не забывайте о дипломатии. Поддержание хороших отношений с окружающими фракциями выгодно.'
|
prologue_1: 'Во время исследований не забывайте о дипломатии. Поддержание хороших отношений с окружающими фракциями выгодно.'
|
||||||
},
|
},
|
||||||
@@ -2046,31 +2136,44 @@ export default {
|
|||||||
},
|
},
|
||||||
'3_3': {
|
'3_3': {
|
||||||
prologue_1:
|
prologue_1:
|
||||||
'Разведка показывает, что враждебные силы наблюдают за вами из тени. Оставайтесь бдительными и разведайте их перемещения.'
|
'Разведка показывает, что враждебные силы наблюдают за вами из тени. Оставайтесь бдительными и разведайте их перемещения.',
|
||||||
|
prologue_2: 'Тёмные силы шевелятся в пустоте... Они заметили вашу растущую мощь...'
|
||||||
},
|
},
|
||||||
'3_4': { prologue_1: 'Заключите официальный альянс с дружественными фракциями для взаимной поддержки против угроз.' },
|
'3_4': { prologue_1: 'Заключите официальный альянс с дружественными фракциями для взаимной поддержки против угроз.' },
|
||||||
'3_5': { prologue_1: 'Угрозы приближаются. Постройте оборонительные сооружения и подготовьтесь к возможному конфликту.' },
|
'3_5': {
|
||||||
|
prologue_1: 'Угрозы приближаются. Постройте оборонительные сооружения и подготовьтесь к возможному конфликту.',
|
||||||
|
epilogue_1: 'Ваша оборона готова. Буря надвигается, но вы подготовлены.'
|
||||||
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: 'Враг начал атаку! Защитите свою планету!',
|
prologue_1: 'Враг начал атаку! Защитите свою планету!',
|
||||||
|
prologue_2: 'Теневой флот приближается... Час твоего испытания настал...',
|
||||||
epilogue_1: 'Вы успешно отбили первую волну врага. Но это только начало...'
|
epilogue_1: 'Вы успешно отбили первую волну врага. Но это только начало...'
|
||||||
},
|
},
|
||||||
'4_2': { prologue_1: 'Враг отступил, но он вернётся. Разведайте их планеты, чтобы понять их силу.' },
|
'4_2': { prologue_1: 'Враг отступил, но он вернётся. Разведайте их планеты, чтобы понять их силу.' },
|
||||||
'4_3': { prologue_1: 'Пора контратаковать. Атакуйте вражеские планеты и ослабьте их силы.' },
|
'4_3': { prologue_1: 'Пора контратаковать. Атакуйте вражеские планеты и ослабьте их силы.' },
|
||||||
'4_4': { prologue_1: 'На поле боя осталось много обломков. Переработайте эти ресурсы для подготовки к следующей битве.' },
|
'4_4': { prologue_1: 'На поле боя осталось много обломков. Переработайте эти ресурсы для подготовки к следующей битве.' },
|
||||||
'4_5': { prologue_1: 'Финальная битва приближается. Постройте мощный флот и приготовьтесь к конечному испытанию.' },
|
'4_5': {
|
||||||
|
prologue_1: 'Финальная битва приближается. Постройте мощный флот и приготовьтесь к конечному испытанию.',
|
||||||
|
epilogue_1: 'Ваш флот собран. Решающий момент приближается...'
|
||||||
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1: 'Все улики указывают на самую глубокую часть руин. Ключевые тайны древней цивилизации находятся там.',
|
prologue_1: 'Все улики указывают на самую глубокую часть руин. Ключевые тайны древней цивилизации находятся там.',
|
||||||
prologue_2: 'Ты наконец прибыл... Истина скоро откроется...'
|
prologue_2: 'Ты наконец прибыл... Истина скоро откроется...'
|
||||||
},
|
},
|
||||||
'5_2': { prologue_1: 'В глубинах руин вы обнаружили утерянные древние технологии. Исследуйте и разблокируйте их мощь.' },
|
'5_2': {
|
||||||
|
prologue_1: 'В глубинах руин вы обнаружили утерянные древние технологии. Исследуйте и разблокируйте их мощь.',
|
||||||
|
prologue_2: 'Эта технология... Она древнее всех известных цивилизаций. Обращайтесь с ней осторожно...'
|
||||||
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: 'Появился загадочный враг. Это финальное испытание. Победите его!',
|
prologue_1: 'Появился загадочный враг. Это финальное испытание. Победите его!',
|
||||||
|
prologue_2: 'Я — страж этих тайн. Докажи свою ценность или будешь уничтожен!',
|
||||||
epilogue_1: 'Вы справились! Древний страж повержен. Тайны галактики теперь открыты для вас.'
|
epilogue_1: 'Вы справились! Древний страж повержен. Тайны галактики теперь открыты для вас.'
|
||||||
},
|
},
|
||||||
'5_4': { prologue_1: 'Мир наконец наступил. В эту новую эру основывайте новые колонии и расширяйте свою империю.' },
|
'5_4': { prologue_1: 'Мир наконец наступил. В эту новую эру основывайте новые колонии и расширяйте свою империю.' },
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: 'Ваша легенда только началась. Продолжайте исследовать и завоёвывать больше звёздных систем!',
|
prologue_1: 'Ваша легенда только началась. Продолжайте исследовать и завоёвывать больше звёздных систем!',
|
||||||
epilogue_1: 'Галактика необъятна и бесконечна, бесчисленные тайны ждут вас...'
|
epilogue_1: 'Галактика необъятна и бесконечна, бесчисленные тайны ждут вас...',
|
||||||
|
epilogue_2: 'Ваше путешествие продолжается... Новые приключения ждут за звёздами...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export default {
|
|||||||
exitConfirmTitle: '退出游戏',
|
exitConfirmTitle: '退出游戏',
|
||||||
exitConfirmMessage: '确定要退出游戏吗?游戏进度会自动保存。',
|
exitConfirmMessage: '确定要退出游戏吗?游戏进度会自动保存。',
|
||||||
points: '积分',
|
points: '积分',
|
||||||
retry: '重试'
|
retry: '重试',
|
||||||
|
playerName: '玩家名称'
|
||||||
},
|
},
|
||||||
errors: {
|
errors: {
|
||||||
requirementsNotMet: '不满足前置条件',
|
requirementsNotMet: '不满足前置条件',
|
||||||
@@ -47,6 +48,8 @@ export default {
|
|||||||
launchFailed: '发射失败',
|
launchFailed: '发射失败',
|
||||||
fleetMissionsFull: '舰队任务槽位已满',
|
fleetMissionsFull: '舰队任务槽位已满',
|
||||||
insufficientFleet: '舰队数量不足',
|
insufficientFleet: '舰队数量不足',
|
||||||
|
insufficientShips: '舰船数量不足',
|
||||||
|
invalidQuantity: '无效数量',
|
||||||
insufficientFuel: '燃料不足',
|
insufficientFuel: '燃料不足',
|
||||||
planetOnly: '该建筑只能在行星上建造',
|
planetOnly: '该建筑只能在行星上建造',
|
||||||
moonOnly: '该建筑只能在月球上建造',
|
moonOnly: '该建筑只能在月球上建造',
|
||||||
@@ -55,7 +58,12 @@ export default {
|
|||||||
buildingLevelZero: '建筑等级为0,无法拆除',
|
buildingLevelZero: '建筑等级为0,无法拆除',
|
||||||
researchQueueFull: '研究队列已满',
|
researchQueueFull: '研究队列已满',
|
||||||
moonExists: '已存在月球',
|
moonExists: '已存在月球',
|
||||||
insufficientDebris: '残骸场不足'
|
insufficientDebris: '残骸场不足',
|
||||||
|
planetNotFound: '找不到星球',
|
||||||
|
cannotAbandonHomePlanet: '不能放弃母星',
|
||||||
|
hasBuildQueue: '请等待建造队列完成',
|
||||||
|
hasFleetOnPlanet: '请先转移或拆除所有舰船',
|
||||||
|
hasDefenseOnPlanet: '请先拆除所有防御设施'
|
||||||
},
|
},
|
||||||
nav: {
|
nav: {
|
||||||
overview: '总览',
|
overview: '总览',
|
||||||
@@ -123,7 +131,12 @@ export default {
|
|||||||
renamePlanet: '重命名星球',
|
renamePlanet: '重命名星球',
|
||||||
renamePlanetTitle: '重命名星球',
|
renamePlanetTitle: '重命名星球',
|
||||||
planetNamePlaceholder: '输入新的星球名称',
|
planetNamePlaceholder: '输入新的星球名称',
|
||||||
rename: '重命名'
|
rename: '重命名',
|
||||||
|
abandonColony: '放弃殖民地',
|
||||||
|
confirmAbandon: '确认放弃殖民地',
|
||||||
|
abandonWarning: '确定要放弃 "{name}" 吗?\n\n此操作无法撤销!\n所有建筑、资源和月球(如有)都将丢失。',
|
||||||
|
confirmAbandonButton: '放弃',
|
||||||
|
abandonFailed: '放弃失败'
|
||||||
},
|
},
|
||||||
player: {
|
player: {
|
||||||
points: '总积分'
|
points: '总积分'
|
||||||
@@ -152,6 +165,7 @@ export default {
|
|||||||
planetDestroyerFactory: '行星毁灭者工厂',
|
planetDestroyerFactory: '行星毁灭者工厂',
|
||||||
geoResearchStation: '地质研究站',
|
geoResearchStation: '地质研究站',
|
||||||
deepDrillingFacility: '深层钻探设施',
|
deepDrillingFacility: '深层钻探设施',
|
||||||
|
university: '大学',
|
||||||
buildTime: '建造时间',
|
buildTime: '建造时间',
|
||||||
production: '产量',
|
production: '产量',
|
||||||
consumption: '消耗',
|
consumption: '消耗',
|
||||||
@@ -199,8 +213,9 @@ export default {
|
|||||||
sensorPhalanx: '侦测周围星系的舰队活动',
|
sensorPhalanx: '侦测周围星系的舰队活动',
|
||||||
jumpGate: '瞬间传送舰队到其他月球',
|
jumpGate: '瞬间传送舰队到其他月球',
|
||||||
planetDestroyerFactory: '建造能够摧毁行星的终极武器',
|
planetDestroyerFactory: '建造能够摧毁行星的终极武器',
|
||||||
geoResearchStation: '研究地质结构,提高矿脉自然恢复速度。每级增加50%恢复速率',
|
geoResearchStation: '研究地质结构,提高矿脉自然恢复速度。每级增加10%恢复速率',
|
||||||
deepDrillingFacility: '深入地壳钻探,解锁更深层的矿脉储量。每级增加20%矿脉上限'
|
deepDrillingFacility: '深入地壳钻探,解锁更深层的矿脉储量。每级增加20%矿脉上限',
|
||||||
|
university: '培养科研人员,加快研究速度。每级减少研究时间8%(与星际研究网络叠加)'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: '轻型战斗机',
|
lightFighter: '轻型战斗机',
|
||||||
@@ -264,6 +279,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: '研究时间',
|
researchTime: '研究时间',
|
||||||
|
levelRange: '等级范围',
|
||||||
|
totalCost: '总成本',
|
||||||
|
totalPoints: '总积分',
|
||||||
|
|
||||||
attackBonus: '攻击加成',
|
attackBonus: '攻击加成',
|
||||||
shieldBonus: '护盾加成',
|
shieldBonus: '护盾加成',
|
||||||
@@ -272,7 +290,11 @@ export default {
|
|||||||
researchQueueBonus: '研究队列',
|
researchQueueBonus: '研究队列',
|
||||||
colonySlots: '殖民地槽位',
|
colonySlots: '殖民地槽位',
|
||||||
forAllPlanets: '(全局)',
|
forAllPlanets: '(全局)',
|
||||||
speedBonus: '速度加成'
|
speedBonus: '速度加成',
|
||||||
|
// 资源产量加成显示
|
||||||
|
mineralResearch: '矿物研究',
|
||||||
|
crystalResearch: '晶体研究',
|
||||||
|
fuelResearch: '燃料研究'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: '能源技术',
|
energyTechnology: '能源技术',
|
||||||
@@ -293,7 +315,11 @@ export default {
|
|||||||
darkMatterTechnology: '暗物质技术',
|
darkMatterTechnology: '暗物质技术',
|
||||||
terraformingTechnology: '地形改造技术',
|
terraformingTechnology: '地形改造技术',
|
||||||
planetDestructionTech: '行星毁灭技术',
|
planetDestructionTech: '行星毁灭技术',
|
||||||
miningTechnology: '采矿技术'
|
miningTechnology: '采矿技术',
|
||||||
|
intergalacticResearchNetwork: '星际研究网络',
|
||||||
|
mineralResearch: '矿物研究',
|
||||||
|
crystalResearch: '晶体研究',
|
||||||
|
fuelResearch: '燃料研究'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: '提高能源利用效率',
|
energyTechnology: '提高能源利用效率',
|
||||||
@@ -315,7 +341,11 @@ export default {
|
|||||||
darkMatterTechnology: '研究暗物质的性质和应用',
|
darkMatterTechnology: '研究暗物质的性质和应用',
|
||||||
terraformingTechnology: '研究行星地形改造技术,每级为所有行星增加30个可用空间',
|
terraformingTechnology: '研究行星地形改造技术,每级为所有行星增加30个可用空间',
|
||||||
planetDestructionTech: '研究如何摧毁整个行星的恐怖技术',
|
planetDestructionTech: '研究如何摧毁整个行星的恐怖技术',
|
||||||
miningTechnology: '改进采矿方法和设备,提升所有星球矿脉储量上限。每级增加15%矿脉上限'
|
miningTechnology: '改进采矿方法和设备,提升所有星球矿脉储量上限。每级增加15%矿脉上限',
|
||||||
|
intergalacticResearchNetwork: '连接多个星球的研究实验室,共享研究资源。每级可连接1个额外的研究实验室(取等级最高的N个实验室)',
|
||||||
|
mineralResearch: '研究更高效的金属提取技术,提升金属矿产量。每级增加金属产量2%',
|
||||||
|
crystalResearch: '研究更高效的晶体提炼技术,提升晶体矿产量。每级增加晶体产量2%',
|
||||||
|
fuelResearch: '研究更高效的重氢合成技术,提升重氢产量。每级增加重氢产量2%'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: '指挥官',
|
commander: '指挥官',
|
||||||
@@ -384,6 +414,7 @@ export default {
|
|||||||
title: '建筑',
|
title: '建筑',
|
||||||
spaceUsage: '占用空间',
|
spaceUsage: '占用空间',
|
||||||
upgradeCost: '升级消耗',
|
upgradeCost: '升级消耗',
|
||||||
|
build: '建造',
|
||||||
upgrade: '升级',
|
upgrade: '升级',
|
||||||
maxLevelReached: '等级已满',
|
maxLevelReached: '等级已满',
|
||||||
requirementsNotMet: '条件不足',
|
requirementsNotMet: '条件不足',
|
||||||
@@ -404,9 +435,19 @@ export default {
|
|||||||
researchFailedMessage: '请检查资源是否足够、前置条件是否满足,或是否有其他研究任务。'
|
researchFailedMessage: '请检查资源是否足够、前置条件是否满足,或是否有其他研究任务。'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: '攻击力',
|
||||||
|
shield: '护盾',
|
||||||
|
armor: '装甲',
|
||||||
speed: '速度',
|
speed: '速度',
|
||||||
cargoCapacity: '载货量',
|
cargoCapacity: '载货量',
|
||||||
fuelConsumption: '燃料消耗'
|
fuelConsumption: '燃料消耗',
|
||||||
|
buildCost: '建造成本',
|
||||||
|
buildTime: '建造时间',
|
||||||
|
perUnit: '单位',
|
||||||
|
batchCalculator: '批量计算器',
|
||||||
|
quantity: '数量',
|
||||||
|
totalCost: '总成本',
|
||||||
|
totalTime: '总时间'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: '船坞',
|
title: '船坞',
|
||||||
@@ -422,7 +463,12 @@ export default {
|
|||||||
inputError: '输入错误',
|
inputError: '输入错误',
|
||||||
inputErrorMessage: '请输入建造数量!',
|
inputErrorMessage: '请输入建造数量!',
|
||||||
buildFailed: '建造失败',
|
buildFailed: '建造失败',
|
||||||
buildFailedMessage: '请检查资源是否足够或前置条件是否满足。'
|
buildFailedMessage: '请检查资源是否足够或前置条件是否满足。',
|
||||||
|
scrapQuantity: '拆除数量',
|
||||||
|
scrapRefund: '拆除返还 (50%)',
|
||||||
|
scrap: '拆除',
|
||||||
|
scrapFailed: '拆除失败',
|
||||||
|
scrapFailedMessage: '请检查舰船数量是否足够。'
|
||||||
},
|
},
|
||||||
defense: {
|
defense: {
|
||||||
attack: '攻击力',
|
attack: '攻击力',
|
||||||
@@ -511,6 +557,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: '回收',
|
recycle: '回收',
|
||||||
destroy: '行星毁灭',
|
destroy: '行星毁灭',
|
||||||
|
harvestDarkMatter: '暗物质采集',
|
||||||
|
station: '驻守协防',
|
||||||
transportResources: '运输资源',
|
transportResources: '运输资源',
|
||||||
totalCargoCapacity: '总载货量',
|
totalCargoCapacity: '总载货量',
|
||||||
used: '已用',
|
used: '已用',
|
||||||
@@ -842,14 +890,14 @@ export default {
|
|||||||
missileAttackFailed: '导弹攻击失败,目标星球不存在',
|
missileAttackFailed: '导弹攻击失败,目标星球不存在',
|
||||||
missileAttackIntercepted: '所有导弹被拦截',
|
missileAttackIntercepted: '所有导弹被拦截',
|
||||||
hits: '枚命中',
|
hits: '枚命中',
|
||||||
expeditionResources: '远征队发现了资源!',
|
expeditionResources: '探险队发现了资源!',
|
||||||
expeditionDarkMatter: '远征队发现了暗物质!',
|
expeditionDarkMatter: '探险队发现了暗物质!',
|
||||||
expeditionFleet: '远征队发现了废弃的舰船!',
|
expeditionFleet: '探险队发现了废弃的舰船!',
|
||||||
expeditionPiratesAttack: '远征队遭遇海盗袭击,损失了部分舰船',
|
expeditionPiratesAttack: '探险队遭遇海盗袭击,损失了部分舰船',
|
||||||
expeditionPiratesEscaped: '远征队遭遇海盗,但成功逃脱',
|
expeditionPiratesEscaped: '探险队遭遇海盗,但成功逃脱',
|
||||||
expeditionAliensAttack: '远征队遭遇外星人袭击,损失了部分舰船',
|
expeditionAliensAttack: '探险队遭遇外星人袭击,损失了部分舰船',
|
||||||
expeditionAliensEscaped: '远征队遭遇外星人,但成功逃脱',
|
expeditionAliensEscaped: '探险队遭遇外星人,但成功逃脱',
|
||||||
expeditionNothing: '远征队什么也没有发现'
|
expeditionNothing: '探险队什么也没有发现'
|
||||||
},
|
},
|
||||||
simulatorView: {
|
simulatorView: {
|
||||||
title: '战斗模拟器',
|
title: '战斗模拟器',
|
||||||
@@ -886,7 +934,10 @@ export default {
|
|||||||
hideRoundDetails: '隐藏回合详情',
|
hideRoundDetails: '隐藏回合详情',
|
||||||
round: '第 {round} 回合',
|
round: '第 {round} 回合',
|
||||||
attackerRemainingPower: '攻击方剩余火力',
|
attackerRemainingPower: '攻击方剩余火力',
|
||||||
defenderRemainingPower: '防守方剩余火力'
|
defenderRemainingPower: '防守方剩余火力',
|
||||||
|
importFromSpyReport: '从侦查报告导入',
|
||||||
|
selectSpyReport: '选择侦查报告',
|
||||||
|
noSpyReports: '没有侦查报告'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
dataManagement: '数据管理',
|
dataManagement: '数据管理',
|
||||||
@@ -897,6 +948,7 @@ export default {
|
|||||||
exporting: '导出中...',
|
exporting: '导出中...',
|
||||||
exportSuccess: '导出成功',
|
exportSuccess: '导出成功',
|
||||||
exportSuccessWithPath: '导出成功,文件已保存到:{path}',
|
exportSuccessWithPath: '导出成功,文件已保存到:{path}',
|
||||||
|
storagePermissionDenied: '存储权限被拒绝,无法导出文件',
|
||||||
exportFailed: '导出失败,请重试',
|
exportFailed: '导出失败,请重试',
|
||||||
importData: '导入数据',
|
importData: '导入数据',
|
||||||
importDataDesc: '从JSON文件恢复游戏进度',
|
importDataDesc: '从JSON文件恢复游戏进度',
|
||||||
@@ -914,6 +966,8 @@ export default {
|
|||||||
gameSettingsDesc: '调整游戏参数和偏好设置',
|
gameSettingsDesc: '调整游戏参数和偏好设置',
|
||||||
gamePause: '游戏暂停',
|
gamePause: '游戏暂停',
|
||||||
gamePauseDesc: '暂停或恢复游戏时间和资源生产',
|
gamePauseDesc: '暂停或恢复游戏时间和资源生产',
|
||||||
|
battleMode: '战斗到底模式',
|
||||||
|
battleModeDesc: '启用后战斗最多进行100回合直到分出胜负,关闭则使用经典6回合模式',
|
||||||
pause: '暂停',
|
pause: '暂停',
|
||||||
resume: '恢复',
|
resume: '恢复',
|
||||||
gamePaused: '游戏已暂停',
|
gamePaused: '游戏已暂停',
|
||||||
@@ -1721,8 +1775,8 @@ export default {
|
|||||||
colonizer: '成功殖民星球次数',
|
colonizer: '成功殖民星球次数',
|
||||||
spy: '执行侦查任务次数',
|
spy: '执行侦查任务次数',
|
||||||
deployer: '执行部署任务次数',
|
deployer: '执行部署任务次数',
|
||||||
explorer: '远征总次数',
|
explorer: '探险总次数',
|
||||||
luckyExplorer: '远征成功次数',
|
luckyExplorer: '探险成功次数',
|
||||||
recycler: '回收任务次数',
|
recycler: '回收任务次数',
|
||||||
scavenger: '回收资源总量',
|
scavenger: '回收资源总量',
|
||||||
destroyer: '摧毁星球次数',
|
destroyer: '摧毁星球次数',
|
||||||
@@ -1782,6 +1836,8 @@ export default {
|
|||||||
branchUnlocked: '新的故事分支已解锁!'
|
branchUnlocked: '新的故事分支已解锁!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: '剧情对话',
|
||||||
|
description: '战役剧情对话内容',
|
||||||
skip: '跳过',
|
skip: '跳过',
|
||||||
continue: '继续',
|
continue: '继续',
|
||||||
finish: '完成',
|
finish: '完成',
|
||||||
@@ -1848,7 +1904,7 @@ export default {
|
|||||||
},
|
},
|
||||||
'2_2': {
|
'2_2': {
|
||||||
title: '深空探险',
|
title: '深空探险',
|
||||||
description: '派遣舰队进行远征探险'
|
description: '派遣舰队进行探险探险'
|
||||||
},
|
},
|
||||||
'2_3': {
|
'2_3': {
|
||||||
title: '神秘信号',
|
title: '神秘信号',
|
||||||
@@ -1944,13 +2000,19 @@ export default {
|
|||||||
questNotActive: '任务未激活',
|
questNotActive: '任务未激活',
|
||||||
questNotCompleted: '任务未完成',
|
questNotCompleted: '任务未完成',
|
||||||
rewardsAlreadyClaimed: '奖励已领取',
|
rewardsAlreadyClaimed: '奖励已领取',
|
||||||
prerequisiteNotMet: '前置任务未完成'
|
prerequisiteNotMet: '前置任务未完成',
|
||||||
|
questLocked: '任务已锁定',
|
||||||
|
notInitialized: '战役未初始化',
|
||||||
|
questAlreadyCompleted: '任务已完成'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: '古代之声',
|
ancientVoice: '古代之声',
|
||||||
neighborNPC: '邻近势力',
|
neighborNPC: '邻近势力',
|
||||||
mysteriousSignal: '神秘信号',
|
mysteriousSignal: '神秘信号',
|
||||||
enemyCommander: '敌方指挥官'
|
enemyCommander: '敌方指挥官',
|
||||||
|
shadowVoice: '暗影之声',
|
||||||
|
allyNPC: '盟友势力',
|
||||||
|
ancientGuardian: '远古守护者'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: '建造金属矿到2级',
|
buildMetalMine: '建造金属矿到2级',
|
||||||
@@ -1966,25 +2028,42 @@ export default {
|
|||||||
spyAnyNPC: '侦查任意NPC星球',
|
spyAnyNPC: '侦查任意NPC星球',
|
||||||
sendGiftToNPC: '向任意NPC送礼',
|
sendGiftToNPC: '向任意NPC送礼',
|
||||||
researchAstrophysics: '研究天体物理学到1级',
|
researchAstrophysics: '研究天体物理学到1级',
|
||||||
|
researchAstrophysicsHigher: '研究天体物理学到3级',
|
||||||
buildColonyShip: '建造殖民船',
|
buildColonyShip: '建造殖民船',
|
||||||
colonizeNewPlanet: '殖民新星球',
|
colonizeNewPlanet: '殖民新星球',
|
||||||
completeExpedition: '完成远征任务',
|
colonizeMultiple: '殖民5个星球',
|
||||||
|
completeExpedition: '完成3次探险任务',
|
||||||
|
expeditionDeepSpace: '完成2次深空探险',
|
||||||
|
expeditionUncharted: '探索1次未知区域',
|
||||||
|
expeditionDangerous: '完成3次危险星云探险',
|
||||||
discoverRuins: '发现古代遗迹',
|
discoverRuins: '发现古代遗迹',
|
||||||
researchComputer: '研究电脑技术到4级',
|
researchComputer: '研究电脑技术到4级',
|
||||||
|
researchImpulse: '研究脉冲驱动到3级',
|
||||||
|
researchLaser: '研究激光技术到5级',
|
||||||
|
researchIntergalactic: '研究电脑技术到10级',
|
||||||
|
researchGraviton: '研究引力子技术到1级',
|
||||||
improveRelation: '提升与NPC的关系',
|
improveRelation: '提升与NPC的关系',
|
||||||
reachFriendly: '与NPC达到友好关系',
|
reachFriendly: '与NPC达到友好关系',
|
||||||
spyHostileNPC: '侦查敌对NPC',
|
reachFriendlyRelation: '与任意NPC达到友好关系',
|
||||||
|
sendMultipleGifts: '向NPC发送3次礼物',
|
||||||
|
spyHostileNPC: '侦查2个敌对NPC',
|
||||||
formAlliance: '与友好NPC结盟',
|
formAlliance: '与友好NPC结盟',
|
||||||
buildDefenses: '建造防御设施',
|
buildDefenses: '建造防御设施',
|
||||||
|
buildMissileSilo: '建造导弹发射井到2级',
|
||||||
|
buildCruisers: '建造10艘巡洋舰',
|
||||||
winDefenseBattle: '赢得防御战斗',
|
winDefenseBattle: '赢得防御战斗',
|
||||||
|
defendAgainstAttack: '成功防御1次攻击',
|
||||||
spyEnemyPlanet: '侦查敌方星球',
|
spyEnemyPlanet: '侦查敌方星球',
|
||||||
|
spyEnemyPlanets: '侦查5个敌方星球',
|
||||||
|
winAttackBattles: '赢得3次进攻战斗',
|
||||||
attackEnemy: '攻击敌方',
|
attackEnemy: '攻击敌方',
|
||||||
recycleDebris: '回收残骸',
|
recycleDebris: '回收5次残骸场',
|
||||||
buildBattleships: '建造10艘战列舰',
|
buildBattleships: '建造20艘战列舰',
|
||||||
exploreDeepRuins: '探索遗迹深处',
|
exploreDeepRuins: '探索遗迹深处',
|
||||||
researchHyperspace: '研究超空间技术',
|
researchHyperspace: '研究超空间驱动到3级',
|
||||||
defeatBoss: '击败神秘敌人',
|
defeatBoss: '击败古代守护者',
|
||||||
colonizeSpecial: '殖民特殊位置',
|
colonizeSpecial: '殖民特殊位置',
|
||||||
|
accumulateWealth: '积累总资源达到100万',
|
||||||
continueDevelopment: '继续发展'
|
continueDevelopment: '继续发展'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2012,18 +2091,23 @@ export default {
|
|||||||
prologue_2: '宇宙是无限的...更多的星球意味着更多的可能性...'
|
prologue_2: '宇宙是无限的...更多的星球意味着更多的可能性...'
|
||||||
},
|
},
|
||||||
'2_2': {
|
'2_2': {
|
||||||
prologue_1: '殖民成功!但宇宙深处还有更多秘密等待发现。派遣舰队进行远征探险吧。',
|
prologue_1: '殖民成功!但宇宙深处还有更多秘密等待发现。派遣舰队进行探险探险吧。',
|
||||||
prologue_2: '远方传来微弱的信号...那里有什么在等待着你...'
|
prologue_2: '远方传来微弱的信号...那里有什么在等待着你...'
|
||||||
},
|
},
|
||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1: '你的探险队发现了异常信号。这些信号似乎来自一个古老的文明...调查它们的来源。',
|
prologue_1: '你的探险队发现了异常信号。这些信号似乎来自一个古老的文明...调查它们的来源。',
|
||||||
|
prologue_2: '这些信号...承载着一个早已消逝的文明的回响。它们的秘密等待被发现...',
|
||||||
epilogue_1: '这些符号...是古代文明的遗迹!继续调查,揭开它们的秘密。'
|
epilogue_1: '这些符号...是古代文明的遗迹!继续调查,揭开它们的秘密。'
|
||||||
},
|
},
|
||||||
'2_4': {
|
'2_4': {
|
||||||
prologue_1: '你发现了古代遗迹的位置。派遣舰队前去探索,看看能发现什么。'
|
prologue_1: '你发现了古代遗迹的位置。派遣舰队前去探索,看看能发现什么。',
|
||||||
|
prologue_2: '遗迹中隐藏着许多秘密...明智地选择你的道路...',
|
||||||
|
choice_1: '谨慎探索 - 优先安全',
|
||||||
|
choice_2: '激进探索 - 优先发现'
|
||||||
},
|
},
|
||||||
'2_5': {
|
'2_5': {
|
||||||
prologue_1: '遗迹中发现了大量数据档案。研究这些数据,也许能解锁新的科技。'
|
prologue_1: '遗迹中发现了大量数据档案。研究这些数据,也许能解锁新的科技。',
|
||||||
|
epilogue_1: '古代数据已被解密!获得了新的科技洞见。'
|
||||||
},
|
},
|
||||||
'3_1': {
|
'3_1': {
|
||||||
prologue_1: '在探索的同时,也不要忘记外交。与周围的势力保持良好关系对你有益。'
|
prologue_1: '在探索的同时,也不要忘记外交。与周围的势力保持良好关系对你有益。'
|
||||||
@@ -2032,16 +2116,19 @@ export default {
|
|||||||
prologue_1: '有些势力对你表示了友好。继续加深关系,也许能获得更多支持。'
|
prologue_1: '有些势力对你表示了友好。继续加深关系,也许能获得更多支持。'
|
||||||
},
|
},
|
||||||
'3_3': {
|
'3_3': {
|
||||||
prologue_1: '情报显示,有敌对势力正在暗中观察你。保持警惕,侦查他们的动向。'
|
prologue_1: '情报显示,有敌对势力正在暗中观察你。保持警惕,侦查他们的动向。',
|
||||||
|
prologue_2: '黑暗势力在虚空中蠢蠢欲动...他们已经注意到你日益增长的力量...'
|
||||||
},
|
},
|
||||||
'3_4': {
|
'3_4': {
|
||||||
prologue_1: '与友好势力建立正式同盟,在面对威胁时互相支持。'
|
prologue_1: '与友好势力建立正式同盟,在面对威胁时互相支持。'
|
||||||
},
|
},
|
||||||
'3_5': {
|
'3_5': {
|
||||||
prologue_1: '威胁正在逼近。建设防御设施,准备迎接可能的冲突。'
|
prologue_1: '威胁正在逼近。建设防御设施,准备迎接可能的冲突。',
|
||||||
|
epilogue_1: '你的防线已经准备就绪。风暴即将来临,但你已经做好了准备。'
|
||||||
},
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: '敌人发动了进攻!保卫你的星球!',
|
prologue_1: '敌人发动了进攻!保卫你的星球!',
|
||||||
|
prologue_2: '暗影舰队正在逼近...你的考验时刻已经到来...',
|
||||||
epilogue_1: '你成功击退了敌人的第一波进攻。但这只是开始...'
|
epilogue_1: '你成功击退了敌人的第一波进攻。但这只是开始...'
|
||||||
},
|
},
|
||||||
'4_2': {
|
'4_2': {
|
||||||
@@ -2054,17 +2141,20 @@ export default {
|
|||||||
prologue_1: '战场上留下了大量残骸。回收这些资源,为下一场战斗做准备。'
|
prologue_1: '战场上留下了大量残骸。回收这些资源,为下一场战斗做准备。'
|
||||||
},
|
},
|
||||||
'4_5': {
|
'4_5': {
|
||||||
prologue_1: '最终决战即将来临。建造强大的舰队,准备迎接最后的挑战。'
|
prologue_1: '最终决战即将来临。建造强大的舰队,准备迎接最后的挑战。',
|
||||||
|
epilogue_1: '你的舰队已经集结完毕。决定性的时刻即将到来...'
|
||||||
},
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1: '所有线索都指向遗迹的最深处。那里隐藏着古代文明最核心的秘密。',
|
prologue_1: '所有线索都指向遗迹的最深处。那里隐藏着古代文明最核心的秘密。',
|
||||||
prologue_2: '你终于来到了这里...真相即将揭晓...'
|
prologue_2: '你终于来到了这里...真相即将揭晓...'
|
||||||
},
|
},
|
||||||
'5_2': {
|
'5_2': {
|
||||||
prologue_1: '在遗迹深处,你发现了失落的古代科技。研究并解锁它们的力量。'
|
prologue_1: '在遗迹深处,你发现了失落的古代科技。研究并解锁它们的力量。',
|
||||||
|
prologue_2: '这项科技...比所有已知文明都要古老。小心处理...'
|
||||||
},
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: '一个神秘的敌人出现了。这是最后的挑战,击败它!',
|
prologue_1: '一个神秘的敌人出现了。这是最后的挑战,击败它!',
|
||||||
|
prologue_2: '我是这些秘密的守护者。证明你的价值,否则将被毁灭!',
|
||||||
epilogue_1: '你做到了!古代的守护者已被击败。银河系的秘密向你敞开。'
|
epilogue_1: '你做到了!古代的守护者已被击败。银河系的秘密向你敞开。'
|
||||||
},
|
},
|
||||||
'5_4': {
|
'5_4': {
|
||||||
@@ -2072,8 +2162,35 @@ export default {
|
|||||||
},
|
},
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: '你的传奇才刚刚开始。继续探索,征服更多的星系!',
|
prologue_1: '你的传奇才刚刚开始。继续探索,征服更多的星系!',
|
||||||
epilogue_1: '银河系广阔无垠,还有无数秘密等待你去发现...'
|
epilogue_1: '银河系广阔无垠,还有无数秘密等待你去发现...',
|
||||||
|
epilogue_2: '你的旅程仍在继续...新的冒险在群星之外等待着你...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
webdav: {
|
||||||
|
// 连接相关
|
||||||
|
connectionSuccess: 'WebDAV 连接成功',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV 连接成功,已创建存档目录',
|
||||||
|
authFailed: '认证失败,请检查用户名和密码',
|
||||||
|
directoryNotExist: '目录不存在且无法创建',
|
||||||
|
connectionFailedHttp: '连接失败: HTTP {status}',
|
||||||
|
networkError: '网络错误,可能是 CORS 限制。建议使用支持 CORS 的 WebDAV 服务或通过代理访问。',
|
||||||
|
connectionError: '连接错误: {error}',
|
||||||
|
// 上传相关
|
||||||
|
uploadSuccess: '上传成功',
|
||||||
|
noWritePermission: '没有写入权限',
|
||||||
|
insufficientStorage: '存储空间不足',
|
||||||
|
uploadFailedHttp: '上传失败: HTTP {status}',
|
||||||
|
uploadError: '上传错误: {error}',
|
||||||
|
// 下载相关
|
||||||
|
fileNotExist: '文件不存在',
|
||||||
|
downloadFailedHttp: '下载失败: HTTP {status}',
|
||||||
|
downloadError: '下载错误: {error}',
|
||||||
|
// 列表相关
|
||||||
|
listFailedHttp: '获取文件列表失败: HTTP {status}',
|
||||||
|
listError: '获取文件列表错误: {error}',
|
||||||
|
// 删除相关
|
||||||
|
deleteFailedHttp: '删除失败: HTTP {status}',
|
||||||
|
deleteError: '删除错误: {error}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ export default {
|
|||||||
planetDestroyerFactory: '行星毀滅者工廠',
|
planetDestroyerFactory: '行星毀滅者工廠',
|
||||||
geoResearchStation: '地質研究站',
|
geoResearchStation: '地質研究站',
|
||||||
deepDrillingFacility: '深層鑽探設施',
|
deepDrillingFacility: '深層鑽探設施',
|
||||||
|
university: '大學',
|
||||||
buildTime: '建造時間',
|
buildTime: '建造時間',
|
||||||
build: '建造',
|
build: '建造',
|
||||||
production: '產量',
|
production: '產量',
|
||||||
@@ -206,7 +207,8 @@ export default {
|
|||||||
jumpGate: '瞬間傳送艦隊到其他月球',
|
jumpGate: '瞬間傳送艦隊到其他月球',
|
||||||
planetDestroyerFactory: '建造能夠摧毀行星的終極武器',
|
planetDestroyerFactory: '建造能夠摧毀行星的終極武器',
|
||||||
geoResearchStation: '研究地質結構,提高礦脈自然恢復速度。每級增加50%恢復速率',
|
geoResearchStation: '研究地質結構,提高礦脈自然恢復速度。每級增加50%恢復速率',
|
||||||
deepDrillingFacility: ''
|
deepDrillingFacility: '',
|
||||||
|
university: '培養科研人員,加快研究速度。每級減少研究時間8%'
|
||||||
},
|
},
|
||||||
ships: {
|
ships: {
|
||||||
lightFighter: '輕型戰鬥機',
|
lightFighter: '輕型戰鬥機',
|
||||||
@@ -270,6 +272,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: '研究時間',
|
researchTime: '研究時間',
|
||||||
|
levelRange: '等級範圍',
|
||||||
|
totalCost: '總成本',
|
||||||
|
totalPoints: '總積分',
|
||||||
capacity: '容量/效果',
|
capacity: '容量/效果',
|
||||||
attackBonus: '攻擊加成',
|
attackBonus: '攻擊加成',
|
||||||
shieldBonus: '護盾加成',
|
shieldBonus: '護盾加成',
|
||||||
@@ -278,7 +283,11 @@ export default {
|
|||||||
researchQueueBonus: '研究隊列',
|
researchQueueBonus: '研究隊列',
|
||||||
colonySlots: '殖民地槽位',
|
colonySlots: '殖民地槽位',
|
||||||
forAllPlanets: '(全局)',
|
forAllPlanets: '(全局)',
|
||||||
speedBonus: '速度加成'
|
speedBonus: '速度加成',
|
||||||
|
// 資源產量加成顯示
|
||||||
|
mineralResearch: '礦物研究',
|
||||||
|
crystalResearch: '晶體研究',
|
||||||
|
fuelResearch: '燃料研究'
|
||||||
},
|
},
|
||||||
technologies: {
|
technologies: {
|
||||||
energyTechnology: '能源技術',
|
energyTechnology: '能源技術',
|
||||||
@@ -299,7 +308,11 @@ export default {
|
|||||||
darkMatterTechnology: '暗物質技術',
|
darkMatterTechnology: '暗物質技術',
|
||||||
terraformingTechnology: '地形改造技術',
|
terraformingTechnology: '地形改造技術',
|
||||||
planetDestructionTech: '行星毀滅技術',
|
planetDestructionTech: '行星毀滅技術',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: '星際研究網絡',
|
||||||
|
mineralResearch: '礦物研究',
|
||||||
|
crystalResearch: '晶體研究',
|
||||||
|
fuelResearch: '燃料研究'
|
||||||
},
|
},
|
||||||
technologyDescriptions: {
|
technologyDescriptions: {
|
||||||
energyTechnology: '提高能源利用效率',
|
energyTechnology: '提高能源利用效率',
|
||||||
@@ -321,7 +334,11 @@ export default {
|
|||||||
darkMatterTechnology: '研究暗物質的性質和應用',
|
darkMatterTechnology: '研究暗物質的性質和應用',
|
||||||
terraformingTechnology: '研究行星地形改造技術,每級為所有行星增加30個可用空間',
|
terraformingTechnology: '研究行星地形改造技術,每級為所有行星增加30個可用空間',
|
||||||
planetDestructionTech: '研究如何摧毀整個行星的恐怖技術',
|
planetDestructionTech: '研究如何摧毀整個行星的恐怖技術',
|
||||||
miningTechnology: ''
|
miningTechnology: '',
|
||||||
|
intergalacticResearchNetwork: '連接多個星球的研究實驗室,共享研究資源。每級可連接1個額外的研究實驗室',
|
||||||
|
mineralResearch: '研究更高效的金屬提取技術,提升金屬礦產量。每級增加金屬產量2%',
|
||||||
|
crystalResearch: '研究更高效的晶體提煉技術,提升晶體礦產量。每級增加晶體產量2%',
|
||||||
|
fuelResearch: '研究更高效的重氫合成技術,提升重氫產量。每級增加重氫產量2%'
|
||||||
},
|
},
|
||||||
officers: {
|
officers: {
|
||||||
commander: '指揮官',
|
commander: '指揮官',
|
||||||
@@ -414,11 +431,20 @@ export default {
|
|||||||
researchFailedMessage: '請檢查資源是否足夠、前置條件是否滿足,或是否有其他研究任務。'
|
researchFailedMessage: '請檢查資源是否足夠、前置條件是否滿足,或是否有其他研究任務。'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
missileAttack: '導彈攻擊',
|
attack: '攻擊力',
|
||||||
|
shield: '護盾',
|
||||||
armor: '裝甲',
|
armor: '裝甲',
|
||||||
|
missileAttack: '導彈攻擊',
|
||||||
speed: '速度',
|
speed: '速度',
|
||||||
cargoCapacity: '載貨量',
|
cargoCapacity: '載貨量',
|
||||||
fuelConsumption: '燃料消耗',
|
fuelConsumption: '燃料消耗',
|
||||||
|
buildCost: '建造成本',
|
||||||
|
buildTime: '建造時間',
|
||||||
|
perUnit: '單位',
|
||||||
|
batchCalculator: '批量計算器',
|
||||||
|
quantity: '數量',
|
||||||
|
totalCost: '總成本',
|
||||||
|
totalTime: '總時間',
|
||||||
build: '建造'
|
build: '建造'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
@@ -530,6 +556,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: '回收',
|
recycle: '回收',
|
||||||
destroy: '行星毀滅',
|
destroy: '行星毀滅',
|
||||||
|
harvestDarkMatter: '暗物質採集',
|
||||||
|
station: '駐守協防',
|
||||||
transportResources: '運輸資源',
|
transportResources: '運輸資源',
|
||||||
totalCargoCapacity: '總載貨量',
|
totalCargoCapacity: '總載貨量',
|
||||||
used: '已用',
|
used: '已用',
|
||||||
@@ -910,7 +938,10 @@ export default {
|
|||||||
hideRoundDetails: '隱藏回合詳情',
|
hideRoundDetails: '隱藏回合詳情',
|
||||||
round: '第 {round} 回合',
|
round: '第 {round} 回合',
|
||||||
attackerRemainingPower: '攻擊方剩餘火力',
|
attackerRemainingPower: '攻擊方剩餘火力',
|
||||||
defenderRemainingPower: '防守方剩餘火力'
|
defenderRemainingPower: '防守方剩餘火力',
|
||||||
|
importFromSpyReport: '從偵查報告匯入',
|
||||||
|
selectSpyReport: '選擇偵查報告',
|
||||||
|
noSpyReports: '沒有偵查報告'
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
dataManagement: '資料管理',
|
dataManagement: '資料管理',
|
||||||
@@ -921,6 +952,7 @@ export default {
|
|||||||
exporting: '匯出中...',
|
exporting: '匯出中...',
|
||||||
exportSuccess: '匯出成功',
|
exportSuccess: '匯出成功',
|
||||||
exportSuccessWithPath: '匯出成功,檔案已儲存到:{path}',
|
exportSuccessWithPath: '匯出成功,檔案已儲存到:{path}',
|
||||||
|
storagePermissionDenied: '儲存權限被拒絕,無法匯出檔案',
|
||||||
exportFailed: '匯出失敗,請重試',
|
exportFailed: '匯出失敗,請重試',
|
||||||
importData: '匯入資料',
|
importData: '匯入資料',
|
||||||
importDataDesc: '從JSON檔案恢復遊戲進度',
|
importDataDesc: '從JSON檔案恢復遊戲進度',
|
||||||
@@ -938,6 +970,8 @@ export default {
|
|||||||
gameSettingsDesc: '調整遊戲參數和偏好設定',
|
gameSettingsDesc: '調整遊戲參數和偏好設定',
|
||||||
gamePause: '遊戲暫停',
|
gamePause: '遊戲暫停',
|
||||||
gamePauseDesc: '暫停或恢復遊戲時間和資源生產',
|
gamePauseDesc: '暫停或恢復遊戲時間和資源生產',
|
||||||
|
battleMode: '戰鬥到底模式',
|
||||||
|
battleModeDesc: '啟用後戰鬥最多進行100回合直到分出勝負,關閉則使用經典6回合模式',
|
||||||
pause: '暫停',
|
pause: '暫停',
|
||||||
resume: '恢復',
|
resume: '恢復',
|
||||||
gamePaused: '遊戲已暫停',
|
gamePaused: '遊戲已暫停',
|
||||||
@@ -1933,6 +1967,27 @@ export default {
|
|||||||
reputationBonusDesc: '你的盟友{npcName}向{targetNpc}說了你的好話'
|
reputationBonusDesc: '你的盟友{npcName}向{targetNpc}說了你的好話'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV 連線成功',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV 連線成功,已建立存檔目錄',
|
||||||
|
authFailed: '認證失敗,請檢查使用者名稱和密碼',
|
||||||
|
directoryNotExist: '目錄不存在且無法建立',
|
||||||
|
networkError: '網路錯誤,請檢查伺服器位址和網路連線',
|
||||||
|
unknownError: '未知錯誤',
|
||||||
|
uploadSuccess: '存檔上傳成功',
|
||||||
|
uploadFailed: '上傳失敗',
|
||||||
|
downloadSuccess: '存檔下載成功',
|
||||||
|
downloadFailed: '下載失敗',
|
||||||
|
noSaveFiles: '伺服器上沒有存檔',
|
||||||
|
fileListSuccess: '取得存檔列表成功',
|
||||||
|
fileListFailed: '取得存檔列表失敗',
|
||||||
|
deleteSuccess: '存檔刪除成功',
|
||||||
|
deleteFailed: '刪除失敗',
|
||||||
|
serverError: '伺服器錯誤',
|
||||||
|
notConfigured: 'WebDAV 未設定',
|
||||||
|
invalidUrl: '無效的 WebDAV URL',
|
||||||
|
timeout: '連線逾時'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: '戰役',
|
name: '戰役',
|
||||||
description: '探索神秘的銀河系,揭開古代文明的秘密',
|
description: '探索神秘的銀河系,揭開古代文明的秘密',
|
||||||
@@ -1960,6 +2015,8 @@ export default {
|
|||||||
branchUnlocked: '新的故事分支已解鎖!'
|
branchUnlocked: '新的故事分支已解鎖!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: '劇情對話',
|
||||||
|
description: '戰役劇情對話內容',
|
||||||
skip: '跳過',
|
skip: '跳過',
|
||||||
continue: '繼續',
|
continue: '繼續',
|
||||||
finish: '完成',
|
finish: '完成',
|
||||||
@@ -2122,13 +2179,19 @@ export default {
|
|||||||
questNotActive: '任務未激活',
|
questNotActive: '任務未激活',
|
||||||
questNotCompleted: '任務未完成',
|
questNotCompleted: '任務未完成',
|
||||||
rewardsAlreadyClaimed: '獎勵已領取',
|
rewardsAlreadyClaimed: '獎勵已領取',
|
||||||
prerequisiteNotMet: '前置任務未完成'
|
prerequisiteNotMet: '前置任務未完成',
|
||||||
|
questLocked: '任務已鎖定',
|
||||||
|
notInitialized: '戰役未初始化',
|
||||||
|
questAlreadyCompleted: '任務已完成'
|
||||||
},
|
},
|
||||||
speakers: {
|
speakers: {
|
||||||
ancientVoice: '古代之聲',
|
ancientVoice: '古代之聲',
|
||||||
neighborNPC: '鄰近勢力',
|
neighborNPC: '鄰近勢力',
|
||||||
mysteriousSignal: '神秘信號',
|
mysteriousSignal: '神秘信號',
|
||||||
enemyCommander: '敵方指揮官'
|
enemyCommander: '敵方指揮官',
|
||||||
|
shadowVoice: '暗影之聲',
|
||||||
|
allyNPC: '盟友勢力',
|
||||||
|
ancientGuardian: '遠古守護者'
|
||||||
},
|
},
|
||||||
objectiveDescriptions: {
|
objectiveDescriptions: {
|
||||||
buildMetalMine: '建造金屬礦到2級',
|
buildMetalMine: '建造金屬礦到2級',
|
||||||
@@ -2144,25 +2207,42 @@ export default {
|
|||||||
spyAnyNPC: '偵查任意NPC星球',
|
spyAnyNPC: '偵查任意NPC星球',
|
||||||
sendGiftToNPC: '向任意NPC送禮',
|
sendGiftToNPC: '向任意NPC送禮',
|
||||||
researchAstrophysics: '研究天體物理學到1級',
|
researchAstrophysics: '研究天體物理學到1級',
|
||||||
|
researchAstrophysicsHigher: '研究天體物理學到3級',
|
||||||
buildColonyShip: '建造殖民船',
|
buildColonyShip: '建造殖民船',
|
||||||
colonizeNewPlanet: '殖民新星球',
|
colonizeNewPlanet: '殖民新星球',
|
||||||
completeExpedition: '完成遠征任務',
|
colonizeMultiple: '殖民5個星球',
|
||||||
|
completeExpedition: '完成3次遠征任務',
|
||||||
|
expeditionDeepSpace: '完成2次深空遠征',
|
||||||
|
expeditionUncharted: '探索1次未知區域',
|
||||||
|
expeditionDangerous: '完成3次危險星雲遠征',
|
||||||
discoverRuins: '發現古代遺跡',
|
discoverRuins: '發現古代遺跡',
|
||||||
researchComputer: '研究電腦技術到4級',
|
researchComputer: '研究電腦技術到4級',
|
||||||
|
researchImpulse: '研究脈衝驅動到3級',
|
||||||
|
researchLaser: '研究雷射技術到5級',
|
||||||
|
researchIntergalactic: '研究電腦技術到10級',
|
||||||
|
researchGraviton: '研究引力子技術到1級',
|
||||||
improveRelation: '提升與NPC的關係',
|
improveRelation: '提升與NPC的關係',
|
||||||
reachFriendly: '與NPC達到友好關係',
|
reachFriendly: '與NPC達到友好關係',
|
||||||
spyHostileNPC: '偵查敵對NPC',
|
reachFriendlyRelation: '與任意NPC達到友好關係',
|
||||||
|
sendMultipleGifts: '向NPC發送3次禮物',
|
||||||
|
spyHostileNPC: '偵查2個敵對NPC',
|
||||||
formAlliance: '與友好NPC結盟',
|
formAlliance: '與友好NPC結盟',
|
||||||
buildDefenses: '建造防禦設施',
|
buildDefenses: '建造防禦設施',
|
||||||
|
buildMissileSilo: '建造飛彈發射井到2級',
|
||||||
|
buildCruisers: '建造10艘巡洋艦',
|
||||||
winDefenseBattle: '贏得防禦戰鬥',
|
winDefenseBattle: '贏得防禦戰鬥',
|
||||||
|
defendAgainstAttack: '成功防禦1次攻擊',
|
||||||
spyEnemyPlanet: '偵查敵方星球',
|
spyEnemyPlanet: '偵查敵方星球',
|
||||||
|
spyEnemyPlanets: '偵查5個敵方星球',
|
||||||
|
winAttackBattles: '贏得3次進攻戰鬥',
|
||||||
attackEnemy: '攻擊敵方',
|
attackEnemy: '攻擊敵方',
|
||||||
recycleDebris: '回收殘骸',
|
recycleDebris: '回收5次殘骸場',
|
||||||
buildBattleships: '建造10艘戰列艦',
|
buildBattleships: '建造20艘戰列艦',
|
||||||
exploreDeepRuins: '探索遺跡深處',
|
exploreDeepRuins: '探索遺跡深處',
|
||||||
researchHyperspace: '研究超空間技術',
|
researchHyperspace: '研究超空間驅動到3級',
|
||||||
defeatBoss: '擊敗神秘敵人',
|
defeatBoss: '擊敗古代守護者',
|
||||||
colonizeSpecial: '殖民特殊位置',
|
colonizeSpecial: '殖民特殊位置',
|
||||||
|
accumulateWealth: '積累總資源達到100萬',
|
||||||
continueDevelopment: '繼續發展'
|
continueDevelopment: '繼續發展'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2195,13 +2275,18 @@ export default {
|
|||||||
},
|
},
|
||||||
'2_3': {
|
'2_3': {
|
||||||
prologue_1: '你的探險隊發現了異常信號。這些信號似乎來自一個古老的文明...調查它們的來源。',
|
prologue_1: '你的探險隊發現了異常信號。這些信號似乎來自一個古老的文明...調查它們的來源。',
|
||||||
|
prologue_2: '這些信號...承載著失落文明的回響。它們的秘密等待被發現...',
|
||||||
epilogue_1: '這些符號...是古代文明的遺跡!繼續調查,揭開它們的秘密。'
|
epilogue_1: '這些符號...是古代文明的遺跡!繼續調查,揭開它們的秘密。'
|
||||||
},
|
},
|
||||||
'2_4': {
|
'2_4': {
|
||||||
prologue_1: '你發現了古代遺跡的位置。派遣艦隊前去探索,看看能發現什麼。'
|
prologue_1: '你發現了古代遺跡的位置。派遣艦隊前去探索,看看能發現什麼。',
|
||||||
|
prologue_2: '遺跡中藏有許多秘密...明智地選擇你的道路...',
|
||||||
|
choice_1: '謹慎探索 - 優先安全',
|
||||||
|
choice_2: '積極探索 - 優先發現'
|
||||||
},
|
},
|
||||||
'2_5': {
|
'2_5': {
|
||||||
prologue_1: '遺跡中發現了大量數據檔案。研究這些數據,也許能解鎖新的科技。'
|
prologue_1: '遺跡中發現了大量數據檔案。研究這些數據,也許能解鎖新的科技。',
|
||||||
|
epilogue_1: '古代數據已被解密!獲得了新的技術見解。'
|
||||||
},
|
},
|
||||||
'3_1': {
|
'3_1': {
|
||||||
prologue_1: '在探索的同時,也不要忘記外交。與周圍的勢力保持良好關係對你有益。'
|
prologue_1: '在探索的同時,也不要忘記外交。與周圍的勢力保持良好關係對你有益。'
|
||||||
@@ -2210,16 +2295,19 @@ export default {
|
|||||||
prologue_1: '有些勢力對你表示了友好。繼續加深關係,也許能獲得更多支持。'
|
prologue_1: '有些勢力對你表示了友好。繼續加深關係,也許能獲得更多支持。'
|
||||||
},
|
},
|
||||||
'3_3': {
|
'3_3': {
|
||||||
prologue_1: '情報顯示,有敵對勢力正在暗中觀察你。保持警惕,偵查他們的動向。'
|
prologue_1: '情報顯示,有敵對勢力正在暗中觀察你。保持警惕,偵查他們的動向。',
|
||||||
|
prologue_2: '黑暗勢力在虛空中蠢蠢欲動...他們已經注意到你日益增長的力量...'
|
||||||
},
|
},
|
||||||
'3_4': {
|
'3_4': {
|
||||||
prologue_1: '與友好勢力建立正式同盟,在面對威脅時互相支持。'
|
prologue_1: '與友好勢力建立正式同盟,在面對威脅時互相支持。'
|
||||||
},
|
},
|
||||||
'3_5': {
|
'3_5': {
|
||||||
prologue_1: '威脅正在逼近。建設防禦設施,準備迎接可能的衝突。'
|
prologue_1: '威脅正在逼近。建設防禦設施,準備迎接可能的衝突。',
|
||||||
|
epilogue_1: '你的防禦已經就緒。風暴即將來臨,但你已準備好了。'
|
||||||
},
|
},
|
||||||
'4_1': {
|
'4_1': {
|
||||||
prologue_1: '敵人發動了進攻!保衛你的星球!',
|
prologue_1: '敵人發動了進攻!保衛你的星球!',
|
||||||
|
prologue_2: '暗影艦隊正在逼近...你的考驗時刻已經到來...',
|
||||||
epilogue_1: '你成功擊退了敵人的第一波進攻。但這只是開始...'
|
epilogue_1: '你成功擊退了敵人的第一波進攻。但這只是開始...'
|
||||||
},
|
},
|
||||||
'4_2': {
|
'4_2': {
|
||||||
@@ -2232,17 +2320,20 @@ export default {
|
|||||||
prologue_1: '戰場上留下了大量殘骸。回收這些資源,為下一場戰鬥做準備。'
|
prologue_1: '戰場上留下了大量殘骸。回收這些資源,為下一場戰鬥做準備。'
|
||||||
},
|
},
|
||||||
'4_5': {
|
'4_5': {
|
||||||
prologue_1: '最終決戰即將來臨。建造強大的艦隊,準備迎接最後的挑戰。'
|
prologue_1: '最終決戰即將來臨。建造強大的艦隊,準備迎接最後的挑戰。',
|
||||||
|
epilogue_1: '你的艦隊已經集結完畢。決戰時刻即將來臨...'
|
||||||
},
|
},
|
||||||
'5_1': {
|
'5_1': {
|
||||||
prologue_1: '所有線索都指向遺跡的最深處。那裡隱藏著古代文明最核心的秘密。',
|
prologue_1: '所有線索都指向遺跡的最深處。那裡隱藏著古代文明最核心的秘密。',
|
||||||
prologue_2: '你終於來到了這裡...真相即將揭曉...'
|
prologue_2: '你終於來到了這裡...真相即將揭曉...'
|
||||||
},
|
},
|
||||||
'5_2': {
|
'5_2': {
|
||||||
prologue_1: '在遺跡深處,你發現了失落的古代科技。研究並解鎖它們的力量。'
|
prologue_1: '在遺跡深處,你發現了失落的古代科技。研究並解鎖它們的力量。',
|
||||||
|
prologue_2: '這項科技...比所有已知文明都要古老。小心處理它...'
|
||||||
},
|
},
|
||||||
'5_3': {
|
'5_3': {
|
||||||
prologue_1: '一個神秘的敵人出現了。這是最後的挑戰,擊敗它!',
|
prologue_1: '一個神秘的敵人出現了。這是最後的挑戰,擊敗它!',
|
||||||
|
prologue_2: '我是這些秘密的守護者。證明你的價值,否則將被毀滅!',
|
||||||
epilogue_1: '你做到了!古代的守護者已被擊敗。銀河系的秘密向你敞開。'
|
epilogue_1: '你做到了!古代的守護者已被擊敗。銀河系的秘密向你敞開。'
|
||||||
},
|
},
|
||||||
'5_4': {
|
'5_4': {
|
||||||
@@ -2250,7 +2341,8 @@ export default {
|
|||||||
},
|
},
|
||||||
'5_5': {
|
'5_5': {
|
||||||
prologue_1: '你的傳奇才剛剛開始。繼續探索,征服更多的星系!',
|
prologue_1: '你的傳奇才剛剛開始。繼續探索,征服更多的星系!',
|
||||||
epilogue_1: '銀河系廣闘無垠,還有無數秘密等待你去發現...'
|
epilogue_1: '銀河系廣闘無垠,還有無數秘密等待你去發現...',
|
||||||
|
epilogue_2: '你的旅程還在繼續...新的冒險在星海彼岸等待著你...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ export const simulateBattle = async (
|
|||||||
_attackerOfficers: Record<OfficerType, Officer>,
|
_attackerOfficers: Record<OfficerType, Officer>,
|
||||||
_defenderOfficers: Record<OfficerType, Officer>,
|
_defenderOfficers: Record<OfficerType, Officer>,
|
||||||
attackerTechnologies: Record<TechnologyType, number>,
|
attackerTechnologies: Record<TechnologyType, number>,
|
||||||
defenderTechnologies: Record<TechnologyType, number>
|
defenderTechnologies: Record<TechnologyType, number>,
|
||||||
|
battleToFinish: boolean = false // 战斗到底模式
|
||||||
): Promise<BattleResult> => {
|
): Promise<BattleResult> => {
|
||||||
// 从科技系统读取实际科技等级
|
// 从科技系统读取实际科技等级
|
||||||
const attackerWeaponTech = attackerTechnologies['weaponsTechnology'] || 0
|
const attackerWeaponTech = attackerTechnologies['weaponsTechnology'] || 0
|
||||||
@@ -41,7 +42,7 @@ export const simulateBattle = async (
|
|||||||
shieldTech: defenderShieldTech,
|
shieldTech: defenderShieldTech,
|
||||||
armorTech: defenderArmorTech
|
armorTech: defenderArmorTech
|
||||||
},
|
},
|
||||||
maxRounds: 6 // 最多6回合
|
maxRounds: battleToFinish ? 100 : 6 // 战斗到底模式最多100回合,经典模式6回合
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算掠夺(仅攻击方胜利时)
|
// 计算掠夺(仅攻击方胜利时)
|
||||||
|
|||||||
@@ -23,30 +23,44 @@ export const calculateBuildingCost = (buildingType: BuildingType, targetLevel: n
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算建筑升级时间
|
* 计算建筑升级时间
|
||||||
|
* 使用 2moons 公式(调整版):
|
||||||
|
* 1. 成本系数 = Σ (资源^0.3 / 0.003)
|
||||||
|
* 2. 时间(秒) = 成本系数 / ((1 + 机器人工厂) × 2^纳米工厂 × 游戏速度)
|
||||||
* @param buildingType 建筑类型
|
* @param buildingType 建筑类型
|
||||||
* @param targetLevel 目标等级
|
* @param targetLevel 目标等级
|
||||||
* @param buildingSpeedBonus 指挥官等提供的速度加成百分比
|
* @param buildingSpeedBonus 指挥官等提供的速度加成百分比
|
||||||
* @param roboticsFactoryLevel 机器人工厂等级
|
* @param roboticsFactoryLevel 机器人工厂等级
|
||||||
* @param naniteFactoryLevel 纳米工厂等级
|
* @param naniteFactoryLevel 纳米工厂等级
|
||||||
|
* @param gameSpeed 游戏速度(默认1)
|
||||||
*/
|
*/
|
||||||
export const calculateBuildingTime = (
|
export const calculateBuildingTime = (
|
||||||
buildingType: BuildingType,
|
buildingType: BuildingType,
|
||||||
targetLevel: number,
|
targetLevel: number,
|
||||||
buildingSpeedBonus: number = 0,
|
buildingSpeedBonus: number = 0,
|
||||||
roboticsFactoryLevel: number = 0,
|
roboticsFactoryLevel: number = 0,
|
||||||
naniteFactoryLevel: number = 0
|
naniteFactoryLevel: number = 0,
|
||||||
|
gameSpeed: number = 1
|
||||||
): number => {
|
): number => {
|
||||||
const config = BUILDINGS[buildingType]
|
// 计算该等级的成本
|
||||||
const multiplier = Math.pow(config.costMultiplier, targetLevel - 1)
|
const cost = calculateBuildingCost(buildingType, targetLevel)
|
||||||
const baseTime = config.baseTime * multiplier
|
|
||||||
|
|
||||||
// 机器人工厂和纳米工厂的加速:建造时间 / (1 + 机器人工厂等级 + 纳米工厂等级 × 2)
|
// 2moons 公式:成本系数 = Σ (资源^0.3 / 0.003)
|
||||||
const factorySpeedDivisor = 1 + roboticsFactoryLevel + naniteFactoryLevel * 2
|
let elementCost = 0
|
||||||
|
if (cost.metal > 0) elementCost += Math.pow(cost.metal, 0.3) / 0.003
|
||||||
|
if (cost.crystal > 0) elementCost += Math.pow(cost.crystal, 0.3) / 0.003
|
||||||
|
if (cost.deuterium > 0) elementCost += Math.pow(cost.deuterium, 0.3) / 0.003
|
||||||
|
|
||||||
|
// 机器人工厂和纳米工厂的加速
|
||||||
|
const factoryBonus = (1 + roboticsFactoryLevel) * Math.pow(2, naniteFactoryLevel)
|
||||||
|
|
||||||
|
// 简化公式:时间(秒) = 成本系数 / (工厂加成 × 游戏速度)
|
||||||
|
const timeInSeconds = elementCost / (factoryBonus * gameSpeed)
|
||||||
|
|
||||||
// 指挥官等的百分比加成
|
// 指挥官等的百分比加成
|
||||||
const speedMultiplier = 1 - buildingSpeedBonus / 100
|
const speedMultiplier = 1 - buildingSpeedBonus / 100
|
||||||
|
|
||||||
return Math.floor((baseTime / factorySpeedDivisor) * speedMultiplier)
|
// 确保最小时间为5秒
|
||||||
|
return Math.max(5, Math.floor(timeInSeconds * speedMultiplier))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +92,16 @@ export const checkBuildingRequirements = (
|
|||||||
for (const [key, level] of Object.entries(requirements)) {
|
for (const [key, level] of Object.entries(requirements)) {
|
||||||
const requiredLevel = level as number
|
const requiredLevel = level as number
|
||||||
if (Object.values(BuildingType).includes(key as BuildingType)) {
|
if (Object.values(BuildingType).includes(key as BuildingType)) {
|
||||||
if ((planet.buildings[key as BuildingType] || 0) < requiredLevel) {
|
const requiredBuildingType = key as BuildingType
|
||||||
|
const requiredBuildingConfig = BUILDINGS[requiredBuildingType]
|
||||||
|
|
||||||
|
// 如果当前是月球,且所需建筑是星球专属建筑(planetOnly),则跳过此前置条件
|
||||||
|
// 这允许在月球上建造机器人工厂等建筑,即使它们的前置条件是无法在月球建造的矿场
|
||||||
|
if (planet.isMoon && requiredBuildingConfig?.planetOnly) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((planet.buildings[requiredBuildingType] || 0) < requiredLevel) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else if (Object.values(TechnologyType).includes(key as TechnologyType)) {
|
} else if (Object.values(TechnologyType).includes(key as TechnologyType)) {
|
||||||
@@ -168,6 +191,12 @@ export const completeBuildQueue = (
|
|||||||
const buildingType = item.itemType as BuildingType
|
const buildingType = item.itemType as BuildingType
|
||||||
const currentLevel = planet.buildings[buildingType] || 0
|
const currentLevel = planet.buildings[buildingType] || 0
|
||||||
planet.buildings[buildingType] = Math.max(0, currentLevel - 1)
|
planet.buildings[buildingType] = Math.max(0, currentLevel - 1)
|
||||||
|
} else if (item.type === 'scrap_ship') {
|
||||||
|
// 舰船拆除完成,减少舰船数量(舰船已在开始拆除时扣除)
|
||||||
|
// 资源返还也在开始拆除时完成,这里不需要额外操作
|
||||||
|
if (onCompleted) {
|
||||||
|
onCompleted('ship', item.itemType, undefined, item.quantity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { FleetMission, Planet, Resources, Fleet, BattleResult, SpyReport, P
|
|||||||
import type { Locale } from '@/locales'
|
import type { Locale } from '@/locales'
|
||||||
import { ShipType, DefenseType, MissionType, BuildingType, OfficerType, TechnologyType, ExpeditionZone } from '@/types/game'
|
import { ShipType, DefenseType, MissionType, BuildingType, OfficerType, TechnologyType, ExpeditionZone } from '@/types/game'
|
||||||
import { FLEET_STORAGE_CONFIG, EXPEDITION_ZONES } from '@/config/gameConfig'
|
import { FLEET_STORAGE_CONFIG, EXPEDITION_ZONES } from '@/config/gameConfig'
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import * as battleLogic from './battleLogic'
|
import * as battleLogic from './battleLogic'
|
||||||
import * as moonLogic from './moonLogic'
|
import * as moonLogic from './moonLogic'
|
||||||
import * as moonValidation from './moonValidation'
|
import * as moonValidation from './moonValidation'
|
||||||
@@ -156,6 +157,7 @@ export const processAttackArrival = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 执行战斗(使用 Worker 进行异步计算)
|
// 执行战斗(使用 Worker 进行异步计算)
|
||||||
|
const gameStore = useGameStore()
|
||||||
const battleResult = await battleLogic.simulateBattle(
|
const battleResult = await battleLogic.simulateBattle(
|
||||||
mission.fleet,
|
mission.fleet,
|
||||||
targetPlanet.fleet,
|
targetPlanet.fleet,
|
||||||
@@ -164,7 +166,8 @@ export const processAttackArrival = async (
|
|||||||
attacker.officers,
|
attacker.officers,
|
||||||
defender?.officers || ({} as Record<OfficerType, Officer>),
|
defender?.officers || ({} as Record<OfficerType, Officer>),
|
||||||
attacker.technologies,
|
attacker.technologies,
|
||||||
defender?.technologies || ({} as Record<TechnologyType, number>)
|
defender?.technologies || ({} as Record<TechnologyType, number>),
|
||||||
|
gameStore.battleToFinish
|
||||||
)
|
)
|
||||||
|
|
||||||
// 更新战斗报告ID
|
// 更新战斗报告ID
|
||||||
@@ -250,6 +253,7 @@ export const processNPCAttackArrival = async (
|
|||||||
allPlanets: Planet[]
|
allPlanets: Planet[]
|
||||||
): Promise<{ battleResult: BattleResult; moon: Planet | null; debrisField: DebrisField | null } | null> => {
|
): Promise<{ battleResult: BattleResult; moon: Planet | null; debrisField: DebrisField | null } | null> => {
|
||||||
// 执行战斗(使用 Worker 进行异步计算)
|
// 执行战斗(使用 Worker 进行异步计算)
|
||||||
|
const gameStore = useGameStore()
|
||||||
const battleResult = await battleLogic.simulateBattle(
|
const battleResult = await battleLogic.simulateBattle(
|
||||||
mission.fleet, // NPC舰队
|
mission.fleet, // NPC舰队
|
||||||
targetPlanet.fleet, // 玩家舰队
|
targetPlanet.fleet, // 玩家舰队
|
||||||
@@ -258,7 +262,8 @@ export const processNPCAttackArrival = async (
|
|||||||
{} as Record<OfficerType, Officer>, // NPC没有军官系统
|
{} as Record<OfficerType, Officer>, // NPC没有军官系统
|
||||||
defender.officers || ({} as Record<OfficerType, Officer>), // 玩家军官
|
defender.officers || ({} as Record<OfficerType, Officer>), // 玩家军官
|
||||||
npc.technologies, // NPC科技等级
|
npc.technologies, // NPC科技等级
|
||||||
defender.technologies // 玩家科技等级
|
defender.technologies, // 玩家科技等级
|
||||||
|
gameStore.battleToFinish
|
||||||
)
|
)
|
||||||
|
|
||||||
// 更新战斗报告ID和参与者信息
|
// 更新战斗报告ID和参与者信息
|
||||||
@@ -721,12 +726,12 @@ export const processRecycleArrival = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 远征事件类型
|
* 探险事件类型
|
||||||
*/
|
*/
|
||||||
export type ExpeditionEventType = 'resources' | 'darkMatter' | 'fleet' | 'nothing' | 'pirates' | 'aliens'
|
export type ExpeditionEventType = 'resources' | 'darkMatter' | 'fleet' | 'nothing' | 'pirates' | 'aliens'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 远征结果
|
* 探险结果
|
||||||
*/
|
*/
|
||||||
export interface ExpeditionResult {
|
export interface ExpeditionResult {
|
||||||
eventType: ExpeditionEventType
|
eventType: ExpeditionEventType
|
||||||
@@ -737,8 +742,8 @@ export interface ExpeditionResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理远征任务到达
|
* 处理探险任务到达
|
||||||
* 远征任务会随机触发各种事件,基于探险区域配置
|
* 探险任务会随机触发各种事件,基于探险区域配置
|
||||||
*/
|
*/
|
||||||
export const processExpeditionArrival = (mission: FleetMission): ExpeditionResult => {
|
export const processExpeditionArrival = (mission: FleetMission): ExpeditionResult => {
|
||||||
// 获取探险区域配置,默认为近空区域
|
// 获取探险区域配置,默认为近空区域
|
||||||
@@ -968,9 +973,18 @@ export interface DestroyResult {
|
|||||||
deathstarDestructionChance: number // 死星反向销毁概率
|
deathstarDestructionChance: number // 死星反向销毁概率
|
||||||
isMoon: boolean // 目标是否为月球
|
isMoon: boolean // 目标是否为月球
|
||||||
failReason?: DestroyFailReason // 失败原因
|
failReason?: DestroyFailReason // 失败原因
|
||||||
|
battleResult?: BattleResult // 战斗结果(如果发生了战斗)
|
||||||
|
debrisField?: DebrisField // 战斗产生的残骸场
|
||||||
|
moon?: Planet // 战斗产生的月球
|
||||||
}
|
}
|
||||||
|
|
||||||
export const processDestroyArrival = (mission: FleetMission, targetPlanet: Planet | undefined, attacker: Player): DestroyResult => {
|
export const processDestroyArrival = async (
|
||||||
|
mission: FleetMission,
|
||||||
|
targetPlanet: Planet | undefined,
|
||||||
|
attacker: Player,
|
||||||
|
defender: Player | null,
|
||||||
|
allPlanets: Planet[]
|
||||||
|
): Promise<DestroyResult> => {
|
||||||
if (!targetPlanet) {
|
if (!targetPlanet) {
|
||||||
mission.status = 'returning'
|
mission.status = 'returning'
|
||||||
return {
|
return {
|
||||||
@@ -995,8 +1009,8 @@ export const processDestroyArrival = (mission: FleetMission, targetPlanet: Plane
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有死星
|
// 检查是否有死星
|
||||||
const deathstarCount = mission.fleet[ShipType.Deathstar] || 0
|
const initialDeathstarCount = mission.fleet[ShipType.Deathstar] || 0
|
||||||
if (deathstarCount === 0) {
|
if (initialDeathstarCount === 0) {
|
||||||
mission.status = 'returning'
|
mission.status = 'returning'
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -1008,10 +1022,113 @@ export const processDestroyArrival = (mission: FleetMission, targetPlanet: Plane
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查目标是否有防御力量(舰队或防御设施)
|
||||||
|
const hasDefenderFleet = Object.values(targetPlanet.fleet || {}).some(count => count > 0)
|
||||||
|
const hasDefense = Object.values(targetPlanet.defense || {}).some(count => count > 0)
|
||||||
|
const needsBattle = hasDefenderFleet || hasDefense
|
||||||
|
|
||||||
|
let battleResult: BattleResult | undefined
|
||||||
|
let debrisField: DebrisField | undefined
|
||||||
|
let newMoon: Planet | undefined
|
||||||
|
let survivingDeathstars = initialDeathstarCount
|
||||||
|
|
||||||
|
// 如果目标有防御力量,先进行战斗
|
||||||
|
if (needsBattle) {
|
||||||
|
const gameStore = useGameStore()
|
||||||
|
|
||||||
|
// 执行战斗
|
||||||
|
battleResult = await battleLogic.simulateBattle(
|
||||||
|
mission.fleet,
|
||||||
|
targetPlanet.fleet,
|
||||||
|
targetPlanet.defense,
|
||||||
|
targetPlanet.resources,
|
||||||
|
attacker.officers,
|
||||||
|
defender?.officers || ({} as Record<OfficerType, Officer>),
|
||||||
|
attacker.technologies,
|
||||||
|
defender?.technologies || ({} as Record<TechnologyType, number>),
|
||||||
|
gameStore.battleToFinish
|
||||||
|
)
|
||||||
|
|
||||||
|
// 更新战斗报告
|
||||||
|
battleResult.id = `battle_${Date.now()}`
|
||||||
|
battleResult.attackerId = attacker.id
|
||||||
|
battleResult.defenderId = targetPlanet.ownerId || 'unknown'
|
||||||
|
battleResult.attackerPlanetId = mission.originPlanetId
|
||||||
|
battleResult.defenderPlanetId = targetPlanet.id
|
||||||
|
|
||||||
|
// 更新舰队 - 计算幸存舰船
|
||||||
|
const survivingFleet: Partial<Fleet> = {}
|
||||||
|
Object.entries(mission.fleet).forEach(([shipType, initialCount]) => {
|
||||||
|
const lost = battleResult!.attackerLosses[shipType as ShipType] || 0
|
||||||
|
const surviving = initialCount - lost
|
||||||
|
if (surviving > 0) {
|
||||||
|
survivingFleet[shipType as ShipType] = surviving
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mission.fleet = survivingFleet
|
||||||
|
|
||||||
|
// 计算存活的死星数量
|
||||||
|
survivingDeathstars = survivingFleet[ShipType.Deathstar] || 0
|
||||||
|
|
||||||
|
// 更新目标星球舰队和防御
|
||||||
|
Object.entries(battleResult.defenderLosses.fleet).forEach(([shipType, lost]) => {
|
||||||
|
const current = targetPlanet.fleet[shipType as ShipType] || 0
|
||||||
|
targetPlanet.fleet[shipType as ShipType] = Math.max(0, current - lost)
|
||||||
|
})
|
||||||
|
Object.entries(battleResult.defenderLosses.defense).forEach(([defenseType, lost]) => {
|
||||||
|
const current = targetPlanet.defense[defenseType as DefenseType] || 0
|
||||||
|
targetPlanet.defense[defenseType as DefenseType] = Math.max(0, current - lost)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算残骸场
|
||||||
|
const debrisResources = battleResult.debrisField
|
||||||
|
if (debrisResources.metal > 0 || debrisResources.crystal > 0) {
|
||||||
|
debrisField = {
|
||||||
|
id: `debris_${targetPlanet.position.galaxy}_${targetPlanet.position.system}_${targetPlanet.position.position}`,
|
||||||
|
position: { ...targetPlanet.position },
|
||||||
|
resources: {
|
||||||
|
metal: debrisResources.metal,
|
||||||
|
crystal: debrisResources.crystal
|
||||||
|
},
|
||||||
|
createdAt: Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否生成月球(只有非月球位置才能生成月球)
|
||||||
|
if (!targetPlanet.isMoon && debrisField) {
|
||||||
|
const moonExists = moonLogic.hasMoonAtPosition(allPlanets, targetPlanet.position)
|
||||||
|
if (!moonExists) {
|
||||||
|
newMoon =
|
||||||
|
moonLogic.tryGenerateMoon(
|
||||||
|
{ ...debrisResources, deuterium: 0, darkMatter: 0, energy: 0 },
|
||||||
|
targetPlanet.position,
|
||||||
|
targetPlanet.id,
|
||||||
|
targetPlanet.ownerId || attacker.id
|
||||||
|
) || undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果攻击方失败或没有存活的死星,直接返回
|
||||||
|
if (battleResult.winner === 'defender' || survivingDeathstars === 0) {
|
||||||
|
mission.status = 'returning'
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
destructionChance: 0,
|
||||||
|
deathstarsLost: initialDeathstarCount > 0 && survivingDeathstars === 0,
|
||||||
|
deathstarDestructionChance: 0,
|
||||||
|
isMoon: targetPlanet.isMoon || false,
|
||||||
|
failReason: survivingDeathstars === 0 ? 'noDeathstar' : 'chanceFailed',
|
||||||
|
battleResult,
|
||||||
|
debrisField,
|
||||||
|
moon: newMoon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 根据目标类型使用不同的销毁逻辑
|
// 根据目标类型使用不同的销毁逻辑
|
||||||
if (targetPlanet.isMoon) {
|
if (targetPlanet.isMoon) {
|
||||||
// 月球销毁使用 OGame 公式
|
// 月球销毁使用 OGame 公式
|
||||||
const result = moonLogic.tryDestroyMoon(targetPlanet, deathstarCount)
|
const result = moonLogic.tryDestroyMoon(targetPlanet, survivingDeathstars)
|
||||||
|
|
||||||
// 如果死星被反向销毁,从任务舰队中移除
|
// 如果死星被反向销毁,从任务舰队中移除
|
||||||
if (result.deathstarsDestroyed) {
|
if (result.deathstarsDestroyed) {
|
||||||
@@ -1027,13 +1144,16 @@ export const processDestroyArrival = (mission: FleetMission, targetPlanet: Plane
|
|||||||
deathstarsLost: result.deathstarsDestroyed,
|
deathstarsLost: result.deathstarsDestroyed,
|
||||||
deathstarDestructionChance: result.deathstarDestructionChance,
|
deathstarDestructionChance: result.deathstarDestructionChance,
|
||||||
isMoon: true,
|
isMoon: true,
|
||||||
failReason: result.moonDestroyed ? undefined : 'chanceFailed'
|
failReason: result.moonDestroyed ? undefined : 'chanceFailed',
|
||||||
|
battleResult,
|
||||||
|
debrisField,
|
||||||
|
moon: newMoon
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 行星销毁使用原有逻辑
|
// 行星销毁使用原有逻辑(基于存活的死星数量)
|
||||||
const planetaryShieldCount = targetPlanet.defense[DefenseType.PlanetaryShield] || 0
|
const planetaryShieldCount = targetPlanet.defense[DefenseType.PlanetaryShield] || 0
|
||||||
const defensePower = calculatePlanetDefensePower(targetPlanet.fleet, targetPlanet.defense)
|
const defensePower = calculatePlanetDefensePower(targetPlanet.fleet, targetPlanet.defense)
|
||||||
const destructionChance = calculateDestructionChance(deathstarCount, planetaryShieldCount, defensePower)
|
const destructionChance = calculateDestructionChance(survivingDeathstars, planetaryShieldCount, defensePower)
|
||||||
|
|
||||||
const randomValue = Math.random() * 100
|
const randomValue = Math.random() * 100
|
||||||
const success = randomValue < destructionChance
|
const success = randomValue < destructionChance
|
||||||
@@ -1047,7 +1167,10 @@ export const processDestroyArrival = (mission: FleetMission, targetPlanet: Plane
|
|||||||
deathstarsLost: false,
|
deathstarsLost: false,
|
||||||
deathstarDestructionChance: 0,
|
deathstarDestructionChance: 0,
|
||||||
isMoon: false,
|
isMoon: false,
|
||||||
failReason: success ? undefined : 'chanceFailed'
|
failReason: success ? undefined : 'chanceFailed',
|
||||||
|
battleResult,
|
||||||
|
debrisField,
|
||||||
|
moon: newMoon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1200,8 +1323,40 @@ export const updateFleetMissions = async (
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case MissionType.Destroy:
|
case MissionType.Destroy: {
|
||||||
const destroyResult = processDestroyArrival(mission, targetPlanet, attacker)
|
const destroyResult = await processDestroyArrival(mission, targetPlanet, attacker, defender, allPlanets)
|
||||||
|
|
||||||
|
// 处理战斗报告
|
||||||
|
if (destroyResult.battleResult) {
|
||||||
|
battleReports.push(destroyResult.battleResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理新生成的月球
|
||||||
|
if (destroyResult.moon) {
|
||||||
|
newMoons.push(destroyResult.moon)
|
||||||
|
const moonKey = `${destroyResult.moon.position.galaxy}:${destroyResult.moon.position.system}:${destroyResult.moon.position.position}`
|
||||||
|
planets.set(moonKey, destroyResult.moon)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理残骸场
|
||||||
|
if (destroyResult.debrisField) {
|
||||||
|
const existingDebris = debrisFields.get(destroyResult.debrisField.id)
|
||||||
|
if (existingDebris) {
|
||||||
|
const updatedDebris: DebrisField = {
|
||||||
|
...existingDebris,
|
||||||
|
resources: {
|
||||||
|
metal: existingDebris.resources.metal + destroyResult.debrisField.resources.metal,
|
||||||
|
crystal: existingDebris.resources.crystal + destroyResult.debrisField.resources.crystal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debrisFields.set(destroyResult.debrisField.id, updatedDebris)
|
||||||
|
updatedDebrisFields.push(updatedDebris)
|
||||||
|
} else {
|
||||||
|
debrisFields.set(destroyResult.debrisField.id, destroyResult.debrisField)
|
||||||
|
newDebrisFields.push(destroyResult.debrisField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (destroyResult.success && destroyResult.planetId) {
|
if (destroyResult.success && destroyResult.planetId) {
|
||||||
// 星球被摧毁
|
// 星球被摧毁
|
||||||
destroyedPlanetIds.push(destroyResult.planetId)
|
destroyedPlanetIds.push(destroyResult.planetId)
|
||||||
@@ -1217,6 +1372,7 @@ export const updateFleetMissions = async (
|
|||||||
planets.delete(targetKey)
|
planets.delete(targetKey)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,11 +150,20 @@ export const processGameUpdate = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新所有星球资源(直接同步计算,避免 Worker 通信开销)
|
// 更新所有星球资源(直接同步计算,避免 Worker 通信开销)
|
||||||
|
// 获取采矿技术等级(用于矿脉恢复上限计算)
|
||||||
|
const miningTechLevel = player.technologies[TechnologyType.MiningTechnology] || 0
|
||||||
|
// 获取资源研究科技等级
|
||||||
|
const techBonuses = {
|
||||||
|
mineralResearchLevel: player.technologies[TechnologyType.MineralResearch] || 0,
|
||||||
|
crystalResearchLevel: player.technologies[TechnologyType.CrystalResearch] || 0,
|
||||||
|
fuelResearchLevel: player.technologies[TechnologyType.FuelResearch] || 0
|
||||||
|
}
|
||||||
|
|
||||||
player.planets.forEach(planet => {
|
player.planets.forEach(planet => {
|
||||||
// 计算更新前的资源(用于计算生产量)
|
// 计算更新前的资源(用于计算生产量)
|
||||||
const resourcesBefore = { ...planet.resources }
|
const resourcesBefore = { ...planet.resources }
|
||||||
|
|
||||||
resourceLogic.updatePlanetResources(planet, now, bonuses, gameSpeed)
|
resourceLogic.updatePlanetResources(planet, now, bonuses, gameSpeed, miningTechLevel, techBonuses)
|
||||||
|
|
||||||
// 追踪资源生产统计
|
// 追踪资源生产统计
|
||||||
if (player.achievementStats) {
|
if (player.achievementStats) {
|
||||||
@@ -209,6 +218,10 @@ export const processGameUpdate = (
|
|||||||
onCompleted
|
onCompleted
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 重要:先更新 player.researchQueue,再处理等待队列
|
||||||
|
// 这样等待队列处理时可以正确 push 新项目到队列中
|
||||||
|
player.researchQueue = updatedResearchQueue
|
||||||
|
|
||||||
// 处理等待队列自动执行
|
// 处理等待队列自动执行
|
||||||
const waitingResult = waitingQueueLogic.processAllWaitingQueues(player, now)
|
const waitingResult = waitingQueueLogic.processAllWaitingQueues(player, now)
|
||||||
// 如果有等待队列项被执行,可以在这里添加通知(可选)
|
// 如果有等待队列项被执行,可以在这里添加通知(可选)
|
||||||
@@ -244,8 +257,9 @@ export const processGameUpdate = (
|
|||||||
onUnlock(allUnlockedItems)
|
onUnlock(allUnlockedItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回当前的研究队列(包含等待队列处理后添加的新项)
|
||||||
return {
|
return {
|
||||||
updatedResearchQueue
|
updatedResearchQueue: player.researchQueue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ import type {
|
|||||||
AllyDefenseNotification,
|
AllyDefenseNotification,
|
||||||
AttitudeChangeNotification
|
AttitudeChangeNotification
|
||||||
} from '@/types/game'
|
} from '@/types/game'
|
||||||
import { MissionType, ShipType, TechnologyType, RelationStatus, NPCAIType } from '@/types/game'
|
import { MissionType, ShipType, DefenseType, TechnologyType, RelationStatus, NPCAIType } from '@/types/game'
|
||||||
|
|
||||||
// 重新导出类型供外部使用
|
// 重新导出类型供外部使用
|
||||||
export type { TradeOffer, IntelReport, JointAttackInvite, IntelType, AidNotification, AllyDefenseNotification, AttitudeChangeNotification }
|
export type { TradeOffer, IntelReport, JointAttackInvite, IntelType, AidNotification, AllyDefenseNotification, AttitudeChangeNotification }
|
||||||
import * as fleetLogic from './fleetLogic'
|
import * as fleetLogic from './fleetLogic'
|
||||||
import * as diplomaticLogic from './diplomaticLogic'
|
import * as diplomaticLogic from './diplomaticLogic'
|
||||||
import * as resourceLogic from './resourceLogic'
|
import * as resourceLogic from './resourceLogic'
|
||||||
import { DIPLOMATIC_CONFIG, SHIPS } from '@/config/gameConfig'
|
import { DIPLOMATIC_CONFIG, SHIPS, DEFENSES } from '@/config/gameConfig'
|
||||||
|
|
||||||
// ========== 敌对NPC增强行为类型定义 ==========
|
// ========== 敌对NPC增强行为类型定义 ==========
|
||||||
|
|
||||||
@@ -908,11 +908,69 @@ export const processNPCSpyArrival = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 决定NPC攻击舰队组成
|
* 计算舰队的战斗力
|
||||||
* 基于侦查报告和NPC实力
|
* 使用 attack + shield + armor 作为单位战斗力
|
||||||
*/
|
*/
|
||||||
const decideAttackFleet = (_npc: NPC, npcPlanet: Planet, _spyReport: SpyReport, config: DynamicBehaviorConfig): Partial<Fleet> | null => {
|
const calculateFleetCombatPower = (fleet: Partial<Fleet> | undefined): number => {
|
||||||
// 简单策略:派出一定比例的可用舰队
|
if (!fleet) return 0
|
||||||
|
let power = 0
|
||||||
|
for (const [shipType, count] of Object.entries(fleet)) {
|
||||||
|
if (count && count > 0) {
|
||||||
|
const config = SHIPS[shipType as ShipType]
|
||||||
|
if (config) {
|
||||||
|
// 战斗力 = (攻击力 + 护盾 + 装甲) * 数量
|
||||||
|
power += (config.attack + config.shield + config.armor) * count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return power
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算防御设施的战斗力
|
||||||
|
*/
|
||||||
|
const calculateDefenseCombatPower = (defense: Partial<Record<DefenseType, number>> | undefined): number => {
|
||||||
|
if (!defense) return 0
|
||||||
|
let power = 0
|
||||||
|
for (const [defenseType, count] of Object.entries(defense)) {
|
||||||
|
if (count && count > 0) {
|
||||||
|
const config = DEFENSES[defenseType as DefenseType]
|
||||||
|
if (config) {
|
||||||
|
// 战斗力 = (攻击力 + 护盾 + 装甲) * 数量
|
||||||
|
power += (config.attack + config.shield + config.armor) * count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return power
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评估NPC是否能赢得战斗
|
||||||
|
* 返回NPC战斗力与敌方战斗力的比值
|
||||||
|
*/
|
||||||
|
export const evaluateWinProbability = (npcFleet: Partial<Fleet>, spyReport: SpyReport): number => {
|
||||||
|
const npcPower = calculateFleetCombatPower(npcFleet)
|
||||||
|
const enemyFleetPower = calculateFleetCombatPower(spyReport.fleet)
|
||||||
|
const enemyDefensePower = calculateDefenseCombatPower(spyReport.defense)
|
||||||
|
const totalEnemyPower = enemyFleetPower + enemyDefensePower
|
||||||
|
|
||||||
|
// 如果敌方没有防御力,返回无限大(可以攻击)
|
||||||
|
if (totalEnemyPower === 0) return Infinity
|
||||||
|
|
||||||
|
return npcPower / totalEnemyPower
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 决定NPC攻击舰队组成
|
||||||
|
* 基于侦查报告和NPC实力,只有当有合理胜算时才攻击
|
||||||
|
*/
|
||||||
|
const decideAttackFleet = (_npc: NPC, npcPlanet: Planet, spyReport: SpyReport, config: DynamicBehaviorConfig): Partial<Fleet> | null => {
|
||||||
|
// 计算敌方总战斗力
|
||||||
|
const enemyFleetPower = calculateFleetCombatPower(spyReport.fleet)
|
||||||
|
const enemyDefensePower = calculateDefenseCombatPower(spyReport.defense)
|
||||||
|
const totalEnemyPower = enemyFleetPower + enemyDefensePower
|
||||||
|
|
||||||
|
// 先计算可用的攻击舰队
|
||||||
const attackFleet: Partial<Fleet> = {}
|
const attackFleet: Partial<Fleet> = {}
|
||||||
let hasShips = false
|
let hasShips = false
|
||||||
|
|
||||||
@@ -939,7 +997,22 @@ const decideAttackFleet = (_npc: NPC, npcPlanet: Planet, _spyReport: SpyReport,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasShips ? attackFleet : null
|
if (!hasShips) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算NPC舰队战斗力
|
||||||
|
const npcPower = calculateFleetCombatPower(attackFleet)
|
||||||
|
|
||||||
|
// 如果敌方有防御力,检查是否有足够的胜算
|
||||||
|
// 要求NPC战斗力至少是敌方的1.2倍才会发动攻击(确保有较大胜算)
|
||||||
|
const MIN_WIN_RATIO = 1.2
|
||||||
|
if (totalEnemyPower > 0 && npcPower < totalEnemyPower * MIN_WIN_RATIO) {
|
||||||
|
// NPC战斗力不足,不发动攻击
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return attackFleet
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1520,6 +1593,25 @@ export const createNPCRecycleMission = (npc: NPC, debris: DebrisField, player: P
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查残骸位置是否有玩家星球
|
||||||
|
const playerPlanetAtDebris = allPlanets.find(
|
||||||
|
p =>
|
||||||
|
p.ownerId === player.id &&
|
||||||
|
p.position.galaxy === debris.position.galaxy &&
|
||||||
|
p.position.system === debris.position.system &&
|
||||||
|
p.position.position === debris.position.position
|
||||||
|
)
|
||||||
|
|
||||||
|
// 如果残骸在玩家星球位置,检查玩家是否有防御
|
||||||
|
// 如果玩家有防御,NPC不应该只派回收船去送死(会被击毁产生更多残骸,形成恶性循环)
|
||||||
|
if (playerPlanetAtDebris) {
|
||||||
|
// 计算玩家星球的总防御数量
|
||||||
|
const totalDefense = Object.values(playerPlanetAtDebris.defense || {}).reduce((sum, count) => sum + (count || 0), 0)
|
||||||
|
if (totalDefense > 0) {
|
||||||
|
return null // 玩家有防御,不派纯回收船去送死
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 检查NPC是否有回收船
|
// 检查NPC是否有回收船
|
||||||
const recyclers = closestPlanet.fleet[ShipType.Recycler] || 0
|
const recyclers = closestPlanet.fleet[ShipType.Recycler] || 0
|
||||||
if (recyclers === 0) {
|
if (recyclers === 0) {
|
||||||
@@ -1566,15 +1658,7 @@ export const createNPCRecycleMission = (npc: NPC, debris: DebrisField, player: P
|
|||||||
}
|
}
|
||||||
npc.fleetMissions.push(mission)
|
npc.fleetMissions.push(mission)
|
||||||
|
|
||||||
// 检查残骸位置是否有玩家星球,如果有则发送警告
|
// 如果残骸在玩家星球位置,发送警告(非敌对NPC才会执行到这里)
|
||||||
const playerPlanetAtDebris = allPlanets.find(
|
|
||||||
p =>
|
|
||||||
p.ownerId === player.id &&
|
|
||||||
p.position.galaxy === debris.position.galaxy &&
|
|
||||||
p.position.system === debris.position.system &&
|
|
||||||
p.position.position === debris.position.position
|
|
||||||
)
|
|
||||||
|
|
||||||
if (playerPlanetAtDebris) {
|
if (playerPlanetAtDebris) {
|
||||||
// 创建即将到来的舰队警告(非敌对)
|
// 创建即将到来的舰队警告(非敌对)
|
||||||
const alert = createIncomingFleetAlert(mission, npc, playerPlanetAtDebris)
|
const alert = createIncomingFleetAlert(mission, npc, playerPlanetAtDebris)
|
||||||
|
|||||||
@@ -266,12 +266,12 @@ export const formatDepletionTime = (hours: number): string => {
|
|||||||
/**
|
/**
|
||||||
* 计算地质研究站带来的恢复速率加成
|
* 计算地质研究站带来的恢复速率加成
|
||||||
* @param geoStationLevel 地质研究站等级
|
* @param geoStationLevel 地质研究站等级
|
||||||
* @returns 恢复速率倍数(1 + 等级 * 0.5)
|
* @returns 恢复速率倍数(1 + 等级 * 0.1)
|
||||||
*/
|
*/
|
||||||
export const calculateGeoStationBonus = (geoStationLevel: number): number => {
|
export const calculateGeoStationBonus = (geoStationLevel: number): number => {
|
||||||
// 每级地质研究站增加50%恢复速度
|
// 每级地质研究站增加50%恢复速度
|
||||||
// 0级 = 1倍, 1级 = 1.5倍, 2级 = 2倍, ..., 10级 = 6倍
|
// 0级 = 1倍, 1级 = 1.5倍, 2级 = 2倍, ..., 10级 = 6倍
|
||||||
return 1 + geoStationLevel * 0.5
|
return 1 + geoStationLevel * 0.1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -280,8 +280,16 @@ export const calculateGeoStationBonus = (geoStationLevel: number): number => {
|
|||||||
* @param deposits 矿脉储量对象
|
* @param deposits 矿脉储量对象
|
||||||
* @param hoursElapsed 经过的小时数
|
* @param hoursElapsed 经过的小时数
|
||||||
* @param geoStationLevel 地质研究站等级(可选,影响恢复速度)
|
* @param geoStationLevel 地质研究站等级(可选,影响恢复速度)
|
||||||
|
* @param deepDrillingLevel 深层钻探设施等级(可选,影响恢复上限)
|
||||||
|
* @param miningTechLevel 采矿技术等级(可选,影响恢复上限)
|
||||||
*/
|
*/
|
||||||
export const regenerateDeposits = (deposits: OreDeposits, hoursElapsed: number, geoStationLevel: number = 0): void => {
|
export const regenerateDeposits = (
|
||||||
|
deposits: OreDeposits,
|
||||||
|
hoursElapsed: number,
|
||||||
|
geoStationLevel: number = 0,
|
||||||
|
deepDrillingLevel: number = 0,
|
||||||
|
miningTechLevel: number = 0
|
||||||
|
): void => {
|
||||||
const { REGENERATION } = ORE_DEPOSIT_CONFIG
|
const { REGENERATION } = ORE_DEPOSIT_CONFIG
|
||||||
|
|
||||||
if (!REGENERATION.ENABLED || hoursElapsed <= 0) return
|
if (!REGENERATION.ENABLED || hoursElapsed <= 0) return
|
||||||
@@ -289,32 +297,36 @@ export const regenerateDeposits = (deposits: OreDeposits, hoursElapsed: number,
|
|||||||
// 计算地质研究站加成
|
// 计算地质研究站加成
|
||||||
const geoBonus = calculateGeoStationBonus(geoStationLevel)
|
const geoBonus = calculateGeoStationBonus(geoStationLevel)
|
||||||
|
|
||||||
// 动态计算初始储量上限
|
// 计算带有建筑和科技加成的储量上限
|
||||||
const initialDeposits = calculateInitialDeposits(deposits.position)
|
const enhancedDeposits = calculateEnhancedDeposits(deposits.position, deepDrillingLevel, miningTechLevel)
|
||||||
|
|
||||||
// 计算恢复量(基于初始储量的百分比,乘以地质研究站加成)
|
// 计算恢复量(基于增强后储量的百分比,乘以地质研究站加成)
|
||||||
const regenRate = REGENERATION.RATE_PER_HOUR * hoursElapsed * geoBonus
|
const regenRate = REGENERATION.RATE_PER_HOUR * hoursElapsed * geoBonus
|
||||||
const maxPercentage = REGENERATION.MAX_PERCENTAGE
|
const maxPercentage = REGENERATION.MAX_PERCENTAGE
|
||||||
|
|
||||||
// 恢复每种资源
|
// 恢复每种资源
|
||||||
const metalRegen = initialDeposits.metal * regenRate
|
const metalRegen = enhancedDeposits.metal * regenRate
|
||||||
const crystalRegen = initialDeposits.crystal * regenRate
|
const crystalRegen = enhancedDeposits.crystal * regenRate
|
||||||
const deuteriumRegen = initialDeposits.deuterium * regenRate
|
const deuteriumRegen = enhancedDeposits.deuterium * regenRate
|
||||||
|
|
||||||
// 添加恢复量,但不超过初始储量的最大百分比
|
// 添加恢复量,但不超过增强后储量的最大百分比
|
||||||
deposits.metal = Math.min(initialDeposits.metal * maxPercentage, deposits.metal + metalRegen)
|
deposits.metal = Math.min(enhancedDeposits.metal * maxPercentage, deposits.metal + metalRegen)
|
||||||
deposits.crystal = Math.min(initialDeposits.crystal * maxPercentage, deposits.crystal + crystalRegen)
|
deposits.crystal = Math.min(enhancedDeposits.crystal * maxPercentage, deposits.crystal + crystalRegen)
|
||||||
deposits.deuterium = Math.min(initialDeposits.deuterium * maxPercentage, deposits.deuterium + deuteriumRegen)
|
deposits.deuterium = Math.min(enhancedDeposits.deuterium * maxPercentage, deposits.deuterium + deuteriumRegen)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取矿脉恢复状态信息
|
* 获取矿脉恢复状态信息
|
||||||
* @param deposits 矿脉储量
|
* @param deposits 矿脉储量
|
||||||
* @param geoStationLevel 地质研究站等级(可选,影响恢复时间计算)
|
* @param geoStationLevel 地质研究站等级(可选,影响恢复时间计算)
|
||||||
|
* @param deepDrillingLevel 深层钻探设施等级(可选,影响恢复上限)
|
||||||
|
* @param miningTechLevel 采矿技术等级(可选,影响恢复上限)
|
||||||
*/
|
*/
|
||||||
export const getRegenerationInfo = (
|
export const getRegenerationInfo = (
|
||||||
deposits: OreDeposits | undefined,
|
deposits: OreDeposits | undefined,
|
||||||
geoStationLevel: number = 0
|
geoStationLevel: number = 0,
|
||||||
|
deepDrillingLevel: number = 0,
|
||||||
|
miningTechLevel: number = 0
|
||||||
): {
|
): {
|
||||||
metalRecovering: boolean
|
metalRecovering: boolean
|
||||||
crystalRecovering: boolean
|
crystalRecovering: boolean
|
||||||
@@ -338,26 +350,26 @@ export const getRegenerationInfo = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动态计算初始储量上限
|
// 计算带有建筑和科技加成的储量上限
|
||||||
const initialDeposits = calculateInitialDeposits(deposits.position)
|
const enhancedDeposits = calculateEnhancedDeposits(deposits.position, deepDrillingLevel, miningTechLevel)
|
||||||
|
|
||||||
const { REGENERATION } = ORE_DEPOSIT_CONFIG
|
const { REGENERATION } = ORE_DEPOSIT_CONFIG
|
||||||
const maxPercentage = REGENERATION.MAX_PERCENTAGE
|
const maxPercentage = REGENERATION.MAX_PERCENTAGE
|
||||||
// 实际恢复速率 = 基础速率 * 地质研究站加成
|
// 实际恢复速率 = 基础速率 * 地质研究站加成
|
||||||
const ratePerHour = REGENERATION.RATE_PER_HOUR * geoBonus
|
const ratePerHour = REGENERATION.RATE_PER_HOUR * geoBonus
|
||||||
|
|
||||||
const metalMax = initialDeposits.metal * maxPercentage
|
const metalMax = enhancedDeposits.metal * maxPercentage
|
||||||
const crystalMax = initialDeposits.crystal * maxPercentage
|
const crystalMax = enhancedDeposits.crystal * maxPercentage
|
||||||
const deuteriumMax = initialDeposits.deuterium * maxPercentage
|
const deuteriumMax = enhancedDeposits.deuterium * maxPercentage
|
||||||
|
|
||||||
const metalRecovering = deposits.metal < metalMax
|
const metalRecovering = deposits.metal < metalMax
|
||||||
const crystalRecovering = deposits.crystal < crystalMax
|
const crystalRecovering = deposits.crystal < crystalMax
|
||||||
const deuteriumRecovering = deposits.deuterium < deuteriumMax
|
const deuteriumRecovering = deposits.deuterium < deuteriumMax
|
||||||
|
|
||||||
// 计算恢复到满需要的小时数(考虑地质研究站加成)
|
// 计算恢复到满需要的小时数(考虑地质研究站加成)
|
||||||
const hoursToFullMetal = metalRecovering ? (metalMax - deposits.metal) / (initialDeposits.metal * ratePerHour) : 0
|
const hoursToFullMetal = metalRecovering ? (metalMax - deposits.metal) / (enhancedDeposits.metal * ratePerHour) : 0
|
||||||
const hoursToFullCrystal = crystalRecovering ? (crystalMax - deposits.crystal) / (initialDeposits.crystal * ratePerHour) : 0
|
const hoursToFullCrystal = crystalRecovering ? (crystalMax - deposits.crystal) / (enhancedDeposits.crystal * ratePerHour) : 0
|
||||||
const hoursToFullDeuterium = deuteriumRecovering ? (deuteriumMax - deposits.deuterium) / (initialDeposits.deuterium * ratePerHour) : 0
|
const hoursToFullDeuterium = deuteriumRecovering ? (deuteriumMax - deposits.deuterium) / (enhancedDeposits.deuterium * ratePerHour) : 0
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metalRecovering,
|
metalRecovering,
|
||||||
|
|||||||
@@ -320,3 +320,63 @@ export const calculateDeuteriumTemperatureBonus = (maxTemperature: number): numb
|
|||||||
// 返回乘数,例如:-40°C时返回1.52,+100°C时返回0.96
|
// 返回乘数,例如:-40°C时返回1.52,+100°C时返回0.96
|
||||||
return 1.36 - 0.004 * maxTemperature
|
return 1.36 - 0.004 * maxTemperature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否可以放弃殖民地
|
||||||
|
* @param planets 玩家所有星球
|
||||||
|
* @param planetId 要放弃的星球ID
|
||||||
|
* @returns 是否可以放弃及原因
|
||||||
|
*/
|
||||||
|
export const canAbandonColony = (
|
||||||
|
planets: Planet[],
|
||||||
|
planetId: string
|
||||||
|
): { canAbandon: boolean; reason?: string } => {
|
||||||
|
const planet = planets.find(p => p.id === planetId)
|
||||||
|
if (!planet) {
|
||||||
|
return { canAbandon: false, reason: 'errors.planetNotFound' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主星(第一个非月球星球)不能放弃
|
||||||
|
const mainPlanet = planets.find(p => !p.isMoon)
|
||||||
|
if (mainPlanet && mainPlanet.id === planetId) {
|
||||||
|
return { canAbandon: false, reason: 'errors.cannotAbandonHomePlanet' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有正在进行的建造队列
|
||||||
|
if (planet.buildQueue.length > 0) {
|
||||||
|
return { canAbandon: false, reason: 'errors.hasBuildQueue' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有舰队在该星球
|
||||||
|
const hasFleet = Object.values(planet.fleet).some(count => count > 0)
|
||||||
|
if (hasFleet) {
|
||||||
|
return { canAbandon: false, reason: 'errors.hasFleetOnPlanet' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有防御在该星球
|
||||||
|
const hasDefense = Object.values(planet.defense).some(count => count > 0)
|
||||||
|
if (hasDefense) {
|
||||||
|
return { canAbandon: false, reason: 'errors.hasDefenseOnPlanet' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { canAbandon: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 放弃殖民地
|
||||||
|
* @param planets 玩家所有星球
|
||||||
|
* @param planetId 要放弃的星球ID
|
||||||
|
* @returns 放弃后的星球列表
|
||||||
|
*/
|
||||||
|
export const abandonColony = (planets: Planet[], planetId: string): Planet[] => {
|
||||||
|
const planet = planets.find(p => p.id === planetId)
|
||||||
|
if (!planet) return planets
|
||||||
|
|
||||||
|
// 如果放弃的是行星,同时删除其月球
|
||||||
|
if (!planet.isMoon) {
|
||||||
|
return planets.filter(p => p.id !== planetId && p.parentPlanetId !== planetId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果放弃的是月球,只删除月球
|
||||||
|
return planets.filter(p => p.id !== planetId)
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,16 +90,27 @@ export const checkRequirements = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算星球的资源产量(包含军官加成)
|
* 计算星球的资源产量(包含军官加成和科技加成)
|
||||||
* @param planet 星球对象
|
* @param planet 星球对象
|
||||||
* @param officers 玩家的军官对象
|
* @param officers 玩家的军官对象
|
||||||
|
* @param resourceSpeed 游戏速度
|
||||||
|
* @param techBonuses 科技加成(可选,矿物研究/晶体研究/燃料研究)
|
||||||
* @returns 每小时各类资源的产量
|
* @returns 每小时各类资源的产量
|
||||||
*/
|
*/
|
||||||
export const getResourceProduction = (planet: Planet, officers: Record<OfficerType, Officer>, resourceSpeed: number = 1): Resources => {
|
export const getResourceProduction = (
|
||||||
|
planet: Planet,
|
||||||
|
officers: Record<OfficerType, Officer>,
|
||||||
|
resourceSpeed: number = 1,
|
||||||
|
techBonuses?: {
|
||||||
|
mineralResearchLevel?: number
|
||||||
|
crystalResearchLevel?: number
|
||||||
|
fuelResearchLevel?: number
|
||||||
|
}
|
||||||
|
): Resources => {
|
||||||
// 计算当前激活的军官加成
|
// 计算当前激活的军官加成
|
||||||
const bonuses = officerLogic.calculateActiveBonuses(officers, Date.now())
|
const bonuses = officerLogic.calculateActiveBonuses(officers, Date.now())
|
||||||
// 根据建筑等级和军官加成计算资源产量
|
// 根据建筑等级和军官加成计算资源产量
|
||||||
const base = resourceLogic.calculateResourceProduction(planet, bonuses)
|
const base = resourceLogic.calculateResourceProduction(planet, bonuses, techBonuses)
|
||||||
return scaleResources(base, resourceSpeed)
|
return scaleResources(base, resourceSpeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,57 @@
|
|||||||
import type { Resources, BuildQueueItem } from '@/types/game'
|
import type { Resources, BuildQueueItem, Planet } from '@/types/game'
|
||||||
import { TechnologyType, BuildingType } from '@/types/game'
|
import { TechnologyType, BuildingType } from '@/types/game'
|
||||||
import { TECHNOLOGIES } from '@/config/gameConfig'
|
import { TECHNOLOGIES } from '@/config/gameConfig'
|
||||||
import * as pointsLogic from './pointsLogic'
|
import * as pointsLogic from './pointsLogic'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算有效研究实验室等级(考虑星际研究网络)
|
||||||
|
* 星际研究网络允许连接多个星球的研究实验室,取等级最高的N个实验室等级之和
|
||||||
|
* @param planets 玩家的所有星球
|
||||||
|
* @param currentPlanetId 当前星球ID(研究所在的星球)
|
||||||
|
* @param intergalacticResearchNetworkLevel 星际研究网络等级
|
||||||
|
* @returns 有效的研究实验室等级总和
|
||||||
|
*/
|
||||||
|
export const calculateEffectiveLabLevel = (
|
||||||
|
planets: Planet[],
|
||||||
|
currentPlanetId: string,
|
||||||
|
intergalacticResearchNetworkLevel: number
|
||||||
|
): number => {
|
||||||
|
// 收集所有星球的研究实验室等级
|
||||||
|
const labLevels: { planetId: string; level: number }[] = []
|
||||||
|
|
||||||
|
for (const planet of planets) {
|
||||||
|
const labLevel = planet.buildings[BuildingType.ResearchLab] || 0
|
||||||
|
if (labLevel > 0) {
|
||||||
|
labLevels.push({ planetId: planet.id, level: labLevel })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有星际研究网络,只返回当前星球的实验室等级
|
||||||
|
if (intergalacticResearchNetworkLevel === 0) {
|
||||||
|
const currentPlanet = planets.find(p => p.id === currentPlanetId)
|
||||||
|
return currentPlanet?.buildings[BuildingType.ResearchLab] || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按等级降序排序
|
||||||
|
labLevels.sort((a, b) => b.level - a.level)
|
||||||
|
|
||||||
|
// 可连接的实验室数量 = 1 + 星际研究网络等级
|
||||||
|
// 等级1可连接2个实验室,等级2可连接3个,以此类推
|
||||||
|
const maxLabs = 1 + intergalacticResearchNetworkLevel
|
||||||
|
|
||||||
|
// 取前N个实验室的等级之和
|
||||||
|
let totalLevel = 0
|
||||||
|
const count = Math.min(maxLabs, labLevels.length)
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const lab = labLevels[i]
|
||||||
|
if (lab) {
|
||||||
|
totalLevel += lab.level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalLevel
|
||||||
|
}
|
||||||
|
|
||||||
// 用于生成唯一ID的计数器
|
// 用于生成唯一ID的计数器
|
||||||
let researchQueueIdCounter = 0
|
let researchQueueIdCounter = 0
|
||||||
|
|
||||||
@@ -23,31 +72,56 @@ export const calculateTechnologyCost = (techType: TechnologyType, targetLevel: n
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算科技研究时间
|
* 计算科技研究时间
|
||||||
|
* 使用 2moons 公式(调整版):
|
||||||
|
* 1. 成本系数 = Σ (资源^0.3 / 0.003)
|
||||||
|
* 2. 时间(秒) = 成本系数 / ((1 + 研究实验室等级) × 能源加成 × 游戏速度)
|
||||||
* @param techType 科技类型
|
* @param techType 科技类型
|
||||||
* @param currentLevel 当前等级
|
* @param currentLevel 当前等级(要研究的目标等级 = currentLevel + 1)
|
||||||
* @param researchSpeedBonus 军官等提供的研究速度加成百分比
|
* @param researchSpeedBonus 军官等提供的研究速度加成百分比
|
||||||
* @param researchLabLevel 研究实验室等级
|
* @param researchLabLevel 研究实验室等级
|
||||||
* @param energyTechLevel 能源技术等级
|
* @param energyTechLevel 能源技术等级(提供额外加速)
|
||||||
|
* @param gameSpeed 游戏速度(默认1)
|
||||||
|
* @param universityLevel 大学等级(每级减少研究时间8%)
|
||||||
*/
|
*/
|
||||||
export const calculateTechnologyTime = (
|
export const calculateTechnologyTime = (
|
||||||
techType: TechnologyType,
|
techType: TechnologyType,
|
||||||
currentLevel: number,
|
currentLevel: number,
|
||||||
researchSpeedBonus: number = 0,
|
researchSpeedBonus: number = 0,
|
||||||
researchLabLevel: number = 1,
|
researchLabLevel: number = 1,
|
||||||
energyTechLevel: number = 0
|
energyTechLevel: number = 0,
|
||||||
|
gameSpeed: number = 1,
|
||||||
|
universityLevel: number = 0
|
||||||
): number => {
|
): number => {
|
||||||
const config = TECHNOLOGIES[techType]
|
// 目标等级 = 当前等级 + 1
|
||||||
const baseTime = config.baseTime * Math.pow(config.costMultiplier, currentLevel)
|
const targetLevel = currentLevel + 1
|
||||||
|
|
||||||
|
// 计算该等级的成本
|
||||||
|
const cost = calculateTechnologyCost(techType, targetLevel)
|
||||||
|
|
||||||
|
// 2moons 公式:成本系数 = Σ (资源^0.3 / 0.003)
|
||||||
|
let elementCost = 0
|
||||||
|
if (cost.metal > 0) elementCost += Math.pow(cost.metal, 0.3) / 0.003
|
||||||
|
if (cost.crystal > 0) elementCost += Math.pow(cost.crystal, 0.3) / 0.003
|
||||||
|
if (cost.deuterium > 0) elementCost += Math.pow(cost.deuterium, 0.3) / 0.003
|
||||||
|
|
||||||
// 研究实验室和能源技术的加速:研究时间 / (研究实验室等级 × (1 + 能源技术等级))
|
|
||||||
// 研究实验室等级至少为1,防止除以0
|
// 研究实验室等级至少为1,防止除以0
|
||||||
const labLevel = Math.max(1, researchLabLevel)
|
const labLevel = Math.max(1, researchLabLevel)
|
||||||
const techSpeedDivisor = labLevel * (1 + energyTechLevel)
|
|
||||||
|
// 能源技术提供额外加速(每级5%)
|
||||||
|
const energyBonus = 1 + energyTechLevel * 0.05
|
||||||
|
|
||||||
|
// 简化公式:时间(秒) = 成本系数 / (实验室加成 × 能源加成 × 游戏速度)
|
||||||
|
const timeInSeconds = elementCost / ((1 + labLevel) * energyBonus * gameSpeed)
|
||||||
|
|
||||||
// 军官等的百分比加成
|
// 军官等的百分比加成
|
||||||
const speedMultiplier = 1 - researchSpeedBonus / 100
|
const speedMultiplier = 1 - researchSpeedBonus / 100
|
||||||
|
|
||||||
return Math.floor((baseTime / techSpeedDivisor) * speedMultiplier)
|
// 大学加成:每级减少研究时间8%(最高10级=57%减少,因为是乘法叠加)
|
||||||
|
// 使用乘法叠加:(1 - 0.08)^level
|
||||||
|
const universityMultiplier = Math.pow(0.92, universityLevel)
|
||||||
|
|
||||||
|
// 确保最小时间为5秒
|
||||||
|
return Math.max(5, Math.floor(timeInSeconds * speedMultiplier * universityMultiplier))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ export const executeTechnologyResearch = (
|
|||||||
techType: TechnologyType,
|
techType: TechnologyType,
|
||||||
currentLevel: number,
|
currentLevel: number,
|
||||||
officers: Record<OfficerType, Officer>,
|
officers: Record<OfficerType, Officer>,
|
||||||
technologies: Partial<Record<TechnologyType, number>>
|
technologies: Partial<Record<TechnologyType, number>>,
|
||||||
|
allPlanets?: Planet[]
|
||||||
): { queueItem: BuildQueueItem } => {
|
): { queueItem: BuildQueueItem } => {
|
||||||
const targetLevel = currentLevel + 1
|
const targetLevel = currentLevel + 1
|
||||||
const cost = researchLogic.calculateTechnologyCost(techType, targetLevel)
|
const cost = researchLogic.calculateTechnologyCost(techType, targetLevel)
|
||||||
@@ -62,11 +63,30 @@ export const executeTechnologyResearch = (
|
|||||||
// 计算军官加成
|
// 计算军官加成
|
||||||
const bonuses = officerLogic.calculateActiveBonuses(officers, Date.now())
|
const bonuses = officerLogic.calculateActiveBonuses(officers, Date.now())
|
||||||
|
|
||||||
// 获取研究实验室等级和能源技术等级
|
// 获取星际研究网络等级
|
||||||
const researchLabLevel = planet.buildings[BuildingType.ResearchLab] || 1
|
const intergalacticResearchNetworkLevel = technologies[TechnologyType.IntergalacticResearchNetwork] || 0
|
||||||
const energyTechLevel = technologies[TechnologyType.EnergyTechnology] || 0
|
|
||||||
|
|
||||||
const time = researchLogic.calculateTechnologyTime(techType, currentLevel, bonuses.researchSpeedBonus, researchLabLevel, energyTechLevel)
|
// 计算有效研究实验室等级(考虑星际研究网络)
|
||||||
|
let researchLabLevel: number
|
||||||
|
if (allPlanets && intergalacticResearchNetworkLevel > 0) {
|
||||||
|
researchLabLevel = researchLogic.calculateEffectiveLabLevel(allPlanets, planet.id, intergalacticResearchNetworkLevel)
|
||||||
|
} else {
|
||||||
|
researchLabLevel = planet.buildings[BuildingType.ResearchLab] || 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const energyTechLevel = technologies[TechnologyType.EnergyTechnology] || 0
|
||||||
|
// 获取大学等级(加速研究)
|
||||||
|
const universityLevel = planet.buildings[BuildingType.University] || 0
|
||||||
|
|
||||||
|
const time = researchLogic.calculateTechnologyTime(
|
||||||
|
techType,
|
||||||
|
currentLevel,
|
||||||
|
bonuses.researchSpeedBonus,
|
||||||
|
researchLabLevel,
|
||||||
|
energyTechLevel,
|
||||||
|
1,
|
||||||
|
universityLevel
|
||||||
|
)
|
||||||
|
|
||||||
// 扣除资源
|
// 扣除资源
|
||||||
resourceLogic.deductResources(planet.resources, cost)
|
resourceLogic.deductResources(planet.resources, cost)
|
||||||
|
|||||||
@@ -5,6 +5,24 @@ import { OFFICERS } from '@/config/gameConfig'
|
|||||||
import * as oreDepositLogic from './oreDepositLogic'
|
import * as oreDepositLogic from './oreDepositLogic'
|
||||||
import * as planetLogic from './planetLogic'
|
import * as planetLogic from './planetLogic'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算资源研究科技加成
|
||||||
|
* @param mineralResearchLevel 矿物研究等级(每级+2%金属产量)
|
||||||
|
* @param crystalResearchLevel 晶体研究等级(每级+2%晶体产量)
|
||||||
|
* @param fuelResearchLevel 燃料研究等级(每级+2%重氢产量)
|
||||||
|
*/
|
||||||
|
export const calculateResearchProductionBonus = (
|
||||||
|
mineralResearchLevel: number = 0,
|
||||||
|
crystalResearchLevel: number = 0,
|
||||||
|
fuelResearchLevel: number = 0
|
||||||
|
): { metalBonus: number; crystalBonus: number; deuteriumBonus: number } => {
|
||||||
|
return {
|
||||||
|
metalBonus: mineralResearchLevel * 2, // 每级2%
|
||||||
|
crystalBonus: crystalResearchLevel * 2,
|
||||||
|
deuteriumBonus: fuelResearchLevel * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算电量产出
|
* 计算电量产出
|
||||||
*/
|
*/
|
||||||
@@ -93,6 +111,9 @@ export const calculateEnergyConsumption = (planet: Planet): number => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算资源产量(每小时)
|
* 计算资源产量(每小时)
|
||||||
|
* @param planet 星球
|
||||||
|
* @param bonuses 军官等加成
|
||||||
|
* @param techBonuses 科技加成(可选,矿物研究/晶体研究/燃料研究)
|
||||||
*/
|
*/
|
||||||
export const calculateResourceProduction = (
|
export const calculateResourceProduction = (
|
||||||
planet: Planet,
|
planet: Planet,
|
||||||
@@ -100,6 +121,11 @@ export const calculateResourceProduction = (
|
|||||||
resourceProductionBonus: number
|
resourceProductionBonus: number
|
||||||
darkMatterProductionBonus: number
|
darkMatterProductionBonus: number
|
||||||
energyProductionBonus: number
|
energyProductionBonus: number
|
||||||
|
},
|
||||||
|
techBonuses?: {
|
||||||
|
mineralResearchLevel?: number
|
||||||
|
crystalResearchLevel?: number
|
||||||
|
fuelResearchLevel?: number
|
||||||
}
|
}
|
||||||
): Resources => {
|
): Resources => {
|
||||||
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
||||||
@@ -110,6 +136,16 @@ export const calculateResourceProduction = (
|
|||||||
const resourceBonus = 1 + (bonuses.resourceProductionBonus || 0) / 100
|
const resourceBonus = 1 + (bonuses.resourceProductionBonus || 0) / 100
|
||||||
const darkMatterBonus = 1 + (bonuses.darkMatterProductionBonus || 0) / 100
|
const darkMatterBonus = 1 + (bonuses.darkMatterProductionBonus || 0) / 100
|
||||||
|
|
||||||
|
// 计算科技加成
|
||||||
|
const researchBonus = calculateResearchProductionBonus(
|
||||||
|
techBonuses?.mineralResearchLevel || 0,
|
||||||
|
techBonuses?.crystalResearchLevel || 0,
|
||||||
|
techBonuses?.fuelResearchLevel || 0
|
||||||
|
)
|
||||||
|
const metalTechBonus = 1 + researchBonus.metalBonus / 100
|
||||||
|
const crystalTechBonus = 1 + researchBonus.crystalBonus / 100
|
||||||
|
const deuteriumTechBonus = 1 + researchBonus.deuteriumBonus / 100
|
||||||
|
|
||||||
// 计算能量产出(每小时)
|
// 计算能量产出(每小时)
|
||||||
const energyProduction = calculateEnergyProduction(planet, { energyProductionBonus: bonuses.energyProductionBonus })
|
const energyProduction = calculateEnergyProduction(planet, { energyProductionBonus: bonuses.energyProductionBonus })
|
||||||
|
|
||||||
@@ -131,13 +167,28 @@ export const calculateResourceProduction = (
|
|||||||
const deuteriumTempBonus = planetLogic.calculateDeuteriumTemperatureBonus(planet.temperature?.max ?? 0)
|
const deuteriumTempBonus = planetLogic.calculateDeuteriumTemperatureBonus(planet.temperature?.max ?? 0)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metal: metalMineLevel * 1500 * Math.pow(1.5, metalMineLevel) * resourceBonus * productionEfficiency * metalDepositEfficiency,
|
metal:
|
||||||
crystal: crystalMineLevel * 1000 * Math.pow(1.5, crystalMineLevel) * resourceBonus * productionEfficiency * crystalDepositEfficiency,
|
metalMineLevel *
|
||||||
|
1500 *
|
||||||
|
Math.pow(1.5, metalMineLevel) *
|
||||||
|
resourceBonus *
|
||||||
|
metalTechBonus *
|
||||||
|
productionEfficiency *
|
||||||
|
metalDepositEfficiency,
|
||||||
|
crystal:
|
||||||
|
crystalMineLevel *
|
||||||
|
1000 *
|
||||||
|
Math.pow(1.5, crystalMineLevel) *
|
||||||
|
resourceBonus *
|
||||||
|
crystalTechBonus *
|
||||||
|
productionEfficiency *
|
||||||
|
crystalDepositEfficiency,
|
||||||
deuterium:
|
deuterium:
|
||||||
deuteriumSynthesizerLevel *
|
deuteriumSynthesizerLevel *
|
||||||
500 *
|
500 *
|
||||||
Math.pow(1.5, deuteriumSynthesizerLevel) *
|
Math.pow(1.5, deuteriumSynthesizerLevel) *
|
||||||
resourceBonus *
|
resourceBonus *
|
||||||
|
deuteriumTechBonus *
|
||||||
productionEfficiency *
|
productionEfficiency *
|
||||||
deuteriumDepositEfficiency *
|
deuteriumDepositEfficiency *
|
||||||
deuteriumTempBonus,
|
deuteriumTempBonus,
|
||||||
@@ -183,7 +234,13 @@ export const updatePlanetResources = (
|
|||||||
energyProductionBonus: number
|
energyProductionBonus: number
|
||||||
storageCapacityBonus: number
|
storageCapacityBonus: number
|
||||||
},
|
},
|
||||||
gameSpeed: number = 1
|
gameSpeed: number = 1,
|
||||||
|
miningTechLevel: number = 0,
|
||||||
|
techBonuses?: {
|
||||||
|
mineralResearchLevel?: number
|
||||||
|
crystalResearchLevel?: number
|
||||||
|
fuelResearchLevel?: number
|
||||||
|
}
|
||||||
): void => {
|
): void => {
|
||||||
const timeDiff = (now - planet.lastUpdate) / 1000 // 转换为秒
|
const timeDiff = (now - planet.lastUpdate) / 1000 // 转换为秒
|
||||||
|
|
||||||
@@ -214,11 +271,15 @@ export const updatePlanetResources = (
|
|||||||
planet.resources.energy = Math.max(0, planet.resources.energy)
|
planet.resources.energy = Math.max(0, planet.resources.energy)
|
||||||
|
|
||||||
// 计算资源产量(会检查能量是否充足,以及矿脉储量效率)
|
// 计算资源产量(会检查能量是否充足,以及矿脉储量效率)
|
||||||
const production = calculateResourceProduction(planet, {
|
const production = calculateResourceProduction(
|
||||||
resourceProductionBonus: bonuses.resourceProductionBonus,
|
planet,
|
||||||
darkMatterProductionBonus: bonuses.darkMatterProductionBonus,
|
{
|
||||||
energyProductionBonus: bonuses.energyProductionBonus
|
resourceProductionBonus: bonuses.resourceProductionBonus,
|
||||||
})
|
darkMatterProductionBonus: bonuses.darkMatterProductionBonus,
|
||||||
|
energyProductionBonus: bonuses.energyProductionBonus
|
||||||
|
},
|
||||||
|
techBonuses
|
||||||
|
)
|
||||||
|
|
||||||
// 计算实际产出量(用于消耗矿脉储量)
|
// 计算实际产出量(用于消耗矿脉储量)
|
||||||
const metalProduced = (production.metal * effectiveTimeDiff) / 3600
|
const metalProduced = (production.metal * effectiveTimeDiff) / 3600
|
||||||
@@ -232,10 +293,11 @@ export const updatePlanetResources = (
|
|||||||
oreDepositLogic.consumeDeposit(planet.oreDeposits, 'deuterium', deuteriumProduced)
|
oreDepositLogic.consumeDeposit(planet.oreDeposits, 'deuterium', deuteriumProduced)
|
||||||
|
|
||||||
// 矿脉缓慢恢复(每次更新时恢复一小部分)
|
// 矿脉缓慢恢复(每次更新时恢复一小部分)
|
||||||
// 地质研究站等级影响恢复速度
|
// 地质研究站等级影响恢复速度,深层钻探设施和采矿技术影响恢复上限
|
||||||
const hoursElapsed = effectiveTimeDiff / 3600
|
const hoursElapsed = effectiveTimeDiff / 3600
|
||||||
const geoStationLevel = planet.buildings[BuildingType.GeoResearchStation] || 0
|
const geoStationLevel = planet.buildings[BuildingType.GeoResearchStation] || 0
|
||||||
oreDepositLogic.regenerateDeposits(planet.oreDeposits, hoursElapsed, geoStationLevel)
|
const deepDrillingLevel = planet.buildings[BuildingType.DeepDrillingFacility] || 0
|
||||||
|
oreDepositLogic.regenerateDeposits(planet.oreDeposits, hoursElapsed, geoStationLevel, deepDrillingLevel, miningTechLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新资源(转换为每秒产量,应用游戏速度)
|
// 更新资源(转换为每秒产量,应用游戏速度)
|
||||||
@@ -386,12 +448,22 @@ export interface ConsumptionDetail {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算资源产量详细breakdown
|
* 计算资源产量详细breakdown
|
||||||
|
* @param planet 星球
|
||||||
|
* @param officers 军官
|
||||||
|
* @param currentTime 当前时间
|
||||||
|
* @param resourceSpeed 游戏速度
|
||||||
|
* @param techBonuses 科技加成(可选,矿物研究/晶体研究/燃料研究)
|
||||||
*/
|
*/
|
||||||
export const calculateProductionBreakdown = (
|
export const calculateProductionBreakdown = (
|
||||||
planet: Planet,
|
planet: Planet,
|
||||||
officers: Record<OfficerType, Officer>,
|
officers: Record<OfficerType, Officer>,
|
||||||
currentTime: number,
|
currentTime: number,
|
||||||
resourceSpeed: number = 1
|
resourceSpeed: number = 1,
|
||||||
|
techBonuses?: {
|
||||||
|
mineralResearchLevel?: number
|
||||||
|
crystalResearchLevel?: number
|
||||||
|
fuelResearchLevel?: number
|
||||||
|
}
|
||||||
): ProductionBreakdown => {
|
): ProductionBreakdown => {
|
||||||
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
||||||
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
|
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
|
||||||
@@ -432,6 +504,13 @@ export const calculateProductionBreakdown = (
|
|||||||
const totalDarkMatterBonus = activeOfficerBonuses.reduce((sum, officer) => sum + officer.darkMatterBonus, 0)
|
const totalDarkMatterBonus = activeOfficerBonuses.reduce((sum, officer) => sum + officer.darkMatterBonus, 0)
|
||||||
const totalEnergyBonus = activeOfficerBonuses.reduce((sum, officer) => sum + officer.energyBonus, 0)
|
const totalEnergyBonus = activeOfficerBonuses.reduce((sum, officer) => sum + officer.energyBonus, 0)
|
||||||
|
|
||||||
|
// 计算科技加成
|
||||||
|
const researchBonus = calculateResearchProductionBonus(
|
||||||
|
techBonuses?.mineralResearchLevel || 0,
|
||||||
|
techBonuses?.crystalResearchLevel || 0,
|
||||||
|
techBonuses?.fuelResearchLevel || 0
|
||||||
|
)
|
||||||
|
|
||||||
// 金属矿产量
|
// 金属矿产量
|
||||||
const metalBase = metalMineLevel * 1500 * Math.pow(1.5, metalMineLevel)
|
const metalBase = metalMineLevel * 1500 * Math.pow(1.5, metalMineLevel)
|
||||||
const metalBonuses: ProductionBonus[] = []
|
const metalBonuses: ProductionBonus[] = []
|
||||||
@@ -449,16 +528,27 @@ export const calculateProductionBreakdown = (
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 添加矿物研究科技加成
|
||||||
|
if (researchBonus.metalBonus > 0) {
|
||||||
|
const techBonusValue = metalBase * (1 + totalResourceBonus / 100) * (researchBonus.metalBonus / 100)
|
||||||
|
metalBonuses.push({
|
||||||
|
name: 'research.mineralResearch',
|
||||||
|
percentage: researchBonus.metalBonus,
|
||||||
|
value: techBonusValue,
|
||||||
|
source: 'technology'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasPositiveEnergyBalance) {
|
if (!hasPositiveEnergyBalance) {
|
||||||
metalBonuses.push({
|
metalBonuses.push({
|
||||||
name: 'resources.noEnergy',
|
name: 'resources.noEnergy',
|
||||||
percentage: -100,
|
percentage: -100,
|
||||||
value: -metalBase * (1 + totalResourceBonus / 100),
|
value: -metalBase * (1 + totalResourceBonus / 100) * (1 + researchBonus.metalBonus / 100),
|
||||||
source: 'other'
|
source: 'other'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const metalFinal = metalBase * (1 + totalResourceBonus / 100) * productionEfficiency
|
const metalFinal = metalBase * (1 + totalResourceBonus / 100) * (1 + researchBonus.metalBonus / 100) * productionEfficiency
|
||||||
|
|
||||||
// 晶体矿产量
|
// 晶体矿产量
|
||||||
const crystalBase = crystalMineLevel * 1000 * Math.pow(1.5, crystalMineLevel)
|
const crystalBase = crystalMineLevel * 1000 * Math.pow(1.5, crystalMineLevel)
|
||||||
@@ -476,16 +566,27 @@ export const calculateProductionBreakdown = (
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 添加晶体研究科技加成
|
||||||
|
if (researchBonus.crystalBonus > 0) {
|
||||||
|
const techBonusValue = crystalBase * (1 + totalResourceBonus / 100) * (researchBonus.crystalBonus / 100)
|
||||||
|
crystalBonuses.push({
|
||||||
|
name: 'research.crystalResearch',
|
||||||
|
percentage: researchBonus.crystalBonus,
|
||||||
|
value: techBonusValue,
|
||||||
|
source: 'technology'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasPositiveEnergyBalance) {
|
if (!hasPositiveEnergyBalance) {
|
||||||
crystalBonuses.push({
|
crystalBonuses.push({
|
||||||
name: 'resources.noEnergy',
|
name: 'resources.noEnergy',
|
||||||
percentage: -100,
|
percentage: -100,
|
||||||
value: -crystalBase * (1 + totalResourceBonus / 100),
|
value: -crystalBase * (1 + totalResourceBonus / 100) * (1 + researchBonus.crystalBonus / 100),
|
||||||
source: 'other'
|
source: 'other'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const crystalFinal = crystalBase * (1 + totalResourceBonus / 100) * productionEfficiency
|
const crystalFinal = crystalBase * (1 + totalResourceBonus / 100) * (1 + researchBonus.crystalBonus / 100) * productionEfficiency
|
||||||
|
|
||||||
// 重氢合成器产量(受温度影响)
|
// 重氢合成器产量(受温度影响)
|
||||||
// OGame 原版规则:温度越低,重氢产量越高
|
// OGame 原版规则:温度越低,重氢产量越高
|
||||||
@@ -518,16 +619,27 @@ export const calculateProductionBreakdown = (
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 添加燃料研究科技加成
|
||||||
|
if (researchBonus.deuteriumBonus > 0) {
|
||||||
|
const techBonusValue = deuteriumBase * (1 + totalResourceBonus / 100) * (researchBonus.deuteriumBonus / 100)
|
||||||
|
deuteriumBonuses.push({
|
||||||
|
name: 'research.fuelResearch',
|
||||||
|
percentage: researchBonus.deuteriumBonus,
|
||||||
|
value: techBonusValue,
|
||||||
|
source: 'technology'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasPositiveEnergyBalance) {
|
if (!hasPositiveEnergyBalance) {
|
||||||
deuteriumBonuses.push({
|
deuteriumBonuses.push({
|
||||||
name: 'resources.noEnergy',
|
name: 'resources.noEnergy',
|
||||||
percentage: -100,
|
percentage: -100,
|
||||||
value: -deuteriumBase * (1 + totalResourceBonus / 100),
|
value: -deuteriumBase * (1 + totalResourceBonus / 100) * (1 + researchBonus.deuteriumBonus / 100),
|
||||||
source: 'other'
|
source: 'other'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deuteriumFinal = deuteriumBase * (1 + totalResourceBonus / 100) * productionEfficiency
|
const deuteriumFinal = deuteriumBase * (1 + totalResourceBonus / 100) * (1 + researchBonus.deuteriumBonus / 100) * productionEfficiency
|
||||||
|
|
||||||
// 暗物质收集器产量
|
// 暗物质收集器产量
|
||||||
const darkMatterBase = darkMatterCollectorLevel * 25 * Math.pow(1.5, darkMatterCollectorLevel)
|
const darkMatterBase = darkMatterCollectorLevel * 25 * Math.pow(1.5, darkMatterCollectorLevel)
|
||||||
|
|||||||
@@ -322,3 +322,49 @@ export const addFleet = (currentFleet: Fleet, fleet: Partial<Fleet>): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算舰船拆除返还资源
|
||||||
|
* 返还50%的建造成本
|
||||||
|
*/
|
||||||
|
export const calculateShipScrapRefund = (shipType: ShipType, quantity: number): Resources => {
|
||||||
|
const cost = calculateShipCost(shipType, quantity)
|
||||||
|
return {
|
||||||
|
metal: Math.floor(cost.metal * 0.5),
|
||||||
|
crystal: Math.floor(cost.crystal * 0.5),
|
||||||
|
deuterium: Math.floor(cost.deuterium * 0.5),
|
||||||
|
darkMatter: Math.floor(cost.darkMatter * 0.5),
|
||||||
|
energy: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算舰船拆除时间
|
||||||
|
* 拆除时间为建造时间的50%
|
||||||
|
*/
|
||||||
|
export const calculateShipScrapTime = (
|
||||||
|
shipType: ShipType,
|
||||||
|
quantity: number,
|
||||||
|
buildingSpeedBonus: number = 0,
|
||||||
|
roboticsFactoryLevel: number = 0,
|
||||||
|
naniteFactoryLevel: number = 0
|
||||||
|
): number => {
|
||||||
|
const buildTime = calculateShipBuildTime(shipType, quantity, buildingSpeedBonus, roboticsFactoryLevel, naniteFactoryLevel)
|
||||||
|
return Math.floor(buildTime * 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建舰船拆除队列项
|
||||||
|
*/
|
||||||
|
export const createShipScrapQueueItem = (shipType: ShipType, quantity: number, scrapTime: number): BuildQueueItem => {
|
||||||
|
const now = Date.now()
|
||||||
|
shipQueueIdCounter++
|
||||||
|
return {
|
||||||
|
id: `scrap_ship_${now}_${shipQueueIdCounter}`,
|
||||||
|
type: 'scrap_ship',
|
||||||
|
itemType: shipType,
|
||||||
|
quantity,
|
||||||
|
startTime: now,
|
||||||
|
endTime: now + scrapTime * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -201,3 +201,66 @@ export const executeFleetDispatch = (
|
|||||||
resourceLogic.deductResources(planet.resources, cargo)
|
resourceLogic.deductResources(planet.resources, cargo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证舰船拆除的所有条件
|
||||||
|
*/
|
||||||
|
export const validateShipScrap = (
|
||||||
|
planet: Planet,
|
||||||
|
shipType: ShipType,
|
||||||
|
quantity: number
|
||||||
|
): {
|
||||||
|
valid: boolean
|
||||||
|
reason?: string
|
||||||
|
} => {
|
||||||
|
// 检查数量是否有效
|
||||||
|
if (quantity <= 0) {
|
||||||
|
return { valid: false, reason: 'errors.invalidQuantity' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查舰船数量是否足够
|
||||||
|
const available = planet.fleet[shipType] || 0
|
||||||
|
if (available < quantity) {
|
||||||
|
return { valid: false, reason: 'errors.insufficientShips' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行舰船拆除
|
||||||
|
*/
|
||||||
|
export const executeShipScrap = (
|
||||||
|
planet: Planet,
|
||||||
|
shipType: ShipType,
|
||||||
|
quantity: number,
|
||||||
|
officers: Record<OfficerType, Officer>
|
||||||
|
): BuildQueueItem => {
|
||||||
|
// 计算军官加成
|
||||||
|
const bonuses = officerLogic.calculateActiveBonuses(officers, Date.now())
|
||||||
|
|
||||||
|
// 获取机器人工厂和纳米工厂等级
|
||||||
|
const roboticsFactoryLevel = planet.buildings[BuildingType.RoboticsFactory] || 0
|
||||||
|
const naniteFactoryLevel = planet.buildings[BuildingType.NaniteFactory] || 0
|
||||||
|
|
||||||
|
// 计算拆除时间
|
||||||
|
const scrapTime = shipLogic.calculateShipScrapTime(
|
||||||
|
shipType,
|
||||||
|
quantity,
|
||||||
|
bonuses.buildingSpeedBonus,
|
||||||
|
roboticsFactoryLevel,
|
||||||
|
naniteFactoryLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
// 计算返还资源
|
||||||
|
const refund = shipLogic.calculateShipScrapRefund(shipType, quantity)
|
||||||
|
|
||||||
|
// 扣除舰船(立即扣除,避免拆除过程中派遣)
|
||||||
|
planet.fleet[shipType] = (planet.fleet[shipType] || 0) - quantity
|
||||||
|
|
||||||
|
// 返还资源
|
||||||
|
resourceLogic.addResources(planet.resources, refund)
|
||||||
|
|
||||||
|
// 创建队列项
|
||||||
|
return shipLogic.createShipScrapQueueItem(shipType, quantity, scrapTime)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* WebDAV 同步服务
|
* WebDAV 同步服务
|
||||||
* 支持将存档上传到 WebDAV 服务器(如坚果云、Nextcloud、NAS等)
|
* 支持将存档上传到 WebDAV 服务器(如坚果云、Nextcloud、NAS等)
|
||||||
|
* 注意:WebDAV 配置存储在 gameStore.webdavConfig 中,与用户数据一起持久化
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface WebDAVConfig {
|
import type { WebDAVConfig } from '@/types/game'
|
||||||
serverUrl: string // WebDAV 服务器地址,如 https://dav.jianguoyun.com/dav/
|
|
||||||
username: string // 用户名
|
// 重新导出类型以保持向后兼容
|
||||||
password: string // 密码或应用专用密码
|
export type { WebDAVConfig }
|
||||||
basePath: string // 存档存放路径,如 /ogame-saves/
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WebDAVFile {
|
export interface WebDAVFile {
|
||||||
name: string
|
name: string
|
||||||
@@ -18,29 +17,47 @@ export interface WebDAVFile {
|
|||||||
isDirectory: boolean
|
isDirectory: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const STORAGE_KEY = 'ogame-webdav-config'
|
// WebDAV 消息 key(用于 i18n)
|
||||||
|
export const WebDAVMessageKey = {
|
||||||
|
// 连接相关
|
||||||
|
connectionSuccess: 'webdav.connectionSuccess',
|
||||||
|
connectionSuccessDirectoryCreated: 'webdav.connectionSuccessDirectoryCreated',
|
||||||
|
authFailed: 'webdav.authFailed',
|
||||||
|
directoryNotExist: 'webdav.directoryNotExist',
|
||||||
|
connectionFailedHttp: 'webdav.connectionFailedHttp',
|
||||||
|
networkError: 'webdav.networkError',
|
||||||
|
connectionError: 'webdav.connectionError',
|
||||||
|
|
||||||
// 获取保存的 WebDAV 配置
|
// 上传相关
|
||||||
export const getWebDAVConfig = (): WebDAVConfig | null => {
|
uploadSuccess: 'webdav.uploadSuccess',
|
||||||
try {
|
noWritePermission: 'webdav.noWritePermission',
|
||||||
const saved = localStorage.getItem(STORAGE_KEY)
|
insufficientStorage: 'webdav.insufficientStorage',
|
||||||
if (saved) {
|
uploadFailedHttp: 'webdav.uploadFailedHttp',
|
||||||
return JSON.parse(saved)
|
uploadError: 'webdav.uploadError',
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to load WebDAV config:', e)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存 WebDAV 配置
|
// 下载相关
|
||||||
export const saveWebDAVConfig = (config: WebDAVConfig): void => {
|
fileNotExist: 'webdav.fileNotExist',
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(config))
|
downloadFailedHttp: 'webdav.downloadFailedHttp',
|
||||||
}
|
downloadError: 'webdav.downloadError',
|
||||||
|
|
||||||
// 清除 WebDAV 配置
|
// 列表相关
|
||||||
export const clearWebDAVConfig = (): void => {
|
listFailedHttp: 'webdav.listFailedHttp',
|
||||||
localStorage.removeItem(STORAGE_KEY)
|
listError: 'webdav.listError',
|
||||||
|
|
||||||
|
// 删除相关
|
||||||
|
deleteFailedHttp: 'webdav.deleteFailedHttp',
|
||||||
|
deleteError: 'webdav.deleteError'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type WebDAVMessageKeyType = (typeof WebDAVMessageKey)[keyof typeof WebDAVMessageKey]
|
||||||
|
|
||||||
|
export interface WebDAVResult {
|
||||||
|
success: boolean
|
||||||
|
messageKey: WebDAVMessageKeyType
|
||||||
|
messageParams?: Record<string, string | number>
|
||||||
|
fileName?: string
|
||||||
|
data?: string
|
||||||
|
files?: WebDAVFile[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建 Authorization header
|
// 构建 Authorization header
|
||||||
@@ -66,7 +83,7 @@ const normalizePath = (serverUrl: string, basePath: string, fileName?: string):
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 测试 WebDAV 连接
|
// 测试 WebDAV 连接
|
||||||
export const testWebDAVConnection = async (config: WebDAVConfig): Promise<{ success: boolean; message: string }> => {
|
export const testWebDAVConnection = async (config: WebDAVConfig): Promise<WebDAVResult> => {
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath)
|
const url = normalizePath(config.serverUrl, config.basePath)
|
||||||
|
|
||||||
@@ -80,33 +97,30 @@ export const testWebDAVConnection = async (config: WebDAVConfig): Promise<{ succ
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.ok || response.status === 207) {
|
if (response.ok || response.status === 207) {
|
||||||
return { success: true, message: 'WebDAV 连接成功' }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败,请检查用户名和密码' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
// 尝试创建目录
|
// 尝试创建目录
|
||||||
const createResult = await createDirectory(config, config.basePath)
|
const createResult = await createDirectory(config, config.basePath)
|
||||||
if (createResult) {
|
if (createResult) {
|
||||||
return { success: true, message: 'WebDAV 连接成功,已创建存档目录' }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccessDirectoryCreated }
|
||||||
}
|
}
|
||||||
return { success: false, message: '目录不存在且无法创建' }
|
return { success: false, messageKey: WebDAVMessageKey.directoryNotExist }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, message: `连接失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.connectionFailedHttp, messageParams: { status: response.status } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
// CORS 错误的处理
|
// CORS 错误的处理
|
||||||
if (message.includes('Failed to fetch') || message.includes('NetworkError')) {
|
if (errorMessage.includes('Failed to fetch') || errorMessage.includes('NetworkError')) {
|
||||||
return {
|
return { success: false, messageKey: WebDAVMessageKey.networkError }
|
||||||
success: false,
|
|
||||||
message: '网络错误,可能是 CORS 限制。建议使用支持 CORS 的 WebDAV 服务或通过代理访问。'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return { success: false, message: `连接错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.connectionError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +144,7 @@ const createDirectory = async (config: WebDAVConfig, path: string): Promise<bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 上传存档到 WebDAV
|
// 上传存档到 WebDAV
|
||||||
export const uploadToWebDAV = async (
|
export const uploadToWebDAV = async (config: WebDAVConfig, data: string, fileName?: string): Promise<WebDAVResult> => {
|
||||||
config: WebDAVConfig,
|
|
||||||
data: string,
|
|
||||||
fileName?: string
|
|
||||||
): Promise<{ success: boolean; message: string; fileName?: string }> => {
|
|
||||||
try {
|
try {
|
||||||
// 生成带时间戳的文件名
|
// 生成带时间戳的文件名
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
||||||
@@ -151,25 +161,25 @@ export const uploadToWebDAV = async (
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.ok || response.status === 201 || response.status === 204) {
|
if (response.ok || response.status === 201 || response.status === 204) {
|
||||||
return { success: true, message: '上传成功', fileName: actualFileName }
|
return { success: true, messageKey: WebDAVMessageKey.uploadSuccess, fileName: actualFileName }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
return { success: false, message: '没有写入权限' }
|
return { success: false, messageKey: WebDAVMessageKey.noWritePermission }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 507) {
|
if (response.status === 507) {
|
||||||
return { success: false, message: '存储空间不足' }
|
return { success: false, messageKey: WebDAVMessageKey.insufficientStorage }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, message: `上传失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.uploadFailedHttp, messageParams: { status: response.status } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `上传错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.uploadError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +222,7 @@ const parsePropfindResponse = (xml: string, _basePath: string): WebDAVFile[] =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 列出 WebDAV 目录中的存档文件
|
// 列出 WebDAV 目录中的存档文件
|
||||||
export const listWebDAVFiles = async (config: WebDAVConfig): Promise<{ success: boolean; files?: WebDAVFile[]; message?: string }> => {
|
export const listWebDAVFiles = async (config: WebDAVConfig): Promise<WebDAVResult> => {
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath)
|
const url = normalizePath(config.serverUrl, config.basePath)
|
||||||
|
|
||||||
@@ -227,29 +237,26 @@ export const listWebDAVFiles = async (config: WebDAVConfig): Promise<{ success:
|
|||||||
|
|
||||||
if (!response.ok && response.status !== 207) {
|
if (!response.ok && response.status !== 207) {
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
return { success: false, message: '目录不存在' }
|
return { success: false, messageKey: WebDAVMessageKey.directoryNotExist }
|
||||||
}
|
}
|
||||||
return { success: false, message: `获取文件列表失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.listFailedHttp, messageParams: { status: response.status } }
|
||||||
}
|
}
|
||||||
|
|
||||||
const xml = await response.text()
|
const xml = await response.text()
|
||||||
const files = parsePropfindResponse(xml, config.basePath)
|
const files = parsePropfindResponse(xml, config.basePath)
|
||||||
|
|
||||||
return { success: true, files }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess, files }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `获取文件列表错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.listError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 WebDAV 下载存档
|
// 从 WebDAV 下载存档
|
||||||
export const downloadFromWebDAV = async (
|
export const downloadFromWebDAV = async (config: WebDAVConfig, fileName: string): Promise<WebDAVResult> => {
|
||||||
config: WebDAVConfig,
|
|
||||||
fileName: string
|
|
||||||
): Promise<{ success: boolean; data?: string; message?: string }> => {
|
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
||||||
|
|
||||||
@@ -262,27 +269,24 @@ export const downloadFromWebDAV = async (
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
return { success: false, message: '文件不存在' }
|
return { success: false, messageKey: WebDAVMessageKey.fileNotExist }
|
||||||
}
|
}
|
||||||
return { success: false, message: `下载失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.downloadFailedHttp, messageParams: { status: response.status } }
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.text()
|
const data = await response.text()
|
||||||
return { success: true, data }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess, data }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `下载错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.downloadError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除 WebDAV 文件
|
// 删除 WebDAV 文件
|
||||||
export const deleteFromWebDAV = async (
|
export const deleteFromWebDAV = async (config: WebDAVConfig, fileName: string): Promise<WebDAVResult> => {
|
||||||
config: WebDAVConfig,
|
|
||||||
fileName: string
|
|
||||||
): Promise<{ success: boolean; message?: string }> => {
|
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
||||||
|
|
||||||
@@ -294,20 +298,20 @@ export const deleteFromWebDAV = async (
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.ok || response.status === 204) {
|
if (response.ok || response.status === 204) {
|
||||||
return { success: true }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
return { success: true } // 文件不存在也视为删除成功
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess } // 文件不存在也视为删除成功
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, message: `删除失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.deleteFailedHttp, messageParams: { status: response.status } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `删除错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.deleteError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import type {
|
|||||||
IncomingFleetAlert,
|
IncomingFleetAlert,
|
||||||
MissileAttack,
|
MissileAttack,
|
||||||
AchievementStats,
|
AchievementStats,
|
||||||
AchievementProgress
|
AchievementProgress,
|
||||||
|
WebDAVConfig
|
||||||
} from '@/types/game'
|
} from '@/types/game'
|
||||||
import { TechnologyType, OfficerType } from '@/types/game'
|
import { TechnologyType, OfficerType } from '@/types/game'
|
||||||
import { initializeAchievementStats, initializeAchievements } from '@/logic/achievementLogic'
|
import { initializeAchievementStats, initializeAchievements } from '@/logic/achievementLogic'
|
||||||
@@ -25,6 +26,7 @@ export const useGameStore = defineStore('game', {
|
|||||||
gameTime: Date.now(),
|
gameTime: Date.now(),
|
||||||
isPaused: false,
|
isPaused: false,
|
||||||
gameSpeed: 1,
|
gameSpeed: 1,
|
||||||
|
battleToFinish: true, // 战斗到底模式:false=经典模式(6回合平局),true=战斗到底(最多100回合)
|
||||||
player: {
|
player: {
|
||||||
id: 'player1',
|
id: 'player1',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -61,7 +63,8 @@ export const useGameStore = defineStore('game', {
|
|||||||
research: true,
|
research: true,
|
||||||
unlock: true
|
unlock: true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
webdavConfig: null as WebDAVConfig | null
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async requestBrowserPermission(): Promise<boolean> {
|
async requestBrowserPermission(): Promise<boolean> {
|
||||||
|
|||||||
@@ -120,6 +120,15 @@
|
|||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* 平滑过渡 */
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
/* 禁用文本大小调整,防止移动端自动放大文本 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-moz-text-size-adjust: 100%;
|
||||||
|
text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
@@ -128,11 +137,6 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 平滑过渡 */
|
|
||||||
html {
|
|
||||||
transition: background-color 0.3s ease, color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 滚动条样式 */
|
/* 滚动条样式 */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@@ -156,12 +160,12 @@
|
|||||||
@keyframes fade-in {
|
@keyframes fade-in {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translate3d(0, 10px, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translate3d(0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +179,7 @@ aside nav a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
aside nav a:hover button {
|
aside nav a:hover button {
|
||||||
transform: translateX(4px);
|
transform: translate3d(4px, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 资源数字更新动画 */
|
/* 资源数字更新动画 */
|
||||||
@@ -217,4 +221,22 @@ aside nav a:hover button {
|
|||||||
:root:not(.dark) .tooltip-content-custom {
|
:root:not(.dark) .tooltip-content-custom {
|
||||||
background-color: oklch(0.3 0.02 85) !important;
|
background-color: oklch(0.3 0.02 85) !important;
|
||||||
color: oklch(0.95 0.008 85) !important;
|
color: oklch(0.95 0.008 85) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修复 Edge-to-Edge 模式下 Portal 容器的定位问题 */
|
||||||
|
[data-reka-portal],
|
||||||
|
[data-radix-portal] {
|
||||||
|
position: fixed !important;
|
||||||
|
left: 0 !important;
|
||||||
|
top: 0 !important;
|
||||||
|
right: 0 !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
pointer-events: none;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-reka-portal] > *,
|
||||||
|
[data-radix-portal] > * {
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,9 @@ export const BuildingType = {
|
|||||||
// 特殊建筑
|
// 特殊建筑
|
||||||
PlanetDestroyerFactory: 'planetDestroyerFactory', // 行星毁灭者工厂
|
PlanetDestroyerFactory: 'planetDestroyerFactory', // 行星毁灭者工厂
|
||||||
GeoResearchStation: 'geoResearchStation', // 地质研究站(影响矿脉恢复速度)
|
GeoResearchStation: 'geoResearchStation', // 地质研究站(影响矿脉恢复速度)
|
||||||
DeepDrillingFacility: 'deepDrillingFacility' // 深层钻探设施(提升矿脉上限)
|
DeepDrillingFacility: 'deepDrillingFacility', // 深层钻探设施(提升矿脉上限)
|
||||||
|
// 2moons新增建筑
|
||||||
|
University: 'university' // 大学(加速研究)
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type BuildingType = (typeof BuildingType)[keyof typeof BuildingType]
|
export type BuildingType = (typeof BuildingType)[keyof typeof BuildingType]
|
||||||
@@ -98,7 +100,12 @@ export const TechnologyType = {
|
|||||||
DarkMatterTechnology: 'darkMatterTechnology', // 暗物质技术
|
DarkMatterTechnology: 'darkMatterTechnology', // 暗物质技术
|
||||||
TerraformingTechnology: 'terraformingTechnology', // 地形改造技术
|
TerraformingTechnology: 'terraformingTechnology', // 地形改造技术
|
||||||
PlanetDestructionTech: 'planetDestructionTech', // 行星毁灭技术
|
PlanetDestructionTech: 'planetDestructionTech', // 行星毁灭技术
|
||||||
MiningTechnology: 'miningTechnology' // 采矿技术(提升矿脉上限)
|
MiningTechnology: 'miningTechnology', // 采矿技术(提升矿脉上限)
|
||||||
|
// 2moons新增科技
|
||||||
|
IntergalacticResearchNetwork: 'intergalacticResearchNetwork', // 星际研究网络(连接多个研究实验室)
|
||||||
|
MineralResearch: 'mineralResearch', // 矿物研究(提升金属产量)
|
||||||
|
CrystalResearch: 'crystalResearch', // 晶体研究(提升晶体产量)
|
||||||
|
FuelResearch: 'fuelResearch' // 燃料研究(提升重氢产量)
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type TechnologyType = (typeof TechnologyType)[keyof typeof TechnologyType]
|
export type TechnologyType = (typeof TechnologyType)[keyof typeof TechnologyType]
|
||||||
@@ -496,6 +503,8 @@ export interface MissionReport {
|
|||||||
// 毁灭任务:概率和死星损失
|
// 毁灭任务:概率和死星损失
|
||||||
destructionChance?: number
|
destructionChance?: number
|
||||||
deathstarsLost?: boolean
|
deathstarsLost?: boolean
|
||||||
|
// 毁灭任务:是否发生了战斗
|
||||||
|
hadBattle?: boolean
|
||||||
// 部署任务:部署的舰队
|
// 部署任务:部署的舰队
|
||||||
deployedFleet?: Partial<Fleet>
|
deployedFleet?: Partial<Fleet>
|
||||||
// 导弹攻击任务:导弹信息
|
// 导弹攻击任务:导弹信息
|
||||||
@@ -656,7 +665,7 @@ export interface DebrisField {
|
|||||||
// 建造队列项
|
// 建造队列项
|
||||||
export interface BuildQueueItem {
|
export interface BuildQueueItem {
|
||||||
id: string
|
id: string
|
||||||
type: 'building' | 'technology' | 'ship' | 'defense' | 'demolish'
|
type: 'building' | 'technology' | 'ship' | 'defense' | 'demolish' | 'scrap_ship'
|
||||||
itemType: BuildingType | TechnologyType | ShipType | DefenseType
|
itemType: BuildingType | TechnologyType | ShipType | DefenseType
|
||||||
targetLevel?: number // 用于建筑和科技
|
targetLevel?: number // 用于建筑和科技
|
||||||
quantity?: number // 用于舰船和防御
|
quantity?: number // 用于舰船和防御
|
||||||
@@ -667,7 +676,7 @@ export interface BuildQueueItem {
|
|||||||
// 等待队列项(尚未开始执行,不需要 startTime/endTime)
|
// 等待队列项(尚未开始执行,不需要 startTime/endTime)
|
||||||
export interface WaitingQueueItem {
|
export interface WaitingQueueItem {
|
||||||
id: string
|
id: string
|
||||||
type: 'building' | 'technology' | 'ship' | 'defense' | 'demolish'
|
type: 'building' | 'technology' | 'ship' | 'defense' | 'demolish' | 'scrap_ship'
|
||||||
itemType: BuildingType | TechnologyType | ShipType | DefenseType
|
itemType: BuildingType | TechnologyType | ShipType | DefenseType
|
||||||
targetLevel?: number // 用于建筑和科技
|
targetLevel?: number // 用于建筑和科技
|
||||||
quantity?: number // 用于舰船和防御
|
quantity?: number // 用于舰船和防御
|
||||||
@@ -1226,3 +1235,11 @@ export interface QuestNotification {
|
|||||||
rewards?: QuestReward
|
rewards?: QuestReward
|
||||||
read?: boolean
|
read?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebDAV 配置
|
||||||
|
export interface WebDAVConfig {
|
||||||
|
serverUrl: string // WebDAV 服务器地址
|
||||||
|
username: string // 用户名
|
||||||
|
password: string // 密码或应用专用密码
|
||||||
|
basePath: string // 存档存放路径
|
||||||
|
}
|
||||||
|
|||||||
@@ -109,7 +109,10 @@ const COMBAT_EFFECTS = {
|
|||||||
* 计算一个单位对另一个单位造成的伤害
|
* 计算一个单位对另一个单位造成的伤害
|
||||||
* 增强版:包含护盾弹回、重型武器穿透、装甲损坏累积
|
* 增强版:包含护盾弹回、重型武器穿透、装甲损坏累积
|
||||||
*/
|
*/
|
||||||
const calculateDamage = (attacker: CombatUnit, defender: CombatUnit): { destroyed: number; damagedShield: number; armorDamageDealt: number } => {
|
const calculateDamage = (
|
||||||
|
attacker: CombatUnit,
|
||||||
|
defender: CombatUnit
|
||||||
|
): { destroyed: number; damagedShield: number; armorDamageDealt: number } => {
|
||||||
const attackPower = attacker.attack
|
const attackPower = attacker.attack
|
||||||
// 使用当前护盾值(如果有),否则使用最大护盾
|
// 使用当前护盾值(如果有),否则使用最大护盾
|
||||||
const defenderCurrentShield = defender.currentShield ?? defender.shield
|
const defenderCurrentShield = defender.currentShield ?? defender.shield
|
||||||
@@ -157,14 +160,8 @@ const calculateDamage = (attacker: CombatUnit, defender: CombatUnit): { destroye
|
|||||||
|
|
||||||
// 穿透护盾后对装甲造成损坏累积
|
// 穿透护盾后对装甲造成损坏累积
|
||||||
if (remainingDamage > 0) {
|
if (remainingDamage > 0) {
|
||||||
armorDamageDealt = Math.min(
|
armorDamageDealt = Math.min(COMBAT_EFFECTS.ARMOR_DAMAGE_RATE, COMBAT_EFFECTS.MAX_ARMOR_DAMAGE - (defender.armorDamage ?? 0))
|
||||||
COMBAT_EFFECTS.ARMOR_DAMAGE_RATE,
|
defender.armorDamage = Math.min(COMBAT_EFFECTS.MAX_ARMOR_DAMAGE, (defender.armorDamage ?? 0) + armorDamageDealt)
|
||||||
COMBAT_EFFECTS.MAX_ARMOR_DAMAGE - (defender.armorDamage ?? 0)
|
|
||||||
)
|
|
||||||
defender.armorDamage = Math.min(
|
|
||||||
COMBAT_EFFECTS.MAX_ARMOR_DAMAGE,
|
|
||||||
(defender.armorDamage ?? 0) + armorDamageDealt
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再消耗装甲
|
// 再消耗装甲
|
||||||
@@ -453,8 +450,24 @@ export const simulateBattle = (
|
|||||||
} else if (defenderUnits.length === 0) {
|
} else if (defenderUnits.length === 0) {
|
||||||
winner = 'attacker'
|
winner = 'attacker'
|
||||||
} else {
|
} else {
|
||||||
// OGame原版规则:6回合后双方都有剩余单位时判定为平局
|
// 达到最大回合数后双方都有剩余单位
|
||||||
winner = 'draw'
|
// 如果是战斗到底模式(maxRounds > 6),根据剩余战力判定胜负
|
||||||
|
if (maxRounds > 6) {
|
||||||
|
// 计算剩余战力
|
||||||
|
const attackerPower = attackerUnits.reduce((sum, u) => sum + u.count * u.armor, 0)
|
||||||
|
const defenderPower = defenderUnits.reduce((sum, u) => sum + u.count * u.armor, 0)
|
||||||
|
// 战力差距超过20%判定胜负,否则平局
|
||||||
|
if (attackerPower > defenderPower * 1.2) {
|
||||||
|
winner = 'attacker'
|
||||||
|
} else if (defenderPower > attackerPower * 1.2) {
|
||||||
|
winner = 'defender'
|
||||||
|
} else {
|
||||||
|
winner = 'draw'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// OGame原版规则:6回合后双方都有剩余单位时判定为平局
|
||||||
|
winner = 'draw'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -57,9 +57,15 @@
|
|||||||
<!-- 防守方配置 -->
|
<!-- 防守方配置 -->
|
||||||
<TabsContent value="defender" class="mt-4">
|
<TabsContent value="defender" class="mt-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle>{{ t('simulatorView.defenderConfig') }}</CardTitle>
|
<div class="space-y-1">
|
||||||
<CardDescription>{{ t('simulatorView.defenderConfigDesc') }}</CardDescription>
|
<CardTitle>{{ t('simulatorView.defenderConfig') }}</CardTitle>
|
||||||
|
<CardDescription>{{ t('simulatorView.defenderConfigDesc') }}</CardDescription>
|
||||||
|
</div>
|
||||||
|
<Button variant="outline" size="sm" @click="showSpyReportSelector = true" :disabled="!gameStore.player?.spyReports?.length">
|
||||||
|
<FileDown class="h-4 w-4 mr-2" />
|
||||||
|
{{ t('simulatorView.importFromSpyReport') }}
|
||||||
|
</Button>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent class="space-y-4">
|
<CardContent class="space-y-4">
|
||||||
<!-- 舰队配置 -->
|
<!-- 舰队配置 -->
|
||||||
@@ -147,28 +153,80 @@
|
|||||||
|
|
||||||
<!-- 战斗结果对话框 -->
|
<!-- 战斗结果对话框 -->
|
||||||
<BattleReportDialog v-model:open="showResultDialog" :report="simulationResult" />
|
<BattleReportDialog v-model:open="showResultDialog" :report="simulationResult" />
|
||||||
|
|
||||||
|
<!-- 侦查报告选择对话框 -->
|
||||||
|
<Dialog v-model:open="showSpyReportSelector">
|
||||||
|
<DialogContent class="max-w-2xl max-h-[80vh] overflow-y-auto">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{{ t('simulatorView.selectSpyReport') }}</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div v-if="!sortedSpyReports.length" class="text-center py-8 text-muted-foreground">
|
||||||
|
{{ t('simulatorView.noSpyReports') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="report in sortedSpyReports"
|
||||||
|
:key="report.id"
|
||||||
|
@click="importFromSpyReport(report)"
|
||||||
|
class="p-3 border rounded-lg cursor-pointer hover:bg-accent transition-colors"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<div>
|
||||||
|
<div class="font-medium">{{ report.targetPlanetName }}</div>
|
||||||
|
<div class="text-sm text-muted-foreground">
|
||||||
|
[{{ report.targetPosition.galaxy }}:{{ report.targetPosition.system }}:{{ report.targetPosition.position }}]
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-muted-foreground">
|
||||||
|
{{ formatTime(report.timestamp) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 flex gap-4 text-xs">
|
||||||
|
<span class="flex items-center gap-1">
|
||||||
|
<ResourceIcon type="metal" size="sm" />
|
||||||
|
{{ formatNumber(report.resources.metal) }}
|
||||||
|
</span>
|
||||||
|
<span class="flex items-center gap-1">
|
||||||
|
<ResourceIcon type="crystal" size="sm" />
|
||||||
|
{{ formatNumber(report.resources.crystal) }}
|
||||||
|
</span>
|
||||||
|
<span class="flex items-center gap-1">
|
||||||
|
<ResourceIcon type="deuterium" size="sm" />
|
||||||
|
{{ formatNumber(report.resources.deuterium) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, toRaw } from 'vue'
|
import { ref, toRaw, computed } from 'vue'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { useGameConfig } from '@/composables/useGameConfig'
|
import { useGameConfig } from '@/composables/useGameConfig'
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { ShipType, DefenseType } from '@/types/game'
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||||
import type { Fleet, BattleResult } from '@/types/game'
|
import { ShipType, DefenseType, TechnologyType } from '@/types/game'
|
||||||
|
import type { Fleet, BattleResult, SpyReport } from '@/types/game'
|
||||||
import { workerManager } from '@/workers/workerManager'
|
import { workerManager } from '@/workers/workerManager'
|
||||||
import ResourceIcon from '@/components/common/ResourceIcon.vue'
|
import ResourceIcon from '@/components/common/ResourceIcon.vue'
|
||||||
import BattleReportDialog from '@/components/dialogs/BattleReportDialog.vue'
|
import BattleReportDialog from '@/components/dialogs/BattleReportDialog.vue'
|
||||||
import { Sword, Shield, Zap, RotateCcw } from 'lucide-vue-next'
|
import { Sword, Shield, Zap, RotateCcw, FileDown } from 'lucide-vue-next'
|
||||||
import * as planetLogic from '@/logic/planetLogic'
|
import * as planetLogic from '@/logic/planetLogic'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { SHIPS, DEFENSES } = useGameConfig()
|
const { SHIPS, DEFENSES } = useGameConfig()
|
||||||
|
const gameStore = useGameStore()
|
||||||
|
|
||||||
|
// 侦查报告选择对话框状态
|
||||||
|
const showSpyReportSelector = ref(false)
|
||||||
|
|
||||||
// 科技类型配置
|
// 科技类型配置
|
||||||
const techTypes = ['weapon', 'shield', 'armor'] as const
|
const techTypes = ['weapon', 'shield', 'armor'] as const
|
||||||
@@ -249,7 +307,8 @@
|
|||||||
// 使用 Worker 执行战斗模拟
|
// 使用 Worker 执行战斗模拟
|
||||||
const result = await workerManager.simulateBattle({
|
const result = await workerManager.simulateBattle({
|
||||||
attacker: attackerSide,
|
attacker: attackerSide,
|
||||||
defender: defenderSide
|
defender: defenderSide,
|
||||||
|
maxRounds: gameStore.battleToFinish ? 100 : 6
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算掠夺和残骸场
|
// 计算掠夺和残骸场
|
||||||
@@ -302,4 +361,65 @@
|
|||||||
simulationResult.value = null
|
simulationResult.value = null
|
||||||
showResultDialog.value = false
|
showResultDialog.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按时间排序的侦查报告
|
||||||
|
const sortedSpyReports = computed(() => {
|
||||||
|
return [...(gameStore.player?.spyReports || [])].sort((a, b) => b.timestamp - a.timestamp)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
const formatTime = (timestamp: number) => {
|
||||||
|
return new Date(timestamp).toLocaleString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化数字
|
||||||
|
const formatNumber = (num: number) => {
|
||||||
|
if (num >= 1e9) return (num / 1e9).toFixed(1) + 'B'
|
||||||
|
if (num >= 1e6) return (num / 1e6).toFixed(1) + 'M'
|
||||||
|
if (num >= 1e3) return (num / 1e3).toFixed(1) + 'K'
|
||||||
|
return num.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从侦查报告导入数据
|
||||||
|
const importFromSpyReport = (report: SpyReport) => {
|
||||||
|
// 先重置防守方数据
|
||||||
|
defenderFleet.value = initializeFleet()
|
||||||
|
defenderDefense.value = initializeDefense()
|
||||||
|
|
||||||
|
// 填入资源
|
||||||
|
if (report.resources) {
|
||||||
|
defenderResources.value = {
|
||||||
|
metal: report.resources.metal || 0,
|
||||||
|
crystal: report.resources.crystal || 0,
|
||||||
|
deuterium: report.resources.deuterium || 0,
|
||||||
|
darkMatter: report.resources.darkMatter || 0,
|
||||||
|
energy: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 填入舰队
|
||||||
|
if (report.fleet) {
|
||||||
|
Object.entries(report.fleet).forEach(([key, value]) => {
|
||||||
|
defenderFleet.value[key as keyof Fleet] = value || 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 填入防御
|
||||||
|
if (report.defense) {
|
||||||
|
Object.entries(report.defense).forEach(([key, value]) => {
|
||||||
|
defenderDefense.value[key as DefenseType] = value || 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 填入科技
|
||||||
|
if (report.technologies) {
|
||||||
|
defenderTech.value.weapon = report.technologies[TechnologyType.WeaponsTechnology] || 0
|
||||||
|
defenderTech.value.shield = report.technologies[TechnologyType.ShieldingTechnology] || 0
|
||||||
|
defenderTech.value.armor = report.technologies[TechnologyType.ArmourTechnology] || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭对话框并切换到防守方标签
|
||||||
|
showSpyReportSelector.value = false
|
||||||
|
activeTab.value = 'defender'
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -181,133 +181,6 @@
|
|||||||
</ScrollableDialogContent>
|
</ScrollableDialogContent>
|
||||||
</Dialog>
|
</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">
|
<div class="relative">
|
||||||
<Search class="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
<Search class="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||||
@@ -503,14 +376,11 @@
|
|||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { useNPCStore } from '@/stores/npcStore'
|
import { useNPCStore } from '@/stores/npcStore'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { toast } from 'vue-sonner'
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card } from '@/components/ui/card'
|
|
||||||
import { Dialog, DialogDescription, DialogTitle } from '@/components/ui/dialog'
|
import { Dialog, DialogDescription, DialogTitle } from '@/components/ui/dialog'
|
||||||
import ScrollableDialogContent from '@/components/ui/dialog/ScrollableDialogContent.vue'
|
import ScrollableDialogContent from '@/components/ui/dialog/ScrollableDialogContent.vue'
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
|
||||||
import {
|
import {
|
||||||
FixedPagination,
|
FixedPagination,
|
||||||
Pagination,
|
Pagination,
|
||||||
@@ -523,7 +393,7 @@
|
|||||||
import NpcRelationCard from '@/components/npc/NpcRelationCard.vue'
|
import NpcRelationCard from '@/components/npc/NpcRelationCard.vue'
|
||||||
import NpcRelationRow from '@/components/npc/NpcRelationRow.vue'
|
import NpcRelationRow from '@/components/npc/NpcRelationRow.vue'
|
||||||
import { RelationStatus } from '@/types/game'
|
import { RelationStatus } from '@/types/game'
|
||||||
import type { DiplomaticRelation, TradeOffer, IntelReport, JointAttackInvite, Resources } from '@/types/game'
|
import type { DiplomaticRelation } from '@/types/game'
|
||||||
import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic'
|
import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic'
|
||||||
import {
|
import {
|
||||||
Search,
|
Search,
|
||||||
@@ -533,11 +403,7 @@
|
|||||||
Swords,
|
Swords,
|
||||||
Activity,
|
Activity,
|
||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
List,
|
List
|
||||||
Handshake,
|
|
||||||
ChevronDown,
|
|
||||||
ArrowLeftRight,
|
|
||||||
Eye
|
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
|
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
|
||||||
|
|
||||||
@@ -548,9 +414,6 @@
|
|||||||
|
|
||||||
const activeTab = ref('all')
|
const activeTab = ref('all')
|
||||||
|
|
||||||
// NPC互动面板状态
|
|
||||||
const interactionPanelOpen = ref(true)
|
|
||||||
|
|
||||||
// 视图模式: 'card' | 'list'
|
// 视图模式: 'card' | 'list'
|
||||||
const viewMode = ref<'card' | 'list'>('list')
|
const viewMode = ref<'card' | 'list'>('list')
|
||||||
|
|
||||||
@@ -864,188 +727,6 @@
|
|||||||
currentPage.value[activeTab.value] = val
|
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>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
<TabsList :class="['grid', 'w-full', showJumpGateTab ? 'grid-cols-3' : 'grid-cols-2']">
|
<TabsList :class="['grid', 'w-full', showJumpGateTab ? 'grid-cols-3' : 'grid-cols-2']">
|
||||||
<TabsTrigger v-for="tab in visibleTabs" :key="tab.value" :value="tab.value">
|
<TabsTrigger v-for="tab in visibleTabs" :key="tab.value" :value="tab.value">
|
||||||
{{ t(`fleetView.${tab.labelKey}`) }}
|
{{ t(`fleetView.${tab.labelKey}`) }}
|
||||||
<Badge v-if="tab.value === 'missions' && gameStore.player.fleetMissions.length > 0" variant="destructive" class="ml-1">
|
<Badge v-if="tab.value === 'missions' && totalMissionsCount > 0" variant="destructive" class="ml-1">
|
||||||
{{ gameStore.player.fleetMissions.length }}
|
{{ totalMissionsCount }}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge v-if="tab.value === 'jumpGate' && jumpGateReady" variant="default" class="ml-1">✓</Badge>
|
<Badge v-if="tab.value === 'jumpGate' && jumpGateReady" variant="default" class="ml-1">✓</Badge>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
@@ -294,13 +294,14 @@
|
|||||||
|
|
||||||
<!-- 飞行任务 -->
|
<!-- 飞行任务 -->
|
||||||
<TabsContent value="missions" class="mt-4 space-y-4">
|
<TabsContent value="missions" class="mt-4 space-y-4">
|
||||||
<Empty v-if="gameStore.player.fleetMissions.length === 0" class="border rounded-lg">
|
<Empty v-if="totalMissionsCount === 0" class="border rounded-lg">
|
||||||
<EmptyContent>
|
<EmptyContent>
|
||||||
<RocketIcon class="h-10 w-10 text-muted-foreground" />
|
<RocketIcon class="h-10 w-10 text-muted-foreground" />
|
||||||
<EmptyDescription>{{ t('fleetView.noFlightMissions') }}</EmptyDescription>
|
<EmptyDescription>{{ t('fleetView.noFlightMissions') }}</EmptyDescription>
|
||||||
</EmptyContent>
|
</EmptyContent>
|
||||||
</Empty>
|
</Empty>
|
||||||
|
|
||||||
|
<!-- 舰队任务 -->
|
||||||
<Card v-for="mission in gameStore.player.fleetMissions" :key="mission.id">
|
<Card v-for="mission in gameStore.player.fleetMissions" :key="mission.id">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div class="flex justify-between items-start">
|
<div class="flex justify-between items-start">
|
||||||
@@ -378,6 +379,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<!-- 导弹攻击任务 -->
|
||||||
|
<Card v-for="missileAttack in flyingMissileAttacks" :key="missileAttack.id">
|
||||||
|
<CardHeader>
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<div>
|
||||||
|
<CardTitle class="text-base sm:text-lg flex items-center gap-2">
|
||||||
|
<Crosshair class="h-4 w-4 text-destructive" />
|
||||||
|
{{ t('galaxyView.missileAttackTitle') }}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription class="text-xs sm:text-sm">
|
||||||
|
{{ getPlanetName(missileAttack.originPlanetId) }} → [{{ missileAttack.targetPosition.galaxy }}:{{ missileAttack.targetPosition.system }}:{{
|
||||||
|
missileAttack.targetPosition.position
|
||||||
|
}}]
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<Badge variant="destructive">
|
||||||
|
{{ t('fleetView.outbound') }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent class="space-y-3">
|
||||||
|
<!-- 导弹数量 -->
|
||||||
|
<div>
|
||||||
|
<p class="text-xs sm:text-sm font-medium mb-2">{{ t('galaxyView.missileCount') }}:</p>
|
||||||
|
<Badge variant="outline">{{ missileAttack.missileCount }}</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 进度条 -->
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="flex justify-between text-xs sm:text-sm">
|
||||||
|
<span>{{ t('fleetView.arrivalTime') }}:</span>
|
||||||
|
<span>{{ formatTime(getMissileRemainingTime(missileAttack)) }}</span>
|
||||||
|
</div>
|
||||||
|
<Progress :model-value="getMissileProgress(missileAttack)" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<!-- 跳跃门 -->
|
<!-- 跳跃门 -->
|
||||||
@@ -591,7 +630,8 @@
|
|||||||
Clock,
|
Clock,
|
||||||
Check,
|
Check,
|
||||||
Globe,
|
Globe,
|
||||||
Moon
|
Moon,
|
||||||
|
Crosshair
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { formatNumber, formatTime } from '@/utils/format'
|
import { formatNumber, formatTime } from '@/utils/format'
|
||||||
import * as shipValidation from '@/logic/shipValidation'
|
import * as shipValidation from '@/logic/shipValidation'
|
||||||
@@ -628,6 +668,16 @@
|
|||||||
return publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots, computerTechLevel)
|
return publicLogic.getMaxFleetMissions(bonuses.additionalFleetSlots, computerTechLevel)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 飞行中的导弹攻击
|
||||||
|
const flyingMissileAttacks = computed(() => {
|
||||||
|
return gameStore.player.missileAttacks?.filter(m => m.status === 'flying') || []
|
||||||
|
})
|
||||||
|
|
||||||
|
// 总任务数量(舰队任务 + 导弹攻击)
|
||||||
|
const totalMissionsCount = computed(() => {
|
||||||
|
return gameStore.player.fleetMissions.length + flyingMissileAttacks.value.length
|
||||||
|
})
|
||||||
|
|
||||||
const activeTab = ref<'send' | 'missions' | 'jumpGate'>('send')
|
const activeTab = ref<'send' | 'missions' | 'jumpGate'>('send')
|
||||||
|
|
||||||
// Tab 配置
|
// Tab 配置
|
||||||
@@ -837,6 +887,8 @@
|
|||||||
selectedMission.value = MissionType.Attack
|
selectedMission.value = MissionType.Attack
|
||||||
} else if (mission === 'colonize') {
|
} else if (mission === 'colonize') {
|
||||||
selectedMission.value = MissionType.Colonize
|
selectedMission.value = MissionType.Colonize
|
||||||
|
} else if (mission === 'recycle') {
|
||||||
|
selectedMission.value = MissionType.Recycle
|
||||||
} else if (gift === '1') {
|
} else if (gift === '1') {
|
||||||
// 如果有gift参数,设置为运输任务并启用赠送模式
|
// 如果有gift参数,设置为运输任务并启用赠送模式
|
||||||
selectedMission.value = MissionType.Transport
|
selectedMission.value = MissionType.Transport
|
||||||
@@ -1413,4 +1465,18 @@
|
|||||||
return Math.max(0, Math.min(100, (elapsed / total) * 100))
|
return Math.max(0, Math.min(100, (elapsed / total) * 100))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取导弹任务剩余时间
|
||||||
|
const getMissileRemainingTime = (missileAttack: any): number => {
|
||||||
|
const now = currentTime.value
|
||||||
|
return Math.max(0, (missileAttack.arrivalTime - now) / 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取导弹任务进度
|
||||||
|
const getMissileProgress = (missileAttack: any): number => {
|
||||||
|
const now = currentTime.value
|
||||||
|
const total = missileAttack.arrivalTime - missileAttack.launchTime
|
||||||
|
const elapsed = now - missileAttack.launchTime
|
||||||
|
return Math.max(0, Math.min(100, (elapsed / total) * 100))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
{{ t('galaxyView.myPlanets') }}
|
{{ t('galaxyView.myPlanets') }}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent class="w-72 p-2" align="start">
|
<PopoverContent class="w-87 p-2 max-h-80 overflow-y-auto" align="start">
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<div class="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
|
<div class="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
|
||||||
{{ t('galaxyView.selectPlanetToView') }}
|
{{ t('galaxyView.selectPlanetToView') }}
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
{{ highlightedNpc.name }} ({{ highlightedNpc.planets.length }})
|
{{ highlightedNpc.name }} ({{ highlightedNpc.planets.length }})
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent class="w-72 p-2" align="start">
|
<PopoverContent class="w-72 p-2 max-h-96 overflow-y-auto" align="start">
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<div class="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
|
<div class="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
|
||||||
{{ t('galaxyView.selectPlanetToView') }}
|
{{ t('galaxyView.selectPlanetToView') }}
|
||||||
|
|||||||
@@ -149,11 +149,11 @@
|
|||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-10px);
|
transform: translate3d(0, -10px, 0);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translate3d(0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1117,13 +1117,17 @@
|
|||||||
// 获取任务类型名称
|
// 获取任务类型名称
|
||||||
const getMissionTypeName = (missionType: string): string => {
|
const getMissionTypeName = (missionType: string): string => {
|
||||||
const typeMap: Record<string, string> = {
|
const typeMap: Record<string, string> = {
|
||||||
|
[MissionType.Attack]: t('fleetView.attack'),
|
||||||
[MissionType.Transport]: t('fleetView.transport'),
|
[MissionType.Transport]: t('fleetView.transport'),
|
||||||
[MissionType.Colonize]: t('fleetView.colonize'),
|
[MissionType.Colonize]: t('fleetView.colonize'),
|
||||||
|
[MissionType.Spy]: t('fleetView.spy'),
|
||||||
[MissionType.Deploy]: t('fleetView.deploy'),
|
[MissionType.Deploy]: t('fleetView.deploy'),
|
||||||
[MissionType.Expedition]: t('fleetView.expedition'),
|
[MissionType.Expedition]: t('fleetView.expedition'),
|
||||||
[MissionType.Recycle]: t('fleetView.recycle'),
|
[MissionType.Recycle]: t('fleetView.recycle'),
|
||||||
[MissionType.Destroy]: t('fleetView.destroy'),
|
[MissionType.Destroy]: t('fleetView.destroy'),
|
||||||
[MissionType.MissileAttack]: t('galaxyView.missileAttack')
|
[MissionType.MissileAttack]: t('galaxyView.missileAttack'),
|
||||||
|
[MissionType.HarvestDarkMatter]: t('fleetView.harvestDarkMatter'),
|
||||||
|
[MissionType.Station]: t('fleetView.station')
|
||||||
}
|
}
|
||||||
return typeMap[missionType] || missionType
|
return typeMap[missionType] || missionType
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,47 @@
|
|||||||
<div v-if="planet.isMoon" class="mt-2">
|
<div v-if="planet.isMoon" class="mt-2">
|
||||||
<Button @click="switchToParentPlanet" variant="outline" size="sm">{{ t('planet.backToPlanet') }}</Button>
|
<Button @click="switchToParentPlanet" variant="outline" size="sm">{{ t('planet.backToPlanet') }}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 放弃殖民地按钮 -->
|
||||||
|
<div v-if="canShowAbandonButton" class="mt-4">
|
||||||
|
<Button @click="showAbandonDialog = true" variant="destructive" size="sm">
|
||||||
|
{{ t('planet.abandonColony') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 放弃殖民地确认对话框 -->
|
||||||
|
<AlertDialog :open="showAbandonDialog" @update:open="showAbandonDialog = $event">
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>{{ t('planet.confirmAbandon') }}</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription class="whitespace-pre-line">
|
||||||
|
{{ t('planet.abandonWarning', { name: planet.name }) }}
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>{{ t('common.cancel') }}</AlertDialogCancel>
|
||||||
|
<AlertDialogAction @click="handleAbandonColony" class="bg-destructive text-destructive-foreground hover:bg-destructive/90">
|
||||||
|
{{ t('planet.confirmAbandonButton') }}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
|
||||||
|
<!-- 错误提示对话框 -->
|
||||||
|
<AlertDialog :open="showErrorDialog" @update:open="showErrorDialog = $event">
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>{{ t('planet.abandonFailed') }}</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
{{ errorMessage }}
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogAction>{{ t('common.confirm') }}</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
|
||||||
<!-- 资源管理 -->
|
<!-- 资源管理 -->
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@@ -195,25 +234,45 @@
|
|||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { useGameConfig } from '@/composables/useGameConfig'
|
import { useGameConfig } from '@/composables/useGameConfig'
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle
|
||||||
|
} from '@/components/ui/alert-dialog'
|
||||||
import ResourceIcon from '@/components/common/ResourceIcon.vue'
|
import ResourceIcon from '@/components/common/ResourceIcon.vue'
|
||||||
import { formatNumber, getResourceColor } from '@/utils/format'
|
import { formatNumber, getResourceColor } from '@/utils/format'
|
||||||
import { scaleNumber } from '@/utils/speed'
|
import { scaleNumber } from '@/utils/speed'
|
||||||
import type { Planet } from '@/types/game'
|
import type { Planet } from '@/types/game'
|
||||||
|
import { TechnologyType } from '@/types/game'
|
||||||
import * as publicLogic from '@/logic/publicLogic'
|
import * as publicLogic from '@/logic/publicLogic'
|
||||||
import * as resourceLogic from '@/logic/resourceLogic'
|
import * as resourceLogic from '@/logic/resourceLogic'
|
||||||
|
import * as planetLogic from '@/logic/planetLogic'
|
||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { SHIPS } = useGameConfig()
|
const { SHIPS } = useGameConfig()
|
||||||
const planet = computed(() => gameStore.currentPlanet)
|
const planet = computed(() => gameStore.currentPlanet)
|
||||||
|
|
||||||
|
// 获取科技加成
|
||||||
|
const techBonuses = computed(() => ({
|
||||||
|
mineralResearchLevel: gameStore.player.technologies[TechnologyType.MineralResearch] || 0,
|
||||||
|
crystalResearchLevel: gameStore.player.technologies[TechnologyType.CrystalResearch] || 0,
|
||||||
|
fuelResearchLevel: gameStore.player.technologies[TechnologyType.FuelResearch] || 0
|
||||||
|
}))
|
||||||
|
|
||||||
const production = computed(() =>
|
const production = computed(() =>
|
||||||
planet.value ? publicLogic.getResourceProduction(planet.value, gameStore.player.officers, gameStore.gameSpeed) : null
|
planet.value ? publicLogic.getResourceProduction(planet.value, gameStore.player.officers, gameStore.gameSpeed, techBonuses.value) : null
|
||||||
)
|
)
|
||||||
const capacity = computed(() => (planet.value ? publicLogic.getResourceCapacity(planet.value, gameStore.player.officers) : null))
|
const capacity = computed(() => (planet.value ? publicLogic.getResourceCapacity(planet.value, gameStore.player.officers) : null))
|
||||||
|
|
||||||
@@ -226,7 +285,7 @@
|
|||||||
// 资源产量详细breakdown
|
// 资源产量详细breakdown
|
||||||
const productionBreakdown = computed(() => {
|
const productionBreakdown = computed(() => {
|
||||||
if (!planet.value) return null
|
if (!planet.value) return null
|
||||||
return resourceLogic.calculateProductionBreakdown(planet.value, gameStore.player.officers, Date.now(), gameStore.gameSpeed)
|
return resourceLogic.calculateProductionBreakdown(planet.value, gameStore.player.officers, Date.now(), gameStore.gameSpeed, techBonuses.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 资源消耗详细breakdown
|
// 资源消耗详细breakdown
|
||||||
@@ -286,4 +345,45 @@
|
|||||||
gameStore.currentPlanetId = planet.value.parentPlanetId
|
gameStore.currentPlanetId = planet.value.parentPlanetId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 放弃殖民地相关
|
||||||
|
const showAbandonDialog = ref(false)
|
||||||
|
const showErrorDialog = ref(false)
|
||||||
|
const errorMessage = ref('')
|
||||||
|
|
||||||
|
// 是否显示放弃按钮(非主星才显示)
|
||||||
|
const canShowAbandonButton = computed(() => {
|
||||||
|
if (!planet.value) return false
|
||||||
|
// 找到主星(第一个非月球星球)
|
||||||
|
const mainPlanet = gameStore.player.planets.find(p => !p.isMoon)
|
||||||
|
// 当前星球不是主星时才显示放弃按钮
|
||||||
|
return mainPlanet && mainPlanet.id !== planet.value.id
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理放弃殖民地
|
||||||
|
const handleAbandonColony = () => {
|
||||||
|
if (!planet.value) return
|
||||||
|
|
||||||
|
const check = planetLogic.canAbandonColony(gameStore.player.planets, planet.value.id)
|
||||||
|
if (!check.canAbandon) {
|
||||||
|
showAbandonDialog.value = false
|
||||||
|
errorMessage.value = check.reason ? t(check.reason) : t('planet.abandonFailed')
|
||||||
|
showErrorDialog.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录当前星球ID用于后续切换
|
||||||
|
const abandonedPlanetId = planet.value.id
|
||||||
|
|
||||||
|
// 执行放弃
|
||||||
|
gameStore.player.planets = planetLogic.abandonColony(gameStore.player.planets, abandonedPlanetId)
|
||||||
|
|
||||||
|
// 切换到主星
|
||||||
|
const mainPlanet = gameStore.player.planets.find(p => !p.isMoon)
|
||||||
|
if (mainPlanet) {
|
||||||
|
gameStore.currentPlanetId = mainPlanet.id
|
||||||
|
}
|
||||||
|
|
||||||
|
showAbandonDialog.value = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -175,7 +175,8 @@
|
|||||||
techType,
|
techType,
|
||||||
currentLevel,
|
currentLevel,
|
||||||
gameStore.player.officers,
|
gameStore.player.officers,
|
||||||
gameStore.player.technologies
|
gameStore.player.technologies,
|
||||||
|
gameStore.player.planets
|
||||||
)
|
)
|
||||||
gameStore.player.researchQueue.push(queueItem)
|
gameStore.player.researchQueue.push(queueItem)
|
||||||
return true
|
return true
|
||||||
@@ -333,11 +334,33 @@
|
|||||||
const getResearchTime = (techType: TechnologyType): number => {
|
const getResearchTime = (techType: TechnologyType): number => {
|
||||||
if (!planet.value) return 0
|
if (!planet.value) return 0
|
||||||
const currentLevel = getTechLevel(techType)
|
const currentLevel = getTechLevel(techType)
|
||||||
const researchLabLevel = planet.value.buildings['researchLab'] || 0
|
const intergalacticResearchNetworkLevel = player.value.technologies[TechnologyType.IntergalacticResearchNetwork] || 0
|
||||||
|
|
||||||
|
// 计算有效研究实验室等级(考虑星际研究网络)
|
||||||
|
let researchLabLevel: number
|
||||||
|
if (intergalacticResearchNetworkLevel > 0) {
|
||||||
|
researchLabLevel = researchLogic.calculateEffectiveLabLevel(
|
||||||
|
gameStore.player.planets,
|
||||||
|
planet.value.id,
|
||||||
|
intergalacticResearchNetworkLevel
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
researchLabLevel = planet.value.buildings['researchLab'] || 0
|
||||||
|
}
|
||||||
|
|
||||||
const energyTechLevel = player.value.technologies['energyTechnology'] || 0
|
const energyTechLevel = player.value.technologies['energyTechnology'] || 0
|
||||||
|
const universityLevel = planet.value.buildings['university'] || 0
|
||||||
const bonuses = officerLogic.calculateActiveBonuses(player.value.officers, gameStore.gameTime)
|
const bonuses = officerLogic.calculateActiveBonuses(player.value.officers, gameStore.gameTime)
|
||||||
|
|
||||||
return researchLogic.calculateTechnologyTime(techType, currentLevel, bonuses.researchSpeedBonus, researchLabLevel, energyTechLevel)
|
return researchLogic.calculateTechnologyTime(
|
||||||
|
techType,
|
||||||
|
currentLevel,
|
||||||
|
bonuses.researchSpeedBonus,
|
||||||
|
researchLabLevel,
|
||||||
|
energyTechLevel,
|
||||||
|
1,
|
||||||
|
universityLevel
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否可以添加到等待队列
|
// 检查是否可以添加到等待队列
|
||||||
|
|||||||
@@ -114,6 +114,15 @@
|
|||||||
{{ gameStore.isPaused ? t('settings.resume') : t('settings.pause') }}
|
{{ gameStore.isPaused ? t('settings.resume') : t('settings.pause') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 战斗模式 -->
|
||||||
|
<div class="flex items-center justify-between p-4 border rounded-lg">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<h3 class="font-medium">{{ t('settings.battleMode') }}</h3>
|
||||||
|
<p class="text-sm text-muted-foreground">{{ t('settings.battleModeDesc') }}</p>
|
||||||
|
</div>
|
||||||
|
<Switch :checked="gameStore.battleToFinish" @update:checked="(val: boolean) => (gameStore.battleToFinish = val)" />
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@@ -334,7 +343,7 @@
|
|||||||
<PrivacyDialog v-model:open="showPrivacyDialog" />
|
<PrivacyDialog v-model:open="showPrivacyDialog" />
|
||||||
|
|
||||||
<!-- WebDAV 配置对话框 -->
|
<!-- WebDAV 配置对话框 -->
|
||||||
<WebDAVConfigDialog v-model:open="showWebDAVConfig" @saved="onWebDAVConfigSaved" />
|
<WebDAVConfigDialog v-model:open="showWebDAVConfig" />
|
||||||
|
|
||||||
<!-- WebDAV 文件列表对话框 -->
|
<!-- WebDAV 文件列表对话框 -->
|
||||||
<WebDAVFileListDialog v-model:open="showWebDAVFiles" :config="webdavConfig" @select="handleWebDAVDownload" />
|
<WebDAVFileListDialog v-model:open="showWebDAVFiles" :config="webdavConfig" @select="handleWebDAVDownload" />
|
||||||
@@ -389,12 +398,7 @@
|
|||||||
import WebDAVConfigDialog from '@/components/settings/WebDAVConfigDialog.vue'
|
import WebDAVConfigDialog from '@/components/settings/WebDAVConfigDialog.vue'
|
||||||
import WebDAVFileListDialog from '@/components/settings/WebDAVFileListDialog.vue'
|
import WebDAVFileListDialog from '@/components/settings/WebDAVFileListDialog.vue'
|
||||||
import { useHints } from '@/composables/useHints'
|
import { useHints } from '@/composables/useHints'
|
||||||
import {
|
import { uploadToWebDAV, downloadFromWebDAV } from '@/services/webdavService'
|
||||||
type WebDAVConfig,
|
|
||||||
getWebDAVConfig,
|
|
||||||
uploadToWebDAV,
|
|
||||||
downloadFromWebDAV
|
|
||||||
} from '@/services/webdavService'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { hintsEnabled, setHintsEnabled, resetHints } = useHints()
|
const { hintsEnabled, setHintsEnabled, resetHints } = useHints()
|
||||||
@@ -415,7 +419,7 @@
|
|||||||
// WebDAV 相关状态
|
// WebDAV 相关状态
|
||||||
const showWebDAVConfig = ref(false)
|
const showWebDAVConfig = ref(false)
|
||||||
const showWebDAVFiles = ref(false)
|
const showWebDAVFiles = ref(false)
|
||||||
const webdavConfig = ref<WebDAVConfig | null>(getWebDAVConfig())
|
const webdavConfig = computed(() => gameStore.webdavConfig)
|
||||||
const isWebDAVUploading = ref(false)
|
const isWebDAVUploading = ref(false)
|
||||||
|
|
||||||
// 确保通知设置存在
|
// 确保通知设置存在
|
||||||
@@ -586,12 +590,22 @@
|
|||||||
const fileName = `${pkg.name}-${new Date().toISOString().slice(0, 10)}-${Date.now()}.json`
|
const fileName = `${pkg.name}-${new Date().toISOString().slice(0, 10)}-${Date.now()}.json`
|
||||||
const jsonString = JSON.stringify(exportData, null, 2)
|
const jsonString = JSON.stringify(exportData, null, 2)
|
||||||
|
|
||||||
// Android 保存到 Documents 目录
|
// Android 保存到公共 Downloads 目录
|
||||||
if (Capacitor.isNativePlatform()) {
|
if (Capacitor.isNativePlatform()) {
|
||||||
|
// 检查并请求存储权限
|
||||||
|
const permStatus = await Filesystem.checkPermissions()
|
||||||
|
if (permStatus.publicStorage !== 'granted') {
|
||||||
|
const reqResult = await Filesystem.requestPermissions()
|
||||||
|
if (reqResult.publicStorage !== 'granted') {
|
||||||
|
toast.error(t('settings.storagePermissionDenied'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result = await Filesystem.writeFile({
|
const result = await Filesystem.writeFile({
|
||||||
path: fileName,
|
path: `Download/${fileName}`,
|
||||||
data: jsonString,
|
data: jsonString,
|
||||||
directory: Directory.Documents,
|
directory: Directory.ExternalStorage,
|
||||||
encoding: Encoding.UTF8
|
encoding: Encoding.UTF8
|
||||||
})
|
})
|
||||||
toast.success(t('settings.exportSuccessWithPath', { path: result.uri }))
|
toast.success(t('settings.exportSuccessWithPath', { path: result.uri }))
|
||||||
@@ -758,11 +772,6 @@
|
|||||||
gameStore.player.backgroundEnabled = val
|
gameStore.player.backgroundEnabled = val
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebDAV 配置保存回调
|
|
||||||
const onWebDAVConfigSaved = () => {
|
|
||||||
webdavConfig.value = getWebDAVConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebDAV 上传
|
// WebDAV 上传
|
||||||
const handleWebDAVUpload = async () => {
|
const handleWebDAVUpload = async () => {
|
||||||
if (!webdavConfig.value) return
|
if (!webdavConfig.value) return
|
||||||
@@ -792,7 +801,7 @@
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success(t('settings.webdav.uploadSuccess'))
|
toast.success(t('settings.webdav.uploadSuccess'))
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || t('settings.webdav.uploadFailed'))
|
toast.error(t(result.messageKey) || t('settings.webdav.uploadFailed'))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('WebDAV upload failed:', error)
|
console.error('WebDAV upload failed:', error)
|
||||||
@@ -810,7 +819,7 @@
|
|||||||
const result = await downloadFromWebDAV(webdavConfig.value, fileName)
|
const result = await downloadFromWebDAV(webdavConfig.value, fileName)
|
||||||
|
|
||||||
if (!result.success || !result.data) {
|
if (!result.success || !result.data) {
|
||||||
toast.error(result.message || t('settings.webdav.downloadFailed'))
|
toast.error(t(result.messageKey) || t('settings.webdav.downloadFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,20 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 拆除数量输入 -->
|
||||||
|
<div v-if="(planet.fleet[shipType] || 0) > 0" class="space-y-2">
|
||||||
|
<Label :for="`scrap-quantity-${shipType}`" class="text-xs sm:text-sm text-destructive">{{ t('shipyardView.scrapQuantity') }}</Label>
|
||||||
|
<Input
|
||||||
|
:id="`scrap-quantity-${shipType}`"
|
||||||
|
v-model.number="scrapQuantities[shipType]"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
:max="planet.fleet[shipType] || 0"
|
||||||
|
placeholder="0"
|
||||||
|
class="text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="quantities[shipType] > 0" class="text-xs sm:text-sm space-y-1.5 sm:space-y-2 p-2.5 sm:p-3 bg-muted rounded-lg">
|
<div v-if="quantities[shipType] > 0" class="text-xs sm:text-sm space-y-1.5 sm:space-y-2 p-2.5 sm:p-3 bg-muted rounded-lg">
|
||||||
<p class="font-medium text-muted-foreground">{{ t('shipyardView.totalCost') }}:</p>
|
<p class="font-medium text-muted-foreground">{{ t('shipyardView.totalCost') }}:</p>
|
||||||
<div class="space-y-1 sm:space-y-1.5">
|
<div class="space-y-1 sm:space-y-1.5">
|
||||||
@@ -133,6 +147,36 @@
|
|||||||
>
|
>
|
||||||
{{ t('queue.addToWaiting') }}
|
{{ t('queue.addToWaiting') }}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<!-- 拆除返还资源显示 -->
|
||||||
|
<div v-if="scrapQuantities[shipType] > 0" class="text-xs sm:text-sm space-y-1.5 sm:space-y-2 p-2.5 sm:p-3 bg-destructive/10 rounded-lg border border-destructive/30">
|
||||||
|
<p class="font-medium text-destructive">{{ t('shipyardView.scrapRefund') }}:</p>
|
||||||
|
<div class="space-y-1 sm:space-y-1.5">
|
||||||
|
<div
|
||||||
|
v-for="resourceType in costResourceTypes"
|
||||||
|
:key="resourceType.key"
|
||||||
|
v-show="resourceType.key !== 'darkMatter' || getScrapRefund(shipType).darkMatter > 0"
|
||||||
|
class="flex items-center gap-1.5 sm:gap-2"
|
||||||
|
>
|
||||||
|
<ResourceIcon :type="resourceType.key" size="sm" />
|
||||||
|
<span class="text-xs">{{ t(`resources.${resourceType.key}`) }}:</span>
|
||||||
|
<span class="font-medium text-xs sm:text-sm text-green-500">
|
||||||
|
+{{ formatNumber(getScrapRefund(shipType)[resourceType.key]) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 拆除按钮 -->
|
||||||
|
<Button
|
||||||
|
v-if="(planet.fleet[shipType] || 0) > 0"
|
||||||
|
@click="handleScrap(shipType, $event)"
|
||||||
|
:disabled="!canScrap(shipType)"
|
||||||
|
variant="destructive"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
{{ t('shipyardView.scrap') }}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -243,6 +287,25 @@
|
|||||||
[ShipType.Deathstar]: 0
|
[ShipType.Deathstar]: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 每种舰船的拆除数量
|
||||||
|
const scrapQuantities = ref<Record<ShipType, number>>({
|
||||||
|
[ShipType.LightFighter]: 0,
|
||||||
|
[ShipType.HeavyFighter]: 0,
|
||||||
|
[ShipType.Cruiser]: 0,
|
||||||
|
[ShipType.Battleship]: 0,
|
||||||
|
[ShipType.Battlecruiser]: 0,
|
||||||
|
[ShipType.Bomber]: 0,
|
||||||
|
[ShipType.Destroyer]: 0,
|
||||||
|
[ShipType.SmallCargo]: 0,
|
||||||
|
[ShipType.LargeCargo]: 0,
|
||||||
|
[ShipType.ColonyShip]: 0,
|
||||||
|
[ShipType.Recycler]: 0,
|
||||||
|
[ShipType.EspionageProbe]: 0,
|
||||||
|
[ShipType.SolarSatellite]: 0,
|
||||||
|
[ShipType.DarkMatterHarvester]: 0,
|
||||||
|
[ShipType.Deathstar]: 0
|
||||||
|
})
|
||||||
|
|
||||||
const buildShip = (shipType: ShipType, quantity: number): { success: boolean; reason?: string } => {
|
const buildShip = (shipType: ShipType, quantity: number): { success: boolean; reason?: string } => {
|
||||||
if (!gameStore.currentPlanet) return { success: false }
|
if (!gameStore.currentPlanet) return { success: false }
|
||||||
const validation = shipValidation.validateShipBuild(gameStore.currentPlanet, shipType, quantity, gameStore.player.technologies)
|
const validation = shipValidation.validateShipBuild(gameStore.currentPlanet, shipType, quantity, gameStore.player.technologies)
|
||||||
@@ -380,4 +443,54 @@
|
|||||||
waitingQueueLogic.addToBuildWaitingQueue(planet.value, item)
|
waitingQueueLogic.addToBuildWaitingQueue(planet.value, item)
|
||||||
quantities.value[shipType] = 0
|
quantities.value[shipType] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算拆除返还资源
|
||||||
|
const getScrapRefund = (shipType: ShipType) => {
|
||||||
|
const quantity = scrapQuantities.value[shipType]
|
||||||
|
return shipLogic.calculateShipScrapRefund(shipType, quantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否可以拆除
|
||||||
|
const canScrap = (shipType: ShipType): boolean => {
|
||||||
|
if (!planet.value) return false
|
||||||
|
|
||||||
|
const quantity = scrapQuantities.value[shipType]
|
||||||
|
if (quantity <= 0) return false
|
||||||
|
|
||||||
|
const available = planet.value.fleet[shipType] || 0
|
||||||
|
return available >= quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拆除舰船
|
||||||
|
const handleScrap = (shipType: ShipType, _event: MouseEvent) => {
|
||||||
|
// 防抖:防止快速点击
|
||||||
|
if (isProcessing.value) return
|
||||||
|
isProcessing.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
isProcessing.value = false
|
||||||
|
}, DEBOUNCE_DELAY)
|
||||||
|
|
||||||
|
const quantity = scrapQuantities.value[shipType]
|
||||||
|
if (quantity <= 0) {
|
||||||
|
alertDialogTitle.value = t('shipyardView.inputError')
|
||||||
|
alertDialogMessage.value = t('shipyardView.inputErrorMessage')
|
||||||
|
alertDialogOpen.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameStore.currentPlanet) return
|
||||||
|
|
||||||
|
const validation = shipValidation.validateShipScrap(gameStore.currentPlanet, shipType, quantity)
|
||||||
|
if (!validation.valid) {
|
||||||
|
alertDialogTitle.value = t('shipyardView.scrapFailed')
|
||||||
|
alertDialogMessage.value = validation.reason ? t(validation.reason) : t('shipyardView.scrapFailedMessage')
|
||||||
|
alertDialogOpen.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行拆除
|
||||||
|
const queueItem = shipValidation.executeShipScrap(gameStore.currentPlanet, shipType, quantity, gameStore.player.officers)
|
||||||
|
gameStore.currentPlanet.buildQueue.push(queueItem)
|
||||||
|
scrapQuantities.value[shipType] = 0
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -304,8 +304,24 @@ const simulateBattle = (attacker: BattleSideData, defender: BattleSideData, maxR
|
|||||||
} else if (defenderUnits.length === 0) {
|
} else if (defenderUnits.length === 0) {
|
||||||
winner = 'attacker'
|
winner = 'attacker'
|
||||||
} else {
|
} else {
|
||||||
// OGame原版规则:6回合后双方都有剩余单位时判定为平局
|
// 达到最大回合数后双方都有剩余单位
|
||||||
winner = 'draw'
|
// 如果是战斗到底模式(maxRounds > 6),根据剩余战力判定胜负
|
||||||
|
if (maxRounds > 6) {
|
||||||
|
// 计算剩余战力
|
||||||
|
const attackerPower = attackerUnits.reduce((sum, u) => sum + u.count * u.armor, 0)
|
||||||
|
const defenderPower = defenderUnits.reduce((sum, u) => sum + u.count * u.armor, 0)
|
||||||
|
// 战力差距超过20%判定胜负,否则平局
|
||||||
|
if (attackerPower > defenderPower * 1.2) {
|
||||||
|
winner = 'attacker'
|
||||||
|
} else if (defenderPower > attackerPower * 1.2) {
|
||||||
|
winner = 'defender'
|
||||||
|
} else {
|
||||||
|
winner = 'draw'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// OGame原版规则:6回合后双方都有剩余单位时判定为平局
|
||||||
|
winner = 'draw'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import tailwindcss from '@tailwindcss/vite'
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import autoprefixer from 'autoprefixer'
|
|
||||||
import pkg from './package.json'
|
import pkg from './package.json'
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
// @ts-expect-error - Vite CSS transformer type
|
||||||
export default defineConfig(async () => {
|
export default defineConfig(async () => {
|
||||||
const plugins = [
|
const plugins = [
|
||||||
vue(),
|
vue(),
|
||||||
@@ -23,14 +23,7 @@ export default defineConfig(async () => {
|
|||||||
background_color: '#000000',
|
background_color: '#000000',
|
||||||
display: 'fullscreen',
|
display: 'fullscreen',
|
||||||
orientation: 'any',
|
orientation: 'any',
|
||||||
icons: [
|
icons: [{ src: 'logo.svg', sizes: 'any', type: 'image/svg+xml', purpose: 'any' }]
|
||||||
{
|
|
||||||
src: 'logo.svg',
|
|
||||||
sizes: 'any',
|
|
||||||
type: 'image/svg+xml',
|
|
||||||
purpose: 'any'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
workbox: {
|
workbox: {
|
||||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,mp3,wav,json}'],
|
globPatterns: ['**/*.{js,css,html,ico,png,svg,mp3,wav,json}'],
|
||||||
@@ -39,13 +32,7 @@ export default defineConfig(async () => {
|
|||||||
{
|
{
|
||||||
urlPattern: ({ request }) => request.destination === 'image' || request.destination === 'audio',
|
urlPattern: ({ request }) => request.destination === 'image' || request.destination === 'audio',
|
||||||
handler: 'CacheFirst', // 优先使用缓存
|
handler: 'CacheFirst', // 优先使用缓存
|
||||||
options: {
|
options: { cacheName: 'game-assets', expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 } }
|
||||||
cacheName: 'game-assets',
|
|
||||||
expiration: {
|
|
||||||
maxEntries: 100,
|
|
||||||
maxAgeSeconds: 30 * 24 * 60 * 60 // 缓存 30 天
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -110,12 +97,17 @@ export default defineConfig(async () => {
|
|||||||
plugins,
|
plugins,
|
||||||
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
|
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
|
||||||
css: {
|
css: {
|
||||||
postcss: {
|
// 使用 lightningcss 处理 CSS,自动转换 oklch 等新语法为兼容格式
|
||||||
plugins: [
|
transformer: 'lightningcss',
|
||||||
autoprefixer({
|
lightningcss: {
|
||||||
overrideBrowserslist: ['Android >= 4.1', 'iOS >= 7.1', 'Chrome >= 31', 'Firefox >= 31', 'ie >= 8']
|
// 目标浏览器:降低到更保守的版本以支持华为等国产手机 WebView
|
||||||
})
|
targets: {
|
||||||
]
|
android: (4 << 16) | (4 << 8), // Android 4.4
|
||||||
|
chrome: 49 << 16, // Chrome 49 (Android 4.4 WebView)
|
||||||
|
ios_saf: (9 << 16) | (3 << 8) // iOS Safari 9.3
|
||||||
|
},
|
||||||
|
// 禁用现代 CSS 特性,确保兼容旧版浏览器
|
||||||
|
drafts: { customMedia: false }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 优化依赖预构建
|
// 优化依赖预构建
|
||||||
|
|||||||
28
调整基础矿脉恢复速度.ini
Normal file
28
调整基础矿脉恢复速度.ini
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
OGame-Vue-Ts v1.6.0 更新说明
|
||||||
|
|
||||||
|
调整基础矿脉恢复速度
|
||||||
|
调整地质研究站每级恢复速率
|
||||||
|
调整建造死星所需资源
|
||||||
|
修复研究等待队列资源扣除但等级不增加的问题
|
||||||
|
修复攻击NPC后NPC拥有资源不减少的问题
|
||||||
|
修复导弹攻击后反弹道导弹不减少的问题
|
||||||
|
修复矿脉上限加成计算错误问题
|
||||||
|
为战斗模拟器新增加从侦查报告一键填入的功能
|
||||||
|
调整建筑与研究升级时间
|
||||||
|
修复文本丢失问题
|
||||||
|
调整月球基地初始建造成本
|
||||||
|
修复无法派遣舰队到自己星球位置的问题
|
||||||
|
修复敌对NPC会无脑派遣舰队回收残骸导致产生更多残骸的恶性循环问题
|
||||||
|
修复敌对NPC反复派回收船到玩家星球的问题
|
||||||
|
修复NPC无脑进攻问题
|
||||||
|
修复贸易邮件大量生成导致淹没其他邮件问题
|
||||||
|
添加舰船拆除功能
|
||||||
|
添加放弃殖民地功能
|
||||||
|
修复从月球切换回母星会触发白屏问题
|
||||||
|
修复死星毁灭任务跳过战斗的问题
|
||||||
|
修复对战动画偶现卡顿导致弹窗无响应问题
|
||||||
|
增加战斗到底模式, 开启后支持最多战斗100回合
|
||||||
|
|
||||||
|
添加建筑: 大学
|
||||||
|
添加科技: 星际研究网络, 矿物研究, 晶体研究, 燃料研究
|
||||||
|
|
||||||
Reference in New Issue
Block a user