initial commit
This commit is contained in:
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
173
SESSION.md
Normal file
173
SESSION.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
# Session Summary
|
||||||
|
|
||||||
|
## Project State
|
||||||
|
|
||||||
|
This repository now contains a playable Three.js/Vite prototype for a space RTS / economy sim testbed inspired by EVE Online and X4.
|
||||||
|
|
||||||
|
The current prototype includes:
|
||||||
|
|
||||||
|
- Two solar systems: `Helios Reach` and `Perseus Gate`
|
||||||
|
- A large space environment with stars, planets, orbit lines, nebulae, asteroid/resource fields, and starfield
|
||||||
|
- RTS-style ship selection, command issuance, camera movement, zoom levels, and follow-camera support
|
||||||
|
- Three view levels based on zoom: `local`, `solar`, and `universe`
|
||||||
|
- A bottom command bar with selection info, order buttons, and a minimap
|
||||||
|
- A strategic HUD overlay that switches to NATO / military-style symbols at higher zoom levels
|
||||||
|
|
||||||
|
## Major Gameplay Systems Added
|
||||||
|
|
||||||
|
### World / Navigation
|
||||||
|
|
||||||
|
- Ships can travel between the two systems using staged FTL travel
|
||||||
|
- Travel flow includes:
|
||||||
|
- leaving gravity well
|
||||||
|
- FTL spool
|
||||||
|
- warp
|
||||||
|
- arrival
|
||||||
|
- FTL speed was increased and a basic warp streak / tunnel effect was added
|
||||||
|
- Local ship movement is no longer purely straight-line:
|
||||||
|
- ships bias toward curved orbital-style transfers around the system center
|
||||||
|
- idle ships hold a passive orbit instead of freezing in place
|
||||||
|
|
||||||
|
### Orbital Model
|
||||||
|
|
||||||
|
- Stations are no longer static arbitrary points
|
||||||
|
- Stations in `Helios` are placed on Lagrange-style offsets relative to planets
|
||||||
|
- Stations update position over time with the planetary orbital motion
|
||||||
|
- Ships and stations are beginning to behave like orbitals rather than free-floating markers
|
||||||
|
|
||||||
|
### Units / AI / Orders
|
||||||
|
|
||||||
|
- Ship roles currently in the prototype:
|
||||||
|
- military
|
||||||
|
- transport
|
||||||
|
- mining
|
||||||
|
- Unit state machine now includes states for:
|
||||||
|
- idle / moving
|
||||||
|
- FTL travel
|
||||||
|
- mining and delivery
|
||||||
|
- docking approach / docking / docked / undocking
|
||||||
|
- patrol / escort
|
||||||
|
- Orders currently supported:
|
||||||
|
- move
|
||||||
|
- transfer
|
||||||
|
- mine
|
||||||
|
- patrol
|
||||||
|
- escort
|
||||||
|
|
||||||
|
### Docking / Logistics
|
||||||
|
|
||||||
|
- Docking was added as a required step for transfer to stations
|
||||||
|
- Stations have limited docking capacity and explicit docking ports
|
||||||
|
- Mining ships now:
|
||||||
|
- mine ore in `Perseus`
|
||||||
|
- return to `Helios`
|
||||||
|
- dock at a refinery
|
||||||
|
- transfer ore
|
||||||
|
- undock and repeat
|
||||||
|
|
||||||
|
### Economy / Inventory Foundations
|
||||||
|
|
||||||
|
- Added item storage classes:
|
||||||
|
- `bulk-solid`
|
||||||
|
- `bulk-liquid`
|
||||||
|
- `bulk-gas`
|
||||||
|
- `container`
|
||||||
|
- `manufactured`
|
||||||
|
- Added module categories and starter module definitions for ships/stations
|
||||||
|
- Ships and stations now expose compatible cargo/storage/module metadata
|
||||||
|
- Refineries track:
|
||||||
|
- ore stored
|
||||||
|
- active refining batch
|
||||||
|
- refining timer
|
||||||
|
- refined output stock
|
||||||
|
|
||||||
|
### Energy / Fuel
|
||||||
|
|
||||||
|
- Ships now track:
|
||||||
|
- fuel
|
||||||
|
- energy
|
||||||
|
- Stations now track:
|
||||||
|
- fuel
|
||||||
|
- energy
|
||||||
|
- Ships consume energy/fuel depending on activity
|
||||||
|
- Docked ships recharge energy
|
||||||
|
- Stations recharge energy passively
|
||||||
|
|
||||||
|
## Testbed Layout
|
||||||
|
|
||||||
|
- `Helios Reach` is now the industrial / infrastructure system
|
||||||
|
- stations are concentrated there
|
||||||
|
- refinery loop terminates there
|
||||||
|
- `Perseus Gate` is now the extraction / resource system
|
||||||
|
- resource asteroid nodes are concentrated there
|
||||||
|
- miners operate there before hauling back
|
||||||
|
|
||||||
|
## UI / UX State
|
||||||
|
|
||||||
|
- Ship and station selection is supported
|
||||||
|
- Ship multi-selection is supported via click modifiers and marquee drag selection
|
||||||
|
- `Solar` and `universe` views now overlay high-level tactical symbology instead of relying only on 3D meshes
|
||||||
|
- Ships use role-specific long-range symbols:
|
||||||
|
- military: hostile/combat-style diamond iconography
|
||||||
|
- transport: boxed logistics symbol
|
||||||
|
- mining: angular resource / industrial symbol
|
||||||
|
- Stations and constructibles use square strategic markers with category-specific internal glyphs
|
||||||
|
- `Universe` view groups ships into fleet counts per system and role for cleaner strategic readability
|
||||||
|
- Focusing works for:
|
||||||
|
- single ships: follow camera
|
||||||
|
- stations: focus camera on the station
|
||||||
|
- Selection panels show:
|
||||||
|
- ship state, order, cargo, hold type, fuel, energy, modules
|
||||||
|
- station role, docking occupancy, stored resources, refinery timing, fuel, energy, modules
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
- `Left Click`: select ships or stations
|
||||||
|
- `Shift + Left Click`: add ships to ship selection
|
||||||
|
- `Ctrl/Cmd + Left Click`: toggle ships in selection
|
||||||
|
- `Left Drag`: marquee-select multiple ships
|
||||||
|
- `Right Click`: issue move/transfer orders
|
||||||
|
- `Mouse Wheel` or `-` / `=`: zoom
|
||||||
|
- `W A S D`: pan camera
|
||||||
|
- `Q / E`: rotate camera
|
||||||
|
- `F`: focus selection, and follow a single selected ship
|
||||||
|
- `Tab`: jump camera between systems
|
||||||
|
- `B`: toggle build mode
|
||||||
|
- `1-5`: choose constructible
|
||||||
|
- `M`: assign mining
|
||||||
|
- `P`: assign patrol
|
||||||
|
- `E`: assign escort
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
- The prototype is built with:
|
||||||
|
- Vite
|
||||||
|
- TypeScript
|
||||||
|
- Three.js
|
||||||
|
- High-level symbology is rendered through a dedicated 2D HUD overlay canvas layered above the 3D scene
|
||||||
|
- Production build is currently passing with `npm run build`
|
||||||
|
|
||||||
|
## Known Limitations / Caveats
|
||||||
|
|
||||||
|
- Orbital behavior is still an approximation for gameplay, not a full orbital mechanics simulation
|
||||||
|
- Stations are on Lagrange-style offsets, but not using a physically rigorous orbital solver
|
||||||
|
- Ship transfer paths are curved and orbit-biased, but still use authored steering rather than patched conics or n-body integration
|
||||||
|
- Fuel / energy exist but station refueling, resupply, and depletion failure states are still minimal
|
||||||
|
- Module definitions exist, but there is no actual ship/station designer yet
|
||||||
|
- Inventory classes exist, but only a subset of economic flows are implemented
|
||||||
|
- Docking works for logistics, but there is not yet a richer docking queue / reservation UI
|
||||||
|
- NATO-style symbology is gameplay-oriented inspiration, not a strict APP-6 / MIL-STD implementation
|
||||||
|
|
||||||
|
## Suggested Next Steps
|
||||||
|
|
||||||
|
- Introduce explicit orbital anchors for:
|
||||||
|
- stars
|
||||||
|
- planets
|
||||||
|
- stations
|
||||||
|
- asteroid belts / resource fields
|
||||||
|
- Replace the current movement approximation with a more formal orbital transfer model
|
||||||
|
- Add refueling and power management gameplay
|
||||||
|
- Add ship/station fitting data structures that can later drive a designer UI
|
||||||
|
- Expand the economy beyond ore/refining into manufactured goods and trade lanes
|
||||||
|
- Improve FTL visuals with a fullscreen post-process distortion or tunnel effect
|
||||||
|
- Expand the strategic overlay with threat rings, route arrows, and fleet stance/status markers
|
||||||
12
index.html
Normal file
12
index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Space Command</title>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1121
package-lock.json
generated
Normal file
1121
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
package.json
Normal file
19
package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "space-game",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"three": "^0.179.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/three": "^0.183.1",
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
"vite": "^7.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
2513
src/game/GameApp.ts
Normal file
2513
src/game/GameApp.ts
Normal file
File diff suppressed because it is too large
Load Diff
273
src/game/definitions.ts
Normal file
273
src/game/definitions.ts
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
export type ShipRole = "military" | "transport" | "mining";
|
||||||
|
export type ConstructibleCategory =
|
||||||
|
| "station"
|
||||||
|
| "refining"
|
||||||
|
| "farm"
|
||||||
|
| "shipyard"
|
||||||
|
| "defense";
|
||||||
|
|
||||||
|
export type UnitState =
|
||||||
|
| "idle"
|
||||||
|
| "moving"
|
||||||
|
| "leaving-gravity-well"
|
||||||
|
| "spooling-ftl"
|
||||||
|
| "warping"
|
||||||
|
| "arriving"
|
||||||
|
| "mining-approach"
|
||||||
|
| "mining"
|
||||||
|
| "delivering"
|
||||||
|
| "docking-approach"
|
||||||
|
| "docking"
|
||||||
|
| "docked"
|
||||||
|
| "undocking"
|
||||||
|
| "patrolling"
|
||||||
|
| "escorting";
|
||||||
|
|
||||||
|
export type UnitOrderKind = "idle" | "move" | "transfer" | "mine" | "patrol" | "escort";
|
||||||
|
|
||||||
|
export type ItemStorageKind = "bulk-solid" | "bulk-liquid" | "bulk-gas" | "container" | "manufactured";
|
||||||
|
export type ModuleCategory =
|
||||||
|
| "bridge"
|
||||||
|
| "engine"
|
||||||
|
| "ftl"
|
||||||
|
| "mining"
|
||||||
|
| "cargo-bulk"
|
||||||
|
| "cargo-container"
|
||||||
|
| "dock"
|
||||||
|
| "refinery"
|
||||||
|
| "defense"
|
||||||
|
| "habitat"
|
||||||
|
| "production";
|
||||||
|
|
||||||
|
export interface ModuleDefinition {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
category: ModuleCategory;
|
||||||
|
summary: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ItemDefinition {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
storage: ItemStorageKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShipDefinition {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
role: ShipRole;
|
||||||
|
speed: number;
|
||||||
|
ftlSpeed: number;
|
||||||
|
spoolTime: number;
|
||||||
|
cargoCapacity: number;
|
||||||
|
cargoKind?: ItemStorageKind;
|
||||||
|
cargoItemId?: string;
|
||||||
|
color: number;
|
||||||
|
hullColor: number;
|
||||||
|
size: number;
|
||||||
|
maxHealth: number;
|
||||||
|
modules: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConstructibleDefinition {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
category: ConstructibleCategory;
|
||||||
|
color: number;
|
||||||
|
radius: number;
|
||||||
|
dockingCapacity: number;
|
||||||
|
storage: Partial<Record<ItemStorageKind, number>>;
|
||||||
|
modules: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlanetDefinition {
|
||||||
|
label: string;
|
||||||
|
orbitRadius: number;
|
||||||
|
orbitSpeed: number;
|
||||||
|
size: number;
|
||||||
|
color: number;
|
||||||
|
tilt: number;
|
||||||
|
hasRing?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SolarSystemDefinition {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
starColor: number;
|
||||||
|
starGlow: number;
|
||||||
|
starSize: number;
|
||||||
|
gravityWellRadius: number;
|
||||||
|
planets: PlanetDefinition[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const itemDefinitions: ItemDefinition[] = [
|
||||||
|
{ id: "ore", label: "Raw Ore", storage: "bulk-solid" },
|
||||||
|
{ id: "refined-metals", label: "Refined Metals", storage: "manufactured" },
|
||||||
|
{ id: "gas", label: "Volatile Gas", storage: "bulk-gas" },
|
||||||
|
{ id: "water", label: "Water", storage: "bulk-liquid" },
|
||||||
|
{ id: "drone-parts", label: "Drone Parts", storage: "container" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const moduleDefinitions: ModuleDefinition[] = [
|
||||||
|
{ id: "command-bridge", label: "Command Bridge", category: "bridge", summary: "Core ship control and crew systems." },
|
||||||
|
{ id: "ion-drive", label: "Ion Drive", category: "engine", summary: "Sub-light propulsion package." },
|
||||||
|
{ id: "ftl-core", label: "FTL Core", category: "ftl", summary: "Spool and warp inter-system engine." },
|
||||||
|
{ id: "strip-miner", label: "Strip Miner", category: "mining", summary: "Excavation laser and ore intake." },
|
||||||
|
{ id: "bulk-bay", label: "Bulk Cargo Bay", category: "cargo-bulk", summary: "Reinforced storage for raw solids." },
|
||||||
|
{ id: "container-bay", label: "Container Hold", category: "cargo-container", summary: "Standardized freight racks." },
|
||||||
|
{ id: "docking-clamps", label: "Docking Clamps", category: "dock", summary: "Docking collar and transfer arms." },
|
||||||
|
{ id: "refinery-stack", label: "Refinery Stack", category: "refinery", summary: "Ore cracking and metal separation." },
|
||||||
|
{ id: "turret-grid", label: "Turret Grid", category: "defense", summary: "Close defense batteries." },
|
||||||
|
{ id: "habitat-ring", label: "Habitat Ring", category: "habitat", summary: "Crew quarters and service modules." },
|
||||||
|
{ id: "fabricator-array", label: "Fabricator Array", category: "production", summary: "Assembly lines for manufactured goods." },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const shipDefinitions: ShipDefinition[] = [
|
||||||
|
{
|
||||||
|
id: "frigate",
|
||||||
|
label: "Vanguard Frigate",
|
||||||
|
role: "military",
|
||||||
|
speed: 50,
|
||||||
|
ftlSpeed: 3200,
|
||||||
|
spoolTime: 2.2,
|
||||||
|
cargoCapacity: 0,
|
||||||
|
color: 0x7ed4ff,
|
||||||
|
hullColor: 0x1f4f78,
|
||||||
|
size: 4,
|
||||||
|
maxHealth: 100,
|
||||||
|
modules: ["command-bridge", "ion-drive", "ftl-core", "turret-grid"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "destroyer",
|
||||||
|
label: "Bulwark Destroyer",
|
||||||
|
role: "military",
|
||||||
|
speed: 34,
|
||||||
|
ftlSpeed: 2900,
|
||||||
|
spoolTime: 2.8,
|
||||||
|
cargoCapacity: 0,
|
||||||
|
color: 0xff8f70,
|
||||||
|
hullColor: 0x6a2e26,
|
||||||
|
size: 7,
|
||||||
|
maxHealth: 240,
|
||||||
|
modules: ["command-bridge", "ion-drive", "ftl-core", "turret-grid", "turret-grid"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "hauler",
|
||||||
|
label: "Atlas Hauler",
|
||||||
|
role: "transport",
|
||||||
|
speed: 22,
|
||||||
|
ftlSpeed: 2600,
|
||||||
|
spoolTime: 3.3,
|
||||||
|
cargoCapacity: 180,
|
||||||
|
cargoKind: "container",
|
||||||
|
cargoItemId: "drone-parts",
|
||||||
|
color: 0xb0ff8d,
|
||||||
|
hullColor: 0x365f2a,
|
||||||
|
size: 8,
|
||||||
|
maxHealth: 180,
|
||||||
|
modules: ["command-bridge", "ion-drive", "ftl-core", "container-bay", "docking-clamps"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "miner",
|
||||||
|
label: "Prospector Miner",
|
||||||
|
role: "mining",
|
||||||
|
speed: 26,
|
||||||
|
ftlSpeed: 2400,
|
||||||
|
spoolTime: 3.1,
|
||||||
|
cargoCapacity: 120,
|
||||||
|
cargoKind: "bulk-solid",
|
||||||
|
cargoItemId: "ore",
|
||||||
|
color: 0xffdd75,
|
||||||
|
hullColor: 0x68552b,
|
||||||
|
size: 6,
|
||||||
|
maxHealth: 150,
|
||||||
|
modules: ["command-bridge", "ion-drive", "ftl-core", "strip-miner", "bulk-bay", "docking-clamps"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const constructibleDefinitions: ConstructibleDefinition[] = [
|
||||||
|
{
|
||||||
|
id: "trade-hub",
|
||||||
|
label: "Trade Hub",
|
||||||
|
category: "station",
|
||||||
|
color: 0x8bd3ff,
|
||||||
|
radius: 20,
|
||||||
|
dockingCapacity: 4,
|
||||||
|
storage: { container: 1200, manufactured: 800 },
|
||||||
|
modules: ["habitat-ring", "docking-clamps", "container-bay"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "refinery",
|
||||||
|
label: "Refining Station",
|
||||||
|
category: "refining",
|
||||||
|
color: 0xffb86c,
|
||||||
|
radius: 24,
|
||||||
|
dockingCapacity: 3,
|
||||||
|
storage: { "bulk-solid": 2000, manufactured: 1000 },
|
||||||
|
modules: ["docking-clamps", "refinery-stack", "bulk-bay", "fabricator-array"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "farm-ring",
|
||||||
|
label: "Farm Station",
|
||||||
|
category: "farm",
|
||||||
|
color: 0x92ef8a,
|
||||||
|
radius: 22,
|
||||||
|
dockingCapacity: 2,
|
||||||
|
storage: { "bulk-liquid": 600, container: 400 },
|
||||||
|
modules: ["habitat-ring", "production", "container-bay"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "shipyard",
|
||||||
|
label: "Orbital Shipyard",
|
||||||
|
category: "shipyard",
|
||||||
|
color: 0xd0a2ff,
|
||||||
|
radius: 28,
|
||||||
|
dockingCapacity: 5,
|
||||||
|
storage: { manufactured: 1800, container: 1200 },
|
||||||
|
modules: ["docking-clamps", "fabricator-array", "habitat-ring"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "defense-grid",
|
||||||
|
label: "Defense Platform",
|
||||||
|
category: "defense",
|
||||||
|
color: 0xff7a95,
|
||||||
|
radius: 18,
|
||||||
|
dockingCapacity: 1,
|
||||||
|
storage: { manufactured: 300 },
|
||||||
|
modules: ["turret-grid", "command-bridge"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const solarSystemDefinitions: SolarSystemDefinition[] = [
|
||||||
|
{
|
||||||
|
id: "helios",
|
||||||
|
label: "Helios Reach",
|
||||||
|
position: [0, 0, 0],
|
||||||
|
starColor: 0xffd27a,
|
||||||
|
starGlow: 0xffb14a,
|
||||||
|
starSize: 56,
|
||||||
|
gravityWellRadius: 210,
|
||||||
|
planets: [
|
||||||
|
{ label: "Icarus", orbitRadius: 180, orbitSpeed: 0.18, size: 20, color: 0xd4a373, tilt: 0.2 },
|
||||||
|
{ label: "Viridia", orbitRadius: 300, orbitSpeed: 0.11, size: 30, color: 0x58a36c, tilt: -0.4 },
|
||||||
|
{ label: "Aster", orbitRadius: 460, orbitSpeed: 0.08, size: 38, color: 0x6ea7d4, tilt: 0.3, hasRing: true },
|
||||||
|
{ label: "Noctis", orbitRadius: 670, orbitSpeed: 0.05, size: 50, color: 0x6958a8, tilt: -0.15 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "perseus",
|
||||||
|
label: "Perseus Gate",
|
||||||
|
position: [4200, 0, 600],
|
||||||
|
starColor: 0x9fd4ff,
|
||||||
|
starGlow: 0x66b6ff,
|
||||||
|
starSize: 48,
|
||||||
|
gravityWellRadius: 190,
|
||||||
|
planets: [
|
||||||
|
{ label: "Kepler", orbitRadius: 150, orbitSpeed: 0.22, size: 16, color: 0xd9b188, tilt: 0.12 },
|
||||||
|
{ label: "Tethys", orbitRadius: 280, orbitSpeed: 0.12, size: 28, color: 0x73b0a1, tilt: -0.22 },
|
||||||
|
{ label: "Orpheon", orbitRadius: 430, orbitSpeed: 0.07, size: 42, color: 0x4a67a8, tilt: 0.25, hasRing: true },
|
||||||
|
{ label: "Cinder", orbitRadius: 610, orbitSpeed: 0.045, size: 36, color: 0xb15e49, tilt: -0.08 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
11
src/main.ts
Normal file
11
src/main.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import "./style.css";
|
||||||
|
import { GameApp } from "./game/GameApp";
|
||||||
|
|
||||||
|
const appRoot = document.querySelector<HTMLDivElement>("#app");
|
||||||
|
|
||||||
|
if (!appRoot) {
|
||||||
|
throw new Error("Missing #app root element");
|
||||||
|
}
|
||||||
|
|
||||||
|
const game = new GameApp(appRoot);
|
||||||
|
game.start();
|
||||||
245
src/style.css
Normal file
245
src/style.css
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
font-family: "Space Grotesk", "Segoe UI", sans-serif;
|
||||||
|
--bg: #050914;
|
||||||
|
--panel: rgba(5, 12, 26, 0.78);
|
||||||
|
--panel-border: rgba(126, 212, 255, 0.18);
|
||||||
|
--text: #ebf7ff;
|
||||||
|
--muted: #9fb6c8;
|
||||||
|
--accent: #7ed4ff;
|
||||||
|
--warning: #ffbf69;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top, rgba(75, 123, 236, 0.18), transparent 36%),
|
||||||
|
radial-gradient(circle at 20% 40%, rgba(255, 134, 91, 0.14), transparent 26%),
|
||||||
|
linear-gradient(180deg, #03070f 0%, #060c18 100%);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hud {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.strategic-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.96;
|
||||||
|
}
|
||||||
|
|
||||||
|
.marquee {
|
||||||
|
position: fixed;
|
||||||
|
display: none;
|
||||||
|
border: 1px solid rgba(126, 212, 255, 0.85);
|
||||||
|
background: rgba(126, 212, 255, 0.14);
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: absolute;
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--panel-border);
|
||||||
|
border-radius: 18px;
|
||||||
|
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel h1,
|
||||||
|
.panel h2,
|
||||||
|
.panel p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
top: 24px;
|
||||||
|
left: 24px;
|
||||||
|
width: min(380px, calc(100vw - 48px));
|
||||||
|
padding: 18px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary h1 {
|
||||||
|
font-size: 1rem;
|
||||||
|
letter-spacing: 0.22em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary p {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
right: 24px;
|
||||||
|
top: 24px;
|
||||||
|
width: min(320px, calc(100vw - 48px));
|
||||||
|
padding: 18px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details h2 {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .content {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.55;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commandbar {
|
||||||
|
left: 24px;
|
||||||
|
right: 24px;
|
||||||
|
bottom: 24px;
|
||||||
|
min-height: 180px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(240px, 300px) 1fr minmax(220px, 260px);
|
||||||
|
gap: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
align-items: stretch;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection-panel,
|
||||||
|
.orders-panel,
|
||||||
|
.minimap-panel {
|
||||||
|
border: 1px solid rgba(126, 212, 255, 0.14);
|
||||||
|
border-radius: 14px;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(7, 15, 29, 0.82), rgba(4, 10, 20, 0.72)),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(126, 212, 255, 0.025) 0,
|
||||||
|
rgba(126, 212, 255, 0.025) 1px,
|
||||||
|
transparent 1px,
|
||||||
|
transparent 16px
|
||||||
|
);
|
||||||
|
padding: 14px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection-title,
|
||||||
|
.orders-panel .mode {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection-panel .compact {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.45;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-panel .mode {
|
||||||
|
color: var(--warning);
|
||||||
|
text-shadow: 0 0 18px rgba(255, 191, 105, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders button {
|
||||||
|
border: 1px solid rgba(126, 212, 255, 0.16);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: linear-gradient(180deg, rgba(13, 30, 56, 0.95), rgba(8, 17, 33, 0.95));
|
||||||
|
color: var(--text);
|
||||||
|
font: inherit;
|
||||||
|
padding: 12px 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 120ms ease, transform 120ms ease, background 120ms ease;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders button:hover {
|
||||||
|
border-color: rgba(126, 212, 255, 0.4);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders[data-mode="none"] button:not([data-action="focus"]) {
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-panel .hint {
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimap-panel {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimap {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(126, 212, 255, 0.16);
|
||||||
|
background: rgba(2, 6, 13, 0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.summary,
|
||||||
|
.details,
|
||||||
|
.commandbar {
|
||||||
|
left: 16px;
|
||||||
|
right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary,
|
||||||
|
.details {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
top: auto;
|
||||||
|
bottom: 92px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commandbar {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"lib": ["ES2022", "DOM"],
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
17
vite.config.ts
Normal file
17
vite.config.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
three: ["three"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 4173,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user