sandsim/js/main.js
2025-04-05 17:24:10 +02:00

131 lines
5.3 KiB
JavaScript

// Main game variables and initialization
let canvas, ctx;
let currentTool = SAND;
let lastFrameTime = 0;
let fps = 0;
let debugMode = false;
// Initialize the simulation
window.onload = function() {
canvas = document.getElementById('simulation-canvas');
ctx = canvas.getContext('2d');
// Set canvas size to fill the screen
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Tool selection
document.getElementById('sand-btn').addEventListener('click', () => setTool(SAND));
document.getElementById('water-btn').addEventListener('click', () => setTool(WATER));
document.getElementById('dirt-btn').addEventListener('click', () => setTool(DIRT));
document.getElementById('stone-btn').addEventListener('click', () => setTool(STONE));
document.getElementById('grass-btn').addEventListener('click', () => setTool(GRASS));
document.getElementById('wood-btn').addEventListener('click', () => setTool(WOOD));
document.getElementById('seed-btn').addEventListener('click', () => setTool(SEED));
document.getElementById('tree-seed-btn').addEventListener('click', () => setTool(TREE_SEED));
document.getElementById('fire-btn').addEventListener('click', () => setTool(FIRE));
document.getElementById('lava-btn').addEventListener('click', () => setTool(LAVA));
document.getElementById('rabbit-btn').addEventListener('click', () => setTool(RABBIT));
document.getElementById('square-btn').addEventListener('click', () => setTool(SQUARE));
document.getElementById('circle-btn').addEventListener('click', () => setTool(CIRCLE));
document.getElementById('triangle-btn').addEventListener('click', () => setTool(TRIANGLE));
document.getElementById('eraser-btn').addEventListener('click', () => setTool(EMPTY));
// Navigation controls
document.getElementById('move-left').addEventListener('click', () => moveWorld(-CHUNK_SIZE/2, 0));
document.getElementById('move-right').addEventListener('click', () => moveWorld(CHUNK_SIZE/2, 0));
document.getElementById('move-up').addEventListener('click', () => moveWorld(0, -CHUNK_SIZE/2));
document.getElementById('move-down').addEventListener('click', () => moveWorld(0, CHUNK_SIZE/2));
document.getElementById('debug-btn').addEventListener('click', toggleDebug);
// Drawing events
canvas.addEventListener('mousedown', handleMouseDown);
canvas.addEventListener('mousemove', handleMouseMove);
canvas.addEventListener('mouseup', handleMouseUp);
canvas.addEventListener('mouseleave', handleMouseUp);
// Touch events for mobile
canvas.addEventListener('touchstart', handleTouchStart);
canvas.addEventListener('touchmove', handleTouchMove);
canvas.addEventListener('touchend', handleMouseUp);
// Initialize the first chunk and generate terrain around it
getOrCreateChunk(0, 0);
// Explicitly create and mark the stone layer as dirty
for (let dx = -5; dx <= 5; dx++) {
const chunkX = dx;
const chunkY = 1; // Stone layer
const key = getChunkKey(chunkX, chunkY);
getOrCreateChunk(chunkX, chunkY);
dirtyChunks.add(key);
}
generateChunksAroundPlayer();
// Start the simulation loop
requestAnimationFrame(simulationLoop);
// Initialize physics variables
window.physicsUpdateRate = 16; // ms between physics updates
window.lastPhysicsTime = 0;
window.fireUpdateCounter = 0;
};
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight - document.querySelector('.controls').offsetHeight;
}
function simulationLoop(timestamp) {
// Calculate FPS
const deltaTime = timestamp - lastFrameTime;
lastFrameTime = timestamp;
fps = Math.round(1000 / deltaTime);
document.getElementById('fps').textContent = `FPS: ${fps}`;
// Update physics with timestamp for rate limiting
updatePhysics(timestamp);
// Render
render();
// Memory management: Clean up chunk cache for chunks that are far away
if (timestamp % 5000 < 16) { // Run every ~5 seconds
cleanupChunkCache();
}
// Continue the loop
requestAnimationFrame(simulationLoop);
}
// Clean up chunk cache to prevent memory leaks
function cleanupChunkCache() {
if (!chunkCanvasCache) return;
const visibleChunks = getVisibleChunks();
const visibleKeys = new Set();
// Get all visible chunk keys
for (const { chunkX, chunkY } of visibleChunks) {
visibleKeys.add(getChunkKey(chunkX, chunkY));
}
// Remove cached canvases for chunks that are far from view
for (const key of chunkCanvasCache.keys()) {
if (!visibleKeys.has(key)) {
// Keep stone layer chunks in cache longer
if (key.split(',')[1] === '1') {
// Only remove if it's really far away
const [chunkX, chunkY] = key.split(',').map(Number);
const centerChunkX = Math.floor(worldOffsetX / CHUNK_SIZE);
if (Math.abs(chunkX - centerChunkX) > 10) {
chunkCanvasCache.delete(key);
}
} else {
chunkCanvasCache.delete(key);
}
}
}
}