Files
Space-Game/archive/legacy-static/js/pages/roadmap.js
francy51 316a44661b Restructure into pnpm monorepo with game shell, docs, and SpacetimeDB backend
- Restructure flat static prototype into pnpm workspace monorepo
- apps/game: playable shell with R3F 3D scene, HUD, SpacetimeDB connection
- apps/docs: design docs and prototypes
- apps/site: landing page
- packages/ui: shared Button and Panel primitives
- services/spacetimedb: backend module (9 tables, 11 reducers)
- Archive legacy static files to archive/legacy-static/
- Game loop: connect, undock, target, approach, dock, mine, sell
- Add pnpm-workspace.yaml, tsconfig.base.json, spacetime.json
2026-05-31 17:56:56 -04:00

304 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
window.GDD = window.GDD || {};
function RoadmapPage() {
const eras = [
{
id: 'solo',
title: 'Era 1 — Single-Player Proof of Concept',
subtitle: 'Validate core loops locally. SpacetimeDB runs on the local machine from Phase 0 — there is no localStorage. One browser window, one player, one simulated galaxy.',
accent: 'var(--accent)',
phases: [
{
num: '0',
title: 'Local Skeleton',
goal: 'Vite app with local SpacetimeDB instance, game state manager, tick loop, and a single rendered star system.',
doneWhen: 'App boots, connects to local SpacetimeDB instance. Shows a star system with a station and 3 asteroids. Game state updates on a local tick (60fps render, 1Hz sim tick). All persistence through SpacetimeDB — no localStorage.',
status: 'current',
},
{
num: '1',
title: 'Movement & Commands',
goal: 'Click-to-move autopilot with local path resolution. Ship accelerates, cruises, decelerates.',
doneWhen: 'Click an asteroid or station. Ship plots a course and moves there with smooth interpolation. ETA display updates. Warp-to for distant objects works.',
status: 'upcoming',
},
{
num: '2',
title: 'Mining & Inventory',
goal: 'Asteroid mining cycle, ore extraction, cargo hold, and jettison.',
doneWhen: 'Approach asteroid, start mining. Mining cycle shows progress. Ore appears in cargo. Cargo full warning. Can jettison into a can.',
status: 'upcoming',
},
{
num: '3',
title: 'Combat — FTL Power Allocation',
goal: 'Auto-engage combat with reactor power management between weapons / shields / engines.',
doneWhen: 'Target a hostile NPC. Ship auto-engages. Player shifts reactor power between 3 subsystems (FTL-style). Power allocation visibly changes combat outcome. Ship can be destroyed.',
status: 'upcoming',
},
{
num: '4',
title: 'Ship Fitting',
goal: 'CPU / Power Grid slot system. High / Med / Low racks with modules that change ship behavior.',
doneWhen: 'Dock at station. Open fitting screen. Equip weapons in high slots, shield booster in mid, cargo expander in low. Fitting affects combat and mining stats. Invalid fits rejected (insufficient CPU/PG). AI module slot type added to fitting schema.',
status: 'upcoming',
},
{
num: '5',
title: 'Refining & Manufacturing',
goal: 'Refine ore into minerals at a station. Use minerals to manufacture modules and ammo.',
doneWhen: 'Dock with ore. Refine at station facility (with yield efficiency). Minerals stored locally. Open manufacturing tab, select a blueprint, queue a job. Job completes after sim time. Product appears in hangar.',
status: 'upcoming',
},
{
num: '6',
title: 'NPC Economy Sim',
goal: 'Simulated NPC market with supply/demand. Prices react to player trades. Regional price differences.',
doneWhen: 'Sell ore at a station. Price adjusts (supply increases, price drops). Fly to another system, price is different. Buy low / sell high works. Market history table shows price movement.',
status: 'upcoming',
},
{
num: '7',
title: 'Single-Player Polish',
goal: 'Complete HUD, notifications, empty states, tutorial hints, and save/load.',
doneWhen: 'Full game loop is playable solo: mine → refine → manufacture → fit → fight → trade. HUD shows all relevant info. SpacetimeDB persists all state (ships, inventory, market, skills) — no localStorage. No dead-end states. Tier 0 Zora: status readouts, basic shield warnings, bare-bones soul state vector in SpacetimeDB. Lightweight exploration events spawn in visited systems.',
status: 'future',
},
],
},
{
id: 'multi',
title: 'Era 2 — Multiplayer Environment',
subtitle: 'Promote local SpacetimeDB to a shared server. Add multiplayer networking, social systems, and the full living galaxy simulation. Multiple players, one persistent world.',
accent: 'var(--cyan)',
phases: [
{
num: '8',
title: 'SpacetimeDB Skeleton',
goal: 'Replace local game state with SpacetimeDB tables and reducers. Client subscribes to state.',
doneWhen: 'Two browser windows connect to the same SpacetimeDB instance. Both see the same star system state. One client issues a move command, the other sees it. Connection status indicator works.',
status: 'future',
},
{
num: '9',
title: 'Presence & Movement Sync',
goal: 'Players see each other in real time. Movement is server-authoritative with client-side interpolation.',
doneWhen: 'Two players in the same system. Each sees the other\'s ship. Click-to-move sends reducer, server validates, all clients interpolate movement. No desync under normal latency.',
status: 'future',
},
{
num: '10',
title: 'Shared Economy',
goal: 'Player-to-player market. Buy/sell orders, contracts, and real price discovery.',
doneWhen: 'Player A places a sell order. Player B sees it in the market table and buys. ISK and items transfer atomically. Order book shows depth. Market history is shared.',
status: 'future',
},
{
num: '11',
title: 'Social — Chat & Bounty',
goal: 'Local chat (system-range), delayed PMs, and player-posted bounty system.',
doneWhen: 'Players in the same system see local chat in real time. PMs arrive with configurable delay (light-speed). Any player can post a bounty on another. Bounty board is visible galaxy-wide.',
status: 'future',
},
{
num: '12',
title: 'Living Galaxy — World Agents + Ship AI Tier 1',
goal: 'Background agent scheduler (BitCraft model). NPC trade convoys, faction skirmishes, anomaly spawns, migration routes. Ship AI promoted to LLM-assisted dialogue.',
doneWhen: 'Server spawns world events without player input. Events appear in the galaxy story log. Faction borders shift over time. Anomalies appear and expire. A returning player sees the galaxy has changed. Tier 1 Zora: soul.md as real system prompt, LLM-generated dialogue, comms module enables natural language responses.',
status: 'future',
},
{
num: '13',
title: 'Multiplayer Combat',
goal: 'PvP combat with FTL power allocation. Multiple ships in an engagement. Target calling, range bands.',
doneWhen: 'Two players engage each other. Both manage power allocation. Server resolves combat ticks authoritatively. Both clients see damage applied. Loser\'s ship drops loot (or wreck). Kill log records the event.',
status: 'future',
},
{
num: '14',
title: 'Corporations & Territory',
goal: 'Player corps, structure anchoring, system sovereignty claims.',
doneWhen: 'Players form a corp. Corp can anchor a structure in a system. Structure provides bonuses (refining yield, market tax). Sovereignty map shows corp-held systems. Rival corps can contest.',
status: 'future',
},
{
num: '15',
title: 'Full MVP — Launch Candidate',
goal: 'Polish pass on all systems. Error handling, reconnection, scaling tests, and onboarding flow.',
doneWhen: 'Fresh player can create account, complete a guided tutorial, mine their first ore, fit their first ship, survive a PvE encounter, make their first trade, and join a corp — all without hitting a dead-end or a crash. Server handles 50 concurrent players.',
status: 'future',
},
],
},
];
function PhaseItem({ phase, isLast }) {
const statusStyle = phase.status === 'current'
? { background: 'var(--accent-bg)', color: 'var(--accent)', border: '1px solid var(--accent-border)' }
: phase.status === 'upcoming'
? { background: 'var(--cyan-bg)', color: 'var(--cyan)', border: '1px solid rgba(34,211,238,0.25)' }
: { background: 'var(--surface-raised)', color: 'var(--muted)', border: '1px solid var(--border)' };
return (
<div className="phase-item">
<div className="phase-marker">
<div
className={`phase-dot${phase.status === 'future' ? ' future' : ''}`}
style={phase.status === 'current' ? {
background: 'var(--accent)',
boxShadow: '0 0 12px rgba(240,160,48,0.4)',
animation: 'pulse 2s infinite',
} : {}}
/>
{!isLast && <div className="phase-line" />}
</div>
<div className="phase-content">
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--sp-3)', marginBottom: 'var(--sp-2)' }}>
<span style={{
fontFamily: 'var(--font-mono)',
fontSize: '0.7rem',
padding: '2px 8px',
borderRadius: 'var(--radius-pill)',
...statusStyle,
}}>
PHASE {phase.num}
</span>
<h4 style={{ margin: 0 }}>{phase.title}</h4>
{phase.status === 'current' && <span className="pill pill-amber">IN PROGRESS</span>}
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>{phase.goal}</p>
<div style={{ background: 'var(--surface-raised)', borderRadius: 'var(--radius-md)', padding: 'var(--sp-2) var(--sp-3)', fontSize: '0.8rem' }}>
<span style={{ color: 'var(--muted)', fontFamily: 'var(--font-mono)', fontSize: '0.7rem' }}>DONE WHEN: </span>
<span style={{ color: 'var(--fg-dim)' }}>{phase.doneWhen}</span>
</div>
</div>
</div>
);
}
return (
<div className="content-inner">
<h1 style={{ marginBottom: '8px' }}>Development Roadmap</h1>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.95rem', maxWidth: '720px' }}>
Two eras, sixteen phases. <strong style={{ color: 'var(--fg)' }}>Era 1</strong> proves the game is fun as a single-player simulation with a local
SpacetimeDB instance the same persistence architecture as multiplayer, just one player. <strong style={{ color: 'var(--fg)' }}>Era 2</strong> promotes
that local SpacetimeDB to a shared server and adds social systems, the living galaxy, and multiplayer combat.
Each phase has a verifiable done-when condition. Integration gates between phase groups ensure every system works together before advancing.
</p>
<div className="callout callout-info" style={{ marginTop: 'var(--sp-4)', maxWidth: '720px' }}>
<strong>Why single-player first with local SpacetimeDB?</strong> Networking is the biggest source of bugs and complexity.
By validating that mining, combat, fitting, and the economy are fun locally using the <em>same</em> SpacetimeDB
persistence that will serve multiplayer we de-risk the entire project. There is no localStorage; SpacetimeDB is the
persistence layer from day 1. When Era 2 begins, the question is only &quot;how do we share this server?&quot; not
&quot;is this game fun?&quot; or &quot;will the persistence migration work?&quot;
</div>
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">IG</span>
<h2 style={{ margin: 0 }}>Integration Gates</h2>
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.95rem', maxWidth: '720px', marginBottom: 'var(--sp-4)' }}>
Between phase groups, an integration gate ensures all systems work together before new ones are added.
A gate is a focused playtest that exercises every previously-built feature end-to-end.
</p>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Gate 1 Core Loop (after Phase 2)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Navigate to asteroid mine fill cargo dock sell ore. The complete economic loop runs in a single session
without errors. SpacetimeDB persists the session closing the browser and reopening restores state.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>Phases covered: 0, 1, 2</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>Gate 2 Combat + Fitting (after Phase 4)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Fit a ship at station undock encounter NPC pirate manage power allocation destroy or be destroyed
insurance payout (if destroyed) refit at station. Combat and fitting form a closed loop with economic consequences.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>Phases covered: 04</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>Gate 3 Full Economy (after Phase 6)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Mine ore refine manufacture a module fit it use it in combat sell excess minerals across systems at different prices.
The complete production chain and NPC market work as an integrated system. Price differences between stations are discoverable.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>Phases covered: 06</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Gate 4 Era 1 Complete (after Phase 7)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Full solo game loop with all systems integrated: mine refine manufacture fit fight trade repeat.
HUD, notifications, Zora Tier 0, exploration events, and missions all work without dead ends. A new player
can learn the game in one session. SpacetimeDB state survives restart.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>Phases covered: 07 (all Era 1)</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--purple)' }}>
<h4 style={{ color: 'var(--purple)' }}>Gate 5 Multiplayer Core (after Phase 10)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Two players in the same galaxy. Both see each other. Both can trade on the shared market. ISK and items
transfer atomically. Movement is synced. Connection loss and reconnection work. No desync under normal latency.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>Phases covered: 810</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--fg-dim)' }}>
<h4 style={{ color: 'var(--fg-dim)' }}>Gate 6 Launch Ready (after Phase 15)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Fresh player can: create account, complete tutorial, mine ore, fit ship, survive PvE, make a trade, join a corp,
participate in a world event all without crashes or dead ends. Server handles 50 concurrent. Full game loop validated.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>Phases covered: 015 (all)</div>
</div>
</div>
<div style={{ marginTop: 'var(--sp-8)' }}>
{eras.map((era, ei) => (
<div key={era.id} style={{ marginBottom: ei < eras.length - 1 ? 'var(--sp-8)' : 0 }}>
{/* Era header */}
<div style={{
display: 'flex',
alignItems: 'center',
gap: 'var(--sp-4)',
marginBottom: 'var(--sp-5)',
paddingBottom: 'var(--sp-3)',
borderBottom: `2px solid ${era.accent}`,
}}>
<span style={{
fontFamily: 'var(--font-mono)',
fontSize: '0.7rem',
fontWeight: 700,
letterSpacing: '0.08em',
padding: '4px 12px',
borderRadius: 'var(--radius-pill)',
background: era.accent === 'var(--accent)' ? 'var(--accent-bg)' : 'var(--cyan-bg)',
color: era.accent,
border: `1px solid ${era.accent === 'var(--accent)' ? 'var(--accent-border)' : 'rgba(34,211,238,0.3)'}`,
}}>
ERA {ei + 1}
</span>
<div>
<h2 style={{ margin: 0, fontSize: '1.1rem' }}>{era.title}</h2>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.82rem', margin: '4px 0 0 0' }}>{era.subtitle}</p>
</div>
</div>
{/* Phase list */}
{era.phases.map((phase, pi) => (
<PhaseItem
key={phase.num}
phase={phase}
isLast={pi === era.phases.length - 1}
/>
))}
</div>
))}
</div>
</div>
);
}
window.GDD.RoadmapPage = RoadmapPage;