Files
DFJ/mobile_battle_1.html
2025-09-10 18:13:28 +08:00

1683 lines
59 KiB
HTML
Raw 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, user-scalable=no, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="theme-color" content="#1a1a2e">
<title>深空战机 - 战斗界面</title>
<!-- PWA Manifest -->
<link rel="manifest" href="data:application/json;base64,ewogICJuYW1lIjogIuaViuepuuaImOacuiAtIOaImOaWl+eVjOmdoiIsCiAgInNob3J0X25hbWUiOiAi5rex56m65oiY5py6IiwKICAic3RhcnRfdXJsIjogIi4vIiwKICAiZGlzcGxheSI6ICJzdGFuZGFsb25lIiwKICAiYmFja2dyb3VuZF9jb2xvciI6ICIjMWExYTJlIiwKICAidGhlbWVfY29sb3IiOiAiIzQwZTBkMCIsCiAgImljb25zIjogW10KfQ==">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
}
html, body {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
position: relative;
width: 100%;
}
body {
font-family: 'PingFang SC', 'Helvetica Neue', 'Arial', sans-serif;
background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 30%, #16213e 70%, #0f3460 100%);
color: #ffffff;
position: relative;
touch-action: manipulation;
}
/* 动态星空背景 */
.star-field {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
overflow: hidden;
}
.star {
position: absolute;
background: rgba(64, 224, 208, 0.8);
border-radius: 50%;
animation: twinkle 3s infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
}
/* 安全区域适配 */
.safe-area {
padding-top: env(safe-area-inset-top);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
}
/* 移动端状态栏 */
.mobile-status-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: calc(60px + env(safe-area-inset-top));
background: linear-gradient(180deg, rgba(26, 26, 46, 0.95) 0%, rgba(26, 26, 46, 0.8) 100%);
backdrop-filter: blur(15px);
border-bottom: 1px solid rgba(64, 224, 208, 0.3);
z-index: 100;
display: flex;
align-items: flex-end;
padding: 0 15px 8px;
}
.status-content {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 44px;
}
.player-section {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
}
.player-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: linear-gradient(135deg, #40e0d0 0%, #26d0ce 100%);
display: flex;
align-items: center;
justify-content: center;
color: #1a1a2e;
font-weight: bold;
font-size: 12px;
}
.opponent-avatar {
background: linear-gradient(135deg, #ff4757 0%, #ff3838 100%);
color: white;
}
.player-name {
font-size: 13px;
color: #40e0d0;
font-weight: 600;
}
.opponent-name {
color: #ff4757;
}
.battle-timer {
text-align: center;
flex: 0 0 auto;
}
.timer-label {
font-size: 10px;
color: rgba(255, 255, 255, 0.6);
line-height: 1;
}
.timer-value {
font-size: 20px;
font-weight: bold;
color: #40e0d0;
line-height: 1.2;
text-shadow: 0 0 10px rgba(64, 224, 208, 0.5);
}
.timer-warning {
color: #ff4757;
animation: timerPulse 1s infinite;
}
@keyframes timerPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
/* 回合指示器 */
.turn-indicator {
position: fixed;
top: calc(70px + env(safe-area-inset-top));
left: 50%;
transform: translateX(-50%);
padding: 8px 20px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
z-index: 99;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.turn-indicator.my-turn {
background: linear-gradient(135deg, #40e0d0 0%, #26d0ce 100%);
color: #1a1a2e;
box-shadow: 0 4px 20px rgba(64, 224, 208, 0.4);
}
.turn-indicator.opponent-turn {
background: linear-gradient(135deg, rgba(255, 71, 87, 0.9) 0%, rgba(255, 56, 56, 0.9) 100%);
color: white;
border: 1px solid rgba(255, 71, 87, 0.6);
box-shadow: 0 4px 20px rgba(255, 71, 87, 0.3);
}
/* 主游戏区域 */
.game-container {
position: relative;
top: calc(110px + env(safe-area-inset-top));
left: 0;
right: 0;
min-height: calc(100vh - 110px - env(safe-area-inset-top));
z-index: 10;
padding-bottom: 20px;
}
.game-content {
min-height: 100%;
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
/* 消息面板 */
.message-panel {
width: 100%;
max-width: 400px;
background: rgba(22, 33, 62, 0.8);
border: 1px solid rgba(64, 224, 208, 0.3);
border-radius: 15px;
padding: 12px 16px;
backdrop-filter: blur(10px);
text-align: center;
}
.message-text {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
font-weight: 500;
}
/* 棋盘容器 - 垂直堆叠 */
.boards-container {
width: 100%;
max-width: 400px;
display: flex;
flex-direction: column;
gap: 20px;
margin-bottom: 20px;
}
.board-section {
width: 100%;
}
.board-title {
text-align: center;
font-size: 15px;
margin-bottom: 10px;
padding: 8px 16px;
border-radius: 15px;
font-weight: 600;
}
.my-board-title {
background: rgba(64, 224, 208, 0.15);
color: #40e0d0;
border: 1px solid rgba(64, 224, 208, 0.4);
}
.enemy-board-title {
background: rgba(255, 71, 87, 0.15);
color: #ff4757;
border: 1px solid rgba(255, 71, 87, 0.4);
}
/* 游戏棋盘 */
.game-board {
width: 100%;
aspect-ratio: 1;
background: rgba(15, 52, 96, 0.6);
border: 2px solid rgba(64, 224, 208, 0.5);
border-radius: 12px;
padding: 6px;
backdrop-filter: blur(8px);
position: relative;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.enemy-board {
border-color: rgba(255, 71, 87, 0.5);
}
.grid-container {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: repeat(10, 1fr);
grid-template-rows: repeat(10, 1fr);
gap: 1px;
background: rgba(64, 224, 208, 0.1);
border-radius: 6px;
overflow: hidden;
}
.grid-cell {
background: rgba(15, 52, 96, 0.8);
border: 1px solid rgba(64, 224, 208, 0.15);
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
color: rgba(255, 255, 255, 0.4);
transition: all 0.2s ease;
position: relative;
min-height: 30px;
font-weight: 600;
}
/* 我的棋盘 - 显示飞机部件 */
.my-board .grid-cell.plane-head {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
color: white;
font-weight: bold;
box-shadow: inset 0 0 10px rgba(255, 255, 255, 0.2);
}
.my-board .grid-cell.plane-wing {
background: linear-gradient(135deg, #40e0d0 0%, #26d0ce 100%);
color: #1a1a2e;
font-weight: bold;
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);
}
.my-board .grid-cell.plane-body {
background: linear-gradient(135deg, #6c5ce7 0%, #5f27cd 100%);
color: white;
font-weight: bold;
box-shadow: inset 0 0 10px rgba(255, 255, 255, 0.2);
}
.my-board .grid-cell.plane-tail {
background: linear-gradient(135deg, #00d2d3 0%, #54a0ff 100%);
color: white;
font-weight: bold;
box-shadow: inset 0 0 10px rgba(255, 255, 255, 0.2);
}
/* 被攻击状态 */
.my-board .grid-cell.hit {
background: linear-gradient(135deg, #ff4757 0%, #ff3838 100%);
color: white;
animation: hitFlash 0.6s ease-out;
box-shadow: 0 0 15px #ff4757;
}
.my-board .grid-cell.destroyed {
background: #2c3e50;
color: #7f8c8d;
opacity: 0.6;
}
/* 敌方棋盘交互 */
.enemy-board .grid-cell {
cursor: pointer;
touch-action: manipulation;
min-height: 32px;
}
.enemy-board .grid-cell:active:not(.attacked) {
background: rgba(64, 224, 208, 0.4);
transform: scale(0.95);
border-color: #40e0d0;
}
.enemy-board .grid-cell.hit {
background: linear-gradient(135deg, #ff4757 0%, #ff3838 100%);
color: white;
cursor: not-allowed;
box-shadow: 0 0 10px #ff4757;
}
.enemy-board .grid-cell.destroyed {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
color: white;
font-weight: bold;
cursor: not-allowed;
box-shadow: 0 0 10px #ff6b6b;
}
.enemy-board .grid-cell.miss {
background: rgba(108, 117, 125, 0.6);
color: #6c757d;
cursor: not-allowed;
}
.enemy-board .grid-cell.attacked {
pointer-events: none;
}
/* 显示被摧毁的敌机 */
.enemy-board .grid-cell.revealed-head {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
color: white;
font-weight: bold;
}
.enemy-board .grid-cell.revealed-wing {
background: linear-gradient(135deg, #40e0d0 0%, #26d0ce 100%);
color: #1a1a2e;
font-weight: bold;
}
.enemy-board .grid-cell.revealed-body {
background: linear-gradient(135deg, #6c5ce7 0%, #5f27cd 100%);
color: white;
font-weight: bold;
}
.enemy-board .grid-cell.revealed-tail {
background: linear-gradient(135deg, #00d2d3 0%, #54a0ff 100%);
color: white;
font-weight: bold;
}
/* 攻击动画效果 */
.attack-ripple {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, rgba(64, 224, 208, 0.8) 0%, transparent 70%);
animation: ripple 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
pointer-events: none;
z-index: 10;
}
@keyframes ripple {
0% {
transform: scale(0);
opacity: 1;
}
100% {
transform: scale(4);
opacity: 0;
}
}
@keyframes hitFlash {
0% {
transform: scale(1);
box-shadow: 0 0 10px #ff4757;
}
50% {
transform: scale(1.1);
box-shadow: 0 0 20px #ff4757;
}
100% {
transform: scale(1);
box-shadow: 0 0 5px #ff4757;
}
}
/* 战斗统计面板 */
.battle-stats {
width: 100%;
max-width: 400px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin: 5px 0 20px 0;
}
.stats-card {
background: rgba(22, 33, 62, 0.8);
border: 1px solid rgba(64, 224, 208, 0.3);
border-radius: 12px;
padding: 12px;
text-align: center;
backdrop-filter: blur(8px);
}
.stats-title {
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 4px;
}
.stats-value {
font-size: 16px;
font-weight: bold;
color: #40e0d0;
text-shadow: 0 0 10px rgba(64, 224, 208, 0.3);
}
/* 攻击历史面板 */
.history-panel {
width: 100%;
max-width: 400px;
background: rgba(22, 33, 62, 0.8);
border: 1px solid rgba(64, 224, 208, 0.3);
border-radius: 15px;
padding: 16px;
backdrop-filter: blur(10px);
margin-bottom: 20px;
}
.panel-title {
font-size: 16px;
font-weight: 600;
color: #40e0d0;
margin-bottom: 12px;
text-align: center;
}
.history-list {
max-height: 150px;
overflow-y: auto;
background: rgba(15, 52, 96, 0.4);
border-radius: 10px;
padding: 10px;
}
.history-item {
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 8px;
padding: 6px;
background: rgba(64, 224, 208, 0.1);
border-radius: 6px;
border-left: 3px solid #40e0d0;
}
.history-item:last-child {
margin-bottom: 0;
}
.history-item.hit {
border-left-color: #ff4757;
background: rgba(255, 71, 87, 0.1);
}
.history-item.miss {
border-left-color: #6c757d;
background: rgba(108, 117, 125, 0.1);
}
.history-item.destroy {
border-left-color: #ff6b6b;
background: rgba(255, 107, 107, 0.2);
font-weight: 600;
}
/* 模拟攻击面板 */
.simulation-panel {
width: 100%;
max-width: 400px;
background: rgba(22, 33, 62, 0.8);
border: 1px solid rgba(64, 224, 208, 0.3);
border-radius: 15px;
padding: 16px;
backdrop-filter: blur(10px);
margin-bottom: 20px;
}
.simulation-controls {
display: flex;
gap: 10px;
margin-bottom: 12px;
}
.simulation-controls .btn {
flex: 1;
padding: 10px;
font-size: 12px;
min-height: 40px;
}
.simulation-status {
display: flex;
align-items: center;
gap: 8px;
justify-content: center;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background: #6c757d;
}
.status-indicator.active {
background: #28a745;
animation: statusPulse 2s infinite;
}
.status-indicator.inactive {
background: #6c757d;
}
.status-text {
font-size: 12px;
color: rgba(255, 255, 255, 0.7);
}
@keyframes statusPulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* 游戏结束遮罩 */
.game-end-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(15px);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 20px;
}
.game-end-modal {
background: linear-gradient(135deg, rgba(26, 26, 46, 0.95) 0%, rgba(22, 33, 62, 0.95) 100%);
border: 2px solid #40e0d0;
border-radius: 20px;
padding: 30px 25px;
text-align: center;
max-width: 350px;
width: 100%;
animation: modalAppear 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
backdrop-filter: blur(20px);
}
@keyframes modalAppear {
0% {
opacity: 0;
transform: scale(0.5) translateY(50px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.end-title {
font-size: 22px;
font-weight: bold;
margin-bottom: 20px;
}
.victory-title {
color: #28a745;
text-shadow: 0 0 15px rgba(40, 167, 69, 0.5);
}
.defeat-title {
color: #dc3545;
text-shadow: 0 0 15px rgba(220, 53, 69, 0.5);
}
.end-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin: 20px 0;
}
.end-stat {
background: rgba(64, 224, 208, 0.1);
padding: 12px 8px;
border-radius: 10px;
border: 1px solid rgba(64, 224, 208, 0.2);
}
.end-stat-label {
font-size: 11px;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 4px;
}
.end-stat-value {
font-size: 18px;
font-weight: bold;
color: #40e0d0;
}
.end-actions {
display: flex;
flex-direction: column;
gap: 12px;
margin-top: 25px;
}
.btn {
padding: 14px 20px;
border: none;
border-radius: 25px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
touch-action: manipulation;
min-height: 44px;
}
.btn-primary {
background: linear-gradient(135deg, #40e0d0 0%, #26d0ce 100%);
color: #1a1a2e;
box-shadow: 0 4px 15px rgba(64, 224, 208, 0.3);
}
.btn-primary:active {
transform: translateY(2px);
box-shadow: 0 2px 8px rgba(64, 224, 208, 0.4);
}
.btn-secondary {
background: rgba(108, 117, 125, 0.2);
color: #ffffff;
border: 1px solid rgba(108, 117, 125, 0.5);
}
.btn-secondary:active {
background: rgba(108, 117, 125, 0.3);
}
/* 网络状态指示器 */
.network-status {
position: fixed;
top: calc(15px + env(safe-area-inset-top));
right: 15px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #28a745;
z-index: 101;
opacity: 0.8;
}
.network-status.offline {
background: #dc3545;
animation: networkPulse 2s infinite;
}
@keyframes networkPulse {
0%, 100% { opacity: 0.8; }
50% { opacity: 0.3; }
}
/* 触觉反馈模拟 */
.haptic-feedback {
animation: hapticVibrate 0.1s ease-out;
}
@keyframes hapticVibrate {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-2px); }
75% { transform: translateX(2px); }
}
/* 响应式适配 */
@media (max-height: 667px) {
.game-content {
gap: 10px;
}
.board-title {
font-size: 14px;
padding: 6px 12px;
}
.message-panel {
padding: 10px 14px;
}
}
@media (max-width: 375px) {
.grid-cell {
font-size: 7px;
min-height: 28px;
}
.game-board {
padding: 5px;
}
.stats-value {
font-size: 14px;
}
}
/* 横屏模式适配 */
@media (orientation: landscape) and (max-height: 500px) {
.game-container {
top: calc(70px + env(safe-area-inset-top));
}
.turn-indicator {
top: calc(40px + env(safe-area-inset-top));
}
.boards-container {
flex-direction: row;
max-width: 90%;
gap: 15px;
}
.board-section {
flex: 1;
}
}
</style>
</head>
<body class="safe-area">
<!-- 动态星空背景 -->
<div class="star-field" id="starField"></div>
<!-- 网络状态指示器 -->
<div class="network-status" id="networkStatus"></div>
<!-- 移动端状态栏 -->
<div class="mobile-status-bar">
<div class="status-content">
<div class="player-section">
<div class="player-avatar"></div>
<div class="player-name">深空战机</div>
</div>
<div class="battle-timer">
<div class="timer-label">倒计时</div>
<div class="timer-value" id="battleTimer">30</div>
</div>
<div class="player-section" style="justify-content: flex-end;">
<div class="player-name opponent-name">敌方战机</div>
<div class="player-avatar opponent-avatar"></div>
</div>
</div>
</div>
<!-- 回合指示器 -->
<div class="turn-indicator my-turn" id="turnIndicator">
我的回合
</div>
<!-- 主游戏容器 -->
<div class="game-container">
<div class="game-content">
<!-- 消息面板 -->
<div class="message-panel">
<div class="message-text" id="battleMessage">选择攻击目标,寻找敌方战机</div>
</div>
<!-- 棋盘容器 -->
<div class="boards-container">
<!-- 敌方棋盘(攻击目标) -->
<div class="board-section">
<div class="board-title enemy-board-title">🎯 敌方海域</div>
<div class="game-board enemy-board">
<div class="grid-container" id="enemyGrid">
<!-- 通过JavaScript生成 -->
</div>
</div>
</div>
<!-- 我的棋盘(被攻击) -->
<div class="board-section">
<div class="board-title my-board-title">🛡️ 我方海域</div>
<div class="game-board my-board">
<div class="grid-container" id="myGrid">
<!-- 通过JavaScript生成 -->
</div>
</div>
</div>
</div>
<!-- 战斗统计 -->
<div class="battle-stats">
<div class="stats-card">
<div class="stats-title">我方击毁</div>
<div class="stats-value" id="myKills">0/3</div>
</div>
<div class="stats-card">
<div class="stats-title">敌方击毁</div>
<div class="stats-value" id="enemyKills">0/3</div>
</div>
<div class="stats-card">
<div class="stats-title">回合数</div>
<div class="stats-value" id="roundCount">1</div>
</div>
<div class="stats-card">
<div class="stats-title">命中率</div>
<div class="stats-value" id="hitRate">0%</div>
</div>
</div>
<!-- 攻击历史面板 -->
<div class="history-panel">
<div class="panel-title">📋 攻击历史</div>
<div class="history-list" id="attackHistory">
<div class="history-item">游戏开始,等待攻击...</div>
</div>
</div>
<!-- 模拟攻击面板 -->
<div class="simulation-panel">
<div class="panel-title">🤖 AI模拟攻击</div>
<div class="simulation-controls">
<button class="btn btn-secondary" onclick="simulateAttack()">模拟一次攻击</button>
<button class="btn btn-primary" onclick="toggleAutoSimulate()">开启自动模拟</button>
</div>
<div class="simulation-status" id="simulationStatus">
<span class="status-indicator inactive"></span>
<span class="status-text">模拟已关闭</span>
</div>
</div>
</div>
</div>
<!-- 游戏结束遮罩 -->
<div class="game-end-overlay" id="gameEndOverlay">
<div class="game-end-modal">
<h2 class="end-title" id="endTitle">游戏结束</h2>
<div class="end-stats">
<div class="end-stat">
<div class="end-stat-label">总攻击</div>
<div class="end-stat-value" id="totalAttacks">0</div>
</div>
<div class="end-stat">
<div class="end-stat-label">命中次数</div>
<div class="end-stat-value" id="totalHits">0</div>
</div>
<div class="end-stat">
<div class="end-stat-label">摧毁战机</div>
<div class="end-stat-value" id="totalKills">0</div>
</div>
<div class="end-stat">
<div class="end-stat-label">游戏时长</div>
<div class="end-stat-value" id="gameDuration">0:00</div>
</div>
</div>
<div class="end-actions">
<button class="btn btn-primary" onclick="restartGame()">再玩一局</button>
<button class="btn btn-secondary" onclick="backToMenu()">返回主菜单</button>
</div>
</div>
</div>
<script>
// 十字形飞机形状定义 - 与游戏玩法文档和飞机放置界面保持一致
const PLANE_SHAPES = {
UP: [
[0, -2], // 机头 (相对于中心点)
[-2, -1], [-1, -1], [0, -1], [1, -1], [2, -1], // 机翼 (5格)
[0, 0], [0, 1], // 机身 (2格包括中心点)
[-1, 2], [0, 2], [1, 2] // 机尾 (3格)
],
DOWN: [
[0, 2], // 机头
[-2, 1], [-1, 1], [0, 1], [1, 1], [2, 1], // 机翼 (5格)
[0, 0], [0, -1], // 机身 (2格)
[-1, -2], [0, -2], [1, -2] // 机尾 (3格)
],
LEFT: [
[-2, 0], // 机头
[-1, -2], [-1, -1], [-1, 0], [-1, 1], [-1, 2], // 机翼 (5格)
[0, 0], [1, 0], // 机身 (2格)
[2, -1], [2, 0], [2, 1] // 机尾 (3格)
],
RIGHT: [
[2, 0], // 机头
[1, -2], [1, -1], [1, 0], [1, 1], [1, 2], // 机翼 (5格)
[0, 0], [-1, 0], // 机身 (2格)
[-2, -1], [-2, 0], [-2, 1] // 机尾 (3格)
]
};
// 游戏状态
const gameState = {
myGrid: Array(10).fill().map(() => Array(10).fill(null)),
enemyGrid: Array(10).fill().map(() => Array(10).fill(null)),
myPlanes: [],
enemyPlanes: [],
isMyTurn: true,
myKills: 0,
enemyKills: 0,
totalAttacks: 0,
totalHits: 0,
roundCount: 1,
gameStartTime: Date.now(),
timer: 30,
attackHistory: [],
isAutoSimulate: false,
autoSimulateInterval: null
};
// PWA和离线支持
let isOnline = navigator.onLine;
const networkStatus = document.getElementById('networkStatus');
// 初始化应用
function initApp() {
createStarField();
setupNetworkMonitoring();
initGame();
registerServiceWorker();
}
// 创建动态星空
function createStarField() {
const starField = document.getElementById('starField');
const numStars = 50;
for (let i = 0; i < numStars; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.width = Math.random() * 3 + 'px';
star.style.height = star.style.width;
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.animationDelay = Math.random() * 3 + 's';
starField.appendChild(star);
}
}
// 网络状态监控
function setupNetworkMonitoring() {
function updateNetworkStatus() {
isOnline = navigator.onLine;
networkStatus.className = isOnline ? 'network-status' : 'network-status offline';
}
window.addEventListener('online', updateNetworkStatus);
window.addEventListener('offline', updateNetworkStatus);
updateNetworkStatus();
}
// 注册Service Worker
function registerServiceWorker() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('data:text/javascript;base64,c2VsZi5hZGRFdmVudExpc3RlbmVyKCdpbnN0YWxsJywgZXZlbnQgPT4geyBzZWxmLnNraXBXYWl0aW5nKCk7IH0pOyBzZWxmLmFkZEV2ZW50TGlzdGVuZXIoJ2FjdGl2YXRlJywgZXZlbnQgPT4geyByZXR1cm4gc2VsZi5jbGllbnRzLmNsYWltKCk7IH0pOyBzZWxmLmFkZEV2ZW50TGlzdGVuZXIoJ2ZldGNoJywgZXZlbnQgPT4geyBldmVudC5yZXNwb25kV2l0aChmZXRjaChldmVudC5yZXF1ZXN0KS5jYXRjaCgoKSA9PiBuZXcgUmVzcG9uc2UoJzxodG1sPjxib2R5Puemu+e6v+aLpeS/neS4jeWPr+eUqDwvYm9keT48L2h0bWw+JywgeyBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAndGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04JyB9IH0pKSk7IH0pOw==')
.catch(() => console.log('Service Worker注册失败'));
}
}
// 触觉反馈
function triggerHapticFeedback(type = 'light') {
if ('vibrate' in navigator) {
const patterns = {
light: [10],
medium: [20],
heavy: [30],
success: [10, 50, 10],
error: [50, 100, 50]
};
navigator.vibrate(patterns[type] || patterns.light);
}
// 视觉反馈
document.body.classList.add('haptic-feedback');
setTimeout(() => document.body.classList.remove('haptic-feedback'), 100);
}
// 初始化游戏
function initGame() {
initGrids();
setupMyFleet();
setupEnemyFleet();
startTimer();
updateMessage("选择攻击目标,寻找敌方战机");
}
// 初始化网格
function initGrids() {
const myGridContainer = document.getElementById('myGrid');
const enemyGridContainer = document.getElementById('enemyGrid');
[myGridContainer, enemyGridContainer].forEach((container, isEnemy) => {
container.innerHTML = '';
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const cell = document.createElement('div');
cell.className = 'grid-cell';
cell.dataset.row = row;
cell.dataset.col = col;
cell.textContent = String.fromCharCode(65 + row) + (col + 1);
if (isEnemy) {
cell.addEventListener('click', () => attackEnemyCell(row, col));
cell.addEventListener('touchstart', (e) => {
e.preventDefault();
triggerHapticFeedback('light');
});
}
container.appendChild(cell);
}
}
});
}
// 生成飞机位置 - 修正版本确保11格结构
function generatePlanePositions(centerRow, centerCol, direction) {
const shape = PLANE_SHAPES[direction];
const positions = [];
const parts = [];
shape.forEach((offset, index) => {
const row = centerRow + offset[1];
const col = centerCol + offset[0];
// 确保在棋盘范围内
if (row >= 0 && row < 10 && col >= 0 && col < 10) {
positions.push([row, col]);
// 正确的部件分类1机头+5机翼+2机身+3机尾=11格
if (index === 0) {
parts.push('head'); // 第1格机头
} else if (index >= 1 && index <= 5) {
parts.push('wing'); // 第2-6格机翼
} else if (index >= 6 && index <= 7) {
parts.push('body'); // 第7-8格机身
} else {
parts.push('tail'); // 第9-11格机尾
}
}
});
return { positions, parts };
}
// 设置我的舰队
function setupMyFleet() {
try {
const savedPlanes = localStorage.getItem('playerPlanes');
if (savedPlanes) {
gameState.myPlanes = JSON.parse(savedPlanes);
} else {
throw new Error('No saved planes');
}
} catch {
// 生成模拟飞机数据
gameState.myPlanes = [
{
id: 1,
center: [2, 4],
direction: 'UP',
positions: [[2, 2], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [2, 4], [2, 5], [1, 6], [2, 6], [3, 6]],
headPosition: [2, 2],
isDestroyed: false
},
{
id: 2,
center: [7, 2],
direction: 'RIGHT',
positions: [[7, 4], [5, 1], [6, 1], [7, 1], [8, 1], [9, 1], [7, 2], [6, 2], [5, 1], [5, 2], [5, 3]],
headPosition: [7, 4],
isDestroyed: false
},
{
id: 3,
center: [6, 7],
direction: 'DOWN',
positions: [[6, 9], [4, 8], [5, 8], [6, 8], [7, 8], [8, 8], [6, 7], [6, 6], [5, 5], [6, 5], [7, 5]],
headPosition: [6, 9],
isDestroyed: false
}
];
}
// 在我的棋盘上显示飞机
gameState.myPlanes.forEach(plane => {
const { positions, parts } = generatePlanePositions(plane.center[0], plane.center[1], plane.direction);
positions.forEach(([row, col], index) => {
if (row >= 0 && row < 10 && col >= 0 && col < 10) {
gameState.myGrid[row][col] = {
planeId: plane.id,
partType: parts[index],
isHead: index === 0
};
const cell = document.querySelector(`#myGrid [data-row="${row}"][data-col="${col}"]`);
if (cell) {
cell.classList.add(`plane-${parts[index]}`);
cell.textContent = parts[index] === 'head' ? '头' :
parts[index] === 'wing' ? '翼' :
parts[index] === 'body' ? '身' : '尾';
}
}
});
});
}
// 设置敌方舰队
function setupEnemyFleet() {
const directions = ['UP', 'DOWN', 'LEFT', 'RIGHT'];
for (let planeId = 1; planeId <= 3; planeId++) {
let placed = false;
let attempts = 0;
while (!placed && attempts < 1000) {
const row = Math.floor(Math.random() * 10);
const col = Math.floor(Math.random() * 10);
const direction = directions[Math.floor(Math.random() * 4)];
const { positions } = generatePlanePositions(row, col, direction);
if (positions.length === 11 && isValidEnemyPlacement(positions)) {
const plane = {
id: planeId,
center: [row, col],
direction: direction,
positions: positions,
headPosition: positions[0],
isDestroyed: false
};
gameState.enemyPlanes.push(plane);
// 在敌方网格中标记(但不显示)
const { parts } = generatePlanePositions(row, col, direction);
positions.forEach(([r, c], index) => {
gameState.enemyGrid[r][c] = {
planeId: planeId,
partType: parts[index],
isHead: index === 0
};
});
placed = true;
}
attempts++;
}
}
}
// 验证敌方飞机放置
function isValidEnemyPlacement(positions) {
return positions.every(([row, col]) => {
return row >= 0 && row < 10 && col >= 0 && col < 10 &&
gameState.enemyGrid[row][col] === null;
});
}
// 攻击敌方格子
function attackEnemyCell(row, col) {
if (!gameState.isMyTurn) {
updateMessage("还没轮到你!");
triggerHapticFeedback('error');
return;
}
const targetCell = document.querySelector(`#enemyGrid [data-row="${row}"][data-col="${col}"]`);
if (targetCell.classList.contains('attacked')) {
updateMessage("这个位置已经攻击过了!");
triggerHapticFeedback('error');
return;
}
// 标记已攻击
targetCell.classList.add('attacked');
gameState.totalAttacks++;
triggerHapticFeedback('medium');
// 创建攻击波纹效果
createAttackRipple(targetCell);
// 检查攻击结果 - 实现正确的三种攻击结果
const cellData = gameState.enemyGrid[row][col];
setTimeout(() => {
let attackResult = 0; // 0=未命中, 1=命中, 2=击毁
if (cellData) {
gameState.totalHits++;
if (cellData.isHead) {
// 击中机头 - 结果为击毁(2)
attackResult = 2;
const plane = gameState.enemyPlanes.find(p => p.id === cellData.planeId);
if (plane && !plane.isDestroyed) {
plane.isDestroyed = true;
gameState.myKills++;
// 显示被摧毁的整架飞机
revealDestroyedPlane(plane);
targetCell.classList.add('destroyed');
updateMessage(`🎯 击毁敌机!击中机头摧毁第${plane.id}架敌机!`);
triggerHapticFeedback('success');
if (gameState.myKills >= 3) {
endGame(true);
return;
}
}
} else {
// 击中其他部件 - 结果为命中(1)
attackResult = 1;
targetCell.classList.add('hit');
updateMessage(`💥 命中敌机${cellData.partType === 'wing' ? '机翼' :
cellData.partType === 'body' ? '机身' : '机尾'}`);
triggerHapticFeedback('medium');
}
} else {
// 未命中 - 结果为未命中(0)
attackResult = 0;
targetCell.classList.add('miss');
targetCell.textContent = '○';
updateMessage("💨 攻击未命中");
triggerHapticFeedback('light');
}
console.log(`攻击结果: ${attackResult} (0=未命中, 1=命中, 2=击毁)`);
// 添加攻击历史记录
addAttackHistory('player', row, col, attackResult, cellData);
updateStats();
switchTurn();
}, 800);
}
// 显示被摧毁的敌机
function revealDestroyedPlane(plane) {
const { positions, parts } = generatePlanePositions(plane.center[0], plane.center[1], plane.direction);
positions.forEach(([row, col], index) => {
const cell = document.querySelector(`#enemyGrid [data-row="${row}"][data-col="${col}"]`);
if (cell) {
cell.classList.add(`revealed-${parts[index]}`);
cell.textContent = parts[index] === 'head' ? '头' :
parts[index] === 'wing' ? '翼' :
parts[index] === 'body' ? '身' : '尾';
}
});
}
// 创建攻击波纹效果
function createAttackRipple(cell) {
const ripple = document.createElement('div');
ripple.className = 'attack-ripple';
ripple.style.left = '50%';
ripple.style.top = '50%';
ripple.style.width = '30px';
ripple.style.height = '30px';
ripple.style.transform = 'translate(-50%, -50%)';
cell.appendChild(ripple);
setTimeout(() => ripple.remove(), 800);
}
// 切换回合
function switchTurn() {
gameState.isMyTurn = !gameState.isMyTurn;
gameState.roundCount++;
const turnIndicator = document.getElementById('turnIndicator');
if (gameState.isMyTurn) {
turnIndicator.textContent = '我的回合';
turnIndicator.className = 'turn-indicator my-turn';
resetTimer();
updateMessage("轮到你了!选择攻击目标");
} else {
turnIndicator.textContent = '对手回合';
turnIndicator.className = 'turn-indicator opponent-turn';
updateMessage("等待对手攻击...");
// 模拟对手攻击
setTimeout(() => {
enemyAttack();
}, 2000 + Math.random() * 3000);
}
updateStats();
}
// 敌方攻击
function enemyAttack() {
const availableCells = [];
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const cell = document.querySelector(`#myGrid [data-row="${row}"][data-col="${col}"]`);
if (!cell.classList.contains('hit') && !cell.classList.contains('miss')) {
availableCells.push([row, col, cell]);
}
}
}
if (availableCells.length === 0) return;
const [row, col, cell] = availableCells[Math.floor(Math.random() * availableCells.length)];
const cellData = gameState.myGrid[row][col];
// 攻击动画
cell.style.background = '#ff4757';
triggerHapticFeedback('heavy');
setTimeout(() => {
let attackResult = 0; // 0=未命中, 1=命中, 2=击毁
if (cellData) {
if (cellData.isHead) {
// 击中我的机头 - 结果为击毁(2)
attackResult = 2;
cell.classList.add('destroyed');
const plane = gameState.myPlanes.find(p => p.id === cellData.planeId);
if (plane && !plane.isDestroyed) {
plane.isDestroyed = true;
gameState.enemyKills++;
updateMessage(`💔 敌方击毁了你的第${plane.id}架战机!`);
if (gameState.enemyKills >= 3) {
endGame(false);
return;
}
}
// 敌方击毁后继续攻击
setTimeout(() => enemyAttack(), 1500);
} else {
// 击中其他部件 - 结果为命中(1)
attackResult = 1;
cell.classList.add('hit');
updateMessage(`⚡ 敌方命中你的战机${cellData.partType === 'wing' ? '机翼' :
cellData.partType === 'body' ? '机身' : '机尾'}`);
// 敌方命中后继续攻击
setTimeout(() => enemyAttack(), 1500);
}
} else {
// 未命中 - 结果为未命中(0)
attackResult = 0;
cell.classList.add('miss');
cell.textContent = '○';
cell.style.background = '';
updateMessage("敌方攻击未命中,轮到你了!");
switchTurn();
}
console.log(`敌方攻击结果: ${attackResult} (0=未命中, 1=命中, 2=击毁)`);
// 添加攻击历史记录
addAttackHistory('enemy', row, col, attackResult, cellData);
updateStats();
}, 1000);
}
// 结束游戏
function endGame(isVictory) {
const overlay = document.getElementById('gameEndOverlay');
const title = document.getElementById('endTitle');
if (isVictory) {
title.textContent = '🎉 恭喜获胜!';
title.className = 'end-title victory-title';
triggerHapticFeedback('success');
} else {
title.textContent = '💔 遗憾失败';
title.className = 'end-title defeat-title';
triggerHapticFeedback('error');
}
// 更新结束统计
document.getElementById('totalAttacks').textContent = gameState.totalAttacks;
document.getElementById('totalHits').textContent = gameState.totalHits;
document.getElementById('totalKills').textContent = gameState.myKills;
const gameDuration = Math.floor((Date.now() - gameState.gameStartTime) / 1000);
const minutes = Math.floor(gameDuration / 60);
const seconds = gameDuration % 60;
document.getElementById('gameDuration').textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
overlay.style.display = 'flex';
}
// 更新统计信息
function updateStats() {
document.getElementById('myKills').textContent = `${gameState.myKills}/3`;
document.getElementById('enemyKills').textContent = `${gameState.enemyKills}/3`;
document.getElementById('roundCount').textContent = gameState.roundCount;
const hitRate = gameState.totalAttacks > 0 ?
Math.round(gameState.totalHits / gameState.totalAttacks * 100) : 0;
document.getElementById('hitRate').textContent = hitRate + '%';
}
// 更新消息
function updateMessage(message) {
document.getElementById('battleMessage').textContent = message;
}
// 计时器相关函数
function startTimer() {
setInterval(() => {
if (gameState.isMyTurn && gameState.timer > 0) {
gameState.timer--;
const timerElement = document.getElementById('battleTimer');
timerElement.textContent = gameState.timer;
if (gameState.timer <= 10) {
timerElement.classList.add('timer-warning');
}
if (gameState.timer === 0) {
// 时间到,随机攻击
autoAttack();
}
}
}, 1000);
}
function resetTimer() {
gameState.timer = 30;
const timerElement = document.getElementById('battleTimer');
timerElement.textContent = gameState.timer;
timerElement.classList.remove('timer-warning');
}
function autoAttack() {
const availableCells = [];
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const cell = document.querySelector(`#enemyGrid [data-row="${row}"][data-col="${col}"]`);
if (!cell.classList.contains('attacked')) {
availableCells.push([row, col]);
}
}
}
if (availableCells.length > 0) {
const [row, col] = availableCells[Math.floor(Math.random() * availableCells.length)];
attackEnemyCell(row, col);
}
}
// 游戏控制函数
function restartGame() {
location.reload();
}
function backToMenu() {
localStorage.removeItem('playerPlanes');
window.location.href = 'mobile_main_menu_1.html';
}
// 添加攻击历史记录
function addAttackHistory(attacker, row, col, result, cellData) {
const position = String.fromCharCode(65 + row) + (col + 1);
let message = '';
let className = 'history-item';
if (attacker === 'player') {
message += `玩家攻击 ${position}: `;
} else {
message += `敌方攻击 ${position}: `;
}
if (result === 0) {
message += '未命中';
className += ' miss';
} else if (result === 1) {
message += `命中${cellData.partType === 'wing' ? '机翼' :
cellData.partType === 'body' ? '机身' : '机尾'}`;
className += ' hit';
} else if (result === 2) {
message += '击毁敌机!';
className += ' destroy';
}
gameState.attackHistory.unshift({
attacker,
position,
result,
message,
className,
timestamp: new Date().toLocaleTimeString()
});
// 限制历史记录数量
if (gameState.attackHistory.length > 20) {
gameState.attackHistory.pop();
}
updateAttackHistoryDisplay();
}
// 更新攻击历史显示
function updateAttackHistoryDisplay() {
const historyList = document.getElementById('attackHistory');
historyList.innerHTML = '';
if (gameState.attackHistory.length === 0) {
historyList.innerHTML = '<div class="history-item">暂无攻击记录</div>';
return;
}
gameState.attackHistory.forEach(record => {
const item = document.createElement('div');
item.className = record.className;
item.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>${record.message}</span>
<span style="font-size: 10px; opacity: 0.7;">${record.timestamp}</span>
</div>
`;
historyList.appendChild(item);
});
}
// 模拟攻击功能
function simulateAttack() {
if (!gameState.isMyTurn) {
updateMessage("现在不是你的回合!");
return;
}
// 随机选择一个未攻击的位置
const availableCells = [];
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const cell = document.querySelector(`#enemyGrid [data-row="${row}"][data-col="${col}"]`);
if (!cell.classList.contains('attacked')) {
availableCells.push([row, col]);
}
}
}
if (availableCells.length === 0) {
updateMessage("没有可攻击的目标了!");
return;
}
const [row, col] = availableCells[Math.floor(Math.random() * availableCells.length)];
attackEnemyCell(row, col);
}
// 切换自动模拟
function toggleAutoSimulate() {
gameState.isAutoSimulate = !gameState.isAutoSimulate;
const statusIndicator = document.querySelector('.status-indicator');
const statusText = document.querySelector('.status-text');
const button = event.target;
if (gameState.isAutoSimulate) {
statusIndicator.className = 'status-indicator active';
statusText.textContent = '自动模拟开启';
button.textContent = '关闭自动模拟';
button.className = 'btn btn-warning';
// 开始自动模拟
gameState.autoSimulateInterval = setInterval(() => {
if (gameState.isMyTurn) {
simulateAttack();
}
}, 2000);
updateMessage("AI模拟攻击已开启");
} else {
statusIndicator.className = 'status-indicator inactive';
statusText.textContent = '模拟已关闭';
button.textContent = '开启自动模拟';
button.className = 'btn btn-primary';
// 停止自动模拟
if (gameState.autoSimulateInterval) {
clearInterval(gameState.autoSimulateInterval);
gameState.autoSimulateInterval = null;
}
updateMessage("AI模拟攻击已关闭");
}
}
// 防止双指缩放和滚动
document.addEventListener('touchmove', function(event) {
if (event.scale !== 1) {
event.preventDefault();
}
}, { passive: false });
document.addEventListener('gesturestart', function(event) {
event.preventDefault();
});
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initApp();
});
</script>
</body>
</html>