// Sprite-Editor: Gebäude in allen Ausbaustufen (1×1 und 2×2/Mega) auf echten // Tiles justieren – Sprite-Breite, Boden-Anker und Solarpanel-Position. // Werte landen live in localStorage (tt_sprite_tuning) und wirken sofort im // Spiel auf diesem Gerät; der Export wird von Atlas fest in iso.js übernommen. import { createState, place } from './model.js'; import { IsoRenderer, SPRITE_DEFS, SOLAR_POS } from './iso.js'; import { BUILDINGS } from './data.js'; const $ = id => document.getElementById(id); // ---------- Test-Welt: flach geräumte Bühne ---------- const state = createState('boom'); state.stageRow = 0; state.season = 'Sommer'; state.money = 9e9; state.guestsToday = 0; for (const row of state.map) { for (const t of row) { t.building = null; t.level = null; delete t.buildingRef; delete t.mega; delete t.eco; } } state.buildings = []; state.pistes = []; state.trails = []; // Bühne: ebene 2×2-Fläche nahe der Kartenmitte suchen let SPOT = { c: 18, r: 24 }; outer: for (let r = 20; r < 34; r++) { for (let c = 10; c < 28; c++) { const ok = [[0, 0], [1, 0], [0, 1], [1, 1]].every(([dc, dr]) => { const t = state.map[r + dr]?.[c + dc]; return t && !t.road && t.terrain !== 'water' && t.terrain !== 'village' && t.terrain !== 'rock' && t.elev >= 1 && t.elev <= 3; }); if (ok) { SPOT = { c, r }; break outer; } } } const renderer = new IsoRenderer(document.getElementById('map'), state, { onTileTap: () => {} }); // ---------- Tuning-Store ---------- let tuned = { sprites: {}, solar: {} }; try { tuned = Object.assign(tuned, JSON.parse(localStorage.getItem('tt_sprite_tuning') || '{}')); } catch {} function saveTuning() { localStorage.setItem('tt_sprite_tuning', JSON.stringify(tuned)); $('edOut').value = JSON.stringify(tuned, null, 2); } // ---------- Auswahl ---------- const sel = $('edType'); for (const [type, def] of Object.entries(BUILDINGS)) { if (def.action) continue; // Aufforsten etc. const o = document.createElement('option'); o.value = type; o.textContent = `${def.emoji} ${def.name}`; sel.appendChild(o); } let curLevel = 1; function spriteKey() { const type = sel.value; if ($('edMega').checked && SPRITE_DEFS[`mega_${type}`]) return `mega_${type}`; if (type === 'hotel' && curLevel >= 2) return 'hotel2'; return type; } // ---------- Bühne neu bestücken ---------- function rebuild() { for (const row of state.map) { for (const t of row) { t.building = null; t.level = null; delete t.buildingRef; delete t.mega; delete t.eco; } } state.buildings = []; state.pistes = []; state.trails = []; const type = sel.value; const def = BUILDINGS[type]; const mega = $('edMega').checked && (def.size || 1) === 1; const { c, r } = SPOT; if (mega) { // 4 gleiche im Quadrat → verschmilzt automatisch (falls merge-fähig), // sonst von Hand zum 2×2 zusammensetzen. place(state, type, c, r); place(state, type, c + 1, r); place(state, type, c, r + 1); place(state, type, c + 1, r + 1); let b = state.buildings.find(x => x.mega); if (!b && state.buildings.length) { b = state.buildings[0]; state.buildings = [b]; b.size = 2; b.mega = true; for (let dr = 0; dr < 2; dr++) for (let dc = 0; dc < 2; dc++) { const t = state.map[r + dr][c + dc]; t.building = type; if (dr || dc) t.buildingRef = { c, r }; else { delete t.buildingRef; t.mega = true; } } } } else { place(state, type, c, r); } const b = state.buildings[0]; if (b) { b.level = curLevel; state.map[b.r][b.c].level = curLevel; if ($('edEco').checked) { b.eco = true; state.map[b.r][b.c].eco = true; } } syncSliders(); } // ---------- Slider ↔ Werte ---------- function syncSliders() { const key = spriteKey(); const sd = SPRITE_DEFS[key] || { w: 60, bottom: 8 }; const sp = SOLAR_POS[key] || ($('edMega').checked || (BUILDINGS[sel.value].size || 1) > 1 ? SOLAR_POS.defaultBig : SOLAR_POS.default); $('edW').value = sd.w; $('edWv').textContent = sd.w; $('edB').value = sd.bottom; $('edBv').textContent = sd.bottom; $('edSX').value = sp.dx ?? 0; $('edSXv').textContent = sp.dx ?? 0; $('edSY').value = sp.dy ?? -27; $('edSYv').textContent = sp.dy ?? -27; $('edSS').value = sp.s ?? 1; $('edSSv').textContent = (sp.s ?? 1).toFixed(2); } function applySprite() { const key = spriteKey(); const w = +$('edW').value, bottom = +$('edB').value; SPRITE_DEFS[key] = Object.assign(SPRITE_DEFS[key] || {}, { w, bottom }); tuned.sprites[key] = { w, bottom }; $('edWv').textContent = w; $('edBv').textContent = bottom; saveTuning(); } function applySolar() { const key = spriteKey(); const v = { dx: +$('edSX').value, dy: +$('edSY').value, s: +$('edSS').value }; SOLAR_POS[key] = v; tuned.solar[key] = v; $('edSXv').textContent = v.dx; $('edSYv').textContent = v.dy; $('edSSv').textContent = v.s.toFixed(2); saveTuning(); } ['edW', 'edB'].forEach(id => $(id).addEventListener('input', applySprite)); ['edSX', 'edSY', 'edSS'].forEach(id => $(id).addEventListener('input', applySolar)); sel.addEventListener('change', rebuild); $('edMega').addEventListener('change', rebuild); $('edEco').addEventListener('change', rebuild); document.querySelectorAll('#edLevels button').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('#edLevels button').forEach(b => b.classList.remove('active')); btn.classList.add('active'); curLevel = +btn.dataset.lvl; rebuild(); }); }); $('edReset').addEventListener('click', () => { const key = spriteKey(); delete tuned.sprites[key]; delete tuned.solar[key]; saveTuning(); location.reload(); // Original-Werte aus dem Code laden }); $('edCopy').addEventListener('click', async () => { $('edOut').value = JSON.stringify(tuned, null, 2); try { await navigator.clipboard.writeText($('edOut').value); $('edCopy').textContent = '✅ kopiert!'; } catch { $('edOut').select(); $('edCopy').textContent = '⬇︎ unten markiert'; } setTimeout(() => { $('edCopy').textContent = '📋 Export kopieren'; }, 1800); }); // ---------- Loop ---------- rebuild(); $('edOut').value = JSON.stringify(tuned, null, 2); let last = performance.now(); function frame(now) { const dt = now - last; last = now; renderer.draw(now, dt, 1); requestAnimationFrame(frame); } requestAnimationFrame(frame);