@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.
Files changed (160) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +143 -0
  3. package/dist/src/base/bounded-body.d.ts +8 -0
  4. package/dist/src/base/bounded-body.js +10 -0
  5. package/dist/src/base/celestial-body.d.ts +12 -0
  6. package/dist/src/base/celestial-body.js +11 -0
  7. package/dist/src/base/hoverable.d.ts +10 -0
  8. package/dist/src/base/hoverable.js +4 -0
  9. package/dist/src/base/massive-body.d.ts +8 -0
  10. package/dist/src/base/massive-body.js +10 -0
  11. package/dist/src/config/effects.d.ts +14 -0
  12. package/dist/src/config/effects.js +14 -0
  13. package/dist/src/config/grid.d.ts +10 -0
  14. package/dist/src/config/grid.js +10 -0
  15. package/dist/src/config/panel.d.ts +26 -0
  16. package/dist/src/config/panel.js +26 -0
  17. package/dist/src/config/simulation.d.ts +21 -0
  18. package/dist/src/config/simulation.js +23 -0
  19. package/dist/src/controllers/clan-controller.d.ts +7 -0
  20. package/dist/src/controllers/clan-controller.js +12 -0
  21. package/dist/src/controllers/debug-controller.d.ts +45 -0
  22. package/dist/src/controllers/debug-controller.js +82 -0
  23. package/dist/src/controllers/effects-controller.d.ts +17 -0
  24. package/dist/src/controllers/effects-controller.js +71 -0
  25. package/dist/src/controllers/hover-controller.d.ts +11 -0
  26. package/dist/src/controllers/hover-controller.js +107 -0
  27. package/dist/src/controllers/layer-controller.d.ts +16 -0
  28. package/dist/src/controllers/layer-controller.js +64 -0
  29. package/dist/src/controllers/physics-controller.d.ts +14 -0
  30. package/dist/src/controllers/physics-controller.js +152 -0
  31. package/dist/src/controllers/star-controller.d.ts +12 -0
  32. package/dist/src/controllers/star-controller.js +38 -0
  33. package/dist/src/controllers/starship-controller.d.ts +17 -0
  34. package/dist/src/controllers/starship-controller.js +58 -0
  35. package/dist/src/draw-debug-line.d.ts +9 -0
  36. package/dist/src/draw-debug-line.js +29 -0
  37. package/dist/src/entities/black-hole-factory.d.ts +15 -0
  38. package/dist/src/entities/black-hole-factory.js +23 -0
  39. package/dist/src/entities/black-hole-shapes.d.ts +9 -0
  40. package/dist/src/entities/black-hole-shapes.js +224 -0
  41. package/dist/src/entities/black-hole.d.ts +69 -0
  42. package/dist/src/entities/black-hole.js +210 -0
  43. package/dist/src/entities/clan-manager.d.ts +12 -0
  44. package/dist/src/entities/clan-manager.js +22 -0
  45. package/dist/src/entities/clans.d.ts +15 -0
  46. package/dist/src/entities/clans.js +76 -0
  47. package/dist/src/entities/comet.d.ts +27 -0
  48. package/dist/src/entities/comet.js +81 -0
  49. package/dist/src/entities/docking-point.d.ts +20 -0
  50. package/dist/src/entities/docking-point.js +22 -0
  51. package/dist/src/entities/fleet.d.ts +45 -0
  52. package/dist/src/entities/fleet.js +374 -0
  53. package/dist/src/entities/formations.d.ts +51 -0
  54. package/dist/src/entities/formations.js +340 -0
  55. package/dist/src/entities/meteor.d.ts +26 -0
  56. package/dist/src/entities/meteor.js +48 -0
  57. package/dist/src/entities/nebula.d.ts +18 -0
  58. package/dist/src/entities/nebula.js +43 -0
  59. package/dist/src/entities/orbit.d.ts +23 -0
  60. package/dist/src/entities/orbit.js +43 -0
  61. package/dist/src/entities/pulsar.d.ts +18 -0
  62. package/dist/src/entities/pulsar.js +41 -0
  63. package/dist/src/entities/ring.d.ts +13 -0
  64. package/dist/src/entities/ring.js +26 -0
  65. package/dist/src/entities/ringed-planet.d.ts +21 -0
  66. package/dist/src/entities/ringed-planet.js +68 -0
  67. package/dist/src/entities/sector-grid.d.ts +16 -0
  68. package/dist/src/entities/sector-grid.js +70 -0
  69. package/dist/src/entities/star-factory.d.ts +29 -0
  70. package/dist/src/entities/star-factory.js +47 -0
  71. package/dist/src/entities/star.d.ts +48 -0
  72. package/dist/src/entities/star.js +167 -0
  73. package/dist/src/entities/starship-classes.d.ts +0 -0
  74. package/dist/src/entities/starship-classes.js +2 -0
  75. package/dist/src/entities/starship.d.ts +91 -0
  76. package/dist/src/entities/starship.js +760 -0
  77. package/dist/src/entities/supernova.d.ts +26 -0
  78. package/dist/src/entities/supernova.js +54 -0
  79. package/dist/src/index.d.ts +20 -0
  80. package/dist/src/index.js +19 -0
  81. package/dist/src/lib/energy-stream.d.ts +5 -0
  82. package/dist/src/lib/energy-stream.js +98 -0
  83. package/dist/src/lib/quadtree.d.ts +31 -0
  84. package/dist/src/lib/quadtree.js +124 -0
  85. package/dist/src/lib/simplified-stream.d.ts +6 -0
  86. package/dist/src/lib/simplified-stream.js +19 -0
  87. package/dist/src/types.d.ts +14 -0
  88. package/dist/src/types.js +1 -0
  89. package/dist/src/ui/black-holes-panel.d.ts +2 -0
  90. package/dist/src/ui/black-holes-panel.js +76 -0
  91. package/dist/src/ui/clans-panel.d.ts +2 -0
  92. package/dist/src/ui/clans-panel.js +20 -0
  93. package/dist/src/ui/debug-panel-controller.d.ts +41 -0
  94. package/dist/src/ui/debug-panel-controller.js +285 -0
  95. package/dist/src/ui/fleets-panel.d.ts +2 -0
  96. package/dist/src/ui/fleets-panel.js +127 -0
  97. package/dist/src/ui/formations-panel.d.ts +3 -0
  98. package/dist/src/ui/formations-panel.js +129 -0
  99. package/dist/src/ui/panel-config.d.ts +26 -0
  100. package/dist/src/ui/panel-config.js +26 -0
  101. package/dist/src/ui/ships-panel.d.ts +12 -0
  102. package/dist/src/ui/ships-panel.js +61 -0
  103. package/dist/src/ui/stars-panel.d.ts +2 -0
  104. package/dist/src/ui/stars-panel.js +120 -0
  105. package/dist/src/where-stars-drift.d.ts +71 -0
  106. package/dist/src/where-stars-drift.js +440 -0
  107. package/dist/tsconfig.tsbuildinfo +1 -0
  108. package/package.json +35 -0
  109. package/src/base/bounded-body.ts +14 -0
  110. package/src/base/celestial-body.ts +20 -0
  111. package/src/base/hoverable.ts +11 -0
  112. package/src/base/massive-body.ts +14 -0
  113. package/src/config/effects.ts +15 -0
  114. package/src/config/grid.ts +11 -0
  115. package/src/config/panel.ts +26 -0
  116. package/src/config/simulation.ts +25 -0
  117. package/src/controllers/clan-controller.ts +19 -0
  118. package/src/controllers/debug-controller.ts +112 -0
  119. package/src/controllers/effects-controller.ts +86 -0
  120. package/src/controllers/hover-controller.ts +128 -0
  121. package/src/controllers/layer-controller.ts +78 -0
  122. package/src/controllers/physics-controller.ts +173 -0
  123. package/src/controllers/star-controller.ts +51 -0
  124. package/src/controllers/starship-controller.ts +76 -0
  125. package/src/draw-debug-line.ts +37 -0
  126. package/src/entities/black-hole-factory.ts +28 -0
  127. package/src/entities/black-hole-shapes.ts +276 -0
  128. package/src/entities/black-hole.ts +246 -0
  129. package/src/entities/clan-manager.ts +33 -0
  130. package/src/entities/clans.ts +98 -0
  131. package/src/entities/comet.ts +102 -0
  132. package/src/entities/docking-point.ts +34 -0
  133. package/src/entities/fleet.ts +446 -0
  134. package/src/entities/formations.ts +423 -0
  135. package/src/entities/meteor.ts +59 -0
  136. package/src/entities/nebula.ts +50 -0
  137. package/src/entities/orbit.ts +53 -0
  138. package/src/entities/pulsar.ts +64 -0
  139. package/src/entities/ring.ts +42 -0
  140. package/src/entities/ringed-planet.ts +85 -0
  141. package/src/entities/sector-grid.ts +81 -0
  142. package/src/entities/star-factory.ts +59 -0
  143. package/src/entities/star.ts +222 -0
  144. package/src/entities/starship-classes.ts +1 -0
  145. package/src/entities/starship.ts +906 -0
  146. package/src/entities/supernova.ts +75 -0
  147. package/src/index.ts +24 -0
  148. package/src/lib/energy-stream.ts +127 -0
  149. package/src/lib/quadtree.ts +159 -0
  150. package/src/lib/simplified-stream.ts +28 -0
  151. package/src/types.ts +16 -0
  152. package/src/ui/black-holes-panel.ts +91 -0
  153. package/src/ui/clans-panel.ts +27 -0
  154. package/src/ui/debug-panel-controller.ts +339 -0
  155. package/src/ui/fleets-panel.ts +153 -0
  156. package/src/ui/formations-panel.ts +155 -0
  157. package/src/ui/panel-config.ts +26 -0
  158. package/src/ui/ships-panel.ts +85 -0
  159. package/src/ui/stars-panel.ts +146 -0
  160. package/src/where-stars-drift.ts +542 -0
@@ -0,0 +1,107 @@
1
+ import { Starship, STARSHIP_CONFIG } from '../entities/starship';
2
+ import { Fleet, FLEET_CONFIG } from '../entities/fleet';
3
+ import { STAR_CONFIG } from '../entities/star';
4
+ import { BlackHole, BLACK_HOLE_CONFIG } from '../entities/black-hole';
5
+ function isHoverable(body) {
6
+ return 'isHovered' in body && 'hoverTimer' in body && 'hoverRadius' in body && 'x' in body && 'y' in body;
7
+ }
8
+ export class HoverController {
9
+ constructor(bodies) {
10
+ this.bodies = bodies;
11
+ }
12
+ update(mouse, isMouseInside, debug) {
13
+ if (!debug) {
14
+ return;
15
+ }
16
+ this.updateHoverStates();
17
+ if (isMouseInside) {
18
+ this.checkHoverState(mouse);
19
+ }
20
+ }
21
+ checkHoverState(mouse) {
22
+ const allHoverables = this.bodies.filter(isHoverable);
23
+ let hoveredShip = null;
24
+ let hoveredFleet = null;
25
+ let hoveredStar = null;
26
+ let hoveredBlackHole = null;
27
+ // Prioritize smaller, more specific objects first by checking them in order.
28
+ // Once a hovered object is found, we stop to prevent hovering multiple objects at once.
29
+ // 1. Ships
30
+ for (const body of allHoverables) {
31
+ if (body instanceof Starship) {
32
+ const dx = body.x - mouse.x;
33
+ const dy = body.y - mouse.y;
34
+ const distSq = dx * dx + dy * dy;
35
+ if (distSq < body.hoverRadius * body.hoverRadius) {
36
+ hoveredShip = body;
37
+ break;
38
+ }
39
+ }
40
+ }
41
+ // 2. Fleets
42
+ if (!hoveredShip) {
43
+ for (const body of allHoverables) {
44
+ if (body instanceof Fleet) {
45
+ const dx = body.x - mouse.x;
46
+ const dy = body.y - mouse.y;
47
+ const distSq = dx * dx + dy * dy;
48
+ if (distSq < body.hoverRadius * body.hoverRadius) {
49
+ hoveredFleet = body;
50
+ break;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ // 3. Black Holes
56
+ if (!hoveredShip && !hoveredFleet) {
57
+ for (const body of allHoverables) {
58
+ if (body instanceof BlackHole) {
59
+ const dx = body.x - mouse.x;
60
+ const dy = body.y - mouse.y;
61
+ const distSq = dx * dx + dy * dy;
62
+ if (distSq < body.hoverRadius * body.hoverRadius) {
63
+ hoveredBlackHole = body;
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ }
69
+ // 4. Stars (as a fallback)
70
+ if (!hoveredShip && !hoveredFleet && !hoveredBlackHole) {
71
+ for (const body of allHoverables) {
72
+ if (!(body instanceof Starship) && !(body instanceof Fleet) && !(body instanceof BlackHole)) {
73
+ const dx = body.x - mouse.x;
74
+ const dy = body.y - mouse.y;
75
+ const distSq = dx * dx + dy * dy;
76
+ if (distSq < body.hoverRadius * body.hoverRadius) {
77
+ hoveredStar = body;
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ // Set timers for all hovered items
84
+ if (hoveredShip) {
85
+ hoveredShip.hoverTimer = STARSHIP_CONFIG.HOVER_TIMEOUT;
86
+ }
87
+ if (hoveredFleet) {
88
+ hoveredFleet.hoverTimer = FLEET_CONFIG.HOVER_TIMEOUT;
89
+ }
90
+ if (hoveredBlackHole) {
91
+ hoveredBlackHole.hoverTimer = BLACK_HOLE_CONFIG.HOVER_TIMEOUT;
92
+ }
93
+ if (hoveredStar) {
94
+ hoveredStar.hoverTimer = STAR_CONFIG.HOVER_TIMEOUT;
95
+ }
96
+ }
97
+ updateHoverStates() {
98
+ this.bodies.forEach(obj => {
99
+ if (isHoverable(obj)) {
100
+ if (obj.hoverTimer > 0) {
101
+ obj.hoverTimer--;
102
+ }
103
+ obj.isHovered = obj.hoverTimer > 0;
104
+ }
105
+ });
106
+ }
107
+ }
@@ -0,0 +1,16 @@
1
+ import type { CelestialBody } from '../base/celestial-body';
2
+ export type Layer = {
3
+ name: string;
4
+ description: string;
5
+ bodies: CelestialBody[];
6
+ isInteractive: boolean;
7
+ };
8
+ export declare class LayerController {
9
+ private layers;
10
+ constructor();
11
+ private initializeLayers;
12
+ getLayers(): Record<string, Layer>;
13
+ getLayer(layerName: string): Layer | undefined;
14
+ getBodies(layerName: string): CelestialBody[];
15
+ addBody(layerName: string, body: CelestialBody): void;
16
+ }
@@ -0,0 +1,64 @@
1
+ export class LayerController {
2
+ constructor() {
3
+ this.layers = {};
4
+ this.initializeLayers();
5
+ }
6
+ initializeLayers() {
7
+ this.layers = {
8
+ backgroundNebulas: {
9
+ name: 'Background Nebulas',
10
+ description: 'Slow-moving, non-interactive nebula clouds for visual depth.',
11
+ bodies: [],
12
+ isInteractive: false,
13
+ },
14
+ sectorGrid: {
15
+ name: 'Sector Grid',
16
+ description: 'A grid of sectors that divides the map.',
17
+ bodies: [],
18
+ isInteractive: false,
19
+ },
20
+ staticStars: {
21
+ name: 'Static Stars',
22
+ description: 'A layer of stars that provides parallax but does not react to mouse interaction.',
23
+ bodies: [],
24
+ isInteractive: false,
25
+ },
26
+ interactiveStars: {
27
+ name: 'Interactive Stars',
28
+ description: 'A layer of stars that reacts to mouse movement and can be hovered.',
29
+ bodies: [],
30
+ isInteractive: true,
31
+ },
32
+ blackHoles: {
33
+ name: 'Black Holes',
34
+ description: 'Gravitational wells that affect other objects.',
35
+ bodies: [],
36
+ isInteractive: true,
37
+ },
38
+ dynamicObjects: {
39
+ name: 'Dynamic Objects',
40
+ description: 'Comets, starships, and fleets that move and interact within the simulation.',
41
+ bodies: [],
42
+ isInteractive: true,
43
+ },
44
+ };
45
+ }
46
+ getLayers() {
47
+ return this.layers;
48
+ }
49
+ getLayer(layerName) {
50
+ return this.layers[layerName];
51
+ }
52
+ getBodies(layerName) {
53
+ var _a;
54
+ return ((_a = this.layers[layerName]) === null || _a === void 0 ? void 0 : _a.bodies) || [];
55
+ }
56
+ addBody(layerName, body) {
57
+ if (this.layers[layerName]) {
58
+ this.layers[layerName].bodies.push(body);
59
+ }
60
+ else {
61
+ console.warn(`Layer "${layerName}" does not exist.`);
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,14 @@
1
+ import { Star } from '../entities/star';
2
+ import { BlackHle } from '../entities/black-hole';
3
+ import { Supernova } from '../entities/supernova';
4
+ import { Quadtree } from '../lib/quadtree';
5
+ import { DebugController } from './debug-controller';
6
+ export declare class PhysicsController {
7
+ private debugController;
8
+ constructor(debugController: DebugController);
9
+ update(blackHoles: BlackHle[], allStars: Star[], quadtree: Quadtree, width: number, height: number): Supernova[];
10
+ private findSafeRespawnLocation;
11
+ private applyGravity;
12
+ private handleStarConsumption;
13
+ private handleBlackHoleCollisions;
14
+ }
@@ -0,0 +1,152 @@
1
+ import { Star } from '../entities/star';
2
+ import { BLACK_HOLE_CONFIG } from '../entities/black-hole';
3
+ import { Supernova } from '../entities/supernova';
4
+ import { DEBUG_CONFIG } from '../config/simulation';
5
+ import { BoundedBody } from '../base/bounded-body';
6
+ const GRAVITATIONAL_CONSTANT = 0.007;
7
+ class SearchArea extends BoundedBody {
8
+ constructor(x, y, radius) {
9
+ super(x, y, radius);
10
+ }
11
+ update() { }
12
+ draw() { }
13
+ }
14
+ export class PhysicsController {
15
+ constructor(debugController) {
16
+ this.debugController = debugController;
17
+ }
18
+ update(blackHoles, allStars, quadtree, width, height) {
19
+ const newSupernovas = [];
20
+ this.applyGravity(blackHoles, quadtree);
21
+ this.handleStarConsumption(blackHoles, allStars, quadtree, newSupernovas, width, height);
22
+ this.handleBlackHoleCollisions(blackHoles, newSupernovas, width, height);
23
+ return newSupernovas;
24
+ }
25
+ findSafeRespawnLocation(blackHoles, width, height) {
26
+ const SAFE_DISTANCE = 200;
27
+ let attempts = 0;
28
+ const MAX_ATTEMPTS = 10;
29
+ while (attempts < MAX_ATTEMPTS) {
30
+ const x = Math.random() * width;
31
+ const y = Math.random() * height;
32
+ let isSafe = true;
33
+ for (const hole of blackHoles) {
34
+ const dx = x - hole.x;
35
+ const dy = y - hole.y;
36
+ const distance = Math.sqrt(dx * dx + dy * dy);
37
+ if (distance < SAFE_DISTANCE) {
38
+ isSafe = false;
39
+ break;
40
+ }
41
+ }
42
+ if (isSafe) {
43
+ return { x, y };
44
+ }
45
+ attempts++;
46
+ }
47
+ return { x: Math.random() * width, y: Math.random() * height };
48
+ }
49
+ ;
50
+ applyGravity(blackHoles, quadtree) {
51
+ for (const blackHole of blackHoles) {
52
+ const searchArea = new SearchArea(blackHole.x, blackHole.y, BLACK_HOLE_CONFIG.STAR_ATTRACTION_RADIUS);
53
+ const nearbyStars = quadtree.query(searchArea).filter(b => b instanceof Star);
54
+ for (const star of nearbyStars) {
55
+ const dx = blackHole.x - star.x;
56
+ const dy = blackHole.y - star.y;
57
+ const distSq = dx * dx + dy * dy;
58
+ if (distSq < BLACK_HOLE_CONFIG.STAR_ATTRACTION_RADIUS * BLACK_HOLE_CONFIG.STAR_ATTRACTION_RADIUS) {
59
+ const dist = Math.sqrt(distSq);
60
+ const force = (GRAVITATIONAL_CONSTANT * blackHole.mass * star.mass) / (distSq + 1);
61
+ const acceleration = force / star.mass;
62
+ star.vx += (dx / dist) * acceleration;
63
+ star.vy += (dy / dist) * acceleration;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ handleStarConsumption(blackHoles, allStars, quadtree, supernovas, width, height) {
69
+ for (const blackHole of blackHoles) {
70
+ const consumptionRadius = blackHole.radius + 5; // A little buffer
71
+ const searchArea = new SearchArea(blackHole.x, blackHole.y, consumptionRadius);
72
+ const nearbyStars = quadtree.query(searchArea).filter(b => b instanceof Star);
73
+ for (const star of nearbyStars) {
74
+ if (star.isEatenBy(blackHole)) {
75
+ blackHole.absorb(star);
76
+ const { x: respawnX, y: respawnY } = this.findSafeRespawnLocation(blackHoles, width, height);
77
+ star.respawn(respawnX, respawnY);
78
+ supernovas.push(new Supernova(blackHole.x, blackHole.y, blackHole.radius, blackHole));
79
+ supernovas.push(new Supernova(respawnX, respawnY, 5));
80
+ }
81
+ }
82
+ }
83
+ }
84
+ handleBlackHoleCollisions(blackHoles, supernovas, width, height) {
85
+ if (blackHoles.length < 2)
86
+ return;
87
+ // Reset attraction targets at the start of each frame
88
+ for (const bh of blackHoles) {
89
+ bh.attractionTargets = [];
90
+ }
91
+ for (let i = 0; i < blackHoles.length; i++) {
92
+ for (let j = i + 1; j < blackHoles.length; j++) {
93
+ const bh1 = blackHoles[i];
94
+ const bh2 = blackHoles[j];
95
+ const dx = bh2.x - bh1.x;
96
+ const dy = bh2.y - bh1.y;
97
+ const distSq = dx * dx + dy * dy;
98
+ const dist = Math.sqrt(distSq);
99
+ if (dist < BLACK_HOLE_CONFIG.ENERGY_STREAM_DISTANCE) {
100
+ // Register targets for stream rendering
101
+ bh1.attractionTargets.push(bh2);
102
+ bh2.attractionTargets.push(bh1);
103
+ // Register line for debug display
104
+ if (DEBUG_CONFIG.SHOW_DEBUG_INFO && DEBUG_CONFIG.SHOW_INTER_HOLE_DISTANCE) {
105
+ this.debugController.registerLine(bh1, bh2, { drawLine: false, showDistance: true });
106
+ }
107
+ }
108
+ if (dist < BLACK_HOLE_CONFIG.ATTRACTION_RADIUS) {
109
+ const force = (GRAVITATIONAL_CONSTANT * bh1.mass * bh2.mass) / (distSq + 1);
110
+ const acceleration1 = force / bh1.mass;
111
+ const acceleration2 = force / bh2.mass;
112
+ bh1.vx += (dx / dist) * acceleration1;
113
+ bh1.vy += (dy / dist) * acceleration1;
114
+ bh2.vx -= (dx / dist) * acceleration2;
115
+ bh2.vy -= (dy / dist) * acceleration2;
116
+ }
117
+ if (dist < bh1.radius + bh2.radius) {
118
+ const absorber = bh1.mass > bh2.mass ? bh1 : bh2;
119
+ const absorbed = bh1.mass > bh2.mass ? bh2 : bh1;
120
+ absorber.absorb(absorbed);
121
+ supernovas.push(new Supernova(absorber.x, absorber.y, absorber.radius * 1.5, absorber));
122
+ const side = Math.floor(Math.random() * 4);
123
+ let x, y;
124
+ const buffer = 50;
125
+ switch (side) {
126
+ case 0:
127
+ x = Math.random() * width;
128
+ y = -buffer;
129
+ break;
130
+ case 1:
131
+ x = width + buffer;
132
+ y = Math.random() * height;
133
+ break;
134
+ case 2:
135
+ x = Math.random() * width;
136
+ y = height + buffer;
137
+ break;
138
+ default:
139
+ x = -buffer;
140
+ y = Math.random() * height;
141
+ break;
142
+ }
143
+ const angleToCenter = Math.atan2(height / 2 - y, width / 2 - x);
144
+ const speed = 0.1;
145
+ const vx = Math.cos(angleToCenter) * speed;
146
+ const vy = Math.sin(angleToCenter) * speed;
147
+ absorbed.reset(x, y, vx, vy);
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
@@ -0,0 +1,12 @@
1
+ import { Star } from '../entities/star';
2
+ import { LayerController } from './layer-controller';
3
+ export declare class StarController {
4
+ private layerController;
5
+ private allStars;
6
+ private starFactory;
7
+ private blackHoleFactory;
8
+ constructor(layerController: LayerController, starsCount: number, width: number, height: number);
9
+ private createStars;
10
+ private createBlackHoles;
11
+ getAllStars(): Star[];
12
+ }
@@ -0,0 +1,38 @@
1
+ import { StarFactory } from '../entities/star-factory';
2
+ import { BlackHoleFactory } from '../entities/black-hole-factory';
3
+ export class StarController {
4
+ constructor(layerController, starsCount, width, height) {
5
+ this.allStars = [];
6
+ this.layerController = layerController;
7
+ this.starFactory = new StarFactory();
8
+ this.blackHoleFactory = new BlackHoleFactory();
9
+ this.createStars(starsCount, width, height);
10
+ this.createBlackHoles(width, height);
11
+ }
12
+ createStars(starsCount, width, height) {
13
+ const staticStarsCount = Math.floor(starsCount * 0.7);
14
+ const interactiveStarsCount = starsCount - staticStarsCount;
15
+ for (let i = 0; i < staticStarsCount; i++) {
16
+ const star = this.starFactory.createRandomStar(Math.random() * width, Math.random() * height);
17
+ this.layerController.addBody('staticStars', star);
18
+ this.allStars.push(star);
19
+ }
20
+ for (let i = 0; i < interactiveStarsCount; i++) {
21
+ const star = this.starFactory.createRandomStar(Math.random() * width, Math.random() * height);
22
+ this.layerController.addBody('interactiveStars', star);
23
+ this.allStars.push(star);
24
+ }
25
+ }
26
+ createBlackHoles(width, height) {
27
+ const numBlackHoles = 3; // Hardcoded for now
28
+ const padding = 100;
29
+ for (let i = 0; i < numBlackHoles; i++) {
30
+ const x = Math.random() * (width - padding * 2) + padding;
31
+ const y = Math.random() * (height - padding * 2) + padding;
32
+ this.layerController.addBody('blackHoles', this.blackHoleFactory.createRandomBlackHole(x, y));
33
+ }
34
+ }
35
+ getAllStars() {
36
+ return this.allStars;
37
+ }
38
+ }
@@ -0,0 +1,17 @@
1
+ import { Starship } from '../entities/starship';
2
+ import { LayerController } from './layer-controller';
3
+ import { Star } from '../entities/star';
4
+ import { DebugController } from './debug-controller';
5
+ import type { Clan } from '../entities/clans';
6
+ export declare class StarshipController {
7
+ private layerController;
8
+ private clanController;
9
+ private debugController;
10
+ private allStarships;
11
+ constructor(layerController: LayerController, width: number, height: number, debugController: DebugController);
12
+ private createStarships;
13
+ manageFleets(width: number, height: number): void;
14
+ manageStarshipTargets(allStars: Star[]): void;
15
+ getAllStarships(): Starship[];
16
+ getAllClans(): Clan[];
17
+ }
@@ -0,0 +1,58 @@
1
+ import { Starship, STARSHIP_CONFIG } from '../entities/starship';
2
+ import { Fleet } from '../entities/fleet';
3
+ import { ClanController } from './clan-controller';
4
+ export class StarshipController {
5
+ constructor(layerController, width, height, debugController) {
6
+ this.allStarships = [];
7
+ this.layerController = layerController;
8
+ this.clanController = new ClanController();
9
+ this.debugController = debugController;
10
+ this.createStarships(width, height);
11
+ }
12
+ createStarships(width, height) {
13
+ for (let i = 0; i < STARSHIP_CONFIG.COUNT; i++) {
14
+ const starship = new Starship(Math.random() * width, Math.random() * height, this.debugController);
15
+ this.layerController.addBody('dynamicObjects', starship);
16
+ this.allStarships.push(starship);
17
+ }
18
+ }
19
+ manageFleets(width, height) {
20
+ const desiredFleetSizes = [7, 3, 5, 4, 3, 3];
21
+ const fleets = this.layerController.getBodies('dynamicObjects').filter(b => b instanceof Fleet);
22
+ const starships = this.allStarships;
23
+ // Disband fleets that have lost too many members
24
+ fleets.forEach(fleet => {
25
+ if (fleet.isActive && fleet.members.length < fleet.size * 0.5) {
26
+ fleet.disband();
27
+ }
28
+ });
29
+ desiredFleetSizes.forEach(size => {
30
+ const fleetExists = fleets.some(f => f.size === size && f.isActive);
31
+ if (!fleetExists) {
32
+ const availableShips = starships.filter(s => s.isAvailable());
33
+ if (availableShips.length >= size) {
34
+ const clan = this.clanController.getNextClan();
35
+ const fleetMembers = availableShips.slice(0, size);
36
+ this.layerController.addBody('dynamicObjects', new Fleet(fleetMembers, clan, width, height));
37
+ }
38
+ }
39
+ });
40
+ }
41
+ manageStarshipTargets(allStars) {
42
+ const idleShips = this.allStarships.filter(ship => ship.state === 'IDLE' && ship.stateTimer <= 0);
43
+ if (idleShips.length === 0)
44
+ return;
45
+ const starsWithDocks = allStars.filter(s => s.dockingPoints.length > 0);
46
+ if (starsWithDocks.length === 0)
47
+ return;
48
+ for (const ship of idleShips) {
49
+ ship.findNewTarget(starsWithDocks);
50
+ }
51
+ }
52
+ getAllStarships() {
53
+ return this.allStarships;
54
+ }
55
+ getAllClans() {
56
+ return this.clanController.getClans();
57
+ }
58
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview Defines a utility function for drawing a debug line with distance.
3
+ */
4
+ interface Point {
5
+ x: number;
6
+ y: number;
7
+ }
8
+ export declare function drawDebugLineWithDistance(ctx: CanvasRenderingContext2D, source: Point, target: Point, drawLine: boolean, showDistance: boolean): void;
9
+ export {};
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @fileoverview Defines a utility function for drawing a debug line with distance.
3
+ */
4
+ export function drawDebugLineWithDistance(ctx, source, target, drawLine, showDistance) {
5
+ const dx = target.x - source.x;
6
+ const dy = target.y - source.y;
7
+ const dist = Math.sqrt(dx * dx + dy * dy);
8
+ // Draw the dashed line if requested
9
+ if (drawLine) {
10
+ ctx.beginPath();
11
+ ctx.setLineDash([2, 4]);
12
+ ctx.moveTo(source.x, source.y);
13
+ ctx.lineTo(target.x, target.y);
14
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
15
+ ctx.lineWidth = 0.5;
16
+ ctx.stroke();
17
+ ctx.setLineDash([]);
18
+ }
19
+ // Draw the distance text if requested
20
+ if (showDistance) {
21
+ const midX = source.x + dx / 2;
22
+ const midY = source.y + dy / 2;
23
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
24
+ ctx.font = '10px "Roboto Mono", monospace';
25
+ ctx.textAlign = 'center';
26
+ ctx.textBaseline = 'bottom';
27
+ ctx.fillText(dist.toFixed(0), midX, midY - 2);
28
+ }
29
+ }
@@ -0,0 +1,15 @@
1
+ import { BlackHole, BlackHoleType } from './black-hole';
2
+ export declare class BlackHoleFactory {
3
+ /**
4
+ * Creates a black hole of a random type.
5
+ */
6
+ createRandomBlackHole(x: number, y: number): BlackHole;
7
+ /**
8
+ * Creates a black hole of a specific type.
9
+ */
10
+ createBlackHole(x: number, y: number, type: BlackHoleType): BlackHole;
11
+ /**
12
+ * Gets an array of all possible black hole types.
13
+ */
14
+ getAllBlackHoleTypes(): BlackHoleType[];
15
+ }
@@ -0,0 +1,23 @@
1
+ import { BlackHole } from './black-hole';
2
+ const BLACK_HOLE_TYPES = ['ACCRETION_DISK', 'VORTEX', 'LENSING', 'WARPED_DISK', 'STANDARD'];
3
+ export class BlackHoleFactory {
4
+ /**
5
+ * Creates a black hole of a random type.
6
+ */
7
+ createRandomBlackHole(x, y) {
8
+ const randomType = BLACK_HOLE_TYPES[Math.floor(Math.random() * BLACK_HOLE_TYPES.length)];
9
+ return new BlackHole(x, y, randomType);
10
+ }
11
+ /**
12
+ * Creates a black hole of a specific type.
13
+ */
14
+ createBlackHole(x, y, type) {
15
+ return new BlackHole(x, y, type);
16
+ }
17
+ /**
18
+ * Gets an array of all possible black hole types.
19
+ */
20
+ getAllBlackHoleTypes() {
21
+ return [...BLACK_HOLE_TYPES];
22
+ }
23
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview Defines all black hole shape rendering functions.
3
+ */
4
+ import type { BlackHle } from './black-hole';
5
+ export declare const drawStandardShape: (ctx: CanvasRenderingContext2D, hole: BlackHle) => void;
6
+ export declare const drawWarpedDiskShape: (ctx: CanvasRenderingContext2D, hole: BlackHle) => void;
7
+ export declare const drawLensingShape: (ctx: CanvasRenderingContext2D, hole: BlackHle) => void;
8
+ export declare const drawAccretionDiskShape: (ctx: CanvasRenderingContext2D, hole: BlackHle) => void;
9
+ export declare const drawVortexShape: (ctx: CanvasRenderingContext2D, hole: BlackHle) => void;