@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,340 @@
|
|
|
1
|
+
// --- Formation Geometry Functions ---
|
|
2
|
+
const getStaggeredColumnPosition = (ships) => {
|
|
3
|
+
const offsets = [];
|
|
4
|
+
const numShips = ships.length;
|
|
5
|
+
const spacingY = 7.5; // Vertical spacing between ships
|
|
6
|
+
const spacingX = 5; // Horizontal zigzag spacing
|
|
7
|
+
// Calculate total height of the formation
|
|
8
|
+
const totalHeight = (numShips - 1) * spacingY;
|
|
9
|
+
const startY = -totalHeight / 2;
|
|
10
|
+
for (let i = 0; i < numShips; i++) {
|
|
11
|
+
const x = (i % 2 === 0 ? -1 : 1) * spacingX;
|
|
12
|
+
const y = startY + i * spacingY;
|
|
13
|
+
offsets.push({ x, y });
|
|
14
|
+
}
|
|
15
|
+
return offsets;
|
|
16
|
+
};
|
|
17
|
+
const getBoxPosition = (ships) => {
|
|
18
|
+
const numShips = ships.length;
|
|
19
|
+
const sideLength = Math.ceil(Math.sqrt(numShips));
|
|
20
|
+
const spacing = 10;
|
|
21
|
+
const offsets = [];
|
|
22
|
+
for (let i = 0; i < numShips; i++) {
|
|
23
|
+
const row = Math.floor(i / sideLength);
|
|
24
|
+
const col = i % sideLength;
|
|
25
|
+
const x = (col - (sideLength - 1) / 2) * spacing;
|
|
26
|
+
const y = (row - (sideLength - 1) / 2) * spacing;
|
|
27
|
+
offsets.push({ x, y });
|
|
28
|
+
}
|
|
29
|
+
return offsets;
|
|
30
|
+
};
|
|
31
|
+
const getVPointPosition = (ships) => {
|
|
32
|
+
// This is a complex strict formation. We'll sort ships by their role from the composition.
|
|
33
|
+
const dreadnought = ships.filter(s => s.shipClass.name === 'Dreadnought');
|
|
34
|
+
const battleships = ships.filter(s => s.shipClass.name === 'Battleship');
|
|
35
|
+
const heavyCruisers = ships.filter(s => s.shipClass.name === 'Heavy Cruiser');
|
|
36
|
+
const destroyers = ships.filter(s => s.shipClass.name === 'Destroyer');
|
|
37
|
+
const sortedShips = [...dreadnought, ...battleships, ...heavyCruisers, ...destroyers];
|
|
38
|
+
const offsets = [];
|
|
39
|
+
// 1 Dreadnought at the point
|
|
40
|
+
if (dreadnought.length > 0)
|
|
41
|
+
offsets.push({ x: 0, y: -20 });
|
|
42
|
+
// 4 Battleships in a smaller V
|
|
43
|
+
for (let i = 0; i < battleships.length; i++) {
|
|
44
|
+
const side = i % 2 === 0 ? -1 : 1;
|
|
45
|
+
const rank = Math.floor(i / 2) + 1;
|
|
46
|
+
offsets.push({ x: side * rank * 7.5, y: -20 + rank * 7.5 });
|
|
47
|
+
}
|
|
48
|
+
// 8 Heavy Cruisers flanking
|
|
49
|
+
for (let i = 0; i < heavyCruisers.length; i++) {
|
|
50
|
+
const side = i % 2 === 0 ? -1 : 1;
|
|
51
|
+
const rank = Math.floor(i / 2) + 1;
|
|
52
|
+
offsets.push({ x: side * (15 + rank * 6), y: -12.5 + rank * 6 });
|
|
53
|
+
}
|
|
54
|
+
// 12 Destroyers screening
|
|
55
|
+
for (let i = 0; i < destroyers.length; i++) {
|
|
56
|
+
const side = i % 2 === 0 ? -1 : 1;
|
|
57
|
+
const rank = Math.floor(i / 2) + 1;
|
|
58
|
+
offsets.push({ x: side * (30 + rank * 4), y: -7.5 + rank * 4 });
|
|
59
|
+
}
|
|
60
|
+
// This logic needs to map offsets back to original ships, so we return offsets for the sorted list.
|
|
61
|
+
return offsets;
|
|
62
|
+
};
|
|
63
|
+
const getSpherePosition = (ships) => {
|
|
64
|
+
const militaryShips = ships.filter(s => ['Capital', 'Cruiser', 'Escort', 'Small Craft'].includes(s.shipClass.category));
|
|
65
|
+
const civilianShips = ships.filter(s => !militaryShips.includes(s));
|
|
66
|
+
const offsets = [];
|
|
67
|
+
const radius = 8 + militaryShips.length * 1.25;
|
|
68
|
+
// Place military ships in a circle
|
|
69
|
+
militaryShips.forEach((ship, i) => {
|
|
70
|
+
const angle = (i / militaryShips.length) * 2 * Math.PI;
|
|
71
|
+
offsets.push({ x: Math.cos(angle) * radius, y: Math.sin(angle) * radius });
|
|
72
|
+
});
|
|
73
|
+
// Place civilian ships in the center
|
|
74
|
+
civilianShips.forEach((ship, i) => {
|
|
75
|
+
const angle = (i / civilianShips.length) * 2 * Math.PI;
|
|
76
|
+
offsets.push({ x: Math.cos(angle) * (radius * 0.25), y: Math.sin(angle) * (radius * 0.25) });
|
|
77
|
+
});
|
|
78
|
+
return offsets;
|
|
79
|
+
};
|
|
80
|
+
// --- Formation Definitions ---
|
|
81
|
+
const FORMATIONS = [
|
|
82
|
+
// --- Military Formations (Strict) ---
|
|
83
|
+
{
|
|
84
|
+
name: 'Dreadnought V-Point',
|
|
85
|
+
category: 'Military',
|
|
86
|
+
type: 'Strict',
|
|
87
|
+
description: 'A massive Dreadnought leads a wedge of 4 Battleships. 8 Heavy Cruisers flank the sides to provide broadside support, while a screen of 12 Destroyers prevents flanking.',
|
|
88
|
+
composition: [
|
|
89
|
+
{ shipClassName: 'Dreadnought', count: 1 },
|
|
90
|
+
{ shipClassName: 'Battleship', count: 4 },
|
|
91
|
+
{ shipClassName: 'Heavy Cruiser', count: 8 },
|
|
92
|
+
{ shipClassName: 'Destroyer', count: 12 },
|
|
93
|
+
],
|
|
94
|
+
getPosition: getVPointPosition,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'Carrier Strike Nest',
|
|
98
|
+
category: 'Military',
|
|
99
|
+
type: 'Strict',
|
|
100
|
+
description: 'Two Carriers are positioned at the center of a sphere formed by 6 Light Cruisers. 20 Corvettes orbit the carriers in a high-speed "swarm" to intercept incoming torpedoes.',
|
|
101
|
+
composition: [
|
|
102
|
+
{ shipClassName: 'Carrier', count: 2 },
|
|
103
|
+
{ shipClassName: 'Light Cruiser', count: 6 },
|
|
104
|
+
{ shipClassName: 'Corvette', count: 20 },
|
|
105
|
+
],
|
|
106
|
+
getPosition: getStaggeredColumnPosition,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'Battlecruiser Intercept Row',
|
|
110
|
+
category: 'Military',
|
|
111
|
+
type: 'Strict',
|
|
112
|
+
description: 'A fast-moving line of 6 Battlecruisers designed for "hit and run." They are supported by 4 Scout ships that stay ahead to provide targeting data for long-range spinal mounts.',
|
|
113
|
+
composition: [
|
|
114
|
+
{ shipClassName: 'Battlecruiser', count: 6 },
|
|
115
|
+
{ shipClassName: 'Pathfinder', count: 4 }, // Assuming Scout = Pathfinder
|
|
116
|
+
],
|
|
117
|
+
getPosition: getStaggeredColumnPosition,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'Assault Picket Line',
|
|
121
|
+
category: 'Military',
|
|
122
|
+
type: 'Strict',
|
|
123
|
+
description: '15 Frigates and 10 Destroyers are spaced in a wide, flat plane. This formation is used to block an entire orbital corridor, relying on overlapping sensor ranges.',
|
|
124
|
+
composition: [
|
|
125
|
+
{ shipClassName: 'Frigate', count: 15 },
|
|
126
|
+
{ shipClassName: 'Destroyer', count: 10 },
|
|
127
|
+
],
|
|
128
|
+
getPosition: getStaggeredColumnPosition,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'Dreadnought Brawler Pair',
|
|
132
|
+
category: 'Military',
|
|
133
|
+
type: 'Strict',
|
|
134
|
+
description: 'Two Dreadnoughts fly hull-to-hull, linking their heavy shields. They are screened by a tight group of 4 Heavy Cruisers to protect their rear engines.',
|
|
135
|
+
composition: [
|
|
136
|
+
{ shipClassName: 'Dreadnought', count: 2 },
|
|
137
|
+
{ shipClassName: 'Heavy Cruiser', count: 4 },
|
|
138
|
+
],
|
|
139
|
+
getPosition: getStaggeredColumnPosition,
|
|
140
|
+
},
|
|
141
|
+
// --- Military Formations (Free) ---
|
|
142
|
+
{
|
|
143
|
+
name: 'The Box',
|
|
144
|
+
category: 'Military',
|
|
145
|
+
type: 'Free',
|
|
146
|
+
description: 'Ships are arranged in a 3D cube. Any mix of heavy and light ships can fill the corners, creating a multi-directional fire zone.',
|
|
147
|
+
getPosition: getBoxPosition,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'The Double Echelon',
|
|
151
|
+
category: 'Military',
|
|
152
|
+
type: 'Free',
|
|
153
|
+
description: 'Ships are arranged in two parallel diagonal lines. This allows any set of ships to retreat in sequence while the next line covers them.',
|
|
154
|
+
getPosition: getStaggeredColumnPosition,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'The Orbital Ring',
|
|
158
|
+
category: 'Military',
|
|
159
|
+
type: 'Free',
|
|
160
|
+
description: 'Ships maintain a circular orbit around a fixed point (like a station). Can be held by as few as 3 ships or as many as 100.',
|
|
161
|
+
getPosition: getStaggeredColumnPosition,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'The Staggered Column',
|
|
165
|
+
category: 'Military',
|
|
166
|
+
type: 'Free',
|
|
167
|
+
description: 'Ships follow each other in a zigzag vertical line. This hides the true number of ships from forward sensors.',
|
|
168
|
+
getPosition: getStaggeredColumnPosition,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'The Dispersed Cloud',
|
|
172
|
+
category: 'Military',
|
|
173
|
+
type: 'Free',
|
|
174
|
+
description: 'Ships maintain maximum sensor range from one another. This "free" formation prevents a single nuclear or area-of-effect weapon from hitting more than one vessel.',
|
|
175
|
+
getPosition: getStaggeredColumnPosition,
|
|
176
|
+
},
|
|
177
|
+
// --- Mixed Formations (Strict) ---
|
|
178
|
+
{
|
|
179
|
+
name: 'Colony Vanguard',
|
|
180
|
+
category: 'Mixed',
|
|
181
|
+
type: 'Strict',
|
|
182
|
+
description: 'A Colony Ship at the center, guarded by 2 Battleships and 4 Frigates. 2 Surveyors lead the formation to scan for habitable zones.',
|
|
183
|
+
composition: [
|
|
184
|
+
{ shipClassName: 'Colony Ship', count: 1 },
|
|
185
|
+
{ shipClassName: 'Battleship', count: 2 },
|
|
186
|
+
{ shipClassName: 'Frigate', count: 4 },
|
|
187
|
+
{ shipClassName: 'Explorer', count: 2 }, // Assuming Surveyor = Explorer
|
|
188
|
+
],
|
|
189
|
+
getPosition: getStaggeredColumnPosition,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'The Refueling Phalanx',
|
|
193
|
+
category: 'Mixed',
|
|
194
|
+
type: 'Strict',
|
|
195
|
+
description: 'A Tanker is positioned directly behind a Dreadnought for protection. 4 Destroyers circle the pair to prevent "engine-sniping" by enemy fighters.',
|
|
196
|
+
composition: [
|
|
197
|
+
{ shipClassName: 'Hauler', count: 1 }, // Assuming Tanker = Hauler
|
|
198
|
+
{ shipClassName: 'Dreadnought', count: 1 },
|
|
199
|
+
{ shipClassName: 'Destroyer', count: 4 },
|
|
200
|
+
],
|
|
201
|
+
getPosition: getStaggeredColumnPosition,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'Extraction Screen',
|
|
205
|
+
category: 'Mixed',
|
|
206
|
+
type: 'Strict',
|
|
207
|
+
description: '5 Asteroid Miners work a field while 2 Monitors provide stationary defense. 4 Corvettes "patrol" the gaps between the miners.',
|
|
208
|
+
composition: [
|
|
209
|
+
{ shipClassName: 'Mining Vessel', count: 5 },
|
|
210
|
+
{ shipClassName: 'Monitor', count: 2 },
|
|
211
|
+
{ shipClassName: 'Corvette', count: 4 },
|
|
212
|
+
],
|
|
213
|
+
getPosition: getStaggeredColumnPosition,
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'Diplomatic Escort',
|
|
217
|
+
category: 'Mixed',
|
|
218
|
+
type: 'Strict',
|
|
219
|
+
description: 'A Diplomatic Courier is surrounded by 4 Light Cruisers in a diamond formation. A Hospital Ship follows at the rear in case of an ambush.',
|
|
220
|
+
composition: [
|
|
221
|
+
{ shipClassName: 'Diplomatic Courier', count: 1 },
|
|
222
|
+
{ shipClassName: 'Light Cruiser', count: 4 },
|
|
223
|
+
{ shipClassName: 'Hospital Ship', count: 1 },
|
|
224
|
+
],
|
|
225
|
+
getPosition: getStaggeredColumnPosition,
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'Salvage Sweep',
|
|
229
|
+
category: 'Mixed',
|
|
230
|
+
type: 'Strict',
|
|
231
|
+
description: 'Two Salvage Ships lead, followed by a Tender to store parts. A Heavy Cruiser provides long-range overwatch from a higher orbital plane.',
|
|
232
|
+
composition: [
|
|
233
|
+
{ shipClassName: 'Salvage Ship', count: 2 },
|
|
234
|
+
{ shipClassName: 'Hauler', count: 1 }, // Assuming Tender = Hauler
|
|
235
|
+
{ shipClassName: 'Heavy Cruiser', count: 1 },
|
|
236
|
+
],
|
|
237
|
+
getPosition: getStaggeredColumnPosition,
|
|
238
|
+
},
|
|
239
|
+
// --- Mixed Formations (Free) ---
|
|
240
|
+
{
|
|
241
|
+
name: 'The Sphere (Bubble)',
|
|
242
|
+
category: 'Mixed',
|
|
243
|
+
type: 'Free',
|
|
244
|
+
description: 'Civilian ships (like Haulers) stay at the dead center while any available military ships form a protective globe around them.',
|
|
245
|
+
getPosition: getSpherePosition,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'The Conical Tail',
|
|
249
|
+
category: 'Mixed',
|
|
250
|
+
type: 'Free',
|
|
251
|
+
description: 'A large military ship (like a Battleship) leads, with peaceful ships (like Freighters) following in its "sensor shadow" to remain undetected.',
|
|
252
|
+
getPosition: getStaggeredColumnPosition,
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'The Hub and Spoke',
|
|
256
|
+
category: 'Mixed',
|
|
257
|
+
type: 'Free',
|
|
258
|
+
description: 'A large ship or station acts as the "hub," with smaller ships (Shuttles, Pathfinders) flying back and forth along set "spokes."',
|
|
259
|
+
getPosition: getStaggeredColumnPosition,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'The Anchor Point',
|
|
263
|
+
category: 'Mixed',
|
|
264
|
+
type: 'Free',
|
|
265
|
+
description: 'One heavy ship remains stationary while all other ships (peaceful or military) orbit it in an erratic, unpredictable pattern to confuse attackers.',
|
|
266
|
+
getPosition: getStaggeredColumnPosition,
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: 'The Lateral Wall',
|
|
270
|
+
category: 'Mixed',
|
|
271
|
+
type: 'Free',
|
|
272
|
+
description: 'All ships, regardless of class, align on a single flat horizontal plane. This maximizes the fleet\'s ability to scan a large area of space simultaneously.',
|
|
273
|
+
getPosition: getStaggeredColumnPosition,
|
|
274
|
+
},
|
|
275
|
+
];
|
|
276
|
+
// --- Formation Registry ---
|
|
277
|
+
class FormationRegistry {
|
|
278
|
+
constructor() {
|
|
279
|
+
this.formations = FORMATIONS;
|
|
280
|
+
this.strictFormations = FORMATIONS.filter(f => f.type === 'Strict');
|
|
281
|
+
this.freeFormations = FORMATIONS.filter(f => f.type === 'Free');
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Finds a suitable formation for a given fleet based on its composition.
|
|
285
|
+
* @param fleetShips The list of starships in the fleet.
|
|
286
|
+
* @returns A formation, or null if no suitable formation is found.
|
|
287
|
+
*/
|
|
288
|
+
getFormationForFleet(fleetShips) {
|
|
289
|
+
// 1. Get the composition of the current fleet.
|
|
290
|
+
const fleetComposition = new Map();
|
|
291
|
+
fleetShips.forEach(ship => {
|
|
292
|
+
const name = ship.shipClass.name;
|
|
293
|
+
fleetComposition.set(name, (fleetComposition.get(name) || 0) + 1);
|
|
294
|
+
});
|
|
295
|
+
// 2. Try to find a matching strict formation.
|
|
296
|
+
const matchingStrictFormation = this.strictFormations.find(formation => {
|
|
297
|
+
// Check if the fleet has the exact number of ships required.
|
|
298
|
+
const totalShipsInFormation = formation.composition.reduce((sum, role) => sum + role.count, 0);
|
|
299
|
+
if (fleetShips.length !== totalShipsInFormation) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
// Check if the fleet has the correct types and counts of ships.
|
|
303
|
+
const formationComposition = new Map();
|
|
304
|
+
formation.composition.forEach(role => {
|
|
305
|
+
formationComposition.set(role.shipClassName, role.count);
|
|
306
|
+
});
|
|
307
|
+
if (formationComposition.size !== fleetComposition.size) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
for (const [shipName, count] of formationComposition) {
|
|
311
|
+
if (fleetComposition.get(shipName) !== count) {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return true;
|
|
316
|
+
});
|
|
317
|
+
if (matchingStrictFormation) {
|
|
318
|
+
return matchingStrictFormation;
|
|
319
|
+
}
|
|
320
|
+
// 3. If no strict formation matches, find a suitable free formation.
|
|
321
|
+
const hasCivilianShips = fleetShips.some(s => s.shipClass.category === 'Commerce & Logistics' ||
|
|
322
|
+
s.shipClass.category === 'Resource Extraction' ||
|
|
323
|
+
s.shipClass.category === 'Diplomatic & Civil');
|
|
324
|
+
const suitableFreeFormations = this.freeFormations.filter(f => {
|
|
325
|
+
return hasCivilianShips ? f.category === 'Mixed' : f.category === 'Military';
|
|
326
|
+
});
|
|
327
|
+
if (suitableFreeFormations.length > 0) {
|
|
328
|
+
return suitableFreeFormations[Math.floor(Math.random() * suitableFreeFormations.length)];
|
|
329
|
+
}
|
|
330
|
+
// 4. As a last resort, return any free formation.
|
|
331
|
+
if (this.freeFormations.length > 0) {
|
|
332
|
+
return this.freeFormations[Math.floor(Math.random() * this.freeFormations.length)];
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
getAllFormations() {
|
|
337
|
+
return this.formations;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
export const formationRegistry = new FormationRegistry();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Meteor class for the night sky simulation.
|
|
3
|
+
*/
|
|
4
|
+
import { BoundedBody } from '../base/bounded-body';
|
|
5
|
+
export declare const METEOR_CONFIG: {
|
|
6
|
+
SHOWER_COOLDOWN_MIN: number;
|
|
7
|
+
SHOWER_COOLDOWN_VARIANCE: number;
|
|
8
|
+
COUNT_MIN: number;
|
|
9
|
+
COUNT_VARIANCE: number;
|
|
10
|
+
SPEED_MIN: number;
|
|
11
|
+
SPEED_VARIANCE: number;
|
|
12
|
+
LIFESPAN_MIN: number;
|
|
13
|
+
LIFESPAN_VARIANCE: number;
|
|
14
|
+
MIN_RADIUS: number;
|
|
15
|
+
RADIUS_VARIANCE: number;
|
|
16
|
+
};
|
|
17
|
+
export declare class Meteor extends BoundedBody {
|
|
18
|
+
radius: number;
|
|
19
|
+
color: string;
|
|
20
|
+
life: number;
|
|
21
|
+
maxLife: number;
|
|
22
|
+
constructor(x: number, y: number, vx: number, vy: number, color: string);
|
|
23
|
+
update(): void;
|
|
24
|
+
isAlive(width: number, height: number): boolean;
|
|
25
|
+
draw(ctx: CanvasRenderingContext2D): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Meteor class for the night sky simulation.
|
|
3
|
+
*/
|
|
4
|
+
import { BoundedBody } from '../base/bounded-body';
|
|
5
|
+
// --- Meteor Shower Configuration ---
|
|
6
|
+
export const METEOR_CONFIG = {
|
|
7
|
+
SHOWER_COOLDOWN_MIN: 900, // 15 seconds in frames
|
|
8
|
+
SHOWER_COOLDOWN_VARIANCE: 900, // 15-30 seconds in frames
|
|
9
|
+
COUNT_MIN: 10,
|
|
10
|
+
COUNT_VARIANCE: 15,
|
|
11
|
+
SPEED_MIN: 3.2,
|
|
12
|
+
SPEED_VARIANCE: 6.4,
|
|
13
|
+
LIFESPAN_MIN: 20,
|
|
14
|
+
LIFESPAN_VARIANCE: 40,
|
|
15
|
+
MIN_RADIUS: 0.5,
|
|
16
|
+
RADIUS_VARIANCE: 1.2,
|
|
17
|
+
};
|
|
18
|
+
export class Meteor extends BoundedBody {
|
|
19
|
+
constructor(x, y, vx, vy, color) {
|
|
20
|
+
const radius = Math.random() * METEOR_CONFIG.RADIUS_VARIANCE + METEOR_CONFIG.MIN_RADIUS;
|
|
21
|
+
super(x, y, radius);
|
|
22
|
+
this.radius = radius;
|
|
23
|
+
this.vx = vx;
|
|
24
|
+
this.vy = vy;
|
|
25
|
+
this.color = color;
|
|
26
|
+
this.maxLife = Math.random() * METEOR_CONFIG.LIFESPAN_VARIANCE + METEOR_CONFIG.LIFESPAN_MIN;
|
|
27
|
+
this.life = this.maxLife;
|
|
28
|
+
}
|
|
29
|
+
update() {
|
|
30
|
+
this.x += this.vx;
|
|
31
|
+
this.y += this.vy;
|
|
32
|
+
this.life--;
|
|
33
|
+
}
|
|
34
|
+
isAlive(width, height) {
|
|
35
|
+
return this.life > 0 && this.x > 0 && this.x < width && this.y > 0 && this.y < height;
|
|
36
|
+
}
|
|
37
|
+
draw(ctx) {
|
|
38
|
+
const alpha = this.life / this.maxLife;
|
|
39
|
+
ctx.beginPath();
|
|
40
|
+
ctx.globalAlpha = alpha;
|
|
41
|
+
ctx.strokeStyle = this.color;
|
|
42
|
+
ctx.lineWidth = this.radius;
|
|
43
|
+
ctx.moveTo(this.x, this.y);
|
|
44
|
+
ctx.lineTo(this.x - this.vx * 2, this.y - this.vy * 2);
|
|
45
|
+
ctx.stroke();
|
|
46
|
+
ctx.globalAlpha = 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Nebula class for the night sky simulation.
|
|
3
|
+
*/
|
|
4
|
+
import { BoundedBody } from '../base/bounded-body';
|
|
5
|
+
export declare const NEBULA_CONFIG: {
|
|
6
|
+
COUNT: number;
|
|
7
|
+
COLORS: string[];
|
|
8
|
+
VELOCITY_RANGE: number;
|
|
9
|
+
RADIUS_BASE_FACTOR: number;
|
|
10
|
+
RADIUS_VARIANCE_FACTOR: number;
|
|
11
|
+
};
|
|
12
|
+
export declare class Nebula extends BoundedBody {
|
|
13
|
+
radius: number;
|
|
14
|
+
color: string;
|
|
15
|
+
constructor(x: number, y: number, width: number);
|
|
16
|
+
update(ctx: CanvasRenderingContext2D, width: number, height: number): void;
|
|
17
|
+
draw(ctx: CanvasRenderingContext2D): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Nebula class for the night sky simulation.
|
|
3
|
+
*/
|
|
4
|
+
import { BoundedBody } from '../base/bounded-body';
|
|
5
|
+
// --- Nebula Configuration ---
|
|
6
|
+
export const NEBULA_CONFIG = {
|
|
7
|
+
COUNT: 5,
|
|
8
|
+
COLORS: ['rgba(139, 0, 255, 0.15)', 'rgba(0, 191, 255, 0.15)', 'rgba(255, 105, 180, 0.15)'],
|
|
9
|
+
VELOCITY_RANGE: 0.1,
|
|
10
|
+
RADIUS_BASE_FACTOR: 1 / 6,
|
|
11
|
+
RADIUS_VARIANCE_FACTOR: 1 / 4,
|
|
12
|
+
};
|
|
13
|
+
export class Nebula extends BoundedBody {
|
|
14
|
+
constructor(x, y, width) {
|
|
15
|
+
const radius = Math.random() * (width * NEBULA_CONFIG.RADIUS_VARIANCE_FACTOR) + (width * NEBULA_CONFIG.RADIUS_BASE_FACTOR);
|
|
16
|
+
super(x, y, radius);
|
|
17
|
+
this.radius = radius;
|
|
18
|
+
this.vx = Math.random() * NEBULA_CONFIG.VELOCITY_RANGE * 2 - NEBULA_CONFIG.VELOCITY_RANGE;
|
|
19
|
+
this.vy = Math.random() * NEBULA_CONFIG.VELOCITY_RANGE * 2 - NEBULA_CONFIG.VELOCITY_RANGE;
|
|
20
|
+
this.color = NEBULA_CONFIG.COLORS[Math.floor(Math.random() * NEBULA_CONFIG.COLORS.length)];
|
|
21
|
+
}
|
|
22
|
+
update(ctx, width, height) {
|
|
23
|
+
this.x += this.vx;
|
|
24
|
+
this.y += this.vy;
|
|
25
|
+
if (this.x - this.radius > width)
|
|
26
|
+
this.x = -this.radius;
|
|
27
|
+
if (this.x + this.radius < 0)
|
|
28
|
+
this.x = width + this.radius;
|
|
29
|
+
if (this.y - this.radius > height)
|
|
30
|
+
this.y = -this.radius;
|
|
31
|
+
if (this.y + this.radius < 0)
|
|
32
|
+
this.y = height + this.radius;
|
|
33
|
+
}
|
|
34
|
+
draw(ctx) {
|
|
35
|
+
const gradient = ctx.createRadialGradient(this.x, this.y, this.radius * 0.2, this.x, this.y, this.radius);
|
|
36
|
+
gradient.addColorStop(0, this.color);
|
|
37
|
+
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
|
|
38
|
+
ctx.fillStyle = gradient;
|
|
39
|
+
ctx.beginPath();
|
|
40
|
+
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
|
41
|
+
ctx.fill();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Orbit class for the night sky simulation.
|
|
3
|
+
*/
|
|
4
|
+
import { Star } from './star';
|
|
5
|
+
import { DockingPoint } from './docking-point';
|
|
6
|
+
export declare const ORBIT_CONFIG: {
|
|
7
|
+
COUNT_MIN: number;
|
|
8
|
+
COUNT_MAX: number;
|
|
9
|
+
RADIUS_MULTIPLIER_MIN: number;
|
|
10
|
+
RADIUS_MULTIPLIER_MAX: number;
|
|
11
|
+
THICKNESS: number;
|
|
12
|
+
ALPHA: number;
|
|
13
|
+
DOCKING_POINTS_PER_ORBIT: number;
|
|
14
|
+
};
|
|
15
|
+
export declare class Orbit {
|
|
16
|
+
parent: Star;
|
|
17
|
+
radius: number;
|
|
18
|
+
thickness: number;
|
|
19
|
+
color: string;
|
|
20
|
+
dockingPoints: DockingPoint[];
|
|
21
|
+
constructor(parent: Star, radius: number, thickness: number, color: string);
|
|
22
|
+
draw(ctx: CanvasRenderingContext2D): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Orbit class for the night sky simulation.
|
|
3
|
+
*/
|
|
4
|
+
import { DockingPoint } from './docking-point';
|
|
5
|
+
// --- Orbit Configuration ---
|
|
6
|
+
export const ORBIT_CONFIG = {
|
|
7
|
+
COUNT_MIN: 1,
|
|
8
|
+
COUNT_MAX: 3,
|
|
9
|
+
RADIUS_MULTIPLIER_MIN: 3,
|
|
10
|
+
RADIUS_MULTIPLIER_MAX: 7,
|
|
11
|
+
THICKNESS: 0.1,
|
|
12
|
+
ALPHA: 0.0,
|
|
13
|
+
DOCKING_POINTS_PER_ORBIT: 8,
|
|
14
|
+
};
|
|
15
|
+
export class Orbit {
|
|
16
|
+
constructor(parent, radius, thickness, color) {
|
|
17
|
+
this.dockingPoints = [];
|
|
18
|
+
this.parent = parent;
|
|
19
|
+
this.radius = radius;
|
|
20
|
+
this.thickness = thickness;
|
|
21
|
+
this.color = color;
|
|
22
|
+
// Add docking points along the orbit
|
|
23
|
+
const pointsOnOrbit = ORBIT_CONFIG.DOCKING_POINTS_PER_ORBIT;
|
|
24
|
+
for (let j = 0; j < pointsOnOrbit; j++) {
|
|
25
|
+
const angle = (j / pointsOnOrbit) * Math.PI * 2;
|
|
26
|
+
const dx = Math.cos(angle) * this.radius;
|
|
27
|
+
const dy = Math.sin(angle) * this.radius;
|
|
28
|
+
this.dockingPoints.push(new DockingPoint(this.parent, dx, dy));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
draw(ctx) {
|
|
32
|
+
if (!this.parent || ORBIT_CONFIG.ALPHA === 0)
|
|
33
|
+
return;
|
|
34
|
+
ctx.save();
|
|
35
|
+
ctx.globalAlpha = ORBIT_CONFIG.ALPHA;
|
|
36
|
+
ctx.beginPath();
|
|
37
|
+
ctx.strokeStyle = this.color;
|
|
38
|
+
ctx.lineWidth = this.thickness;
|
|
39
|
+
ctx.arc(this.parent.x, this.parent.y, this.radius, 0, 2 * Math.PI);
|
|
40
|
+
ctx.stroke();
|
|
41
|
+
ctx.restore();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Pulsar class, a specialized type of Star.
|
|
3
|
+
*/
|
|
4
|
+
import { Star } from './star';
|
|
5
|
+
export declare const PULSAR_CONFIG: {
|
|
6
|
+
PULSAR_SPEED_MIN: number;
|
|
7
|
+
PULSAR_SPEED_VARIANCE: number;
|
|
8
|
+
};
|
|
9
|
+
export declare class Pulsar extends Star {
|
|
10
|
+
pulsarPhase: number;
|
|
11
|
+
pulsationSpeed: number;
|
|
12
|
+
constructor(x: number, y: number);
|
|
13
|
+
update(ctx: CanvasRenderingContext2D, width: number, height: number, mouse: {
|
|
14
|
+
dx: number;
|
|
15
|
+
dy: number;
|
|
16
|
+
}, interactive: boolean, isMouseInside: boolean, pulseTime?: number): void;
|
|
17
|
+
draw(ctx: CanvasRenderingContext2D, pulseTime: number): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Pulsar class, a specialized type of Star.
|
|
3
|
+
*/
|
|
4
|
+
import { Star, STAR_CONFIG } from './star';
|
|
5
|
+
// --- Pulsar Configuration ---
|
|
6
|
+
export const PULSAR_CONFIG = {
|
|
7
|
+
PULSAR_SPEED_MIN: 12.0,
|
|
8
|
+
PULSAR_SPEED_VARIANCE: 12.0,
|
|
9
|
+
};
|
|
10
|
+
let pulsarCounter = 0;
|
|
11
|
+
export class Pulsar extends Star {
|
|
12
|
+
constructor(x, y) {
|
|
13
|
+
super(x, y);
|
|
14
|
+
this.name = `P-${++pulsarCounter}`;
|
|
15
|
+
this.pulsarPhase = Math.random() * Math.PI * 2;
|
|
16
|
+
this.pulsationSpeed = Math.random() * PULSAR_CONFIG.PULSAR_SPEED_VARIANCE + PULSAR_CONFIG.PULSAR_SPEED_MIN;
|
|
17
|
+
this.hasAura = Math.random() < STAR_CONFIG.AURA_CHANCE;
|
|
18
|
+
}
|
|
19
|
+
update(ctx, width, height, mouse, interactive, isMouseInside, pulseTime) {
|
|
20
|
+
// Call the parent update method to handle movement only in the main simulation
|
|
21
|
+
if (width > 0 && height > 0) {
|
|
22
|
+
super.update(ctx, width, height, mouse, interactive, isMouseInside);
|
|
23
|
+
}
|
|
24
|
+
// Update radius for the live simulation instance
|
|
25
|
+
const effectivePulseTime = (pulseTime || 0) * this.pulsationSpeed;
|
|
26
|
+
const pulseFactor = (Math.sin(effectivePulseTime + this.pulsarPhase) + 1.5) / 2.5;
|
|
27
|
+
this.radius = this.baseRadius * pulseFactor;
|
|
28
|
+
this.boundingRadius = this.radius;
|
|
29
|
+
}
|
|
30
|
+
draw(ctx, pulseTime) {
|
|
31
|
+
const effectivePulseTime = pulseTime * this.pulsationSpeed;
|
|
32
|
+
ctx.save();
|
|
33
|
+
// Calculate alpha for the "blinking" effect
|
|
34
|
+
const alphaPulseFactor = (Math.sin(effectivePulseTime + this.pulsarPhase) + 1) / 2; // varies between 0 and 1
|
|
35
|
+
ctx.globalAlpha = 0.5 + alphaPulseFactor * 0.5;
|
|
36
|
+
// Call the base star drawing method which handles auras and hover effects correctly.
|
|
37
|
+
// We wrap it in save/restore to contain the globalAlpha change.
|
|
38
|
+
super.drawBase(ctx, pulseTime);
|
|
39
|
+
ctx.restore();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the Ring class, a visual ring around a celestial body.
|
|
3
|
+
*/
|
|
4
|
+
import type { CelestialBody } from '../base/celestial-body';
|
|
5
|
+
export declare class Ring {
|
|
6
|
+
parent: CelestialBody;
|
|
7
|
+
radius: number;
|
|
8
|
+
thickness: number;
|
|
9
|
+
color: string;
|
|
10
|
+
isDotted: boolean;
|
|
11
|
+
constructor(parent: CelestialBody, radius: number, thickness: number, color: string, isDotted: boolean);
|
|
12
|
+
draw(ctx: CanvasRenderingContext2D): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class Ring {
|
|
2
|
+
constructor(parent, radius, thickness, color, isDotted) {
|
|
3
|
+
this.parent = parent;
|
|
4
|
+
this.radius = radius;
|
|
5
|
+
this.thickness = thickness;
|
|
6
|
+
this.color = color;
|
|
7
|
+
this.isDotted = isDotted;
|
|
8
|
+
}
|
|
9
|
+
draw(ctx) {
|
|
10
|
+
// When drawing rings individually in the catalog, the parent might not be at (0,0) relative to the canvas.
|
|
11
|
+
// We will draw relative to the current transform.
|
|
12
|
+
const drawX = this.parent ? this.parent.x : 0;
|
|
13
|
+
const drawY = this.parent ? this.parent.y : 0;
|
|
14
|
+
ctx.beginPath();
|
|
15
|
+
ctx.strokeStyle = this.color;
|
|
16
|
+
ctx.lineWidth = this.thickness;
|
|
17
|
+
if (this.isDotted) {
|
|
18
|
+
ctx.setLineDash([2, 3]);
|
|
19
|
+
}
|
|
20
|
+
ctx.arc(drawX, drawY, this.radius, 0, 2 * Math.PI);
|
|
21
|
+
ctx.stroke();
|
|
22
|
+
if (this.isDotted) {
|
|
23
|
+
ctx.setLineDash([]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the RingedPlanet class, a specialized type of Star.
|
|
3
|
+
*/
|
|
4
|
+
import { Star } from './star';
|
|
5
|
+
import { Ring } from './ring';
|
|
6
|
+
export declare const RINGED_PLANET_CONFIG: {
|
|
7
|
+
RING_COUNT_MIN: number;
|
|
8
|
+
RING_COUNT_MAX: number;
|
|
9
|
+
RING_RADIUS_MULTIPLIER_MIN: number;
|
|
10
|
+
RING_RADIUS_MULTIPLIER_MAX: number;
|
|
11
|
+
RING_THICKNESS_MIN: number;
|
|
12
|
+
RING_THICKNESS_MAX: number;
|
|
13
|
+
RING_ALPHA: number;
|
|
14
|
+
RING_DOTTED_CHANCE: number;
|
|
15
|
+
DOCKING_POINTS_PER_RING: number;
|
|
16
|
+
};
|
|
17
|
+
export declare class RingedPlanet extends Star {
|
|
18
|
+
rings: Ring[];
|
|
19
|
+
constructor(x: number, y: number, ringCount?: number);
|
|
20
|
+
draw(ctx: CanvasRenderingContext2D, pulseTime: number): void;
|
|
21
|
+
}
|