1182 lines
47 KiB
HTML
1182 lines
47 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">
|
||
<title>对战页面 - 打飞机游戏</title>
|
||
<meta name="theme-color" content="#0f1419">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
|
||
<!-- 引入通用样式 -->
|
||
<link rel="stylesheet" href="common-styles.css">
|
||
|
||
<style>
|
||
/* 页面特定CSS变量 */
|
||
:root {
|
||
--cell-size: min(8.5vw, 36px);
|
||
}
|
||
|
||
body {
|
||
background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 30%, #16213e 70%, #0f3460 100%);
|
||
}
|
||
|
||
/* 顶部状态栏 */
|
||
.status-header {
|
||
padding: 8px var(--safe-area-padding);
|
||
background: rgba(26, 29, 41, 0.95);
|
||
backdrop-filter: blur(20px);
|
||
border-bottom: 1px solid var(--border-primary);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.network-status {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
color: var(--text-tertiary);
|
||
}
|
||
|
||
.connection-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--secondary-color);
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.4; transform: scale(1.3); }
|
||
}
|
||
|
||
.turn-indicator {
|
||
padding: 6px 16px;
|
||
border-radius: 16px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
backdrop-filter: blur(10px);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.turn-indicator.my-turn {
|
||
background: linear-gradient(135deg, var(--secondary-color) 0%, var(--secondary-light) 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);
|
||
}
|
||
|
||
/* 主内容区域 */
|
||
.main-content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: var(--safe-area-padding);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 棋盘区域 */
|
||
.board-container {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.board-wrapper {
|
||
position: relative;
|
||
}
|
||
|
||
.game-board {
|
||
display: grid;
|
||
grid-template-columns: repeat(10, var(--cell-size));
|
||
grid-template-rows: repeat(10, var(--cell-size));
|
||
gap: 1px;
|
||
background: rgba(64, 224, 208, 0.1);
|
||
border: 2px solid rgba(64, 224, 208, 0.5);
|
||
border-radius: 12px;
|
||
padding: 6px;
|
||
backdrop-filter: blur(8px);
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.board-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: 10px;
|
||
color: rgba(255, 255, 255, 0.4);
|
||
transition: all 0.2s ease;
|
||
position: relative;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.board-cell:active {
|
||
background: rgba(64, 224, 208, 0.4);
|
||
transform: scale(0.95);
|
||
border-color: var(--secondary-color);
|
||
}
|
||
|
||
/* 攻击结果状态 */
|
||
.board-cell.hit {
|
||
background: linear-gradient(135deg, var(--danger-color) 0%, var(--danger-light) 100%);
|
||
color: white;
|
||
animation: hitFlash 0.6s ease-out;
|
||
box-shadow: 0 0 15px var(--danger-color);
|
||
}
|
||
|
||
.board-cell.miss {
|
||
background: rgba(108, 117, 125, 0.6);
|
||
color: var(--text-tertiary);
|
||
}
|
||
|
||
.board-cell.destroy {
|
||
background: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-light) 100%);
|
||
color: white;
|
||
animation: destroyFlash 1s ease-out;
|
||
box-shadow: 0 0 20px var(--accent-color);
|
||
}
|
||
|
||
/* 我方飞机状态 */
|
||
.board-cell.my-plane {
|
||
background: var(--primary-color);
|
||
color: white;
|
||
}
|
||
|
||
.board-cell.my-plane-head {
|
||
background: var(--accent-color);
|
||
}
|
||
|
||
.board-cell.my-plane-destroyed {
|
||
background: rgba(108, 117, 125, 0.8);
|
||
color: var(--text-tertiary);
|
||
position: relative;
|
||
}
|
||
|
||
.board-cell.my-plane-destroyed::after {
|
||
content: '💥';
|
||
position: absolute;
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* 模拟飞机样式 */
|
||
.board-cell.simulated-plane {
|
||
background: rgba(99, 102, 241, 0.3);
|
||
border: 2px dashed var(--primary-color);
|
||
color: var(--primary-color);
|
||
}
|
||
|
||
/* 目标选择状态 */
|
||
.board-cell.selected-target {
|
||
background: rgba(245, 158, 11, 0.6);
|
||
border: 2px solid var(--accent-color);
|
||
animation: targetPulse 1s infinite;
|
||
}
|
||
|
||
/* 对手瞄准状态 */
|
||
.board-cell.opponent-targeting {
|
||
background: rgba(255, 71, 87, 0.4);
|
||
border: 2px solid var(--danger-color);
|
||
animation: dangerPulse 0.5s infinite;
|
||
}
|
||
|
||
@keyframes hitFlash {
|
||
0% { transform: scale(1); box-shadow: 0 0 10px var(--danger-color); }
|
||
50% { transform: scale(1.1); box-shadow: 0 0 20px var(--danger-color); }
|
||
100% { transform: scale(1); box-shadow: 0 0 5px var(--danger-color); }
|
||
}
|
||
|
||
@keyframes destroyFlash {
|
||
0%, 100% { transform: scale(1); }
|
||
25% { transform: scale(1.2); }
|
||
50% { transform: scale(0.9); }
|
||
75% { transform: scale(1.1); }
|
||
}
|
||
|
||
@keyframes targetPulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.6; }
|
||
}
|
||
|
||
@keyframes dangerPulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.4; }
|
||
}
|
||
|
||
/* 坐标轴标识 */
|
||
.col-label, .row-label {
|
||
position: absolute;
|
||
font-size: 10px;
|
||
color: var(--text-tertiary);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.col-label {
|
||
top: -20px;
|
||
height: 15px;
|
||
width: var(--cell-size);
|
||
}
|
||
|
||
.row-label {
|
||
left: -20px;
|
||
width: 15px;
|
||
height: var(--cell-size);
|
||
}
|
||
|
||
/* 控制面板 */
|
||
.control-panel {
|
||
background: rgba(22, 33, 62, 0.8);
|
||
border: 1px solid rgba(64, 224, 208, 0.3);
|
||
border-radius: 16px;
|
||
backdrop-filter: blur(10px);
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||
overflow: hidden;
|
||
height: 320px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 页签头部 */
|
||
.tab-header {
|
||
display: flex;
|
||
background: var(--bg-tertiary);
|
||
border-bottom: 1px solid var(--border-primary);
|
||
}
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
padding: 12px;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
color: var(--text-secondary);
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
user-select: none;
|
||
min-height: var(--touch-target-min);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.tab-item.active {
|
||
color: var(--primary-color);
|
||
background: var(--bg-secondary);
|
||
border-bottom: 2px solid var(--primary-color);
|
||
}
|
||
|
||
.tab-item:active {
|
||
background: rgba(99, 102, 241, 0.1);
|
||
}
|
||
|
||
/* 页签内容 */
|
||
.tab-content {
|
||
padding: 16px;
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.tab-pane {
|
||
display: none;
|
||
height: 100%;
|
||
}
|
||
|
||
.tab-pane.active {
|
||
display: block;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(10px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* 飞机模拟控制 */
|
||
.simulate-controls {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
gap: 16px;
|
||
margin-bottom: 16px;
|
||
flex: 1;
|
||
}
|
||
|
||
.plane-selection {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.direction-control {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 44px);
|
||
grid-template-rows: repeat(3, 44px);
|
||
gap: 4px;
|
||
}
|
||
|
||
/* 页面特定按钮样式覆盖 */
|
||
.plane-btn.placed {
|
||
background: linear-gradient(135deg, var(--primary-color), var(--primary-light)) !important;
|
||
color: white !important;
|
||
border-color: var(--primary-light) !important;
|
||
box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3);
|
||
}
|
||
|
||
.plane-btn.selected {
|
||
background: linear-gradient(135deg, var(--secondary-color), var(--secondary-light)) !important;
|
||
color: #1a1a2e !important;
|
||
border-color: var(--secondary-color) !important;
|
||
box-shadow: 0 4px 16px rgba(64, 224, 208, 0.3);
|
||
}
|
||
|
||
/* 攻击控制区域 */
|
||
.attack-controls {
|
||
text-align: center;
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
|
||
.attack-target-info {
|
||
background: rgba(15, 52, 96, 0.4);
|
||
border-radius: 8px;
|
||
padding: 12px;
|
||
margin-bottom: 16px;
|
||
color: var(--text-secondary);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.attack-buttons {
|
||
display: flex;
|
||
gap: 12px;
|
||
justify-content: center;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
/* 历史记录 */
|
||
.history-container {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.history-item {
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
margin-bottom: 8px;
|
||
padding: 8px;
|
||
background: rgba(64, 224, 208, 0.1);
|
||
border-radius: 8px;
|
||
border-left: 3px solid var(--secondary-color);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.history-item.hit {
|
||
border-left-color: var(--danger-color);
|
||
background: rgba(255, 71, 87, 0.1);
|
||
}
|
||
|
||
.history-item.miss {
|
||
border-left-color: var(--text-tertiary);
|
||
background: rgba(108, 117, 125, 0.1);
|
||
}
|
||
|
||
.history-item.destroy {
|
||
border-left-color: var(--accent-color);
|
||
background: rgba(245, 158, 11, 0.2);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 状态信息区域 */
|
||
.status-info {
|
||
text-align: center;
|
||
padding: 12px;
|
||
background: rgba(15, 52, 96, 0.4);
|
||
border-radius: 8px;
|
||
margin-top: auto;
|
||
color: var(--text-secondary);
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 375px) {
|
||
:root {
|
||
--cell-size: min(8vw, 32px);
|
||
}
|
||
|
||
.btn {
|
||
font-size: 13px;
|
||
min-height: 40px;
|
||
padding: 8px 12px;
|
||
}
|
||
|
||
.tab-item {
|
||
font-size: 13px;
|
||
padding: 10px;
|
||
}
|
||
}
|
||
|
||
@media (max-height: 640px) {
|
||
.main-content {
|
||
padding: 12px;
|
||
}
|
||
|
||
.tab-content {
|
||
padding: 12px;
|
||
}
|
||
}
|
||
|
||
@media (orientation: landscape) and (max-height: 500px) {
|
||
.app-container {
|
||
flex-direction: row;
|
||
}
|
||
|
||
.main-content {
|
||
flex-direction: row;
|
||
gap: 16px;
|
||
}
|
||
|
||
.board-container {
|
||
flex: 1;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.control-panel {
|
||
width: 300px;
|
||
max-height: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app-container">
|
||
<!-- 顶部状态栏 -->
|
||
<header class="status-header">
|
||
<div class="network-status">
|
||
<div class="connection-dot"></div>
|
||
<span>在线</span>
|
||
</div>
|
||
<div class="turn-indicator my-turn" id="turnIndicator">我的回合</div>
|
||
<div style="width: 60px;"></div>
|
||
</header>
|
||
|
||
<!-- 主内容区域 -->
|
||
<main class="main-content">
|
||
<!-- 棋盘容器 -->
|
||
<div class="board-container">
|
||
<div class="board-wrapper" id="boardWrapper">
|
||
<div class="game-board" id="gameBoard"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 控制面板 -->
|
||
<div class="control-panel">
|
||
<div class="tab-header">
|
||
<div class="tab-item active" data-tab="simulate">🎯 模拟</div>
|
||
<div class="tab-item" data-tab="attack">⚡ 攻击</div>
|
||
<div class="tab-item" data-tab="history">📋 历史</div>
|
||
</div>
|
||
|
||
<div class="tab-content">
|
||
<!-- 飞机模拟页签 -->
|
||
<div class="tab-pane active" id="simulate">
|
||
<div class="simulate-controls">
|
||
<div class="plane-selection">
|
||
<button class="btn btn-secondary plane-btn" id="simulatePlane1" data-plane-id="1">飞机1</button>
|
||
<button class="btn btn-secondary plane-btn" id="simulatePlane2" data-plane-id="2">飞机2</button>
|
||
<button class="btn btn-secondary plane-btn" id="simulatePlane3" data-plane-id="3">飞机3</button>
|
||
</div>
|
||
|
||
<div class="direction-control">
|
||
<div></div>
|
||
<button class="btn btn-secondary" id="moveUp">↑</button>
|
||
<button class="btn btn-danger" id="deletePlane">🗑️</button>
|
||
|
||
<button class="btn btn-secondary" id="moveLeft">←</button>
|
||
<button class="btn btn-secondary" id="rotatePlane">↻</button>
|
||
<button class="btn btn-secondary" id="moveRight">→</button>
|
||
|
||
<div></div>
|
||
<button class="btn btn-secondary" id="moveDown">↓</button>
|
||
<button class="btn btn-secondary" id="clearSimulation">🔄</button>
|
||
</div>
|
||
</div>
|
||
<div class="status-info" id="simulateStatus">点击棋盘放置模拟飞机</div>
|
||
</div>
|
||
|
||
<!-- 攻击页签 -->
|
||
<div class="tab-pane" id="attack">
|
||
<div class="attack-controls">
|
||
<div class="attack-target-info" id="attackTargetInfo">
|
||
选择攻击目标
|
||
</div>
|
||
<div class="attack-buttons">
|
||
<button class="btn btn-success" id="attackBtn" disabled>🚀 打击</button>
|
||
<button class="btn btn-secondary" id="switchToDefenseBtn">👁️ 观察我方</button>
|
||
</div>
|
||
</div>
|
||
<div class="status-info" id="attackStatus">点击敌方棋盘选择攻击目标</div>
|
||
</div>
|
||
|
||
<!-- 历史记录页签 -->
|
||
<div class="tab-pane" id="history">
|
||
<div class="history-container" id="historyContainer">
|
||
<div class="history-item hit">A5 - 命中敌机!</div>
|
||
<div class="history-item miss">B3 - 攻击落空</div>
|
||
<div class="history-item destroy">C7 - 摧毁敌机头部!</div>
|
||
<div class="history-item miss">D2 - 攻击落空</div>
|
||
</div>
|
||
<div class="status-info">攻击历史记录</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
class BattlePage {
|
||
constructor() {
|
||
this.BOARD_SIZE = 10;
|
||
this.currentGameState = 'my_turn'; // 'my_turn' | 'opponent_turn'
|
||
this.currentMode = 'attack'; // 'attack' | 'simulate' | 'observe'
|
||
this.selectedTarget = null;
|
||
this.simulatedPlanes = [];
|
||
this.selectedSimulatePlane = null;
|
||
this.attackHistory = [];
|
||
|
||
// 棋盘状态
|
||
this.attackBoard = Array(this.BOARD_SIZE).fill(null).map(() => Array(this.BOARD_SIZE).fill(0));
|
||
this.myBoard = Array(this.BOARD_SIZE).fill(null).map(() => Array(this.BOARD_SIZE).fill(0));
|
||
|
||
this.dom = {
|
||
board: document.getElementById('gameBoard'),
|
||
boardWrapper: document.getElementById('boardWrapper'),
|
||
turnIndicator: document.getElementById('turnIndicator'),
|
||
tabItems: document.querySelectorAll('.tab-item'),
|
||
tabPanes: document.querySelectorAll('.tab-pane'),
|
||
// 模拟控件
|
||
simulatePlane1: document.getElementById('simulatePlane1'),
|
||
simulatePlane2: document.getElementById('simulatePlane2'),
|
||
simulatePlane3: document.getElementById('simulatePlane3'),
|
||
moveUp: document.getElementById('moveUp'),
|
||
moveDown: document.getElementById('moveDown'),
|
||
moveLeft: document.getElementById('moveLeft'),
|
||
moveRight: document.getElementById('moveRight'),
|
||
rotatePlane: document.getElementById('rotatePlane'),
|
||
deletePlane: document.getElementById('deletePlane'),
|
||
clearSimulation: document.getElementById('clearSimulation'),
|
||
simulateStatus: document.getElementById('simulateStatus'),
|
||
// 攻击控件
|
||
attackBtn: document.getElementById('attackBtn'),
|
||
switchToDefenseBtn: document.getElementById('switchToDefenseBtn'),
|
||
attackTargetInfo: document.getElementById('attackTargetInfo'),
|
||
attackStatus: document.getElementById('attackStatus'),
|
||
// 历史记录
|
||
historyContainer: document.getElementById('historyContainer')
|
||
};
|
||
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
this.createBoard();
|
||
this.bindEvents();
|
||
this.loadMyPlanes(); // 从localStorage加载我方飞机布局
|
||
this.updateUI();
|
||
console.log("对战页面已初始化");
|
||
}
|
||
|
||
createBoard() {
|
||
this.dom.board.innerHTML = '';
|
||
|
||
// 创建网格单元格
|
||
for (let r = 0; r < this.BOARD_SIZE; r++) {
|
||
for (let c = 0; c < this.BOARD_SIZE; c++) {
|
||
const cell = document.createElement('div');
|
||
cell.className = 'board-cell';
|
||
cell.dataset.row = r;
|
||
cell.dataset.col = c;
|
||
this.dom.board.appendChild(cell);
|
||
}
|
||
}
|
||
|
||
// 添加坐标轴标签
|
||
for (let i = 0; i < this.BOARD_SIZE; i++) {
|
||
const colLabel = document.createElement('div');
|
||
colLabel.className = 'col-label';
|
||
colLabel.textContent = String.fromCharCode(65 + i);
|
||
colLabel.style.left = `calc(${i} * var(--cell-size) + ${i + 1}px + 6px)`;
|
||
this.dom.boardWrapper.appendChild(colLabel);
|
||
|
||
const rowLabel = document.createElement('div');
|
||
rowLabel.className = 'row-label';
|
||
rowLabel.textContent = i + 1;
|
||
rowLabel.style.top = `calc(${i} * var(--cell-size) + ${i + 1}px + 6px)`;
|
||
this.dom.boardWrapper.appendChild(rowLabel);
|
||
}
|
||
}
|
||
|
||
bindEvents() {
|
||
// 棋盘点击事件
|
||
this.dom.board.addEventListener('click', this.handleBoardClick.bind(this));
|
||
|
||
// 页签切换事件
|
||
this.dom.tabItems.forEach(tab => {
|
||
tab.addEventListener('click', this.handleTabClick.bind(this));
|
||
});
|
||
|
||
// 模拟控件事件
|
||
[this.dom.simulatePlane1, this.dom.simulatePlane2, this.dom.simulatePlane3].forEach(btn => {
|
||
btn.addEventListener('click', this.handleSimulatePlaneClick.bind(this));
|
||
});
|
||
|
||
this.dom.moveUp.addEventListener('click', () => this.moveSimulatePlane(0, -1));
|
||
this.dom.moveDown.addEventListener('click', () => this.moveSimulatePlane(0, 1));
|
||
this.dom.moveLeft.addEventListener('click', () => this.moveSimulatePlane(-1, 0));
|
||
this.dom.moveRight.addEventListener('click', () => this.moveSimulatePlane(1, 0));
|
||
this.dom.rotatePlane.addEventListener('click', this.rotateSimulatePlane.bind(this));
|
||
this.dom.deletePlane.addEventListener('click', this.deleteSimulatePlane.bind(this));
|
||
this.dom.clearSimulation.addEventListener('click', this.clearAllSimulation.bind(this));
|
||
|
||
// 攻击控件事件
|
||
this.dom.attackBtn.addEventListener('click', this.executeAttack.bind(this));
|
||
this.dom.switchToDefenseBtn.addEventListener('click', this.switchToDefenseView.bind(this));
|
||
|
||
// 触觉反馈
|
||
document.querySelectorAll('.btn, .board-cell, .tab-item').forEach(element => {
|
||
element.addEventListener('click', () => {
|
||
if ('vibrate' in navigator) {
|
||
navigator.vibrate(10);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
handleBoardClick(e) {
|
||
const cell = e.target.closest('.board-cell');
|
||
if (!cell) return;
|
||
|
||
const row = parseInt(cell.dataset.row);
|
||
const col = parseInt(cell.dataset.col);
|
||
const activeTab = document.querySelector('.tab-item.active').dataset.tab;
|
||
|
||
if (activeTab === 'simulate') {
|
||
this.handleSimulateBoardClick(row, col);
|
||
} else if (activeTab === 'attack') {
|
||
this.handleAttackBoardClick(row, col);
|
||
}
|
||
}
|
||
|
||
handleSimulateBoardClick(row, col) {
|
||
if (this.selectedSimulatePlane) {
|
||
// 放置选中的模拟飞机
|
||
this.tryPlaceSimulatePlane(this.selectedSimulatePlane, {x: col, y: row});
|
||
} else {
|
||
// 点击已有模拟飞机进行选择
|
||
const plane = this.simulatedPlanes.find(p =>
|
||
p.positions.some(pos => pos.x === col && pos.y === row)
|
||
);
|
||
if (plane) {
|
||
this.selectedSimulatePlane = plane.id;
|
||
this.updateSimulateStatus(`已选择模拟飞机 ${plane.id}`);
|
||
} else {
|
||
this.updateSimulateStatus("请先选择要放置的飞机");
|
||
}
|
||
}
|
||
this.updateUI();
|
||
}
|
||
|
||
handleAttackBoardClick(row, col) {
|
||
if (this.currentGameState !== 'my_turn') return;
|
||
|
||
// 检查是否已经攻击过这个位置
|
||
if (this.attackBoard[row][col] !== 0) {
|
||
this.updateAttackStatus("该位置已经攻击过了");
|
||
return;
|
||
}
|
||
|
||
// 设置攻击目标
|
||
this.selectedTarget = {x: col, y: row};
|
||
const coordinate = String.fromCharCode(65 + col) + (row + 1);
|
||
this.updateAttackTargetInfo(`目标: ${coordinate}`);
|
||
this.updateAttackStatus(`已选择目标 ${coordinate},点击打击按钮发起攻击`);
|
||
this.dom.attackBtn.disabled = false;
|
||
this.updateUI();
|
||
}
|
||
|
||
handleTabClick(e) {
|
||
const tabId = e.target.dataset.tab;
|
||
if (!tabId) return;
|
||
|
||
// 更新页签状态
|
||
this.dom.tabItems.forEach(tab => tab.classList.remove('active'));
|
||
e.target.classList.add('active');
|
||
|
||
this.dom.tabPanes.forEach(pane => pane.classList.remove('active'));
|
||
document.getElementById(tabId).classList.add('active');
|
||
|
||
// 根据页签切换模式
|
||
if (tabId === 'simulate') {
|
||
this.currentMode = 'simulate';
|
||
} else if (tabId === 'attack') {
|
||
this.currentMode = 'attack';
|
||
}
|
||
|
||
this.updateUI();
|
||
}
|
||
|
||
handleSimulatePlaneClick(e) {
|
||
const planeId = parseInt(e.target.dataset.planeId);
|
||
this.selectedSimulatePlane = (this.selectedSimulatePlane === planeId) ? null : planeId;
|
||
this.updateSimulateStatus(
|
||
this.selectedSimulatePlane ? `已选择飞机 ${planeId},点击棋盘放置` : "请选择要操作的飞机"
|
||
);
|
||
this.updateUI();
|
||
}
|
||
|
||
tryPlaceSimulatePlane(planeId, center) {
|
||
const directions = ['up', 'right', 'down', 'left'];
|
||
let placed = false;
|
||
|
||
// 移除该飞机的旧位置
|
||
this.simulatedPlanes = this.simulatedPlanes.filter(p => p.id !== planeId);
|
||
|
||
// 尝试各个方向
|
||
for (const direction of directions) {
|
||
const positions = this.getPlanePositions(center, direction);
|
||
if (this.isSimulatePlacementValid(positions)) {
|
||
this.simulatedPlanes.push({
|
||
id: planeId,
|
||
center: center,
|
||
direction: direction,
|
||
positions: positions
|
||
});
|
||
placed = true;
|
||
this.updateSimulateStatus(`飞机 ${planeId} 已放置在 ${String.fromCharCode(65 + center.x)}${center.y + 1}`);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!placed) {
|
||
this.updateSimulateStatus(`无法在该位置放置飞机 ${planeId}`);
|
||
}
|
||
}
|
||
|
||
moveSimulatePlane(dx, dy) {
|
||
if (!this.selectedSimulatePlane) return;
|
||
|
||
const plane = this.simulatedPlanes.find(p => p.id === this.selectedSimulatePlane);
|
||
if (!plane) return;
|
||
|
||
const newCenter = {
|
||
x: plane.center.x + dx,
|
||
y: plane.center.y + dy
|
||
};
|
||
|
||
this.tryPlaceSimulatePlane(this.selectedSimulatePlane, newCenter);
|
||
this.updateUI();
|
||
}
|
||
|
||
rotateSimulatePlane() {
|
||
if (!this.selectedSimulatePlane) return;
|
||
|
||
const plane = this.simulatedPlanes.find(p => p.id === this.selectedSimulatePlane);
|
||
if (!plane) return;
|
||
|
||
const directions = ['up', 'right', 'down', 'left'];
|
||
const currentIndex = directions.indexOf(plane.direction);
|
||
const nextDirection = directions[(currentIndex + 1) % 4];
|
||
|
||
// 移除旧飞机
|
||
this.simulatedPlanes = this.simulatedPlanes.filter(p => p.id !== this.selectedSimulatePlane);
|
||
|
||
// 尝试新方向
|
||
const positions = this.getPlanePositions(plane.center, nextDirection);
|
||
if (this.isSimulatePlacementValid(positions)) {
|
||
this.simulatedPlanes.push({
|
||
id: this.selectedSimulatePlane,
|
||
center: plane.center,
|
||
direction: nextDirection,
|
||
positions: positions
|
||
});
|
||
this.updateSimulateStatus(`飞机 ${this.selectedSimulatePlane} 已旋转`);
|
||
} else {
|
||
// 恢复原飞机
|
||
this.simulatedPlanes.push(plane);
|
||
this.updateSimulateStatus(`无法旋转飞机 ${this.selectedSimulatePlane}`);
|
||
}
|
||
|
||
this.updateUI();
|
||
}
|
||
|
||
deleteSimulatePlane() {
|
||
if (!this.selectedSimulatePlane) return;
|
||
|
||
this.simulatedPlanes = this.simulatedPlanes.filter(p => p.id !== this.selectedSimulatePlane);
|
||
this.updateSimulateStatus(`已删除模拟飞机 ${this.selectedSimulatePlane}`);
|
||
this.selectedSimulatePlane = null;
|
||
this.updateUI();
|
||
}
|
||
|
||
clearAllSimulation() {
|
||
this.simulatedPlanes = [];
|
||
this.selectedSimulatePlane = null;
|
||
this.updateSimulateStatus("已清除所有模拟飞机");
|
||
this.updateUI();
|
||
}
|
||
|
||
executeAttack() {
|
||
if (!this.selectedTarget || this.currentGameState !== 'my_turn') return;
|
||
|
||
const {x, y} = this.selectedTarget;
|
||
const coordinate = String.fromCharCode(65 + x) + (y + 1);
|
||
|
||
// 模拟攻击结果(实际应用中通过WebSocket获取)
|
||
const result = this.simulateAttackResult(x, y);
|
||
|
||
// 更新攻击棋盘
|
||
this.attackBoard[y][x] = result.type;
|
||
|
||
// 添加到历史记录
|
||
this.attackHistory.unshift({
|
||
coordinate: coordinate,
|
||
result: result.type,
|
||
message: result.message,
|
||
timestamp: Date.now()
|
||
});
|
||
|
||
// 清除模拟飞机(攻击后自动清除)
|
||
this.simulatedPlanes = [];
|
||
this.selectedSimulatePlane = null;
|
||
|
||
// 重置攻击目标
|
||
this.selectedTarget = null;
|
||
this.dom.attackBtn.disabled = true;
|
||
|
||
this.updateAttackTargetInfo("选择攻击目标");
|
||
this.updateAttackStatus(`${coordinate} ${result.message}`);
|
||
|
||
// 更新历史记录显示
|
||
this.updateHistoryDisplay();
|
||
|
||
// 模拟切换到对手回合
|
||
setTimeout(() => {
|
||
this.switchToOpponentTurn();
|
||
}, 2000);
|
||
|
||
this.updateUI();
|
||
}
|
||
|
||
simulateAttackResult(x, y) {
|
||
// 简单的随机攻击结果模拟
|
||
const random = Math.random();
|
||
if (random < 0.3) {
|
||
return { type: 'hit', message: '命中敌机!' };
|
||
} else if (random < 0.4) {
|
||
return { type: 'destroy', message: '摧毁敌机头部!' };
|
||
} else {
|
||
return { type: 'miss', message: '攻击落空' };
|
||
}
|
||
}
|
||
|
||
switchToDefenseView() {
|
||
this.switchToOpponentTurn();
|
||
}
|
||
|
||
switchToOpponentTurn() {
|
||
this.currentGameState = 'opponent_turn';
|
||
this.currentMode = 'observe';
|
||
|
||
// 切换页签到攻击页面,但显示为观察模式
|
||
this.dom.tabItems.forEach(tab => tab.classList.remove('active'));
|
||
this.dom.tabItems[1].classList.add('active'); // 攻击页签
|
||
|
||
this.dom.tabPanes.forEach(pane => pane.classList.remove('active'));
|
||
document.getElementById('attack').classList.add('active');
|
||
|
||
this.dom.turnIndicator.textContent = '对手回合';
|
||
this.dom.turnIndicator.className = 'turn-indicator opponent-turn';
|
||
|
||
this.updateAttackTargetInfo("观察对手动向");
|
||
this.updateAttackStatus("对手正在思考...");
|
||
|
||
// 模拟对手瞄准
|
||
this.simulateOpponentTargeting();
|
||
|
||
// 模拟5秒后切换回我的回合
|
||
setTimeout(() => {
|
||
this.switchToMyTurn();
|
||
}, 5000);
|
||
|
||
this.updateUI();
|
||
}
|
||
|
||
switchToMyTurn() {
|
||
this.currentGameState = 'my_turn';
|
||
this.currentMode = 'attack';
|
||
|
||
this.dom.turnIndicator.textContent = '我的回合';
|
||
this.dom.turnIndicator.className = 'turn-indicator my-turn';
|
||
|
||
this.updateAttackTargetInfo("选择攻击目标");
|
||
this.updateAttackStatus("点击敌方棋盘选择攻击目标");
|
||
|
||
this.updateUI();
|
||
}
|
||
|
||
simulateOpponentTargeting() {
|
||
// 模拟对手瞄准过程
|
||
let targetCount = 0;
|
||
const maxTargets = 3;
|
||
|
||
const targetInterval = setInterval(() => {
|
||
if (targetCount >= maxTargets || this.currentGameState === 'my_turn') {
|
||
clearInterval(targetInterval);
|
||
return;
|
||
}
|
||
|
||
// 随机选择一个未被攻击的位置
|
||
const availablePositions = [];
|
||
for (let r = 0; r < this.BOARD_SIZE; r++) {
|
||
for (let c = 0; c < this.BOARD_SIZE; c++) {
|
||
if (this.myBoard[r][c] === 0) {
|
||
availablePositions.push({x: c, y: r});
|
||
}
|
||
}
|
||
}
|
||
|
||
if (availablePositions.length > 0) {
|
||
const randomPos = availablePositions[Math.floor(Math.random() * availablePositions.length)];
|
||
this.simulateOpponentTarget(randomPos);
|
||
}
|
||
|
||
targetCount++;
|
||
}, 1500);
|
||
}
|
||
|
||
simulateOpponentTarget(position) {
|
||
// 清除之前的瞄准状态
|
||
const cells = this.dom.board.querySelectorAll('.board-cell');
|
||
cells.forEach(cell => cell.classList.remove('opponent-targeting'));
|
||
|
||
// 设置新的瞄准状态
|
||
const cell = cells[position.y * this.BOARD_SIZE + position.x];
|
||
cell.classList.add('opponent-targeting');
|
||
|
||
const coordinate = String.fromCharCode(65 + position.x) + (position.y + 1);
|
||
this.updateAttackStatus(`对手正在瞄准 ${coordinate}...`);
|
||
}
|
||
|
||
getPlanePositions(center, direction) {
|
||
const geometry = {
|
||
up: [
|
||
{ x: 0, y: -2, type: 'head' },
|
||
{ x: -2, y: -1, type: 'wing' }, { x: -1, y: -1, type: 'wing' },
|
||
{ x: 0, y: -1, type: 'wing' }, { x: 1, y: -1, type: 'wing' }, { x: 2, y: -1, type: 'wing' },
|
||
{ x: 0, y: 0, type: 'body' }, { x: 0, y: 1, type: 'body' },
|
||
{ x: -1, y: 2, type: 'tail' }, { x: 0, y: 2, type: 'tail' }, { x: 1, y: 2, type: 'tail' }
|
||
],
|
||
down: [
|
||
{ x: 0, y: 2, type: 'head' },
|
||
{ x: -2, y: 1, type: 'wing' }, { x: -1, y: 1, type: 'wing' },
|
||
{ x: 0, y: 1, type: 'wing' }, { x: 1, y: 1, type: 'wing' }, { x: 2, y: 1, type: 'wing' },
|
||
{ x: 0, y: 0, type: 'body' }, { x: 0, y: -1, type: 'body' },
|
||
{ x: -1, y: -2, type: 'tail' }, { x: 0, y: -2, type: 'tail' }, { x: 1, y: -2, type: 'tail' }
|
||
],
|
||
left: [
|
||
{ x: -2, y: 0, type: 'head' },
|
||
{ x: -1, y: -2, type: 'wing' }, { x: -1, y: -1, type: 'wing' },
|
||
{ x: -1, y: 0, type: 'wing' }, { x: -1, y: 1, type: 'wing' }, { x: -1, y: 2, type: 'wing' },
|
||
{ x: 0, y: 0, type: 'body' }, { x: 1, y: 0, type: 'body' },
|
||
{ x: 2, y: -1, type: 'tail' }, { x: 2, y: 0, type: 'tail' }, { x: 2, y: 1, type: 'tail' }
|
||
],
|
||
right: [
|
||
{ x: 2, y: 0, type: 'head' },
|
||
{ x: 1, y: -2, type: 'wing' }, { x: 1, y: -1, type: 'wing' },
|
||
{ x: 1, y: 0, type: 'wing' }, { x: 1, y: 1, type: 'wing' }, { x: 1, y: 2, type: 'wing' },
|
||
{ x: 0, y: 0, type: 'body' }, { x: -1, y: 0, type: 'body' },
|
||
{ x: -2, y: -1, type: 'tail' }, { x: -2, y: 0, type: 'tail' }, { x: -2, y: 1, type: 'tail' }
|
||
]
|
||
};
|
||
|
||
const offsets = geometry[direction];
|
||
if (!offsets) return [];
|
||
|
||
return offsets.map(o => ({
|
||
x: center.x + o.x,
|
||
y: center.y + o.y,
|
||
type: o.type
|
||
}));
|
||
}
|
||
|
||
isSimulatePlacementValid(positions) {
|
||
for (const pos of positions) {
|
||
if (pos.x < 0 || pos.x >= this.BOARD_SIZE || pos.y < 0 || pos.y >= this.BOARD_SIZE) {
|
||
return false; // 越界
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
loadMyPlanes() {
|
||
// 从localStorage加载我方飞机布局(从准备页面)
|
||
try {
|
||
const savedPlanes = localStorage.getItem('playerPlanes');
|
||
if (savedPlanes) {
|
||
const planes = JSON.parse(savedPlanes);
|
||
planes.forEach(plane => {
|
||
if (plane.isPlaced && plane.center) {
|
||
const positions = this.getPlanePositions(plane.center, plane.direction);
|
||
positions.forEach(pos => {
|
||
if (pos.x >= 0 && pos.x < this.BOARD_SIZE && pos.y >= 0 && pos.y < this.BOARD_SIZE) {
|
||
this.myBoard[pos.y][pos.x] = plane.id;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.warn('无法加载飞机布局:', e);
|
||
}
|
||
}
|
||
|
||
updateHistoryDisplay() {
|
||
this.dom.historyContainer.innerHTML = '';
|
||
this.attackHistory.forEach(item => {
|
||
const div = document.createElement('div');
|
||
div.className = `history-item ${item.result}`;
|
||
div.textContent = `${item.coordinate} - ${item.message}`;
|
||
this.dom.historyContainer.appendChild(div);
|
||
});
|
||
}
|
||
|
||
updateUI() {
|
||
this.updateBoardDisplay();
|
||
this.updateControlsState();
|
||
}
|
||
|
||
updateBoardDisplay() {
|
||
const cells = this.dom.board.querySelectorAll('.board-cell');
|
||
|
||
cells.forEach((cell, index) => {
|
||
const row = Math.floor(index / this.BOARD_SIZE);
|
||
const col = index % this.BOARD_SIZE;
|
||
|
||
// 重置单元格类名
|
||
cell.className = 'board-cell';
|
||
cell.textContent = '';
|
||
|
||
if (this.currentGameState === 'my_turn' && this.currentMode !== 'observe') {
|
||
// 我的回合 - 显示攻击棋盘
|
||
const attackState = this.attackBoard[row][col];
|
||
if (attackState === 'hit') {
|
||
cell.classList.add('hit');
|
||
cell.textContent = '🔥';
|
||
} else if (attackState === 'miss') {
|
||
cell.classList.add('miss');
|
||
cell.textContent = '×';
|
||
} else if (attackState === 'destroy') {
|
||
cell.classList.add('destroy');
|
||
cell.textContent = '💥';
|
||
}
|
||
|
||
// 显示模拟飞机
|
||
const simulatedPlane = this.simulatedPlanes.find(plane =>
|
||
plane.positions.some(pos => pos.x === col && pos.y === row)
|
||
);
|
||
if (simulatedPlane) {
|
||
cell.classList.add('simulated-plane');
|
||
if (simulatedPlane.id === this.selectedSimulatePlane) {
|
||
//cell.style.borderColor = 'var(--success-color)';
|
||
}
|
||
}
|
||
|
||
// 显示选中的攻击目标
|
||
if (this.selectedTarget && this.selectedTarget.x === col && this.selectedTarget.y === row) {
|
||
cell.classList.add('selected-target');
|
||
}
|
||
} else {
|
||
// 对手回合 - 显示我方棋盘
|
||
const myPlaneId = this.myBoard[row][col];
|
||
if (myPlaneId > 0) {
|
||
cell.classList.add('my-plane');
|
||
// 可以根据部位类型添加不同样式
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
updateControlsState() {
|
||
// 更新模拟飞机按钮状态
|
||
[this.dom.simulatePlane1, this.dom.simulatePlane2, this.dom.simulatePlane3].forEach(btn => {
|
||
const planeId = parseInt(btn.dataset.planeId);
|
||
btn.classList.remove('placed', 'selected');
|
||
|
||
if (this.simulatedPlanes.find(p => p.id === planeId)) {
|
||
btn.classList.add('placed');
|
||
}
|
||
if (planeId === this.selectedSimulatePlane) {
|
||
btn.classList.add('selected');
|
||
}
|
||
});
|
||
|
||
// 更新方向控制按钮状态
|
||
const hasSelectedPlane = this.selectedSimulatePlane !== null;
|
||
this.dom.moveUp.disabled = !hasSelectedPlane;
|
||
this.dom.moveDown.disabled = !hasSelectedPlane;
|
||
this.dom.moveLeft.disabled = !hasSelectedPlane;
|
||
this.dom.moveRight.disabled = !hasSelectedPlane;
|
||
this.dom.rotatePlane.disabled = !hasSelectedPlane;
|
||
this.dom.deletePlane.disabled = !hasSelectedPlane;
|
||
|
||
// 更新攻击按钮状态
|
||
const canAttack = this.selectedTarget && this.currentGameState === 'my_turn';
|
||
this.dom.attackBtn.disabled = !canAttack;
|
||
|
||
// 更新切换按钮文本
|
||
if (this.currentGameState === 'my_turn') {
|
||
this.dom.switchToDefenseBtn.textContent = '👁️ 观察我方';
|
||
} else {
|
||
this.dom.switchToDefenseBtn.textContent = '⚡ 返回攻击';
|
||
this.dom.switchToDefenseBtn.onclick = () => this.switchToMyTurn();
|
||
}
|
||
}
|
||
|
||
updateSimulateStatus(message) {
|
||
this.dom.simulateStatus.textContent = message;
|
||
}
|
||
|
||
updateAttackStatus(message) {
|
||
this.dom.attackStatus.textContent = message;
|
||
}
|
||
|
||
updateAttackTargetInfo(message) {
|
||
this.dom.attackTargetInfo.textContent = message;
|
||
}
|
||
}
|
||
|
||
// 初始化对战页面
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
new BattlePage();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |