@auraindustry/aurajs 0.1.0 → 0.1.3
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/package.json +1 -1
- package/src/asset-pack.mjs +5 -1
- package/src/authored-runtime.mjs +14 -0
- package/src/bin-integrity.mjs +33 -26
- package/src/cli.mjs +17 -2
- package/src/commands/project-authoring.mjs +20 -0
- package/src/config.mjs +17 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +861 -6
- package/src/external-package-surface.mjs +1 -1
- package/src/package-integrity.mjs +18 -4
- package/src/publish-command.mjs +133 -13
- package/src/publish-validation.mjs +22 -11
- package/src/scaffold/project-docs.mjs +60 -41
- package/src/web-conformance.mjs +4 -4
- package/templates/create/2d/src/runtime/app.js +4 -0
- package/templates/create/2d-survivor/src/runtime/app.js +4 -0
- package/templates/create/3d/src/runtime/app.js +4 -0
- package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
- package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/blank/assets/splash/bg.webp +0 -0
- package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
- package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
- package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/blank/assets/splash/logoholo.webp +0 -0
- package/templates/create/blank/src/main.js +5 -1
- package/templates/create/blank/src/runtime/splash.js +305 -0
- package/templates/create/local-multiplayer/aura.config.json +1 -0
- package/templates/create/local-multiplayer/docs/design/loop.md +3 -1
- package/templates/create/local-multiplayer/scenes/gameplay.scene.js +216 -13
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
- package/templates/create/local-multiplayer/ui/hud.screen.js +12 -7
- package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/shared/assets/splash/bg.webp +0 -0
- package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
- package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
- package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/shared/assets/splash/logoholo.webp +0 -0
- package/templates/create/shared/src/runtime/splash.js +305 -0
- package/templates/create/video-cutscene/src/runtime/app.js +4 -0
- package/templates/create-bin/play.js +121 -4
- package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/starter/assets/splash/bg.webp +0 -0
- package/templates/starter/assets/splash/boot-loop.wav +0 -0
- package/templates/starter/assets/splash/boot-sting.wav +0 -0
- package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/starter/assets/splash/logoholo.webp +0 -0
- package/templates/starter/src/main.js +4 -0
- package/templates/starter/src/runtime/splash.js +305 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
// AuraJS Splash — unified retro paper-card boot screen.
|
|
2
|
+
// Auto-wired by createApp(). Shows once on launch, then hands off to the game.
|
|
3
|
+
|
|
4
|
+
const FADE_IN = 0.7;
|
|
5
|
+
const HOLD = 1.8;
|
|
6
|
+
const FADE_OUT = 0.7;
|
|
7
|
+
const TOTAL = FADE_IN + HOLD + FADE_OUT;
|
|
8
|
+
|
|
9
|
+
const PAPER = [0.89, 0.89, 0.89];
|
|
10
|
+
const PAPER_SHADOW = [0.80, 0.80, 0.80];
|
|
11
|
+
const PAPER_EDGE = [0.41, 0.41, 0.41];
|
|
12
|
+
const INK = [0.1, 0.1, 0.1];
|
|
13
|
+
const INK_MUTED = [0.42, 0.42, 0.42];
|
|
14
|
+
const MASCOT_FRAME_W = 64;
|
|
15
|
+
const MASCOT_FRAME_H = 84;
|
|
16
|
+
const MASCOT_SEQUENCE = Object.freeze([0, 1, 2, 1]);
|
|
17
|
+
const SPLASH_STING_PATH = 'splash/boot-sting.wav';
|
|
18
|
+
const SPLASH_LOOP_PATH = 'splash/boot-loop.wav';
|
|
19
|
+
const SPLASH_BUS = 'splash';
|
|
20
|
+
const QUIET_BUSES = Object.freeze(['default', 'music', 'sfx']);
|
|
21
|
+
|
|
22
|
+
let state = null;
|
|
23
|
+
|
|
24
|
+
function has(obj, method) {
|
|
25
|
+
return Boolean(obj) && typeof obj[method] === 'function';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function color(rgb, alpha = 1) {
|
|
29
|
+
return aura.rgba(rgb[0], rgb[1], rgb[2], alpha);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function drawShadowText(text, x, y, options = {}) {
|
|
33
|
+
const {
|
|
34
|
+
shadowOffset = 2,
|
|
35
|
+
shadowColor = color(INK_MUTED, 0.3),
|
|
36
|
+
...rest
|
|
37
|
+
} = options;
|
|
38
|
+
aura.draw2d.text(text, x + shadowOffset, y + shadowOffset, {
|
|
39
|
+
...rest,
|
|
40
|
+
color: shadowColor,
|
|
41
|
+
});
|
|
42
|
+
aura.draw2d.text(text, x, y, rest);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function initSplash() {
|
|
46
|
+
state = {
|
|
47
|
+
t: 0,
|
|
48
|
+
logo: null,
|
|
49
|
+
mascot: null,
|
|
50
|
+
wordmark: null,
|
|
51
|
+
font: null,
|
|
52
|
+
stingHandle: null,
|
|
53
|
+
loopHandle: null,
|
|
54
|
+
busVolumes: null,
|
|
55
|
+
pausedHandles: [],
|
|
56
|
+
};
|
|
57
|
+
try { if (has(aura.assets, 'load')) state.logo = aura.assets.load('splash/logoholo.webp'); } catch (_) {}
|
|
58
|
+
try { if (has(aura.assets, 'load')) state.mascot = aura.assets.load('splash/logo-mascot-sheet.webp'); } catch (_) {}
|
|
59
|
+
try { if (has(aura.assets, 'load')) state.wordmark = aura.assets.load('splash/aurajs-gg-wordmark.webp'); } catch (_) {}
|
|
60
|
+
try {
|
|
61
|
+
if (has(aura.assets, 'loadBitmapFont')) {
|
|
62
|
+
const result = aura.assets.loadBitmapFont();
|
|
63
|
+
if (result && result.ok && result.font) state.font = result.font;
|
|
64
|
+
}
|
|
65
|
+
} catch (_) {}
|
|
66
|
+
captureBusVolumes();
|
|
67
|
+
applySplashBusIsolation();
|
|
68
|
+
try {
|
|
69
|
+
if (aura.audio && aura.audio.supported !== false && typeof aura.audio.play === 'function') {
|
|
70
|
+
state.loopHandle = aura.audio.play(SPLASH_LOOP_PATH, {
|
|
71
|
+
loop: true,
|
|
72
|
+
volume: 0.22,
|
|
73
|
+
bus: SPLASH_BUS,
|
|
74
|
+
});
|
|
75
|
+
state.stingHandle = aura.audio.play(SPLASH_STING_PATH, {
|
|
76
|
+
loop: false,
|
|
77
|
+
volume: 0.54,
|
|
78
|
+
bus: SPLASH_BUS,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
} catch (_) {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function isSplashActive() {
|
|
85
|
+
return state !== null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function updateSplash(dt) {
|
|
89
|
+
if (!state) return;
|
|
90
|
+
syncPausedTracks();
|
|
91
|
+
try {
|
|
92
|
+
if (aura.audio && typeof aura.audio.update === 'function') {
|
|
93
|
+
aura.audio.update(Number(dt) > 0 ? Number(dt) : (1 / 60));
|
|
94
|
+
}
|
|
95
|
+
} catch (_) {}
|
|
96
|
+
state.t += dt;
|
|
97
|
+
if (state.t >= TOTAL) {
|
|
98
|
+
stopSplashAudio();
|
|
99
|
+
state = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function drawSplash() {
|
|
104
|
+
if (!state) return;
|
|
105
|
+
const { width: w, height: h } = has(aura.window, 'getSize')
|
|
106
|
+
? aura.window.getSize()
|
|
107
|
+
: { width: 640, height: 480 };
|
|
108
|
+
const t = state.t;
|
|
109
|
+
const cx = w / 2;
|
|
110
|
+
const cy = h / 2;
|
|
111
|
+
|
|
112
|
+
let a = 1;
|
|
113
|
+
if (t < FADE_IN) a = easeOut(t / FADE_IN);
|
|
114
|
+
else if (t > FADE_IN + HOLD) a = 1 - easeIn((t - FADE_IN - HOLD) / FADE_OUT);
|
|
115
|
+
|
|
116
|
+
aura.draw2d.clear(color(PAPER));
|
|
117
|
+
|
|
118
|
+
const panelW = Math.min(Math.floor(w * 0.48), 580);
|
|
119
|
+
const panelH = Math.min(Math.floor(h * 0.72), 620);
|
|
120
|
+
const panelX = Math.floor(cx - (panelW * 0.5));
|
|
121
|
+
const panelY = Math.floor(cy - (panelH * 0.5));
|
|
122
|
+
|
|
123
|
+
aura.draw2d.rectFill(panelX + 6, panelY + 6, panelW, panelH, color(INK, 0.08 * a));
|
|
124
|
+
aura.draw2d.rectFill(panelX, panelY, panelW, panelH, color(PAPER_EDGE, a));
|
|
125
|
+
aura.draw2d.rectFill(panelX + 6, panelY + 6, panelW - 12, panelH - 12, color(PAPER, a));
|
|
126
|
+
aura.draw2d.rectFill(panelX + 12, panelY + 12, panelW - 24, panelH - 24, color(PAPER, a * 0.96));
|
|
127
|
+
|
|
128
|
+
const floatY = Math.sin(t * 1.4 * Math.PI * 2) * 2;
|
|
129
|
+
const breathe = 0.985 + 0.015 * (0.5 + 0.5 * Math.sin(t * Math.PI * 2));
|
|
130
|
+
const mascotScale = Math.max(1, Math.min(Math.floor(Math.min(w, h) / 260), 2));
|
|
131
|
+
const mascotW = Math.floor(MASCOT_FRAME_W * mascotScale * breathe);
|
|
132
|
+
const mascotH = Math.floor(MASCOT_FRAME_H * mascotScale * breathe);
|
|
133
|
+
const mascotX = Math.floor(cx - (mascotW * 0.5));
|
|
134
|
+
const mascotY = Math.floor(panelY + panelH * 0.40 + floatY);
|
|
135
|
+
const mascotFrame = MASCOT_SEQUENCE[Math.floor(t * 7.5) % MASCOT_SEQUENCE.length] || 0;
|
|
136
|
+
|
|
137
|
+
aura.draw2d.rectFill(
|
|
138
|
+
mascotX + Math.floor(mascotW * 0.18),
|
|
139
|
+
mascotY + mascotH - 8,
|
|
140
|
+
Math.floor(mascotW * 0.64),
|
|
141
|
+
6,
|
|
142
|
+
color(INK, a * 0.08),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (state.mascot) {
|
|
146
|
+
aura.draw2d.sprite(state.mascot, mascotX, mascotY, {
|
|
147
|
+
width: mascotW,
|
|
148
|
+
height: mascotH,
|
|
149
|
+
frameX: mascotFrame * MASCOT_FRAME_W,
|
|
150
|
+
frameY: 0,
|
|
151
|
+
frameW: MASCOT_FRAME_W,
|
|
152
|
+
frameH: MASCOT_FRAME_H,
|
|
153
|
+
alpha: a,
|
|
154
|
+
});
|
|
155
|
+
} else if (state.logo) {
|
|
156
|
+
const sz = Math.min(Math.floor(panelW * 0.24), Math.floor(h * 0.14)) * breathe;
|
|
157
|
+
aura.draw2d.sprite(state.logo, Math.floor(cx - sz / 2), Math.floor(mascotY + 6), {
|
|
158
|
+
width: sz,
|
|
159
|
+
height: sz,
|
|
160
|
+
alpha: a,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const ta = clamp(a * easeOut(clamp((t - 0.2) / (FADE_IN * 0.6))));
|
|
165
|
+
const sa = clamp(a * easeOut(clamp((t - 0.4) / (FADE_IN * 0.6))));
|
|
166
|
+
const scale = Math.min(w, h) / 480;
|
|
167
|
+
const tsz = Math.max(24, Math.round(scale * 28));
|
|
168
|
+
const ssz = Math.max(11, Math.round(scale * 12));
|
|
169
|
+
const wordmarkW = Math.min(Math.floor(panelW * 0.52), 290);
|
|
170
|
+
const wordmarkH = Math.floor(wordmarkW * (768 / 1408));
|
|
171
|
+
const wordmarkX = Math.floor(cx - (wordmarkW * 0.5));
|
|
172
|
+
const wordmarkY = Math.floor(panelY + 36);
|
|
173
|
+
const sY = Math.floor(panelY + panelH - 108);
|
|
174
|
+
const fo = state.font ? { font: state.font } : {};
|
|
175
|
+
|
|
176
|
+
if (state.wordmark) {
|
|
177
|
+
aura.draw2d.sprite(state.wordmark, wordmarkX, wordmarkY, {
|
|
178
|
+
width: wordmarkW,
|
|
179
|
+
height: wordmarkH,
|
|
180
|
+
alpha: ta,
|
|
181
|
+
tint: color(INK, ta),
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
drawShadowText('AuraJS.gg', cx, wordmarkY + Math.floor(wordmarkH * 0.55), {
|
|
185
|
+
...fo,
|
|
186
|
+
size: tsz,
|
|
187
|
+
color: color(INK, ta),
|
|
188
|
+
shadowColor: color(INK_MUTED, ta * 0.28),
|
|
189
|
+
align: 'center',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
drawShadowText('Open-Source. MIT.', cx, sY, {
|
|
194
|
+
...fo,
|
|
195
|
+
size: ssz,
|
|
196
|
+
color: color(INK_MUTED, sa),
|
|
197
|
+
shadowColor: color(PAPER_EDGE, sa * 0.18),
|
|
198
|
+
shadowOffset: 1,
|
|
199
|
+
align: 'center',
|
|
200
|
+
});
|
|
201
|
+
drawShadowText('Who needs publishers?', cx, sY + Math.max(16, Math.round(ssz * 1.45)), {
|
|
202
|
+
...fo,
|
|
203
|
+
size: ssz,
|
|
204
|
+
color: color(INK_MUTED, sa),
|
|
205
|
+
shadowColor: color(PAPER_EDGE, sa * 0.18),
|
|
206
|
+
shadowOffset: 1,
|
|
207
|
+
align: 'center',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const rw = Math.min(panelW - 140, 240);
|
|
211
|
+
aura.draw2d.rectFill(Math.floor(cx - rw / 2), Math.floor(sY + Math.max(34, ssz * 3.2)), rw, 2, color(PAPER_EDGE, sa * 0.38));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function easeOut(t) {
|
|
215
|
+
const u = 1 - clamp(t);
|
|
216
|
+
return 1 - (u * u * u);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function easeIn(t) {
|
|
220
|
+
const c = clamp(t);
|
|
221
|
+
return c * c * c;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function clamp(v) {
|
|
225
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function stopSplashAudio() {
|
|
229
|
+
if (!state || !aura.audio || typeof aura.audio.stop !== 'function') return;
|
|
230
|
+
try {
|
|
231
|
+
if (state.stingHandle != null) aura.audio.stop(state.stingHandle);
|
|
232
|
+
} catch (_) {}
|
|
233
|
+
try {
|
|
234
|
+
if (state.loopHandle != null) aura.audio.stop(state.loopHandle);
|
|
235
|
+
} catch (_) {}
|
|
236
|
+
restoreAudioState();
|
|
237
|
+
state.stingHandle = null;
|
|
238
|
+
state.loopHandle = null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function captureBusVolumes() {
|
|
242
|
+
if (!state || !aura.audio || typeof aura.audio.getMixerState !== 'function') return;
|
|
243
|
+
try {
|
|
244
|
+
const mixer = aura.audio.getMixerState();
|
|
245
|
+
const buses = Array.isArray(mixer?.buses) ? mixer.buses : [];
|
|
246
|
+
state.busVolumes = buses.reduce((acc, entry) => {
|
|
247
|
+
const bus = String(entry?.bus || '').trim();
|
|
248
|
+
if (!bus) return acc;
|
|
249
|
+
acc[bus] = Number(entry?.volume);
|
|
250
|
+
return acc;
|
|
251
|
+
}, {});
|
|
252
|
+
} catch (_) {}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function applySplashBusIsolation() {
|
|
256
|
+
if (!state || !aura.audio || typeof aura.audio.setBusVolume !== 'function') return;
|
|
257
|
+
try { aura.audio.setBusVolume(SPLASH_BUS, 1); } catch (_) {}
|
|
258
|
+
for (const bus of QUIET_BUSES) {
|
|
259
|
+
try { aura.audio.setBusVolume(bus, 0); } catch (_) {}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function syncPausedTracks() {
|
|
264
|
+
if (!state || !aura.audio || typeof aura.audio.getMixerState !== 'function' || typeof aura.audio.pause !== 'function') return;
|
|
265
|
+
try {
|
|
266
|
+
const mixer = aura.audio.getMixerState();
|
|
267
|
+
const tracks = Array.isArray(mixer?.tracks) ? mixer.tracks : [];
|
|
268
|
+
const splashHandles = [state.stingHandle, state.loopHandle].filter((handle) => handle != null);
|
|
269
|
+
const pausedHandles = new Set(Array.isArray(state.pausedHandles) ? state.pausedHandles : []);
|
|
270
|
+
for (const track of tracks) {
|
|
271
|
+
const handle = Number(track?.handle);
|
|
272
|
+
if (!Number.isInteger(handle) || handle <= 0) continue;
|
|
273
|
+
if (splashHandles.includes(handle)) continue;
|
|
274
|
+
if (track?.paused === true || pausedHandles.has(handle)) continue;
|
|
275
|
+
try {
|
|
276
|
+
aura.audio.pause(handle);
|
|
277
|
+
pausedHandles.add(handle);
|
|
278
|
+
} catch (_) {}
|
|
279
|
+
}
|
|
280
|
+
state.pausedHandles = Array.from(pausedHandles);
|
|
281
|
+
} catch (_) {}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function restoreAudioState() {
|
|
285
|
+
if (!state || !aura.audio) return;
|
|
286
|
+
if (typeof aura.audio.setBusVolume === 'function') {
|
|
287
|
+
const snapshot = state.busVolumes && typeof state.busVolumes === 'object' ? state.busVolumes : null;
|
|
288
|
+
if (snapshot) {
|
|
289
|
+
for (const [bus, volume] of Object.entries(snapshot)) {
|
|
290
|
+
try { aura.audio.setBusVolume(bus, Number(volume)); } catch (_) {}
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
for (const bus of QUIET_BUSES) {
|
|
294
|
+
try { aura.audio.setBusVolume(bus, 1); } catch (_) {}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
try { aura.audio.setBusVolume(SPLASH_BUS, 1); } catch (_) {}
|
|
298
|
+
}
|
|
299
|
+
if (typeof aura.audio.resume === 'function') {
|
|
300
|
+
for (const handle of Array.isArray(state.pausedHandles) ? state.pausedHandles : []) {
|
|
301
|
+
try { aura.audio.resume(handle); } catch (_) {}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
state.pausedHandles = [];
|
|
305
|
+
}
|