Files
Space-Game/archive/legacy-static/js/pages/gameplay.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

1431 lines
109 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 GameplayPage() {
const [activeTab, setActiveTab] = React.useState('loop');
return (
<div className="content-inner">
<h1 style={{ marginBottom: '8px' }}>MVP Gameplay Loop</h1>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.95rem', maxWidth: '680px' }}>
The core loop: connect spawn navigate mine inventory station <strong style={{ color: 'var(--accent)' }}>sell on the market</strong> chat.
Steps 17 work in Era 1 (single-player). Step 8 (Chat) requires multiplayer and ships in Era 2 (Phase 11).
Each step is UI-driven. The player's main interface is tables, charts, and panels — not a cockpit.
</p>
<div style={{ display: 'flex', gap: 'var(--sp-2)', marginBottom: 'var(--sp-6)', flexWrap: 'wrap' }}>
{[
{ id: 'loop', label: 'Core Loop' },
{ id: 'security', label: 'Security Levels' },
{ id: 'pirates', label: 'NPC Pirates' },
{ id: 'concord', label: 'CONCORD' },
{ id: 'insurance', label: 'Insurance' },
{ id: 'missions', label: 'Missions' },
{ id: 'travel', label: '🚀 Travel & Warp' },
{ id: 'events', label: 'World Events UX' },
{ id: 'balancer', label: ' Balancing Agent' },
].map(t => (
<button key={t.id} className={`btn btn-sm${activeTab === t.id ? ' btn-primary' : ''}`}
onClick={() => setActiveTab(t.id)}>{t.label}</button>
))}
</div>
{activeTab === 'loop' && (<>
{/* Loop steps */}
<div style={{ marginTop: 'var(--sp-6)' }}>
{[
{ step: 1, title: 'Connect', desc: 'Player opens the app and connects to SpacetimeDB. Identity is established, player row loaded.', color: 'var(--cyan)' },
{ step: 2, title: 'Spawn', desc: 'A player row and ship row exist; the ship appears in the shared star system for all subscribers.', color: 'var(--cyan)' },
{ step: 3, title: 'Navigate (click & autopilot)', desc: 'Player clicks a point of interest asteroid, station, hostile and the ship automatically pilots there. No manual flight control at all. The player sets <em>intent</em>, the ship executes.', color: 'var(--green)' },
{ step: 4, title: 'Mine (activate & wait)', desc: 'Player clicks an asteroid to approach, then activates mining modules. The ship navigates on its own. Mining runs on a timer. The decision is <em>what</em> to mine and <em>when</em>, not <em>how</em>.', color: 'var(--accent)' },
{ step: 5, title: 'Inventory', desc: 'Ore appears in inventory panel. Quantity, type, and value visible. Player manages cargo space.', color: 'var(--purple)' },
{ step: 6, title: 'Dock at Station', desc: 'Player docks at a station. Station UI becomes available: sell, market, refit.', color: 'var(--cyan)' },
{ step: 7, title: 'Sell Ore', desc: 'Player sells ore through station UI for ISK (in-game currency). Simple fixed pricing for MVP; market orders later.', color: 'var(--accent)' },
{ step: 8, title: 'Chat', desc: 'Players send and receive messages in current system. Basic rate limiting and length validation. Requires multiplayer — ships in Phase 11 (Era 2).', color: 'var(--green)' },
].map((s, i) => (
<div key={i} className="phase-item">
<div className="phase-marker">
<div className="phase-dot" style={{ background: s.color, boxShadow: `0 0 8px ${s.color}40` }} />
{i < 7 && <div className="phase-line" />}
</div>
<div className="phase-content">
<div style={{ display: 'flex', alignItems: 'baseline', gap: 'var(--sp-3)', marginBottom: 'var(--sp-1)' }}>
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.7rem', color: s.color }}>STEP {s.step}</span>
<h4 style={{ margin: 0 }}>{s.title}</h4>
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>{s.desc}</p>
</div>
</div>
))}
</div>
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">GP-UI</span>
<h2 style={{ margin: 0 }}>Screen Specifications</h2>
</div>
<div className="grid-2">
{[
{ name: 'Login / Connect', color: 'var(--cyan)', items: ['Display current SpacetimeDB identity', 'Show connection status indicator', 'Enter/choose display name'] },
{ name: '3D Star Map', color: 'var(--accent)', items: ['Render ships, asteroids, station', 'Click-to-move navigation', 'Entity selection and info panel', 'Camera orbit/zoom controls'] },
{ name: 'Ship Status', color: 'var(--green)', items: ['Ship name, class, owner', 'Current status (idle/mining/warping)', 'Cargo capacity used/total', 'Active action timer'] },
{ name: 'Inventory', color: 'var(--purple)', items: ['Item type + quantity grid', 'Sell button (when docked)', 'Cargo capacity bar', 'Item value estimation'] },
{ name: 'Station Panel', color: 'var(--cyan)', items: ['Dock/undock controls', 'Quick-sell ore for ISK', 'View station market orders', 'Fit ship (when docked)'] },
{ name: 'Market', color: 'var(--accent)', items: ['Order book (buy/sell)', 'Price per unit + quantity', 'Place sell order from inventory', 'Station-filtered view'] },
{ name: 'Chat', color: 'var(--green)', items: ['Local/system channel (instant)', 'Private messages (delayed by range)', 'Sender name + timestamp', 'Auto-scroll to latest'] },
{ name: 'Combat HUD', color: 'var(--red)', items: ['Target selection + lock timer', 'Module activation buttons', 'Capacitor / shield / armor bars', 'Combat log / damage notifications'] },
{ name: 'Bounty Board', color: 'var(--accent)', items: ['Active bounties by tier', 'Place bounty on player', 'Kill feed (galaxy-wide)', 'Your bounty status'] },
{ name: 'Debug Panel', color: 'var(--muted)', items: ['Reducer call log with timestamps', 'Error display with stack trace', 'Connection metrics (latency, subscription count)', 'Entity count / state dump', 'SpacetimeDB table row counts', 'Agent tick scheduler status', 'Force-spawn NPC/entity controls (dev mode)', 'Game time display (tick counter, sim time)'] },
{ name: 'Galaxy Map', color: 'var(--accent)', items: ['Region/constellation/system hierarchy', 'Faction territory overlay', 'Active world events (icons)', 'Fauna migration routes', 'Anomaly locations and timers', 'Story log access'] },
{ name: 'World Event Panel', color: 'var(--green)', items: ['Active events in current region', 'Event countdown timers', 'Participation status', 'Story log for this event', 'Sensor alerts for nearby events'] },
].map((screen, i) => (
<div key={i} className="card">
<h4 style={{ color: screen.color, marginBottom: 'var(--sp-3)' }}>{screen.name}</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.82rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
{screen.items.map((item, j) => <li key={j}>{item}</li>)}
</ul>
</div>
))}
</div>
{/* Combat Model */}
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">GP-COMBAT</span>
<h2 style={{ margin: 0 }}>Combat Model</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Combat style FTL power management, not action.</strong> The player never directly controls the ship's movement or aiming.
Instead, the player clicks a hostile target and the ship <strong>automatically navigates to engagement range and engages</strong>.
The player's job is <strong>resource allocation</strong> — inspired by FTL: distribute reactor power between weapons, shields, engines,
and auxiliary systems. Modules auto-cycle when powered. The skill is in <strong>when to reroute power</strong> (e.g. divert from engines
to shields during a spike), <strong>which subsystem to target</strong> on the enemy, and <strong>fitting choices made before the fight</strong>.
Combat exists to create economic consequences (ship loss, loot, insurance), not to be a reflex game.
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-5)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>PvE Content</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>NPC pirates spawn in belts and at gates</li>
<li>Difficulty scales with system security level</li>
<li>High-sec: weak frigates, easy kills</li>
<li>Low-sec/null: cruiser+ NPCs, dangerous but rewarding</li>
<li>Bounty payouts + module drops as loot</li>
<li>NPC ratting is a primary ISK faucet</li>
</ul>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Player Pirating</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Players can attack other players in low-sec and null-sec</li>
<li>High-sec attacks trigger CONCORD response (ship destruction)</li>
<li>Pirates can loot cargo from destroyed ships</li>
<li>Piracy lowers security status → eventually locked out of high-sec</li>
<li>Bounty system creates natural consequences</li>
<li>Creates risk/reward dynamics for haulers and miners</li>
</ul>
</div>
</div>
<div className="card card-accent">
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Combat Flow (FTL-style resource management)</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
1. <span style={{ color: 'var(--cyan)' }}>Click hostile target</span> — ship auto-navigates to engagement range, no manual piloting<br/>
2. <span style={{ color: 'var(--accent)' }}>Auto-lock & engage</span> — targeting computer locks on, weapons begin auto-cycling<br/>
3. <span style={{ color: 'var(--red)' }}>Manage reactor power</span> — FTL-style: drag power between Weapons / Shields / Engines / Aux<br/>
4. <span style={{ color: 'var(--green)' }}>Pick subsystem to attack</span> — target enemy shields, weapons, engines, or hull directly<br/>
5. <span style={{ color: 'var(--purple)' }}>React to damage</span> — reroute power mid-fight: divert engines→shields when taking fire, shields→weapons to finish<br/>
6. <span style={{ color: 'var(--fg)' }}>Monitor capacitor</span> — all systems drain energy; running dry = modules go offline<br/>
7. <span style={{ color: 'var(--red)' }}>Destroy or flee</span> — hull reaches 0 → ship explodes, loot drops, economic consequences
</div>
<div style={{ marginTop: 'var(--sp-3)', padding: 'var(--sp-3)', background: 'rgba(34,211,238,0.06)', borderRadius: '6px', borderLeft: '3px solid var(--cyan)' }}>
<span style={{ fontSize: '0.8rem', color: 'var(--cyan)', fontWeight: 600 }}>FTL DNA:</span>
<span style={{ fontSize: '0.8rem', color: 'var(--fg-dim)' }}> The player's only input during combat is power allocation and subsystem targeting.
The ship flies itself. Weapons fire themselves. The player is the <em>chief engineer</em> deciding where
the reactor output goes not the pilot or gunner.</span>
</div>
<div className="section-header" style={{ marginTop: 'var(--sp-6)' }}>
<span className="section-num">GP-FAIL</span>
<h2 style={{ margin: 0 }}>Power Allocation Failure Modes</h2>
</div>
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>No free lunch.</strong> Every subsystem needs energy. When power is diverted away from a subsystem,
it doesn't merely weaken — it fails. The reactor produces a fixed total output, and every allocation decision
creates a vulnerability somewhere else. This is the core tension of the combat system.
</div>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Subsystem</th><th>Powered Behavior</th><th>Unpowered Failure Mode</th><th>Reroute Time</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-red">Weapons</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Turrets auto-cycle at full rate. Missile launchers reload. Damage output at fitted value.</td>
<td style={{ color: 'var(--red)' }}><strong>No firing.</strong> Turrets cease fire. Missiles cannot launch. Active weapons go offline after 3s. Cannot deal damage.</td>
<td className="mono">2s to full power</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Shields</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Shield booster recharges shield HP. Passive regen active. Damage absorption at full.</td>
<td style={{ color: 'var(--red)' }}><strong>No recharge.</strong> Shield HP stops regenerating. Existing shield HP decays at 2%/s. After 15s, shields at 0% and all damage hits armor directly.</td>
<td className="mono">3s to full regen</td>
</tr>
<tr>
<td><span className="pill pill-green">Engines</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Ship maintains orbit speed. Can close distance or withdraw. Evasion against tracking.</td>
<td style={{ color: 'var(--red)' }}><strong>Ship immobile.</strong> Speed drops to 0. No orbit, no approach, no withdrawal. Sitting duck — all hostile weapons have perfect tracking. Cannot flee.</td>
<td className="mono">2s to full speed</td>
</tr>
<tr>
<td><span className="pill pill-purple">Auxiliary</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Active modules cycle normally: ECM jamming, sensor boosters, cap boosters, warfare links.</td>
<td style={{ color: 'var(--red)' }}><strong>No special abilities.</strong> All auxiliary modules go offline. ECM stops jamming. Sensors degrade. No capacitor boosting. Warfare links disconnect.</td>
<td className="mono">1.5s to full power</td>
</tr>
</tbody>
</table>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>&#x26A0; Red Alert Mode</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
When the ship takes shield-piercing damage (shields below 25% and taking armor hits), the HUD
automatically enters <strong style={{ color: 'var(--red)' }}>Red Alert</strong>. Non-essential HUD elements collapse:
chat minimizes, commodity ticker hides, overview panel shrinks to hostiles-only. The combat HUD
expands to fill the viewport: shield/armor/hull bars enlarge, power allocation bars gain prominent markers,
and a pulsing red border frames the screen. Audio alarm plays once.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>
Trigger: shields &lt; 25% AND armor damage incoming · Clears: shields restored above 50% · Cannot be disabled
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Reroute Timing</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Power rerouting is not instant — there's a 1.53 second spool-up depending on the subsystem.
This prevents instant reactive ping-pong between subsystems. The player must <em>commit</em>
to a power distribution and accept the vulnerability window. Rerouting to shields (3s) is deliberately
slower than rerouting to weapons (2s), creating a "damage race vs. survival" decision.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--muted)' }}>
During spool-up: subsystem operates at 50% capacity · Visual: power bar fills with animation · Audio: reactor hum changes pitch
</div>
</div>
</div>
</div>
<div className="callout callout-danger" style={{ marginTop: 'var(--sp-5)' }}>
<strong>On death:</strong> Ship destroyed, cargo and modules partially lost (50% drop as loot, 50% destroyed).
Player respawns at home station in a rookie frigate. Ship insurance partially reimburses the loss.
See Ships & Fitting DEATH (Ship Destruction) for full details.
</div>
{/* ═══ DYNAMIC WORLD ═══ */}
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">GP-WORLD</span>
<h2 style={{ margin: 0 }}>Dynamic Galaxy Living World Events</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>The galaxy is alive.</strong> The server simulates a single persistent galaxy with hundreds of star systems, planets,
moons, asteroid belts, space stations, wormholes, and anomalies. On top of this static geography runs a <strong>world simulation layer</strong>
that spawns PvE events dynamically, creating a unique story for every server. No two servers ever tell the same tale.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Galaxy Structure</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Single Galaxy, Many Systems</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
All players share one galaxy. The galaxy contains multiple regions, each with dozens of star systems.
Systems are connected by stargates forming a navigable graph. Travel between systems takes time,
creating <strong style={{ color: 'var(--fg)' }}>geographic identity</strong> the frontier feels different from the core.
</p>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Orbiting Objects & Celestials</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Each system has a star, planets, moons, asteroid belts, and stations. Planets orbit their star on slow cycles;
moons orbit planets. This is not cosmetic <strong style={{ color: 'var(--fg)' }}>orbital positions affect mining yields,
travel times, and line-of-sight for sensors</strong>. The galaxy has real spatial rhythm.
</p>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>World Event Categories</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}> Faction Conflicts</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
NPC factions have territory, resources, and grievances. The server tracks faction relations as a dynamic matrix.
When tensions cross a threshold, war erupts faction fleets clash in contested systems, trade routes are disrupted,
and players can choose sides or profit from the chaos.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Triggers: resource disputes, border skirmishes, diplomatic insults, player provocations
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--purple)' }}>
<h4 style={{ color: 'var(--purple)' }}>🌀 Space Anomalies</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
The galaxy spawns temporary spatial phenomena wormholes, nebulae, radiation storms, gravity wells, dark matter corridors.
Anomalies appear unannounced and expire on their own schedule. Some are dangerous, some are lucrative,
all create <strong style={{ color: 'var(--fg)' }}>time-limited opportunities</strong> that reward the first to arrive.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Lifespan: 15 min 48 hrs · Rarity scales with distance from trade hubs
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>🐋 Space Fauna Migrations</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Massive space creatures migrate across the galaxy on seasonal cycles space whales, crystal leviathans, plasma mantas.
Their migration routes cross player space, creating <strong style={{ color: 'var(--fg)' }}>temporary resource hotspots</strong>
(biological materials, rare isotopes) and navigation hazards. Players can hunt, follow, or study them.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Cycle: real-time weeks · Route shifts each migration · First contact is always a surprise
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>🏛 NPC Faction Events</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
NPC factions expand, contract, build stations, abandon outposts, and respond to player activity.
A mining rush in a remote system might attract NPC traders, who attract pirates, who attract bounty hunters.
The world <strong style={{ color: 'var(--fg)' }}>reacts to player density and economic activity</strong> organically.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Includes: station construction, pirate raids, convoy escorts, refugee crises, tech discoveries
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>🌑 Cosmic Catastrophes</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Rare galaxy-shaking events a star going supernova, a rogue planet entering a system, a dormant wormhole network
activating. These are the <strong style={{ color: 'var(--fg)' }}>server-defining moments</strong> that players talk about for months.
"Were you online when the Rhea star collapsed?" becomes server legend.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Frequency: ~1 per month · Permanently alters the galaxy map · Foreshadowed days in advance
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--fg-dim)' }}>
<h4 style={{ color: 'var(--fg-dim)' }}>📡 Signals & Mysteries</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-2) 0' }}>
Unknown signals appear at random derelict stations, alien artifacts, encrypted transmissions. Players investigate,
decode, and sometimes trigger chain events. Some mysteries are one-shot puzzles; others are the opening move
of a multi-week server arc that the world simulation has been building toward.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Discovery: player-initiated · Can trigger larger events · Some are red herrings
</div>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>How the World Simulation Works</h3>
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>World Simulation Pipeline</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--cyan)' }}>Galaxy State</span> — persistent topology: systems, gates, planets, stations, faction territories<br/>
<span style={{ color: 'var(--accent)' }}>World Tick</span> — every 5 min, server evaluates galaxy state + player density + event cooldowns + faction matrix<br/>
<span style={{ color: 'var(--green)' }}>Event Spawn</span> — world tick may spawn an event: choose type, location, duration, severity based on weighted probabilities<br/>
<span style={{ color: 'var(--purple)' }}>Event Propagation</span> — nearby players get sensor readings; distant players hear through news feed with delay<br/>
<span style={{ color: 'var(--red)' }}>Event Resolution</span> — events end by timer, player action, or cascading trigger; outcomes feed back into galaxy state<br/>
<span style={{ color: 'var(--fg)' }}>Story Log</span> every event is recorded in a server-wide story timeline; players can read "the history of this galaxy"
</div>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-5)' }}>
<div className="card">
<h4 style={{ color: 'var(--accent)' }}>Player-Event Interaction</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Players can <strong style={{ color: 'var(--fg)' }}>participate</strong> in events (fight invaders, escort convoys, study anomalies)</li>
<li>Player actions can <strong style={{ color: 'var(--fg)' }}>escalate or defuse</strong> events (kill the pirate boss raid ends)</li>
<li>Player density <strong style={{ color: 'var(--fg)' }}>attracts</strong> certain events (trade hubs get more NPC activity)</li>
<li>Player inaction lets events <strong style={{ color: 'var(--fg)' }}>escalate naturally</strong> (uncontested raids grow larger)</li>
</ul>
</div>
<div className="card">
<h4 style={{ color: 'var(--cyan)' }}>Server-Uniqueness</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Event seed is randomized per server no two galaxies evolve the same way</li>
<li>Faction relationships drift based on <strong style={{ color: 'var(--fg)' }}>what players actually do</strong></li>
<li>A server where players are peaceful traders develops different world events than a server of pirates</li>
<li>The story timeline is the server's unique identity — "this is the galaxy where the Kaalani wiped out the Vren homeworld"</li>
</ul>
</div>
</div>
<div className="callout callout-warn" style={{ marginTop: 'var(--sp-5)' }}>
<strong>Design intent:</strong> The world simulation ensures that even when players are doing routine economic loops,
<em>something</em> is always happening somewhere. The galaxy never feels static. A player who takes a week off returns
to find the political map has shifted, a new anomaly appeared in their home system, or a migration route has changed.
The world remembers, the world evolves, and the world tells a story that belongs to <strong>this server's</strong> players alone.
</div>
</>)}
{/* ═══ SECURITY LEVELS ═══ */}
{activeTab === 'security' && (<>
<div className="section-header">
<span className="section-num">GP-SEC</span>
<h2 style={{ margin: 0 }}>Security Level System</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Security levels define the rules of engagement.</strong> Every star system has a security status from <strong>+1.0</strong> (safest) to <strong>1.0</strong> (lawless).
Security 0.0 is explicitly <strong>null-sec</strong> there is no border case. Systems at exactly 0.0 have the same rules as systems at 0.4: no CONCORD, no gate guns, full PvP freedom.
This single number controls CONCORD response, NPC pirate spawns, PvE difficulty, and the economic risk/reward balance.
Inspired by EVE Online's sec-space but simplified for the prototype's scope.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Security Bands</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Band</th><th>Sec Range</th><th>PvP Rules</th><th>CONCORD</th><th>NPC Pirates</th><th>Risk / Reward</th></tr>
</thead>
<tbody>
{[
{ band: 'High-Sec', range: '+0.5 → +1.0', color: 'var(--green)', pvp: 'Aggression triggers CONCORD. PvP possible but punished.', concord: 'Immediate (38s). Lethal.', pirates: 'Weak frigates. Easy kills, low bounties.', risk: 'Very low risk. Baseline ore prices. Starter systems.' },
{ band: 'Low-Sec', range: '+0.1 → +0.4', color: 'var(--accent)', pvp: 'No CONCORD. Gate/station guns fire on aggressors. PvP is free.', concord: 'None. Gate/station guns only.', pirates: 'Mixed frigates/destroyers. Moderate bounties.', risk: 'Medium risk. Better ore. Faction missions. Trade route choke points.' },
{ band: 'Null-Sec', range: '0.0 → 0.4', color: 'var(--red)', pvp: 'No rules. No guns. Anything goes.', concord: 'None.', pirates: 'Cruiser+ NPCs. Dangerous. High bounties + rare loot.', risk: 'High risk. Richest ore. Best anomalies. Faction conflict zones.' },
{ band: 'Deep Null', range: '0.5 → 1.0', color: 'var(--purple)', pvp: 'No rules. Wormhole connections only.', concord: 'None.', pirates: 'Battlecruiser/battleship NPCs. Elite loot tables.', risk: 'Extreme risk. Unique resources. Story event spawning grounds.' },
].map((row, i) => (
<tr key={i}>
<td><span style={{ color: row.color, fontWeight: 600 }}>{row.band}</span></td>
<td className="mono">{row.range}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.pvp}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.concord}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.pirates}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.risk}</td>
</tr>
))}
</tbody>
</table>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Player Security Status</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>Gaining Status</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Destroying NPC pirates: +0.01 to +0.05 per kill (scaled by NPC tier)</li>
<li>Completing NPC missions: +0.1 to +0.3 per mission</li>
<li>Passive recovery: +0.01 per hour while clean (no hostile acts)</li>
<li>Maximum: +5.0 ("exemplary citizen")</li>
</ul>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>Losing Status</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Attacking a player in high-sec: 0.5 to 2.0 (scaled by victim's ship value)</li>
<li>Destroying a player ship in high-sec: 2.0 to 5.0</li>
<li>Podding (if applicable): 5.0 (extreme penalty)</li>
<li>Below 2.0: barred from +0.8+ systems (CONCORD patrols eject)</li>
<li>Below 5.0: freely attackable anywhere ("outlaw")</li>
</ul>
</div>
</div>
<div className="section-header">
<span className="section-num">GP-SEC-DB</span>
<h2 style={{ margin: 0 }}>Backend Impact</h2>
</div>
<div className="card card-accent">
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Schema Changes</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--accent)' }}>systems</span> — add column: <code>security_level</code> (Float, range 1.0 to +1.0, immutable at galaxy gen time)<br/>
<span style={{ color: 'var(--cyan)' }}>players</span> — add column: <code>security_status</code> (Float, range 10.0 to +5.0, default 0.0)<br/>
<span style={{ color: 'var(--green)' }}>ships</span> — add column: <code>combat_flag</code> (Enum: none / weaponstimer / suspect / criminal, with timestamp)<br/>
<span style={{ color: 'var(--red)' }}>New reducer:</span> <code>adjust_security_status(player_id, delta, reason)</code> — server-side only, never client-callable
</div>
</div>
</>)}
{/* ═══ NPC PIRATES ═══ */}
{activeTab === 'pirates' && (<>
<div className="section-header">
<span className="section-num">GP-NPC</span>
<h2 style={{ margin: 0 }}>NPC Pirate AI</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>NPC pirates are the primary PvE threat and a core ISK faucet.</strong>
They spawn in asteroid belts, at stargates, and in combat anomalies.
Their difficulty scales with system security level — easy frigates in high-sec,
deadly battleships in deep null. The pirate AI uses simple behavior templates, not machine learning:
deterministic state machines that create <em>believable</em> behavior without unpredictable edge cases.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Spawning Rules</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Spawn Locations</h4>
<table className="data-table">
<thead><tr><th>Location</th><th>Spawn Rate</th><th>Trigger</th></tr></thead>
<tbody>
{[
{ loc: 'Asteroid belt', rate: '13 NPCs per belt', trigger: 'Player enters belt or world tick' },
{ loc: 'Stargate', rate: '02 NPCs per gate', trigger: 'World tick (low-sec/null only)' },
{ loc: 'Combat anomaly', rate: '38 NPCs (waves)', trigger: 'Player warps to anomaly' },
{ loc: 'Mission site', rate: 'Scripted per mission', trigger: 'Player accepts mission' },
].map((r, i) => (
<tr key={i}>
<td style={{ color: 'var(--fg)' }}>{r.loc}</td>
<td className="mono">{r.rate}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{r.trigger}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Spawn Conditions</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li><strong style={{ color: 'var(--fg)' }}>Player proximity:</strong> NPCs spawn when a player enters a belt or anomaly ("pull" model). Gates spawn NPCs on world ticks only in low-sec/null.</li>
<li><strong style={{ color: 'var(--fg)' }}>Density cap:</strong> max 10 active NPCs per system. If at cap, oldest idle NPCs despawn.</li>
<li><strong style={{ color: 'var(--fg)' }}>Despawn:</strong> NPCs that have been idle (no aggro) for 5 minutes despawn. Combat NPCs persist until destroyed or player leaves system for 2+ minutes.</li>
<li><strong style={{ color: 'var(--fg)' }}>No spawn zones:</strong> within 20km of stations (safe zone). On-grid with a station = no NPC spawn.</li>
<li><strong style={{ color: 'var(--fg)' }}>World tick spawns:</strong> every 5 minutes, world tick may add NPCs to low-pop systems to keep them feeling dangerous.</li>
</ul>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Difficulty Tiers by Security Level</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Sec Band</th><th>NPC Classes</th><th>Hull HP</th><th>Bounty</th><th>Behavior</th><th>Loot</th></tr>
</thead>
<tbody>
{[
{ band: 'High-Sec', classes: 'Frigate (T1)', hp: '200400', bounty: '5002,000 ', behavior: 'Passive orbit. Low aggression. Flees below 30% HP.', loot: 'Basic modules, small ammo, common ore' },
{ band: 'Low-Sec', classes: 'Frigate (T2), Destroyer', hp: '400800', bounty: '2,0008,000 ', behavior: 'Aggressive orbit. Target priority (weakest ship). Kites if outgunned.', loot: 'T2 modules, blueprint fragments, mid-tier ore' },
{ band: 'Null-Sec', classes: 'Destroyer, Cruiser', hp: '8002,500', bounty: '8,00030,000 ', behavior: 'Coordinated packs. Spider tanking (remote reps). Escorts target jamming.', loot: 'Rare modules, full blueprints, rare minerals' },
{ band: 'Deep Null', classes: 'Cruiser, Battlecruiser, Boss', hp: '2,50012,000', bounty: '30,000200,000 ', behavior: 'Boss mechanics. Phase triggers. Spawns reinforcements. Requires fleet tactics.', loot: 'Officer modules, ship BPCs, unique cosmetics, story items' },
].map((row, i) => (
<tr key={i}>
<td style={{ fontWeight: 600, color: ['var(--green)', 'var(--accent)', 'var(--red)', 'var(--purple)'][i] }}>{row.band}</td>
<td style={{ color: 'var(--fg-dim)' }}>{row.classes}</td>
<td className="mono">{row.hp}</td>
<td className="mono">{row.bounty}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{row.behavior}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{row.loot}</td>
</tr>
))}
</tbody>
</table>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>AI Behavior Templates</h3>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Deterministic state machines.</strong> Each NPC has a behavior template with a fixed set of states and transitions.
No randomness in decision-making — the "intelligence" comes from the state machine's design, not from randomness.
This makes NPC behavior predictable enough to learn but complex enough to be engaging.
</div>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>NPC State Machine</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--muted)' }}>IDLE</span> → orbit spawn point, passive scan every 2s (aggro_scan agent)<br/>
<span style={{ color: 'var(--accent)' }}>AGGRO</span> → target detected in aggro range (30km default) → transition to COMBAT<br/>
<span style={{ color: 'var(--red)' }}>COMBAT</span> execute behavior template (orbit/approach/kite) fight to death or flee threshold<br/>
<span style={{ color: 'var(--purple)' }}>FLEE</span> → HP below flee threshold (30% default) → MWD away from player, warp out if possible<br/>
<span style={{ color: 'var(--green)' }}>DEAD</span> generate loot, award bounty to damage contributor(s), schedule respawn
</div>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
{[
{
name: 'Orbit Kiter', color: 'var(--cyan)',
desc: 'Maintains optimal range (1520km). Orbits target at speed. Fires long-range weapons. If player closes distance, thrusts away. Good against slow ships.',
stats: 'Preferred range: 1520km · Speed bonus: +20% · Damage: low · Survivability: high',
},
{
name: 'Brawler', color: 'var(--red)',
desc: 'Burns directly toward target. Gets into close range (25km) and applies heavy short-range damage. Vulnerable to kiting but devastating if they close.',
stats: 'Preferred range: 25km · Speed bonus: +10% · Damage: high · Survivability: medium',
},
{
name: 'Shield Tank', color: 'var(--green)',
desc: 'Prioritizes shield regeneration over damage. Self-reps during combat. Slow but durable. Good at outlasting opponents.',
stats: 'Preferred range: 1015km · Shield regen: +50% · Damage: medium · Survivability: very high',
},
{
name: 'Support / EWAR', color: 'var(--purple)',
desc: 'Stays at range, jams target locks, disrupts targeting. Weak in direct combat but makes the pack deadlier. Always spawns with escorts.',
stats: 'Preferred range: 2030km · EWAR strength: medium · Damage: very low · Survivability: low (prioritizes escape)',
},
].map((tmpl, i) => (
<div key={i} className="card" style={{ borderLeft: `3px solid ${tmpl.color}` }}>
<h4 style={{ color: tmpl.color, marginBottom: 'var(--sp-2)' }}>{tmpl.name}</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>{tmpl.desc}</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>{tmpl.stats}</div>
</div>
))}
</div>
<div className="section-header">
<span className="section-num">GP-NPC-DB</span>
<h2 style={{ margin: 0 }}>Backend Impact</h2>
</div>
<div className="card card-accent">
<h4 style={{ marginBottom: 'var(--sp-4)' }}>New Tables & Agents</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--red)' }}>npc_entities</span> — <code>npc_id, system_id, class_id, behavior_template, x/y/z, hull/armor/shield, target_id, state (idle/combat/flee/dead), spawn_location, spawn_time</code><br/>
<span style={{ color: 'var(--accent)' }}>npc_class_templates</span> <code>class_id, name, tier, hull_base, armor_base, shield_base, speed, damage, behavior, loot_table_id, bounty</code><br/>
<span style={{ color: 'var(--green)' }}>loot_tables</span> <code>table_id, entries (item_type, min_qty, max_qty, drop_chance), security_band</code><br/>
<span style={{ color: 'var(--cyan)' }}>New agents:</span> <code>pirate_spawn</code> (conditional, 300s), <code>pirate_combat_tick</code> (fixed, 1s per engaged NPC), <code>pirate_loot_drop</code> (one-shot, 0s on death)
</div>
</div>
</>)}
{/* ═══ CONCORD ═══ */}
{activeTab === 'concord' && (<>
<div className="section-header">
<span className="section-num">GP-CONC</span>
<h2 style={{ margin: 0 }}>CONCORD Law Enforcement</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>CONCORD is the NPC police force in high-security space.</strong>
They do not prevent crime they <em>punish</em> it. High-sec is not safe; it is <em>consequential</em>.
A determined player can destroy a target in high-sec, but they will lose their ship to CONCORD.
This creates a cost equation: is the target's loot worth more than the attacker's ship? Most of the time, it isn't.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Response Model</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>System Sec</th><th>Response Time</th><th>CONCORD Force</th><th>Outcome</th></tr>
</thead>
<tbody>
{[
{ sec: '1.0', time: '3s', force: '2 Battleships + 2 Cruisers', outcome: 'Near-instant destruction. No time for a second volley.' },
{ sec: '0.9', time: '4s', force: '1 Battleship + 2 Cruisers', outcome: 'Quick destruction. Alpha-strike kills possible on soft targets.' },
{ sec: '0.8', time: '5s', force: '1 Battleship + 1 Cruiser', outcome: 'Brief window for damage. Tanky ships can survive one cycle.' },
{ sec: '0.7', time: '8s', force: '2 Cruisers', outcome: 'Meaningful attack window. Destroyers can kill frigates before CONCORD arrives.' },
{ sec: '0.6', time: '12s', force: '1 Cruiser', outcome: 'Extended window. Cruiser can kill a mining barge before response.' },
{ sec: '0.5', time: '15s', force: '2 Frigates', outcome: 'Longest high-sec response. Organized ganks are viable. The cost equation shifts toward attackers.' },
{ sec: '0.4', time: ' (no response)', force: 'None', outcome: 'No CONCORD. Gate/station guns fire in 0.10.4. Nothing in 0.0.' },
].map((row, i) => (
<tr key={i}>
<td className="mono" style={{ fontWeight: 600, color: parseFloat(row.sec) >= 0.5 ? 'var(--green)' : parseFloat(row.sec) >= 0 ? 'var(--accent)' : 'var(--red)' }}>{row.sec}</td>
<td className="mono" style={{ color: 'var(--cyan)' }}>{row.time}</td>
<td style={{ color: 'var(--fg-dim)' }}>{row.force}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.outcome}</td>
</tr>
))}
</tbody>
</table>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>How CONCORD Works</h3>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-6)' }}>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--red)' }}>1. Aggression</span> — Player A attacks Player B in high-sec. <code>combat_flag</code> set to <strong>criminal</strong>.<br/>
<span style={{ color: 'var(--accent)' }}>2. CONCORD Spawn</span> — Server spawns CONCORD NPCs at attacker's location. Response time based on system sec.<br/>
<span style={{ color: 'var(--cyan)' }}>3. Point + Web</span> — CONCORD warp-scrambles and stasis-webs the attacker. No escape.<br/>
<span style={{ color: 'var(--red)' }}>4. Destruction</span> — CONCORD applies overwhelming damage. Ship destroyed. No survival possible.<br/>
<span style={{ color: 'var(--green)' }}>5. Status Penalty</span> — Attacker's security status reduced. Large bounty placed automatically at low status.<br/>
<span style={{ color: 'var(--purple)' }}>6. Kill Log</span> Event logged in kill feed. Galaxy-wide notification if bounty collected.
</div>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>Anti-Exploit Rules</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>CONCORD cannot be avoided, tanked, or outrun response is guaranteed</li>
<li>Warping away delays CONCORD by 2s; they follow and catch up</li>
<li>CONCORD damage scales to always exceed attacker's EHP (no tanking)</li>
<li>If a player exploits a bug to avoid CONCORD, server admin can force-destroy the ship</li>
<li>"Criminal" flag persists through session — logging out doesn't clear it</li>
</ul>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Suspect vs. Criminal</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li><strong style={{ color: 'var(--accent)' }}>Suspect</strong> stole loot from another player's wreck. Anyone can attack the suspect freely. No CONCORD response for attacking a suspect. 15-minute timer.</li>
<li><strong style={{ color: 'var(--red)' }}>Criminal</strong> attacked an innocent in high-sec. CONCORD responds. Ship will be destroyed. Security status penalty applied. 15-minute weapons timer.</li>
<li><strong style={{ color: 'var(--green)' }}>Weapons Timer</strong> 60s after any aggressive module activation. Cannot dock, gate-jump, or tether during timer. Prevents "dock games."</li>
</ul>
</div>
</div>
<div className="callout callout-warn">
<strong>Design intent:</strong> High-sec is not safe it is <em>punitively expensive</em> to attack there.
The gank-vs-cost equation is the core balance lever. If ganking becomes too easy, reduce CONCORD response time.
If high-sec is too safe, increase response time slightly or reduce CONCORD force.
The goal is a living ecosystem where piracy exists but is a <em>strategic choice</em>, not a griefing tool.
</div>
</>)}
{/* ═══ INSURANCE ═══ */}
{activeTab === 'insurance' && (<>
<div className="section-header">
<span className="section-num">GP-INS</span>
<h2 style={{ margin: 0 }}>Ship Insurance</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Insurance is both an ISK sink (premiums) and an ISK faucet (payouts).</strong>
It cushions ship loss without eliminating risk. A fully insured ship still costs the player money to replace
modules, cargo, and the deductible are never covered. Insurance makes the game playable for casual players
while keeping ship loss meaningful for everyone.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Coverage Tiers</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Tier</th><th>Premium</th><th>Payout (% of hull value)</th><th>Duration</th><th>Best For</th></tr>
</thead>
<tbody>
{[
{ tier: 'None', premium: 'Free', payout: '0%', duration: '—', best: 'Rookie frigates (free replacement anyway)' },
{ tier: 'Basic', premium: '10% of hull value', payout: '40%', duration: '30 days', best: "Cheap ships you don't care about" },
{ tier: 'Standard', premium: '25% of hull value', payout: '70%', duration: '30 days', best: 'Default choice for most ships' },
{ tier: 'Platinum', premium: '50% of hull value', payout: '95%', duration: '30 days', best: 'Expensive ships, PvP main combat ships, mission runners' },
].map((row, i) => (
<tr key={i}>
<td style={{ fontWeight: 600, color: ['var(--muted)', 'var(--green)', 'var(--cyan)', 'var(--accent)'][i] }}>{row.tier}</td>
<td className="mono">{row.premium}</td>
<td className="mono" style={{ fontWeight: 600 }}>{row.payout}</td>
<td className="mono">{row.duration}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.best}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>How Insurance Works</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
1. Player buys insurance at station (deducted from wallet)<br/>
2. Policy active for 30 days or until ship is destroyed<br/>
3. On ship destruction, payout queued (30120s delay via insurance_payout agent)<br/>
4. Payout = hull value × coverage %<br/>
5. ISK deposited to player wallet<br/>
6. Policy consumed must re-insure new ship
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>What Insurance Does NOT Cover</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Fitted modules (separate loss 50% destroyed, 50% loot)</li>
<li>Cargo (destroyed or looted separate system)</li>
<li>AI crew injuries / medical costs</li>
<li>Rig slots (permanently destroyed on ship loss)</li>
<li>Market value above base hull price</li>
</ul>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Economic Balance</h3>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Insurance as ISK Faucet/Sink</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--red)' }}>ISK sink:</span> Premium payments leave the economy (25% of hull value for Standard = net ISK destruction)<br/>
<span style={{ color: 'var(--green)' }}>ISK faucet:</span> Payouts inject ISK (70% of hull value for Standard = net ISK creation when ship is destroyed)<br/>
<span style={{ color: 'var(--accent)' }}>Net effect:</span> At Standard tier, each ship loss creates ISK equal to 70% 25% = 45% of hull value. This is the "cost of dying" net ISK injection. Balanced by: ship replacement costs (minerals), module losses, and the fact that hulls are manufactured from player-mined resources (zero ISK creation).<br/>
<span style={{ color: 'var(--cyan)' }}>Delay purpose:</span> The 30120s payout delay prevents instant-rebuy combat loops. You can't die, collect insurance, and re-ship in the same fight.
</div>
</div>
<div className="callout callout-danger">
<strong>Anti-abuse:</strong> Self-destructing a ship for insurance payout is an exploit.
Insurance payout never exceeds premium paid unless the ship was destroyed by a <em>different</em> player.
NPC kills pay full insurance. Self-destruct pays 0%. Alt-character kills are tracked via IP/connection heuristics
and flagged for review (bounty system uses the same check). For MVP: if killer and victim are the same player, payout = 0.
</div>
<div className="section-header" style={{ marginTop: 'var(--sp-6)' }}>
<span className="section-num">GP-INS-DB</span>
<h2 style={{ margin: 0 }}>Backend Impact</h2>
</div>
<div className="card card-accent">
<h4 style={{ marginBottom: 'var(--sp-4)' }}>New Tables & Reducers</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--accent)' }}>insurance_policies</span> <code>policy_id, player_id, ship_id, tier, premium_paid, payout_value, purchased_at, expires_at, active (bool)</code><br/>
<span style={{ color: 'var(--cyan)' }}>ship_type_base_values</span> <code>ship_type_id, base_hull_value (ISK), insurance_premium_mult (per tier)</code><br/>
<span style={{ color: 'var(--green)' }}>New reducer:</span> <code>purchase_insurance(ship_id, tier)</code> validate docked + wallet, deduct premium, create policy<br/>
<span style={{ color: 'var(--red)' }}>New reducer:</span> <code>process_insurance_payout(ship_id)</code> called by insurance_payout agent, validate policy active + ship destroyed, credit ISK
</div>
</div>
</>)}
{/* ═══ MISSIONS ═══ */}
{activeTab === 'missions' && (<>
<div className="section-header">
<span className="section-num">GP-MIS</span>
<h2 style={{ margin: 0 }}>Mission System</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Missions are a core ISK faucet and the primary PvE progression path.</strong>
NPC agents at stations offer missions that send players on scripted encounters kill pirates, haul cargo,
mine specific ores, survey anomalies, or escort convoys. Missions scale with security band, player standing,
and skill level. They are repeatable but not grindable: each agent has a limited pool that refreshes on a timer.
The mission system is the "guided content" that teaches new players the game loop and gives veteran players
a reliable ISK income alongside market trading and PvP.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Mission Types</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}> Kill Mission</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Warp to a deadspace pocket and destroy NPC hostiles. Difficulty scales with agent tier and system security.
May include multiple waves, a boss NPC, or structure destruction. The bread-and-butter of PvE combat.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Reward: Bounty + mission bonus · Time limit: 24h · Standing: +0.05 to +0.30 per completion
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>📦 Courier Mission</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Transport goods from one station to another. Cargo may be large (requiring a hauler) or valuable
(attracting pirates). Low-sec courier missions pay more. Creates organic hauler traffic that pirates can hunt.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Reward: Flat fee + time bonus · Time limit: 16h · Standing: +0.03 to +0.15 · Risk: low-sec route = ambush
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}> Mining Mission</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Mine a specific quantity of a specific ore type and deliver it to the agent. May require traveling to a
mission-only asteroid belt (deadspace). Sometimes includes NPC spawns in the belt for added risk.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Reward: ISK + ore market value · Time limit: 24h · Standing: +0.03 to +0.10 · Dual income: sell excess ore
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>📡 Survey / Exploration Mission</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Travel to a specific system or anomaly and scan/interact with objects. May involve hacking, analyzing,
or simply being at a location for a duration. Low combat risk, high travel time. Good for exploring the galaxy.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Reward: Flat fee + loot from sites · Time limit: 624h · Standing: +0.02 to +0.10 · Unlocks exploration content
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--purple)' }}>
<h4 style={{ color: 'var(--purple)' }}>🚀 Escort Mission</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Protect an NPC convoy as it travels between systems. NPCs spawn ambushes along the route. The convoy must
survive. Failure = no reward + standing loss. Success = high reward + standing bonus. Group content (post-MVP).
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Reward: High ISK + faction items · Time limit: 12h · Standing: +0.10 to +0.30 · Group recommended
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--amber)' }}>
<h4 style={{ color: '#f59e0b' }}>🔄 Trade Mission</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Buy a specific commodity at market price and deliver it to the agent. The agent reimburses cost + bonus.
Risk: market price may have moved since you accepted. Teaches market awareness and trade route planning.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Reward: Reimbursement + 1030% bonus · Time limit: 26h · Standing: +0.02 to +0.08 · Market risk
</div>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>NPC Agent Interaction</h3>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Every station has 13 NPC agents.</strong> Agents are persistent characters with names, factions,
and specialties. A player's relationship with each agent is tracked via <strong>standing</strong> — a value from 10.0 to +10.0
that determines what missions the agent offers and what rewards they give. Higher standing unlocks harder missions
with better payouts.
</div>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Agent Interaction Flow</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--cyan)' }}>1. Dock at station</span> — Agent Panel shows available agents at this station (with name, faction icon, standing, available missions count)<br/>
<span style={{ color: 'var(--accent)' }}>2. Select agent</span> — Agent portrait + dialogue box. Agent greets based on standing level (hostile/neutral/friendly/loyal). Lists available missions.<br/>
<span style={{ color: 'var(--green)' }}>3. Browse missions</span> — Each mission shows: type, brief description, reward estimate, location hint, time limit, difficulty tier. Player picks one.<br/>
<span style={{ color: 'var(--purple)' }}>4. Accept mission</span> — Mission added to active journal. Waypoint auto-created in navigation. Objective markers appear on overview and map.<br/>
<span style={{ color: 'var(--red)' }}>5. Complete objectives</span> — Kill targets, deliver cargo, mine ore, scan sites. Journal tracks progress (0/5 pirates killed, etc.).<br/>
<span style={{ color: 'var(--fg)' }}>6. Turn in</span> — Return to agent (or any agent of same faction for courier). Reward paid. Standing updated. New mission unlocked.r/>
<span style={{ color: 'var(--muted)' }}>Fail: Time expires or objective becomes impossible → standing loss (0.05 to 0.50). Can be declined before accepting with no penalty.</span>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Standing Mechanics</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Standing Range</th><th>Agent Attitude</th><th>Mission Access</th><th>Reward Modifier</th></tr>
</thead>
<tbody>
{[
{ range: '10.0 2.0', attitude: 'Hostile', access: 'None agent refuses to talk', reward: 'N/A' },
{ range: '2.0 +1.0', attitude: 'Neutral', access: 'Level 1 missions only (easy)', reward: 'Base reward ×0.8' },
{ range: '+1.0 +3.0', attitude: 'Friendly', access: 'Level 12 missions', reward: 'Base reward ×1.0' },
{ range: '+3.0 +6.0', attitude: 'Trusted', access: 'Level 13 missions (medium)', reward: 'Base reward ×1.2' },
{ range: '+6.0 +8.0', attitude: 'Loyal', access: 'Level 14 missions (hard)', reward: 'Base reward ×1.5 + rare loot' },
{ range: '+8.0 +10.0', attitude: 'Inner Circle', access: 'All levels + exclusive storyline arc', reward: 'Base reward ×2.0 + unique items + COSMOS missions' },
].map((row, i) => (
<tr key={i}>
<td className="mono" style={{ fontWeight: 600, color: ['var(--red)', 'var(--red)', 'var(--fg-dim)', 'var(--green)', 'var(--cyan)', 'var(--accent)'][i] }}>{row.range}</td>
<td style={{ color: 'var(--fg)' }}>{row.attitude}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.access}</td>
<td className="mono">{row.reward}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>Gaining Standing</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Complete missions: +0.05 to +0.30 per mission (scaled by difficulty)</li>
<li>Faction-wide: completing a mission for Agent A improves standing with Agent B of the same faction (at 50% rate)</li>
<li>Storyline missions: every 16 missions of the same level triggers a storyline mission with large standing boost (+1.0 to +3.0)</li>
<li>Derived standing: faction standing affects all agents in that faction. corp standing is agent-specific.</li>
</ul>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>Losing Standing</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Fail a mission: 0.05 to 0.50 (scaled by mission level)</li>
<li>Decline 2+ missions from same agent in 4h: 0.02 per decline after the first</li>
<li>Attack NPC of a faction: 0.10 to 1.0 (scaled by NPC importance)</li>
<li>Fail storyline mission: 1.0 to 3.0 (harsh penalty)</li>
<li>Standing decays toward 0.0 at 1% per day (prevents permanent locks)</li>
</ul>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Reward Scaling</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Level</th><th>Base Reward</th><th>Time Bonus</th><th>Loyalty Points</th><th>Skill Req.</th><th>Security Band</th></tr>
</thead>
<tbody>
{[
{ level: '1', reward: '5,000 15,000 ', bonus: '+20% if &lt;30 min', lp: '50150', skill: 'None', sec: 'High-sec' },
{ level: '2', reward: '15,000 40,000 ', bonus: '+25% if &lt;45 min', lp: '150400', skill: 'Industry II or Gunnery II', sec: 'High/Low' },
{ level: '3', reward: '40,000 100,000 ', bonus: '+30% if &lt;60 min', lp: '4001,000', skill: 'Industry III or Gunnery III', sec: 'Low/Null' },
{ level: '4', reward: '100,000 300,000 ', bonus: '+35% if &lt;90 min', lp: '1,0003,000', skill: 'Industry IV or Gunnery IV + ship class skill', sec: 'Null/Deep' },
].map((row, i) => (
<tr key={i}>
<td style={{ fontWeight: 600, color: ['var(--green)', 'var(--cyan)', 'var(--accent)', 'var(--red)'][i] }}>Level {row.level}</td>
<td className="mono">{row.reward}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.bonus}</td>
<td className="mono">{row.lp}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.skill}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.sec}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Loyalty Points (LP):</strong> Completing missions earns LP with the agent's faction. LP can be spent at faction stations
for faction-specific items (modules, ships, BPCs) at below-market prices. This creates a secondary currency that rewards
mission runners and cannot be traded between players. LP stores are faction-specific Caldari LP can't be spent at Gallente stations.
</div>
<div className="section-header">
<span className="section-num">GP-MIS-DB</span>
<h2 style={{ margin: 0 }}>Backend Impact</h2>
</div>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>New Tables, Reducers & Agent Updates</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--accent)' }}>npc_agents</span> — <code>agent_id, name, faction_id, station_id, specialty (kill/courier/mining/survey/trade/escort), quality (u32), mission_levels_offered, dialogue_seed</code><br/>
<span style={{ color: 'var(--cyan)' }}>mission_templates</span> — <code>template_id, type (enum), level (14), title, description_template, objectives_json, reward_base, time_limit_seconds, security_band_min, skill_requirements_json, faction_id</code><br/>
<span style={{ color: 'var(--green)' }}>active_missions</span> — <code>mission_id, player_id, agent_id, template_id, objectives_state_json, status (active/completed/failed/expired), accepted_at, expires_at, completed_at</code><br/>
<span style={{ color: 'var(--red)' }}>player_standing</span> — <code>player_id, entity_id (agent or faction), entity_type (agent/faction), standing (f64, 10 to +10), last_mission_at</code><br/>
<span style={{ color: 'var(--purple)' }}>player_loyalty_points</span> — <code>player_id, faction_id, lp_balance (u64), lifetime_earned (u64)</code><br/>
<span style={{ color: 'var(--fg)' }}>mission_offers</span> — <code>offer_id, agent_id, station_id, template_id, reward_modifier, expires_at, generated_at</code><br/>
<br/>
<span style={{ color: 'var(--cyan)' }}>Reducers:</span> <code>accept_mission(offer_id)</code>, <code>complete_mission_objective(mission_id, objective_idx)</code>, <code>turn_in_mission(mission_id)</code>, <code>decline_mission(offer_id)</code>, <code>fail_mission(mission_id)</code><br/>
<span style={{ color: 'var(--accent)' }}>Agent update:</span> <code>npc_mission_refresh</code> (existing, 1800s) — generates 25 new offers per agent from template pool, weighted by faction state, player activity, and standing. Removes expired offers.
</div>
</div>
<div className="callout callout-info">
<strong>Mission journal UI:</strong> A docked-only panel showing all active missions with objective progress, time remaining,
reward estimate, and a "set waypoint" button. In Flight Mode, active mission objectives appear as HUD indicators
(e.g., "Pirates remaining: 3/5" in the top-right corner). Mission completion triggers a notification toast.
</div>
</>)}
{/* ═══ TRAVEL & WARP ═══ */}
{activeTab === 'travel' && (<>
<div className="section-header">
<span className="section-num">GP-TRAVEL</span>
<h2 style={{ margin: 0 }}>Travel & Warp Mechanics</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Click to autopilot — the player sets intent, the ship executes.</strong>
There is no manual flight control. The player clicks a destination (asteroid, station, stargate, bookmark,
or any point in space) and the ship navigates there. Travel has three modes: sub-warp (slow, in-system),
warp (fast, in-system), and gate jump (instant, inter-system). Each mode has different speed, acceleration,
and interaction rules. Travel time is the primary "geography tax" that makes distant systems feel distant
and creates real trade route logistics.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Travel Modes</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Mode</th><th>Speed</th><th>Use Case</th><th>Player Input</th></tr>
</thead>
<tbody>
{[
{ mode: 'Sub-Warp', speed: '150300 m/s (ship speed stat)', use: 'Approach objects within 500km. Orbiting, mining range, docking approach.', input: 'Click target Approach. Ship moves at base speed.' },
{ mode: 'Warp', speed: '3.06.0 AU/s (ship class dependent)', use: 'Travel within a system. Warp to station, belt, gate, bookmark, or any celestials.', input: 'Click target Warp To. Ship aligns, then enters warp.' },
{ mode: 'Gate Jump', speed: 'Instant', use: 'Travel between star systems. Jump through a stargate to the paired gate in the destination system.', input: 'Click stargate Jump. Must be within 2,500m of gate.' },
].map((row, i) => (
<tr key={i}>
<td style={{ fontWeight: 600, color: ['var(--cyan)', 'var(--accent)', 'var(--green)'][i] }}>{row.mode}</td>
<td className="mono">{row.speed}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.use}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.85rem' }}>{row.input}</td>
</tr>
))}
</tbody>
</table>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Warp Mechanics — Detailed</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Warp Sequence</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--cyan)' }}>1. Align:</span> Ship turns toward destination. Align time = 28s by ship class (frigate fastest, battleship slowest).<br/>
<span style={{ color: 'var(--accent)' }}>2. Accelerate:</span> Ship reaches 75% of max speed while aligned. ~1s for frigates, ~3s for battleships.<br/>
<span style={{ color: 'var(--green)' }}>3. Enter Warp:</span> Ship enters warp tunnel. Speed ramps from 0 to max warp speed over 2s.<br/>
<span style={{ color: 'var(--purple)' }}>4. Cruise:</span> Ship travels at warp speed (36 AU/s). Deceleration starts at 50% of remaining distance.<br/>
<span style={{ color: 'var(--red)' }}>5. Exit Warp:</span> Ship decelerates from warp speed to 0 over 2s. Appears 050km from destination.
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Warp Speed by Ship Class</h4>
<table className="data-table">
<thead><tr><th>Class</th><th>Warp Speed</th><th>Align Time</th><th>System Cross (30 AU)</th></tr></thead>
<tbody>
{[
{ cls: 'Frigate', warp: '6.0 AU/s', align: '2s', cross: '~7s' },
{ cls: 'Destroyer', warp: '5.0 AU/s', align: '3s', cross: '~9s' },
{ cls: 'Cruiser', warp: '4.5 AU/s', align: '4s', cross: '~11s' },
{ cls: 'Battlecruiser', warp: '3.5 AU/s', align: '6s', cross: '~14s' },
{ cls: 'Battleship', warp: '3.0 AU/s', align: '8s', cross: '~18s' },
].map((r, i) => (
<tr key={i}>
<td style={{ fontWeight: 600, color: 'var(--accent)' }}>{r.cls}</td>
<td className="mono">{r.warp}</td>
<td className="mono">{r.align}</td>
<td className="mono" style={{ color: 'var(--fg-dim)' }}>{r.cross}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Warp disruption:</strong> A warp scrambler module (medium slot, combat module) prevents the target ship from
entering warp while the scrambler is active and the target is within scram range (10km). If a ship is scrambled
while aligning, the warp is cancelled. If scrambled during warp exit, the next warp is blocked. This is the primary
PvP tackle mechanic. NPCs do not use warp scramblers in MVP (Phase 3+ only).
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Stargate Mechanics</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>Gate Jump Sequence</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
1. Warp to stargate (arrive 020km from gate)<br/>
2. Sub-warp approach to within <strong style={{ color: 'var(--fg)' }}>2,500m</strong> (activation range)<br/>
3. Click <strong style={{ color: 'var(--accent)' }}>"Jump"</strong> — ship activates gate<br/>
4. <strong style={{ color: 'var(--cyan)' }}>Jump delay:</strong> 5s (global cooldown per character, prevents rapid gate-hopping)<br/>
5. Ship appears at paired gate in destination system<br/>
6. <strong style={{ color: 'var(--purple)' }}>Gate cloak:</strong> 30s invulnerability after jump (ship is invisible to other players, cannot be targeted, cannot activate modules). Breaks early if you move or activate anything.<br/>
7. Player must warp away from gate before cloak expires or risk being targeted
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>Gate Guns & Security</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li><strong style={{ color: 'var(--fg)' }}>High-sec gates:</strong> No gate guns needed — CONCORD handles aggression.</li>
<li><strong style={{ color: 'var(--fg)' }}>Low-sec gates (+0.1 to +0.4):</strong> Gate guns fire on any aggressor within 150km. Moderate damage — can be tanked by battlecruiser+ for a short time. Guns do not prevent aggression, they punish it.</li>
<li><strong style={{ color: 'var(--fg)' }}>Null-sec gates (0.0 and below):</strong> No gate guns. No rules. Anything goes. Gate camps are a primary PvP activity.</li>
<li><strong style={{ color: 'var(--fg)' }}>Weapons timer:</strong> 60s after any aggressive module activation. Cannot jump gates during weapons timer. Prevents "shoot and jump" tactics ("gate games").</li>
</ul>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Docking & Undocking</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Docking</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
1. Warp to station (arrive 020km)<br/>
2. Approach to within <strong style={{ color: 'var(--fg)' }}>500m</strong> (docking range)<br/>
3. Click <strong style={{ color: 'var(--accent)' }}>"Dock"</strong><br/>
4. Docking is instant — ship disappears from space, player enters Station Mode<br/>
5. Cannot dock while weapons timer is active (60s after aggressive action)
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Undocking</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
1. Click <strong style={{ color: 'var(--accent)' }}>"Undock"</strong> in Station Mode<br/>
2. Ship appears outside station, moving at base speed away from station<br/>
3. <strong style={{ color: 'var(--green)' }}>Undock invulnerability:</strong> 20s. Cannot be targeted. Cannot activate modules. Breaks if you change direction or activate anything.<br/>
4. Player has 20s to assess the situation and warp to a safe location<br/>
5. No "station games" — you always get a safe undock window
</div>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Autopilot & Route Planning</h3>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Autopilot flies the route automatically, but at a cost.</strong> Players can set a multi-system route
(via waypoints) and activate autopilot. The ship will warp to each gate, jump, warp to the next gate, and repeat.
Autopilot is <em>slower</em> than manual piloting: it warps to 15km from each gate instead of 0km, requiring a
sub-warp approach each time. Manual pilots who click precisely arrive faster. Autopilot is a convenience,
not a replacement for active play.
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card">
<h4 style={{ color: 'var(--green)' }}>Manual Warp</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Click gate → "Warp To 0m" → arrive on grid</li>
<li>Jump immediately (within activation range)</li>
<li>Fastest possible travel</li>
<li>Requires active player attention at each gate</li>
</ul>
</div>
<div className="card">
<h4 style={{ color: 'var(--accent)' }}>Autopilot</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>Set route → activate autopilot</li>
<li>Warps to 15km from each gate, approaches, jumps</li>
<li>~30% slower than manual (extra approach time per gate)</li>
<li>Vulnerable during gate approach (PvP risk in low/null)</li>
<li>Can be cancelled at any time by clicking anywhere</li>
</ul>
</div>
</div>
<div className="section-header" style={{ marginTop: 'var(--sp-6)' }}>
<span className="section-num">GP-TRAVEL-DB</span>
<h2 style={{ margin: 0 }}>Backend Impact</h2>
</div>
<div className="card card-accent">
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Travel-Related State & Reducers</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--accent)' }}>ships</span> — add column: <code>travel_mode</code> (enum: idle / aligning / in_warp / gate_jump / sub_warp_approach / docked)<br/>
<span style={{ color: 'var(--cyan)' }}>ships</span> — add column: <code>gate_cloak_until</code> (timestamp, null when not cloaked). 30s after gate jump.<br/>
<span style={{ color: 'var(--green)' }}>ships</span> — add column: <code>undock_invuln_until</code> (timestamp, null when not active). 20s after undock.<br/>
<span style={{ color: 'var(--purple)' }}>ships</span> — add column: <code>weapons_timer_until</code> (timestamp, null when not active). 60s after aggressive action.<br/>
<span style={{ color: 'var(--red)' }}>ships</span> — add column: <code>jump_cooldown_until</code> (timestamp). 5s global per character after gate jump.<br/>
<br/>
<span style={{ color: 'var(--cyan)' }}>New reducer:</span> <code>warp_to(ship_id, target_system_entity_id, distance_km)</code> — validate not scrambled, begin align sequence.<br/>
<span style={{ color: 'var(--green)' }}>New reducer:</span> <code>jump_gate(ship_id, gate_id)</code> — validate within 2,500m, validate no weapons timer, validate cooldown expired, execute jump.<br/>
<span style={{ color: 'var(--accent)' }}>New reducer:</span> <code>dock(ship_id, station_id)</code> — validate within 500m, validate no weapons timer, set docked.<br/>
<span style={{ color: 'var(--purple)' }}>New reducer:</span> <code>undock(ship_id)</code> — validate docked, place ship outside station, set 20s invulnerability.<br/>
<span style={{ color: 'var(--fg)' }}>New reducer:</span> <code>set_autopilot(ship_id, route_id)</code> — begin automated route following.<br/>
<span style={{ color: 'var(--red)' }}>New reducer:</span> <code>cancel_autopilot(ship_id)</code> — stop route following, resume idle.
</div>
</div>
</>)}
{/* ═══ WORLD EVENTS UX ═══ */}
{activeTab === 'events' && (<>
<div className="section-header">
<span className="section-num">GP-EVT</span>
<h2 style={{ margin: 0 }}>World Event Player UX</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>The player needs to see, understand, and act on world events without leaving their current activity.</strong>
The backend pipeline (spawn → propagate → resolve → story log) is fully specified in the Dynamic Galaxy tab.
This tab specifies the <em>player-facing surface</em>: how events appear, what information is shown, and how
players interact with events in both Flight Mode and Station Mode.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Event Notification Tiers</h3>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
Not all events are equally urgent. The notification system uses three tiers that determine how aggressively
the player is interrupted:
</p>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Tier</th><th>Trigger</th><th>Flight Mode</th><th>Station Mode</th><th>Examples</th></tr>
</thead>
<tbody>
{[
{ tier: ' Critical', trigger: 'Event in current system, player directly affected', flight: 'HUD alert flash + audio ping + top-center banner. Pause-safe: combat continues but banner demands attention. Banner auto-dismisses after 10s or on click.', station: 'Modal dialog with event details + action buttons ("Warp to site"). Market panel may show price impact callout.', examples: 'Supernova warning, pirate raid on your station, faction invasion of your home system' },
{ tier: '📍 Nearby', trigger: 'Event in adjacent system or current region', flight: 'Side panel notification (slides in from right). No audio. Click to expand details. Dismisses when event resolves or player leaves region.', station: 'News feed sidebar item. Glowing dot on region map. Agent dialogue may reference it.', examples: 'Anomaly detected 2 jumps away, convoy departing nearby station, faction skirmish in adjacent constellation' },
{ tier: '📰 Background', trigger: 'Event anywhere in galaxy, not in player region', flight: 'Chat-style ticker in bottom-left corner. No interruption. Logged in story journal for later review.', station: 'Galaxy News tab in station UI. Story log entry. Price impact shown in market history charts.', examples: 'Distant faction war, migration route shift, station construction completed, cosmic catastrophe aftermath' },
].map((row, i) => (
<tr key={i}>
<td style={{ fontWeight: 600, color: ['var(--red)', 'var(--accent)', 'var(--fg-dim)'][i] }}>{row.tier}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{row.trigger}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{row.flight}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{row.station}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{row.examples}</td>
</tr>
))}
</tbody>
</table>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Event Detail Panel</h3>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
Clicking on any event notification opens the Event Detail Panel — a shared component used in both Flight and Station modes.
The panel shows everything the player needs to decide whether and how to participate:
</p>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Event Detail Panel Layout</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--red)' }}>Header:</span> Event icon + name + severity gauge (15 bar) + countdown timer to resolution/escalation<br/>
<span style={{ color: 'var(--accent)' }}>Location:</span> System name + region + distance in jumps + security level of target system<br/>
<span style={{ color: 'var(--cyan)' }}>Narrative:</span> 23 sentence in-universe description. "A Guristas raiding fleet has been spotted massing at the Ostingale gate. Local defense forces are overwhelmed."
<br/>
<span style={{ color: 'var(--green)' }}>Rewards:</span> Participation reward tier (bronze/silver/gold based on contribution). Expected ISK + LP. Special loot possibilities.<br/>
<span style={{ color: 'var(--purple)' }}>Participants:</span> Live count of players at the event site. "12 pilots engaged" — signals competition or cooperation opportunity.<br/>
<span style={{ color: 'var(--fg)' }}>Actions:</span> <code>[Warp to Site]</code> (if in-system) / <code>[Set Route]</code> (if distant) / <code>[Watch]</code> (track without participating) / <code>[Dismiss]</code><br/>
<span style={{ color: 'var(--muted)' }}>History:</span> Collapsible log of event progression. "14:23 — First wave defeated. 14:31 — Boss spawned. 14:35 — CMDR Riker destroyed."
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Event Map Integration</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>System Map (Era 1)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Events in the current system appear as pulsing icons at their location. Clicking an event icon on the map
opens the Event Detail Panel. Active events have a glowing radius showing their area of effect.
Resolved events fade to a dim marker for 5 minutes before disappearing.
</p>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Galaxy Map (Era 2)</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Events across all systems appear as icons on the galaxy map, sized by severity and colored by type.
A filter sidebar lets players show/hide by type (faction conflict, anomaly, migration, catastrophe).
Hover shows tooltip with event name + countdown. Click opens Event Detail Panel.
</p>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Event Participation & Contribution Tracking</h3>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Contribution determines reward.</strong> Players who arrive first and contribute most get the best rewards.
This is tracked server-side and is resistant to AFK exploitation.
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>Contribution Metrics</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li><strong style={{ color: 'var(--fg)' }}>Damage dealt:</strong> to event NPCs (primary metric for combat events)</li>
<li><strong style={{ color: 'var(--fg)' }}>Time on-site:</strong> must be present for ≥60s to qualify (anti-AFK)</li>
<li><strong style={{ color: 'var(--fg)' }}>Logistics:</strong> remote repairs to other participants count at 80% of damage value</li>
<li><strong style={{ color: 'var(--fg)' }}>Objective completion:</strong> hacking a can, mining a special asteroid, escorting the convoy</li>
<li><strong style={{ color: 'var(--fg)' }}>Early arrival:</strong> first 5 participants get a 10% bonus (rewards information speed)</li>
</ul>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Reward Tiers</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li><span style={{ color: '#cd7f32' }}>Bronze:</span> Any contribution. 50% base reward. Common loot table.</li>
<li><span style={{ color: '#c0c0c0' }}>Silver:</span> Top 50% contributors. 100% base reward + 100 LP. Uncommon loot table.</li>
<li><span style={{ color: '#ffd700' }}>Gold:</span> Top 10% contributors. 200% base reward + 500 LP + rare loot. Name appears in story log.</li>
<li><span style={{ color: 'var(--muted)' }}>AFK check:</span> If no client input for 90s while on event site, contribution pauses. Must take an action (module activate, move, target) to resume.</li>
</ul>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Story Log</h3>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Galaxy Story Log — The Server's History</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--cyan)' }}>Access:</span> Station Mode → Story Log tab. Also accessible from Galaxy Map sidebar.<br/>
<span style={{ color: 'var(--accent)' }}>Format:</span> Chronological timeline of all events, filterable by region/type/era. Each entry has headline + body + timestamp + participants list.<br/>
<span style={{ color: 'var(--green)' }}>Personal history:</span> A separate filter shows "Events I participated in" — the player's personal saga.<br/>
<span style={{ color: 'var(--purple)' }}>Persistence:</span> Story log is permanent. Even years later, a player can look back at "the Battle of Jita" and see the full narrative.<br/>
<span style={{ color: 'var(--fg)' }}>Search:</span> Full-text search across all entries. "What happened in Pure Blind last week?" → search results with highlighted entries.<br/>
<span style={{ color: 'var(--muted)' }}>Export:</span> Copy link to any story log entry for sharing in chat. "Did you see [this]?" with a clickable link to the event.
</div>
</div>
<div className="callout callout-warn">
<strong>Design intent:</strong> The event UX must feel like <em>news</em>, not like a quest log. Players should discover events naturally
through sensor alerts, chat rumors, price changes, or map icons not through a mandatory event panel.
The notification system provides <em>awareness</em>; the detail panel provides <em>information</em>; the decision to participate
is always the player's choice. The best stories are the ones players tell each other: "You should have been at Jita yesterday —
there was a massive Guristas raid and I scored a faction BPC from the gold tier."
</div>
</>)}
{/* ═══ BALANCING AGENT ═══ */}
{activeTab === 'balancer' && (<>
<div className="section-header">
<span className="section-num">GP-BAL</span>
<h2 style={{ margin: 0 }}>Balancing Agent — Adaptive Economy & PvE Control</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>This is at heart a PvE game.</strong> The environment has hostile elements — NPC pirates, faction raids,
anomalies — that challenge players and drain resources. But a static PvE difficulty can't adapt to a changing player
population or economy. The <strong>Balancing Agent</strong> is an automated system (similar to the world event agent) that
monitors key economic and gameplay metrics and adjusts hostile encounter rates, ISK faucet rates, and difficulty
to keep the game within healthy parameters. It is the invisible hand that keeps the galaxy challenging but fair.
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Metrics Monitored</h3>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Metric</th><th>Source</th><th>Healthy Range</th><th>Tick Interval</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-green">ISK Velocity</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Total ISK earned per player-hour (faucets)</td>
<td className="mono">2,0008,000 \u2262/hr</td>
<td className="mono">15 min</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Price Index</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Weighted basket of commodity prices vs. baseline</td>
<td className="mono">0.851.15 (inflation-adjusted)</td>
<td className="mono">30 min</td>
</tr>
<tr>
<td><span className="pill pill-red">Player Death Rate</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Ship destructions per active player per hour</td>
<td className="mono">0.10.5 deaths/hr</td>
<td className="mono">10 min</td>
</tr>
<tr>
<td><span className="pill pill-purple">Faucet/Sink Ratio</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Total ISK created vs. destroyed per tick</td>
<td className="mono">0.951.05 (near balance)</td>
<td className="mono">30 min</td>
</tr>
<tr>
<td><span className="pill pill-amber">Player Engagement</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Active players / total registered players</td>
<td className="mono">> 40%</td>
<td className="mono">60 min</td>
</tr>
</tbody>
</table>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Control Levers</h3>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>NPC Spawn Rate Multiplier</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Adjusts the base spawn rate of NPC pirates across all security bands. If ISK velocity is too high
(players are earning too fast), spawn rate increases to create more danger and ship losses.
If players are dying too much, spawn rate decreases to ease pressure.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Range: 0.5\u00d72.0\u00d7 · Applied per security band · Smooth transition (10% per tick)
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>NPC Difficulty Tier</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Shifts the NPC class distribution within each security band. Higher difficulty means more cruisers
in low-sec instead of frigates. This affects bounty payouts (ISK faucet) and ship destruction rates (ISK sink via insurance).
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Range: \u22121 tier to +1 tier from baseline · Applied per region · Shifts over 6 ticks
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)' }}>ISK Faucet Multiplier</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Directly scales all ISK faucets: NPC bounties, mission rewards, insurance payouts.
Used as a last resort when spawn rate and difficulty aren't enough. If the price index shows deflation
(players can't afford ships), faucet multiplier increases. If inflating, it decreases.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Range: 0.8\u00d71.2\u00d7 · Applied globally · Changed max once per 24h
</div>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>World Event Frequency</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>
Controls how often world events spawn. More events = more ISK entering the economy (event rewards)
and more engagement. Fewer events = calmer galaxy. Tied to engagement metric if players are logging off,
event frequency increases to create excitement.
</p>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem', color: 'var(--muted)' }}>
Range: 0.5\u00d71.5\u00d7 spawn weight · Applied per event type · Updated hourly
</div>
</div>
</div>
<h3 style={{ marginBottom: 'var(--sp-4)' }}>Agent Behavior</h3>
<div className="card card-accent" style={{ marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Balancing Agent Tick Pipeline</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--cyan)' }}>1. Collect metrics</span> query ISK velocity, price index, death rate, faucet/sink ratio from SpacetimeDB aggregation tables<br/>
<span style={{ color: 'var(--accent)' }}>2. Compare to thresholds</span> — each metric has a healthy range; deviations trigger adjustment signals<br/>
<span style={{ color: 'var(--green)' }}>3. Weighted decision</span> — adjustments are weighted: spawn rate (40%), difficulty (30%), faucet multiplier (20%), events (10%)<br/>
<span style={{ color: 'var(--purple)' }}>4. Smooth application</span> — changes apply gradually (10% per tick toward target) to avoid jarring player experience<br/>
<span style={{ color: 'var(--red)' }}>5. Safety clamp</span> — all multipliers clamped to their ranges. If multiple metrics conflict, death rate takes priority (don't kill the newbies)<br/>
<span style={{ color: 'var(--fg)' }}>6. Log intervention</span> every adjustment is logged in a balance_audit table for developer review. No silent changes.
</div>
</div>
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Visibility to players:</strong> The Balancing Agent is invisible by default. Players should never feel "the game
is adjusting difficulty." However, observant players may notice that NPC spawns increase after a quiet period or that
bounties are slightly lower during a gold rush. Zora may comment on market conditions ("NPC activity has been high
in this region lately") without revealing the underlying mechanism. The agent is a safety net, not a theme park ride.
</div>
<div className="section-header">
<span className="section-num">GP-BAL-DB</span>
<h2 style={{ margin: 0 }}>Backend Impact</h2>
</div>
<div className="card card-accent">
<h4 style={{ marginBottom: 'var(--sp-4)' }}>New Tables & Agent</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--accent)' }}>balance_metrics</span> <code>metric_name, current_value, healthy_min, healthy_max, last_updated, trend (rising/falling/stable)</code><br/>
<span style={{ color: 'var(--cyan)' }}>balance_levers</span> <code>lever_name, current_multiplier, target_multiplier, clamp_min, clamp_max, last_adjusted_at</code><br/>
<span style={{ color: 'var(--green)' }}>balance_audit</span> <code>audit_id, tick_time, metrics_snapshot_json, adjustments_json, reason</code><br/>
<br/>
<span style={{ color: 'var(--red)' }}>New agent:</span> <code>balancing_tick</code> (fixed interval, 900s) collects metrics, evaluates thresholds, adjusts levers, logs audit entry
</div>
</div>
</>)}
</div>
);
}
window.GDD.GameplayPage = GameplayPage;