1683 lines
59 KiB
HTML
1683 lines
59 KiB
HTML
<!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> |