@auraindustry/aurajs 0.1.1 → 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/asset-pack.mjs +2 -1
- package/src/authored-project.mjs +498 -2
- package/src/authored-runtime.mjs +14 -0
- package/src/bin-integrity.mjs +33 -26
- 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 +1126 -10
- package/src/conformance-mobile.mjs +166 -0
- package/src/conformance.mjs +89 -30
- package/src/evidence-bundle.mjs +242 -0
- package/src/external-package-surface.mjs +1 -1
- 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/package-integrity.mjs +18 -4
- 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 +434 -17
- package/src/publish-validation.mjs +22 -11
- 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 +101 -41
- package/src/scaffold.mjs +4 -0
- package/src/session-runtime.mjs +4 -3
- package/src/web-conformance.mjs +0 -36
- package/templates/create/2d/src/runtime/app.js +4 -0
- 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/2d-survivor/src/runtime/app.js +4 -0
- package/templates/create/3d/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d/src/runtime/app.js +4 -0
- 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/app.js +4 -0
- 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/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/scenes/gameplay.scene.js +186 -12
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
- 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/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/video-cutscene/src/runtime/app.js +4 -0
- package/templates/create-bin/play.js +148 -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/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
- 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,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.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# AuraMaxx Starter Recipes
|
|
2
|
+
|
|
3
|
+
Use the current template id from `aura.capabilities.json` or `aura.config.json`
|
|
4
|
+
to pick the right recipe.
|
|
5
|
+
|
|
6
|
+
## 2d-adventure
|
|
7
|
+
|
|
8
|
+
- Edit first:
|
|
9
|
+
- `content/gameplay/world.js`
|
|
10
|
+
- `config/gameplay/adventure.config.js`
|
|
11
|
+
- `scenes/gameplay.scene.js`
|
|
12
|
+
- `prefabs/world.prefab.js`
|
|
13
|
+
- `prefabs/relic.prefab.js`
|
|
14
|
+
- `ui/hud.screen.js`
|
|
15
|
+
- Good first tasks:
|
|
16
|
+
- tighten movement and interaction feel
|
|
17
|
+
- rewrite room nouns, relic goals, dialogue, and route beats
|
|
18
|
+
- add one strong interaction before widening scope
|
|
19
|
+
- Avoid:
|
|
20
|
+
- burying route data inside the scene
|
|
21
|
+
- replacing starter-utils with duplicated local helpers
|
|
22
|
+
|
|
23
|
+
## 2d-shooter and 2d-survivor
|
|
24
|
+
|
|
25
|
+
- Edit first:
|
|
26
|
+
- `config/gameplay/*.config.js`
|
|
27
|
+
- `scenes/gameplay.scene.js`
|
|
28
|
+
- `prefabs/player.prefab.js`
|
|
29
|
+
- enemy or projectile prefabs
|
|
30
|
+
- `ui/*.screen.js`
|
|
31
|
+
- Good first tasks:
|
|
32
|
+
- pacing, spawn pressure, weapon feel, hit feedback, restart loop
|
|
33
|
+
- Avoid:
|
|
34
|
+
- mixing balance constants into render code
|
|
35
|
+
- adding many enemy types before one wave feels good
|
|
36
|
+
|
|
37
|
+
## deckbuilder-2d
|
|
38
|
+
|
|
39
|
+
- Edit first:
|
|
40
|
+
- starter-owned `content/registries/*`
|
|
41
|
+
- `config/gameplay/*`
|
|
42
|
+
- combat scene files
|
|
43
|
+
- HUD and card presentation screens
|
|
44
|
+
- Good first tasks:
|
|
45
|
+
- one coherent card set
|
|
46
|
+
- one enemy intent loop
|
|
47
|
+
- one relic or progression rule
|
|
48
|
+
- Avoid:
|
|
49
|
+
- putting card data directly in scene logic
|
|
50
|
+
|
|
51
|
+
## 3d-adventure, 3d-platformer, 3d-collectathon
|
|
52
|
+
|
|
53
|
+
- Edit first:
|
|
54
|
+
- `content/gameplay/course.js`
|
|
55
|
+
- `config/gameplay/*.config.js`
|
|
56
|
+
- `scenes/gameplay.scene.js`
|
|
57
|
+
- `prefabs/world.prefab.js`
|
|
58
|
+
- `prefabs/player.prefab.js`
|
|
59
|
+
- `src/starter-utils/avatar-3d.js`
|
|
60
|
+
- `src/starter-utils/platformer-3d.js`
|
|
61
|
+
- Good first tasks:
|
|
62
|
+
- movement feel, camera follow, checkpoints, collectibles, route readability
|
|
63
|
+
- Avoid:
|
|
64
|
+
- hardcoding course layout in the scene
|
|
65
|
+
- treating imported models as the only source of gameplay truth
|
|
66
|
+
|
|
67
|
+
## local-multiplayer
|
|
68
|
+
|
|
69
|
+
- Edit first:
|
|
70
|
+
- `config/gameplay/local-multiplayer.config.js`
|
|
71
|
+
- `content/gameplay/room-layout.js`
|
|
72
|
+
- `scenes/gameplay.scene.js`
|
|
73
|
+
- `ui/hud.screen.js`
|
|
74
|
+
- Good first tasks:
|
|
75
|
+
- one host/join room loop
|
|
76
|
+
- one shared objective
|
|
77
|
+
- one clear disconnect or retry path
|
|
78
|
+
- Avoid:
|
|
79
|
+
- breaking the local-first host flow while chasing internet features
|
|
80
|
+
- assuming headless state or action flows work unless you verify them
|
|
81
|
+
|
|
82
|
+
## blank
|
|
83
|
+
|
|
84
|
+
- Edit first:
|
|
85
|
+
- `src/main.js`
|
|
86
|
+
- `scenes/gameplay.scene.js`
|
|
87
|
+
- `prefabs/player.prefab.js`
|
|
88
|
+
- `ui/hud.screen.js`
|
|
89
|
+
- `config/gameplay/game.config.json`
|
|
90
|
+
- Good first tasks:
|
|
91
|
+
- one visible mechanic
|
|
92
|
+
- one HUD element
|
|
93
|
+
- one authored config value
|
|
94
|
+
- Avoid:
|
|
95
|
+
- recreating the whole scaffold model from scratch
|
|
96
|
+
|
|
97
|
+
## Cross-starter recipe
|
|
98
|
+
|
|
99
|
+
For any starter:
|
|
100
|
+
|
|
101
|
+
1. make one mechanic feel good
|
|
102
|
+
2. make the goal readable in HUD or world feedback
|
|
103
|
+
3. move stable data into `config/` or `content/`
|
|
104
|
+
4. only then add more systems
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# AuraMaxx Validation Checklist
|
|
2
|
+
|
|
3
|
+
Pick the smallest set that matches the task.
|
|
4
|
+
|
|
5
|
+
## Always
|
|
6
|
+
|
|
7
|
+
- Run `auramaxx explain` after structure changes.
|
|
8
|
+
- Run `auramaxx check` before and after edits.
|
|
9
|
+
- If you add new scaffold nouns, run `auramaxx make list --json` first and use
|
|
10
|
+
`auramaxx make ...` where possible.
|
|
11
|
+
|
|
12
|
+
## Gameplay slice
|
|
13
|
+
|
|
14
|
+
- Run `npm run dev`.
|
|
15
|
+
- Confirm:
|
|
16
|
+
- the game boots
|
|
17
|
+
- the first objective is readable
|
|
18
|
+
- controls match the intended loop
|
|
19
|
+
- HUD or world feedback reflects the new mechanic
|
|
20
|
+
- there are no obvious stuck states or soft-locks
|
|
21
|
+
|
|
22
|
+
## State and action tooling
|
|
23
|
+
|
|
24
|
+
- Run `npm run state -- export --compact` when the project claims state export.
|
|
25
|
+
- Run `npm run action -- schema --compact` when the project claims action
|
|
26
|
+
tooling.
|
|
27
|
+
- If these fail, record the exact reason instead of pretending the flow works.
|
|
28
|
+
|
|
29
|
+
## Session and headless flows
|
|
30
|
+
|
|
31
|
+
- Use `auramaxx session start --headless --name ci-loop --compact` only after
|
|
32
|
+
verifying the starter and runtime actually support that lane.
|
|
33
|
+
- If the project is multiplayer or starter-heavy, verify headless flows
|
|
34
|
+
explicitly instead of assuming parity with visible `npm run dev`.
|
|
35
|
+
|
|
36
|
+
## Publish and package surface
|
|
37
|
+
|
|
38
|
+
- Run `auramaxx publish --dry-run` for package-surface and build validation.
|
|
39
|
+
- Run `auramaxx external-assets generate --public-base-url <url>` only after the
|
|
40
|
+
build artifacts exist and the project actually needs self-hosted assets.
|
|
41
|
+
|
|
42
|
+
## What to report
|
|
43
|
+
|
|
44
|
+
When you finish, report:
|
|
45
|
+
|
|
46
|
+
- which commands passed
|
|
47
|
+
- which commands failed
|
|
48
|
+
- whether failures are task regressions or pre-existing runtime gaps
|
|
49
|
+
- what the player-visible outcome is
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// - modules.physics: enables aura.physics.*
|
|
4
4
|
// - modules.network: enables aura.net.*
|
|
5
5
|
// - modules.steam: enables aura.steam.*
|
|
6
|
+
import { initSplash, updateSplash, drawSplash, isSplashActive } from './runtime/splash.js';
|
|
6
7
|
|
|
7
8
|
let x = 400;
|
|
8
9
|
let y = 300;
|
|
@@ -153,6 +154,7 @@ function clearScreen(color) {
|
|
|
153
154
|
|
|
154
155
|
aura.setup = function () {
|
|
155
156
|
assertRuntimeCapabilities();
|
|
157
|
+
initSplash();
|
|
156
158
|
console.log("{{PROJECT_NAME}} started");
|
|
157
159
|
if (!hasAsset(PLAYER_SPRITE_PATH)) {
|
|
158
160
|
console.log(`Tip: add assets/${PLAYER_SPRITE_PATH} to preview sprite draw2d options.`);
|
|
@@ -163,6 +165,7 @@ aura.setup = function () {
|
|
|
163
165
|
};
|
|
164
166
|
|
|
165
167
|
aura.update = function (dt) {
|
|
168
|
+
if (isSplashActive()) { updateSplash(dt); return; }
|
|
166
169
|
if (keyDown("arrowright")) x += speed * dt;
|
|
167
170
|
if (keyDown("arrowleft")) x -= speed * dt;
|
|
168
171
|
if (keyDown("arrowup")) y -= speed * dt;
|
|
@@ -186,6 +189,7 @@ aura.update = function (dt) {
|
|
|
186
189
|
};
|
|
187
190
|
|
|
188
191
|
aura.draw = function () {
|
|
192
|
+
if (isSplashActive()) { drawSplash(); return; }
|
|
189
193
|
const bg = rgba(0.08, 0.08, 0.12, 1.0);
|
|
190
194
|
clearScreen(bg);
|
|
191
195
|
|
|
@@ -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
|
+
}
|