import { config, saveSettings, defaultConfig, setConfig } from './config.js'; import { grid, cols, rows, selectedType, setSelectedType, setGrid, canvas, cellSize } from './state.js'; import { spawnCell } from './physics.js'; export const buttons = [ { type: 'dirt', color: '#ffaa00', label: 'Dirt' }, { type: 'plant', color: '#228B22', label: 'Plant' }, { type: 'water', color: '#0000FF', label: 'Water' }, { type: 'fire', color: '#FF5500', label: 'Fire' }, { type: 'rock', color: '#808080', label: 'Rock' }, { type: 'wood', color: '#8B4513', label: 'Wood' }, { type: 'explosive', color: '#8B0000', label: 'TNT' }, { type: 'acid', color: '#7FFF00', label: 'Acid' }, { type: 'steam', color: '#E0FFFF', label: 'Steam' }, { type: 'clear', color: '#FFFFFF', label: 'Clear' } ]; export function setupUI() { // Settings Drawer const drawer = document.getElementById('settingsContent'); const toggle = document.getElementById('toggleDrawer'); const drawerContainer = document.getElementById('settingsDrawer'); const pauseBtn = document.getElementById('togglePause'); toggle.addEventListener('click', () => { drawerContainer.classList.toggle('open'); }); function updatePauseBtn() { pauseBtn.textContent = config.time.paused ? '▶️' : '⏸️'; } pauseBtn.addEventListener('click', () => { config.time.paused = !config.time.paused; saveSettings(); updatePauseBtn(); }); updatePauseBtn(); renderSettings(drawer); // Elements Drawer const elDrawer = document.getElementById('elementContent'); const elToggle = document.getElementById('toggleElements'); const elContainer = document.getElementById('elementDrawer'); elToggle.addEventListener('click', () => { elContainer.classList.toggle('closed'); }); renderElements(elDrawer); } function renderElements(container) { container.innerHTML = ''; buttons.forEach(btn => { const button = document.createElement('button'); button.className = 'element-btn'; if (btn.type === selectedType) button.classList.add('selected'); button.style.backgroundColor = btn.color; button.textContent = btn.label; if (btn.type === 'clear' || btn.type === 'steam' || btn.type === 'acid') button.style.color = '#000'; if (btn.type === 'clear') { button.style.marginTop = 'auto'; } button.onclick = () => { if (btn.type === 'clear') { setGrid(new Array(cols).fill(null).map(() => new Array(rows).fill(null))); } else { setSelectedType(btn.type); document.querySelectorAll('.element-btn').forEach(b => b.classList.remove('selected')); button.classList.add('selected'); } }; container.appendChild(button); }); } export function renderSettings(container) { container.innerHTML = ''; // Clear existing function addControl(group, key, subkey, def) { const container = document.createElement('div'); const labelEl = document.createElement('label'); let val = subkey ? config[group][key][subkey] : config[group][key]; labelEl.textContent = def.label; labelEl.style.display = 'inline-block'; const input = document.createElement('input'); input.type = def.type || 'range'; if (def.type === 'checkbox') { container.className = 'setting-checkbox'; input.checked = val; input.style.marginRight = '10px'; input.addEventListener('change', (e) => { if (subkey) config[group][key][subkey] = e.target.checked; else config[group][key] = e.target.checked; saveSettings(); }); container.append(input, labelEl); } else if (def.type === 'radio') { const fieldset = document.createElement('fieldset'); const legend = document.createElement('legend'); legend.textContent = def.label; fieldset.appendChild(legend); def.options.forEach(option => { const radioId = `radio-${group}--`; const radioContainer = document.createElement('div'); radioContainer.style.display = 'inline-block'; radioContainer.style.marginRight = '15px'; const radioInput = document.createElement('input'); radioInput.type = 'radio'; radioInput.name = `radio--`; radioInput.value = option; radioInput.id = radioId; if (val === option) radioInput.checked = true; radioInput.addEventListener('change', (e) => { config[group][key] = e.target.value; saveSettings(); }); const radioLabel = document.createElement('label'); radioLabel.textContent = option.charAt(0).toUpperCase() + option.slice(1); radioLabel.setAttribute('for', radioId); radioLabel.style.marginLeft = '5px'; radioContainer.append(radioInput, radioLabel); fieldset.appendChild(radioContainer); }); return fieldset; } else { container.className = 'setting-control'; input.min = def.min; input.max = def.max; input.step = def.step; input.value = val; const valueSpan = document.createElement('span'); valueSpan.textContent = val; input.addEventListener('input', (e) => { const newVal = parseFloat(e.target.value); if (subkey) config[group][key][subkey] = newVal; else config[group][key] = newVal; saveSettings(); valueSpan.textContent = newVal.toFixed(Math.max(def.step.toString().split('.')[1]?.length || 0, 2)); }); container.append(labelEl, input, valueSpan); } return container; } const groups = { time: [ { key: 'speed', label: 'Speed', min: 0, max: 20, step: 1 } ], global: [ { key: 'gravity', label: 'Gravity', min: -1.0, max: 2.0, step: 0.1 }, { key: 'drag', label: 'Drag', min: 0.1, max: 1.0, step: 0.05 } ], explosive: [ { key: 'force', label: 'Blast Force', min: 0, max: 20, step: 1 }, { key: 'radius', label: 'Blast Reach', min: 1, max: 50, step: 1 }, { key: 'destroy', subkey: 'rock', type: 'checkbox', label: 'Destroy Rock' }, { key: 'destroy', subkey: 'wood', type: 'checkbox', label: 'Destroy Wood' }, { key: 'destroy', subkey: 'dirt', type: 'checkbox', label: 'Destroy Dirt' }, { key: 'destroy', subkey: 'plant', type: 'checkbox', label: 'Destroy Plant' }, { key: 'destroy', subkey: 'water', type: 'checkbox', label: 'Destroy Water' }, { key: 'destroy', subkey: 'acid', type: 'checkbox', label: 'Destroy Acid' } ], acid: [ { key: 'dissolveChance', label: 'Dissolve Rate', min: 0, max: 1.0, step: 0.01 }, { key: 'sinkChance', label: 'Sink Rate', min: 0, max: 1.0, step: 0.05 }, { key: 'explodeTNT', type: 'checkbox', label: 'Explode TNT' }, { key: 'dissolve', subkey: 'rock', type: 'checkbox', label: 'Dissolve Rock' }, { key: 'dissolve', subkey: 'wood', type: 'checkbox', label: 'Dissolve Wood' }, { key: 'dissolve', subkey: 'dirt', type: 'checkbox', label: 'Dissolve Dirt' }, { key: 'dissolve', subkey: 'plant', type: 'checkbox', label: 'Dissolve Plant' }, { key: 'dissolve', subkey: 'water', type: 'checkbox', label: 'Infect Water' } ], fire: [ { key: 'burnChance', label: 'Fire Decay Rate', min: 0, max: 1.0, step: 0.01 } ], water: [ { key: 'flowChance', label: 'Flow Rate', min: 0, max: 1.0, step: 0.05 } ], steam: [ { key: 'condenseChance', label: 'Condense Rate', min: 0, max: 1.0, step: 0.01 } ], plant: [ { key: 'growChance', label: 'Growth Rate', min: 0, max: 1.0, step: 0.01 }, { key: 'woodificationChance', label: 'Wood Rate', min: 0, max: 1.0, step: 0.01 }, { key: 'burnChance', label: 'Burn Rate', min: 0, max: 1.0, step: 0.05 } ], wood: [ { key: 'burnChance', label: 'Burn Rate', min: 0, max: 1.0, step: 0.01 } ], visual: [ { key: 'style', type: 'radio', label: 'Style', options: ['blocky', 'bloby', 'fuzzy'] } ] }; for (const [groupName, settings] of Object.entries(groups)) { const groupDiv = document.createElement('div'); groupDiv.className = 'setting-group'; const title = document.createElement('h3'); title.textContent = groupName.charAt(0).toUpperCase() + groupName.slice(1); groupDiv.appendChild(title); settings.forEach(s => { groupDiv.appendChild(addControl(groupName, s.key, s.subkey, s)); }); container.appendChild(groupDiv); } // Defaults Button const resetBtn = document.createElement('button'); resetBtn.textContent = 'Reset to Defaults'; resetBtn.style.width = '100%'; resetBtn.style.padding = '10px'; resetBtn.style.marginTop = '20px'; resetBtn.style.cursor = 'pointer'; resetBtn.onclick = () => { setConfig(JSON.parse(JSON.stringify(defaultConfig))); saveSettings(); renderSettings(container); }; container.appendChild(resetBtn); } export function handleInput(e) { const rect = canvas.getBoundingClientRect(); function getPos(clientX, clientY) { return { x: clientX - rect.left, y: clientY - rect.top }; } // Handle Drawing const drawAt = (clientX, clientY) => { const {x, y} = getPos(clientX, clientY); // No need to check isOverButton because DOM elements handle their own clicks const col = Math.floor(x / cellSize); const row = Math.floor(y / cellSize); for (let i = -1; i <= 1; i++) { for (let j = -1; j <= 1; j++) { if (Math.random() > 0.5) { spawnCell(col + i, row + j, selectedType); } } } }; if (e.type.startsWith('mouse')) { drawAt(e.clientX, e.clientY); } else if (e.type.startsWith('touch')) { for (let i = 0; i < e.touches.length; i++) { const t = e.touches[i]; drawAt(t.clientX, t.clientY); } } }