@auraindustry/aurajs 0.1.3 → 0.1.5
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/README.md +7 -0
- package/benchmarks/perf-thresholds.json +27 -0
- package/package.json +6 -1
- package/src/ai-guidance.mjs +302 -0
- package/src/authored-project.mjs +498 -2
- package/src/build-contract/capabilities.mjs +87 -1
- package/src/build-contract/constants.mjs +1 -0
- package/src/build-contract.mjs +2 -0
- package/src/bundler.mjs +143 -13
- package/src/cli.mjs +681 -13
- package/src/commands/packs.mjs +741 -0
- package/src/commands/project-authoring.mjs +128 -1
- package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
- package/src/conformance/cases/core-runtime-cases.mjs +6 -2
- package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +265 -4
- package/src/conformance-mobile.mjs +166 -0
- package/src/conformance.mjs +89 -30
- package/src/evidence-bundle.mjs +242 -0
- package/src/headless-test/runtime-coordinator.mjs +186 -33
- package/src/headless-test.mjs +2 -0
- package/src/helpers/2d/index.mjs +183 -0
- package/src/helpers/index.mjs +26 -0
- package/src/helpers/starter-utils/adventure-objectives.js +102 -0
- package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
- package/src/helpers/starter-utils/animation-2d.js +337 -0
- package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
- package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
- package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
- package/src/helpers/starter-utils/avatar-3d.js +404 -0
- package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
- package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
- package/src/helpers/starter-utils/core.js +150 -0
- package/src/helpers/starter-utils/dialogue-2d.js +351 -0
- package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
- package/src/helpers/starter-utils/index.js +26 -0
- package/src/helpers/starter-utils/inventory-2d.js +268 -0
- package/src/helpers/starter-utils/journal-2d.js +267 -0
- package/src/helpers/starter-utils/platformer-3d.js +132 -0
- package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
- package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
- package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
- package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
- package/src/helpers/starter-utils/triggers.js +662 -0
- package/src/helpers/starter-utils/tween-2d.js +615 -0
- package/src/helpers/starter-utils/wave-director.js +101 -0
- package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
- package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
- package/src/mobile/android/build.mjs +606 -0
- package/src/mobile/android/host-artifact.mjs +280 -0
- package/src/mobile/ios/build.mjs +1323 -0
- package/src/mobile/ios/host-artifact.mjs +819 -0
- package/src/mobile/shared/capabilities.mjs +174 -0
- package/src/packs/catalog.mjs +259 -0
- package/src/perf-benchmark-runner.mjs +17 -12
- package/src/perf-benchmark.mjs +408 -4
- package/src/publish-command.mjs +303 -6
- package/src/replay-runtime.mjs +257 -0
- package/src/scaffold/config.mjs +2 -0
- package/src/scaffold/fs.mjs +8 -1
- package/src/scaffold/project-docs.mjs +43 -1
- package/src/scaffold.mjs +4 -0
- package/src/session-runtime.mjs +4 -3
- package/src/web-conformance.mjs +0 -36
- package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
- package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
- package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
- package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
- package/templates/create/2d-adventure/docs/design/loop.md +4 -3
- package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
- package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
- package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
- package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
- package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
- package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
- package/templates/create/3d/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d/src/runtime/capabilities.js +5 -0
- package/templates/create/3d/src/runtime/materials.js +10 -0
- package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
- package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
- package/templates/create/shared/src/runtime/ui-forms.js +552 -0
- package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
- package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
- package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
- package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
- package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
- package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
- package/templates/create/shared/src/starter-utils/index.js +15 -1
- package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
- package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
- package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
- package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
- package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
- package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
- package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
- package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
- package/templates/create-bin/play.js +36 -7
- package/templates/skills/auramaxx/SKILL.md +46 -0
- package/templates/skills/auramaxx/project-requirements.md +68 -0
- package/templates/skills/auramaxx/starter-recipes.md +104 -0
- package/templates/skills/auramaxx/validation-checklist.md +49 -0
- package/templates/skills/aurajs/SKILL.md +0 -96
- package/templates/skills/aurajs/api-contract-3d.md +0 -7
- package/templates/skills/aurajs/api-contract.md +0 -7
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
function finite(value, fallback = 0) {
|
|
2
|
+
const numeric = Number(value);
|
|
3
|
+
return Number.isFinite(numeric) ? numeric : fallback;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function positiveInteger(value, fallback = null) {
|
|
7
|
+
const numeric = Math.floor(finite(value, Number.NaN));
|
|
8
|
+
if (numeric >= 1) return numeric;
|
|
9
|
+
return fallback;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeText(value, fallback) {
|
|
13
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolveAura(auraRef = globalThis.aura) {
|
|
17
|
+
return auraRef && typeof auraRef === 'object' ? auraRef : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeStageIds(value) {
|
|
21
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
22
|
+
return ['scene', 'light', 'final'];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const stageIds = [];
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
for (const entry of value) {
|
|
28
|
+
const id = normalizeText(entry, null);
|
|
29
|
+
if (!id || seen.has(id)) continue;
|
|
30
|
+
seen.add(id);
|
|
31
|
+
stageIds.push(id);
|
|
32
|
+
}
|
|
33
|
+
return stageIds.length > 0 ? stageIds : ['scene', 'light', 'final'];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function createTarget(aura, width, height) {
|
|
37
|
+
if (!aura?.draw2d || typeof aura.draw2d.createRenderTarget !== 'function') return null;
|
|
38
|
+
const target = aura.draw2d.createRenderTarget(width, height);
|
|
39
|
+
return target && typeof target === 'object' && target.ok === true ? target : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function destroyTarget(aura, target) {
|
|
43
|
+
if (!target || typeof target !== 'object' || target.ok !== true) return false;
|
|
44
|
+
if (!aura?.draw2d || typeof aura.draw2d.destroyRenderTarget !== 'function') return false;
|
|
45
|
+
aura.draw2d.destroyRenderTarget(target);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveDimensions(compositor, options = {}) {
|
|
50
|
+
const width = positiveInteger(
|
|
51
|
+
options.width ?? compositor.fixedWidth,
|
|
52
|
+
null,
|
|
53
|
+
);
|
|
54
|
+
const height = positiveInteger(
|
|
55
|
+
options.height ?? compositor.fixedHeight,
|
|
56
|
+
null,
|
|
57
|
+
);
|
|
58
|
+
if (width == null || height == null) return null;
|
|
59
|
+
return { width, height };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getStageRecord(compositor, stageId) {
|
|
63
|
+
return Array.isArray(compositor?.stages)
|
|
64
|
+
? compositor.stages.find((entry) => entry.id === stageId) || null
|
|
65
|
+
: null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeStageDefinitions(compositor, stageDefinitions) {
|
|
69
|
+
if (Array.isArray(stageDefinitions)) {
|
|
70
|
+
return compositor.stageIds.map((stageId) => (
|
|
71
|
+
stageDefinitions.find((entry) => entry?.id === stageId) || { id: stageId }
|
|
72
|
+
));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (stageDefinitions && typeof stageDefinitions === 'object') {
|
|
76
|
+
return compositor.stageIds.map((stageId) => {
|
|
77
|
+
const entry = stageDefinitions[stageId];
|
|
78
|
+
if (typeof entry === 'function') {
|
|
79
|
+
return { id: stageId, draw: entry };
|
|
80
|
+
}
|
|
81
|
+
if (entry && typeof entry === 'object') {
|
|
82
|
+
return { id: stageId, ...entry };
|
|
83
|
+
}
|
|
84
|
+
return { id: stageId };
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return compositor.stageIds.map((stageId) => ({ id: stageId }));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createWorldCompositor2D(options = {}) {
|
|
92
|
+
const stageIds = normalizeStageIds(options.stageIds);
|
|
93
|
+
const finalStageId = stageIds.includes(options.finalStageId)
|
|
94
|
+
? options.finalStageId
|
|
95
|
+
: stageIds[stageIds.length - 1];
|
|
96
|
+
return {
|
|
97
|
+
graphName: normalizeText(options.graphName, 'world-compositor-2d'),
|
|
98
|
+
stageIds,
|
|
99
|
+
finalStageId,
|
|
100
|
+
fixedWidth: positiveInteger(options.width ?? options.fixedWidth, null),
|
|
101
|
+
fixedHeight: positiveInteger(options.height ?? options.fixedHeight, null),
|
|
102
|
+
width: 0,
|
|
103
|
+
height: 0,
|
|
104
|
+
targets: null,
|
|
105
|
+
stages: null,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function destroyWorldCompositor2D(compositor, auraRef = globalThis.aura) {
|
|
110
|
+
const aura = resolveAura(auraRef);
|
|
111
|
+
if (!compositor || typeof compositor !== 'object') return false;
|
|
112
|
+
if (compositor.targets && typeof compositor.targets === 'object') {
|
|
113
|
+
for (const stageId of compositor.stageIds || []) {
|
|
114
|
+
destroyTarget(aura, compositor.targets[stageId]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
compositor.width = 0;
|
|
118
|
+
compositor.height = 0;
|
|
119
|
+
compositor.targets = null;
|
|
120
|
+
compositor.stages = null;
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function ensureWorldCompositorTargets2D(compositor, options = {}, auraRef = globalThis.aura) {
|
|
125
|
+
const aura = resolveAura(auraRef);
|
|
126
|
+
if (!compositor || typeof compositor !== 'object') {
|
|
127
|
+
throw new Error('world compositor state is required.');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const dimensions = resolveDimensions(compositor, options);
|
|
131
|
+
if (!dimensions) return null;
|
|
132
|
+
if (
|
|
133
|
+
compositor.targets
|
|
134
|
+
&& compositor.width === dimensions.width
|
|
135
|
+
&& compositor.height === dimensions.height
|
|
136
|
+
) {
|
|
137
|
+
return compositor.targets;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
destroyWorldCompositor2D(compositor, aura);
|
|
141
|
+
|
|
142
|
+
const targets = {};
|
|
143
|
+
for (const stageId of compositor.stageIds) {
|
|
144
|
+
const target = createTarget(aura, dimensions.width, dimensions.height);
|
|
145
|
+
if (!target) {
|
|
146
|
+
for (const created of Object.values(targets)) {
|
|
147
|
+
destroyTarget(aura, created);
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
targets[stageId] = target;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
compositor.width = dimensions.width;
|
|
155
|
+
compositor.height = dimensions.height;
|
|
156
|
+
compositor.targets = targets;
|
|
157
|
+
return targets;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function runWorldCompositor2D(
|
|
161
|
+
compositor,
|
|
162
|
+
stageDefinitions,
|
|
163
|
+
options = {},
|
|
164
|
+
auraRef = globalThis.aura,
|
|
165
|
+
) {
|
|
166
|
+
const aura = resolveAura(auraRef);
|
|
167
|
+
if (!compositor || typeof compositor !== 'object') {
|
|
168
|
+
throw new Error('world compositor state is required.');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const dimensions = resolveDimensions(compositor, options);
|
|
172
|
+
const fallbackDraw = typeof options.fallbackDraw === 'function' ? options.fallbackDraw : null;
|
|
173
|
+
if (!dimensions) {
|
|
174
|
+
if (fallbackDraw) fallbackDraw({ reasonCode: 'invalid_world_compositor_size' });
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
reasonCode: 'invalid_world_compositor_size',
|
|
178
|
+
graph: null,
|
|
179
|
+
targets: null,
|
|
180
|
+
finalStage: null,
|
|
181
|
+
width: 0,
|
|
182
|
+
height: 0,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const targets = ensureWorldCompositorTargets2D(compositor, dimensions, aura);
|
|
187
|
+
if (!targets) {
|
|
188
|
+
if (fallbackDraw) fallbackDraw({ reasonCode: 'draw2d_render_target_unavailable' });
|
|
189
|
+
return {
|
|
190
|
+
ok: false,
|
|
191
|
+
reasonCode: 'draw2d_render_target_unavailable',
|
|
192
|
+
graph: null,
|
|
193
|
+
targets: null,
|
|
194
|
+
finalStage: null,
|
|
195
|
+
width: dimensions.width,
|
|
196
|
+
height: dimensions.height,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const definitions = normalizeStageDefinitions(compositor, stageDefinitions);
|
|
201
|
+
compositor.stages = definitions;
|
|
202
|
+
if (!aura?.draw2d || typeof aura.draw2d.runCompositorGraph !== 'function') {
|
|
203
|
+
if (fallbackDraw) {
|
|
204
|
+
fallbackDraw({
|
|
205
|
+
reasonCode: 'draw2d_compositor_unavailable',
|
|
206
|
+
width: dimensions.width,
|
|
207
|
+
height: dimensions.height,
|
|
208
|
+
targets,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
reasonCode: 'draw2d_compositor_unavailable',
|
|
214
|
+
graph: null,
|
|
215
|
+
targets,
|
|
216
|
+
finalStage: targets[compositor.finalStageId] || null,
|
|
217
|
+
width: dimensions.width,
|
|
218
|
+
height: dimensions.height,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const graph = aura.draw2d.runCompositorGraph(
|
|
223
|
+
compositor.graphName,
|
|
224
|
+
definitions.map((entry) => ({
|
|
225
|
+
id: entry.id,
|
|
226
|
+
target: targets[entry.id],
|
|
227
|
+
draw: () => {
|
|
228
|
+
if (typeof entry.draw !== 'function') return;
|
|
229
|
+
entry.draw({
|
|
230
|
+
id: entry.id,
|
|
231
|
+
width: dimensions.width,
|
|
232
|
+
height: dimensions.height,
|
|
233
|
+
graphName: compositor.graphName,
|
|
234
|
+
targets,
|
|
235
|
+
aura,
|
|
236
|
+
stage: getStageRecord(compositor, entry.id),
|
|
237
|
+
finalStageId: compositor.finalStageId,
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
})),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const finalStageIndex = compositor.stageIds.indexOf(compositor.finalStageId);
|
|
244
|
+
return {
|
|
245
|
+
ok: graph?.ok === true,
|
|
246
|
+
reasonCode: graph?.reasonCode || null,
|
|
247
|
+
graph,
|
|
248
|
+
targets,
|
|
249
|
+
finalStage: graph?.stages?.[finalStageIndex] || targets[compositor.finalStageId] || null,
|
|
250
|
+
width: dimensions.width,
|
|
251
|
+
height: dimensions.height,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { syncStreamedWorldFocus2D } from './streamed-world-2d.js';
|
|
2
|
+
|
|
3
|
+
function cloneSnapshot(value) {
|
|
4
|
+
if (value == null) return value;
|
|
5
|
+
return JSON.parse(JSON.stringify(value));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function resolveStorage(auraRef = globalThis.aura) {
|
|
9
|
+
const storage = auraRef?.storage;
|
|
10
|
+
if (!storage || typeof storage !== 'object') return null;
|
|
11
|
+
return storage;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function storageSave(storage, key, value) {
|
|
15
|
+
if (!storage || typeof key !== 'string' || key.length === 0) return false;
|
|
16
|
+
if (typeof storage.save === 'function') {
|
|
17
|
+
storage.save(key, value);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (typeof storage.set === 'function') {
|
|
21
|
+
storage.set(key, value);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function storageLoad(storage, key, fallback = null) {
|
|
28
|
+
if (!storage || typeof key !== 'string' || key.length === 0) return fallback;
|
|
29
|
+
if (typeof storage.load === 'function') {
|
|
30
|
+
return storage.load(key, fallback);
|
|
31
|
+
}
|
|
32
|
+
if (typeof storage.get === 'function') {
|
|
33
|
+
return storage.get(key, fallback);
|
|
34
|
+
}
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function storageDelete(storage, key) {
|
|
39
|
+
if (!storage || typeof key !== 'string' || key.length === 0) return false;
|
|
40
|
+
if (typeof storage.delete === 'function') {
|
|
41
|
+
storage.delete(key);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeSnapshot(snapshot) {
|
|
48
|
+
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return null;
|
|
49
|
+
return {
|
|
50
|
+
version: Number.isFinite(Number(snapshot.version)) ? Number(snapshot.version) : 1,
|
|
51
|
+
scope: typeof snapshot.scope === 'string' ? snapshot.scope : 'streamed-world-2d',
|
|
52
|
+
activeRegionId: typeof snapshot.activeRegionId === 'string' ? snapshot.activeRegionId : null,
|
|
53
|
+
focus: snapshot.focus && typeof snapshot.focus === 'object'
|
|
54
|
+
? {
|
|
55
|
+
x: Number(snapshot.focus.x) || 0,
|
|
56
|
+
y: Number(snapshot.focus.y) || 0,
|
|
57
|
+
}
|
|
58
|
+
: null,
|
|
59
|
+
world: snapshot.world == null ? null : cloneSnapshot(snapshot.world),
|
|
60
|
+
regions: Array.isArray(snapshot.regions)
|
|
61
|
+
? snapshot.regions
|
|
62
|
+
.filter((entry) => entry && typeof entry === 'object' && typeof entry.id === 'string')
|
|
63
|
+
.map((entry) => ({
|
|
64
|
+
id: entry.id,
|
|
65
|
+
data: cloneSnapshot(entry.data),
|
|
66
|
+
}))
|
|
67
|
+
: [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function captureStreamedWorldSnapshot2D(stream, options = {}) {
|
|
72
|
+
if (!stream || !(stream.regionById instanceof Map)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const captureRegion = typeof options.captureRegion === 'function' ? options.captureRegion : null;
|
|
76
|
+
const captureWorld = typeof options.captureWorld === 'function' ? options.captureWorld : null;
|
|
77
|
+
const regionIds = Array.isArray(options.regionIds)
|
|
78
|
+
? options.regionIds.filter((entry) => typeof entry === 'string')
|
|
79
|
+
: stream.regions.map((entry) => entry.id);
|
|
80
|
+
const regions = [];
|
|
81
|
+
|
|
82
|
+
regionIds.forEach((regionId) => {
|
|
83
|
+
const descriptor = stream.regionById.get(regionId) || null;
|
|
84
|
+
if (!descriptor || !captureRegion) return;
|
|
85
|
+
const record = stream.loaded?.get(regionId) || null;
|
|
86
|
+
const data = captureRegion(descriptor, record, stream, options);
|
|
87
|
+
if (data == null) return;
|
|
88
|
+
regions.push({
|
|
89
|
+
id: regionId,
|
|
90
|
+
data: cloneSnapshot(data),
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
version: 1,
|
|
96
|
+
scope: options.scope ?? 'streamed-world-2d',
|
|
97
|
+
activeRegionId: stream.activeRegionId ?? null,
|
|
98
|
+
focus: stream.focus ? { x: Number(stream.focus.x) || 0, y: Number(stream.focus.y) || 0 } : null,
|
|
99
|
+
world: captureWorld ? cloneSnapshot(captureWorld(stream, options)) : null,
|
|
100
|
+
regions,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function applyStreamedWorldSnapshot2D(stream, snapshot, options = {}) {
|
|
105
|
+
if (!stream || !(stream.regionById instanceof Map)) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
reasonCode: 'invalid_stream',
|
|
109
|
+
appliedRegionIds: [],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const normalized = normalizeSnapshot(snapshot);
|
|
113
|
+
if (!normalized) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
reasonCode: 'invalid_snapshot',
|
|
117
|
+
appliedRegionIds: [],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const applyRegion = typeof options.applyRegion === 'function' ? options.applyRegion : null;
|
|
121
|
+
const applyWorld = typeof options.applyWorld === 'function' ? options.applyWorld : null;
|
|
122
|
+
const appliedRegionIds = [];
|
|
123
|
+
|
|
124
|
+
if (applyWorld && normalized.world != null) {
|
|
125
|
+
applyWorld(cloneSnapshot(normalized.world), stream, options);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
normalized.regions.forEach((entry) => {
|
|
129
|
+
const descriptor = stream.regionById.get(entry.id) || null;
|
|
130
|
+
if (!descriptor || !applyRegion) return;
|
|
131
|
+
const record = stream.loaded?.get(entry.id) || null;
|
|
132
|
+
applyRegion(descriptor, record, cloneSnapshot(entry.data), stream, options);
|
|
133
|
+
appliedRegionIds.push(entry.id);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (options.restoreFocus === true && normalized.focus) {
|
|
137
|
+
syncStreamedWorldFocus2D(stream, options.auraRef, normalized.focus);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
reasonCode: null,
|
|
143
|
+
appliedRegionIds,
|
|
144
|
+
restoredFocus: options.restoreFocus === true && normalized.focus != null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function saveStreamedWorldSnapshot2D(key, stream, auraRef = globalThis.aura, options = {}) {
|
|
149
|
+
const storage = resolveStorage(auraRef);
|
|
150
|
+
const snapshot = captureStreamedWorldSnapshot2D(stream, options);
|
|
151
|
+
if (!snapshot) {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
reasonCode: 'invalid_stream',
|
|
155
|
+
snapshot: null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (!storageSave(storage, key, snapshot)) {
|
|
159
|
+
return {
|
|
160
|
+
ok: false,
|
|
161
|
+
reasonCode: 'storage_unavailable',
|
|
162
|
+
snapshot,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
reasonCode: null,
|
|
168
|
+
snapshot,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function loadStreamedWorldSnapshot2D(key, auraRef = globalThis.aura, fallback = null) {
|
|
173
|
+
const storage = resolveStorage(auraRef);
|
|
174
|
+
const snapshot = storageLoad(storage, key, fallback);
|
|
175
|
+
return normalizeSnapshot(snapshot);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function deleteStreamedWorldSnapshot2D(key, auraRef = globalThis.aura) {
|
|
179
|
+
return storageDelete(resolveStorage(auraRef), key);
|
|
180
|
+
}
|
|
@@ -62,18 +62,47 @@ function resolveAuraMaxxBinary() {
|
|
|
62
62
|
return process.platform === 'win32' ? 'auramaxx.cmd' : 'auramaxx';
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function resolveAuraAliasBinary() {
|
|
66
|
+
return process.platform === 'win32' ? 'aura.cmd' : 'aura';
|
|
67
|
+
}
|
|
68
|
+
|
|
65
69
|
function resolveNpmBinary() {
|
|
66
70
|
return process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
function
|
|
70
|
-
const
|
|
73
|
+
function resolveInvocationCwd() {
|
|
74
|
+
const forwardedCwd = process.env.AURA_INVOKE_CWD;
|
|
75
|
+
if (forwardedCwd && isAbsolute(forwardedCwd)) {
|
|
76
|
+
return resolve(forwardedCwd);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const shellPwd = process.env.PWD;
|
|
80
|
+
if (shellPwd && isAbsolute(shellPwd)) {
|
|
81
|
+
return resolve(shellPwd);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return resolve(process.cwd());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function canProbeBinary(binary) {
|
|
88
|
+
const probe = spawnSync(binary, ['--help'], {
|
|
71
89
|
stdio: 'ignore',
|
|
72
90
|
env: process.env,
|
|
73
91
|
});
|
|
74
92
|
return !probe.error && probe.status === 0;
|
|
75
93
|
}
|
|
76
94
|
|
|
95
|
+
function isAuraMaxxInstalled() {
|
|
96
|
+
if (process.env.AURAMAXX_CLI_AVAILABLE === '1') {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (localAuraCli) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
const candidates = [resolveAuraMaxxBinary(), resolveAuraAliasBinary()];
|
|
103
|
+
return candidates.some((binary, index) => candidates.indexOf(binary) === index && canProbeBinary(binary));
|
|
104
|
+
}
|
|
105
|
+
|
|
77
106
|
async function installAuraMaxxGlobally() {
|
|
78
107
|
return new Promise((resolveInstall) => {
|
|
79
108
|
const child = spawn(
|
|
@@ -373,7 +402,7 @@ async function promptSelect(message, options, defaultValue) {
|
|
|
373
402
|
|
|
374
403
|
function normalizeDisplayPath(targetPath) {
|
|
375
404
|
const resolvedPath = resolve(targetPath);
|
|
376
|
-
const relativePath = relative(
|
|
405
|
+
const relativePath = relative(resolveInvocationCwd(), resolvedPath).replaceAll('\\', '/');
|
|
377
406
|
if (!relativePath) return '.';
|
|
378
407
|
if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
|
|
379
408
|
return `./${relativePath}`;
|
|
@@ -402,10 +431,10 @@ function resolveUniqueDestination(preferredPath) {
|
|
|
402
431
|
|
|
403
432
|
function resolveDefaultForkDestination() {
|
|
404
433
|
const defaultBaseName = `${toPackageShortName(packageName)}-fork`;
|
|
405
|
-
const
|
|
406
|
-
const baseDir = isSubpath(projectRoot,
|
|
434
|
+
const invocationCwd = resolveInvocationCwd();
|
|
435
|
+
const baseDir = isSubpath(projectRoot, invocationCwd)
|
|
407
436
|
? dirname(projectRoot)
|
|
408
|
-
:
|
|
437
|
+
: invocationCwd;
|
|
409
438
|
return resolveUniqueDestination(resolve(baseDir, defaultBaseName));
|
|
410
439
|
}
|
|
411
440
|
|
|
@@ -454,7 +483,7 @@ function parseForkArgs(args) {
|
|
|
454
483
|
async function resolveForkDestination(args) {
|
|
455
484
|
const parsed = parseForkArgs(args);
|
|
456
485
|
if (parsed.destination) {
|
|
457
|
-
return resolve(
|
|
486
|
+
return resolve(resolveInvocationCwd(), parsed.destination);
|
|
458
487
|
}
|
|
459
488
|
|
|
460
489
|
const suggestedDestination = resolveDefaultForkDestination();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: auramaxx
|
|
3
|
+
description: Use when building or modifying AuraMaxx or AuraJS game projects. Helps agents infer game requirements from local project files, pick the right starter-owned edit points, and validate gameplay work with the current CLI and project wrapper commands.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AuraMaxx Skill
|
|
7
|
+
|
|
8
|
+
Use this skill when working inside an AuraMaxx or AuraJS game project.
|
|
9
|
+
|
|
10
|
+
## Working Set
|
|
11
|
+
|
|
12
|
+
Read these files in order:
|
|
13
|
+
|
|
14
|
+
1. `project-requirements.md`
|
|
15
|
+
2. `starter-recipes.md`
|
|
16
|
+
3. `validation-checklist.md`
|
|
17
|
+
4. the current project files named by those docs
|
|
18
|
+
|
|
19
|
+
Use `https://www.aurajs.gg/llm.txt` only when you need broader public docs
|
|
20
|
+
context than the local project and installed package sources provide.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
- Treat the current project files as the primary source of truth.
|
|
25
|
+
- Keep `src/main.js` thin; prefer the registry-backed runtime flow already in the scaffold.
|
|
26
|
+
- Put durable authored data in `config/` or `content/`, not scene-local constants.
|
|
27
|
+
- Put mutable runtime state in scenes or `src/runtime/app-state.js`.
|
|
28
|
+
- Extend `src/starter-utils/` before inventing one-off helpers.
|
|
29
|
+
- Use `auramaxx make` instead of hand-wiring new scaffold nouns when a generator exists.
|
|
30
|
+
|
|
31
|
+
## Default Loop
|
|
32
|
+
|
|
33
|
+
1. Infer the current game requirements with `project-requirements.md`.
|
|
34
|
+
2. Choose the starter-specific edit path in `starter-recipes.md`.
|
|
35
|
+
3. Implement the smallest playable slice that changes one mechanic at a time.
|
|
36
|
+
4. Run the relevant checks from `validation-checklist.md`.
|
|
37
|
+
|
|
38
|
+
## Retrieval Boundary
|
|
39
|
+
|
|
40
|
+
Open local project files first. If you still need package-level behavior or
|
|
41
|
+
template intent, inspect:
|
|
42
|
+
|
|
43
|
+
- `node_modules/@auraindustry/aurajs/src/`
|
|
44
|
+
- `node_modules/@auraindustry/aurajs/templates/`
|
|
45
|
+
|
|
46
|
+
Use `aurajs.gg/llm.txt` after that, not before.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# AuraMaxx Project Requirements
|
|
2
|
+
|
|
3
|
+
Use this checklist to understand the game before you edit it.
|
|
4
|
+
|
|
5
|
+
## 1. Identity
|
|
6
|
+
|
|
7
|
+
- Read `package.json` and `aura.config.json`.
|
|
8
|
+
- Capture: package name, bin name, project title, version, runtime targets, and
|
|
9
|
+
whether the project is intended to be published.
|
|
10
|
+
|
|
11
|
+
## 2. Template and module shape
|
|
12
|
+
|
|
13
|
+
- Read `aura.capabilities.json` and `config/gameplay/game.config.json`.
|
|
14
|
+
- Capture: template id, required APIs, optional modules, `startingSceneId`,
|
|
15
|
+
`gameplaySceneId`, `hudScreenId`, and `playerPrefabId`.
|
|
16
|
+
|
|
17
|
+
## 3. Player promise
|
|
18
|
+
|
|
19
|
+
- Read `README.md`, `RUNBOOK.md`, `docs/design/game-pillars.md`,
|
|
20
|
+
`docs/design/loop.md`, and `assets/starter/dev-notes.md`.
|
|
21
|
+
- Write down:
|
|
22
|
+
- genre and player fantasy
|
|
23
|
+
- first 60-second goal
|
|
24
|
+
- controls
|
|
25
|
+
- win and lose states
|
|
26
|
+
- what “done for this task” should feel like on screen
|
|
27
|
+
|
|
28
|
+
## 4. Authored owners
|
|
29
|
+
|
|
30
|
+
- Read `src/runtime/project-registry.js`, `config/gameplay/*`,
|
|
31
|
+
`content/gameplay/*`, `content/registries/*`, `prefabs/*.prefab.js`,
|
|
32
|
+
`scenes/*.scene.js`, and `ui/*.screen.js`.
|
|
33
|
+
- Map which files own:
|
|
34
|
+
- loop logic
|
|
35
|
+
- tuning values
|
|
36
|
+
- authored nouns and layout
|
|
37
|
+
- HUD and modal UI
|
|
38
|
+
- progression, save, dialogue, inventory, or multiplayer state
|
|
39
|
+
|
|
40
|
+
## 5. Assets and starter data
|
|
41
|
+
|
|
42
|
+
- Read `assets/starter/*` first, then only the `assets/**` files actually used
|
|
43
|
+
by the touched scene or prefab.
|
|
44
|
+
- Capture: placeholder assets still in use, required art/audio hooks, and any
|
|
45
|
+
starter JSON or notes that define layout, waves, cards, checkpoints, or room
|
|
46
|
+
behavior.
|
|
47
|
+
|
|
48
|
+
## 6. Decide where the change belongs
|
|
49
|
+
|
|
50
|
+
- Stable tunable: `config/`
|
|
51
|
+
- Authored noun or layout data: `content/`
|
|
52
|
+
- Reusable entity or world descriptor: `prefabs/`
|
|
53
|
+
- Live mechanic or per-frame flow: `scenes/`
|
|
54
|
+
- HUD, overlay, or modal surface: `ui/`
|
|
55
|
+
- Cross-scene mutable app state: `src/runtime/app-state.js`
|
|
56
|
+
- Reusable starter helper: `src/starter-utils/`
|
|
57
|
+
|
|
58
|
+
## 7. Discovery commands
|
|
59
|
+
|
|
60
|
+
- `auramaxx explain`
|
|
61
|
+
- `auramaxx check`
|
|
62
|
+
- `auramaxx make list --json`
|
|
63
|
+
- `npm run dev`
|
|
64
|
+
- `npm run state -- export --compact`
|
|
65
|
+
- `npm run action -- schema --compact`
|
|
66
|
+
|
|
67
|
+
Use these to confirm what the project already exposes before you add new
|
|
68
|
+
structure.
|