Spaces:
Running
Running
Create a player turn based strategy game in the style of "Warlords" on the map of a fantasy kingdom for 2 players - Follow Up Deployment
487426f
verified
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Warlords of the Fantasy Kingdom</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script> | |
<style> | |
.hexagon { | |
position: relative; | |
width: 100px; | |
height: 57.74px; | |
background-color: #64C7CC; | |
margin: 28.87px 0; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
} | |
.hexagon:before, | |
.hexagon:after { | |
content: ""; | |
position: absolute; | |
width: 0; | |
border-left: 50px solid transparent; | |
border-right: 50px solid transparent; | |
} | |
.hexagon:before { | |
bottom: 100%; | |
border-bottom: 28.87px solid #64C7CC; | |
} | |
.hexagon:after { | |
top: 100%; | |
width: 0; | |
border-top: 28.87px solid #64C7CC; | |
} | |
.hexagon:hover { | |
background-color: #4CAF50; | |
} | |
.hexagon:hover:before { | |
border-bottom-color: #4CAF50; | |
} | |
.hexagon:hover:after { | |
border-top-color: #4CAF50; | |
} | |
.hex-row { | |
display: flex; | |
margin-left: -50px; | |
} | |
.hex-row:nth-child(even) { | |
margin-left: 0; | |
} | |
.hex-grid { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
.player1 { | |
background-color: #FF5252; | |
} | |
.player1:before { | |
border-bottom-color: #FF5252; | |
} | |
.player1:after { | |
border-top-color: #FF5252; | |
} | |
.player2 { | |
background-color: #536DFE; | |
} | |
.player2:before { | |
border-bottom-color: #536DFE; | |
} | |
.player2:after { | |
border-top-color: #536DFE; | |
} | |
.neutral { | |
background-color: #64C7CC; | |
} | |
.neutral:before { | |
border-bottom-color: #64C7CC; | |
} | |
.neutral:after { | |
border-top-color: #64C7CC; | |
} | |
.mountain { | |
background-color: #795548; | |
} | |
.mountain:before { | |
border-bottom-color: #795548; | |
} | |
.mountain:after { | |
border-top-color: #795548; | |
} | |
.water { | |
background-color: #2196F3; | |
} | |
.water:before { | |
border-bottom-color: #2196F3; | |
} | |
.water:after { | |
border-top-color: #2196F3; | |
} | |
.forest { | |
background-color: #4CAF50; | |
} | |
.forest:before { | |
border-bottom-color: #4CAF50; | |
} | |
.forest:after { | |
border-top-color: #4CAF50; | |
} | |
.castle { | |
position: relative; | |
z-index: 2; | |
} | |
.unit { | |
position: absolute; | |
z-index: 3; | |
font-size: 24px; | |
pointer-events: none; | |
} | |
.selected { | |
box-shadow: 0 0 15px gold; | |
} | |
.movable { | |
box-shadow: 0 0 15px #4CAF50; | |
} | |
.attackable { | |
box-shadow: 0 0 15px #FF5252; | |
} | |
@media (max-width: 768px) { | |
.hexagon { | |
width: 60px; | |
height: 34.64px; | |
} | |
.hexagon:before, | |
.hexagon:after { | |
border-left: 30px solid transparent; | |
border-right: 30px solid transparent; | |
} | |
.hexagon:before { | |
border-bottom: 17.32px solid #64C7CC; | |
} | |
.hexagon:after { | |
border-top: 17.32px solid #64C7CC; | |
} | |
.unit { | |
font-size: 16px; | |
} | |
} | |
</style> | |
</head> | |
<body class="bg-gray-900 text-white"> | |
<div class="container mx-auto px-4 py-8"> | |
<header class="text-center mb-8"> | |
<h1 class="text-4xl font-bold mb-2 text-yellow-400">Warlords of the Fantasy Kingdom</h1> | |
<p class="text-xl text-gray-300">A turn-based strategy game for 2 players</p> | |
</header> | |
<div class="flex flex-col lg:flex-row gap-8"> | |
<!-- Game Board --> | |
<div class="flex-1"> | |
<div class="hex-grid" id="game-board"></div> | |
</div> | |
<!-- Game Info Panel --> | |
<div class="w-full lg:w-1/4 bg-gray-800 rounded-lg p-6"> | |
<div class="mb-6"> | |
<h2 class="text-2xl font-bold mb-4 text-center">Game Status</h2> | |
<div id="current-player" class="text-xl font-bold mb-2 text-center py-2 rounded"></div> | |
<div class="grid grid-cols-2 gap-4 mb-4"> | |
<div class="bg-red-600 p-3 rounded-lg"> | |
<h3 class="font-bold">Player 1</h3> | |
<p>Gold: <span id="p1-gold">100</span></p> | |
<p>Territories: <span id="p1-territories">1</span></p> | |
</div> | |
<div class="bg-blue-600 p-3 rounded-lg"> | |
<h3 class="font-bold">Player 2</h3> | |
<p>Gold: <span id="p2-gold">100</span></p> | |
<p>Territories: <span id="p2-territories">1</span></p> | |
</div> | |
</div> | |
</div> | |
<div class="mb-6"> | |
<h2 class="text-2xl font-bold mb-4 text-center">Selected Tile</h2> | |
<div id="tile-info" class="bg-gray-700 p-4 rounded-lg"> | |
<p>Type: <span id="tile-type">None</span></p> | |
<p>Owner: <span id="tile-owner">None</span></p> | |
<p>Units: <span id="tile-units">0</span></p> | |
</div> | |
</div> | |
<div class="mb-6"> | |
<h2 class="text-2xl font-bold mb-4 text-center">Actions</h2> | |
<div class="grid grid-cols-2 gap-2"> | |
<button id="end-turn" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded"> | |
End Turn | |
</button> | |
<button id="recruit" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> | |
Recruit (20g) | |
</button> | |
<button id="build-castle" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-4 rounded"> | |
Build Castle (50g) | |
</button> | |
<button id="move-units" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> | |
Move Units | |
</button> | |
</div> | |
</div> | |
<div> | |
<h2 class="text-2xl font-bold mb-4 text-center">Game Log</h2> | |
<div id="game-log" class="bg-gray-700 p-4 rounded-lg h-40 overflow-y-auto"> | |
<p>Game started! Player 1's turn.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Help Modal --> | |
<div id="help-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden"> | |
<div class="bg-gray-800 rounded-lg p-6 max-w-2xl w-full max-h-screen overflow-y-auto"> | |
<div class="flex justify-between items-center mb-4"> | |
<h2 class="text-2xl font-bold">Game Rules</h2> | |
<button id="close-help" class="text-gray-400 hover:text-white"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="space-y-4"> | |
<div> | |
<h3 class="text-xl font-semibold mb-2">Objective</h3> | |
<p>Conquer the entire map by capturing all territories and eliminating your opponent's castles.</p> | |
</div> | |
<div> | |
<h3 class="text-xl font-semibold mb-2">Gameplay</h3> | |
<ul class="list-disc pl-5 space-y-2"> | |
<li>Players take turns performing actions</li> | |
<li>Each turn you can move units, recruit new units, or build castles</li> | |
<li>Units can move to adjacent tiles (1 tile per turn)</li> | |
<li>To capture a neutral tile, move at least 1 unit onto it</li> | |
<li>To attack an enemy tile, move more units than the defender has</li> | |
<li>Castles provide defense bonuses and allow recruitment</li> | |
</ul> | |
</div> | |
<div> | |
<h3 class="text-xl font-semibold mb-2">Controls</h3> | |
<ul class="list-disc pl-5 space-y-2"> | |
<li>Click on a tile to select it</li> | |
<li>Use the action buttons to perform actions</li> | |
<li>When moving, first select your units, then select destination</li> | |
<li>Click "End Turn" when finished with your actions</li> | |
</ul> | |
</div> | |
<div> | |
<h3 class="text-xl font-semibold mb-2">Tile Types</h3> | |
<div class="grid grid-cols-2 gap-4"> | |
<div class="flex items-center"> | |
<div class="hexagon neutral w-8 h-4.62 mr-2"> | |
<div class="hexagon:before border-bottom-color: #64C7CC"></div> | |
<div class="hexagon:after border-top-color: #64C7CC"></div> | |
</div> | |
<span>Neutral Land</span> | |
</div> | |
<div class="flex items-center"> | |
<div class="hexagon mountain w-8 h-4.62 mr-2"> | |
<div class="hexagon:before border-bottom-color: #795548"></div> | |
<div class="hexagon:after border-top-color: #795548"></div> | |
</div> | |
<span>Mountain (Impassable)</span> | |
</div> | |
<div class="flex items-center"> | |
<div class="hexagon water w-8 h-4.62 mr-2"> | |
<div class="hexagon:before border-bottom-color: #2196F3"></div> | |
<div class="hexagon:after border-top-color: #2196F3"></div> | |
</div> | |
<span>Water (Impassable)</span> | |
</div> | |
<div class="flex items-center"> | |
<div class="hexagon forest w-8 h-4.62 mr-2"> | |
<div class="hexagon:before border-bottom-color: #4CAF50"></div> | |
<div class="hexagon:after border-top-color: #4CAF50"></div> | |
</div> | |
<span>Forest (Movement cost 2)</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Game Over Modal --> | |
<div id="game-over-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden"> | |
<div class="bg-gray-800 rounded-lg p-6 max-w-md w-full"> | |
<h2 class="text-3xl font-bold mb-4 text-center" id="game-over-title">Victory!</h2> | |
<p class="text-xl mb-6 text-center" id="game-over-message"></p> | |
<div class="flex justify-center"> | |
<button id="play-again" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded"> | |
Play Again | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Game state | |
const gameState = { | |
currentPlayer: 1, | |
players: { | |
1: { gold: 100, territories: 1, color: 'player1' }, | |
2: { gold: 100, territories: 1, color: 'player2' } | |
}, | |
map: [], | |
selectedTile: null, | |
actionMode: null, // 'move', 'recruit', 'build-castle' | |
moveOrigin: null, | |
mapSize: { rows: 9, cols: 9 }, | |
terrainTypes: ['neutral', 'mountain', 'water', 'forest'], | |
gameOver: false | |
}; | |
// Initialize the game | |
function initGame() { | |
createMap(); | |
renderMap(); | |
updateUI(); | |
addEventListeners(); | |
addToGameLog('Game started! Player 1\'s turn.'); | |
} | |
// Create the game map | |
function createMap() { | |
const { rows, cols } = gameState.mapSize; | |
for (let row = 0; row < rows; row++) { | |
gameState.map[row] = []; | |
for (let col = 0; col < cols; col++) { | |
// Determine terrain type (weighted probabilities) | |
let terrainType; | |
const rand = Math.random(); | |
if (rand < 0.1) terrainType = 'mountain'; | |
else if (rand < 0.2) terrainType = 'water'; | |
else if (rand < 0.35) terrainType = 'forest'; | |
else terrainType = 'neutral'; | |
// Set starting positions for players | |
let owner = null; | |
let units = 0; | |
let castle = false; | |
// Player 1 starting position (top-left) | |
if (row === 1 && col === 1) { | |
owner = 1; | |
units = 5; | |
castle = true; | |
} | |
// Player 2 starting position (bottom-right) | |
if (row === rows - 2 && col === cols - 2) { | |
owner = 2; | |
units = 5; | |
castle = true; | |
} | |
gameState.map[row][col] = { | |
row, | |
col, | |
terrain: terrainType, | |
owner, | |
units, | |
castle | |
}; | |
} | |
} | |
// Update territory counts | |
updateTerritoryCounts(); | |
} | |
// Render the game map | |
function renderMap() { | |
const gameBoard = document.getElementById('game-board'); | |
gameBoard.innerHTML = ''; | |
const { rows, cols } = gameState.mapSize; | |
for (let row = 0; row < rows; row++) { | |
const hexRow = document.createElement('div'); | |
hexRow.className = 'hex-row'; | |
for (let col = 0; col < cols; col++) { | |
const tile = gameState.map[row][col]; | |
const hexagon = document.createElement('div'); | |
hexagon.className = 'hexagon'; | |
// Set terrain class | |
hexagon.classList.add(tile.terrain); | |
// Set owner class if owned | |
if (tile.owner) { | |
hexagon.classList.add(gameState.players[tile.owner].color); | |
} | |
// Add castle if present | |
if (tile.castle) { | |
hexagon.classList.add('castle'); | |
const castleIcon = document.createElement('div'); | |
castleIcon.className = 'unit'; | |
castleIcon.innerHTML = '🏰'; | |
hexagon.appendChild(castleIcon); | |
} | |
// Add units if present | |
if (tile.units > 0) { | |
const unitCount = document.createElement('div'); | |
unitCount.className = 'unit'; | |
unitCount.innerHTML = tile.owner === 1 ? '⚔️' : '🛡️'; | |
unitCount.innerHTML += tile.units; | |
hexagon.appendChild(unitCount); | |
} | |
// Add click event | |
hexagon.addEventListener('click', () => handleTileClick(tile)); | |
// Set data attributes | |
hexagon.dataset.row = row; | |
hexagon.dataset.col = col; | |
hexRow.appendChild(hexagon); | |
} | |
gameBoard.appendChild(hexRow); | |
} | |
} | |
// Handle tile clicks | |
function handleTileClick(tile) { | |
// Clear previous selections | |
clearHighlights(); | |
// If in move mode and origin is selected | |
if (gameState.actionMode === 'move' && gameState.moveOrigin) { | |
attemptMove(gameState.moveOrigin, tile); | |
return; | |
} | |
// Select the clicked tile | |
gameState.selectedTile = tile; | |
// Highlight selected tile | |
const hexagon = document.querySelector(`.hexagon[data-row="${tile.row}"][data-col="${tile.col}"]`); | |
if (hexagon) { | |
hexagon.classList.add('selected'); | |
} | |
// If in move mode and this tile has movable units | |
if (gameState.actionMode === 'move' && tile.owner === gameState.currentPlayer && tile.units > 0) { | |
gameState.moveOrigin = tile; | |
highlightMovableTiles(tile); | |
} | |
// Update tile info panel | |
updateTileInfo(tile); | |
} | |
// Highlight movable tiles from a position | |
function highlightMovableTiles(tile) { | |
const { row, col } = tile; | |
const moves = getAdjacentTiles(row, col); | |
for (const move of moves) { | |
const targetTile = gameState.map[move.row][move.col]; | |
const hexagon = document.querySelector(`.hexagon[data-row="${move.row}"][data-col="${move.col}"]`); | |
if (!hexagon) continue; | |
// Can move to neutral or friendly tiles | |
if (targetTile.owner === null || targetTile.owner === gameState.currentPlayer) { | |
hexagon.classList.add('movable'); | |
} | |
// Can attack enemy tiles | |
else if (targetTile.owner !== gameState.currentPlayer) { | |
hexagon.classList.add('attackable'); | |
} | |
} | |
} | |
// Get adjacent tiles (hexagonal grid) | |
function getAdjacentTiles(row, col) { | |
const { rows, cols } = gameState.mapSize; | |
const directions = [ | |
{ dr: -1, dc: 0 }, // up | |
{ dr: 1, dc: 0 }, // down | |
{ dr: 0, dc: -1 }, // left | |
{ dr: 0, dc: 1 }, // right | |
{ dr: -1, dc: row % 2 === 0 ? -1 : 1 }, // up-left or up-right | |
{ dr: 1, dc: row % 2 === 0 ? -1 : 1 } // down-left or down-right | |
]; | |
const adjacent = []; | |
for (const dir of directions) { | |
const newRow = row + dir.dr; | |
const newCol = col + dir.dc; | |
if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) { | |
const tile = gameState.map[newRow][newCol]; | |
// Can't move to mountains or water | |
if (tile.terrain === 'mountain' || tile.terrain === 'water') { | |
continue; | |
} | |
adjacent.push(tile); | |
} | |
} | |
return adjacent; | |
} | |
// Attempt to move units from origin to target | |
function attemptMove(origin, target) { | |
// Check if target is adjacent | |
const adjacentTiles = getAdjacentTiles(origin.row, origin.col); | |
if (!adjacentTiles.some(t => t.row === target.row && t.col === target.col)) { | |
addToGameLog('Cannot move there - not adjacent!'); | |
return; | |
} | |
// Check if origin has units | |
if (origin.units <= 0) { | |
addToGameLog('No units to move!'); | |
return; | |
} | |
// Check if target is passable | |
if (target.terrain === 'mountain' || target.terrain === 'water') { | |
addToGameLog('Cannot move to impassable terrain!'); | |
return; | |
} | |
// Moving to neutral or friendly tile | |
if (target.owner === null || target.owner === gameState.currentPlayer) { | |
// Move all units | |
target.units += origin.units; | |
origin.units = 0; | |
// If neutral, claim it | |
if (target.owner === null) { | |
target.owner = gameState.currentPlayer; | |
gameState.players[gameState.currentPlayer].territories++; | |
addToGameLog(`Player ${gameState.currentPlayer} captured new territory!`); | |
} | |
// Update castle ownership if present | |
if (target.castle) { | |
target.owner = gameState.currentPlayer; | |
} | |
addToGameLog(`Moved ${origin.units} units to (${target.row}, ${target.col})`); | |
} | |
// Attacking enemy tile | |
else { | |
const attackerUnits = origin.units; | |
const defenderUnits = target.units; | |
const defenderBonus = target.castle ? 2 : 1; // Castle gives defense bonus | |
// Attacker wins | |
if (attackerUnits > defenderUnits * defenderBonus) { | |
const remainingUnits = attackerUnits - defenderUnits; | |
target.owner = gameState.currentPlayer; | |
target.units = remainingUnits; | |
origin.units = 0; | |
// Update territory counts | |
gameState.players[gameState.currentPlayer].territories++; | |
gameState.players[target.owner].territories--; | |
addToGameLog(`Player ${gameState.currentPlayer} conquered enemy territory! ${defenderUnits} defenders defeated.`); | |
// Check if castle was captured | |
if (target.castle) { | |
addToGameLog(`Player ${gameState.currentPlayer} captured an enemy castle!`); | |
// Check for game over (all castles captured) | |
checkGameOver(); | |
} | |
} | |
// Defender wins | |
else { | |
const remainingDefenders = defenderUnits - Math.floor(attackerUnits / defenderBonus); | |
target.units = remainingDefenders; | |
origin.units = 0; | |
addToGameLog(`Attack failed! ${attackerUnits} units lost.`); | |
} | |
} | |
// Reset move state | |
gameState.actionMode = null; | |
gameState.moveOrigin = null; | |
gameState.selectedTile = target; | |
// Update UI | |
updateUI(); | |
renderMap(); | |
updateTileInfo(target); | |
} | |
// Recruit new units | |
function recruitUnits() { | |
const tile = gameState.selectedTile; | |
if (!tile) { | |
addToGameLog('Select a tile first!'); | |
return; | |
} | |
if (tile.owner !== gameState.currentPlayer) { | |
addToGameLog('You can only recruit on your own territory!'); | |
return; | |
} | |
if (!tile.castle) { | |
addToGameLog('You need a castle to recruit units!'); | |
return; | |
} | |
const cost = 20; | |
if (gameState.players[gameState.currentPlayer].gold < cost) { | |
addToGameLog(`Not enough gold! Need ${cost}g.`); | |
return; | |
} | |
// Deduct gold and add units | |
gameState.players[gameState.currentPlayer].gold -= cost; | |
tile.units += 3; | |
addToGameLog(`Recruited 3 new units at (${tile.row}, ${tile.col}) for ${cost}g.`); | |
// Update UI | |
updateUI(); | |
renderMap(); | |
updateTileInfo(tile); | |
} | |
// Build a castle | |
function buildCastle() { | |
const tile = gameState.selectedTile; | |
if (!tile) { | |
addToGameLog('Select a tile first!'); | |
return; | |
} | |
if (tile.owner !== gameState.currentPlayer) { | |
addToGameLog('You can only build on your own territory!'); | |
return; | |
} | |
if (tile.castle) { | |
addToGameLog('This tile already has a castle!'); | |
return; | |
} | |
const cost = 50; | |
if (gameState.players[gameState.currentPlayer].gold < cost) { | |
addToGameLog(`Not enough gold! Need ${cost}g.`); | |
return; | |
} | |
// Deduct gold and build castle | |
gameState.players[gameState.currentPlayer].gold -= cost; | |
tile.castle = true; | |
addToGameLog(`Built a castle at (${tile.row}, ${tile.col}) for ${cost}g.`); | |
// Update UI | |
updateUI(); | |
renderMap(); | |
updateTileInfo(tile); | |
} | |
// End current player's turn | |
function endTurn() { | |
// Give income based on territories | |
const income = gameState.players[gameState.currentPlayer].territories * 10; | |
gameState.players[gameState.currentPlayer].gold += income; | |
addToGameLog(`Player ${gameState.currentPlayer} earned ${income}g from territories.`); | |
// Switch player | |
gameState.currentPlayer = gameState.currentPlayer === 1 ? 2 : 1; | |
gameState.actionMode = null; | |
gameState.moveOrigin = null; | |
gameState.selectedTile = null; | |
addToGameLog(`Player ${gameState.currentPlayer}'s turn.`); | |
// Update UI | |
updateUI(); | |
clearHighlights(); | |
} | |
// Check if game is over (all castles of one player captured) | |
function checkGameOver() { | |
let p1Castles = 0; | |
let p2Castles = 0; | |
for (let row = 0; row < gameState.mapSize.rows; row++) { | |
for (let col = 0; col < gameState.mapSize.cols; col++) { | |
const tile = gameState.map[row][col]; | |
if (tile.castle) { | |
if (tile.owner === 1) p1Castles++; | |
else if (tile.owner === 2) p2Castles++; | |
} | |
} | |
} | |
if (p1Castles === 0 || p2Castles === 0) { | |
gameState.gameOver = true; | |
const winner = p1Castles === 0 ? 2 : 1; | |
// Show game over modal | |
document.getElementById('game-over-title').textContent = `Player ${winner} Wins!`; | |
document.getElementById('game-over-message').textContent = `Player ${winner} has conquered the kingdom!`; | |
document.getElementById('game-over-modal').classList.remove('hidden'); | |
} | |
} | |
// Update territory counts for both players | |
function updateTerritoryCounts() { | |
let p1Territories = 0; | |
let p2Territories = 0; | |
for (let row = 0; row < gameState.mapSize.rows; row++) { | |
for (let col = 0; col < gameState.mapSize.cols; col++) { | |
const tile = gameState.map[row][col]; | |
if (tile.owner === 1) p1Territories++; | |
else if (tile.owner === 2) p2Territories++; | |
} | |
} | |
gameState.players[1].territories = p1Territories; | |
gameState.players[2].territories = p2Territories; | |
} | |
// Clear all tile highlights | |
function clearHighlights() { | |
const highlighted = document.querySelectorAll('.selected, .movable, .attackable'); | |
highlighted.forEach(el => { | |
el.classList.remove('selected', 'movable', 'attackable'); | |
}); | |
} | |
// Update the UI elements | |
function updateUI() { | |
// Update current player display | |
const currentPlayerEl = document.getElementById('current-player'); | |
currentPlayerEl.textContent = `Player ${gameState.currentPlayer}'s Turn`; | |
currentPlayerEl.className = `text-xl font-bold mb-2 text-center py-2 rounded ${gameState.currentPlayer === 1 ? 'bg-red-600' : 'bg-blue-600'}`; | |
// Update player stats | |
document.getElementById('p1-gold').textContent = gameState.players[1].gold; | |
document.getElementById('p1-territories').textContent = gameState.players[1].territories; | |
document.getElementById('p2-gold').textContent = gameState.players[2].gold; | |
document.getElementById('p2-territories').textContent = gameState.players[2].territories; | |
// Update action buttons based on mode | |
const moveBtn = document.getElementById('move-units'); | |
if (gameState.actionMode === 'move') { | |
moveBtn.textContent = 'Cancel Move'; | |
moveBtn.className = 'bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded'; | |
} else { | |
moveBtn.textContent = 'Move Units'; | |
moveBtn.className = 'bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'; | |
} | |
} | |
// Update the tile info panel | |
function updateTileInfo(tile) { | |
if (!tile) { | |
document.getElementById('tile-type').textContent = 'None'; | |
document.getElementById('tile-owner').textContent = 'None'; | |
document.getElementById('tile-units').textContent = '0'; | |
return; | |
} | |
document.getElementById('tile-type').textContent = tile.terrain.charAt(0).toUpperCase() + tile.terrain.slice(1); | |
document.getElementById('tile-owner').textContent = tile.owner ? `Player ${tile.owner}` : 'Neutral'; | |
document.getElementById('tile-units').textContent = tile.units; | |
if (tile.castle) { | |
document.getElementById('tile-type').textContent += ' (Castle)'; | |
} | |
} | |
// Add message to game log | |
function addToGameLog(message) { | |
const gameLog = document.getElementById('game-log'); | |
const entry = document.createElement('p'); | |
entry.textContent = message; | |
gameLog.appendChild(entry); | |
gameLog.scrollTop = gameLog.scrollHeight; | |
} | |
// Add event listeners | |
function addEventListeners() { | |
// Action buttons | |
document.getElementById('end-turn').addEventListener('click', endTurn); | |
document.getElementById('recruit').addEventListener('click', recruitUnits); | |
document.getElementById('build-castle').addEventListener('click', buildCastle); | |
// Move units button toggles move mode | |
document.getElementById('move-units').addEventListener('click', () => { | |
if (gameState.actionMode === 'move') { | |
gameState.actionMode = null; | |
gameState.moveOrigin = null; | |
clearHighlights(); | |
} else { | |
gameState.actionMode = 'move'; | |
if (gameState.selectedTile && gameState.selectedTile.owner === gameState.currentPlayer && gameState.selectedTile.units > 0) { | |
gameState.moveOrigin = gameState.selectedTile; | |
highlightMovableTiles(gameState.selectedTile); | |
} | |
} | |
updateUI(); | |
}); | |
// Help modal | |
document.getElementById('close-help').addEventListener('click', () => { | |
document.getElementById('help-modal').classList.add('hidden'); | |
}); | |
// Game over modal | |
document.getElementById('play-again').addEventListener('click', () => { | |
document.getElementById('game-over-modal').classList.add('hidden'); | |
resetGame(); | |
}); | |
} | |
// Reset the game | |
function resetGame() { | |
gameState.currentPlayer = 1; | |
gameState.players = { | |
1: { gold: 100, territories: 1, color: 'player1' }, | |
2: { gold: 100, territories: 1, color: 'player2' } | |
}; | |
gameState.map = []; | |
gameState.selectedTile = null; | |
gameState.actionMode = null; | |
gameState.moveOrigin = null; | |
gameState.gameOver = false; | |
document.getElementById('game-log').innerHTML = ''; | |
createMap(); | |
renderMap(); | |
updateUI(); | |
addToGameLog('New game started! Player 1\'s turn.'); | |
} | |
// Initialize the game when the page loads | |
window.addEventListener('DOMContentLoaded', initGame); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=yazdaabdul/mount-and-blade" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |