exafunction--windsurf-demo
5fbefc8949
- Replace drawCircle function with drawTriangle for equilateral triangles - Update drawCellWithName to render triangular player and AI entities - Convert food items to triangular shapes - Update minimap rendering to show triangular representations - Increase triangle sizes by 50% for better visibility - Maintain proper scaling based on entity scores
202 行
6.6 KiB
JavaScript
202 行
6.6 KiB
JavaScript
import { gameState } from './gameState.js';
|
|
import { getSize, calculateCenterOfMass } from './utils.js';
|
|
import { WORLD_SIZE, COLORS, FOOD_SIZE } from './config.js';
|
|
|
|
let canvas, ctx, minimapCanvas, minimapCtx, scoreElement, leaderboardContent;
|
|
|
|
export function initRenderer(canvasElements) {
|
|
canvas = canvasElements.gameCanvas;
|
|
ctx = canvas.getContext('2d');
|
|
minimapCanvas = canvasElements.minimapCanvas;
|
|
minimapCtx = minimapCanvas.getContext('2d');
|
|
scoreElement = canvasElements.scoreElement;
|
|
leaderboardContent = canvasElements.leaderboardContent;
|
|
|
|
// Initial canvas setup
|
|
resizeCanvas();
|
|
}
|
|
|
|
export function resizeCanvas() {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
}
|
|
|
|
function drawTriangle(x, y, value, color, isFood) {
|
|
const baseSize = isFood ? value : getSize(value);
|
|
const size = baseSize * 1.5; // Make triangles 50% bigger
|
|
const height = size * Math.sqrt(3) / 2; // Height of equilateral triangle
|
|
|
|
ctx.beginPath();
|
|
// Top vertex
|
|
ctx.moveTo(x, y - height * 2/3);
|
|
// Bottom left vertex
|
|
ctx.lineTo(x - size/2, y + height * 1/3);
|
|
// Bottom right vertex
|
|
ctx.lineTo(x + size/2, y + height * 1/3);
|
|
ctx.closePath();
|
|
|
|
ctx.fillStyle = color;
|
|
ctx.fill();
|
|
}
|
|
|
|
function drawCellWithName(x, y, score, color, name) {
|
|
const size = getSize(score);
|
|
|
|
// Draw cell
|
|
drawTriangle(x, y, score, color, false);
|
|
|
|
// Draw name
|
|
if (size > 20) { // Only draw name if cell is big enough
|
|
ctx.save();
|
|
|
|
// Calculate font size based on cell size
|
|
const fontSize = Math.max(12, Math.min(20, size / 2));
|
|
ctx.font = `bold ${fontSize}px Arial`;
|
|
ctx.fillStyle = 'white';
|
|
ctx.strokeStyle = 'black';
|
|
ctx.lineWidth = 3;
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'middle';
|
|
|
|
// Draw text stroke (outline)
|
|
ctx.strokeText(name, x, y);
|
|
// Draw text fill
|
|
ctx.fillText(name, x, y);
|
|
|
|
ctx.restore();
|
|
}
|
|
}
|
|
|
|
export function drawGame() {
|
|
if (!ctx) return;
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Update camera to follow player's center of mass
|
|
const centerOfMass = calculateCenterOfMass(gameState.playerCells);
|
|
gameState.camera.x = centerOfMass.x - canvas.width / 2;
|
|
gameState.camera.y = centerOfMass.y - canvas.height / 2;
|
|
|
|
// Draw food
|
|
gameState.food.forEach(food => {
|
|
const screenX = food.x - gameState.camera.x;
|
|
const screenY = food.y - gameState.camera.y;
|
|
|
|
if (screenX >= -FOOD_SIZE && screenX <= canvas.width + FOOD_SIZE &&
|
|
screenY >= -FOOD_SIZE && screenY <= canvas.height + FOOD_SIZE) {
|
|
drawTriangle(screenX, screenY, FOOD_SIZE, food.color, true);
|
|
}
|
|
});
|
|
|
|
// Draw AI players
|
|
gameState.aiPlayers.forEach(ai => {
|
|
const screenX = ai.x - gameState.camera.x;
|
|
const screenY = ai.y - gameState.camera.y;
|
|
const size = getSize(ai.score);
|
|
|
|
if (screenX >= -size && screenX <= canvas.width + size &&
|
|
screenY >= -size && screenY <= canvas.height + size) {
|
|
drawCellWithName(screenX, screenY, ai.score, ai.color, ai.name);
|
|
}
|
|
});
|
|
|
|
// Draw player cells
|
|
gameState.playerCells.forEach(cell => {
|
|
const screenX = cell.x - gameState.camera.x;
|
|
const screenY = cell.y - gameState.camera.y;
|
|
const size = getSize(cell.score);
|
|
|
|
if (screenX >= -size && screenX <= canvas.width + size &&
|
|
screenY >= -size && screenY <= canvas.height + size) {
|
|
drawCellWithName(screenX, screenY, cell.score, COLORS.PLAYER, gameState.playerName);
|
|
}
|
|
});
|
|
|
|
// Update score display
|
|
scoreElement.textContent = `Score: ${Math.floor(gameState.playerCells.reduce((sum, cell) => sum + cell.score, 0))}`;
|
|
}
|
|
|
|
export function drawMinimap() {
|
|
if (!minimapCtx) return;
|
|
|
|
const MINIMAP_SIZE = 150;
|
|
const scale = MINIMAP_SIZE / WORLD_SIZE;
|
|
|
|
minimapCtx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
|
minimapCtx.fillRect(0, 0, MINIMAP_SIZE, MINIMAP_SIZE);
|
|
|
|
const viewWidth = canvas.width * scale;
|
|
const viewHeight = canvas.height * scale;
|
|
const viewX = gameState.camera.x * scale;
|
|
const viewY = gameState.camera.y * scale;
|
|
minimapCtx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
|
|
minimapCtx.strokeRect(viewX, viewY, viewWidth, viewHeight);
|
|
|
|
// Draw AI players on minimap
|
|
gameState.aiPlayers.forEach(ai => {
|
|
const size = 6; // Increased size for minimap triangles
|
|
const height = size * Math.sqrt(3) / 2;
|
|
|
|
minimapCtx.beginPath();
|
|
// Top vertex
|
|
minimapCtx.moveTo(ai.x * scale, ai.y * scale - height * 2/3);
|
|
// Bottom left vertex
|
|
minimapCtx.lineTo(ai.x * scale - size/2, ai.y * scale + height * 1/3);
|
|
// Bottom right vertex
|
|
minimapCtx.lineTo(ai.x * scale + size/2, ai.y * scale + height * 1/3);
|
|
minimapCtx.closePath();
|
|
|
|
minimapCtx.fillStyle = COLORS.MINIMAP.OTHER;
|
|
minimapCtx.fill();
|
|
});
|
|
|
|
// Draw player cells on minimap
|
|
gameState.playerCells.forEach(cell => {
|
|
const size = 9; // Increased size for player cells on minimap
|
|
const height = size * Math.sqrt(3) / 2;
|
|
|
|
minimapCtx.beginPath();
|
|
// Top vertex
|
|
minimapCtx.moveTo(cell.x * scale, cell.y * scale - height * 2/3);
|
|
// Bottom left vertex
|
|
minimapCtx.lineTo(cell.x * scale - size/2, cell.y * scale + height * 1/3);
|
|
// Bottom right vertex
|
|
minimapCtx.lineTo(cell.x * scale + size/2, cell.y * scale + height * 1/3);
|
|
minimapCtx.closePath();
|
|
|
|
minimapCtx.fillStyle = COLORS.MINIMAP.PLAYER;
|
|
minimapCtx.fill();
|
|
});
|
|
}
|
|
|
|
export function updateLeaderboard() {
|
|
if (!leaderboardContent) return;
|
|
|
|
const playerTotalScore = gameState.playerCells.reduce((sum, cell) => sum + cell.score, 0);
|
|
|
|
// Combine player score with AI scores
|
|
const allPlayers = [
|
|
{
|
|
name: gameState.playerName,
|
|
score: playerTotalScore,
|
|
isPlayer: true
|
|
},
|
|
...gameState.aiPlayers.map(ai => ({
|
|
name: ai.name,
|
|
score: ai.score,
|
|
isPlayer: false
|
|
}))
|
|
];
|
|
|
|
allPlayers.sort((a, b) => b.score - a.score);
|
|
|
|
leaderboardContent.innerHTML = allPlayers
|
|
.slice(0, 5)
|
|
.map((player, index) => `
|
|
<div class="leaderboard-item">
|
|
<span class="${player.isPlayer ? 'player-name' : ''}">${index + 1}. ${player.name}</span>
|
|
<span>${Math.floor(player.score)}</span>
|
|
</div>
|
|
`)
|
|
.join('');
|
|
} |