Remove unused Attack button - attack happens automatically on target click

This commit is contained in:
sokol
2026-02-21 17:45:44 +03:00
parent f6855022dc
commit f19e178217
3 changed files with 272 additions and 10 deletions

View File

@@ -2,7 +2,7 @@
* Hexo Game UI - Canvas Rendering and Interactions * Hexo Game UI - Canvas Rendering and Interactions
*/ */
import { HexMap, CELL_TYPES } from '../src/map.js'; import { HexMap, CELL_TYPES } from './map.js';
// Game constants // Game constants
const HEX_SIZE = 18; const HEX_SIZE = 18;
@@ -110,7 +110,6 @@ class GameUI {
document.getElementById('end-turn-btn').addEventListener('click', () => this.endTurn()); document.getElementById('end-turn-btn').addEventListener('click', () => this.endTurn());
document.getElementById('new-game-btn').addEventListener('click', () => this.newGame()); document.getElementById('new-game-btn').addEventListener('click', () => this.newGame());
document.getElementById('attack-btn').addEventListener('click', () => this.executeAttack());
document.getElementById('cancel-btn').addEventListener('click', () => this.cancelSelection()); document.getElementById('cancel-btn').addEventListener('click', () => this.cancelSelection());
} }
@@ -524,11 +523,9 @@ class GameUI {
} }
// Update buttons // Update buttons
const attackBtn = document.getElementById('attack-btn');
const cancelBtn = document.getElementById('cancel-btn'); const cancelBtn = document.getElementById('cancel-btn');
const endTurnBtn = document.getElementById('end-turn-btn'); const endTurnBtn = document.getElementById('end-turn-btn');
attackBtn.disabled = !this.selectedCell;
cancelBtn.disabled = !this.selectedCell; cancelBtn.disabled = !this.selectedCell;
endTurnBtn.disabled = !this.hasMoved; endTurnBtn.disabled = !this.hasMoved;
} }

View File

@@ -83,10 +83,7 @@
<div class="actions-panel"> <div class="actions-panel">
<h3>Actions</h3> <h3>Actions</h3>
<p class="instruction" id="action-instruction">Select a cell to move</p> <p class="instruction" id="action-instruction">Select a cell to move</p>
<div class="action-buttons"> <button class="btn btn-action" id="cancel-btn" style="width:100%" disabled>Cancel</button>
<button class="btn btn-action" id="attack-btn" disabled>Attack</button>
<button class="btn btn-action" id="cancel-btn" disabled>Cancel</button>
</div>
</div> </div>
<div class="battle-log"> <div class="battle-log">

268
public/map.js Normal file
View File

@@ -0,0 +1,268 @@
/**
* Hexagonal grid map for the DiceWars game.
*
* Map is a 20x20 hexagonal grid where each cell can be:
* - passable (playable)
* - impassable (blocked)
*
* Uses axial coordinates (q, r) for hexagon positioning.
*/
const MAP_SIZE = 20;
const CELL_TYPES = {
EMPTY: 0, // Passable, unowned
BLOCKED: 1, // Impassable terrain
PLAYER1: 2, // Player 1 owned
PLAYER2: 3, // Player 2 owned
};
/**
* Represents a single hex cell on the map
*/
class HexCell {
constructor(q, r, type = CELL_TYPES.EMPTY) {
this.q = q; // Axial coordinate q
this.r = r; // Axial coordinate r
this.type = type; // Cell ownership/type
this.dice = []; // Array of dice values on this cell
}
/**
* Calculate unit strength: F = (cnt-1)*full_dice + current_dice
*/
getStrength() {
if (this.dice.length === 0) return 0;
const cnt = this.dice.length;
const fullDice = 6;
const currentDice = this.dice[this.dice.length - 1]; // Top die
return (cnt - 1) * fullDice + currentDice;
}
/**
* Check if cell has maximum strength (8 dice with 6 on top)
*/
isMaxStrength() {
return this.dice.length >= 8 && this.getStrength() >= 48;
}
/**
* Add a die to this cell
*/
addDie(value) {
if (this.dice.length < 8) {
this.dice.push(value);
return true;
}
return false;
}
/**
* Remove dice from cell, leaving specified strength
*/
setStrength(targetStrength) {
if (targetStrength <= 0) {
this.dice = [];
return;
}
const fullDice = 6;
const cnt = Math.floor((targetStrength - 1) / fullDice) + 1;
const remainder = targetStrength - (cnt - 1) * fullDice;
this.dice = new Array(cnt - 1).fill(6);
if (remainder > 0) {
this.dice.push(remainder);
}
}
isPassable() {
return this.type !== CELL_TYPES.BLOCKED;
}
isOwned() {
return this.type === CELL_TYPES.PLAYER1 || this.type === CELL_TYPES.PLAYER2;
}
getOwner() {
if (this.type === CELL_TYPES.PLAYER1) return 1;
if (this.type === CELL_TYPES.PLAYER2) return 2;
return 0;
}
}
/**
* Hexagonal map generator and manager
*/
class HexMap {
constructor(size = MAP_SIZE) {
this.size = size;
this.cells = new Map(); // Key: "q,r", Value: HexCell
this.generate();
}
/**
* Generate the hexagonal grid
*/
generate() {
this.cells.clear();
for (let q = 0; q < this.size; q++) {
for (let r = 0; r < this.size; r++) {
const key = this.getKey(q, r);
const type = Math.random() < 0.15 ? CELL_TYPES.BLOCKED : CELL_TYPES.EMPTY;
this.cells.set(key, new HexCell(q, r, type));
}
}
}
/**
* Get cell by axial coordinates
*/
getCell(q, r) {
const key = this.getKey(q, r);
return this.cells.get(key);
}
/**
* Get cell by key string
*/
getCellByKey(key) {
return this.cells.get(key);
}
/**
* Generate key from coordinates
*/
getKey(q, r) {
return `${q},${r}`;
}
/**
* Get all passable cells
*/
getPassableCells() {
return Array.from(this.cells.values()).filter(cell => cell.isPassable());
}
/**
* Get all empty (unowned) passable cells
*/
getEmptyCells() {
return Array.from(this.cells.values()).filter(
cell => cell.isPassable() && !cell.isOwned()
);
}
/**
* Get all cells owned by a player
*/
getPlayerCells(playerId) {
const targetType = playerId === 1 ? CELL_TYPES.PLAYER1 : CELL_TYPES.PLAYER2;
return Array.from(this.cells.values()).filter(
cell => cell.type === targetType
);
}
/**
* Get neighboring cells (6 directions in hex grid)
*/
getNeighbors(q, r) {
const directions = [
[1, 0], [1, -1], [0, -1],
[-1, 0], [-1, 1], [0, 1]
];
const neighbors = [];
for (const [dq, dr] of directions) {
const nq = q + dq;
const nr = r + dr;
if (nq >= 0 && nq < this.size && nr >= 0 && nr < this.size) {
const cell = this.getCell(nq, nr);
if (cell && cell.isPassable()) {
neighbors.push(cell);
}
}
}
return neighbors;
}
/**
* Calculate supply for a player: S = sum of all owned cells
*/
calculateSupply(playerId) {
const playerCells = this.getPlayerCells(playerId);
let supply = 0;
for (const cell of playerCells) {
supply += 1; // Each owned cell gives +1 supply
}
return supply;
}
/**
* Render map to console (simplified ASCII representation)
*/
render() {
let output = '';
for (let r = 0; r < this.size; r++) {
// Offset every other row for hex appearance
const offset = r % 2 === 0 ? 0 : 2;
output += ' '.repeat(offset);
for (let q = 0; q < this.size; q++) {
const cell = this.getCell(q, r);
const symbol = this.getCellSymbol(cell);
output += `[${symbol}]`;
}
output += '\n';
}
console.log(output);
}
/**
* Get symbol for cell visualization
*/
getCellSymbol(cell) {
if (cell.type === CELL_TYPES.BLOCKED) return '██';
if (cell.type === CELL_TYPES.PLAYER1) return 'P1';
if (cell.type === CELL_TYPES.PLAYER2) return 'P2';
if (cell.dice.length > 0) return cell.getStrength().toString().padStart(2, ' ');
return ' ';
}
/**
* Set cell ownership
*/
setOwner(q, r, playerId) {
const cell = this.getCell(q, r);
if (cell && cell.isPassable()) {
cell.type = playerId === 1 ? CELL_TYPES.PLAYER1 : CELL_TYPES.PLAYER2;
return true;
}
return false;
}
/**
* Clear cell ownership
*/
clearOwner(q, r) {
const cell = this.getCell(q, r);
if (cell) {
cell.type = CELL_TYPES.EMPTY;
cell.dice = [];
return true;
}
return false;
}
}
// ES Module exports for browser
export { HexMap, HexCell, CELL_TYPES, MAP_SIZE };
// CommonJS exports for Node.js
if (typeof module !== 'undefined' && module.exports) {
module.exports = { HexMap, HexCell, CELL_TYPES, MAP_SIZE };
}