@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,85 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Defines the RingedPlanet class, a specialized type of Star.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Star, STAR_CONFIG } from './star';
|
|
7
|
+
import { DockingPoint } from './docking-point';
|
|
8
|
+
import { Ring } from './ring';
|
|
9
|
+
|
|
10
|
+
// --- Ringed Planet Configuration ---
|
|
11
|
+
export const RINGED_PLANET_CONFIG = {
|
|
12
|
+
RING_COUNT_MIN: 1,
|
|
13
|
+
RING_COUNT_MAX: 2,
|
|
14
|
+
RING_RADIUS_MULTIPLIER_MIN: 2.2,
|
|
15
|
+
RING_RADIUS_MULTIPLIER_MAX: 2.8,
|
|
16
|
+
RING_THICKNESS_MIN: 0.2,
|
|
17
|
+
RING_THICKNESS_MAX: 0.8,
|
|
18
|
+
RING_ALPHA: 0.7,
|
|
19
|
+
RING_DOTTED_CHANCE: 0.3, // Chance for a ring to be dotted
|
|
20
|
+
DOCKING_POINTS_PER_RING: 4,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let ringedPlanetCounter = 0;
|
|
24
|
+
|
|
25
|
+
export class RingedPlanet extends Star {
|
|
26
|
+
rings: Ring[];
|
|
27
|
+
|
|
28
|
+
constructor(x: number, y: number, ringCount?: number) {
|
|
29
|
+
super(x, y);
|
|
30
|
+
this.name = `RP-${++ringedPlanetCounter}`;
|
|
31
|
+
this.rings = [];
|
|
32
|
+
this.dockingPoints = []; // A planet itself has no docking points, they are on rings/orbits.
|
|
33
|
+
this.orbits = []; // Clear star-generated orbits
|
|
34
|
+
this.hasAura = Math.random() < STAR_CONFIG.AURA_CHANCE;
|
|
35
|
+
|
|
36
|
+
const finalRingCount = ringCount !== undefined
|
|
37
|
+
? ringCount
|
|
38
|
+
: Math.floor(Math.random() * (RINGED_PLANET_CONFIG.RING_COUNT_MAX - RINGED_PLANET_CONFIG.RING_COUNT_MIN + 1)) +
|
|
39
|
+
RINGED_PLANET_CONFIG.RING_COUNT_MIN;
|
|
40
|
+
|
|
41
|
+
let maxRingRadius = this.radius;
|
|
42
|
+
for (let i = 0; i < finalRingCount; i++) {
|
|
43
|
+
const radiusMultiplier =
|
|
44
|
+
(Math.random() * (RINGED_PLANET_CONFIG.RING_RADIUS_MULTIPLIER_MAX - RINGED_PLANET_CONFIG.RING_RADIUS_MULTIPLIER_MIN) +
|
|
45
|
+
RINGED_PLANET_CONFIG.RING_RADIUS_MULTIPLIER_MIN) + i * 3.0; // Stagger rings
|
|
46
|
+
|
|
47
|
+
const ringRadius = this.radius * radiusMultiplier;
|
|
48
|
+
maxRingRadius = Math.max(maxRingRadius, ringRadius);
|
|
49
|
+
const ringColor = this.color.startsWith('rgba')
|
|
50
|
+
? this.color.replace(/, [0-9\.]+$/, `, ${RINGED_PLANET_CONFIG.RING_ALPHA})`)
|
|
51
|
+
: `rgba(255, 255, 255, ${RINGED_PLANET_CONFIG.RING_ALPHA})`;
|
|
52
|
+
const isDotted = Math.random() < RINGED_PLANET_CONFIG.RING_DOTTED_CHANCE;
|
|
53
|
+
|
|
54
|
+
const ring = new Ring(this, ringRadius, Math.random() * (RINGED_PLANET_CONFIG.RING_THICKNESS_MAX - RINGED_PLANET_CONFIG.RING_THICKNESS_MIN) + RINGED_PLANET_CONFIG.RING_THICKNESS_MIN, ringColor, isDotted);
|
|
55
|
+
this.rings.push(ring);
|
|
56
|
+
|
|
57
|
+
// Add docking points along the rings
|
|
58
|
+
const pointsOnRing = RINGED_PLANET_CONFIG.DOCKING_POINTS_PER_RING;
|
|
59
|
+
for(let j = 0; j < pointsOnRing; j++) {
|
|
60
|
+
const angle = (j / pointsOnRing) * Math.PI * 2;
|
|
61
|
+
const dx = Math.cos(angle) * ringRadius;
|
|
62
|
+
const dy = Math.sin(angle) * ringRadius;
|
|
63
|
+
this.dockingPoints.push(new DockingPoint(this, dx, dy));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Update hoverRadius to encompass the rings
|
|
68
|
+
this.hoverRadius = maxRingRadius + 10;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
draw(ctx: CanvasRenderingContext2D, pulseTime: number) {
|
|
72
|
+
// Call the parent draw method to draw the planet body itself on top
|
|
73
|
+
this.drawBase(ctx, pulseTime);
|
|
74
|
+
|
|
75
|
+
// Draw rings
|
|
76
|
+
this.rings.forEach(ring => {
|
|
77
|
+
ring.draw(ctx);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Draw orbits
|
|
81
|
+
this.orbits.forEach(orbit => {
|
|
82
|
+
orbit.draw(ctx);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the SectorGrid class for the background grid.
|
|
3
|
+
*/
|
|
4
|
+
import { CelestialBody } from '../base/celestial-body';
|
|
5
|
+
import { GRID_CONFIG } from '../config/grid';
|
|
6
|
+
|
|
7
|
+
export class SectorGrid extends CelestialBody {
|
|
8
|
+
private numCols: number = 0;
|
|
9
|
+
private numRows: number = 0;
|
|
10
|
+
private sectorWidth: number = 0;
|
|
11
|
+
private sectorHeight: number = 0;
|
|
12
|
+
private width: number = 0;
|
|
13
|
+
private height: number = 0;
|
|
14
|
+
|
|
15
|
+
constructor(width: number, height: number) {
|
|
16
|
+
super(0, 0);
|
|
17
|
+
this.resize(width, height);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public resize(width: number, height: number) {
|
|
21
|
+
this.width = width;
|
|
22
|
+
this.height = height;
|
|
23
|
+
this.numCols = Math.ceil(width / GRID_CONFIG.SECTOR_SIZE);
|
|
24
|
+
this.numRows = Math.ceil(height / GRID_CONFIG.SECTOR_SIZE);
|
|
25
|
+
this.sectorWidth = width / this.numCols;
|
|
26
|
+
this.sectorHeight = height / this.numRows;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
update(context: CanvasRenderingContext2D, width: number, height: number) {
|
|
30
|
+
// update width/height in case it changed and resize wasn't called (defensive)
|
|
31
|
+
if (this.width !== width || this.height !== height) {
|
|
32
|
+
this.resize(width, height);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
draw(ctx: CanvasRenderingContext2D) {
|
|
37
|
+
if (this.width === 0 || this.height === 0) return;
|
|
38
|
+
|
|
39
|
+
ctx.save();
|
|
40
|
+
ctx.strokeStyle = GRID_CONFIG.LINE_COLOR;
|
|
41
|
+
ctx.lineWidth = GRID_CONFIG.LINE_WIDTH;
|
|
42
|
+
ctx.setLineDash([2, 4]);
|
|
43
|
+
|
|
44
|
+
// Draw vertical lines
|
|
45
|
+
for (let i = 1; i < this.numCols; i++) {
|
|
46
|
+
const x = i * this.sectorWidth;
|
|
47
|
+
ctx.beginPath();
|
|
48
|
+
ctx.moveTo(x, 0);
|
|
49
|
+
ctx.lineTo(x, this.height);
|
|
50
|
+
ctx.stroke();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Draw horizontal lines
|
|
54
|
+
for (let i = 1; i < this.numRows; i++) {
|
|
55
|
+
const y = i * this.sectorHeight;
|
|
56
|
+
ctx.beginPath();
|
|
57
|
+
ctx.moveTo(0, y);
|
|
58
|
+
ctx.lineTo(this.width, y);
|
|
59
|
+
ctx.stroke();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ctx.setLineDash([]);
|
|
63
|
+
|
|
64
|
+
// Draw sector names
|
|
65
|
+
ctx.fillStyle = GRID_CONFIG.FONT_COLOR;
|
|
66
|
+
ctx.font = GRID_CONFIG.FONT;
|
|
67
|
+
ctx.textAlign = 'center';
|
|
68
|
+
ctx.textBaseline = 'middle';
|
|
69
|
+
|
|
70
|
+
for (let row = 0; row < this.numRows; row++) {
|
|
71
|
+
for (let col = 0; col < this.numCols; col++) {
|
|
72
|
+
const sectorName = `${String.fromCharCode(65 + row)}-${col + 1}`;
|
|
73
|
+
const centerX = col * this.sectorWidth + this.sectorWidth / 2;
|
|
74
|
+
const centerY = row * this.sectorHeight + this.sectorHeight / 2;
|
|
75
|
+
ctx.fillText(sectorName, centerX, centerY);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
ctx.restore();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
|
|
2
|
+
import { Star, STAR_CONFIG } from './star';
|
|
3
|
+
import { Pulsar, PULSAR_CONFIG } from './pulsar';
|
|
4
|
+
import { RingedPlanet, RINGED_PLANET_CONFIG } from './ringed-planet';
|
|
5
|
+
|
|
6
|
+
interface StarOptions {
|
|
7
|
+
radius?: number;
|
|
8
|
+
hasAura?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface RingedPlanetOptions {
|
|
12
|
+
ringCount?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class StarFactory {
|
|
16
|
+
/**
|
|
17
|
+
* Creates a star of a random type (Standard, Pulsar, or Ringed Planet).
|
|
18
|
+
*/
|
|
19
|
+
createRandomStar(x: number, y: number): Star {
|
|
20
|
+
const rand = Math.random();
|
|
21
|
+
if (rand < 0.02) {
|
|
22
|
+
return this.createPulsar(x, y);
|
|
23
|
+
} else if (rand < 0.02 + 0.03) {
|
|
24
|
+
return this.createRingedPlanet(x, y);
|
|
25
|
+
} else {
|
|
26
|
+
return this.createStar(x, y);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a standard star.
|
|
32
|
+
*/
|
|
33
|
+
createStar(x: number, y: number, options: StarOptions = {}): Star {
|
|
34
|
+
const star = new Star(x, y);
|
|
35
|
+
if (options.radius) {
|
|
36
|
+
star.radius = options.radius;
|
|
37
|
+
star.baseRadius = options.radius;
|
|
38
|
+
}
|
|
39
|
+
if (options.hasAura !== undefined) {
|
|
40
|
+
star.hasAura = options.hasAura;
|
|
41
|
+
}
|
|
42
|
+
return star;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a pulsar star.
|
|
47
|
+
*/
|
|
48
|
+
createPulsar(x: number, y: number): Pulsar {
|
|
49
|
+
return new Pulsar(x, y);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a ringed planet.
|
|
54
|
+
*/
|
|
55
|
+
createRingedPlanet(x: number, y: number, options: RingedPlanetOptions = {}): RingedPlanet {
|
|
56
|
+
const planet = new RingedPlanet(x, y, options.ringCount);
|
|
57
|
+
return planet;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Defines the Star class for the night sky simulation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { MassiveBody } from '../base/massive-body';
|
|
7
|
+
import { DockingPoint } from './docking-point';
|
|
8
|
+
import { Orbit, ORBIT_CONFIG } from './orbit';
|
|
9
|
+
import type { Hoverable } from '../base/hoverable';
|
|
10
|
+
import { DEBUG_CONFIG } from '../config/simulation';
|
|
11
|
+
import type { BlackHole } from './black-hole';
|
|
12
|
+
|
|
13
|
+
// --- Star Configuration ---
|
|
14
|
+
export const STAR_CONFIG = {
|
|
15
|
+
RARE_STAR_CHANCE: 0.1,
|
|
16
|
+
RARE_STAR_COLORS: ['#FFD700', '#ADD8E6', '#FFB6C1', '#87CEEB', '#DDA0DD', '#ffabab', '#a6d9ff'],
|
|
17
|
+
AURA_CHANCE: 0.15,
|
|
18
|
+
RINGED_PLANET_CHANCE: 0.03, // Chance to spawn a ringed planet
|
|
19
|
+
MIN_RADIUS: 0.5,
|
|
20
|
+
RADIUS_VARIANCE: 1.1,
|
|
21
|
+
INITIAL_VELOCITY_RANGE: 0.05,
|
|
22
|
+
INTERACTION_ACCELERATION: 0.02,
|
|
23
|
+
VELOCITY_DECAY: 0.96,
|
|
24
|
+
HOVER_RADIUS_MULTIPLIER: 8,
|
|
25
|
+
HOVER_GLOW_COLOR: 'rgba(255, 255, 255, 0.7)',
|
|
26
|
+
HOVER_TIMEOUT: 120, // 2 seconds in frames
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let starCounter = 0;
|
|
30
|
+
|
|
31
|
+
export class Star extends MassiveBody implements Hoverable {
|
|
32
|
+
id: number;
|
|
33
|
+
name: string;
|
|
34
|
+
radius: number;
|
|
35
|
+
baseRadius: number;
|
|
36
|
+
ivx: number;
|
|
37
|
+
ivy: number;
|
|
38
|
+
color: string;
|
|
39
|
+
hasAura: boolean;
|
|
40
|
+
dockingPoints: DockingPoint[];
|
|
41
|
+
orbits: Orbit[];
|
|
42
|
+
isHovered: boolean = false;
|
|
43
|
+
hoverRadius: number;
|
|
44
|
+
hoverTimer: number;
|
|
45
|
+
respawnTimer: number;
|
|
46
|
+
|
|
47
|
+
constructor(x: number, y: number) {
|
|
48
|
+
const radius = Math.random() * STAR_CONFIG.RADIUS_VARIANCE + STAR_CONFIG.MIN_RADIUS;
|
|
49
|
+
const mass = radius * radius; // Mass is proportional to the square of the radius
|
|
50
|
+
super(x, y, radius, mass);
|
|
51
|
+
|
|
52
|
+
this.id = ++starCounter;
|
|
53
|
+
this.name = `S-${this.id}`;
|
|
54
|
+
this.radius = radius;
|
|
55
|
+
this.baseRadius = this.radius;
|
|
56
|
+
this.ivx = Math.random() * STAR_CONFIG.INITIAL_VELOCITY_RANGE * 2 - STAR_CONFIG.INITIAL_VELOCITY_RANGE;
|
|
57
|
+
this.ivy = Math.random() * STAR_CONFIG.INITIAL_VELOCITY_RANGE * 2 - STAR_CONFIG.INITIAL_VELOCITY_RANGE;
|
|
58
|
+
this.hasAura = Math.random() < STAR_CONFIG.AURA_CHANCE;
|
|
59
|
+
|
|
60
|
+
const isRare = Math.random() < STAR_CONFIG.RARE_STAR_CHANCE;
|
|
61
|
+
this.color = isRare
|
|
62
|
+
? STAR_CONFIG.RARE_STAR_COLORS[Math.floor(Math.random() * STAR_CONFIG.RARE_STAR_COLORS.length)]
|
|
63
|
+
: 'rgba(255, 255, 255, 0.8)';
|
|
64
|
+
|
|
65
|
+
this.dockingPoints = [];
|
|
66
|
+
this.orbits = [];
|
|
67
|
+
|
|
68
|
+
this.hoverTimer = 0;
|
|
69
|
+
this.respawnTimer = 0;
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const orbitCount = Math.floor(Math.random() * (ORBIT_CONFIG.COUNT_MAX - ORBIT_CONFIG.COUNT_MIN + 1)) + ORBIT_CONFIG.COUNT_MIN;
|
|
73
|
+
let maxOrbitRadius = this.radius;
|
|
74
|
+
for (let i = 0; i < orbitCount; i++) {
|
|
75
|
+
const radiusMultiplier = (Math.random() * (ORBIT_CONFIG.RADIUS_MULTIPLIER_MAX - ORBIT_CONFIG.RADIUS_MULTIPLIER_MIN) + ORBIT_CONFIG.RADIUS_MULTIPLIER_MIN);
|
|
76
|
+
const orbitRadius = this.radius * radiusMultiplier;
|
|
77
|
+
maxOrbitRadius = Math.max(maxOrbitRadius, orbitRadius);
|
|
78
|
+
const orbitColor = this.color.startsWith('rgba')
|
|
79
|
+
? this.color.replace(/, [0-9\.]+$/, `, ${ORBIT_CONFIG.ALPHA})`)
|
|
80
|
+
: `rgba(255, 255, 255, ${ORBIT_CONFIG.ALPHA})`;
|
|
81
|
+
|
|
82
|
+
const orbit = new Orbit(this, orbitRadius, ORBIT_CONFIG.THICKNESS, orbitColor);
|
|
83
|
+
this.orbits.push(orbit);
|
|
84
|
+
this.dockingPoints.push(...orbit.dockingPoints);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.hoverRadius = maxOrbitRadius + 10;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
isEatenBy(blackHole: BlackHole): boolean {
|
|
91
|
+
const dx = blackHole.x - this.x;
|
|
92
|
+
const dy = blackHole.y - this.y;
|
|
93
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
94
|
+
return distance < blackHole.radius;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
respawn(newX: number, newY: number): void {
|
|
98
|
+
this.x = newX;
|
|
99
|
+
this.y = newY;
|
|
100
|
+
this.vx = 0;
|
|
101
|
+
this.vy = 0;
|
|
102
|
+
this.ivx = Math.random() * STAR_CONFIG.INITIAL_VELOCITY_RANGE * 2 - STAR_CONFIG.INITIAL_VELOCITY_RANGE;
|
|
103
|
+
this.ivy = Math.random() * STAR_CONFIG.INITIAL_VELOCITY_RANGE * 2 - STAR_CONFIG.INITIAL_VELOCITY_RANGE;
|
|
104
|
+
this.respawnTimer = 60; // 60 frames = ~1 second
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
update(
|
|
109
|
+
ctx: CanvasRenderingContext2D,
|
|
110
|
+
width: number,
|
|
111
|
+
height: number,
|
|
112
|
+
mouse: { dx: number; dy: number },
|
|
113
|
+
interactive: boolean,
|
|
114
|
+
isMouseInside: boolean,
|
|
115
|
+
pulseTime?: number,
|
|
116
|
+
) {
|
|
117
|
+
if (this.respawnTimer > 0) {
|
|
118
|
+
this.respawnTimer--;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (interactive && isMouseInside) {
|
|
123
|
+
const acc = STAR_CONFIG.INTERACTION_ACCELERATION;
|
|
124
|
+
this.vx += mouse.dx * acc;
|
|
125
|
+
this.vy += mouse.dy * acc;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.vx *= STAR_CONFIG.VELOCITY_DECAY;
|
|
129
|
+
this.vy *= STAR_CONFIG.VELOCITY_DECAY;
|
|
130
|
+
|
|
131
|
+
this.x += this.vx + this.ivx;
|
|
132
|
+
this.y += this.vy + this.ivy;
|
|
133
|
+
|
|
134
|
+
if (this.x < 0) this.x = width;
|
|
135
|
+
if (this.x > width) this.x = 0;
|
|
136
|
+
if (this.y < 0) this.y = height;
|
|
137
|
+
if (this.y > height) this.y = 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private drawDebugInfo(ctx: CanvasRenderingContext2D) {
|
|
141
|
+
const startX = this.x + this.hoverRadius + 15;
|
|
142
|
+
const boxPadding = 10;
|
|
143
|
+
const lineHeight = 14;
|
|
144
|
+
const infoLines = [
|
|
145
|
+
`Name: ${this.name}`,
|
|
146
|
+
`Type: ${this.constructor.name}`,
|
|
147
|
+
`Radius: ${this.radius.toFixed(2)}`,
|
|
148
|
+
`Mass: ${this.mass.toFixed(2)}`,
|
|
149
|
+
`Color: ${this.color}`,
|
|
150
|
+
`Position: (${this.x.toFixed(0)}, ${this.y.toFixed(0)})`,
|
|
151
|
+
`Velocity: (${this.vx.toFixed(2)}, ${this.vy.toFixed(2)})`,
|
|
152
|
+
`Orbits: ${this.orbits.length}`,
|
|
153
|
+
`Docking Points: ${this.dockingPoints.length}`,
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const boxHeight = infoLines.length * lineHeight + boxPadding * 2;
|
|
157
|
+
const boxWidth = Math.max(...infoLines.map(line => ctx.measureText(line).width)) + boxPadding * 2;
|
|
158
|
+
const boxY = this.y - boxHeight / 2;
|
|
159
|
+
|
|
160
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
|
161
|
+
ctx.strokeStyle = this.color;
|
|
162
|
+
ctx.lineWidth = 1;
|
|
163
|
+
ctx.beginPath();
|
|
164
|
+
ctx.roundRect(startX, boxY, boxWidth, boxHeight, 5);
|
|
165
|
+
ctx.fill();
|
|
166
|
+
ctx.stroke();
|
|
167
|
+
|
|
168
|
+
ctx.fillStyle = 'white';
|
|
169
|
+
ctx.font = '10px "Roboto Mono", monospace';
|
|
170
|
+
ctx.textAlign = 'left';
|
|
171
|
+
ctx.textBaseline = 'top';
|
|
172
|
+
|
|
173
|
+
infoLines.forEach((line, index) => {
|
|
174
|
+
ctx.fillText(line, startX + boxPadding, boxY + boxPadding + index * lineHeight);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// A new base drawing function that can be called by subclasses
|
|
179
|
+
drawBase(ctx: CanvasRenderingContext2D, pulseTime: number) {
|
|
180
|
+
// Orbits are drawn first, behind the star
|
|
181
|
+
this.orbits.forEach(orbit => orbit.draw(ctx));
|
|
182
|
+
|
|
183
|
+
// Draw hover effect if hovered
|
|
184
|
+
this.isHovered = this.hoverTimer > 0;
|
|
185
|
+
if (this.isHovered) {
|
|
186
|
+
ctx.strokeStyle = STAR_CONFIG.HOVER_GLOW_COLOR;
|
|
187
|
+
ctx.lineWidth = 0.5;
|
|
188
|
+
ctx.shadowColor = STAR_CONFIG.HOVER_GLOW_COLOR;
|
|
189
|
+
ctx.shadowBlur = 10;
|
|
190
|
+
ctx.beginPath();
|
|
191
|
+
ctx.arc(this.x, this.y, this.hoverRadius, 0, Math.PI * 2);
|
|
192
|
+
ctx.stroke();
|
|
193
|
+
ctx.shadowBlur = 0;
|
|
194
|
+
|
|
195
|
+
if (DEBUG_CONFIG.SHOW_DEBUG_INFO && DEBUG_CONFIG.SHOW_ENTITY_HOVER_INFO) {
|
|
196
|
+
this.drawDebugInfo(ctx);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
ctx.save();
|
|
201
|
+
ctx.fillStyle = this.color;
|
|
202
|
+
|
|
203
|
+
ctx.beginPath();
|
|
204
|
+
if (this.hasAura && !this.isHovered) { // Apply aura only if not hovered to avoid conflicting glows
|
|
205
|
+
ctx.shadowColor = this.color;
|
|
206
|
+
ctx.shadowBlur = 20;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
|
210
|
+
ctx.fill();
|
|
211
|
+
ctx.restore();
|
|
212
|
+
|
|
213
|
+
// Reset shadow blur after drawing the main body
|
|
214
|
+
if (this.hasAura || this.isHovered) {
|
|
215
|
+
ctx.shadowBlur = 0;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
draw(ctx: CanvasRenderingContext2D, pulseTime: number) {
|
|
220
|
+
this.drawBase(ctx, pulseTime);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// This file is intentionally left empty after its contents were merged into starship.ts to resolve a build issue.
|