@where-stars-drift/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +143 -0
- package/dist/src/base/bounded-body.d.ts +8 -0
- package/dist/src/base/bounded-body.js +10 -0
- package/dist/src/base/celestial-body.d.ts +12 -0
- package/dist/src/base/celestial-body.js +11 -0
- package/dist/src/base/hoverable.d.ts +10 -0
- package/dist/src/base/hoverable.js +4 -0
- package/dist/src/base/massive-body.d.ts +8 -0
- package/dist/src/base/massive-body.js +10 -0
- package/dist/src/config/effects.d.ts +14 -0
- package/dist/src/config/effects.js +14 -0
- package/dist/src/config/grid.d.ts +10 -0
- package/dist/src/config/grid.js +10 -0
- package/dist/src/config/panel.d.ts +26 -0
- package/dist/src/config/panel.js +26 -0
- package/dist/src/config/simulation.d.ts +21 -0
- package/dist/src/config/simulation.js +23 -0
- package/dist/src/controllers/clan-controller.d.ts +7 -0
- package/dist/src/controllers/clan-controller.js +12 -0
- package/dist/src/controllers/debug-controller.d.ts +45 -0
- package/dist/src/controllers/debug-controller.js +82 -0
- package/dist/src/controllers/effects-controller.d.ts +17 -0
- package/dist/src/controllers/effects-controller.js +71 -0
- package/dist/src/controllers/hover-controller.d.ts +11 -0
- package/dist/src/controllers/hover-controller.js +107 -0
- package/dist/src/controllers/layer-controller.d.ts +16 -0
- package/dist/src/controllers/layer-controller.js +64 -0
- package/dist/src/controllers/physics-controller.d.ts +14 -0
- package/dist/src/controllers/physics-controller.js +152 -0
- package/dist/src/controllers/star-controller.d.ts +12 -0
- package/dist/src/controllers/star-controller.js +38 -0
- package/dist/src/controllers/starship-controller.d.ts +17 -0
- package/dist/src/controllers/starship-controller.js +58 -0
- package/dist/src/draw-debug-line.d.ts +9 -0
- package/dist/src/draw-debug-line.js +29 -0
- package/dist/src/entities/black-hole-factory.d.ts +15 -0
- package/dist/src/entities/black-hole-factory.js +23 -0
- package/dist/src/entities/black-hole-shapes.d.ts +9 -0
- package/dist/src/entities/black-hole-shapes.js +224 -0
- package/dist/src/entities/black-hole.d.ts +69 -0
- package/dist/src/entities/black-hole.js +210 -0
- package/dist/src/entities/clan-manager.d.ts +12 -0
- package/dist/src/entities/clan-manager.js +22 -0
- package/dist/src/entities/clans.d.ts +15 -0
- package/dist/src/entities/clans.js +76 -0
- package/dist/src/entities/comet.d.ts +27 -0
- package/dist/src/entities/comet.js +81 -0
- package/dist/src/entities/docking-point.d.ts +20 -0
- package/dist/src/entities/docking-point.js +22 -0
- package/dist/src/entities/fleet.d.ts +45 -0
- package/dist/src/entities/fleet.js +374 -0
- package/dist/src/entities/formations.d.ts +51 -0
- package/dist/src/entities/formations.js +340 -0
- package/dist/src/entities/meteor.d.ts +26 -0
- package/dist/src/entities/meteor.js +48 -0
- package/dist/src/entities/nebula.d.ts +18 -0
- package/dist/src/entities/nebula.js +43 -0
- package/dist/src/entities/orbit.d.ts +23 -0
- package/dist/src/entities/orbit.js +43 -0
- package/dist/src/entities/pulsar.d.ts +18 -0
- package/dist/src/entities/pulsar.js +41 -0
- package/dist/src/entities/ring.d.ts +13 -0
- package/dist/src/entities/ring.js +26 -0
- package/dist/src/entities/ringed-planet.d.ts +21 -0
- package/dist/src/entities/ringed-planet.js +68 -0
- package/dist/src/entities/sector-grid.d.ts +16 -0
- package/dist/src/entities/sector-grid.js +70 -0
- package/dist/src/entities/star-factory.d.ts +29 -0
- package/dist/src/entities/star-factory.js +47 -0
- package/dist/src/entities/star.d.ts +48 -0
- package/dist/src/entities/star.js +167 -0
- package/dist/src/entities/starship-classes.d.ts +0 -0
- package/dist/src/entities/starship-classes.js +2 -0
- package/dist/src/entities/starship.d.ts +91 -0
- package/dist/src/entities/starship.js +760 -0
- package/dist/src/entities/supernova.d.ts +26 -0
- package/dist/src/entities/supernova.js +54 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +19 -0
- package/dist/src/lib/energy-stream.d.ts +5 -0
- package/dist/src/lib/energy-stream.js +98 -0
- package/dist/src/lib/quadtree.d.ts +31 -0
- package/dist/src/lib/quadtree.js +124 -0
- package/dist/src/lib/simplified-stream.d.ts +6 -0
- package/dist/src/lib/simplified-stream.js +19 -0
- package/dist/src/types.d.ts +14 -0
- package/dist/src/types.js +1 -0
- package/dist/src/ui/black-holes-panel.d.ts +2 -0
- package/dist/src/ui/black-holes-panel.js +76 -0
- package/dist/src/ui/clans-panel.d.ts +2 -0
- package/dist/src/ui/clans-panel.js +20 -0
- package/dist/src/ui/debug-panel-controller.d.ts +41 -0
- package/dist/src/ui/debug-panel-controller.js +285 -0
- package/dist/src/ui/fleets-panel.d.ts +2 -0
- package/dist/src/ui/fleets-panel.js +127 -0
- package/dist/src/ui/formations-panel.d.ts +3 -0
- package/dist/src/ui/formations-panel.js +129 -0
- package/dist/src/ui/panel-config.d.ts +26 -0
- package/dist/src/ui/panel-config.js +26 -0
- package/dist/src/ui/ships-panel.d.ts +12 -0
- package/dist/src/ui/ships-panel.js +61 -0
- package/dist/src/ui/stars-panel.d.ts +2 -0
- package/dist/src/ui/stars-panel.js +120 -0
- package/dist/src/where-stars-drift.d.ts +71 -0
- package/dist/src/where-stars-drift.js +440 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +35 -0
- package/src/base/bounded-body.ts +14 -0
- package/src/base/celestial-body.ts +20 -0
- package/src/base/hoverable.ts +11 -0
- package/src/base/massive-body.ts +14 -0
- package/src/config/effects.ts +15 -0
- package/src/config/grid.ts +11 -0
- package/src/config/panel.ts +26 -0
- package/src/config/simulation.ts +25 -0
- package/src/controllers/clan-controller.ts +19 -0
- package/src/controllers/debug-controller.ts +112 -0
- package/src/controllers/effects-controller.ts +86 -0
- package/src/controllers/hover-controller.ts +128 -0
- package/src/controllers/layer-controller.ts +78 -0
- package/src/controllers/physics-controller.ts +173 -0
- package/src/controllers/star-controller.ts +51 -0
- package/src/controllers/starship-controller.ts +76 -0
- package/src/draw-debug-line.ts +37 -0
- package/src/entities/black-hole-factory.ts +28 -0
- package/src/entities/black-hole-shapes.ts +276 -0
- package/src/entities/black-hole.ts +246 -0
- package/src/entities/clan-manager.ts +33 -0
- package/src/entities/clans.ts +98 -0
- package/src/entities/comet.ts +102 -0
- package/src/entities/docking-point.ts +34 -0
- package/src/entities/fleet.ts +446 -0
- package/src/entities/formations.ts +423 -0
- package/src/entities/meteor.ts +59 -0
- package/src/entities/nebula.ts +50 -0
- package/src/entities/orbit.ts +53 -0
- package/src/entities/pulsar.ts +64 -0
- package/src/entities/ring.ts +42 -0
- package/src/entities/ringed-planet.ts +85 -0
- package/src/entities/sector-grid.ts +81 -0
- package/src/entities/star-factory.ts +59 -0
- package/src/entities/star.ts +222 -0
- package/src/entities/starship-classes.ts +1 -0
- package/src/entities/starship.ts +906 -0
- package/src/entities/supernova.ts +75 -0
- package/src/index.ts +24 -0
- package/src/lib/energy-stream.ts +127 -0
- package/src/lib/quadtree.ts +159 -0
- package/src/lib/simplified-stream.ts +28 -0
- package/src/types.ts +16 -0
- package/src/ui/black-holes-panel.ts +91 -0
- package/src/ui/clans-panel.ts +27 -0
- package/src/ui/debug-panel-controller.ts +339 -0
- package/src/ui/fleets-panel.ts +153 -0
- package/src/ui/formations-panel.ts +155 -0
- package/src/ui/panel-config.ts +26 -0
- package/src/ui/ships-panel.ts +85 -0
- package/src/ui/stars-panel.ts +146 -0
- package/src/where-stars-drift.ts +542 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Supernova class for a visual pulse effect.
|
|
3
|
+
*/
|
|
4
|
+
import type { BlackHle } from './black-hole';
|
|
5
|
+
export declare const SUPERNOVA_CONFIG: {
|
|
6
|
+
MAX_LIFE: number;
|
|
7
|
+
RADIUS_MULTIPLIER: number;
|
|
8
|
+
INITIAL_RADIUS_MULTIPLIER: number;
|
|
9
|
+
COLOR: string;
|
|
10
|
+
SHADOW_BLUR: number;
|
|
11
|
+
};
|
|
12
|
+
export declare class Supernova {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
radius: number;
|
|
16
|
+
maxRadius: number;
|
|
17
|
+
life: number;
|
|
18
|
+
maxLife: number;
|
|
19
|
+
color: string;
|
|
20
|
+
initialRadius: number;
|
|
21
|
+
parent: BlackHle | null;
|
|
22
|
+
constructor(x: number, y: number, parentRadius: number, parent?: BlackHle | null);
|
|
23
|
+
isAlive(): boolean;
|
|
24
|
+
update(): void;
|
|
25
|
+
draw(ctx: CanvasRenderingContext2D): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Supernova class for a visual pulse effect.
|
|
3
|
+
*/
|
|
4
|
+
// --- Supernova Configuration ---
|
|
5
|
+
export const SUPERNOVA_CONFIG = {
|
|
6
|
+
MAX_LIFE: 60, // 1 second in frames
|
|
7
|
+
RADIUS_MULTIPLIER: 2.5,
|
|
8
|
+
INITIAL_RADIUS_MULTIPLIER: 0.2,
|
|
9
|
+
COLOR: 'rgba(255, 255, 255, 0.8)',
|
|
10
|
+
SHADOW_BLUR: 30,
|
|
11
|
+
};
|
|
12
|
+
export class Supernova {
|
|
13
|
+
constructor(x, y, parentRadius, parent = null) {
|
|
14
|
+
this.x = x;
|
|
15
|
+
this.y = y;
|
|
16
|
+
this.initialRadius = parentRadius * SUPERNOVA_CONFIG.INITIAL_RADIUS_MULTIPLIER;
|
|
17
|
+
this.radius = this.initialRadius;
|
|
18
|
+
this.maxRadius = parentRadius * SUPERNOVA_CONFIG.RADIUS_MULTIPLIER;
|
|
19
|
+
this.maxLife = SUPERNOVA_CONFIG.MAX_LIFE;
|
|
20
|
+
this.life = this.maxLife;
|
|
21
|
+
this.color = SUPERNOVA_CONFIG.COLOR;
|
|
22
|
+
this.parent = parent;
|
|
23
|
+
}
|
|
24
|
+
isAlive() {
|
|
25
|
+
return this.life > 0;
|
|
26
|
+
}
|
|
27
|
+
update() {
|
|
28
|
+
this.life--;
|
|
29
|
+
if (this.parent) {
|
|
30
|
+
this.x = this.parent.x;
|
|
31
|
+
this.y = this.parent.y;
|
|
32
|
+
}
|
|
33
|
+
const progress = (this.maxLife - this.life) / this.maxLife; // 0 to 1
|
|
34
|
+
// Ease-out cubic function for radius expansion
|
|
35
|
+
this.radius = this.initialRadius + (this.maxRadius - this.initialRadius) * (1 - Math.pow(1 - progress, 3));
|
|
36
|
+
}
|
|
37
|
+
draw(ctx) {
|
|
38
|
+
const progress = this.life / this.maxLife; // 1 to 0
|
|
39
|
+
const alpha = progress * 0.8; // Fade out
|
|
40
|
+
ctx.save();
|
|
41
|
+
ctx.globalAlpha = alpha;
|
|
42
|
+
ctx.shadowColor = this.color;
|
|
43
|
+
ctx.shadowBlur = SUPERNOVA_CONFIG.SHADOW_BLUR;
|
|
44
|
+
const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius);
|
|
45
|
+
gradient.addColorStop(0, 'rgba(255, 255, 255, 0)');
|
|
46
|
+
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)');
|
|
47
|
+
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
|
48
|
+
ctx.fillStyle = gradient;
|
|
49
|
+
ctx.beginPath();
|
|
50
|
+
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
|
51
|
+
ctx.fill();
|
|
52
|
+
ctx.restore();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { WhereStarsDrift, type SimulationConfig, type SimulationStats } from './where-stars-drift';
|
|
2
|
+
export { Star } from './entities/star';
|
|
3
|
+
export { Pulsar } from './entities/pulsar';
|
|
4
|
+
export { RingedPlanet } from './entities/ringed-planet';
|
|
5
|
+
export { BlackHole, BlackHle, type BlackHoleType } from './entities/black-hole';
|
|
6
|
+
export { Starship, type ShipState, type ShipClass } from './entities/starship';
|
|
7
|
+
export { Fleet } from './entities/fleet';
|
|
8
|
+
export { type Clan } from './entities/clans';
|
|
9
|
+
export { formationRegistry, type Formation, type StrictFormation, type FreeFormation } from './entities/formations';
|
|
10
|
+
export { Comet } from './entities/comet';
|
|
11
|
+
export { Nebula } from './entities/nebula';
|
|
12
|
+
export { Supernova } from './entities/supernova';
|
|
13
|
+
export { SectorGrid } from './entities/sector-grid';
|
|
14
|
+
export { LayerController, type Layer } from './controllers/layer-controller';
|
|
15
|
+
export { PhysicsController } from './controllers/physics-controller';
|
|
16
|
+
export { StarshipController } from './controllers/starship-controller';
|
|
17
|
+
export { StarController } from './controllers/star-controller';
|
|
18
|
+
export { EffectsController } from './controllers/effects-controller';
|
|
19
|
+
export { SIMULATION_CONFIG, DEBUG_CONFIG } from './config/simulation';
|
|
20
|
+
export { GRID_CONFIG } from './config/grid';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { WhereStarsDrift } from './where-stars-drift';
|
|
2
|
+
export { Star } from './entities/star';
|
|
3
|
+
export { Pulsar } from './entities/pulsar';
|
|
4
|
+
export { RingedPlanet } from './entities/ringed-planet';
|
|
5
|
+
export { BlackHole, BlackHle } from './entities/black-hole';
|
|
6
|
+
export { Starship } from './entities/starship';
|
|
7
|
+
export { Fleet } from './entities/fleet';
|
|
8
|
+
export { formationRegistry } from './entities/formations';
|
|
9
|
+
export { Comet } from './entities/comet';
|
|
10
|
+
export { Nebula } from './entities/nebula';
|
|
11
|
+
export { Supernova } from './entities/supernova';
|
|
12
|
+
export { SectorGrid } from './entities/sector-grid';
|
|
13
|
+
export { LayerController } from './controllers/layer-controller';
|
|
14
|
+
export { PhysicsController } from './controllers/physics-controller';
|
|
15
|
+
export { StarshipController } from './controllers/starship-controller';
|
|
16
|
+
export { StarController } from './controllers/star-controller';
|
|
17
|
+
export { EffectsController } from './controllers/effects-controller';
|
|
18
|
+
export { SIMULATION_CONFIG, DEBUG_CONFIG } from './config/simulation';
|
|
19
|
+
export { GRID_CONFIG } from './config/grid';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the energy stream rendering function.
|
|
3
|
+
*/
|
|
4
|
+
import type { BlackHle } from '../entities/black-hole';
|
|
5
|
+
export declare const drawEnergyStream: (ctx: CanvasRenderingContext2D, hole: BlackHle, target: BlackHle, pulseTime: number, frameCount: number) => void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the energy stream rendering function.
|
|
3
|
+
*/
|
|
4
|
+
import { BLACK_HOLE_CONFIG } from '../entities/black-hole';
|
|
5
|
+
export const drawEnergyStream = (ctx, hole, target, pulseTime, frameCount) => {
|
|
6
|
+
const dx = target.x - hole.x;
|
|
7
|
+
const dy = target.y - hole.y;
|
|
8
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
9
|
+
const angle = Math.atan2(dy, dx);
|
|
10
|
+
const perpAngle = angle + Math.PI / 2;
|
|
11
|
+
let glowColor;
|
|
12
|
+
let baseR, baseG, baseB;
|
|
13
|
+
switch (hole.type) {
|
|
14
|
+
case 'VORTEX':
|
|
15
|
+
glowColor = BLACK_HOLE_CONFIG.VORTEX.VORTEX_COLOR_INNER;
|
|
16
|
+
[baseR, baseG, baseB] = [255, 100, 0];
|
|
17
|
+
break;
|
|
18
|
+
case 'WARPED_DISK':
|
|
19
|
+
glowColor = BLACK_HOLE_CONFIG.WARPED_DISK.DISK_COLOR_INNER;
|
|
20
|
+
[baseR, baseG, baseB] = [255, 220, 150];
|
|
21
|
+
break;
|
|
22
|
+
case 'ACCRETION_DISK':
|
|
23
|
+
glowColor = `rgba(200, 220, 255, 0.8)`;
|
|
24
|
+
[baseR, baseG, baseB] = [200, 220, 255];
|
|
25
|
+
break;
|
|
26
|
+
case 'LENSING':
|
|
27
|
+
default:
|
|
28
|
+
glowColor = `rgba(255, 255, 255, 0.7)`;
|
|
29
|
+
[baseR, baseG, baseB] = [255, 255, 255];
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
const pulse = (Math.sin(pulseTime * hole.pulseFrequency + hole.pulsePhase) + 1) / 2; // 0 to 1
|
|
33
|
+
const startX = hole.x;
|
|
34
|
+
const startY = hole.y;
|
|
35
|
+
ctx.save();
|
|
36
|
+
ctx.globalCompositeOperation = 'lighter';
|
|
37
|
+
ctx.lineCap = 'round';
|
|
38
|
+
// --- Emission Nozzle ---
|
|
39
|
+
if (frameCount % 5 === 0) {
|
|
40
|
+
const nozzleLengthMultiplier = Math.random() * 7 + 3;
|
|
41
|
+
const potentialNozzleLength = hole.radius * nozzleLengthMultiplier;
|
|
42
|
+
hole.nozzleLength = Math.min(potentialNozzleLength, (4 / 5) * dist);
|
|
43
|
+
const nozzleTipX = startX + Math.cos(angle) * hole.nozzleLength;
|
|
44
|
+
const nozzleTipY = startY + Math.sin(angle) * hole.nozzleLength;
|
|
45
|
+
const nozzleGradient = ctx.createLinearGradient(startX, startY, nozzleTipX, nozzleTipY);
|
|
46
|
+
nozzleGradient.addColorStop(0, `rgba(${baseR}, ${baseG}, ${baseB}, ${0.1 + pulse * 0.2})`);
|
|
47
|
+
nozzleGradient.addColorStop(1, `rgba(${baseR}, ${baseG}, ${baseB}, 0)`);
|
|
48
|
+
hole.nozzleColor = nozzleGradient;
|
|
49
|
+
}
|
|
50
|
+
if (hole.nozzleLength > 0 && hole.nozzleColor) {
|
|
51
|
+
const nozzleTipX = startX + Math.cos(angle) * hole.nozzleLength;
|
|
52
|
+
const nozzleTipY = startY + Math.sin(angle) * hole.nozzleLength;
|
|
53
|
+
const nozzleBaseWidth = hole.radius * 2;
|
|
54
|
+
ctx.beginPath();
|
|
55
|
+
ctx.moveTo(nozzleTipX, nozzleTipY);
|
|
56
|
+
ctx.lineTo(startX + Math.cos(perpAngle) * nozzleBaseWidth / 2, startY + Math.sin(perpAngle) * nozzleBaseWidth / 2);
|
|
57
|
+
ctx.lineTo(startX - Math.cos(perpAngle) * nozzleBaseWidth / 2, startY - Math.sin(perpAngle) * nozzleBaseWidth / 2);
|
|
58
|
+
ctx.closePath();
|
|
59
|
+
ctx.fillStyle = hole.nozzleColor;
|
|
60
|
+
ctx.shadowColor = glowColor;
|
|
61
|
+
ctx.shadowBlur = 30;
|
|
62
|
+
ctx.fill();
|
|
63
|
+
}
|
|
64
|
+
// --- Main Stream ---
|
|
65
|
+
const endX = startX + dx;
|
|
66
|
+
const endY = startY + dy;
|
|
67
|
+
const minThicknessFactor = 0.3;
|
|
68
|
+
const maxThicknessFactor = 3.5;
|
|
69
|
+
const distanceFactor = Math.max(0, 1 - dist / BLACK_HOLE_CONFIG.ENERGY_STREAM_DISTANCE);
|
|
70
|
+
const thicknessMultiplier = minThicknessFactor + (maxThicknessFactor - minThicknessFactor) * distanceFactor;
|
|
71
|
+
// --- Outer Glow ---
|
|
72
|
+
const outerGlowAlpha = 0.2 + pulse * 0.3;
|
|
73
|
+
ctx.shadowColor = glowColor;
|
|
74
|
+
ctx.shadowBlur = 25;
|
|
75
|
+
const gradientOuter = ctx.createLinearGradient(startX, startY, endX, endY);
|
|
76
|
+
gradientOuter.addColorStop(0, `rgba(${baseR}, ${baseG}, ${baseB}, ${outerGlowAlpha * 0.7})`);
|
|
77
|
+
gradientOuter.addColorStop(0.95, `rgba(${baseR}, ${baseG}, ${baseB}, 0)`);
|
|
78
|
+
ctx.strokeStyle = gradientOuter;
|
|
79
|
+
ctx.lineWidth = hole.radius * (0.2 + pulse * 0.15) * thicknessMultiplier;
|
|
80
|
+
ctx.beginPath();
|
|
81
|
+
ctx.moveTo(startX, startY);
|
|
82
|
+
ctx.lineTo(endX, endY);
|
|
83
|
+
ctx.stroke();
|
|
84
|
+
// --- Inner Core ---
|
|
85
|
+
const innerGlowAlpha = 0.4 + pulse * 0.6;
|
|
86
|
+
ctx.shadowColor = `rgba(${baseR}, ${baseG}, ${baseB}, ${0.5 + pulse * 0.5})`;
|
|
87
|
+
ctx.shadowBlur = 12;
|
|
88
|
+
const gradientInner = ctx.createLinearGradient(startX, startY, endX, endY);
|
|
89
|
+
gradientInner.addColorStop(0, `rgba(${baseR}, ${baseG}, ${baseB}, ${innerGlowAlpha})`);
|
|
90
|
+
gradientInner.addColorStop(0.95, `rgba(${baseR}, ${baseG}, ${baseB}, 0)`);
|
|
91
|
+
ctx.strokeStyle = gradientInner;
|
|
92
|
+
ctx.lineWidth = hole.radius * 0.1 * thicknessMultiplier;
|
|
93
|
+
ctx.beginPath();
|
|
94
|
+
ctx.moveTo(startX, startY);
|
|
95
|
+
ctx.lineTo(endX, endY);
|
|
96
|
+
ctx.stroke();
|
|
97
|
+
ctx.restore();
|
|
98
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Quadtree class for spatial partitioning.
|
|
3
|
+
*/
|
|
4
|
+
import type { BoundedBody } from '../base/bounded-body';
|
|
5
|
+
export declare const QUADTREE_CONFIG: {
|
|
6
|
+
MAX_OBJECTS: number;
|
|
7
|
+
MAX_LEVELS: number;
|
|
8
|
+
};
|
|
9
|
+
interface Rectangle {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class Quadtree {
|
|
16
|
+
private maxObjects;
|
|
17
|
+
private maxLevels;
|
|
18
|
+
private level;
|
|
19
|
+
private bounds;
|
|
20
|
+
private objects;
|
|
21
|
+
private nodes;
|
|
22
|
+
constructor(bounds: Rectangle, level?: number);
|
|
23
|
+
clear(): void;
|
|
24
|
+
private split;
|
|
25
|
+
private getIndex;
|
|
26
|
+
insert(body: BoundedBody): void;
|
|
27
|
+
query(range: BoundedBody): BoundedBody[];
|
|
28
|
+
private intersects;
|
|
29
|
+
draw(ctx: CanvasRenderingContext2D): void;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// --- Quadtree Configuration ---
|
|
2
|
+
export const QUADTREE_CONFIG = {
|
|
3
|
+
MAX_OBJECTS: 10,
|
|
4
|
+
MAX_LEVELS: 5,
|
|
5
|
+
};
|
|
6
|
+
export class Quadtree {
|
|
7
|
+
constructor(bounds, level = 0) {
|
|
8
|
+
this.maxObjects = QUADTREE_CONFIG.MAX_OBJECTS;
|
|
9
|
+
this.maxLevels = QUADTREE_CONFIG.MAX_LEVELS;
|
|
10
|
+
this.level = level;
|
|
11
|
+
this.bounds = bounds;
|
|
12
|
+
this.objects = [];
|
|
13
|
+
this.nodes = [];
|
|
14
|
+
}
|
|
15
|
+
clear() {
|
|
16
|
+
this.objects = [];
|
|
17
|
+
this.nodes = [];
|
|
18
|
+
}
|
|
19
|
+
split() {
|
|
20
|
+
const nextLevel = this.level + 1;
|
|
21
|
+
const subWidth = this.bounds.width / 2;
|
|
22
|
+
const subHeight = this.bounds.height / 2;
|
|
23
|
+
const x = this.bounds.x;
|
|
24
|
+
const y = this.bounds.y;
|
|
25
|
+
// Top right
|
|
26
|
+
this.nodes[0] = new Quadtree({ x: x + subWidth, y: y, width: subWidth, height: subHeight }, nextLevel);
|
|
27
|
+
// Top left
|
|
28
|
+
this.nodes[1] = new Quadtree({ x: x, y: y, width: subWidth, height: subHeight }, nextLevel);
|
|
29
|
+
// Bottom left
|
|
30
|
+
this.nodes[2] = new Quadtree({ x: x, y: y + subHeight, width: subWidth, height: subHeight }, nextLevel);
|
|
31
|
+
// Bottom right
|
|
32
|
+
this.nodes[3] = new Quadtree({ x: x + subWidth, y: y + subHeight, width: subWidth, height: subHeight }, nextLevel);
|
|
33
|
+
}
|
|
34
|
+
getIndex(body) {
|
|
35
|
+
let index = -1;
|
|
36
|
+
const verticalMidpoint = this.bounds.x + (this.bounds.width / 2);
|
|
37
|
+
const horizontalMidpoint = this.bounds.y + (this.bounds.height / 2);
|
|
38
|
+
const topQuadrant = body.y < horizontalMidpoint && body.y + body.boundingRadius < horizontalMidpoint;
|
|
39
|
+
const bottomQuadrant = body.y > horizontalMidpoint;
|
|
40
|
+
if (body.x < verticalMidpoint && body.x + body.boundingRadius < verticalMidpoint) {
|
|
41
|
+
if (topQuadrant) {
|
|
42
|
+
index = 1; // Top left
|
|
43
|
+
}
|
|
44
|
+
else if (bottomQuadrant) {
|
|
45
|
+
index = 2; // Bottom left
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (body.x > verticalMidpoint) {
|
|
49
|
+
if (topQuadrant) {
|
|
50
|
+
index = 0; // Top right
|
|
51
|
+
}
|
|
52
|
+
else if (bottomQuadrant) {
|
|
53
|
+
index = 3; // Bottom right
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return index;
|
|
57
|
+
}
|
|
58
|
+
insert(body) {
|
|
59
|
+
if (this.nodes.length) {
|
|
60
|
+
const index = this.getIndex(body);
|
|
61
|
+
if (index !== -1) {
|
|
62
|
+
this.nodes[index].insert(body);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
this.objects.push(body);
|
|
67
|
+
if (this.objects.length > this.maxObjects && this.level < this.maxLevels) {
|
|
68
|
+
if (!this.nodes.length) {
|
|
69
|
+
this.split();
|
|
70
|
+
}
|
|
71
|
+
let i = 0;
|
|
72
|
+
while (i < this.objects.length) {
|
|
73
|
+
const index = this.getIndex(this.objects[i]);
|
|
74
|
+
if (index !== -1) {
|
|
75
|
+
this.nodes[index].insert(this.objects.splice(i, 1)[0]);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
i++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
query(range) {
|
|
84
|
+
let found = [];
|
|
85
|
+
const { x, y, boundingRadius } = range;
|
|
86
|
+
const queryBounds = {
|
|
87
|
+
x: x - boundingRadius,
|
|
88
|
+
y: y - boundingRadius,
|
|
89
|
+
width: boundingRadius * 2,
|
|
90
|
+
height: boundingRadius * 2,
|
|
91
|
+
};
|
|
92
|
+
if (!this.intersects(this.bounds, queryBounds)) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
found = found.concat(this.objects);
|
|
96
|
+
if (this.nodes.length) {
|
|
97
|
+
const index = this.getIndex(range);
|
|
98
|
+
if (index !== -1) {
|
|
99
|
+
found = found.concat(this.nodes[index].query(range));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
103
|
+
found = found.concat(this.nodes[i].query(range));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
109
|
+
intersects(rect1, rect2) {
|
|
110
|
+
return (rect1.x < rect2.x + rect2.width &&
|
|
111
|
+
rect1.x + rect1.width > rect2.x &&
|
|
112
|
+
rect1.y < rect2.y + rect2.height &&
|
|
113
|
+
rect1.y + rect1.height > rect2.y);
|
|
114
|
+
}
|
|
115
|
+
draw(ctx) {
|
|
116
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
|
|
117
|
+
ctx.strokeRect(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height);
|
|
118
|
+
if (this.nodes.length) {
|
|
119
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
120
|
+
this.nodes[i].draw(ctx);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines a simplified, performant energy stream for star-to-hole effects.
|
|
3
|
+
*/
|
|
4
|
+
import type { Star } from '../entities/star';
|
|
5
|
+
import type { BlackHle } from '../entities/black-hole';
|
|
6
|
+
export declare const drawSimpleStarStream: (ctx: CanvasRenderingContext2D, star: Star, hole: BlackHle) => void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const drawSimpleStarStream = (ctx, star, hole) => {
|
|
2
|
+
const dx = hole.x - star.x;
|
|
3
|
+
const dy = hole.y - star.y;
|
|
4
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
5
|
+
// Fade the stream as it gets closer to the hole
|
|
6
|
+
const maxDist = hole.radius + 50;
|
|
7
|
+
const alpha = Math.max(0, 1 - (dist / maxDist)) * 0.5;
|
|
8
|
+
if (alpha <= 0)
|
|
9
|
+
return;
|
|
10
|
+
ctx.save();
|
|
11
|
+
ctx.beginPath();
|
|
12
|
+
ctx.moveTo(star.x, star.y);
|
|
13
|
+
ctx.lineTo(hole.x, hole.y);
|
|
14
|
+
ctx.strokeStyle = star.color;
|
|
15
|
+
ctx.globalAlpha = alpha;
|
|
16
|
+
ctx.lineWidth = star.radius * 0.8;
|
|
17
|
+
ctx.stroke();
|
|
18
|
+
ctx.restore();
|
|
19
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Starship } from './entities/starship';
|
|
2
|
+
import type { Formation } from './entities/formations';
|
|
3
|
+
import type { Clan } from './entities/clans';
|
|
4
|
+
import type { Fleet } from './entities/fleet';
|
|
5
|
+
import type { Star } from './entities/star';
|
|
6
|
+
import type { BlackHle } from './entities/black-hole';
|
|
7
|
+
export type CatalogData = {
|
|
8
|
+
starships: Starship[];
|
|
9
|
+
formations: Formation[];
|
|
10
|
+
clans: Clan[];
|
|
11
|
+
fleets: Fleet[];
|
|
12
|
+
stars: Star[];
|
|
13
|
+
blackHoles: BlackHle[];
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { PANEL_CONFIG } from './panel-config';
|
|
2
|
+
const DESCRIPTIONS = {
|
|
3
|
+
STANDARD: 'A simple black hole with a soft, ethereal glow.',
|
|
4
|
+
LENSING: 'Features a bright, sharp ring of light caused by gravitational lensing.',
|
|
5
|
+
ACCRETION_DISK: 'Characterized by a stable, flat disk of matter with a bright central line and relativistic jets.',
|
|
6
|
+
WARPED_DISK: 'Shows a dynamic, warped accretion disk that gives a 3D perspective.',
|
|
7
|
+
VORTEX: 'A highly active black hole with swirling arms of super-heated gas.',
|
|
8
|
+
};
|
|
9
|
+
function drawBlackHoleParameters(ctx, hole, x, y) {
|
|
10
|
+
ctx.font = 'bold 12px "Roboto Mono", monospace';
|
|
11
|
+
ctx.fillStyle = '#fff';
|
|
12
|
+
ctx.textAlign = 'left';
|
|
13
|
+
ctx.textBaseline = 'top';
|
|
14
|
+
ctx.fillText(hole.type, x, y);
|
|
15
|
+
// Draw description
|
|
16
|
+
ctx.font = '11px "Roboto Mono", monospace';
|
|
17
|
+
ctx.fillStyle = PANEL_CONFIG.ACCENT_COLOR;
|
|
18
|
+
const description = DESCRIPTIONS[hole.type] || 'A mysterious gravitational anomaly.';
|
|
19
|
+
const words = description.split(' ');
|
|
20
|
+
let line = '';
|
|
21
|
+
let currentY = y + 20; // Start description lower
|
|
22
|
+
for (let n = 0; n < words.length; n++) {
|
|
23
|
+
const testLine = line + words[n] + ' ';
|
|
24
|
+
const metrics = ctx.measureText(testLine);
|
|
25
|
+
if (metrics.width > 200 && n > 0) { // Wrap text
|
|
26
|
+
ctx.fillText(line, x, currentY);
|
|
27
|
+
line = words[n] + ' ';
|
|
28
|
+
currentY += 12;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
line = testLine;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
ctx.fillText(line, x, currentY);
|
|
35
|
+
currentY += 20;
|
|
36
|
+
// Draw parameters
|
|
37
|
+
ctx.font = '10px "Roboto Mono", monospace';
|
|
38
|
+
const lineHeight = 12;
|
|
39
|
+
const params = [
|
|
40
|
+
{ label: 'Type', value: hole.type },
|
|
41
|
+
{ label: 'Base Radius', value: hole.baseRadius.toFixed(2) },
|
|
42
|
+
{ label: 'Mass', value: hole.mass.toFixed(2) },
|
|
43
|
+
{ label: 'Merge State', value: `${hole.mergeState}` },
|
|
44
|
+
];
|
|
45
|
+
params.forEach(param => {
|
|
46
|
+
ctx.fillStyle = PANEL_CONFIG.ACCENT_COLOR;
|
|
47
|
+
ctx.fillText(`${param.label}:`, x, currentY);
|
|
48
|
+
ctx.fillStyle = PANEL_CONFIG.TEXT_COLOR;
|
|
49
|
+
ctx.fillText(param.value, x + 70, currentY);
|
|
50
|
+
currentY += lineHeight;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export function drawBlackHoleCatalog(ctx, startY, blackHoleCatalog, pulseTime, frameCount) {
|
|
54
|
+
let currentY = startY + PANEL_CONFIG.PADDING;
|
|
55
|
+
ctx.font = PANEL_CONFIG.GROUP_FONT;
|
|
56
|
+
ctx.fillStyle = PANEL_CONFIG.TEXT_COLOR;
|
|
57
|
+
ctx.textAlign = 'left';
|
|
58
|
+
ctx.textBaseline = 'top';
|
|
59
|
+
ctx.fillText('All Black Hole Types', PANEL_CONFIG.X + PANEL_CONFIG.PADDING, currentY);
|
|
60
|
+
currentY += PANEL_CONFIG.GROUP_HEADER_HEIGHT;
|
|
61
|
+
blackHoleCatalog.forEach(sampleHole => {
|
|
62
|
+
const drawingAreaHeight = 80;
|
|
63
|
+
const rowY = currentY + drawingAreaHeight / 2;
|
|
64
|
+
const xPos = PANEL_CONFIG.X + PANEL_CONFIG.PADDING + 30;
|
|
65
|
+
ctx.save();
|
|
66
|
+
// Position the drawing relative to the catalog panel
|
|
67
|
+
ctx.translate(xPos, rowY);
|
|
68
|
+
// sampleHole.update(ctx, 0, 0, frameCount); // This is expensive, disable animation in catalog
|
|
69
|
+
sampleHole.draw(ctx, 0, 0); // Pass static pulseTime and frameCount
|
|
70
|
+
ctx.restore();
|
|
71
|
+
// Draw Title
|
|
72
|
+
drawBlackHoleParameters(ctx, sampleHole, xPos + 70, rowY - 35);
|
|
73
|
+
currentY += drawingAreaHeight + 45; // Add padding between entries
|
|
74
|
+
});
|
|
75
|
+
return currentY - startY;
|
|
76
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PANEL_CONFIG } from './panel-config';
|
|
2
|
+
export function drawClanCatalog(ctx, startY, clans) {
|
|
3
|
+
ctx.font = PANEL_CONFIG.ROW_FONT;
|
|
4
|
+
clans.forEach((clan, i) => {
|
|
5
|
+
const y = startY + (i * PANEL_CONFIG.ROW_HEIGHT) + PANEL_CONFIG.PADDING + 15;
|
|
6
|
+
// Draw clan icon
|
|
7
|
+
ctx.save();
|
|
8
|
+
ctx.translate(PANEL_CONFIG.X + PANEL_CONFIG.PADDING + 10, y);
|
|
9
|
+
ctx.fillStyle = clan.color;
|
|
10
|
+
ctx.strokeStyle = clan.color;
|
|
11
|
+
clan.icon(ctx, 12);
|
|
12
|
+
ctx.restore();
|
|
13
|
+
// Draw text
|
|
14
|
+
ctx.fillStyle = PANEL_CONFIG.TEXT_COLOR;
|
|
15
|
+
ctx.textAlign = 'left';
|
|
16
|
+
ctx.textBaseline = 'middle';
|
|
17
|
+
ctx.fillText(`${clan.name} (Fleets: ${clan.fleets.length}, Ships: ${clan.starships.length})`, PANEL_CONFIG.X + PANEL_CONFIG.PADDING + 30, y);
|
|
18
|
+
});
|
|
19
|
+
return (clans.length * PANEL_CONFIG.ROW_HEIGHT) + (PANEL_CONFIG.PADDING * 2);
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Formation } from '../entities/formations';
|
|
2
|
+
import type { CatalogData } from '../types';
|
|
3
|
+
export declare class DebugPanelController {
|
|
4
|
+
private ctx;
|
|
5
|
+
private activeTab;
|
|
6
|
+
private tabs;
|
|
7
|
+
private shipCatalog;
|
|
8
|
+
private formationCatalog;
|
|
9
|
+
private starCatalog;
|
|
10
|
+
private blackHoleCatalog;
|
|
11
|
+
private panelY;
|
|
12
|
+
private panelHeight;
|
|
13
|
+
private scrollTop;
|
|
14
|
+
private contentHeight;
|
|
15
|
+
private starFactory;
|
|
16
|
+
private blackHoleFactory;
|
|
17
|
+
private isDraggingScrollbar;
|
|
18
|
+
private dragStartY;
|
|
19
|
+
private dragStartScrollTop;
|
|
20
|
+
private scrollbarThumb;
|
|
21
|
+
private scrollbarTrack;
|
|
22
|
+
private showFormationShips;
|
|
23
|
+
constructor(ctx: CanvasRenderingContext2D, allFormations: Formation[]);
|
|
24
|
+
private initializeTabs;
|
|
25
|
+
private cacheShipClasses;
|
|
26
|
+
private cacheFormations;
|
|
27
|
+
private cacheStarVariations;
|
|
28
|
+
private cacheBlackHoleTypes;
|
|
29
|
+
private calculatePanelDimensions;
|
|
30
|
+
isMouseOver(x: number, y: number): boolean;
|
|
31
|
+
handleScroll(deltaY: number): void;
|
|
32
|
+
isDragging(): boolean;
|
|
33
|
+
handleMouseDown(x: number, y: number): void;
|
|
34
|
+
handleMouseMove(x: number, y: number): void;
|
|
35
|
+
handleMouseUp(): void;
|
|
36
|
+
handleResize(): void;
|
|
37
|
+
draw(data: CatalogData, pulseTime: number, frameCount: number): void;
|
|
38
|
+
private drawPanelBase;
|
|
39
|
+
private drawTabs;
|
|
40
|
+
private drawScrollbar;
|
|
41
|
+
}
|