Feature: Map size configuration
This commit is contained in:
@@ -8,7 +8,7 @@ import { AIBot } from './ai-bot.js';
|
||||
|
||||
// Game constants
|
||||
const HEX_SIZE = 14;
|
||||
const MAP_SIZE = 20;
|
||||
const DEFAULT_MAP_SIZE = 20;
|
||||
const ANIMATION_DURATION = 300;
|
||||
|
||||
// Player colors
|
||||
@@ -45,6 +45,7 @@ class GameUI {
|
||||
this.canvas = document.getElementById('game-canvas');
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.map = null;
|
||||
this.mapSize = DEFAULT_MAP_SIZE;
|
||||
this.selectedCell = null;
|
||||
this.currentTarget = null;
|
||||
this.currentPlayer = 1;
|
||||
@@ -95,21 +96,22 @@ class GameUI {
|
||||
startGame() {
|
||||
// Get settings
|
||||
this.playerCount = parseInt(document.getElementById('player-count').value);
|
||||
|
||||
this.mapSize = parseInt(document.getElementById('map-size').value);
|
||||
|
||||
for (let i = 1; i <= this.playerCount; i++) {
|
||||
const typeSelect = document.getElementById(`player${i}-type`);
|
||||
this.playerTypes[i] = typeSelect.value;
|
||||
}
|
||||
|
||||
|
||||
// Show game screen
|
||||
document.getElementById('start-screen').style.display = 'none';
|
||||
document.getElementById('game-screen').style.display = 'flex';
|
||||
|
||||
|
||||
this.newGame();
|
||||
}
|
||||
|
||||
newGame() {
|
||||
this.map = new HexMap(MAP_SIZE);
|
||||
this.map = new HexMap(this.mapSize);
|
||||
this.selectedCell = null;
|
||||
this.currentTarget = null;
|
||||
this.currentPlayer = 1;
|
||||
@@ -174,17 +176,19 @@ class GameUI {
|
||||
initializePlayers() {
|
||||
// Place starting units for each player at fixed positions that are always passable
|
||||
// Use corners of the map to ensure they don't overlap
|
||||
// Positions scale with map size
|
||||
const offset = Math.max(1, Math.floor(this.mapSize / 10));
|
||||
const positions = [
|
||||
{ q: 1, r: 1 }, // Player 1 - top-left
|
||||
{ q: MAP_SIZE - 2, r: MAP_SIZE - 2 }, // Player 2 - bottom-right
|
||||
{ q: 1, r: MAP_SIZE - 2 }, // Player 3 - bottom-left
|
||||
{ q: MAP_SIZE - 2, r: 1 } // Player 4 - top-right
|
||||
{ q: offset, r: offset }, // Player 1 - top-left
|
||||
{ q: this.mapSize - 1 - offset, r: this.mapSize - 1 - offset }, // Player 2 - bottom-right
|
||||
{ q: offset, r: this.mapSize - 1 - offset }, // Player 3 - bottom-left
|
||||
{ q: this.mapSize - 1 - offset, r: offset } // Player 4 - top-right
|
||||
];
|
||||
|
||||
for (let i = 1; i <= this.playerCount; i++) {
|
||||
const pos = positions[i - 1];
|
||||
if (!pos) continue;
|
||||
|
||||
|
||||
// Force the cell to be passable and set ownership
|
||||
const cell = this.map.getCell(pos.q, pos.r);
|
||||
if (cell) {
|
||||
@@ -206,26 +210,26 @@ class GameUI {
|
||||
const canvasHeight = this.canvas.height;
|
||||
|
||||
const sqrt3 = Math.sqrt(3);
|
||||
|
||||
// Calculate actual map bounds
|
||||
|
||||
// Calculate actual map bounds based on dynamic map size
|
||||
// For pointy-top hex grid:
|
||||
// - Width spans from q=0 to q=19, with r offset
|
||||
// - Rightmost point: (q=19, r=19) at x = HEX_SIZE * sqrt3 * 28.5
|
||||
// - Height spans from r=0 to r=19
|
||||
// - Bottommost point: (any q, r=19) at y = HEX_SIZE * 1.5 * 19
|
||||
|
||||
const mapWidth = HEX_SIZE * sqrt3 * ((MAP_SIZE - 1) + (MAP_SIZE - 1) / 2);
|
||||
const mapHeight = HEX_SIZE * 1.5 * (MAP_SIZE - 1);
|
||||
|
||||
// - Width spans from q=0 to q=mapSize-1, with r offset
|
||||
// - Rightmost point: (q=mapSize-1, r=mapSize-1) at x = HEX_SIZE * sqrt3 * (mapSize - 1 + (mapSize - 1) / 2)
|
||||
// - Height spans from r=0 to r=mapSize-1
|
||||
// - Bottommost point: (any q, r=mapSize-1) at y = HEX_SIZE * 1.5 * (mapSize - 1)
|
||||
|
||||
const mapWidth = HEX_SIZE * sqrt3 * ((this.mapSize - 1) + (this.mapSize - 1) / 2);
|
||||
const mapHeight = HEX_SIZE * 1.5 * (this.mapSize - 1);
|
||||
|
||||
// Add padding for hex radius (hex extends beyond center point)
|
||||
const hexRadius = HEX_SIZE;
|
||||
const totalWidth = mapWidth + 2 * hexRadius;
|
||||
const totalHeight = mapHeight + 2 * hexRadius;
|
||||
|
||||
|
||||
// Center the map on canvas
|
||||
this.offsetX = (canvasWidth - totalWidth) / 2 + hexRadius;
|
||||
this.offsetY = (canvasHeight - totalHeight) / 2 + hexRadius;
|
||||
|
||||
|
||||
console.log(`[GAME] Map: canvas=${canvasWidth}x${canvasHeight}, map=${mapWidth.toFixed(0)}x${mapHeight.toFixed(0)}, total=${totalWidth.toFixed(0)}x${totalHeight.toFixed(0)}`);
|
||||
console.log(`[GAME] Offset: x=${this.offsetX.toFixed(1)}, y=${this.offsetY.toFixed(1)}`);
|
||||
}
|
||||
@@ -256,7 +260,7 @@ class GameUI {
|
||||
const qi = Math.round(q);
|
||||
const ri = Math.round(r);
|
||||
|
||||
if (qi >= 0 && qi < MAP_SIZE && ri >= 0 && ri < MAP_SIZE) {
|
||||
if (qi >= 0 && qi < this.mapSize && ri >= 0 && ri < this.mapSize) {
|
||||
return { q: qi, r: ri };
|
||||
}
|
||||
return null;
|
||||
@@ -276,7 +280,7 @@ class GameUI {
|
||||
for (const [dq, dr] of directions) {
|
||||
const nq = q + dq;
|
||||
const nr = r + dr;
|
||||
if (nq >= 0 && nq < MAP_SIZE && nr >= 0 && nr < MAP_SIZE) {
|
||||
if (nq >= 0 && nq < this.mapSize && nr >= 0 && nr < this.mapSize) {
|
||||
const cell = this.map.getCell(nq, nr);
|
||||
if (cell && cell.isPassable()) {
|
||||
targets.push(cell);
|
||||
@@ -367,14 +371,14 @@ class GameUI {
|
||||
render() {
|
||||
this.ctx.fillStyle = COLORS.stroke;
|
||||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
|
||||
if (!this.map) return;
|
||||
|
||||
for (let r = 0; r < MAP_SIZE; r++) {
|
||||
for (let q = 0; q < MAP_SIZE; q++) {
|
||||
|
||||
for (let r = 0; r < this.mapSize; r++) {
|
||||
for (let q = 0; q < this.mapSize; q++) {
|
||||
const cell = this.map.getCell(q, r);
|
||||
let color;
|
||||
|
||||
|
||||
if (cell.type === CELL_TYPES.BLOCKED) {
|
||||
color = COLORS.blocked;
|
||||
} else if (cell.type === CELL_TYPES.PLAYER1) {
|
||||
@@ -388,32 +392,32 @@ class GameUI {
|
||||
} else {
|
||||
color = COLORS.empty;
|
||||
}
|
||||
|
||||
|
||||
if (!cell.isOwned()) {
|
||||
color = this.hexToRgba(color, 0.6);
|
||||
}
|
||||
|
||||
|
||||
this.drawHex(q, r, color);
|
||||
this.drawOwnerIndicator(cell);
|
||||
this.drawDice(cell);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.selectedCell) {
|
||||
this.drawHex(
|
||||
this.selectedCell.q,
|
||||
this.selectedCell.r,
|
||||
this.selectedCell.q,
|
||||
this.selectedCell.r,
|
||||
COLORS.selected,
|
||||
COLORS.text,
|
||||
3
|
||||
);
|
||||
|
||||
|
||||
const targets = this.getValidTargets(this.selectedCell.q, this.selectedCell.r);
|
||||
for (const target of targets) {
|
||||
if (target.getOwner() !== this.currentPlayer) {
|
||||
this.drawHex(
|
||||
target.q,
|
||||
target.r,
|
||||
target.q,
|
||||
target.r,
|
||||
COLORS.target,
|
||||
PLAYER_COLORS[this.currentPlayer] || COLORS.player1,
|
||||
2
|
||||
@@ -642,18 +646,21 @@ class GameUI {
|
||||
const playerCells = this.map.getPlayerCells(i);
|
||||
const playerStrength = playerCells.reduce((sum, c) => sum + c.getStrength(), 0);
|
||||
const playerSupply = this.map.calculateSupply(i);
|
||||
|
||||
|
||||
document.getElementById(`player${i}-cells`).textContent = playerCells.length;
|
||||
document.getElementById(`player${i}-supply`).textContent = playerSupply;
|
||||
document.getElementById(`player${i}-strength`).textContent = playerStrength;
|
||||
|
||||
|
||||
const card = document.getElementById(`player${i}-card`);
|
||||
card.classList.toggle('active', i === this.currentPlayer);
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('current-turn').textContent = this.currentPlayer;
|
||||
document.getElementById('game-phase').textContent = this.gamePhase;
|
||||
|
||||
|
||||
// Update map size display
|
||||
document.getElementById('map-size-display').textContent = `${this.mapSize}x${this.mapSize}`;
|
||||
|
||||
const instruction = document.getElementById('action-instruction');
|
||||
if (this.selectedCell) {
|
||||
instruction.textContent = `Select target (strength: ${this.selectedCell.getStrength()})`;
|
||||
@@ -662,10 +669,10 @@ class GameUI {
|
||||
} else {
|
||||
instruction.textContent = 'Select a cell with dice to move';
|
||||
}
|
||||
|
||||
|
||||
const cellInfo = document.getElementById('selected-cell-info');
|
||||
cellInfo.style.display = 'block';
|
||||
|
||||
|
||||
if (this.selectedCell) {
|
||||
document.getElementById('cell-strength').textContent = this.selectedCell.getStrength();
|
||||
document.getElementById('cell-dice').textContent = this.selectedCell.dice.length;
|
||||
|
||||
Reference in New Issue
Block a user