Files
2026-07-13 12:35:44 +08:00

187 lines
5.7 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 drawCircle(x, y, value, color, isFood) {
const size = isFood ? value : getSize(value);
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
}
function drawCellWithName(x, y, score, color, name) {
const size = getSize(score);
// Draw cell
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
// 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) {
drawCircle(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 => {
minimapCtx.beginPath();
minimapCtx.arc(
ai.x * scale,
ai.y * scale,
2,
0,
Math.PI * 2
);
minimapCtx.fillStyle = COLORS.MINIMAP.OTHER;
minimapCtx.fill();
});
// Draw player cells on minimap
gameState.playerCells.forEach(cell => {
minimapCtx.beginPath();
minimapCtx.arc(
cell.x * scale,
cell.y * scale,
3,
0,
Math.PI * 2
);
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('');
}