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

1927 lines
70 KiB
HTML
Raw Normal View History

<!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>