@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,246 @@
1
+
2
+ /**
3
+ * @fileoverview Defines the BlackHle class for the night sky simulation.
4
+ */
5
+
6
+ import { MassiveBody } from '../base/massive-body';
7
+ import type { Hoverable } from '../base/hoverable';
8
+ import { DEBUG_CONFIG } from '../config/simulation';
9
+ import { Star } from './star';
10
+ import { drawAccretionDiskShape, drawLensingShape, drawVortexShape, drawWarpedDiskShape, drawStandardShape } from './black-hole-shapes';
11
+ import { drawEnergyStream } from '../lib/energy-stream';
12
+ import { EFFECTS_CONFIG } from '../config/effects';
13
+
14
+ // --- Black Hole Configuration ---
15
+ export const BLACK_HOLE_CONFIG = {
16
+ RADIUS_BASE: 8,
17
+ RADIUS_VARIANCE: 4,
18
+ MASS_MULTIPLIER: 500,
19
+ HOVER_TIMEOUT: 120,
20
+ INITIAL_VELOCITY_RANGE: 0.025,
21
+ ATTRACTION_RADIUS: 100, // Only attract other black holes within this distance
22
+ STAR_ATTRACTION_RADIUS: 100, // Only attract stars within this distance
23
+ DECELERATION_DURATION: 180, // 3 seconds at 60fps
24
+ PAUSE_DURATION: 1200, // 20 seconds at 60fps
25
+ ENERGY_STREAM_DISTANCE: 300,
26
+ PULSE_FREQUENCY_MIN: 0.5,
27
+ PULSE_FREQUENCY_VARIANCE: 1.0,
28
+ ACCRETION_DISK: {
29
+ GLOW_COLOR: 'rgba(180, 210, 255, 0.4)',
30
+ },
31
+ VORTEX: {
32
+ PARTICLE_COUNT: 40,
33
+ HOTSPOT_COUNT: 5,
34
+ VORTEX_COLOR_INNER: 'rgba(255, 100, 0, 0.8)',
35
+ VORTEX_COLOR_MID: 'rgba(200, 50, 0, 0.3)',
36
+ VORTEX_COLOR_OUTER: 'rgba(150, 20, 0, 0)',
37
+ },
38
+ WARPED_DISK: {
39
+ WARPED_LINE_COUNT: 10,
40
+ DISK_COLOR_INNER: 'rgba(255, 220, 150, 1)',
41
+ DISK_COLOR_OUTER: 'rgba(255, 150, 0, 0.8)',
42
+ }
43
+ };
44
+
45
+ let blackHoleCounter = 0;
46
+
47
+ export type BlackHoleType = 'LENSING' | 'ACCRETION_DISK' | 'VORTEX' | 'WARPED_DISK' | 'STANDARD';
48
+ type MergeState = 'IDLE' | 'DECELERATING' | 'PAUSED';
49
+
50
+ export class BlackHole extends MassiveBody implements Hoverable {
51
+ id: number;
52
+ name: string;
53
+ radius: number;
54
+ baseRadius: number;
55
+ color: string;
56
+ isHovered: boolean = false;
57
+ hoverRadius: number;
58
+ hoverTimer: number;
59
+ eatenStars: number;
60
+ type: BlackHoleType;
61
+ vortexAngle: number = 0;
62
+ warpAngle: number = 0;
63
+ accretionDiskAngle: number = 0;
64
+ mergeState: MergeState = 'IDLE';
65
+ mergeStateTimer: number = 0;
66
+ preDecelerationVx: number = 0;
67
+ preDecelerationVy: number = 0;
68
+ attractionTargets: BlackHole[] = [];
69
+ pulseFrequency: number;
70
+ pulsePhase: number;
71
+ nozzleLength: number = 0; // Cached nozzle length for stream effect
72
+ nozzleColor: string | CanvasGradient | null = null; // Cached nozzle color for stream effect
73
+ frameCount: number = 0; // Frame count for animations
74
+
75
+
76
+ constructor(x: number, y: number, type: BlackHoleType) {
77
+ const radius = Math.random() * BLACK_HOLE_CONFIG.RADIUS_VARIANCE + BLACK_HOLE_CONFIG.RADIUS_BASE;
78
+ const mass = radius * radius * BLACK_HOLE_CONFIG.MASS_MULTIPLIER;
79
+ super(x, y, radius, mass);
80
+
81
+ this.id = ++blackHoleCounter;
82
+ this.name = `BH-${this.id}`;
83
+ this.radius = radius;
84
+ this.baseRadius = radius;
85
+ this.color = '#000000';
86
+ this.hoverTimer = 0;
87
+ this.hoverRadius = this.radius * 5;
88
+ this.eatenStars = 0;
89
+ this.type = type;
90
+
91
+ this.vx = Math.random() * BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE * 2 - BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE;
92
+ this.vy = Math.random() * BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE * 2 - BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE;
93
+
94
+ this.pulseFrequency = Math.random() * BLACK_HOLE_CONFIG.PULSE_FREQUENCY_VARIANCE + BLACK_HOLE_CONFIG.PULSE_FREQUENCY_MIN;
95
+ this.pulsePhase = Math.random() * Math.PI * 2;
96
+ }
97
+
98
+ absorb(body: Star | BlackHole) {
99
+ if (body instanceof Star) {
100
+ this.eatenStars++;
101
+ } else if (body instanceof BlackHole) {
102
+ this.mergeState = 'DECELERATING';
103
+ this.mergeStateTimer = BLACK_HOLE_CONFIG.DECELERATION_DURATION;
104
+ this.preDecelerationVx = this.vx;
105
+ this.preDecelerationVy = this.vy;
106
+ }
107
+ this.mass += body.mass;
108
+ this.radius = Math.sqrt(this.mass / BLACK_HOLE_CONFIG.MASS_MULTIPLIER);
109
+ this.boundingRadius = this.radius;
110
+ this.hoverRadius = this.radius * 5;
111
+ }
112
+
113
+ reset(x: number, y: number, vx: number, vy: number) {
114
+ const newRadius = Math.random() * BLACK_HOLE_CONFIG.RADIUS_VARIANCE + BLACK_HOLE_CONFIG.RADIUS_BASE;
115
+ this.mass = newRadius * newRadius * BLACK_HOLE_CONFIG.MASS_MULTIPLIER;
116
+ this.radius = newRadius;
117
+ this.x = x;
118
+ this.y = y;
119
+ this.vx = vx;
120
+ this.vy = vy;
121
+ this.eatenStars = 0;
122
+ this.mergeState = 'IDLE';
123
+ this.mergeStateTimer = 0;
124
+ }
125
+
126
+ update(ctx: CanvasRenderingContext2D, width: number, height: number, frameCount: number) {
127
+ this.frameCount = frameCount;
128
+
129
+ // State machine for post-merger behavior
130
+ if (this.mergeState === 'DECELERATING') {
131
+ const decelerationFactor = Math.max(0, this.mergeStateTimer / BLACK_HOLE_CONFIG.DECELERATION_DURATION);
132
+ this.vx = this.preDecelerationVx * decelerationFactor;
133
+ this.vy = this.preDecelerationVy * decelerationFactor;
134
+ }
135
+
136
+ if (this.mergeState !== 'IDLE') {
137
+ this.mergeStateTimer--;
138
+ if (this.mergeStateTimer <= 0) {
139
+ if (this.mergeState === 'DECELERATING') {
140
+ this.vx = 0;
141
+ this.vy = 0;
142
+ this.mergeState = 'PAUSED';
143
+ this.mergeStateTimer = BLACK_HOLE_CONFIG.PAUSE_DURATION;
144
+ } else if (this.mergeState === 'PAUSED') {
145
+ this.mergeState = 'IDLE';
146
+ this.vx = Math.random() * BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE * 2 - BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE;
147
+ this.vy = Math.random() * BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE * 2 - BLACK_HOLE_CONFIG.INITIAL_VELOCITY_RANGE;
148
+ }
149
+ }
150
+ }
151
+
152
+ this.x += this.vx;
153
+ this.y += this.vy;
154
+
155
+ if (this.x < -this.radius) this.x = width + this.radius;
156
+ if (this.x > width + this.radius) this.x = -this.radius;
157
+ if (this.y < -this.radius) this.y = height + this.radius;
158
+ if (this.y > height + this.radius) this.y = -this.radius;
159
+
160
+ if(this.type === 'VORTEX') {
161
+ this.vortexAngle += 0.01;
162
+ }
163
+ if(this.type === 'WARPED_DISK') {
164
+ this.warpAngle += 0.002;
165
+ }
166
+ if(this.type === 'ACCRETION_DISK') {
167
+ this.accretionDiskAngle += 0.005 / 3;
168
+ }
169
+ }
170
+
171
+ private drawDebugInfo(ctx: CanvasRenderingContext2D) {
172
+ const startX = this.x + this.hoverRadius + 15;
173
+ const boxPadding = 10;
174
+ const lineHeight = 14;
175
+ const infoLines = [
176
+ `Name: ${this.name}`,
177
+ `Type: ${this.constructor.name} (${this.type})`,
178
+ `Radius: ${this.radius.toFixed(2)}`,
179
+ `Mass: ${this.mass.toFixed(2)}`,
180
+ `Position: (${this.x.toFixed(0)}, ${this.y.toFixed(0)})`,
181
+ `Stars Eaten: ${this.eatenStars}`,
182
+ `Merge State: ${this.mergeState} (${this.mergeStateTimer})`
183
+ ];
184
+
185
+ const boxHeight = infoLines.length * lineHeight + boxPadding * 2;
186
+ const boxWidth = Math.max(...infoLines.map(line => ctx.measureText(line).width)) + boxPadding * 2;
187
+ const boxY = this.y - boxHeight / 2;
188
+
189
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
190
+ ctx.strokeStyle = '#fff';
191
+ ctx.lineWidth = 1;
192
+ ctx.beginPath();
193
+ ctx.roundRect(startX, boxY, boxWidth, boxHeight, 5);
194
+ ctx.fill();
195
+ ctx.stroke();
196
+
197
+ ctx.fillStyle = 'white';
198
+ ctx.font = '10px "Roboto Mono", monospace';
199
+ ctx.textAlign = 'left';
200
+ ctx.textBaseline = 'top';
201
+
202
+ infoLines.forEach((line, index) => {
203
+ ctx.fillText(line, startX + boxPadding, boxY + boxPadding + index * lineHeight);
204
+ });
205
+ }
206
+
207
+ draw(ctx: CanvasRenderingContext2D, pulseTime: number, frameCount: number) {
208
+ this.frameCount = frameCount;
209
+ // 1. Draw streams from behind
210
+ if (EFFECTS_CONFIG.RENDER_INTER_HOLE_STREAMS && this.attractionTargets.length > 0) {
211
+ for (const target of this.attractionTargets) {
212
+ drawEnergyStream(ctx, this, target, pulseTime, frameCount);
213
+ }
214
+ }
215
+
216
+ // 2. Draw the main black hole shape
217
+ switch (this.type) {
218
+ case 'STANDARD':
219
+ drawStandardShape(ctx, this);
220
+ break;
221
+ case 'ACCRETION_DISK':
222
+ drawAccretionDiskShape(ctx, this);
223
+ break;
224
+ case 'VORTEX':
225
+ drawVortexShape(ctx, this);
226
+ break;
227
+ case 'WARPED_DISK':
228
+ drawWarpedDiskShape(ctx, this);
229
+ break;
230
+ case 'LENSING':
231
+ default:
232
+ drawLensingShape(ctx, this);
233
+ break;
234
+ }
235
+
236
+ // 3. Draw hover effect if hovered
237
+ this.isHovered = this.hoverTimer > 0;
238
+ if (this.isHovered) {
239
+ if (DEBUG_CONFIG.SHOW_DEBUG_INFO && DEBUG_CONFIG.SHOW_ENTITY_HOVER_INFO) {
240
+ this.drawDebugInfo(ctx);
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ export { BlackHole as BlackHle };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview Defines the ClanManager for the night sky simulation.
3
+ */
4
+ import { CLANS_CONFIG, type Clan } from './clans';
5
+
6
+ export class ClanManager {
7
+ private clans: Clan[] = [];
8
+ private lastAssignedClanIndex: number = -1;
9
+
10
+ constructor() {
11
+ this.initializeClans();
12
+ }
13
+
14
+ private initializeClans() {
15
+ this.clans = CLANS_CONFIG.map(config => ({
16
+ ...config,
17
+ fleets: [],
18
+ starships: [],
19
+ fleetsCount: 0,
20
+ starshipsCount: 0,
21
+ }));
22
+ }
23
+
24
+ public getClans(): Clan[] {
25
+ return this.clans;
26
+ }
27
+
28
+ public getClanForNewFleet(): Clan {
29
+ // Simple round-robin assignment for now
30
+ this.lastAssignedClanIndex = (this.lastAssignedClanIndex + 1) % this.clans.length;
31
+ return this.clans[this.lastAssignedClanIndex];
32
+ }
33
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @fileoverview Defines all clan properties for the night sky simulation.
3
+ */
4
+ import type { Fleet } from './fleet';
5
+ import type { Starship } from './starship';
6
+
7
+ export interface Clan {
8
+ name: string;
9
+ color: string;
10
+ icon: (ctx: CanvasRenderingContext2D, size: number) => void;
11
+ fleets: Fleet[];
12
+ starships: Starship[];
13
+ fleetsCount: number;
14
+ starshipsCount: number;
15
+ }
16
+
17
+ const drawIconCrescent = (ctx: CanvasRenderingContext2D, size: number) => {
18
+ ctx.beginPath();
19
+ ctx.arc(0, 0, size / 2, 0, Math.PI * 2);
20
+ ctx.fill();
21
+ ctx.save();
22
+ ctx.globalCompositeOperation = 'destination-out';
23
+ ctx.beginPath();
24
+ ctx.arc(size * 0.2, 0, size / 2.5, 0, Math.PI * 2);
25
+ ctx.fill();
26
+ ctx.restore();
27
+ };
28
+
29
+ const drawIconSpikes = (ctx: CanvasRenderingContext2D, size: number) => {
30
+ const points = 6;
31
+ ctx.beginPath();
32
+ for (let i = 0; i < points * 2; i++) {
33
+ const radius = i % 2 === 0 ? size / 2 : size / 4;
34
+ const angle = (i / points) * Math.PI;
35
+ ctx.lineTo(radius * Math.cos(angle), radius * Math.sin(angle));
36
+ }
37
+ ctx.closePath();
38
+ ctx.fill();
39
+ };
40
+
41
+ const drawIconTriangle = (ctx: CanvasRenderingContext2D, size: number) => {
42
+ const h = size * (Math.sqrt(3)/2);
43
+ ctx.beginPath();
44
+ ctx.moveTo(0, -h/2);
45
+ ctx.lineTo(-size/2, h/2);
46
+ ctx.lineTo(size/2, h/2);
47
+ ctx.closePath();
48
+ ctx.stroke();
49
+ };
50
+
51
+ const drawIconHexagon = (ctx: CanvasRenderingContext2D, size: number) => {
52
+ const r = size / 2;
53
+ ctx.beginPath();
54
+ ctx.moveTo(r * Math.cos(0), r * Math.sin(0));
55
+ for (let i = 1; i <= 6; i++) {
56
+ ctx.lineTo(r * Math.cos(i * 2 * Math.PI / 6), r * Math.sin(i * 2 * Math.PI / 6));
57
+ }
58
+ ctx.stroke();
59
+ };
60
+
61
+ const drawIconCross = (ctx: CanvasRenderingContext2D, size: number) => {
62
+ const armLength = size / 2;
63
+ ctx.beginPath();
64
+ ctx.moveTo(-armLength, 0);
65
+ ctx.lineTo(armLength, 0);
66
+ ctx.moveTo(0, -armLength);
67
+ ctx.lineTo(0, armLength);
68
+ ctx.stroke();
69
+ };
70
+
71
+
72
+ export const CLANS_CONFIG: Pick<Clan, 'name' | 'color' | 'icon'>[] = [
73
+ {
74
+ name: "Starkwood Sovereignty",
75
+ color: "#ADD8E6",
76
+ icon: drawIconCrescent
77
+ },
78
+ {
79
+ name: "Crimson Covenant",
80
+ color: "#FF6B6B",
81
+ icon: drawIconSpikes
82
+ },
83
+ {
84
+ name: "Golden Imperium",
85
+ color: "#FFD700",
86
+ icon: drawIconTriangle
87
+ },
88
+ {
89
+ name: "Azure Assembly",
90
+ color: "#87CEEB",
91
+ icon: drawIconHexagon
92
+ },
93
+ {
94
+ name: "Veridian Syndicate",
95
+ color: "#3D9970",
96
+ icon: drawIconCross
97
+ }
98
+ ];
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @fileoverview Defines the Comet class for the night sky simulation.
3
+ */
4
+
5
+ import { BoundedBody } from '../base/bounded-body';
6
+ import { STAR_CONFIG } from './star';
7
+
8
+ // --- Comet Configuration ---
9
+ export const COMET_CONFIG = {
10
+ COUNT: 1,
11
+ DORMANT_LIFE_MIN: 1600,
12
+ DORMANT_LIFE_VARIANCE: 6400,
13
+ MIN_RADIUS: 1,
14
+ RADIUS_VARIANCE: 1.5,
15
+ SPEED_MIN: 3,
16
+ SPEED_VARIANCE: 4,
17
+ MAX_LIFE_MIN: 150,
18
+ MAX_LIFE_VARIANCE: 200,
19
+ };
20
+
21
+ export class Comet extends BoundedBody {
22
+ radius: number;
23
+ color: string;
24
+ life: number;
25
+ maxLife: number;
26
+ dormant: boolean;
27
+ dormantLife: number;
28
+
29
+ constructor(width: number, height: number) {
30
+ super(0, 0, 0); // Temporary initial values
31
+ this.radius = 0;
32
+ this.color = '';
33
+ this.life = 0;
34
+ this.maxLife = 0;
35
+ this.dormant = true;
36
+ this.dormantLife = Math.random() * COMET_CONFIG.DORMANT_LIFE_VARIANCE + COMET_CONFIG.DORMANT_LIFE_MIN; // Start dormant
37
+ this.reset(width, height);
38
+ }
39
+
40
+ reset(width: number, height: number) {
41
+ this.x = Math.random() * width;
42
+ this.y = Math.random() > 0.5 ? -50 : height + 50;
43
+ this.radius = Math.random() * COMET_CONFIG.RADIUS_VARIANCE + COMET_CONFIG.MIN_RADIUS;
44
+ this.boundingRadius = this.radius;
45
+ const angle = Math.random() * Math.PI * 0.4 - Math.PI * 0.2;
46
+ const speed = Math.random() * COMET_CONFIG.SPEED_VARIANCE + COMET_CONFIG.SPEED_MIN;
47
+ this.vx = Math.sin(angle) * speed;
48
+ this.vy = Math.cos(angle) * speed * (this.y > height ? -1 : 1);
49
+ this.color = STAR_CONFIG.RARE_STAR_COLORS[Math.floor(Math.random() * STAR_CONFIG.RARE_STAR_COLORS.length)];
50
+ this.maxLife = (Math.random() * COMET_CONFIG.MAX_LIFE_VARIANCE + COMET_CONFIG.MAX_LIFE_MIN);
51
+ this.life = this.maxLife;
52
+ this.dormant = false;
53
+ this.dormantLife = 0;
54
+ }
55
+
56
+ update(ctx: CanvasRenderingContext2D, width: number, height: number) {
57
+ if (this.dormant) {
58
+ this.dormantLife--;
59
+ if (this.dormantLife <= 0) {
60
+ this.reset(width, height);
61
+ }
62
+ return;
63
+ }
64
+
65
+ this.x += this.vx;
66
+ this.y += this.vy;
67
+ this.life--;
68
+
69
+ if (this.life <= 0) {
70
+ this.dormant = true;
71
+ this.dormantLife = Math.random() * COMET_CONFIG.DORMANT_LIFE_VARIANCE + COMET_CONFIG.DORMANT_LIFE_MIN;
72
+ }
73
+ }
74
+
75
+ draw(ctx: CanvasRenderingContext2D) {
76
+ if (this.dormant) return;
77
+
78
+ const tailLength = this.radius * 30 * (this.life / this.maxLife);
79
+
80
+ ctx.beginPath();
81
+ ctx.globalAlpha = this.life / this.maxLife * 0.8;
82
+
83
+ const gradient = ctx.createLinearGradient(this.x, this.y, this.x - this.vx * tailLength, this.y - this.vy * tailLength);
84
+ gradient.addColorStop(0, this.color);
85
+ gradient.addColorStop(1, "rgba(0,0,0,0)");
86
+
87
+ ctx.strokeStyle = gradient;
88
+ ctx.lineWidth = this.radius;
89
+ ctx.lineCap = 'round';
90
+
91
+ ctx.moveTo(this.x, this.y);
92
+ ctx.lineTo(this.x - this.vx * (tailLength / 10), this.y - this.vy * (tailLength / 10));
93
+ ctx.stroke();
94
+
95
+ ctx.beginPath();
96
+ ctx.fillStyle = this.color;
97
+ ctx.arc(this.x, this.y, this.radius / 2, 0, Math.PI * 2);
98
+ ctx.fill();
99
+
100
+ ctx.globalAlpha = 1;
101
+ }
102
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @fileoverview Defines the DockingPoint class for the night sky simulation.
3
+ */
4
+
5
+ import { CelestialBody } from '../base/celestial-body';
6
+ import { Star } from './star';
7
+
8
+ let dockingPointCounter = 0;
9
+
10
+ export class DockingPoint {
11
+ id: number;
12
+ parent: CelestialBody | Star;
13
+ // Relative coordinates to the parent
14
+ relativeX: number;
15
+ relativeY: number;
16
+
17
+ constructor(parent: CelestialBody | Star, relativeX: number, relativeY: number) {
18
+ this.id = ++dockingPointCounter;
19
+ this.parent = parent;
20
+ this.relativeX = relativeX;
21
+ this.relativeY = relativeY;
22
+ }
23
+
24
+ /**
25
+ * Gets the absolute world coordinates of the docking point.
26
+ * @returns {{x: number, y: number}} The absolute position.
27
+ */
28
+ getAbsolutePosition(): { x: number, y: number } {
29
+ return {
30
+ x: this.parent.x + this.relativeX,
31
+ y: this.parent.y + this.relativeY
32
+ };
33
+ }
34
+ }