diff --git a/src/App.tsx b/src/App.tsx index 519abb0..a9ddd8a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,11 +2,11 @@ import { useRef, useEffect, useState, useCallback } from 'react' import './App.css' import { DEFAULT_ZOOM, MAX_ZOOM } from './sim/constants' -type Material = 'sand' | 'water' | 'dirt' | 'stone' | 'plant' | 'fire' | 'gas' | 'fluff' | 'bug' | 'plasma' | 'nitro' | 'glass' | 'lightning' | 'slime' | 'ant' | 'alien' | 'quark' | 'crystal' | 'ember' | 'static' | 'bird' | 'gunpowder' | 'tap' | 'anthill' | 'bee' | 'flower' | 'hive' | 'honey' | 'nest' | 'gun' | 'cloud' | 'acid' | 'lava' | 'snow' | 'volcano' | 'mold' | 'mercury' | 'void' | 'seed' | 'rust' | 'spore' | 'algae' | 'poison' | 'dust' | 'firework' | 'bubble' | 'glitter' | 'star' | 'comet' | 'blackhole' | 'firefly' | 'worm' | 'fairy' | 'fish' | 'moth' | 'vent' -type Tool = Material | 'erase' +type Material = 'empty' | 'sand' | 'water' | 'dirt' | 'stone' | 'plant' | 'fire' | 'gas' | 'fluff' | 'bug' | 'plasma' | 'nitro' | 'glass' | 'lightning' | 'slime' | 'ant' | 'alien' | 'quark' | 'crystal' | 'ember' | 'static' | 'bird' | 'gunpowder' | 'tap' | 'anthill' | 'bee' | 'flower' | 'hive' | 'honey' | 'nest' | 'gun' | 'cloud' | 'acid' | 'lava' | 'snow' | 'volcano' | 'mold' | 'mercury' | 'void' | 'seed' | 'rust' | 'spore' | 'algae' | 'poison' | 'dust' | 'firework' | 'bubble' | 'glitter' | 'star' | 'comet' | 'blackhole' | 'firefly' | 'worm' | 'fairy' | 'fish' | 'moth' | 'vent' +type Tool = Material const BUTTON_COLORS: Record = { - erase: '#f87171', + empty: '#f87171', sand: '#e6c86e', water: '#4a90d9', dirt: '#8b5a2b', stone: '#666666', plant: '#228b22', fire: '#ff6600', gas: '#a8b844', fluff: '#f5e6d3', bug: '#ff69b4', plasma: '#c8a2c8', nitro: '#39ff14', glass: '#a8d8ea', @@ -489,7 +489,7 @@ function App() { }, [dropdownOpen]) const categories: Array<{ label: string; items: Tool[] }> = [ - { label: 'basic', items: ['erase', 'sand', 'water', 'dirt', 'stone', 'glass', 'snow', 'dust', 'fluff'] }, + { label: 'basic', items: ['empty', 'sand', 'water', 'dirt', 'stone', 'glass', 'snow', 'dust', 'fluff'] }, { label: 'fluid', items: ['slime', 'acid', 'lava', 'mercury', 'honey', 'poison', 'gas', 'bubble'] }, { label: 'energy', items: ['fire', 'ember', 'plasma', 'lightning', 'static', 'nitro', 'gunpowder', 'firework', 'quark', 'comet'] }, { label: 'nature', items: ['plant', 'seed', 'flower', 'algae', 'mold', 'spore', 'rust', 'crystal', 'void', 'glitter'] }, @@ -542,6 +542,7 @@ function App() { className="material-dropdown-trigger" style={{ '--material-color': BUTTON_COLORS[tool] } as React.CSSProperties} onPointerDown={(e) => { + e.preventDefault(); (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId) brushDragRef.current = { startX: e.clientX, startSize: brushSizeRef.current, moved: false } }} @@ -587,7 +588,7 @@ function App() { - ))} - - -
Changing size resets the simulation
- -
-
-
World
-
- - + settingsOpen && ( +
setSettingsOpen(false)}> +
e.stopPropagation()}> +
+
Map Size
+
+ Current: {gridDims.cols} x {gridDims.rows} +
+
+ {PRESET_SIZES.map((size) => ( + + ))} + +
+
Changing size resets the simulation
+
+
+
+
World
+
+ + +
+
-
-
- ) - } + ) + }
) } diff --git a/src/physics.worker.ts b/src/physics.worker.ts index 5893c48..e931054 100644 --- a/src/physics.worker.ts +++ b/src/physics.worker.ts @@ -2,7 +2,7 @@ // Runs physics simulation and rendering off the main thread // Uses two-canvas pipeline: world buffer (1px/cell) → GPU-scaled display canvas -import { MATERIAL_TO_ID, type Material, EMPTY, STONE, TAP, GUN, BLACK_HOLE, +import { MATERIAL_TO_ID, type Material, STONE, TAP, GUN, BLACK_HOLE, DEFAULT_ZOOM, BG_COLOR } from './sim/constants' import { ARCHETYPES } from './sim/archetypes' import { renderSystem } from './sim/systems/render' @@ -22,7 +22,7 @@ let sim: Simulation | null = null let isPaused = false let pauseAtStep: number | null = null let debugChunks = false -let pendingInputs: Array<{ x: number; y: number; prevX: number; prevY: number; tool: Material | 'erase'; brushSize: number }> = [] +let pendingInputs: Array<{ x: number; y: number; prevX: number; prevY: number; tool: Material; brushSize: number }> = [] // Camera state let camX = 0 // top-left world cell (float) @@ -47,10 +47,10 @@ function initGrid(displayWidth: number, displayHeight: number, cols?: number, ro camY = 0 } -function addParticles(cellX: number, cellY: number, tool: Material | 'erase', brushSize: number) { +function addParticles(cellX: number, cellY: number, tool: Material, brushSize: number) { if (!sim) return const { grid, cols, rows, rand, chunkMap } = sim - const matId = tool === 'erase' ? EMPTY : MATERIAL_TO_ID[tool as Material] + const matId = MATERIAL_TO_ID[tool] if (matId === GUN) { if (cellX >= 0 && cellX < cols && cellY >= 0 && cellY < rows) { @@ -70,7 +70,7 @@ function addParticles(cellX: number, cellY: number, tool: Material | 'erase', br if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) { const idx = ny * cols + nx const spawnRate = ARCHETYPES[matId]?.spawnRate ?? 0.45 - if ((tool === 'erase' || (rand() < spawnRate && grid[idx] === EMPTY))) { + if (rand() < spawnRate) { grid[idx] = matId } } diff --git a/src/sim/constants.ts b/src/sim/constants.ts index ff511cd..cb67e40 100644 --- a/src/sim/constants.ts +++ b/src/sim/constants.ts @@ -16,10 +16,10 @@ export const WORM = 62, FAIRY = 63 export const FISH = 64, MOTH = 65, VENT = 66 export const LIT_GUNPOWDER = 67 -export type Material = 'sand' | 'water' | 'dirt' | 'stone' | 'plant' | 'fire' | 'gas' | 'fluff' | 'bug' | 'plasma' | 'nitro' | 'glass' | 'lightning' | 'slime' | 'ant' | 'alien' | 'quark' | 'crystal' | 'ember' | 'static' | 'bird' | 'gunpowder' | 'tap' | 'anthill' | 'bee' | 'flower' | 'hive' | 'honey' | 'nest' | 'gun' | 'cloud' | 'acid' | 'lava' | 'snow' | 'volcano' | 'mold' | 'mercury' | 'void' | 'seed' | 'rust' | 'spore' | 'algae' | 'poison' | 'dust' | 'firework' | 'bubble' | 'glitter' | 'star' | 'comet' | 'blackhole' | 'firefly' | 'worm' | 'fairy' | 'fish' | 'moth' | 'vent' +export type Material = 'empty' | 'sand' | 'water' | 'dirt' | 'stone' | 'plant' | 'fire' | 'gas' | 'fluff' | 'bug' | 'plasma' | 'nitro' | 'glass' | 'lightning' | 'slime' | 'ant' | 'alien' | 'quark' | 'crystal' | 'ember' | 'static' | 'bird' | 'gunpowder' | 'tap' | 'anthill' | 'bee' | 'flower' | 'hive' | 'honey' | 'nest' | 'gun' | 'cloud' | 'acid' | 'lava' | 'snow' | 'volcano' | 'mold' | 'mercury' | 'void' | 'seed' | 'rust' | 'spore' | 'algae' | 'poison' | 'dust' | 'firework' | 'bubble' | 'glitter' | 'star' | 'comet' | 'blackhole' | 'firefly' | 'worm' | 'fairy' | 'fish' | 'moth' | 'vent' export const MATERIAL_TO_ID: Record = { - sand: SAND, water: WATER, dirt: DIRT, stone: STONE, plant: PLANT, + empty: EMPTY, sand: SAND, water: WATER, dirt: DIRT, stone: STONE, plant: PLANT, fire: FIRE, gas: GAS, fluff: FLUFF, bug: BUG, plasma: PLASMA, nitro: NITRO, glass: GLASS, lightning: LIGHTNING, slime: SLIME, ant: ANT, alien: ALIEN, quark: QUARK, crystal: CRYSTAL, ember: EMBER, static: STATIC, bird: BIRD, gunpowder: GUNPOWDER, tap: TAP, anthill: ANTHILL, diff --git a/tests/controls.spec.ts b/tests/controls.spec.ts index 118af59..5cd854c 100644 --- a/tests/controls.spec.ts +++ b/tests/controls.spec.ts @@ -21,7 +21,8 @@ test.describe('Control Buttons', () => { test('material dropdown opens and shows items', async ({ page }) => { const trigger = page.locator('.material-dropdown-trigger') - await trigger.click() + const box = await trigger.boundingBox() + await page.mouse.click(box!.x + box!.width / 2, box!.y + box!.height / 2) const sandBtn = page.locator('.material-dropdown-item', { hasText: 'sand' }) await expect(sandBtn).toBeVisible() await sandBtn.click() diff --git a/tests/particles.spec.ts b/tests/particles.spec.ts index af2f385..a22b26f 100644 --- a/tests/particles.spec.ts +++ b/tests/particles.spec.ts @@ -37,7 +37,8 @@ test.describe('Particle Interactions', () => { test('selecting materials via dropdown works', async ({ page }) => { const trigger = page.locator('.material-dropdown-trigger') - await trigger.click() + const box = await trigger.boundingBox() + await page.mouse.click(box!.x + box!.width / 2, box!.y + box!.height / 2) const waterBtn = page.locator('.material-dropdown-item', { hasText: 'water' }) await expect(waterBtn).toBeVisible()