@fluritheme/moon-explorer 0.0.0-stage → 0.1.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/CHANGELOG.md +5 -0
- package/LICENSE +56 -0
- package/LICENSE-MIT-LEGACY +23 -0
- package/LICENSING.md +22 -0
- package/README.md +159 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.js +428 -0
- package/dist/model.d.ts +100 -0
- package/dist/model.js +429 -0
- package/dist/render.d.ts +13 -0
- package/dist/render.js +467 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +1 -0
- package/licensing.json +51 -0
- package/package.json +79 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { Expedition, clamp } from "./model.js";
|
|
2
|
+
import { paint } from "./render.js";
|
|
3
|
+
const validMissions = [
|
|
4
|
+
"explore",
|
|
5
|
+
"sample",
|
|
6
|
+
"flag",
|
|
7
|
+
"rover",
|
|
8
|
+
"observe",
|
|
9
|
+
"wave",
|
|
10
|
+
"jump",
|
|
11
|
+
"rest",
|
|
12
|
+
];
|
|
13
|
+
function finite(n, fallback, min, max) {
|
|
14
|
+
return typeof n === "number" && Number.isFinite(n)
|
|
15
|
+
? clamp(n, min, max)
|
|
16
|
+
: fallback;
|
|
17
|
+
}
|
|
18
|
+
/** Create a self-contained lunar expedition. Importing the module is SSR-safe. */
|
|
19
|
+
export function createMoonExplorer(input = {}) {
|
|
20
|
+
const browser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
21
|
+
let opts = {
|
|
22
|
+
size: finite(input.size, 1, 0.55, 1.8),
|
|
23
|
+
pace: finite(input.pace, 1, 0.3, 2.5),
|
|
24
|
+
autoExplore: input.autoExplore ?? true,
|
|
25
|
+
interactive: input.interactive ?? true,
|
|
26
|
+
rover: input.rover ?? true,
|
|
27
|
+
lander: input.lander ?? true,
|
|
28
|
+
shootingStars: input.shootingStars ?? true,
|
|
29
|
+
maxFlags: Math.round(finite(input.maxFlags, 5, 0, 12)),
|
|
30
|
+
accent: typeof input.accent === "string" && /^#[0-9a-f]{3,8}$/i.test(input.accent)
|
|
31
|
+
? input.accent
|
|
32
|
+
: "#ef9459",
|
|
33
|
+
maxFPS: finite(input.maxFPS, 40, 12, 60),
|
|
34
|
+
maxDPR: finite(input.maxDPR, 1.5, 1, 2.5),
|
|
35
|
+
zIndex: Math.round(finite(input.zIndex, 30, -2147483647, 2147483647)),
|
|
36
|
+
motion: input.motion ?? "respect",
|
|
37
|
+
};
|
|
38
|
+
let state = browser ? "running" : "idle";
|
|
39
|
+
let reduced = false, destroyed = false, manualPause = false, hidden = false;
|
|
40
|
+
const listeners = new Map();
|
|
41
|
+
const emptyWorld = {
|
|
42
|
+
bounds: { x: 0, y: 0, width: 800, height: 600 },
|
|
43
|
+
view: { x: 0, y: 0, width: 800, height: 600 },
|
|
44
|
+
surfaces: [{ id: "floor", x: 40, y: 480, width: 720 }],
|
|
45
|
+
};
|
|
46
|
+
let model = new Expedition(emptyWorld, opts.size, opts.autoExplore, opts.rover, opts.maxFlags, input.seed ?? 42);
|
|
47
|
+
model.onEvent = (event, data) => {
|
|
48
|
+
for (const f of listeners.get(event) ?? [])
|
|
49
|
+
try {
|
|
50
|
+
f({ ...data });
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
/* A listener cannot interrupt the animation loop. */
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
let canvas = null, context = null, root = null, frame = 0, last = 0, drawLast = 0, resize = null, mutation = null, media = null;
|
|
57
|
+
let refreshFrame = 0;
|
|
58
|
+
const pointer = { x: 0, y: 0, active: false };
|
|
59
|
+
const elements = new Map();
|
|
60
|
+
let ids = 0;
|
|
61
|
+
const rootIsPage = () => root === document.body || root === document.documentElement;
|
|
62
|
+
function measure() {
|
|
63
|
+
if (!root)
|
|
64
|
+
return emptyWorld;
|
|
65
|
+
const page = rootIsPage();
|
|
66
|
+
const rect = root.getBoundingClientRect();
|
|
67
|
+
const sx = window.scrollX, sy = window.scrollY;
|
|
68
|
+
const bounds = page
|
|
69
|
+
? {
|
|
70
|
+
x: 0,
|
|
71
|
+
y: 0,
|
|
72
|
+
width: Math.max(innerWidth, document.documentElement.scrollWidth),
|
|
73
|
+
height: Math.max(innerHeight, document.documentElement.scrollHeight),
|
|
74
|
+
}
|
|
75
|
+
: {
|
|
76
|
+
x: rect.left + sx,
|
|
77
|
+
y: rect.top + sy,
|
|
78
|
+
width: rect.width,
|
|
79
|
+
height: rect.height,
|
|
80
|
+
};
|
|
81
|
+
const view = { x: sx, y: sy, width: innerWidth, height: innerHeight };
|
|
82
|
+
let targets = [];
|
|
83
|
+
try {
|
|
84
|
+
targets = [
|
|
85
|
+
...root.querySelectorAll(input.surfaces ??
|
|
86
|
+
"[data-moon-surface], button, h1, h2, article, .card"),
|
|
87
|
+
].slice(0, 180);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
targets = [];
|
|
91
|
+
}
|
|
92
|
+
const surfaces = [];
|
|
93
|
+
for (const el of targets) {
|
|
94
|
+
if (el === canvas || el.contains(canvas))
|
|
95
|
+
continue;
|
|
96
|
+
const r = el.getBoundingClientRect();
|
|
97
|
+
if (r.width < 110 * opts.size ||
|
|
98
|
+
r.height < 2 ||
|
|
99
|
+
r.top + sy < bounds.y ||
|
|
100
|
+
r.top + sy > bounds.y + bounds.height)
|
|
101
|
+
continue;
|
|
102
|
+
const css = getComputedStyle(el);
|
|
103
|
+
if (css.display === "none" ||
|
|
104
|
+
css.visibility === "hidden" ||
|
|
105
|
+
css.position === "fixed")
|
|
106
|
+
continue;
|
|
107
|
+
if (!elements.has(el))
|
|
108
|
+
elements.set(el, "surface-" + ++ids);
|
|
109
|
+
surfaces.push({
|
|
110
|
+
id: elements.get(el),
|
|
111
|
+
x: r.left + sx + 5,
|
|
112
|
+
y: r.top + sy,
|
|
113
|
+
width: r.width - 10,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
surfaces.push({
|
|
117
|
+
id: "floor",
|
|
118
|
+
x: bounds.x + 30,
|
|
119
|
+
y: bounds.y + bounds.height - 30,
|
|
120
|
+
width: Math.max(120, bounds.width - 60),
|
|
121
|
+
});
|
|
122
|
+
return { bounds, view, surfaces };
|
|
123
|
+
}
|
|
124
|
+
function refresh() {
|
|
125
|
+
if (destroyed || !browser)
|
|
126
|
+
return;
|
|
127
|
+
model.size = opts.size;
|
|
128
|
+
model.auto = opts.autoExplore;
|
|
129
|
+
model.rover = opts.rover;
|
|
130
|
+
model.maxFlags = opts.maxFlags;
|
|
131
|
+
model.refresh(measure());
|
|
132
|
+
draw();
|
|
133
|
+
}
|
|
134
|
+
function queueRefresh() {
|
|
135
|
+
if (!refreshFrame)
|
|
136
|
+
refreshFrame = requestAnimationFrame(() => {
|
|
137
|
+
refreshFrame = 0;
|
|
138
|
+
refresh();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function draw() {
|
|
142
|
+
if (!canvas || !context || !root)
|
|
143
|
+
return;
|
|
144
|
+
const dpr = Math.min(devicePixelRatio || 1, opts.maxDPR);
|
|
145
|
+
const w = innerWidth, h = innerHeight;
|
|
146
|
+
if (canvas.width !== Math.round(w * dpr) ||
|
|
147
|
+
canvas.height !== Math.round(h * dpr)) {
|
|
148
|
+
canvas.width = Math.round(w * dpr);
|
|
149
|
+
canvas.height = Math.round(h * dpr);
|
|
150
|
+
canvas.style.width = w + "px";
|
|
151
|
+
canvas.style.height = h + "px";
|
|
152
|
+
}
|
|
153
|
+
context.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
154
|
+
context.clearRect(0, 0, w, h);
|
|
155
|
+
context.save();
|
|
156
|
+
if (!rootIsPage()) {
|
|
157
|
+
const r = root.getBoundingClientRect();
|
|
158
|
+
context.beginPath();
|
|
159
|
+
context.rect(r.left, r.top, r.width, r.height);
|
|
160
|
+
context.clip();
|
|
161
|
+
}
|
|
162
|
+
context.translate(-scrollX, -scrollY);
|
|
163
|
+
paint(context, model, {
|
|
164
|
+
accent: opts.accent,
|
|
165
|
+
lander: opts.lander,
|
|
166
|
+
rover: opts.rover,
|
|
167
|
+
pointer,
|
|
168
|
+
reduced,
|
|
169
|
+
});
|
|
170
|
+
context.restore();
|
|
171
|
+
}
|
|
172
|
+
function tick(now) {
|
|
173
|
+
frame = 0;
|
|
174
|
+
if (destroyed || manualPause || hidden || reduced)
|
|
175
|
+
return;
|
|
176
|
+
const delta = last ? Math.min(0.06, (now - last) / 1000) : 0;
|
|
177
|
+
last = now;
|
|
178
|
+
model.step(delta * opts.pace);
|
|
179
|
+
if (opts.shootingStars && model.rng() < delta * 0.012)
|
|
180
|
+
model.shootingStar();
|
|
181
|
+
if (now - drawLast >= 1000 / opts.maxFPS) {
|
|
182
|
+
drawLast = now;
|
|
183
|
+
draw();
|
|
184
|
+
}
|
|
185
|
+
frame = requestAnimationFrame(tick);
|
|
186
|
+
}
|
|
187
|
+
function sync() {
|
|
188
|
+
if (!browser || destroyed)
|
|
189
|
+
return;
|
|
190
|
+
cancelAnimationFrame(frame);
|
|
191
|
+
frame = 0;
|
|
192
|
+
last = 0;
|
|
193
|
+
state = manualPause || hidden ? "paused" : "running";
|
|
194
|
+
if (reduced) {
|
|
195
|
+
model.staticPose();
|
|
196
|
+
draw();
|
|
197
|
+
}
|
|
198
|
+
else if (!manualPause && !hidden)
|
|
199
|
+
frame = requestAnimationFrame(tick);
|
|
200
|
+
}
|
|
201
|
+
function motionChange() {
|
|
202
|
+
reduced = opts.motion === "static" || Boolean(media?.matches);
|
|
203
|
+
sync();
|
|
204
|
+
}
|
|
205
|
+
function visibility() {
|
|
206
|
+
hidden = document.hidden;
|
|
207
|
+
sync();
|
|
208
|
+
}
|
|
209
|
+
function scroll() {
|
|
210
|
+
model.world.view = {
|
|
211
|
+
x: scrollX,
|
|
212
|
+
y: scrollY,
|
|
213
|
+
width: innerWidth,
|
|
214
|
+
height: innerHeight,
|
|
215
|
+
};
|
|
216
|
+
if (!rootIsPage()) {
|
|
217
|
+
const r = root.getBoundingClientRect();
|
|
218
|
+
const nextHidden = document.hidden || r.bottom < 0 || r.top > innerHeight;
|
|
219
|
+
if (nextHidden !== hidden) {
|
|
220
|
+
hidden = nextHidden;
|
|
221
|
+
sync();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
draw();
|
|
225
|
+
}
|
|
226
|
+
function move(e) {
|
|
227
|
+
pointer.x = e.clientX + scrollX;
|
|
228
|
+
pointer.y = e.clientY + scrollY;
|
|
229
|
+
pointer.active = e.pointerType !== "touch";
|
|
230
|
+
}
|
|
231
|
+
function click(e) {
|
|
232
|
+
if (!opts.interactive || reduced || manualPause)
|
|
233
|
+
return;
|
|
234
|
+
if (Math.hypot(e.clientX + scrollX - model.x, e.clientY + scrollY - (model.y - 45 * opts.size)) <
|
|
235
|
+
70 * opts.size)
|
|
236
|
+
model.enqueue("wave");
|
|
237
|
+
}
|
|
238
|
+
function leave() {
|
|
239
|
+
pointer.active = false;
|
|
240
|
+
}
|
|
241
|
+
function resolve(selector) {
|
|
242
|
+
if (!selector || !root)
|
|
243
|
+
return undefined;
|
|
244
|
+
try {
|
|
245
|
+
const el = root.matches(selector) ? root : root.querySelector(selector);
|
|
246
|
+
if (!el)
|
|
247
|
+
return undefined;
|
|
248
|
+
const r = el.getBoundingClientRect();
|
|
249
|
+
if (r.width < 100)
|
|
250
|
+
return undefined;
|
|
251
|
+
if (!elements.has(el))
|
|
252
|
+
elements.set(el, "surface-" + ++ids);
|
|
253
|
+
return {
|
|
254
|
+
id: elements.get(el),
|
|
255
|
+
x: r.left + scrollX + 5,
|
|
256
|
+
y: r.top + scrollY,
|
|
257
|
+
width: r.width - 10,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function mission(kind, selector) {
|
|
265
|
+
if (destroyed || !validMissions.includes(kind) || !browser)
|
|
266
|
+
return false;
|
|
267
|
+
const target = resolve(selector);
|
|
268
|
+
if (selector && !target)
|
|
269
|
+
return false;
|
|
270
|
+
if (reduced)
|
|
271
|
+
return false;
|
|
272
|
+
return model.enqueue(kind, target);
|
|
273
|
+
}
|
|
274
|
+
if (browser) {
|
|
275
|
+
root = input.root ?? document.body;
|
|
276
|
+
canvas = document.createElement("canvas");
|
|
277
|
+
canvas.dataset.moonExplorer = "";
|
|
278
|
+
canvas.setAttribute("aria-hidden", "true");
|
|
279
|
+
canvas.style.cssText = `position:fixed;inset:0;pointer-events:none;z-index:${opts.zIndex};contain:strict;`;
|
|
280
|
+
document.body.append(canvas);
|
|
281
|
+
context = canvas.getContext("2d");
|
|
282
|
+
media = matchMedia("(prefers-reduced-motion: reduce)");
|
|
283
|
+
reduced = opts.motion === "static" || media.matches;
|
|
284
|
+
model.world = measure();
|
|
285
|
+
model.land();
|
|
286
|
+
if (reduced)
|
|
287
|
+
model.staticPose();
|
|
288
|
+
window.addEventListener("scroll", scroll, { passive: true });
|
|
289
|
+
window.addEventListener("resize", queueRefresh, { passive: true });
|
|
290
|
+
window.addEventListener("pointermove", move, { passive: true });
|
|
291
|
+
window.addEventListener("pointerdown", click, { passive: true });
|
|
292
|
+
window.addEventListener("pointerleave", leave, { passive: true });
|
|
293
|
+
document.addEventListener("visibilitychange", visibility);
|
|
294
|
+
media.addEventListener("change", motionChange);
|
|
295
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
296
|
+
resize = new ResizeObserver(queueRefresh);
|
|
297
|
+
resize.observe(root);
|
|
298
|
+
}
|
|
299
|
+
if (typeof MutationObserver !== "undefined") {
|
|
300
|
+
mutation = new MutationObserver((records) => {
|
|
301
|
+
if (records.some((r) => [...r.addedNodes, ...r.removedNodes].some((n) => n !== canvas)))
|
|
302
|
+
queueRefresh();
|
|
303
|
+
});
|
|
304
|
+
mutation.observe(root, { childList: true, subtree: true });
|
|
305
|
+
}
|
|
306
|
+
draw();
|
|
307
|
+
sync();
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
mission,
|
|
311
|
+
explore: (selector) => mission("explore", selector),
|
|
312
|
+
collect: () => mission("sample"),
|
|
313
|
+
plantFlag: () => mission("flag"),
|
|
314
|
+
ride: () => mission("rover"),
|
|
315
|
+
observe: () => mission("observe"),
|
|
316
|
+
jump: () => mission("jump"),
|
|
317
|
+
wave: () => mission("wave"),
|
|
318
|
+
rest: () => mission("rest"),
|
|
319
|
+
shootingStar() {
|
|
320
|
+
if (!destroyed && !reduced) {
|
|
321
|
+
model.shootingStar();
|
|
322
|
+
draw();
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
land(selector) {
|
|
326
|
+
if (destroyed || !browser || reduced)
|
|
327
|
+
return false;
|
|
328
|
+
const target = resolve(selector);
|
|
329
|
+
if (selector && !target)
|
|
330
|
+
return false;
|
|
331
|
+
model.land(target);
|
|
332
|
+
return true;
|
|
333
|
+
},
|
|
334
|
+
pause() {
|
|
335
|
+
if (destroyed)
|
|
336
|
+
return;
|
|
337
|
+
manualPause = true;
|
|
338
|
+
sync();
|
|
339
|
+
if (!browser)
|
|
340
|
+
state = "paused";
|
|
341
|
+
},
|
|
342
|
+
resume() {
|
|
343
|
+
if (destroyed)
|
|
344
|
+
return;
|
|
345
|
+
manualPause = false;
|
|
346
|
+
sync();
|
|
347
|
+
},
|
|
348
|
+
refresh,
|
|
349
|
+
update(options) {
|
|
350
|
+
if (destroyed)
|
|
351
|
+
return;
|
|
352
|
+
opts = {
|
|
353
|
+
...opts,
|
|
354
|
+
size: finite(options.size, opts.size, 0.55, 1.8),
|
|
355
|
+
pace: finite(options.pace, opts.pace, 0.3, 2.5),
|
|
356
|
+
maxFPS: finite(options.maxFPS, opts.maxFPS, 12, 60),
|
|
357
|
+
maxDPR: finite(options.maxDPR, opts.maxDPR, 1, 2.5),
|
|
358
|
+
maxFlags: Math.round(finite(options.maxFlags, opts.maxFlags, 0, 12)),
|
|
359
|
+
autoExplore: options.autoExplore ?? opts.autoExplore,
|
|
360
|
+
interactive: options.interactive ?? opts.interactive,
|
|
361
|
+
rover: options.rover ?? opts.rover,
|
|
362
|
+
lander: options.lander ?? opts.lander,
|
|
363
|
+
shootingStars: options.shootingStars ?? opts.shootingStars,
|
|
364
|
+
motion: options.motion ?? opts.motion,
|
|
365
|
+
accent: typeof options.accent === "string" &&
|
|
366
|
+
/^#[0-9a-f]{3,8}$/i.test(options.accent)
|
|
367
|
+
? options.accent
|
|
368
|
+
: opts.accent,
|
|
369
|
+
zIndex: Math.round(finite(options.zIndex, opts.zIndex, -2147483647, 2147483647)),
|
|
370
|
+
};
|
|
371
|
+
if (canvas)
|
|
372
|
+
canvas.style.zIndex = String(opts.zIndex);
|
|
373
|
+
refresh();
|
|
374
|
+
motionChange();
|
|
375
|
+
},
|
|
376
|
+
destroy() {
|
|
377
|
+
if (destroyed)
|
|
378
|
+
return;
|
|
379
|
+
destroyed = true;
|
|
380
|
+
state = "destroyed";
|
|
381
|
+
if (browser) {
|
|
382
|
+
cancelAnimationFrame(frame);
|
|
383
|
+
cancelAnimationFrame(refreshFrame);
|
|
384
|
+
resize?.disconnect();
|
|
385
|
+
mutation?.disconnect();
|
|
386
|
+
media?.removeEventListener("change", motionChange);
|
|
387
|
+
window.removeEventListener("scroll", scroll);
|
|
388
|
+
window.removeEventListener("resize", queueRefresh);
|
|
389
|
+
window.removeEventListener("pointermove", move);
|
|
390
|
+
window.removeEventListener("pointerdown", click);
|
|
391
|
+
window.removeEventListener("pointerleave", leave);
|
|
392
|
+
document.removeEventListener("visibilitychange", visibility);
|
|
393
|
+
canvas?.remove();
|
|
394
|
+
}
|
|
395
|
+
listeners.clear();
|
|
396
|
+
elements.clear();
|
|
397
|
+
},
|
|
398
|
+
getState() {
|
|
399
|
+
return Object.freeze({
|
|
400
|
+
state,
|
|
401
|
+
phase: model.phase,
|
|
402
|
+
mission: model.mission,
|
|
403
|
+
x: model.x,
|
|
404
|
+
y: model.y,
|
|
405
|
+
facing: model.facing,
|
|
406
|
+
surface: model.surface.id,
|
|
407
|
+
samples: model.samples,
|
|
408
|
+
flags: model.flags.length,
|
|
409
|
+
distance: Math.round(model.distance),
|
|
410
|
+
roverDistance: Math.round(model.roverDistance),
|
|
411
|
+
missionsCompleted: model.missionsCompleted,
|
|
412
|
+
elapsed: model.elapsed,
|
|
413
|
+
reducedMotion: reduced,
|
|
414
|
+
});
|
|
415
|
+
},
|
|
416
|
+
on(event, handler) {
|
|
417
|
+
if (destroyed)
|
|
418
|
+
return () => { };
|
|
419
|
+
let set = listeners.get(event);
|
|
420
|
+
if (!set) {
|
|
421
|
+
set = new Set();
|
|
422
|
+
listeners.set(event, set);
|
|
423
|
+
}
|
|
424
|
+
set.add(handler);
|
|
425
|
+
return () => set.delete(handler);
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ExplorerPhase, Mission, MoonWorld, LunarSurface, ExplorerEvent, ExplorerEventData } from "./types.js";
|
|
2
|
+
export declare const clamp: (n: number, a: number, b: number) => number;
|
|
3
|
+
export declare const lerp: (a: number, b: number, t: number) => number;
|
|
4
|
+
export declare const ease: (t: number) => number;
|
|
5
|
+
export declare function random(seed: number): () => number;
|
|
6
|
+
export interface Dust {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
vx: number;
|
|
10
|
+
vy: number;
|
|
11
|
+
life: number;
|
|
12
|
+
ttl: number;
|
|
13
|
+
radius: number;
|
|
14
|
+
}
|
|
15
|
+
export interface Footprint {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
facing: number;
|
|
19
|
+
side: number;
|
|
20
|
+
life: number;
|
|
21
|
+
}
|
|
22
|
+
export interface Flag {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
phase: number;
|
|
26
|
+
surface: string;
|
|
27
|
+
}
|
|
28
|
+
export interface Rock {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
size: number;
|
|
32
|
+
collected: boolean;
|
|
33
|
+
surface: string;
|
|
34
|
+
angle: number;
|
|
35
|
+
}
|
|
36
|
+
export interface ShootingStar {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
vx: number;
|
|
40
|
+
vy: number;
|
|
41
|
+
life: number;
|
|
42
|
+
ttl: number;
|
|
43
|
+
}
|
|
44
|
+
export declare class Expedition {
|
|
45
|
+
world: MoonWorld;
|
|
46
|
+
size: number;
|
|
47
|
+
auto: boolean;
|
|
48
|
+
rover: boolean;
|
|
49
|
+
maxFlags: number;
|
|
50
|
+
phase: ExplorerPhase;
|
|
51
|
+
mission: Mission | null;
|
|
52
|
+
phaseTime: number;
|
|
53
|
+
elapsed: number;
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
startX: number;
|
|
57
|
+
startY: number;
|
|
58
|
+
targetX: number;
|
|
59
|
+
targetY: number;
|
|
60
|
+
facing: 1 | -1;
|
|
61
|
+
surface: LunarSurface;
|
|
62
|
+
distance: number;
|
|
63
|
+
roverDistance: number;
|
|
64
|
+
samples: number;
|
|
65
|
+
missionsCompleted: number;
|
|
66
|
+
dust: Dust[];
|
|
67
|
+
footprints: Footprint[];
|
|
68
|
+
flags: Flag[];
|
|
69
|
+
rocks: Rock[];
|
|
70
|
+
stars: ShootingStar[];
|
|
71
|
+
queue: {
|
|
72
|
+
kind: Mission;
|
|
73
|
+
target?: LunarSurface;
|
|
74
|
+
}[];
|
|
75
|
+
roverX: number;
|
|
76
|
+
roverY: number;
|
|
77
|
+
landerX: number;
|
|
78
|
+
landerY: number;
|
|
79
|
+
landingStartY: number;
|
|
80
|
+
sampled: boolean;
|
|
81
|
+
flagged: boolean;
|
|
82
|
+
lastStep: number;
|
|
83
|
+
nextAuto: number;
|
|
84
|
+
rng: () => number;
|
|
85
|
+
onEvent: (event: ExplorerEvent, data: ExplorerEventData) => void;
|
|
86
|
+
constructor(world: MoonWorld, size?: number, auto?: boolean, rover?: boolean, maxFlags?: number, seed?: number);
|
|
87
|
+
event(event: ExplorerEvent): void;
|
|
88
|
+
choose(target?: LunarSurface): LunarSurface;
|
|
89
|
+
setPhase(phase: ExplorerPhase): void;
|
|
90
|
+
land(target?: LunarSurface): void;
|
|
91
|
+
populateRocks(): void;
|
|
92
|
+
enqueue(kind: Mission, target?: LunarSurface): boolean;
|
|
93
|
+
startMission(kind: Mission, target?: LunarSurface): void;
|
|
94
|
+
finish(): void;
|
|
95
|
+
burst(x?: number, y?: number, amount?: number, power?: number): void;
|
|
96
|
+
shootingStar(): void;
|
|
97
|
+
staticPose(): void;
|
|
98
|
+
refresh(world: MoonWorld): void;
|
|
99
|
+
step(dt: number): void;
|
|
100
|
+
}
|