diff --git a/src/main.js b/src/main.js index eb28795..8edce7e 100644 --- a/src/main.js +++ b/src/main.js @@ -2,20 +2,36 @@ import { createWorld, advance } from './sim/world.js'; import { createRenderer } from './render/app.js'; import { GalaxyView } from './render/galaxyView.js'; import { SystemView } from './render/systemView.js'; +import { Panel } from './ui/panel.js'; import { goTo, playerSystem } from './sim/player.js'; -const world = createWorld('neon-1'); +const world = createWorld(new URLSearchParams(location.search).get('seed') ?? 'neon-1'); const renderer = await createRenderer(document.getElementById('stage')); +let view = 'system'; +let speed = 1; +const controls = { + getSpeed: () => speed, + setSpeed: (v) => { speed = v; }, + getView: () => view, + setView: (name) => setView(name), +}; + +const panel = new Panel(document.getElementById('panel'), world, controls); const handlers = { - onSelectSystem: (id) => console.log('select system', id), + onSelectSystem: (id) => { panel.selectSystem(id); panel.update(); }, onJump: (laneId) => goTo(world, { laneId }), onGotoPlanet: (planetId) => goTo(world, { planetId }), }; const galaxyView = new GalaxyView(renderer, world, handlers); const systemView = new SystemView(renderer, world, handlers); -let view = 'system'; +// Rebuild static geometry when the canvas resizes (review: cached transforms went stale). +renderer.app.renderer.on('resize', () => { + galaxyView.buildStatic(); + systemView.builtFor = null; +}); + function setView(name) { view = name; galaxyView.setVisible(name === 'galaxy'); @@ -23,15 +39,36 @@ function setView(name) { } setView('system'); window.addEventListener('keydown', (e) => { + if (e.target !== document.body) return; // don't hijack keys from panel buttons (review) if (e.key === 'Tab') { e.preventDefault(); setView(view === 'galaxy' ? 'system' : 'galaxy'); } + if (e.key === ' ') { + e.preventDefault(); + speed = speed === 0 ? 1 : 0; + } }); const DAYS_PER_SEC = 0.25; +let banner = null; +let uiClock = 0; renderer.app.ticker.add(() => { - advance(world, world.t + (renderer.app.ticker.deltaMS / 1000) * DAYS_PER_SEC); + const dt = renderer.app.ticker.deltaMS / 1000; + if (speed > 0 && world.player.alive && !world.player.won) { + advance(world, world.t + dt * DAYS_PER_SEC * speed); + } + // Switch to system view automatically when arriving somewhere new. if (view === 'galaxy') galaxyView.update(); else if (playerSystem(world) != null) systemView.update(); + + uiClock += dt; + if (uiClock > 0.25) { uiClock = 0; panel.update(); } // 4 Hz DOM refresh + + if (!banner && (world.player.won || !world.player.alive)) { + banner = document.createElement('div'); + banner.className = 'banner'; + banner.textContent = world.player.won ? 'VICTORY' : 'SHIP LOST'; + document.getElementById('stage').appendChild(banner); + } }); diff --git a/src/render/systemView.js b/src/render/systemView.js index 532a3fe..68b7f62 100644 --- a/src/render/systemView.js +++ b/src/render/systemView.js @@ -38,7 +38,9 @@ export class SystemView { const there = galaxy.systems[otherEnd(lane, systemId)]; const ang = Math.atan2(there.y - here.y, there.x - here.x); const c = this.center(); - return { x: c.x + Math.cos(ang) * GATE_RADIUS, y: c.y + Math.sin(ang) * GATE_RADIUS, ang }; + // Keep gates on-screen on small canvases (review: fixed 290 clipped hit targets). + const r = Math.min(GATE_RADIUS, Math.min(c.x, c.y) - 40); + return { x: c.x + Math.cos(ang) * r, y: c.y + Math.sin(ang) * r, ang }; } build(systemId) { diff --git a/src/ui/panel.js b/src/ui/panel.js new file mode 100644 index 0000000..60c1b0e --- /dev/null +++ b/src/ui/panel.js @@ -0,0 +1,152 @@ +import { GOODS } from '../sim/constants.js'; +import { goTo, buyGood, sellGood, playerSystem, playerMarket, cargoUsed } from '../sim/player.js'; +import { playerKnownPrices } from '../sim/knowledge.js'; +import { otherEnd } from '../sim/galaxy.js'; + +export class Panel { + constructor(el, world, controls) { + this.el = el; + this.world = world; + this.controls = controls; // { getSpeed, setSpeed, getView, setView } + this.selectedSystem = null; + el.innerHTML = ` +
| good | stock | price |
|---|