LLM-test/test/code/3/minimax-2.1-cc.html

1927 lines
70 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>星际文明探索者</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@font-face {
font-family: 'Tech';
src: local('Orbitron'), local('Rajdhani'), local('Segoe UI');
}
body {
overflow: hidden;
background: #000;
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
color: #fff;
}
#game-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
#game-canvas {
width: 100%;
height: 100%;
display: block;
}
/* Glassmorphism UI Panels */
.glass-panel {
background: rgba(15, 20, 40, 0.7);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(100, 150, 255, 0.3);
border-radius: 12px;
box-shadow: 0 0 30px rgba(50, 100, 200, 0.2),
inset 0 0 20px rgba(100, 150, 255, 0.05);
}
/* HUD Elements */
#hud-top {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 30px;
padding: 15px 30px;
z-index: 100;
}
.hud-item {
text-align: center;
}
.hud-label {
font-size: 11px;
color: rgba(150, 180, 255, 0.7);
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 5px;
}
.hud-value {
font-size: 24px;
font-weight: bold;
background: linear-gradient(135deg, #00d4ff, #7b68ee);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Shield Bar */
#shield-bar {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
width: 300px;
height: 20px;
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
overflow: hidden;
border: 1px solid rgba(100, 150, 255, 0.3);
z-index: 100;
}
#shield-fill {
height: 100%;
width: 100%;
background: linear-gradient(90deg, #00d4ff, #7b68ee);
border-radius: 10px;
transition: width 0.3s ease;
box-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
}
#shield-text {
position: absolute;
width: 100%;
text-align: center;
line-height: 20px;
font-size: 12px;
text-shadow: 0 0 10px rgba(0, 212, 255, 0.8);
}
/* Resource Display */
#resource-panel {
position: fixed;
top: 80px;
right: 20px;
padding: 20px;
min-width: 180px;
z-index: 100;
}
.resource-item {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
}
.resource-icon {
width: 24px;
height: 24px;
border-radius: 50%;
}
.resource-icon.mineral { background: linear-gradient(135deg, #ff6b6b, #ffa500); }
.resource-icon.crystal { background: linear-gradient(135deg, #00d4ff, #7b68ee); }
.resource-icon.artifact { background: linear-gradient(135deg, #ffd700, #ff69b4); }
.resource-name {
font-size: 12px;
color: rgba(200, 220, 255, 0.7);
}
.resource-count {
margin-left: auto;
font-size: 18px;
font-weight: bold;
color: #fff;
}
/* Upgrade Panel */
#upgrade-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 30px;
display: none;
z-index: 200;
max-width: 500px;
}
#upgrade-panel.active {
display: block;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.upgrade-title {
font-size: 28px;
text-align: center;
margin-bottom: 25px;
background: linear-gradient(135deg, #00d4ff, #7b68ee);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.upgrade-option {
background: rgba(30, 40, 70, 0.8);
border: 1px solid rgba(100, 150, 255, 0.3);
border-radius: 10px;
padding: 20px;
margin-bottom: 15px;
cursor: pointer;
transition: all 0.3s ease;
}
.upgrade-option:hover {
border-color: rgba(0, 212, 255, 0.8);
background: rgba(50, 70, 120, 0.8);
transform: translateX(5px);
}
.upgrade-name {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
color: #00d4ff;
}
.upgrade-desc {
font-size: 13px;
color: rgba(200, 220, 255, 0.7);
margin-bottom: 10px;
}
.upgrade-level {
font-size: 12px;
color: #7b68ee;
}
/* Scan Panel */
#scan-panel {
position: fixed;
bottom: 70px;
left: 20px;
padding: 20px;
max-width: 300px;
display: none;
z-index: 100;
}
#scan-panel.active {
display: block;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
.scan-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 15px;
color: #00d4ff;
}
.scan-data {
font-size: 13px;
line-height: 1.8;
color: rgba(200, 220, 255, 0.8);
}
/* Minimap */
#minimap {
position: fixed;
bottom: 20px;
right: 20px;
width: 150px;
height: 150px;
border-radius: 50%;
overflow: hidden;
border: 2px solid rgba(100, 150, 255, 0.5);
z-index: 100;
}
#minimap-canvas {
width: 100%;
height: 100%;
}
/* Start Screen */
#start-screen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: linear-gradient(135deg, #0a0a1a 0%, #1a1a3a 50%, #0a0a2a 100%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 1000;
}
#start-screen.hidden {
display: none;
}
.game-title {
font-size: 56px;
font-weight: bold;
margin-bottom: 20px;
background: linear-gradient(135deg, #00d4ff, #7b68ee, #ff69b4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 50px rgba(0, 212, 255, 0.5);
animation: titleGlow 2s ease-in-out infinite alternate;
}
@keyframes titleGlow {
from { filter: drop-shadow(0 0 20px rgba(0, 212, 255, 0.5)); }
to { filter: drop-shadow(0 0 40px rgba(123, 104, 238, 0.8)); }
}
.game-subtitle {
font-size: 18px;
color: rgba(200, 220, 255, 0.6);
margin-bottom: 50px;
letter-spacing: 5px;
}
.start-btn {
padding: 15px 60px;
font-size: 20px;
background: linear-gradient(135deg, rgba(0, 212, 255, 0.2), rgba(123, 104, 238, 0.2));
border: 2px solid rgba(0, 212, 255, 0.5);
border-radius: 30px;
color: #fff;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.start-btn:hover {
background: linear-gradient(135deg, rgba(0, 212, 255, 0.4), rgba(123, 104, 238, 0.4));
border-color: rgba(0, 212, 255, 0.8);
transform: scale(1.05);
box-shadow: 0 0 30px rgba(0, 212, 255, 0.5);
}
.controls-info {
margin-top: 50px;
font-size: 14px;
color: rgba(200, 220, 255, 0.5);
text-align: center;
line-height: 2;
}
/* Crosshair */
#crosshair {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
z-index: 50;
}
.crosshair-line {
position: absolute;
background: rgba(0, 212, 255, 0.8);
}
.crosshair-line.horizontal {
width: 20px;
height: 2px;
top: 50%;
transform: translateY(-50%);
}
.crosshair-line.left { left: -30px; }
.crosshair-line.right { right: -30px; }
.crosshair-line.vertical {
width: 2px;
height: 20px;
left: 50%;
transform: translateX(-50%);
}
.crosshair-line.top { top: -30px; }
.crosshair-line.bottom { bottom: -30px; }
/* Notification */
#notification {
position: fixed;
top: 100px;
left: 50%;
transform: translateX(-50%);
padding: 15px 30px;
background: rgba(30, 40, 70, 0.9);
border: 1px solid rgba(0, 212, 255, 0.5);
border-radius: 10px;
font-size: 16px;
color: #00d4ff;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 300;
}
#notification.show {
opacity: 1;
}
/* Pause Menu */
#pause-menu {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.8);
display: none;
justify-content: center;
align-items: center;
z-index: 500;
}
#pause-menu.active {
display: flex;
}
.pause-content {
text-align: center;
}
.pause-title {
font-size: 48px;
margin-bottom: 40px;
background: linear-gradient(135deg, #00d4ff, #7b68ee);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.pause-btn {
display: block;
width: 200px;
margin: 15px auto;
padding: 12px;
font-size: 18px;
background: rgba(30, 40, 70, 0.8);
border: 1px solid rgba(100, 150, 255, 0.3);
border-radius: 10px;
color: #fff;
cursor: pointer;
transition: all 0.3s ease;
}
.pause-btn:hover {
background: rgba(50, 70, 120, 0.8);
border-color: rgba(0, 212, 255, 0.5);
}
/* Combat Indicator */
#combat-indicator {
position: fixed;
top: 50%;
right: 50px;
transform: translateY(-50%);
padding: 10px 20px;
background: rgba(255, 50, 50, 0.2);
border: 1px solid rgba(255, 50, 50, 0.5);
border-radius: 5px;
color: #ff4444;
font-size: 14px;
display: none;
z-index: 100;
}
#combat-indicator.active {
display: block;
animation: pulse 0.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Level Up Animation */
#levelup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: radial-gradient(circle, rgba(123, 104, 238, 0.3), transparent);
display: none;
justify-content: center;
align-items: center;
z-index: 400;
}
#levelup-overlay.active {
display: flex;
animation: levelupFade 0.5s ease;
}
@keyframes levelupFade {
from { opacity: 0; }
to { opacity: 1; }
}
.levelup-text {
font-size: 72px;
font-weight: bold;
background: linear-gradient(135deg, #ffd700, #ff6b6b);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: levelupBounce 0.5s ease;
}
@keyframes levelupBounce {
0% { transform: scale(0.5); opacity: 0; }
50% { transform: scale(1.2); }
100% { transform: scale(1); opacity: 1; }
}
/* Loading Screen */
#loading-screen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: #0a0a1a;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 2000;
transition: opacity 0.5s ease;
}
#loading-screen.hidden {
opacity: 0;
pointer-events: none;
}
.loading-spinner {
width: 60px;
height: 60px;
border: 3px solid rgba(100, 150, 255, 0.2);
border-top-color: #00d4ff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
margin-top: 20px;
font-size: 16px;
color: rgba(200, 220, 255, 0.7);
}
/* XP Bar */
#xp-bar {
position: fixed;
bottom: 60px;
left: 50%;
transform: translateX(-50%);
width: 400px;
height: 8px;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
overflow: hidden;
border: 1px solid rgba(100, 150, 255, 0.2);
z-index: 100;
}
#xp-fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, #ffd700, #ff6b6b);
border-radius: 4px;
transition: width 0.3s ease;
}
#xp-text {
position: absolute;
width: 100%;
text-align: center;
font-size: 10px;
line-height: 8px;
color: rgba(255, 255, 255, 0.8);
}
/* Help Panel */
#help-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 30px;
max-width: 600px;
display: none;
z-index: 200;
}
#help-panel.active {
display: block;
animation: fadeIn 0.3s ease;
}
.help-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 20px;
}
.help-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
}
.help-key {
background: rgba(0, 212, 255, 0.2);
border: 1px solid rgba(0, 212, 255, 0.5);
padding: 5px 10px;
border-radius: 5px;
font-family: monospace;
min-width: 80px;
text-align: center;
}
</style>
</head>
<body>
<!-- Loading Screen -->
<div id="loading-screen">
<div class="loading-spinner"></div>
<div class="loading-text">正在初始化星际引擎...</div>
</div>
<!-- Start Screen -->
<div id="start-screen">
<h1 class="game-title">星际文明探索者</h1>
<p class="game-subtitle">STELLAR CIVILIZATION EXPLORER</p>
<button class="start-btn" onclick="game.start()">开始探索</button>
<div class="controls-info">
<p>WASD - 移动飞船 | 鼠标 - 瞄准 | 左键 - 射击</p>
<p>空格 - 护盾 | E - 扫描星球 | Q - 特殊技能 | ESC - 暂停</p>
</div>
</div>
<!-- Game Container -->
<div id="game-container">
<canvas id="game-canvas"></canvas>
</div>
<!-- HUD Top -->
<div id="hud-top" class="glass-panel" style="display: none;">
<div class="hud-item">
<div class="hud-label">等级</div>
<div class="hud-value" id="level-display">1</div>
</div>
<div class="hud-item">
<div class="hud-label">分数</div>
<div class="hud-value" id="score-display">0</div>
</div>
<div class="hud-item">
<div class="hud-label">敌人</div>
<div class="hud-value" id="enemy-display">0</div>
</div>
</div>
<!-- Shield Bar -->
<div id="shield-bar" style="display: none;">
<div id="shield-fill"></div>
<div id="shield-text">护盾 100%</div>
</div>
<!-- XP Bar -->
<div id="xp-bar" style="display: none;">
<div id="xp-fill"></div>
<div id="xp-text">0 / 100 XP</div>
</div>
<!-- Resource Panel -->
<div id="resource-panel" class="glass-panel" style="display: none;">
<div class="resource-item">
<div class="resource-icon mineral"></div>
<span class="resource-name">矿物</span>
<span class="resource-count" id="mineral-count">0</span>
</div>
<div class="resource-item">
<div class="resource-icon crystal"></div>
<span class="resource-name">能量晶体</span>
<span class="resource-count" id="crystal-count">0</span>
</div>
<div class="resource-item">
<div class="resource-icon artifact"></div>
<span class="resource-name">遗迹碎片</span>
<span class="resource-count" id="artifact-count">0</span>
</div>
</div>
<!-- Minimap -->
<div id="minimap" class="glass-panel" style="display: none;">
<canvas id="minimap-canvas"></canvas>
</div>
<!-- Crosshair -->
<div id="crosshair" style="display: none;">
<div class="crosshair-line horizontal left"></div>
<div class="crosshair-line horizontal right"></div>
<div class="crosshair-line vertical top"></div>
<div class="crosshair-line vertical bottom"></div>
</div>
<!-- Combat Indicator -->
<div id="combat-indicator">⚠ 战斗进行中</div>
<!-- Scan Panel -->
<div id="scan-panel" class="glass-panel">
<div class="scan-title" id="scan-name">扫描数据</div>
<div class="scan-data" id="scan-data"></div>
</div>
<!-- Upgrade Panel -->
<div id="upgrade-panel" class="glass-panel">
<div class="upgrade-title">系统升级</div>
<div class="upgrade-option" onclick="game.selectUpgrade('engine')">
<div class="upgrade-name">推进系统</div>
<div class="upgrade-desc">提升移动速度和加速度</div>
<div class="upgrade-level" id="engine-level">当前等级: 1</div>
</div>
<div class="upgrade-option" onclick="game.selectUpgrade('shield')">
<div class="upgrade-name">能量护盾</div>
<div class="upgrade-desc">增加护盾容量和恢复速度</div>
<div class="upgrade-level" id="shield-level">当前等级: 1</div>
</div>
<div class="upgrade-option" onclick="game.selectUpgrade('weapon')">
<div class="upgrade-name">武器系统</div>
<div class="upgrade-desc">提升武器威力和射速</div>
<div class="upgrade-level" id="weapon-level">当前等级: 1</div>
</div>
<div class="upgrade-option" onclick="game.selectUpgrade('scanner')">
<div class="upgrade-name">扫描仪</div>
<div class="upgrade-desc">增加资源发现概率和信息完整度</div>
<div class="upgrade-level" id="scanner-level">当前等级: 1</div>
</div>
</div>
<!-- Notification -->
<div id="notification"></div>
<!-- Pause Menu -->
<div id="pause-menu">
<div class="pause-content glass-panel" style="padding: 40px;">
<div class="pause-title">暂停</div>
<button class="pause-btn" onclick="game.resume()">继续游戏</button>
<button class="pause-btn" onclick="game.showHelp()">操作说明</button>
<button class="pause-btn" onclick="game.saveGame()">保存进度</button>
<button class="pause-btn" onclick="game.loadGame()">读取存档</button>
<button class="pause-btn" onclick="game.returnToMenu()">返回主菜单</button>
</div>
</div>
<!-- Help Panel -->
<div id="help-panel" class="glass-panel">
<div class="upgrade-title">操作说明</div>
<div class="help-grid">
<div class="help-item">
<span class="help-key">WASD</span>
<span>移动飞船</span>
</div>
<div class="help-item">
<span class="help-key">鼠标</span>
<span>瞄准方向</span>
</div>
<div class="help-item">
<span class="help-key">左键</span>
<span>发射武器</span>
</div>
<div class="help-item">
<span class="help-key">空格</span>
<span>激活护盾</span>
</div>
<div class="help-item">
<span class="help-key">E</span>
<span>扫描附近星球</span>
</div>
<div class="help-item">
<span class="help-key">Q</span>
<span>释放特殊技能</span>
</div>
<div class="help-item">
<span class="help-key">ESC</span>
<span>暂停菜单</span>
</div>
</div>
<button class="pause-btn" style="margin-top: 20px;" onclick="game.hideHelp()">返回游戏</button>
</div>
<!-- Level Up Overlay -->
<div id="levelup-overlay">
<div class="levelup-text">LEVEL UP!</div>
</div>
<!-- CDN Libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/howler/2.2.4/howler.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/tsparticles/3.9.1/tsparticles.min.js"></script>
<script>
// ==================== Audio System ====================
class AudioSystem {
constructor() {
this.sounds = {};
this.bgmVolume = 0.3;
this.sfxVolume = 0.5;
this.initialized = false;
}
init() {
// Create audio context for synthesized sounds
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
this.initialized = true;
console.log('音频系统已初始化');
}
playShoot() {
if (!this.initialized) return;
const osc = this.audioContext.createOscillator();
const gain = this.audioContext.createGain();
osc.connect(gain);
gain.connect(this.audioContext.destination);
osc.frequency.setValueAtTime(880, this.audioContext.currentTime);
osc.frequency.exponentialRampToValueAtTime(110, this.audioContext.currentTime + 0.1);
gain.gain.setValueAtTime(0.3, this.audioContext.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + 0.1);
osc.start();
osc.stop(this.audioContext.currentTime + 0.1);
}
playExplosion() {
if (!this.initialized) return;
const bufferSize = this.audioContext.sampleRate * 0.3;
const buffer = this.audioContext.createBuffer(1, bufferSize, this.audioContext.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / bufferSize, 2);
}
const source = this.audioContext.createBufferSource();
const gain = this.audioContext.createGain();
source.buffer = buffer;
source.connect(gain);
gain.connect(this.audioContext.destination);
gain.gain.setValueAtTime(0.5, this.audioContext.currentTime);
source.start();
}
playCollect() {
if (!this.initialized) return;
const osc = this.audioContext.createOscillator();
const gain = this.audioContext.createGain();
osc.connect(gain);
gain.connect(this.audioContext.destination);
osc.frequency.setValueAtTime(523, this.audioContext.currentTime);
osc.frequency.setValueAtTime(659, this.audioContext.currentTime + 0.1);
osc.frequency.setValueAtTime(784, this.audioContext.currentTime + 0.2);
gain.gain.setValueAtTime(0.2, this.audioContext.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + 0.3);
osc.start();
osc.stop(this.audioContext.currentTime + 0.3);
}
playLevelUp() {
if (!this.initialized) return;
const notes = [523, 659, 784, 1047];
notes.forEach((freq, i) => {
const osc = this.audioContext.createOscillator();
const gain = this.audioContext.createGain();
osc.connect(gain);
gain.connect(this.audioContext.destination);
osc.frequency.setValueAtTime(freq, this.audioContext.currentTime + i * 0.1);
gain.gain.setValueAtTime(0.3, this.audioContext.currentTime + i * 0.1);
gain.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + i * 0.1 + 0.3);
osc.start(this.audioContext.currentTime + i * 0.1);
osc.stop(this.audioContext.currentTime + i * 0.1 + 0.3);
});
}
}
// ==================== Particle System ====================
class ParticleSystem {
constructor(scene) {
this.scene = scene;
this.particles = [];
this.maxParticles = 500;
}
createEngineTrail(position, direction) {
for (let i = 0; i < 3; i++) {
const geometry = new THREE.SphereGeometry(0.1, 8, 8);
const material = new THREE.MeshBasicMaterial({
color: new THREE.Color().setHSL(0.1 + Math.random() * 0.1, 1, 0.5),
transparent: true,
opacity: 0.8
});
const particle = new THREE.Mesh(geometry, material);
particle.position.copy(position);
particle.velocity = new THREE.Vector3(
-direction.x * 2 + (Math.random() - 0.5),
-direction.y * 2 + (Math.random() - 0.5),
-direction.z * 2 + (Math.random() - 0.5)
);
particle.life = 1.0;
particle.decay = 0.02 + Math.random() * 0.02;
this.scene.add(particle);
this.particles.push(particle);
}
}
createExplosion(position, color = 0xff6600) {
const count = 30;
for (let i = 0; i < count; i++) {
const geometry = new THREE.SphereGeometry(0.15, 6, 6);
const material = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 1
});
const particle = new THREE.Mesh(geometry, material);
particle.position.copy(position);
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
const speed = 2 + Math.random() * 3;
particle.velocity = new THREE.Vector3(
Math.sin(phi) * Math.cos(theta) * speed,
Math.sin(phi) * Math.sin(theta) * speed,
Math.cos(phi) * speed
);
particle.life = 1.0;
particle.decay = 0.015 + Math.random() * 0.01;
this.scene.add(particle);
this.particles.push(particle);
}
}
createLaser(start, end, color = 0x00ffff) {
const geometry = new THREE.BufferGeometry().setFromPoints([start, end]);
const material = new THREE.LineBasicMaterial({
color: color,
transparent: true,
opacity: 0.8
});
const line = new THREE.Line(geometry, material);
line.life = 0.1;
line.decay = 0.1;
this.scene.add(line);
this.particles.push(line);
}
update() {
for (let i = this.particles.length - 1; i >= 0; i--) {
const p = this.particles[i];
p.life -= p.decay;
if (p.life <= 0) {
this.scene.remove(p);
this.particles.splice(i, 1);
} else {
if (p.velocity) {
p.position.add(p.velocity.clone().multiplyScalar(0.1));
}
if (p.material) {
p.material.opacity = p.life * 0.8;
}
}
}
}
}
// ==================== Spaceship ====================
class Spaceship {
constructor(scene) {
this.scene = scene;
this.group = new THREE.Group();
this.velocity = new THREE.Vector3();
this.acceleration = new THREE.Vector3();
this.rotationVelocity = new THREE.Vector2();
this.createModel();
this.group.position.set(0, 0, 0);
scene.add(this.group);
// Stats
this.maxShield = 100;
this.shield = 100;
this.speed = 0.5;
this.turnSpeed = 0.03;
this.weaponLevel = 1;
this.engineLevel = 1;
this.shieldLevel = 1;
this.scannerLevel = 1;
// State
this.shieldActive = false;
this.shieldRegenCooldown = 0;
this.lastShot = 0;
this.fireRate = 150;
}
createModel() {
// Main body
const bodyGeo = new THREE.ConeGeometry(0.5, 2, 8);
const bodyMat = new THREE.MeshPhongMaterial({
color: 0x3366ff,
emissive: 0x112244,
shininess: 100
});
const body = new THREE.Mesh(bodyGeo, bodyMat);
body.rotation.x = Math.PI / 2;
this.group.add(body);
// Wings
const wingGeo = new THREE.BoxGeometry(2.5, 0.1, 0.8);
const wingMat = new THREE.MeshPhongMaterial({
color: 0x2255cc,
emissive: 0x111133
});
const wings = new THREE.Mesh(wingGeo, wingMat);
wings.position.z = 0.3;
this.group.add(wings);
// Cockpit
const cockpitGeo = new THREE.SphereGeometry(0.3, 16, 16);
const cockpitMat = new THREE.MeshPhongMaterial({
color: 0x00ffff,
emissive: 0x00aaaa,
transparent: true,
opacity: 0.8
});
const cockpit = new THREE.Mesh(cockpitGeo, cockpitMat);
cockpit.position.set(0, 0.2, -0.3);
cockpit.scale.set(1, 0.6, 1.5);
this.group.add(cockpit);
// Engine glow
const engineGeo = new THREE.CylinderGeometry(0.2, 0.3, 0.3, 16);
const engineMat = new THREE.MeshBasicMaterial({ color: 0x00aaff });
const engine = new THREE.Mesh(engineGeo, engineMat);
engine.rotation.x = Math.PI / 2;
engine.position.z = 1;
this.group.add(engine);
// Shield sphere
const shieldGeo = new THREE.SphereGeometry(1.5, 32, 32);
const shieldMat = new THREE.MeshBasicMaterial({
color: 0x00aaff,
transparent: true,
opacity: 0,
side: THREE.DoubleSide
});
this.shieldMesh = new THREE.Mesh(shieldGeo, shieldMat);
this.group.add(this.shieldMesh);
}
update(input, deltaTime) {
// Rotation
if (input.mouseX !== 0 || input.mouseY !== 0) {
const targetRotation = Math.atan2(input.mouseX, 1);
this.group.rotation.z += (targetRotation - this.group.rotation.z) * this.turnSpeed;
}
// Movement
this.acceleration.set(0, 0, 0);
if (input.keys['w'] || input.keys['arrowup']) this.acceleration.y = 1;
if (input.keys['s'] || input.keys['arrowdown']) this.acceleration.y = -1;
if (input.keys['a'] || input.keys['arrowleft']) this.acceleration.x = -1;
if (input.keys['d'] || input.keys['arrowright']) this.acceleration.x = 1;
if (this.acceleration.length() > 0) {
this.acceleration.normalize().multiplyScalar(this.speed * this.engineLevel * 0.3);
}
this.velocity.add(this.acceleration);
this.velocity.multiplyScalar(0.95);
this.group.position.add(this.velocity);
// Boundary wrap
if (this.group.position.x > 200) this.group.position.x = -200;
if (this.group.position.x < -200) this.group.position.x = 200;
if (this.group.position.y > 150) this.group.position.y = -150;
if (this.group.position.y < -150) this.group.position.y = 150;
// Shield
if (input.keys[' '] && this.shield > 0) {
this.shieldActive = true;
this.shield -= deltaTime * 10;
this.shieldMesh.material.opacity = 0.3;
} else {
this.shieldActive = false;
this.shieldMesh.material.opacity = 0;
if (this.shield < this.maxShield) {
this.shieldRegenCooldown -= deltaTime;
if (this.shieldRegenCooldown <= 0) {
this.shield += deltaTime * 5 * this.shieldLevel;
}
}
}
this.shield = Math.max(0, Math.min(this.maxShield, this.shield));
}
getMuzzlePosition() {
const pos = new THREE.Vector3(0, 0, -1.2);
pos.applyQuaternion(this.group.quaternion);
pos.add(this.group.position);
return pos;
}
shoot() {
const now = Date.now();
if (now - this.lastShot < this.fireRate / (1 + this.weaponLevel * 0.2)) return null;
this.lastShot = now;
return {
position: this.getMuzzlePosition(),
direction: new THREE.Vector3(0, 0, -1).applyQuaternion(this.group.quaternion),
damage: 10 * this.weaponLevel
};
}
takeDamage(amount) {
if (this.shieldActive) {
amount *= 0.3;
}
this.shield -= amount;
this.shieldRegenCooldown = 3;
return amount;
}
}
// ==================== Planet ====================
class Planet {
constructor(scene, x, y) {
this.scene = scene;
this.group = new THREE.Group();
this.position = new THREE.Vector2(x, y);
this.scanned = false;
this.hasResource = Math.random() < 0.3;
this.resourceType = this.hasResource ?
['mineral', 'crystal', 'artifact'][Math.floor(Math.random() * 3)] : null;
// Generate properties
this.size = 3 + Math.random() * 8;
this.hue = Math.random();
this.hasRing = Math.random() < 0.2;
this.hasAtmosphere = Math.random() < 0.6;
this.createModel();
this.group.position.set(x, y, -20 - Math.random() * 30);
this.scanData = this.generateScanData();
scene.add(this.group);
}
createModel() {
// Planet sphere
const geometry = new THREE.SphereGeometry(this.size, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: new THREE.Color().setHSL(this.hue, 0.6, 0.4),
emissive: new THREE.Color().setHSL(this.hue, 0.4, 0.1)
});
this.planet = new THREE.Mesh(geometry, material);
this.group.add(this.planet);
// Atmosphere
if (this.hasAtmosphere) {
const atmoGeo = new THREE.SphereGeometry(this.size * 1.1, 32, 32);
const atmoMat = new THREE.MeshBasicMaterial({
color: new THREE.Color().setHSL(this.hue, 0.8, 0.6),
transparent: true,
opacity: 0.2,
side: THREE.BackSide
});
const atmosphere = new THREE.Mesh(atmoGeo, atmoMat);
this.group.add(atmosphere);
}
// Ring
if (this.hasRing) {
const ringGeo = new THREE.RingGeometry(this.size * 1.4, this.size * 2, 64);
const ringMat = new THREE.MeshBasicMaterial({
color: new THREE.Color().setHSL(this.hue + 0.1, 0.5, 0.7),
transparent: true,
opacity: 0.6,
side: THREE.DoubleSide
});
const ring = new THREE.Mesh(ringGeo, ringMat);
ring.rotation.x = Math.PI / 2;
ring.rotation.y = Math.random() * Math.PI;
this.group.add(ring);
}
}
generateScanData() {
const types = ['气态巨行星', '岩石行星', '冰巨星', '熔岩世界', '海洋行星'];
const habitability = ['不适宜居住', '可能存在微生物', '可能存在液态水', '可能存在生命', '高度适宜居住'];
return {
name: `PLANET-${Math.random().toString(36).substring(2, 8).toUpperCase()}`,
type: types[Math.floor(Math.random() * types.length)],
diameter: (this.size * 2000).toFixed(0) + ' km',
temperature: Math.floor(-150 + Math.random() * 300) + '°C',
gravity: (0.5 + Math.random() * 1.5).toFixed(2) + ' G',
atmosphere: ['氮气', '氧气', '二氧化碳', '甲烷', '氢气'][Math.floor(Math.random() * 5)],
habitability: habitability[Math.floor(Math.random() * habitability.length)],
resources: this.hasResource ? {
mineral: '稀有金属矿脉',
crystal: '能量晶体矿床',
artifact: '古代文明遗迹'
}[this.resourceType] : '未检测到有价值资源'
};
}
update(time) {
this.planet.rotation.y += 0.001;
}
isNear(shipPosition, distance = 15) {
const dx = this.group.position.x - shipPosition.x;
const dy = this.group.position.y - shipPosition.y;
return Math.sqrt(dx * dx + dy * dy) < distance;
}
}
// ==================== Enemy ====================
class Enemy {
constructor(scene, shipPosition) {
this.scene = scene;
this.group = new THREE.Group();
this.type = ['drone', 'pirate', 'asteroid'][Math.floor(Math.random() * 3)];
this.health = this.type === 'asteroid' ? 30 : 20;
this.maxHealth = this.health;
this.damage = this.type === 'asteroid' ? 5 : 10;
// Spawn position (away from ship)
const angle = Math.random() * Math.PI * 2;
const distance = 30 + Math.random() * 50;
this.group.position.set(
shipPosition.x + Math.cos(angle) * distance,
shipPosition.y + Math.sin(angle) * distance,
-25
);
this.createModel();
scene.add(this.group);
this.lastAttack = 0;
}
createModel() {
if (this.type === 'drone') {
const body = new THREE.Mesh(
new THREE.OctahedronGeometry(0.8),
new THREE.MeshPhongMaterial({ color: 0xff3333, emissive: 0x330000 })
);
this.group.add(body);
this.speed = 0.08;
} else if (this.type === 'pirate') {
const body = new THREE.Mesh(
new THREE.BoxGeometry(1.5, 0.8, 2),
new THREE.MeshPhongMaterial({ color: 0xff6600, emissive: 0x331100 })
);
this.group.add(body);
this.speed = 0.05;
} else {
const asteroid = new THREE.Mesh(
new THREE.DodecahedronGeometry(1.5),
new THREE.MeshPhongMaterial({ color: 0x888888 })
);
this.group.add(asteroid);
this.speed = 0.02;
}
}
update(shipPosition, deltaTime) {
// Move towards ship
const dx = shipPosition.x - this.group.position.x;
const dy = shipPosition.y - this.group.position.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 5) {
this.group.position.x += (dx / dist) * this.speed;
this.group.position.y += (dy / dist) * this.speed;
}
// Rotation
this.group.rotation.x += deltaTime;
this.group.rotation.z += deltaTime * 0.5;
}
takeDamage(amount) {
this.health -= amount;
// Flash effect
this.group.children.forEach(child => {
if (child.material) {
child.material.emissive = new THREE.Color(0xffffff);
setTimeout(() => {
if (child.material) {
child.material.emissive = new THREE.Color(
this.type === 'asteroid' ? 0x000000 :
this.type === 'drone' ? 0x330000 : 0x331100
);
}
}, 50);
}
});
return this.health <= 0;
}
}
// ==================== Starfield ====================
class Starfield {
constructor(scene) {
this.scene = scene;
this.createStars();
this.createNebula();
}
createStars() {
const starGeo = new THREE.BufferGeometry();
const starCount = 3000;
const positions = new Float32Array(starCount * 3);
const colors = new Float32Array(starCount * 3);
const sizes = new Float32Array(starCount);
for (let i = 0; i < starCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * 800;
positions[i * 3 + 1] = (Math.random() - 0.5) * 600;
positions[i * 3 + 2] = -100 - Math.random() * 200;
const color = new THREE.Color();
color.setHSL(Math.random() * 0.2 + 0.5, 0.8, 0.8);
colors[i * 3] = color.r;
colors[i * 3 + 1] = color.g;
colors[i * 3 + 2] = color.b;
sizes[i] = Math.random() * 2 + 0.5;
}
starGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
starGeo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
starGeo.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
const starMat = new THREE.PointsMaterial({
size: 1.5,
vertexColors: true,
transparent: true,
opacity: 0.8,
sizeAttenuation: true
});
this.stars = new THREE.Points(starGeo, starMat);
this.scene.add(this.stars);
}
createNebula() {
const nebulaGeo = new THREE.PlaneGeometry(600, 400);
const nebulaMat = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0.15,
side: THREE.DoubleSide,
blending: THREE.AdditiveBlending
});
this.nebula = new THREE.Mesh(nebulaGeo, nebulaMat);
this.nebula.position.z = -150;
this.scene.add(this.nebula);
// Create gradient texture for nebula
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d');
const gradient = ctx.createRadialGradient(128, 128, 0, 128, 128, 128);
gradient.addColorStop(0, 'rgba(100, 50, 150, 0.8)');
gradient.addColorStop(0.3, 'rgba(50, 100, 200, 0.4)');
gradient.addColorStop(0.6, 'rgba(30, 150, 200, 0.2)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 256, 256);
const texture = new THREE.CanvasTexture(canvas);
nebulaMat.map = texture;
nebulaMat.needsUpdate = true;
}
update(shipPosition) {
// Parallax effect
this.stars.position.x = shipPosition.x * 0.1;
this.stars.position.y = shipPosition.y * 0.1;
}
}
// ==================== Input Handler ====================
class InputHandler {
constructor() {
this.keys = {};
this.mouseX = 0;
this.mouseY = 0;
this.mouseDown = false;
document.addEventListener('keydown', (e) => {
this.keys[e.key.toLowerCase()] = true;
});
document.addEventListener('keyup', (e) => {
this.keys[e.key.toLowerCase()] = false;
});
document.addEventListener('mousemove', (e) => {
this.mouseX = (e.clientX / window.innerWidth - 0.5) * 2;
this.mouseY = (e.clientY / window.innerHeight - 0.5) * 2;
});
document.addEventListener('mousedown', () => {
this.mouseDown = true;
});
document.addEventListener('mouseup', () => {
this.mouseDown = false;
});
}
}
// ==================== Game Class ====================
class Game {
constructor() {
this.canvas = document.getElementById('game-canvas');
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
this.camera.position.z = 50;
this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas, antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// Lighting
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 10);
this.scene.add(directionalLight);
// Systems
this.input = new InputHandler();
this.audio = new AudioSystem();
this.particles = new ParticleSystem(this.scene);
this.starfield = new Starfield(this.scene);
// Game state
this.spaceship = null;
this.planets = [];
this.enemies = [];
this.projectiles = [];
this.resources = { mineral: 0, crystal: 0, artifact: 0 };
this.score = 0;
this.level = 1;
this.xp = 0;
this.xpToLevel = 100;
this.isPaused = true;
this.isPlaying = false;
this.inCombat = false;
// Last times
this.lastTime = 0;
this.planetSpawnTimer = 0;
this.enemySpawnTimer = 0;
// Minimap
this.setupMinimap();
// Window resize
window.addEventListener('resize', () => this.onResize());
console.log('游戏引擎初始化完成');
}
setupMinimap() {
this.minimapCanvas = document.getElementById('minimap-canvas');
this.minimapCtx = this.minimapCanvas.getContext('2d');
this.minimapCanvas.width = 150;
this.minimapCanvas.height = 150;
}
updateMinimap() {
const ctx = this.minimapCtx;
ctx.fillStyle = 'rgba(0, 10, 30, 0.8)';
ctx.fillRect(0, 0, 150, 150);
const scale = 0.3;
const centerX = 75;
const centerY = 75;
// Draw planets
ctx.fillStyle = '#4466aa';
this.planets.forEach(planet => {
const x = centerX + (planet.position.x - this.spaceship.group.position.x) * scale;
const y = centerY + (planet.position.y - this.spaceship.group.position.y) * scale;
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI * 2);
ctx.fill();
});
// Draw enemies
ctx.fillStyle = '#ff4444';
this.enemies.forEach(enemy => {
const x = centerX + (enemy.group.position.x - this.spaceship.group.position.x) * scale;
const y = centerY + (enemy.group.position.y - this.spaceship.group.position.y) * scale;
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI * 2);
ctx.fill();
});
// Draw ship
ctx.fillStyle = '#00ffff';
ctx.beginPath();
ctx.arc(centerX, centerY, 3, 0, Math.PI * 2);
ctx.fill();
}
start() {
document.getElementById('start-screen').classList.add('hidden');
document.getElementById('loading-screen').classList.add('hidden');
// Show HUD
document.getElementById('hud-top').style.display = 'flex';
document.getElementById('shield-bar').style.display = 'block';
document.getElementById('xp-bar').style.display = 'block';
document.getElementById('resource-panel').style.display = 'block';
document.getElementById('minimap').style.display = 'block';
document.getElementById('crosshair').style.display = 'block';
// Initialize systems
this.audio.init();
this.spaceship = new Spaceship(this.scene);
// Spawn initial planets
for (let i = 0; i < 50; i++) {
this.spawnPlanet();
}
this.isPaused = false;
this.isPlaying = true;
this.lastTime = Date.now();
// Start game loop
this.gameLoop();
this.showNotification('探索者,欢迎来到星际文明!按 E 扫描附近星球');
}
spawnPlanet() {
const x = (Math.random() - 0.5) * 400;
const y = (Math.random() - 0.5) * 300;
this.planets.push(new Planet(this.scene, x, y));
}
spawnEnemy() {
this.enemies.push(new Enemy(this.scene, this.spaceship.group.position));
}
gameLoop() {
if (!this.isPaused) {
requestAnimationFrame(() => this.gameLoop());
}
const now = Date.now();
const deltaTime = (now - this.lastTime) / 1000;
this.lastTime = now;
this.update(deltaTime);
this.render();
}
update(deltaTime) {
// Input
this.input.keys;
const now = Date.now();
// Update spaceship
if (this.spaceship) {
this.spaceship.update(this.input, deltaTime);
// Shooting
if (this.input.mouseDown) {
const shot = this.spaceship.shoot();
if (shot) {
this.projectiles.push({
position: shot.position.clone(),
direction: shot.direction,
damage: shot.damage,
life: 2
});
this.audio.playShoot();
}
}
}
// Update starfield
this.starfield.update(this.spaceship.group.position);
// Update planets
this.planets.forEach(planet => planet.update(now * 0.001));
// Update enemies
this.enemies.forEach(enemy => enemy.update(this.spaceship.group.position, deltaTime));
// Spawn new planets
this.planetSpawnTimer += deltaTime;
if (this.planetSpawnTimer > 5 && this.planets.length < 80) {
this.spawnPlanet();
this.planetSpawnTimer = 0;
}
// Spawn enemies
this.enemySpawnTimer += deltaTime;
if (this.enemySpawnTimer > 8 && this.enemies.length < 15) {
this.spawnEnemy();
this.enemySpawnTimer = 0;
}
// Update projectiles
for (let i = this.projectiles.length - 1; i >= 0; i--) {
const p = this.projectiles[i];
p.position.add(p.direction.clone().multiplyScalar(2));
p.life -= deltaTime;
// Draw laser
this.particles.createLaser(
p.position.clone().add(p.direction.clone().multiplyScalar(-0.5)),
p.position,
0x00ffff
);
// Check enemy collisions
for (let j = this.enemies.length - 1; j >= 0; j--) {
const enemy = this.enemies[j];
const dist = p.position.distanceTo(enemy.group.position);
if (dist < 2) {
const killed = enemy.takeDamage(p.damage);
if (killed) {
this.enemies.splice(j, 1);
this.particles.createExplosion(enemy.group.position.clone());
this.audio.playExplosion();
this.score += enemy.type === 'pirate' ? 50 : 30;
this.addXP(enemy.type === 'pirate' ? 25 : 15);
}
this.projectiles.splice(i, 1);
break;
}
}
if (p.life <= 0) {
this.projectiles.splice(i, 1);
}
}
// Check enemy-ship collisions
this.inCombat = false;
for (let i = this.enemies.length - 1; i >= 0; i--) {
const enemy = this.enemies[i];
const dist = enemy.group.position.distanceTo(this.spaceship.group.position);
if (dist < 3) {
this.inCombat = true;
this.spaceship.takeDamage(enemy.damage * deltaTime);
this.particles.createExplosion(
this.spaceship.group.position.clone(),
0x00aaff
);
}
}
// Update particles
this.particles.update();
// Scan planets
if (this.input.keys['e']) {
this.scanNearbyPlanets();
this.input.keys['e'] = false;
}
// Update UI
this.updateUI();
this.updateMinimap();
// Check game over
if (this.spaceship.shield <= 0) {
this.gameOver();
}
}
scanNearbyPlanets() {
let scanned = false;
this.planets.forEach(planet => {
if (planet.isNear(this.spaceship.group.position) && !planet.scanned) {
planet.scanned = true;
scanned = true;
this.showScanPanel(planet);
}
});
if (!scanned) {
this.showNotification('附近未发现可扫描的星球');
}
}
showScanPanel(planet) {
const panel = document.getElementById('scan-panel');
const data = planet.scanData;
document.getElementById('scan-name').textContent = data.name;
document.getElementById('scan-data').innerHTML = `
类型: ${data.type}<br>
直径: ${data.diameter}<br>
温度: ${data.temperature}<br>
重力: ${data.gravity}<br>
大气: ${data.atmosphere}<br>
宜居度: ${data.habitability}<br>
资源: ${data.resources}
`;
panel.classList.add('active');
setTimeout(() => {
panel.classList.remove('active');
}, 5000);
// Collect resource if available
if (planet.hasResource) {
this.resources[planet.resourceType]++;
this.audio.playCollect();
this.showNotification(`发现 ${planet.resourceType === 'mineral' ? '矿物' : planet.resourceType === 'crystal' ? '能量晶体' : '遗迹碎片'}`);
this.score += 100;
}
}
addXP(amount) {
this.xp += amount;
if (this.xp >= this.xpToLevel) {
this.levelUp();
}
this.updateUI();
}
levelUp() {
this.level++;
this.xp -= this.xpToLevel;
this.xpToLevel = Math.floor(this.xpToLevel * 1.5);
this.spaceship.maxShield = 100 + (this.level - 1) * 20;
this.spaceship.shield = this.spaceship.maxShield;
// Show level up effect
const overlay = document.getElementById('levelup-overlay');
overlay.classList.add('active');
this.audio.playLevelUp();
setTimeout(() => {
overlay.classList.remove('active');
}, 1000);
// Show upgrade panel
this.showUpgradePanel();
}
showUpgradePanel() {
document.getElementById('upgrade-panel').classList.add('active');
this.updateUpgradeUI();
this.isPaused = true;
}
updateUpgradeUI() {
document.getElementById('engine-level').textContent = `当前等级: ${this.spaceship.engineLevel}`;
document.getElementById('shield-level').textContent = `当前等级: ${this.spaceship.shieldLevel}`;
document.getElementById('weapon-level').textContent = `当前等级: ${this.spaceship.weaponLevel}`;
document.getElementById('scanner-level').textContent = `当前等级: ${this.spaceship.scannerLevel}`;
}
selectUpgrade(type) {
const cost = (this.resources.mineral + this.resources.crystal + this.resources.artifact) >= 10;
if (type === 'engine') this.spaceship.engineLevel++;
if (type === 'shield') this.spaceship.shieldLevel++;
if (type === 'weapon') this.spaceship.weaponLevel++;
if (type === 'scanner') this.spaceship.scannerLevel++;
this.showNotification(`${type === 'engine' ? '推进系统' : type === 'shield' ? '能量护盾' : type === 'weapon' ? '武器系统' : '扫描仪'}已升级!`);
document.getElementById('upgrade-panel').classList.remove('active');
this.isPaused = false;
this.lastTime = Date.now();
this.gameLoop();
}
updateUI() {
document.getElementById('level-display').textContent = this.level;
document.getElementById('score-display').textContent = this.score;
document.getElementById('enemy-display').textContent = this.enemies.length;
document.getElementById('shield-fill').style.width = `${this.spaceship.shield}%`;
document.getElementById('shield-text').textContent = `护盾 ${Math.floor(this.spaceship.shield)}%`;
document.getElementById('xp-fill').style.width = `${(this.xp / this.xpToLevel) * 100}%`;
document.getElementById('xp-text').textContent = `${this.xp} / ${this.xpToLevel} XP`;
document.getElementById('mineral-count').textContent = this.resources.mineral;
document.getElementById('crystal-count').textContent = this.resources.crystal;
document.getElementById('artifact-count').textContent = this.resources.artifact;
const combatIndicator = document.getElementById('combat-indicator');
if (this.inCombat) {
combatIndicator.classList.add('active');
} else {
combatIndicator.classList.remove('active');
}
}
render() {
this.renderer.render(this.scene, this.camera);
}
showNotification(message) {
const notif = document.getElementById('notification');
notif.textContent = message;
notif.classList.add('show');
setTimeout(() => notif.classList.remove('show'), 3000);
}
pause() {
this.isPaused = true;
document.getElementById('pause-menu').classList.add('active');
}
resume() {
this.isPaused = false;
document.getElementById('pause-menu').classList.remove('active');
document.getElementById('help-panel').classList.remove('active');
this.lastTime = Date.now();
this.gameLoop();
}
showHelp() {
document.getElementById('pause-menu').classList.remove('active');
document.getElementById('help-panel').classList.add('active');
}
hideHelp() {
document.getElementById('help-panel').classList.remove('active');
this.resume();
}
saveGame() {
const saveData = {
level: this.level,
xp: this.xp,
xpToLevel: this.xpToLevel,
score: this.score,
resources: this.resources,
engineLevel: this.spaceship.engineLevel,
shieldLevel: this.spaceship.shieldLevel,
weaponLevel: this.spaceship.weaponLevel,
scannerLevel: this.spaceship.scannerLevel
};
localStorage.setItem('stellarExplorer', JSON.stringify(saveData));
this.showNotification('游戏已保存');
}
loadGame() {
const saved = localStorage.getItem('stellarExplorer');
if (saved) {
const data = JSON.parse(saved);
this.level = data.level || 1;
this.xp = data.xp || 0;
this.xpToLevel = data.xpToLevel || 100;
this.score = data.score || 0;
this.resources = data.resources || { mineral: 0, crystal: 0, artifact: 0 };
if (this.spaceship) {
this.spaceship.engineLevel = data.engineLevel || 1;
this.spaceship.shieldLevel = data.shieldLevel || 1;
this.spaceship.weaponLevel = data.weaponLevel || 1;
this.spaceship.scannerLevel = data.scannerLevel || 1;
this.spaceship.maxShield = 100 + (this.level - 1) * 20;
this.spaceship.shield = this.spaceship.maxShield;
}
this.showNotification('存档已读取');
} else {
this.showNotification('未找到存档');
}
}
returnToMenu() {
location.reload();
}
gameOver() {
this.isPlaying = false;
this.showNotification(`游戏结束!最终得分: ${this.score}`);
setTimeout(() => {
this.returnToMenu();
}, 3000);
}
onResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
}
// Initialize game
const game = new Game();
// Global keyboard handler for pause
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && game.isPlaying) {
if (game.isPaused) {
game.resume();
} else {
game.pause();
}
}
});
// Hide loading screen after initialization
window.addEventListener('load', () => {
setTimeout(() => {
document.getElementById('loading-screen').classList.add('hidden');
}, 1000);
});
</script>
</body>
</html>