798 lines
33 KiB
HTML
798 lines
33 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">
|
|
|
|
<!-- 引入导航功能 -->
|
|
<script src="navigation.js"></script>
|
|
|
|
<style>
|
|
/* 页面特定样式 - 覆盖通用样式中的部分设置 */
|
|
:root {
|
|
--primary-color: #6366f1;
|
|
--primary-light: #8b5cf6;
|
|
--accent-color: #f59e0b;
|
|
--danger-color: #ff4757;
|
|
--success-color: #10b981;
|
|
--bg-primary: #0f1419;
|
|
--bg-secondary: #1a1d29;
|
|
--bg-tertiary: #252837;
|
|
--border-primary: #3d4159;
|
|
--border-secondary: #4a5073;
|
|
--border-highlight: #6366f1;
|
|
--text-primary: #ffffff;
|
|
--text-secondary: #b4b7c9;
|
|
--text-tertiary: #9ca3af;
|
|
--text-disabled: #6b7280;
|
|
--cell-size: min(8.5vw, 38px);
|
|
--safe-area-padding: 16px;
|
|
}
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
html, body { height: 100%; overflow: hidden; -webkit-user-select: none; user-select: none; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Microsoft YaHei UI", sans-serif;
|
|
background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 30%, #16213e 70%, #0f3460 100%);
|
|
color: var(--text-primary);
|
|
-webkit-font-smoothing: antialiased;
|
|
touch-action: none;
|
|
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
|
|
}
|
|
|
|
.app-container { display: flex; flex-direction: column; height: 100%; }
|
|
|
|
.top-nav {
|
|
padding: 12px var(--safe-area-padding);
|
|
border-bottom: 1px solid var(--border-primary);
|
|
text-align: center;
|
|
font-weight: 600;
|
|
background: rgba(26, 29, 41, 0.95);
|
|
backdrop-filter: blur(20px);
|
|
}
|
|
|
|
.main-content {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
padding: var(--safe-area-padding);
|
|
}
|
|
|
|
.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);
|
|
transition: all 0.2s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 10px;
|
|
color: rgba(255, 255, 255, 0.4);
|
|
font-weight: 600;
|
|
position: relative;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
}
|
|
.board-cell:active {
|
|
background: rgba(64, 224, 208, 0.4);
|
|
transform: scale(0.95);
|
|
border-color: var(--secondary-color);
|
|
}
|
|
.board-cell.plane-part { background-color: var(--primary-color); }
|
|
.board-cell.plane-head { background-color: var(--accent-color); }
|
|
.board-cell.plane-body { background-color: var(--primary-light); }
|
|
.board-cell.plane-wing { background-color: var(--primary-light); }
|
|
.board-cell.plane-tail { background-color: var(--primary-color); }
|
|
.board-cell.selected {
|
|
box-shadow: inset 0 0 0 2px var(--success-color);
|
|
z-index: 1;
|
|
border-color: var(--success-color);
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
.controls-area {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
padding: var(--safe-area-padding);
|
|
background: rgba(22, 33, 62, 0.8);
|
|
border-top: 1px solid var(--border-primary);
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.main-controls {
|
|
display: flex;
|
|
gap: 16px;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.direction-control {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.direction-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 44px);
|
|
grid-template-rows: repeat(3, 44px);
|
|
gap: 4px;
|
|
}
|
|
|
|
.direction-grid button {
|
|
width: 44px;
|
|
height: 44px;
|
|
}
|
|
|
|
/* 通用按钮样式 */
|
|
.btn {
|
|
min-height: var(--touch-target-min);
|
|
border: none;
|
|
border-radius: 12px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
padding: 14px 24px;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: transparent;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.btn::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 0;
|
|
height: 0;
|
|
background: rgba(255, 255, 255, 0.3);
|
|
border-radius: 50%;
|
|
transform: translate(-50%, -50%);
|
|
transition: width 0.3s, height 0.3s;
|
|
}
|
|
|
|
.btn:active::before {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* 主要按钮样式 */
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
|
|
color: #ffffff;
|
|
box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3);
|
|
}
|
|
|
|
.btn-primary:hover, .btn-primary:focus {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 25px rgba(99, 102, 241, 0.4);
|
|
}
|
|
|
|
.btn-primary:active {
|
|
transform: translateY(0);
|
|
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.4);
|
|
}
|
|
|
|
/* 成功按钮样式 */
|
|
.btn-success {
|
|
background: linear-gradient(135deg, var(--secondary-color), var(--secondary-light));
|
|
color: #1a1a2e;
|
|
box-shadow: 0 4px 16px rgba(64, 224, 208, 0.3);
|
|
}
|
|
|
|
.btn-success:hover, .btn-success:focus {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 25px rgba(64, 224, 208, 0.4);
|
|
}
|
|
|
|
.btn-success:active {
|
|
transform: translateY(0);
|
|
box-shadow: 0 2px 8px rgba(64, 224, 208, 0.4);
|
|
}
|
|
|
|
/* 次要按钮样式 */
|
|
.btn-secondary {
|
|
background: rgba(99, 102, 241, 0.15);
|
|
color: var(--primary-color);
|
|
border: 1px solid var(--primary-color);
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.btn-secondary:hover, .btn-secondary:focus {
|
|
background: rgba(99, 102, 241, 0.25);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn-secondary:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
/* 危险按钮样式 */
|
|
.btn-danger {
|
|
background: linear-gradient(135deg, var(--danger-color), var(--danger-light));
|
|
color: white;
|
|
box-shadow: 0 4px 16px rgba(255, 71, 87, 0.3);
|
|
}
|
|
|
|
.btn:disabled {
|
|
background: var(--bg-tertiary) !important;
|
|
color: var(--text-disabled) !important;
|
|
border: 1px solid var(--border-primary) !important;
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
.btn:active:not(:disabled) { transform: scale(0.95); }
|
|
|
|
.plane-selection {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
align-items: center;
|
|
margin-right: 20px;
|
|
}
|
|
|
|
.plane-btn {
|
|
min-height: var(--touch-target-min);
|
|
border: none;
|
|
border-radius: 12px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
padding: 14px 24px;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
user-select: none;
|
|
-webkit-tap-highlight-color: transparent;
|
|
position: relative;
|
|
overflow: hidden;
|
|
background: rgba(99, 102, 241, 0.15);
|
|
color: var(--primary-color);
|
|
border: 1px solid var(--primary-color);
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.plane-btn::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 0;
|
|
height: 0;
|
|
background: rgba(99, 102, 241, 0.2);
|
|
border-radius: 50%;
|
|
transform: translate(-50%, -50%);
|
|
transition: width 0.3s, height 0.3s;
|
|
}
|
|
|
|
.plane-btn:active::before {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
.status-display {
|
|
flex-grow: 1;
|
|
text-align: center;
|
|
font-size: 14px;
|
|
color: var(--text-secondary);
|
|
align-self: center;
|
|
padding: 12px;
|
|
background: rgba(15, 52, 96, 0.4);
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.confirmation-group {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
gap: 10px;
|
|
}
|
|
|
|
.confirmation-group .btn {
|
|
flex: 1;
|
|
max-width: 120px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app-container">
|
|
<header class="top-nav">准备页面</header>
|
|
|
|
<main class="main-content">
|
|
<div class="board-wrapper" id="boardWrapper">
|
|
<div class="game-board" id="gameBoard"></div>
|
|
</div>
|
|
</main>
|
|
|
|
<footer class="controls-area">
|
|
<div class="status-display" id="statusDisplay">请点击棋盘放置飞机</div>
|
|
|
|
<div class="main-controls">
|
|
<div class="direction-control">
|
|
<div class="direction-grid">
|
|
<div></div>
|
|
<button class="btn btn-secondary" id="moveUpBtn">↑</button>
|
|
<button class="btn btn-danger" id="deleteBtn">🗑️</button>
|
|
|
|
<button class="btn btn-secondary" id="moveLeftBtn">←</button>
|
|
<button class="btn btn-secondary" id="rotateBtn">↻</button>
|
|
<button class="btn btn-secondary" id="moveRightBtn">→</button>
|
|
|
|
<div></div>
|
|
<button class="btn btn-secondary" id="moveDownBtn">↓</button>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="plane-selection">
|
|
<button class="btn plane-btn" id="planeBtn1" data-plane-id="1">飞机1</button>
|
|
<button class="btn plane-btn" id="planeBtn2" data-plane-id="2">飞机2</button>
|
|
<button class="btn plane-btn" id="planeBtn3" data-plane-id="3">飞机3</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="confirmation-group">
|
|
<button class="btn btn-secondary" id="randomBtn">🎲 随机</button>
|
|
<button class="btn btn-danger" id="resetBtn">🔄 重置</button>
|
|
<button class="btn btn-primary" id="doneBtn">✓ 完成</button>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<script>
|
|
class MobilePlacementGame {
|
|
constructor() {
|
|
this.BOARD_SIZE = 10;
|
|
this.PLANE_COUNT = 3;
|
|
this.boardState = Array(this.BOARD_SIZE).fill(null).map(() => Array(this.BOARD_SIZE).fill(0));
|
|
this.planes = [];
|
|
this.selectedPlaneId = null;
|
|
|
|
this.dom = {
|
|
board: document.getElementById('gameBoard'),
|
|
boardWrapper: document.getElementById('boardWrapper'),
|
|
statusDisplay: document.getElementById('statusDisplay'),
|
|
planeBtns: [
|
|
document.getElementById('planeBtn1'),
|
|
document.getElementById('planeBtn2'),
|
|
document.getElementById('planeBtn3'),
|
|
],
|
|
moveUpBtn: document.getElementById('moveUpBtn'),
|
|
moveDownBtn: document.getElementById('moveDownBtn'),
|
|
moveLeftBtn: document.getElementById('moveLeftBtn'),
|
|
moveRightBtn: document.getElementById('moveRightBtn'),
|
|
rotateBtn: document.getElementById('rotateBtn'),
|
|
deleteBtn: document.getElementById('deleteBtn'),
|
|
randomBtn: document.getElementById('randomBtn'),
|
|
resetBtn: document.getElementById('resetBtn'),
|
|
doneBtn: document.getElementById('doneBtn'),
|
|
};
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.createBoard();
|
|
this.createPlanes();
|
|
this.bindEvents();
|
|
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);
|
|
}
|
|
}
|
|
|
|
const labelsWrapper = document.createElement('div');
|
|
labelsWrapper.className = 'labels';
|
|
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 + 0.5)} * var(--cell-size))`;
|
|
labelsWrapper.appendChild(colLabel);
|
|
|
|
const rowLabel = document.createElement('div');
|
|
rowLabel.className = 'row-label';
|
|
rowLabel.textContent = i + 1;
|
|
rowLabel.style.top = `calc(${(i + 0.5)} * var(--cell-size))`;
|
|
labelsWrapper.appendChild(rowLabel);
|
|
}
|
|
this.dom.boardWrapper.appendChild(labelsWrapper);
|
|
}
|
|
|
|
createPlanes() {
|
|
this.planes = [];
|
|
for(let i = 1; i <= this.PLANE_COUNT; i++) {
|
|
this.planes.push({
|
|
id: i,
|
|
center: null,
|
|
direction: 'up',
|
|
isPlaced: false,
|
|
});
|
|
}
|
|
}
|
|
|
|
bindEvents() {
|
|
this.dom.board.addEventListener('click', this.handleBoardClick.bind(this));
|
|
this.dom.planeBtns.forEach(btn => btn.addEventListener('click', this.handlePlaneBtnClick.bind(this)));
|
|
this.dom.moveUpBtn.addEventListener('click', () => this.moveSelectedPlane(0, -1));
|
|
this.dom.moveDownBtn.addEventListener('click', () => this.moveSelectedPlane(0, 1));
|
|
this.dom.moveLeftBtn.addEventListener('click', () => this.moveSelectedPlane(-1, 0));
|
|
this.dom.moveRightBtn.addEventListener('click', () => this.moveSelectedPlane(1, 0));
|
|
this.dom.rotateBtn.addEventListener('click', this.rotateSelectedPlane.bind(this));
|
|
this.dom.deleteBtn.addEventListener('click', this.deleteSelectedPlane.bind(this));
|
|
this.dom.randomBtn.addEventListener('click', this.randomPlacePlane.bind(this));
|
|
this.dom.resetBtn.addEventListener('click', this.resetGame.bind(this));
|
|
this.dom.doneBtn.addEventListener('click', this.completePlacement.bind(this));
|
|
}
|
|
|
|
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 cellContent = this.boardState[row][col];
|
|
|
|
if (cellContent > 0) { // Clicked on an existing plane
|
|
this.selectedPlaneId = cellContent;
|
|
} else { // Clicked on an empty cell
|
|
const unplacedPlane = this.planes.find(p => !p.isPlaced);
|
|
if (unplacedPlane) {
|
|
this.tryPlacePlane(unplacedPlane.id, {x: col, y: row}, 'up');
|
|
} else {
|
|
this.updateStatus("所有飞机都已放置。");
|
|
}
|
|
}
|
|
this.updateUI();
|
|
}
|
|
|
|
handlePlaneBtnClick(e) {
|
|
const planeId = parseInt(e.target.dataset.planeId);
|
|
const plane = this.planes.find(p => p.id === planeId);
|
|
if (plane && plane.isPlaced) {
|
|
this.selectedPlaneId = (this.selectedPlaneId === planeId) ? null : planeId;
|
|
this.updateUI();
|
|
} else {
|
|
this.updateStatus(`飞机 ${planeId} 尚未放置。`);
|
|
}
|
|
}
|
|
|
|
tryPlacePlane(planeId, center, direction) {
|
|
const plane = this.planes.find(p => p.id === planeId);
|
|
if (!plane) return;
|
|
|
|
const directions = ['up', 'right', 'down', 'left'];
|
|
const startIndex = directions.indexOf(direction);
|
|
let placedSuccessfully = false;
|
|
let usedDirection = direction;
|
|
|
|
// 尝试所有方向,从指定方向开始
|
|
for (let i = 0; i < directions.length; i++) {
|
|
const currentDirection = directions[(startIndex + i) % 4];
|
|
const positions = this.getPlanePositions(center, currentDirection);
|
|
|
|
if (this.isPlacementValid(positions, planeId)) {
|
|
// Clear old position if any
|
|
this.clearPlaneFromBoard(planeId);
|
|
|
|
plane.center = center;
|
|
plane.direction = currentDirection;
|
|
plane.isPlaced = true;
|
|
|
|
positions.forEach(p => { this.boardState[p.y][p.x] = planeId; });
|
|
this.selectedPlaneId = planeId;
|
|
placedSuccessfully = true;
|
|
usedDirection = currentDirection;
|
|
|
|
// 如果使用了不同于初始请求的方向,显示特殊提示
|
|
if (currentDirection !== direction) {
|
|
this.updateStatus(`飞机 ${planeId} 已放置,已自动旋转为 ${this.getDirectionName(currentDirection)} 方向。`);
|
|
} else {
|
|
this.updateStatus(`飞机 ${planeId} 操作成功。`);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!placedSuccessfully) {
|
|
this.updateStatus(`操作无效: 所有方向都存在位置冲突或越界。`);
|
|
}
|
|
|
|
this.updateUI();
|
|
}
|
|
|
|
moveSelectedPlane(dx, dy) {
|
|
if (!this.selectedPlaneId) return;
|
|
const plane = this.planes.find(p => p.id === this.selectedPlaneId);
|
|
const newCenter = { x: plane.center.x + dx, y: plane.center.y + dy };
|
|
this.tryPlacePlane(plane.id, newCenter, plane.direction);
|
|
}
|
|
|
|
rotateSelectedPlane() {
|
|
if (!this.selectedPlaneId) return;
|
|
const plane = this.planes.find(p => p.id === this.selectedPlaneId);
|
|
const directions = ['up', 'right', 'down', 'left'];
|
|
const currentIndex = directions.indexOf(plane.direction);
|
|
const nextDirection = directions[(currentIndex + 1) % 4];
|
|
this.tryPlacePlane(plane.id, plane.center, nextDirection);
|
|
}
|
|
|
|
deleteSelectedPlane() {
|
|
if (!this.selectedPlaneId) return;
|
|
const planeId = this.selectedPlaneId;
|
|
this.clearPlaneFromBoard(planeId);
|
|
const plane = this.planes.find(p => p.id === planeId);
|
|
plane.isPlaced = false;
|
|
plane.center = null;
|
|
this.selectedPlaneId = null;
|
|
this.updateStatus(`飞机 ${planeId} 已移除。`);
|
|
this.updateUI();
|
|
}
|
|
|
|
randomPlacePlane() {
|
|
const unplacedPlane = this.planes.find(p => !p.isPlaced);
|
|
if (!unplacedPlane) {
|
|
this.updateStatus("所有飞机都已放置。");
|
|
return;
|
|
}
|
|
|
|
// 尝试随机位置和方向
|
|
const directions = ['up', 'right', 'down', 'left'];
|
|
const maxAttempts = 100; // 防止无限循环
|
|
let placed = false;
|
|
|
|
for (let attempt = 0; attempt < maxAttempts && !placed; attempt++) {
|
|
const randomX = Math.floor(Math.random() * this.BOARD_SIZE);
|
|
const randomY = Math.floor(Math.random() * this.BOARD_SIZE);
|
|
const randomDirection = directions[Math.floor(Math.random() * directions.length)];
|
|
|
|
const center = { x: randomX, y: randomY };
|
|
const positions = this.getPlanePositions(center, randomDirection);
|
|
|
|
if (this.isPlacementValid(positions, unplacedPlane.id)) {
|
|
this.clearPlaneFromBoard(unplacedPlane.id);
|
|
|
|
unplacedPlane.center = center;
|
|
unplacedPlane.direction = randomDirection;
|
|
unplacedPlane.isPlaced = true;
|
|
|
|
positions.forEach(p => { this.boardState[p.y][p.x] = unplacedPlane.id; });
|
|
this.selectedPlaneId = unplacedPlane.id;
|
|
placed = true;
|
|
|
|
this.updateStatus(`飞机 ${unplacedPlane.id} 已随机放置在 ${String.fromCharCode(65 + randomX)}${randomY + 1},方向为${this.getDirectionName(randomDirection)}。`);
|
|
}
|
|
}
|
|
|
|
if (!placed) {
|
|
this.updateStatus(`无法为飞机 ${unplacedPlane.id} 找到合适的随机位置。`);
|
|
}
|
|
|
|
this.updateUI();
|
|
}
|
|
|
|
resetGame() {
|
|
this.boardState = Array(this.BOARD_SIZE).fill(null).map(() => Array(this.BOARD_SIZE).fill(0));
|
|
this.createPlanes();
|
|
this.selectedPlaneId = null;
|
|
this.updateStatus("已重置棋盘,请重新放置。");
|
|
this.updateUI();
|
|
}
|
|
|
|
completePlacement() {
|
|
if(this.planes.every(p => p.isPlaced)) {
|
|
this.updateStatus("准备就绪,正在进入对战...");
|
|
localStorage.setItem('playerPlanes', JSON.stringify(this.planes));
|
|
|
|
// 延迟1秒后跳转到对战页面
|
|
setTimeout(() => {
|
|
if (typeof window.completePlacementAndNavigate === 'function') {
|
|
window.completePlacementAndNavigate();
|
|
} else if (typeof navigateTo === 'function') {
|
|
navigateTo('battle');
|
|
} else {
|
|
window.location.href = '05_对战页面.html';
|
|
}
|
|
}, 1000);
|
|
} else {
|
|
this.updateStatus("请先放置所有飞机。");
|
|
}
|
|
}
|
|
|
|
clearPlaneFromBoard(planeId) {
|
|
for (let r = 0; r < this.BOARD_SIZE; r++) {
|
|
for (let c = 0; c < this.BOARD_SIZE; c++) {
|
|
if (this.boardState[r][c] === planeId) {
|
|
this.boardState[r][c] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 }));
|
|
}
|
|
|
|
isPlacementValid(positions, planeId) {
|
|
for (const pos of positions) {
|
|
if (pos.x < 0 || pos.x >= this.BOARD_SIZE || pos.y < 0 || pos.y >= this.BOARD_SIZE) return false; // Out of bounds
|
|
const occupyingId = this.boardState[pos.y][pos.x];
|
|
if (occupyingId !== 0 && occupyingId !== planeId) return false; // Collision
|
|
}
|
|
return true;
|
|
}
|
|
|
|
getDirectionName(direction) {
|
|
const directionNames = {
|
|
'up': '向上',
|
|
'right': '向右',
|
|
'down': '向下',
|
|
'left': '向左'
|
|
};
|
|
return directionNames[direction] || direction;
|
|
}
|
|
|
|
updateUI() {
|
|
// Update board
|
|
for (let r = 0; r < this.BOARD_SIZE; r++) {
|
|
for (let c = 0; c < this.BOARD_SIZE; c++) {
|
|
const cell = this.dom.board.children[r * this.BOARD_SIZE + c];
|
|
cell.className = 'board-cell';
|
|
const planeId = this.boardState[r][c];
|
|
if (planeId > 0) {
|
|
cell.classList.add('plane-part');
|
|
const plane = this.planes.find(p => p.id === planeId);
|
|
|
|
if (plane && plane.isPlaced && plane.center) {
|
|
const planePositions = this.getPlanePositions(plane.center, plane.direction);
|
|
const part = planePositions.find(p => p.x === c && p.y === r);
|
|
|
|
if (part && part.type) {
|
|
cell.classList.add(`plane-${part.type}`); // Adds plane-head, plane-body etc.
|
|
}
|
|
if (planeId === this.selectedPlaneId) {
|
|
cell.classList.add('selected');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update plane buttons
|
|
this.dom.planeBtns.forEach(btn => {
|
|
const planeId = parseInt(btn.dataset.planeId);
|
|
const plane = this.planes.find(p => p.id === planeId);
|
|
btn.classList.remove('placed', 'selected');
|
|
if (plane.isPlaced) btn.classList.add('placed');
|
|
if (planeId === this.selectedPlaneId) btn.classList.add('selected');
|
|
});
|
|
|
|
// Update manipulation buttons
|
|
const isPlaneSelected = this.selectedPlaneId !== null;
|
|
const hasUnplacedPlane = this.planes.some(p => !p.isPlaced);
|
|
this.dom.moveUpBtn.disabled = !isPlaneSelected;
|
|
this.dom.moveDownBtn.disabled = !isPlaneSelected;
|
|
this.dom.moveLeftBtn.disabled = !isPlaneSelected;
|
|
this.dom.moveRightBtn.disabled = !isPlaneSelected;
|
|
this.dom.rotateBtn.disabled = !isPlaneSelected;
|
|
this.dom.deleteBtn.disabled = !isPlaneSelected;
|
|
this.dom.randomBtn.disabled = !hasUnplacedPlane;
|
|
|
|
// Update confirmation buttons
|
|
this.dom.doneBtn.disabled = !this.planes.every(p => p.isPlaced);
|
|
}
|
|
|
|
updateStatus(message) {
|
|
this.dom.statusDisplay.textContent = message;
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => new MobilePlacementGame());
|
|
</script>
|
|
</body>
|
|
</html> |