castle-web-cli 0.4.113 → 0.4.115
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/dist/castleJson.d.ts +1 -0
- package/dist/editorConfig.d.ts +34 -0
- package/dist/editorConfig.js +101 -0
- package/dist/ide.js +245 -120
- package/dist/imports.d.ts +6 -0
- package/dist/imports.js +90 -7
- package/dist/init.js +15 -2
- package/dist/serve.js +16 -0
- package/dist/shell/assets/index-CUamb8rK.js +144 -0
- package/dist/shell/assets/index-Y6cJRRCX.css +1 -0
- package/dist/shell/index.html +2 -2
- package/dist/unsupportedMedia.d.ts +1 -0
- package/dist/unsupportedMedia.js +54 -0
- package/dist/vitePlugins.js +12 -19
- package/kits/basic-2d/castle.json +1 -1
- package/kits/basic-2d/editors/behaviorRegistry.js +5 -7
- package/kits/physics-2d/CLAUDE.md +42 -23
- package/kits/physics-2d/{physics/behaviors → behaviors}/AnalogStick.jsx +3 -3
- package/kits/physics-2d/behaviors/Collider.jsx +1 -1
- package/kits/physics-2d/{physics/behaviors → behaviors}/Draggable.jsx +3 -3
- package/kits/physics-2d/{physics/behaviors → behaviors}/Joints.jsx +4 -4
- package/kits/physics-2d/{physics/behaviors → behaviors}/RigidBody.jsx +2 -2
- package/kits/physics-2d/{physics/behaviors → behaviors}/Slingshot.jsx +3 -3
- package/kits/physics-2d/behaviors/Sound.jsx +152 -0
- package/kits/physics-2d/behaviors/Sprite.jsx +98 -33
- package/kits/physics-2d/behaviors/Tone.jsx +83 -0
- package/kits/physics-2d/behaviors/Video.jsx +111 -0
- package/kits/physics-2d/behaviors/tint.js +24 -9
- package/kits/physics-2d/castle.json +64 -2
- package/kits/physics-2d/editors/BlueprintLibrary.jsx +7 -3
- package/kits/physics-2d/editors/ImageViewer.jsx +206 -0
- package/kits/physics-2d/editors/MediaPlayer.jsx +57 -0
- package/kits/physics-2d/editors/SceneEditor.jsx +7 -3
- package/kits/physics-2d/editors/SingleEditor.jsx +8 -0
- package/kits/physics-2d/editors/behaviorRegistry.js +5 -7
- package/kits/physics-2d/editors/mediaFile.js +20 -0
- package/kits/physics-2d/editors/mediaViewer.module.css +128 -0
- package/kits/physics-2d/engine/ScenePlayer.jsx +1 -0
- package/kits/physics-2d/engine/art.js +105 -0
- package/kits/physics-2d/engine/assets.js +11 -3
- package/kits/physics-2d/engine/audioContext.js +34 -0
- package/kits/physics-2d/engine/collider.js +28 -17
- package/kits/physics-2d/engine/files.js +74 -0
- package/kits/physics-2d/engine/media.js +212 -0
- package/kits/physics-2d/{physics → engine/physics}/PhysicsSystem.js +1 -1
- package/kits/physics-2d/{physics → engine/physics}/jointArt.js +1 -2
- package/kits/physics-2d/{physics → engine/physics}/matterBridge.js +2 -9
- package/kits/physics-2d/engine/scene.js +12 -0
- package/kits/physics-2d/engine/tone.js +112 -0
- package/kits/physics-2d/systems/media.js +34 -0
- package/kits/physics-2d/systems/physics.js +12 -3
- package/package.json +1 -1
- package/dist/shell/assets/index-BjOuaDJM.js +0 -144
- package/dist/shell/assets/index-CdXEpv_P.css +0 -1
- package/kits/physics-2d/physics/index.js +0 -26
- /package/kits/physics-2d/{physics → engine/physics}/controls.js +0 -0
- /package/kits/physics-2d/{physics → engine/physics}/joints.js +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// A synthesized note: one oscillator through an attack/release envelope. This
|
|
2
|
+
// is the sound a deck can make with no audio file at all -- a blip on a pickup,
|
|
3
|
+
// a beep on a wrong answer -- and it costs nothing to ship.
|
|
4
|
+
//
|
|
5
|
+
// Deliberately one voice and two envelope stages, matching what the classic
|
|
6
|
+
// Castle editor's "tone" sample offers. Anything richer is an instrument, and an
|
|
7
|
+
// instrument belongs in a music system, not here.
|
|
8
|
+
|
|
9
|
+
import { getAudioContext, resumeAudioContext } from './audioContext';
|
|
10
|
+
|
|
11
|
+
export const WAVEFORMS = ['square', 'sawtooth', 'sine', 'triangle', 'noise'];
|
|
12
|
+
|
|
13
|
+
// Voices currently sounding, so play mode can be silenced on Stop.
|
|
14
|
+
const voices = new Set();
|
|
15
|
+
|
|
16
|
+
// MIDI note number to Hz. 69 is A440, 12 notes to the octave.
|
|
17
|
+
export function noteToFrequency(note) {
|
|
18
|
+
const midi = Number.isFinite(note) ? note : 60;
|
|
19
|
+
return 440 * Math.pow(2, (midi - 69) / 12);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// A short burst of white noise, made once and reused. Noise is the one waveform
|
|
23
|
+
// an oscillator can't produce, and it's what a percussive hit wants.
|
|
24
|
+
let noiseBuffer = null;
|
|
25
|
+
function getNoiseBuffer(ctx) {
|
|
26
|
+
if (noiseBuffer && noiseBuffer.sampleRate === ctx.sampleRate) return noiseBuffer;
|
|
27
|
+
const frames = Math.floor(ctx.sampleRate * 2);
|
|
28
|
+
const buffer = ctx.createBuffer(1, frames, ctx.sampleRate);
|
|
29
|
+
const data = buffer.getChannelData(0);
|
|
30
|
+
for (let i = 0; i < frames; i++) data[i] = Math.random() * 2 - 1;
|
|
31
|
+
noiseBuffer = buffer;
|
|
32
|
+
return buffer;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function makeSource(ctx, waveform, frequency) {
|
|
36
|
+
if (waveform === 'noise') {
|
|
37
|
+
const source = ctx.createBufferSource();
|
|
38
|
+
source.buffer = getNoiseBuffer(ctx);
|
|
39
|
+
source.loop = true;
|
|
40
|
+
return source;
|
|
41
|
+
}
|
|
42
|
+
const osc = ctx.createOscillator();
|
|
43
|
+
osc.type = WAVEFORMS.includes(waveform) ? waveform : 'square';
|
|
44
|
+
osc.frequency.value = frequency;
|
|
45
|
+
return osc;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Play one note. Returns `{ stop, finished }` -- `finished` resolves when the
|
|
49
|
+
// release has run out, so a caller can wait for it the way a Sound can.
|
|
50
|
+
export function playTone({ note = 60, waveform = 'square', attack = 0, release = 0.4, volume = 1, pan = 0 } = {}) {
|
|
51
|
+
const ctx = getAudioContext();
|
|
52
|
+
if (!ctx) return { stop() {}, finished: Promise.resolve() };
|
|
53
|
+
resumeAudioContext();
|
|
54
|
+
|
|
55
|
+
const level = Number.isFinite(volume) ? Math.min(1, Math.max(0, volume)) : 1;
|
|
56
|
+
const rise = Math.max(0, Number.isFinite(attack) ? attack : 0);
|
|
57
|
+
// A note with no release at all clicks; give it a floor short enough to still
|
|
58
|
+
// read as instant.
|
|
59
|
+
const fall = Math.max(0.01, Number.isFinite(release) ? release : 0.4);
|
|
60
|
+
const start = ctx.currentTime;
|
|
61
|
+
const end = start + rise + fall;
|
|
62
|
+
|
|
63
|
+
const source = makeSource(ctx, waveform, noteToFrequency(note));
|
|
64
|
+
const gain = ctx.createGain();
|
|
65
|
+
gain.gain.setValueAtTime(0, start);
|
|
66
|
+
gain.gain.linearRampToValueAtTime(level, start + rise);
|
|
67
|
+
gain.gain.linearRampToValueAtTime(0, end);
|
|
68
|
+
|
|
69
|
+
let tail = gain;
|
|
70
|
+
if (pan && ctx.createStereoPanner) {
|
|
71
|
+
const panner = ctx.createStereoPanner();
|
|
72
|
+
panner.pan.value = Math.min(1, Math.max(-1, pan));
|
|
73
|
+
gain.connect(panner);
|
|
74
|
+
tail = panner;
|
|
75
|
+
}
|
|
76
|
+
source.connect(gain);
|
|
77
|
+
tail.connect(ctx.destination);
|
|
78
|
+
source.start(start);
|
|
79
|
+
source.stop(end);
|
|
80
|
+
|
|
81
|
+
const voice = { source, gain };
|
|
82
|
+
voices.add(voice);
|
|
83
|
+
const finished = new Promise((resolve) => {
|
|
84
|
+
source.addEventListener('ended', () => {
|
|
85
|
+
voices.delete(voice);
|
|
86
|
+
resolve();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
stop() {
|
|
91
|
+
voices.delete(voice);
|
|
92
|
+
try {
|
|
93
|
+
source.stop();
|
|
94
|
+
} catch {
|
|
95
|
+
// Already stopped -- nothing to do.
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
finished,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Silence every sounding note. Play mode ending must not leave one ringing.
|
|
103
|
+
export function stopAllTones() {
|
|
104
|
+
for (const voice of voices) {
|
|
105
|
+
try {
|
|
106
|
+
voice.source.stop();
|
|
107
|
+
} catch {
|
|
108
|
+
// Already stopped.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
voices.clear();
|
|
112
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Registers sound / video playback as a runtime system. Auto-discovered by
|
|
2
|
+
// engine/systemRegistry.js (`systems/*.js`) and invoked by makeScene, so the
|
|
3
|
+
// media pool plugs in without the shared engine referencing it.
|
|
4
|
+
//
|
|
5
|
+
// The system owns the LIFECYCLE only -- behaviors decide what plays. It reaps
|
|
6
|
+
// idle elements once a frame (a despawned actor's sound stops), and silences
|
|
7
|
+
// everything on `reset` (a scene load / restart / transition) and on `dispose`
|
|
8
|
+
// (the player unmounting, i.e. pressing Stop).
|
|
9
|
+
//
|
|
10
|
+
// It also puts `scene.sound` on the runtime: the scene-wide controls, which
|
|
11
|
+
// belong to nobody's actor.
|
|
12
|
+
|
|
13
|
+
import { reapUnusedMedia, stopAllMedia } from '../engine/media';
|
|
14
|
+
import { stopAllTones } from '../engine/tone';
|
|
15
|
+
|
|
16
|
+
function silence() {
|
|
17
|
+
stopAllMedia();
|
|
18
|
+
stopAllTones();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function installSystem(runtime) {
|
|
22
|
+
runtime.sound = {
|
|
23
|
+
// Everything this scene is playing, off. The blunt instrument for a game
|
|
24
|
+
// over, a pause, or leaving a level: individual actors need not be found.
|
|
25
|
+
stopAll: silence,
|
|
26
|
+
};
|
|
27
|
+
runtime.registerSystem({
|
|
28
|
+
afterBehaviors(scene) {
|
|
29
|
+
reapUnusedMedia(new Set(scene.actors.keys()));
|
|
30
|
+
},
|
|
31
|
+
reset: silence,
|
|
32
|
+
dispose: silence,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
// Registers the physics simulation as a runtime system. Auto-discovered by
|
|
2
2
|
// engine/systemRegistry.js (`systems/*.js`) and invoked by makeScene, so the
|
|
3
|
-
//
|
|
4
|
-
|
|
3
|
+
// shared engine never has to reference physics directly.
|
|
4
|
+
//
|
|
5
|
+
// The simulation itself lives in `engine/physics/`; its behaviors (RigidBody,
|
|
6
|
+
// Joints, and the touch-first controls) are ordinary behaviors in `behaviors/`.
|
|
7
|
+
import { PhysicsSystem } from '../engine/physics/PhysicsSystem';
|
|
5
8
|
|
|
9
|
+
// Register a physics system on a runtime and wire up `runtime.physics`. Cheap
|
|
10
|
+
// to call on every runtime -- the matter engine is created lazily on the first
|
|
11
|
+
// simulation step.
|
|
6
12
|
export function installSystem(runtime) {
|
|
7
|
-
|
|
13
|
+
const system = new PhysicsSystem();
|
|
14
|
+
runtime.registerSystem(system);
|
|
15
|
+
system.attach(runtime);
|
|
16
|
+
return system;
|
|
8
17
|
}
|