summaryrefslogtreecommitdiff
path: root/objects.js
diff options
context:
space:
mode:
Diffstat (limited to 'objects.js')
-rw-r--r--objects.js354
1 files changed, 339 insertions, 15 deletions
diff --git a/objects.js b/objects.js
index 52292ca..39aa567 100644
--- a/objects.js
+++ b/objects.js
@@ -415,6 +415,11 @@ SpriteMorph.prototype.initBlocks = function () {
spec: 'go back %n layers',
defaults: [1]
},
+ doNotify: {
+ type: 'command',
+ category: 'looks',
+ spec: 'notification with title %s and content %s'
+ },
doScreenshot: {
type: 'command',
category: 'looks',
@@ -459,6 +464,23 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sound',
spec: 'stop all sounds'
},
+ doSetVolume: {
+ type: 'command',
+ category: 'sound',
+ spec: 'set volume to %n %',
+ defaults: [100]
+ },
+ doChangeVolume: {
+ type: 'command',
+ category: 'sound',
+ spec: 'change volume by %n',
+ defaults: [-10]
+ },
+ reportVolume: {
+ type: 'reporter',
+ category: 'sound',
+ spec: 'volume'
+ },
doRest: {
type: 'command',
category: 'sound',
@@ -569,6 +591,16 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'pen',
spec: 'stamp'
},
+ doStreamCamera: {
+ type: 'command',
+ category: 'pen',
+ spec: 'start streaming from the camera'
+ },
+ doStopCamera: {
+ type: 'command',
+ category: 'pen',
+ spec: 'stop streaming from the camera'
+ },
// Control
receiveGo: {
@@ -786,6 +818,23 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sensing',
spec: 'color %clr is touching %clr ?'
},
+ reportCameraMotion: {
+ only: SpriteMorph,
+ type: 'predicate',
+ category: 'sensing',
+ spec: 'camera motion at my position?'
+ },
+ reportCameraDirection: {
+ only: SpriteMorph,
+ type: 'reporter',
+ category: 'sensing',
+ spec: 'camera motion direction'
+ },
+ reportStreamingCamera: {
+ type: 'predicate',
+ category: 'sensing',
+ spec: 'streaming from the camera?'
+ },
colorFiltered: {
dev: true,
type: 'reporter',
@@ -847,6 +896,11 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sensing',
spec: 'key %key pressed?'
},
+ getKeysPressed: {
+ type: 'reporter',
+ category: 'sensing',
+ spec: 'keys pressed'
+ },
reportDistanceTo: {
type: 'reporter',
category: 'sensing',
@@ -895,6 +949,31 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sensing',
spec: 'current %dates'
},
+ doVibrate: {
+ type: 'command',
+ category: 'sensing',
+ spec: 'vibrate %n seconds'
+ },
+ reportCompassHeading: {
+ type: 'reporter',
+ category: 'sensing',
+ spec: 'current compass heading'
+ },
+ reportAccelerationX: {
+ type: 'reporter',
+ category: 'sensing',
+ spec: 'current acceleration along the x axes'
+ },
+ reportAccelerationY: {
+ type: 'reporter',
+ category: 'sensing',
+ spec: 'current acceleration along the y axes'
+ },
+ reportAccelerationZ: {
+ type: 'reporter',
+ category: 'sensing',
+ spec: 'current acceleration along the z axes'
+ },
// Operators
reifyScript: {
@@ -1183,6 +1262,29 @@ SpriteMorph.prototype.initBlocks = function () {
defaults: [localize('each item')]
},
+ // peer to peer communication
+ receivePeerMessage: {
+ type: 'hat',
+ category: 'other',
+ spec: 'when I receive %upvar from %upvar',
+ defaults: [localize('message'), localize('peer')]
+ },
+ sendPeerMessage: {
+ type: 'command',
+ category: 'other',
+ spec: 'send %s to %s'
+ },
+ reportPeerId: {
+ type: 'reporter',
+ category: 'other',
+ spec: 'my peer id'
+ },
+ reportPeerList: {
+ type: 'reporter',
+ category: 'other',
+ spec: 'peers online'
+ },
+
// Code mapping - experimental
doMapCodeOrHeader: { // experimental
type: 'command',
@@ -1329,6 +1431,8 @@ SpriteMorph.prototype.init = function (globals) {
this.version = Date.now(); // for observer optimization
this.isClone = false; // indicate a "temporary" Scratch-style clone
this.cloneOriginName = '';
+ this.volume = 100;
+ this.activeSounds = [];
// sprite nesting properties
this.parts = []; // not serialized, only anchor (name)
@@ -1434,8 +1538,11 @@ SpriteMorph.prototype.appearIn = function (ide) {
// SpriteMorph versioning
SpriteMorph.prototype.setName = function (string) {
- this.name = string || this.name;
- this.version = Date.now();
+ if (string != 'mouse-pointer' && string != 'pen trails'
+ && string != 'edge') { // used by system
+ this.name = string || this.name;
+ this.version = Date.now();
+ }
};
// SpriteMorph rendering
@@ -1787,6 +1894,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
blocks.push(block('comeToFront'));
blocks.push(block('goBack'));
+ blocks.push('-');
+ blocks.push(block('doNotify'));
// for debugging: ///////////////
@@ -1815,6 +1924,11 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doPlaySoundUntilDone'));
blocks.push(block('doStopAllSounds'));
blocks.push('-');
+ blocks.push(block('doSetVolume'));
+ blocks.push(block('doChangeVolume'));
+ blocks.push(watcherToggle('reportVolume'));
+ blocks.push(block('reportVolume'));
+ blocks.push('-');
blocks.push(block('doRest'));
blocks.push('-');
blocks.push(block('doPlayNote'));
@@ -1918,6 +2032,10 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('reportTouchingColor'));
blocks.push(block('reportColorIsTouchingColor'));
blocks.push('-');
+ blocks.push(block('reportCameraMotion'));
+ blocks.push(block('reportCameraDirection'));
+ blocks.push(block('reportStreamingCamera'));
+ blocks.push('-');
blocks.push(block('doAsk'));
blocks.push(watcherToggle('getLastAnswer'));
blocks.push(block('getLastAnswer'));
@@ -1929,6 +2047,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('reportMouseDown'));
blocks.push('-');
blocks.push(block('reportKeyPressed'));
+ blocks.push(block('getKeysPressed'));
blocks.push('-');
blocks.push(block('reportDistanceTo'));
blocks.push('-');
@@ -1944,6 +2063,14 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doSetFastTracking'));
blocks.push('-');
blocks.push(block('reportDate'));
+ blocks.push('-');
+ blocks.push(block('doVibrate'));
+ blocks.push('-');
+ blocks.push(block('reportCompassHeading'));
+ blocks.push('-');
+ blocks.push(block('reportAccelerationX'));
+ blocks.push(block('reportAccelerationY'));
+ blocks.push(block('reportAccelerationZ'));
// for debugging: ///////////////
@@ -2126,6 +2253,13 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push('=');
+ blocks.push(block('receivePeerMessage'));
+ blocks.push(block('sendPeerMessage'));
+ blocks.push(block('reportPeerId'));
+ blocks.push(block('reportPeerList'));
+
+ blocks.push('=');
+
if (StageMorph.prototype.enableCodeMapping) {
blocks.push(block('doMapCodeOrHeader'));
blocks.push(block('doMapStringCode'));
@@ -2669,7 +2803,8 @@ SpriteMorph.prototype.reportCostumes = function () {
// SpriteMorph sound management
SpriteMorph.prototype.addSound = function (audio, name) {
- this.sounds.add(new Sound(audio, name));
+ var volume = this.volume;
+ this.sounds.add(new Sound(audio, name, volume));
};
SpriteMorph.prototype.playSound = function (name) {
@@ -2680,7 +2815,18 @@ SpriteMorph.prototype.playSound = function (name) {
),
active;
if (sound) {
+ sound.volume = this.volume;
active = sound.play();
+
+ if (stage.muted === true) {
+ active.volume = 0;
+ }
+
+ this.activeSounds.push(active);
+ this.activeSounds = this.activeSounds.filter(function (aud) {
+ return !aud.ended && !aud.terminated;
+ });
+
if (stage) {
stage.activeSounds.push(active);
stage.activeSounds = stage.activeSounds.filter(function (aud) {
@@ -2691,6 +2837,34 @@ SpriteMorph.prototype.playSound = function (name) {
}
};
+SpriteMorph.prototype.doSetVolume = function (val) {
+ var myself = this;
+ myself.volume = Math.min(Math.max(0, val), 100);
+
+ if (myself.parentThatIsA(StageMorph).muted === true) {
+ return;
+ }
+
+ myself.activeSounds.forEach(function (snd) {
+ snd.volume = myself.volume / 100; // 'audio' objects
+ });
+};
+
+SpriteMorph.prototype.doChangeVolume = function (val) {
+ this.doSetVolume(this.volume + val);
+};
+
+SpriteMorph.prototype.reportVolume = function () {
+ return this.volume;
+}
+
+SpriteMorph.prototype.unmuteAllSounds = function () {
+ var stage = this.parentThatIsA(StageMorph);
+ stage.muted = false;
+
+ this.doSetVolume(this.volume);
+};
+
SpriteMorph.prototype.reportSounds = function () {
return this.sounds;
};
@@ -3343,9 +3517,7 @@ Morph.prototype.setPosition = function (aPoint, justMe) {
// override the inherited default to make sure my parts follow
// unless it's justMe
var delta = aPoint.subtract(this.topLeft());
- if ((delta.x !== 0) || (delta.y !== 0)) {
- this.moveBy(delta, justMe);
- }
+ this.moveBy(delta, justMe);
};
SpriteMorph.prototype.forward = function (steps) {
@@ -3566,6 +3738,9 @@ SpriteMorph.prototype.allHatBlocksFor = function (message) {
if (morph.selector === 'receiveOnClone') {
return message === '__clone__init__';
}
+ if (morph.selector === 'receivePeerMessage') {
+ return message === '__peer__message__';
+ }
}
return false;
});
@@ -3575,7 +3750,19 @@ SpriteMorph.prototype.allHatBlocksForKey = function (key) {
return this.scripts.children.filter(function (morph) {
if (morph.selector) {
if (morph.selector === 'receiveKey') {
- return morph.inputs()[0].evaluate()[0] === key;
+ var selectedOption = morph.inputs()[0].evaluate()[0];
+
+ if (selectedOption === 'any key') {
+ return true;
+ }
+ if (selectedOption === 'number key' &&
+ (key >= '0' && key <= '9')) {
+ return true;
+ }
+ if (selectedOption === key) {
+ return true;
+ }
+ return false;
}
}
return false;
@@ -3644,6 +3831,16 @@ SpriteMorph.prototype.getTempo = function () {
return 0;
};
+// SpriteMorph last key
+
+SpriteMorph.prototype.getKeysPressed = function () {
+ var stage = this.parentThatIsA(StageMorph);
+ if (stage) {
+ return stage.getKeysPressed();
+ }
+ return '';
+};
+
// SpriteMorph last message
SpriteMorph.prototype.getLastMessage = function () {
@@ -4566,6 +4763,8 @@ StageMorph.prototype.init = function (globals) {
this.version = Date.now(); // for observers
this.isFastTracked = false;
this.cloneCount = 0;
+ this.volume = 100;
+ this.muted = false;
this.timerStart = Date.now();
this.tempo = 60; // bpm
@@ -4581,6 +4780,11 @@ StageMorph.prototype.init = function (globals) {
this.paletteCache = {}; // not to be serialized (!)
this.lastAnswer = ''; // last user input, do not persist
this.activeSounds = []; // do not persist
+ this.acceleration = null; // do not persist
+ this.compassHeading = null; // do not persist
+ this.streamingCamera = false;
+ this.lastCameraCanvas = null;
+ this.lastCameraMotion = new Point(0, 0);
this.trailsCanvas = null;
this.isThreadSafe = false;
@@ -4604,6 +4808,51 @@ StageMorph.prototype.init = function (globals) {
this.fps = this.frameRate;
};
+StageMorph.prototype.newPeerMessage = function (data, peer) {
+ var ide = this.parentThatIsA(IDE_Morph);
+ if (!ide || !peer) return;
+ var myself = this;
+ var hats = [], model, message;
+
+ try {
+ model = ide.serializer.parse(data);
+ message = ide.serializer.loadValue(model);
+
+ // TODO: If a Context is sent, a new Sprite appears.
+ // This below is just a workaround for one-level rings,
+ // objects should be cleaned recursively.
+ message.receiver = null;
+ message.outerContext = null;
+ } catch (err) {
+ console.log(err); // DEBUG
+ // Ok, it does not seem to be XML. It must be a string then.
+ message = data;
+ }
+
+ // call hat blocks
+ this.children.concat(myself).forEach(function (morph) {
+ if (morph instanceof SpriteMorph
+ || morph instanceof StageMorph) {
+ hats = hats.concat(
+ morph.allHatBlocksFor('__peer__message__'));
+ }
+ });
+ hats.forEach(function (block) {
+ var process = myself.threads.startProcess(block,
+ myself.isThreadSafe);
+ process.context.outerContext.variables.addVar(localize('message'));
+ process.context.outerContext.variables.setVar(
+ localize('message'),
+ message
+ );
+ process.context.outerContext.variables.addVar(localize('peer'));
+ process.context.outerContext.variables.setVar(
+ localize('peer'),
+ peer
+ );
+ });
+};
+
// StageMorph scaling
StageMorph.prototype.setScale = function (number) {
@@ -4849,6 +5098,16 @@ StageMorph.prototype.getTempo = function () {
return +this.tempo;
};
+// StageMorph keys
+
+StageMorph.prototype.getKeysPressed = function () {
+ var keys = [];
+ for (var key in this.keysPressed) {
+ keys.push(key);
+ }
+ return new List(keys);
+};
+
// StageMorph messages
StageMorph.prototype.getLastMessage = function () {
@@ -4902,7 +5161,7 @@ StageMorph.prototype.step = function () {
world.keyboardReceiver = this;
}
if (world.currentKey === null) {
- this.keyPressed = null;
+ this.keysPressed = {};
}
// manage threads
@@ -5090,7 +5349,7 @@ StageMorph.prototype.fireGreenFlagEvent = function () {
StageMorph.prototype.fireStopAllEvent = function () {
var ide = this.parentThatIsA(IDE_Morph);
- this.threads.resumeAll(this.stage);
+ //this.threads.resumeAll(this.stage); // leads to a strange Note bug
this.keysPressed = {};
this.threads.stopAll();
this.stopAllActiveSounds();
@@ -5216,6 +5475,8 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
blocks.push(block('show'));
blocks.push(block('hide'));
+ blocks.push('-');
+ blocks.push(block('doNotify'));
// for debugging: ///////////////
@@ -5244,6 +5505,11 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doPlaySoundUntilDone'));
blocks.push(block('doStopAllSounds'));
blocks.push('-');
+ blocks.push(block('doSetVolume'));
+ blocks.push(block('doChangeVolume'));
+ blocks.push(watcherToggle('reportVolume'));
+ blocks.push(block('reportVolume'));
+ blocks.push('-');
blocks.push(block('doRest'));
blocks.push('-');
blocks.push(block('doPlayNote'));
@@ -5270,6 +5536,9 @@ StageMorph.prototype.blockTemplates = function (category) {
} else if (cat === 'pen') {
blocks.push(block('clear'));
+ blocks.push('-');
+ blocks.push(block('doStreamCamera'));
+ blocks.push(block('doStopCamera'));
} else if (cat === 'control') {
@@ -5326,6 +5595,8 @@ StageMorph.prototype.blockTemplates = function (category) {
} else if (cat === 'sensing') {
+ blocks.push(block('reportStreamingCamera'));
+ blocks.push('-');
blocks.push(block('doAsk'));
blocks.push(watcherToggle('getLastAnswer'));
blocks.push(block('getLastAnswer'));
@@ -5337,6 +5608,7 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('reportMouseDown'));
blocks.push('-');
blocks.push(block('reportKeyPressed'));
+ blocks.push(block('getKeysPressed'));
blocks.push('-');
blocks.push(block('doResetTimer'));
blocks.push(watcherToggle('getTimer'));
@@ -5350,6 +5622,14 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doSetFastTracking'));
blocks.push('-');
blocks.push(block('reportDate'));
+ blocks.push('-');
+ blocks.push(block('doVibrate'));
+ blocks.push('-');
+ blocks.push(block('reportCompassHeading'));
+ blocks.push('-');
+ blocks.push(block('reportAccelerationX'));
+ blocks.push(block('reportAccelerationY'));
+ blocks.push(block('reportAccelerationZ'));
// for debugging: ///////////////
@@ -5519,6 +5799,12 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push('=');
+ blocks.push(block('receivePeerMessage'));
+ blocks.push(block('sendPeerMessage'));
+ blocks.push(block('reportPeerId'));
+ blocks.push(block('reportPeerList'));
+ blocks.push('=');
+
if (StageMorph.prototype.enableCodeMapping) {
blocks.push(block('doMapCodeOrHeader'));
blocks.push(block('doMapStringCode'));
@@ -5777,6 +6063,15 @@ StageMorph.prototype.addSound
StageMorph.prototype.playSound
= SpriteMorph.prototype.playSound;
+StageMorph.prototype.doSetVolume
+ = SpriteMorph.prototype.doSetVolume;
+
+StageMorph.prototype.doChangeVolume
+ = SpriteMorph.prototype.doChangeVolume;
+
+StageMorph.prototype.reportVolume
+ = SpriteMorph.prototype.reportVolume;
+
StageMorph.prototype.stopAllActiveSounds = function () {
this.activeSounds.forEach(function (audio) {
audio.pause();
@@ -5791,11 +6086,29 @@ StageMorph.prototype.pauseAllActiveSounds = function () {
};
StageMorph.prototype.resumeAllActiveSounds = function () {
+ var newSounds = []; // remove Sounds that have been played so they do not resume
+
this.activeSounds.forEach(function (audio) {
- audio.play();
+ if (audio.ended === false) {
+ newSounds.push(audio);
+ audio.play();
+ }
});
+
+ this.activeSounds = newSounds;
};
+StageMorph.prototype.muteAllSounds = function () {
+ this.muted = true;
+
+ this.activeSounds.forEach(function (audio) {
+ audio.volume = 0;
+ });
+};
+
+StageMorph.prototype.unmuteAllSounds
+ = SpriteMorph.prototype.unmuteAllSounds;
+
StageMorph.prototype.reportSounds
= SpriteMorph.prototype.reportSounds;
@@ -6577,9 +6890,10 @@ CostumeEditorMorph.prototype.mouseMove
// Sound instance creation
-function Sound(audio, name) {
+function Sound(audio, name, volume) {
this.audio = audio; // mandatory
this.name = name || "Sound";
+ this.volume = volume || 100;
}
Sound.prototype.play = function () {
@@ -6587,6 +6901,7 @@ Sound.prototype.play = function () {
// externally (i.e. by the stage)
var aud = document.createElement('audio');
aud.src = this.audio.src;
+ aud.volume = Math.min(Math.max(0, this.volume), 100) / 100;
aud.play();
return aud;
};
@@ -6596,7 +6911,8 @@ Sound.prototype.copy = function () {
cpy;
snd.src = this.audio.src;
- cpy = new Sound(snd, this.name ? copy(this.name) : null);
+ snd.volume = this.volume;
+ cpy = new Sound(snd, this.name ? copy(this.name) : null, this.volume ? copy(this.volume) : null);
return cpy;
};
@@ -6610,8 +6926,9 @@ Sound.prototype.toDataURL = function () {
// Note instance creation
-function Note(pitch) {
+function Note(pitch, volume) {
this.pitch = pitch === 0 ? 0 : pitch || 69;
+ this.volume = volume;
this.setupContext();
this.oscillator = null;
}
@@ -6642,12 +6959,13 @@ Note.prototype.setupContext = function () {
}
Note.prototype.audioContext = new AudioContext();
Note.prototype.gainNode = Note.prototype.audioContext.createGain();
- Note.prototype.gainNode.gain.value = 0.25; // reduce volume by 1/4
};
// Note playing
Note.prototype.play = function () {
+ this.gainNode.gain.value = 0.25 * this.volume / 100; // reduce volume by 1/4
+
this.oscillator = this.audioContext.createOscillator();
if (!this.oscillator.start) {
this.oscillator.start = this.oscillator.noteOn;
@@ -6663,6 +6981,12 @@ Note.prototype.play = function () {
this.oscillator.start(0);
};
+Note.prototype.setVolume = function (volume) {
+ this.stop();
+ this.volume = volume;
+ this.play();
+}
+
Note.prototype.stop = function () {
if (this.oscillator) {
this.oscillator.stop(0);
@@ -7091,7 +7415,7 @@ WatcherMorph.prototype.object = function () {
WatcherMorph.prototype.isGlobal = function (selector) {
return contains(
- ['getLastAnswer', 'getLastMessage', 'getTempo', 'getTimer',
+ ['getLastAnswer', 'getKeysPressed', 'getLastMessage', 'getTempo', 'getTimer',
'reportMouseX', 'reportMouseY', 'reportThreadCount'],
selector
);