Merge branch 'LLK/master'
This commit is contained in:
commit
ba40cd3396
24 changed files with 764 additions and 698 deletions
|
@ -1,9 +1,16 @@
|
|||
Scratch HTML5 Player
|
||||
# Scratch HTML5 Player
|
||||
|
||||
This project aims to create a Scratch Player in HTML5. Scratch is currently implemented with Actionscript 3 and requires the Flash Player version 10.2. Since Flash does not run on iOS (iPads, iPods, etc) and newer Android devices, we would like to have an HTML5 version to display (but not edit) projects on mobile devices. Scratch projects played in the HTML5 player should look and behave as closely as possible to the way they look and behave when played by the Flash player. We will not be accepting pull requests for new features that don't already exist in the Flash based Scratch project player.
|
||||
|
||||
There are a few github issues created that represent some of the missing features. At this point, the HTML5 player is about 40% complete and can run some simple projects. Running the HTML5 player on your own website, or locally, you will need to have PHP so that the proxy.php file can be used to load assets from the same domain. This is done to be compatible with Javascript security models in today's browsers. To test the HTML5 player against the Flash player you can use the compare.html web page.
|
||||
There are a few github issues created that represent some of the missing features. At this point, the HTML5 player is about 40% complete and can run some simple projects.
|
||||
|
||||
Unimplementable Features on iOS: Image effects for whirl, fisheye, mosaic, and pixelate. Sound and video input for loudness, video motion, and touching colors from the video.
|
||||
|
||||
More documentation will be added as time permits. Thanks for contributing, and Scratch On!
|
||||
|
||||
## Installation
|
||||
|
||||
Running the HTML5 player on your own website, or locally, you will need to have
|
||||
PHP so that the `proxy.php` file can be used to load assets from the same domain. This is done to be compatible with Javascript security models in today's browsers. To test the HTML5 player against the Flash player you can use the compare.html web page.
|
||||
|
||||
See the file `TESTING.md` for more details.
|
45
TESTING.md
Normal file
45
TESTING.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
Running the HTML5 player on your own website, or locally, you will need to have
|
||||
PHP so that the `proxy.php` file can be used to load assets from the same domain. This is done to be compatible with Javascript security models in today's browsers. To test the HTML5 player against the Flash player you can use the compare.html web page.
|
||||
|
||||
# Ubuntu
|
||||
|
||||
If you're using Ubuntu, you can follow the following steps to set up the proxy correctly. You'll need to type the following commands in Terminal.
|
||||
|
||||
Install PHP and Apache for running the proxy file:
|
||||
|
||||
$ sudo apt-get install apache2 php5
|
||||
|
||||
Fork this repository on Github, and then clone it into your home folder somewhere (replacing `<username>` with your Github username):
|
||||
|
||||
$ git clone https://github.com/<username>/scratch-html5
|
||||
|
||||
We'd like to add a new localhost domain, so that we can access the player from our web browser. Something like `scratch.localhost`. Run this command to edit the `/etc/hosts` file:
|
||||
|
||||
$ sudo nano /etc/hosts
|
||||
|
||||
Add the following line:
|
||||
|
||||
127.0.0.1 scratch.localhost
|
||||
|
||||
Use <key>Ctrl-O</key>, <key>Return</key>, <key>Ctrl-X</key> to quit.
|
||||
|
||||
Now we want to add a new Apache configuration for the new domain. Run the following:
|
||||
|
||||
$ cd /etc/apache2/sites-available/
|
||||
$ sudo nano scratch
|
||||
|
||||
And type in the following, replacing `user` with your username, and making sure the `DocumentRoot` matches where you cloned the repository earlier:
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName scratch.localhost
|
||||
DocumentRoot /home/user/scratch-html5
|
||||
</VirtualHost>
|
||||
|
||||
Finally, run the following commands to enable the site:
|
||||
|
||||
$ sudo a2ensite scratch
|
||||
$ sudo service apache2 reload
|
||||
|
||||
Now when you go to <http://scratch.localhost/>, the project should play. If you get a "Forbidden" error message, you may need to check the permissions of the folders that the player code is in.
|
||||
|
||||
Now you can go fix bugs in the HTML5 player!
|
201
compare.html
201
compare.html
|
@ -4,112 +4,113 @@ header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
|
|||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Scratch HTML5 vs. Flash</title>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: #222;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<link href="player.css" type="text/css" rel="stylesheet" />
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
|
||||
<script src="js/util/Timer.js"></script>
|
||||
<script src="js/util/OffsetBuffer.js"></script>
|
||||
<script src="js/util/Color.js"></script>
|
||||
<script src="js/util/Rectangle.js"></script>
|
||||
<script src="js/Sprite.js"></script>
|
||||
<script src="js/Reporter.js"></script>
|
||||
<script src="js/Stage.js"></script>
|
||||
<script src="js/sound/WAVFile.js"></script>
|
||||
<script src="js/sound/SoundDecoder.js"></script>
|
||||
<script src="js/sound/SoundBank.js"></script>
|
||||
<script src="js/sound/NotePlayer.js"></script>
|
||||
<script src="soundbank/Instr.js"></script>
|
||||
<script src="js/IO.js"></script>
|
||||
<script src="js/primitives/VarListPrims.js"></script>
|
||||
<script src="js/primitives/MotionAndPenPrims.js"></script>
|
||||
<script src="js/primitives/LooksPrims.js"></script>
|
||||
<script src="js/primitives/SensingPrims.js"></script>
|
||||
<script src="js/primitives/SoundPrims.js"></script>
|
||||
<script src="js/primitives/Primitives.js"></script>
|
||||
<script src="js/Interpreter.js"></script>
|
||||
<script src="js/Runtime.js"></script>
|
||||
<script src="js/Scratch.js"></script>
|
||||
<script type="text/javascript">
|
||||
if (window.location.hash) {
|
||||
var project_id = parseInt(window.location.hash.substr(1));
|
||||
} else {
|
||||
var project_id = 10000160; // Default project for display
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
$(function() {
|
||||
// The flashvars tell flash about the project data (and autostart=true)
|
||||
var flashvars = {
|
||||
server: encodeURIComponent('scratch.mit.edu'),
|
||||
project_id: project_id
|
||||
};
|
||||
<head>
|
||||
<title>Scratch HTML5 vs. Flash</title>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: #222;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<link href="player.css" type="text/css" rel="stylesheet" />
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
|
||||
<script src="js/util/Timer.js"></script>
|
||||
<script src="js/util/OffsetBuffer.js"></script>
|
||||
<script src="js/util/Color.js"></script>
|
||||
<script src="js/util/Rectangle.js"></script>
|
||||
<script src="js/Sprite.js"></script>
|
||||
<script src="js/Reporter.js"></script>
|
||||
<script src="js/Stage.js"></script>
|
||||
<script src="js/sound/WAVFile.js"></script>
|
||||
<script src="js/sound/SoundDecoder.js"></script>
|
||||
<script src="js/sound/SoundBank.js"></script>
|
||||
<script src="js/sound/NotePlayer.js"></script>
|
||||
<script src="soundbank/Instr.js"></script>
|
||||
<script src="js/IO.js"></script>
|
||||
<script src="js/primitives/VarListPrims.js"></script>
|
||||
<script src="js/primitives/MotionAndPenPrims.js"></script>
|
||||
<script src="js/primitives/LooksPrims.js"></script>
|
||||
<script src="js/primitives/SensingPrims.js"></script>
|
||||
<script src="js/primitives/SoundPrims.js"></script>
|
||||
<script src="js/primitives/Primitives.js"></script>
|
||||
<script src="js/Interpreter.js"></script>
|
||||
<script src="js/Runtime.js"></script>
|
||||
<script src="js/Scratch.js"></script>
|
||||
<script type="text/javascript">
|
||||
if (window.location.hash) {
|
||||
var project_id = parseInt(window.location.hash.substr(1));
|
||||
} else {
|
||||
var project_id = 10000160; // Default project for display
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
$(function() {
|
||||
// The flashvars tell flash about the project data (and autostart=true)
|
||||
var flashvars = {
|
||||
server: encodeURIComponent('scratch.mit.edu'),
|
||||
project_id: project_id
|
||||
};
|
||||
|
||||
// Pass in the cloud token for the project
|
||||
if(window.getCloudToken)
|
||||
flashvars.cloud_token = encodeURIComponent(getCloudToken());
|
||||
// Pass in the cloud token for the project
|
||||
if (window.getCloudToken) {
|
||||
flashvars.cloud_token = encodeURIComponent(getCloudToken());
|
||||
}
|
||||
|
||||
var params = {
|
||||
allowscriptaccess: 'always',
|
||||
allowfullscreen: 'false',
|
||||
wmode: 'direct',
|
||||
menu: 'false'};
|
||||
var params = {
|
||||
allowscriptaccess: 'always',
|
||||
allowfullscreen: 'false',
|
||||
wmode: 'direct',
|
||||
menu: 'false'
|
||||
};
|
||||
|
||||
var flashPlayer = null;
|
||||
//var swf_url = "http://cdn.scratch.mit.edu/scratchr2/static/Scratch.swf";
|
||||
var swf_url = "http://jiggler.media.mit.edu/shanemc/scratchr2/static/Scratch.swf";
|
||||
swfobject.embedSWF(swf_url, "flashScratch", "480", "400", "10.2.0",
|
||||
"http://cdn.scratch.mit.edu/scratchr2/static/expressInstall.swf",
|
||||
flashvars, params, null, function(e) {
|
||||
$('#flashScratch').css('visibility', 'visible');
|
||||
if(e.success) flashPlayer = e.ref;
|
||||
var flashPlayer = null;
|
||||
//var swf_url = "http://cdn.scratch.mit.edu/scratchr2/static/Scratch.swf";
|
||||
var swf_url = "http://jiggler.media.mit.edu/shanemc/scratchr2/static/Scratch.swf";
|
||||
swfobject.embedSWF(swf_url, "flashScratch", "480", "400", "10.2.0",
|
||||
"http://cdn.scratch.mit.edu/scratchr2/static/expressInstall.swf",
|
||||
flashvars, params, null, function(e) {
|
||||
$('#flashScratch').css('visibility', 'visible');
|
||||
if (e.success) flashPlayer = e.ref;
|
||||
});
|
||||
|
||||
$('#trigger_green_flag, #greenSlide').click(function() {
|
||||
flashPlayer.ASstartRunning();
|
||||
});
|
||||
|
||||
// Stop button behavior
|
||||
$('#trigger_stop').click(function() {
|
||||
flashPlayer.ASstopRunning();
|
||||
});
|
||||
});
|
||||
|
||||
$('#trigger_green_flag, #greenSlide').click(function() {
|
||||
flashPlayer.ASstartRunning();
|
||||
});
|
||||
|
||||
// Stop button behavior
|
||||
$('#trigger_stop').click(function() {
|
||||
flashPlayer.ASstopRunning();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="display: inline-block;">
|
||||
<div id="up"> </div>
|
||||
<div id="left"> </div>
|
||||
<div id="overContainer">
|
||||
<div id="greenSlide"><div id="greenSlideFg"><img src="img/greenflag.png"></div></div>
|
||||
<div id="container"></div>
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="display: inline-block;">
|
||||
<div id="up"> </div>
|
||||
<div id="left"> </div>
|
||||
<div id="overContainer">
|
||||
<div id="greenSlide"><div id="greenSlideFg"><img src="img/greenflag.png"></div></div>
|
||||
<div id="container"></div>
|
||||
</div>
|
||||
<div id="right"> </div>
|
||||
<div id="down"> </div>
|
||||
</div>
|
||||
<div id="right"> </div>
|
||||
<div id="down"> </div>
|
||||
</div>
|
||||
<div style="display: inline-block;">
|
||||
<div id="flashScratch" style="text-align:center;visibility:hidden;">
|
||||
<p style="color:#aaa;font-size:22px;margin-top:14px;line-height:28px;">Oh Noes! Scratch project cannot display.<br/>Flash player is disabled, missing, or less than version 10.2.</p>
|
||||
<a href="http://www.adobe.com/go/getflashplayer">
|
||||
<img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" target="_blank" />
|
||||
</a>
|
||||
<div style="display: inline-block;">
|
||||
<div id="flashScratch" style="text-align:center;visibility:hidden;">
|
||||
<p style="color:#aaa;font-size:22px;margin-top:14px;line-height:28px;">Oh Noes! Scratch project cannot display.<br/>Flash player is disabled, missing, or less than version 10.2.</p>
|
||||
<a href="http://www.adobe.com/go/getflashplayer">
|
||||
<img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" target="_blank" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="info">Loading...</div>
|
||||
<div id="info">Loading...</div>
|
||||
|
||||
<button id="trigger_green_flag">Green flag</button>
|
||||
<button id="trigger_stop">Stop</button>
|
||||
<button id="trigger_green_flag">Green flag</button>
|
||||
<button id="trigger_stop">Stop</button>
|
||||
|
||||
<input type="text" name="project_id" id="project_id" /><button id='go_project'>Go!</button>
|
||||
</body>
|
||||
<input type="text" name="project_id" id="project_id" /><button id='go_project'>Go!</button>
|
||||
</body>
|
||||
</html>
|
||||
|
|
45
js/IO.js
45
js/IO.js
|
@ -27,26 +27,24 @@ var IO = function() {
|
|||
// In production, simply use the local path (no proxy)
|
||||
// since we won't be hampered by the same-origin policy.
|
||||
this.base = 'proxy.php?resource=internalapi/';
|
||||
this.project_base = this.base + 'project/';
|
||||
this.project_base = this.base + 'project/';
|
||||
this.project_suffix = '/get/';
|
||||
this.asset_base = this.base + 'asset/';
|
||||
this.asset_suffix = '/get/';
|
||||
this.soundbank_base = 'soundbank/';
|
||||
this.spriteLayerCount = 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
IO.prototype.loadProject = function(project_id) {
|
||||
var runningIO = this;
|
||||
$.getJSON(this.project_base + project_id + this.project_suffix,
|
||||
function(data) {
|
||||
runningIO.data = data;
|
||||
runningIO.makeObjects();
|
||||
runningIO.loadThreads();
|
||||
runningIO.loadNotesDrums();
|
||||
runtime.loadStart(); // Try to run the project.
|
||||
}
|
||||
);
|
||||
}
|
||||
$.getJSON(this.project_base + project_id + this.project_suffix, function(data) {
|
||||
runningIO.data = data;
|
||||
runningIO.makeObjects();
|
||||
runningIO.loadThreads();
|
||||
runningIO.loadNotesDrums();
|
||||
runtime.loadStart(); // Try to run the project.
|
||||
});
|
||||
};
|
||||
|
||||
IO.prototype.soundRequest = function(sound, sprite) {
|
||||
var request = new XMLHttpRequest();
|
||||
|
@ -63,9 +61,9 @@ IO.prototype.soundRequest = function(sound, sprite) {
|
|||
data[i] = samples[i];
|
||||
}
|
||||
sprite.soundsLoaded++;
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
}
|
||||
};
|
||||
|
||||
IO.prototype.loadNotesDrums = function() {
|
||||
var self = this;
|
||||
|
@ -81,18 +79,17 @@ IO.prototype.loadNotesDrums = function() {
|
|||
var soundBuffer = waveData.readBytes(2 * info.sampleCount);
|
||||
Instr.samples[name] = soundBuffer;
|
||||
Instr.wavsLoaded++;
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
IO.prototype.makeObjects = function() {
|
||||
// Create the stage
|
||||
runtime.stage = new Stage(this.data);
|
||||
runtime.stage.attach(runtime.scene);
|
||||
runtime.stage.attachPenLayer(runtime.scene);
|
||||
runtime.stage.loadSounds();
|
||||
|
||||
// Create the sprites and watchers
|
||||
$.each(this.data.children.concat(this.data.lists), function i(index, obj) {
|
||||
var newSprite;
|
||||
|
@ -111,8 +108,8 @@ IO.prototype.makeObjects = function() {
|
|||
if (!obj.cmd && !obj.listName)
|
||||
newSprite.loadSounds();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
IO.prototype.loadThreads = function() {
|
||||
var target = runtime.stage;
|
||||
var scripts = target.data.scripts;
|
||||
|
@ -127,9 +124,9 @@ IO.prototype.loadThreads = function() {
|
|||
$.each(target.data.scripts, function(j, s) {
|
||||
target.stacks.push(interp.makeBlockList(s[2]));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Returns the number sprite we are rendering
|
||||
// used for initial layering assignment
|
||||
|
@ -137,4 +134,4 @@ IO.prototype.getCount = function() {
|
|||
var rv = this.spriteLayerCount;
|
||||
this.spriteLayerCount++;
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
var Block = function(opAndArgs, optionalSubstack) {
|
||||
this.op = opAndArgs[0];
|
||||
this.primFcn = interp.lookupPrim(this.op);
|
||||
this.primFcn = interp.lookupPrim(this.op);
|
||||
this.args = opAndArgs.slice(1); // arguments can be either or constants (numbers, boolean strings, etc.) or expressions (Blocks)
|
||||
this.isLoop = false; // set to true for loop blocks the first time they run
|
||||
this.substack = optionalSubstack;
|
||||
|
@ -30,7 +30,7 @@ var Block = function(opAndArgs, optionalSubstack) {
|
|||
this.nextBlock = null;
|
||||
this.tmp = -1;
|
||||
interp.fixArgs(this);
|
||||
}
|
||||
};
|
||||
|
||||
var Thread = function(block, target) {
|
||||
this.nextBlock = block; // next block to run; null when thread is finished
|
||||
|
@ -40,7 +40,7 @@ var Thread = function(block, target) {
|
|||
this.tmp = null; // used for thread operations like Timer
|
||||
this.tmpObj = []; // used for Sprite operations like glide
|
||||
this.firstTime = true;
|
||||
}
|
||||
};
|
||||
|
||||
var Interpreter = function() {
|
||||
// Interpreter state
|
||||
|
@ -54,10 +54,10 @@ var Interpreter = function() {
|
|||
this.yield = false;
|
||||
this.doRedraw = false;
|
||||
this.opCount = 0; // used to benchmark the interpreter
|
||||
}
|
||||
};
|
||||
|
||||
// Utilities for building blocks and sequences of blocks
|
||||
Interpreter.prototype.fixArgs = function(b) {
|
||||
Interpreter.prototype.fixArgs = function(b) {
|
||||
// Convert the arguments of the given block into blocks or substacks if necessary.
|
||||
// A block argument can be a constant (numbers, boolean strings, etc.), an expression (Blocks), or a substack (an array of blocks).
|
||||
var newArgs = [];
|
||||
|
@ -66,7 +66,7 @@ Interpreter.prototype.fixArgs = function(b) {
|
|||
if (arg && arg.constructor == Array) {
|
||||
if ((arg.length > 0) && (arg[0].constructor == Array)) {
|
||||
// if first element arg is itself an array, then arg is a substack
|
||||
if(!b.substack) {
|
||||
if (!b.substack) {
|
||||
b.substack = this.makeBlockList(arg);
|
||||
} else {
|
||||
b.substack2 = this.makeBlockList(arg);
|
||||
|
@ -80,7 +80,7 @@ Interpreter.prototype.fixArgs = function(b) {
|
|||
}
|
||||
}
|
||||
b.args = newArgs;
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.makeBlockList = function(blockList) {
|
||||
var firstBlock = null, lastBlock = null;
|
||||
|
@ -91,7 +91,7 @@ Interpreter.prototype.makeBlockList = function(blockList) {
|
|||
lastBlock = b;
|
||||
}
|
||||
return firstBlock;
|
||||
}
|
||||
};
|
||||
|
||||
// The Interpreter proper
|
||||
Interpreter.prototype.stepThreads = function() {
|
||||
|
@ -115,17 +115,17 @@ Interpreter.prototype.stepThreads = function() {
|
|||
if (this.threads[a].nextBlock != null) {
|
||||
newThreads.push(this.threads[a]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.threads = newThreads;
|
||||
if (this.threads.length == 0) return;
|
||||
}
|
||||
this.currentMSecs = this.timer.time();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.stepActiveThread = function() {
|
||||
// Run the active thread until it yields.
|
||||
if(typeof(this.activeThread) == 'undefined') {
|
||||
if (typeof(this.activeThread) == 'undefined') {
|
||||
return;
|
||||
}
|
||||
var b = this.activeThread.nextBlock;
|
||||
|
@ -141,7 +141,7 @@ Interpreter.prototype.stepActiveThread = function() {
|
|||
b = this.activeThread.nextBlock; // refresh local variable b in case primitive did some control flow
|
||||
while (b == null) {
|
||||
// end of a substack; pop the owning control flow block from stack
|
||||
// Note: This is a loop to handle nested control flow blocks.
|
||||
// Note: This is a loop to handle nested control flow blocks.
|
||||
b = this.activeThread.stack.pop();
|
||||
if ((b == null) || (b.isLoop)) {
|
||||
this.activeThread.nextBlock = b;
|
||||
|
@ -149,8 +149,8 @@ Interpreter.prototype.stepActiveThread = function() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Interpreter.prototype.toggleThread = function(b, targetObj) {
|
||||
var newThreads = [], wasRunning = false;
|
||||
for (var i = 0; i < this.threads.length; i++) {
|
||||
|
@ -161,16 +161,16 @@ Interpreter.prototype.toggleThread = function(b, targetObj) {
|
|||
}
|
||||
}
|
||||
this.threads = newThreads;
|
||||
if(!wasRunning) {
|
||||
if (!wasRunning) {
|
||||
this.startThread(b, targetObj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Interpreter.prototype.startThread = function(b, targetObj) {
|
||||
this.activeThread = new Thread(b, targetObj);
|
||||
this.threads.push(this.activeThread);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Interpreter.prototype.restartThread = function(b, targetObj) {
|
||||
// used by broadcast; stop any thread running on b, then start a new thread on b
|
||||
var newThread = new Thread(b, targetObj);
|
||||
|
@ -184,7 +184,7 @@ Interpreter.prototype.restartThread = function(b, targetObj) {
|
|||
if (!wasRunning) {
|
||||
this.threads.push(newThread);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.arg = function(block, index) {
|
||||
var arg = block.args[index];
|
||||
|
@ -193,11 +193,11 @@ Interpreter.prototype.arg = function(block, index) {
|
|||
return arg.primFcn(arg); // expression
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Interpreter.prototype.targetSprite = function() {
|
||||
return this.activeThread.target;
|
||||
}
|
||||
};
|
||||
|
||||
// Timer
|
||||
Interpreter.prototype.startTimer = function(secs) {
|
||||
|
@ -206,7 +206,7 @@ Interpreter.prototype.startTimer = function(secs) {
|
|||
this.activeThread.tmp = this.currentMSecs + waitMSecs; // end time in milliseconds
|
||||
this.activeThread.firstTime = false;
|
||||
this.yield = true;
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.checkTimer = function() {
|
||||
// check for timer expiration and clean up if expired. return true when expired
|
||||
|
@ -219,11 +219,11 @@ Interpreter.prototype.checkTimer = function() {
|
|||
this.yield = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Interpreter.prototype.redraw = function() {
|
||||
this.doRedraw = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Primitive operations
|
||||
Interpreter.prototype.initPrims = function() {
|
||||
|
@ -231,14 +231,14 @@ Interpreter.prototype.initPrims = function() {
|
|||
this.primitiveTable['whenGreenFlag'] = this.primNoop;
|
||||
this.primitiveTable['whenKeyPressed'] = this.primNoop;
|
||||
this.primitiveTable['whenClicked'] = this.primNoop;
|
||||
this.primitiveTable['if'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b) };
|
||||
this.primitiveTable['doForever'] = function(b) { interp.startSubstack(b, true) };
|
||||
this.primitiveTable['if'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b); };
|
||||
this.primitiveTable['doForever'] = function(b) { interp.startSubstack(b, true); };
|
||||
this.primitiveTable['doForeverIf'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b, true); else interp.yield = true; };
|
||||
this.primitiveTable['doIf'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b); };
|
||||
this.primitiveTable['doRepeat'] = this.primRepeat;
|
||||
this.primitiveTable['doIfElse'] = function(b) { if (interp.arg(b, 0)) interp.startSubstack(b); else interp.startSubstack(b, false, true); };
|
||||
this.primitiveTable['doWaitUntil'] = function(b) { if (!interp.arg(b, 0)) interp.yield = true };
|
||||
this.primitiveTable['doUntil'] = function(b) { if (!interp.arg(b, 0)) interp.startSubstack(b, true) };
|
||||
this.primitiveTable['doWaitUntil'] = function(b) { if (!interp.arg(b, 0)) interp.yield = true; };
|
||||
this.primitiveTable['doUntil'] = function(b) { if (!interp.arg(b, 0)) interp.startSubstack(b, true); };
|
||||
this.primitiveTable['doReturn'] = function(b) { interp.activeThread = new Thread(null); };
|
||||
this.primitiveTable['stopAll'] = function(b) { interp.activeThread = new Thread(null); interp.threads = []; }
|
||||
this.primitiveTable['whenIReceive'] = this.primNoop;
|
||||
|
@ -247,27 +247,27 @@ Interpreter.prototype.initPrims = function() {
|
|||
this.primitiveTable['wait:elapsed:from:'] = this.primWait;
|
||||
|
||||
// added by John:
|
||||
this.primitiveTable['showBubble'] = function(b) { console.log(interp.arg(b, 1)) }
|
||||
this.primitiveTable['timerReset'] = function(b) {interp.timerBase = (new Date()).getTime() }
|
||||
this.primitiveTable['timer'] = function(b) {return ((new Date()).getTime() - interp.timerBase) / 1000 }
|
||||
this.primitiveTable['showBubble'] = function(b) { console.log(interp.arg(b, 1)); };
|
||||
this.primitiveTable['timerReset'] = function(b) { interp.timerBase = Date.now(); };
|
||||
this.primitiveTable['timer'] = function(b) { return (Date.now() - interp.timerBase) / 1000; };
|
||||
|
||||
new Primitives().addPrimsTo(this.primitiveTable);
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.timerBase = (new Date()).getTime();
|
||||
Interpreter.prototype.timerBase = Date.now();
|
||||
Interpreter.prototype.lookupPrim = function(op) {
|
||||
var fcn = interp.primitiveTable[op];
|
||||
if (fcn == null) fcn = function(b) { console.log('not implemented: ' + b.op) }
|
||||
if (fcn == null) fcn = function(b) { console.log('not implemented: ' + b.op); };
|
||||
return fcn;
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.primNoop = function(b) { console.log(b.op); };
|
||||
|
||||
Interpreter.prototype.primNoop = function(b) { console.log(b.op); }
|
||||
|
||||
Interpreter.prototype.primWait = function(b) {
|
||||
if (interp.activeThread.firstTime) interp.startTimer(interp.arg(b, 0));
|
||||
else interp.checkTimer();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Interpreter.prototype.primRepeat = function(b) {
|
||||
if (b.tmp == -1) {
|
||||
b.tmp = Math.max(interp.arg(b, 0), 0); // Initialize repeat count on this block
|
||||
|
@ -280,14 +280,14 @@ Interpreter.prototype.primRepeat = function(b) {
|
|||
b.tmp = -1;
|
||||
b = null;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Interpreter.prototype.broadcast = function(b, waitFlag) {
|
||||
var pair;
|
||||
if (interp.activeThread.firstTime) {
|
||||
var receivers = [];
|
||||
var msg = String(interp.arg(b, 0)).toLowerCase();
|
||||
var findReceivers = function (stack, target) {
|
||||
var findReceivers = function(stack, target) {
|
||||
if ((stack.op == "whenIReceive") && (stack.args[0].toLowerCase() == msg)) {
|
||||
receivers.push([stack, target]);
|
||||
}
|
||||
|
@ -303,14 +303,14 @@ Interpreter.prototype.broadcast = function(b, waitFlag) {
|
|||
if (interp.isRunning(interp.activeThread.tmpObj[pair][0])) {
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
interp.activeThread.tmpObj = null;
|
||||
interp.activeThread.firstTime = true;
|
||||
} else {
|
||||
interp.yield = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.isRunning = function(b) {
|
||||
for (t in interp.threads) {
|
||||
|
@ -319,7 +319,7 @@ Interpreter.prototype.isRunning = function(b) {
|
|||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Interpreter.prototype.startSubstack = function(b, isLoop, secondSubstack) {
|
||||
// Start the substack of a control structure command such as if or forever.
|
||||
|
@ -327,9 +327,9 @@ Interpreter.prototype.startSubstack = function(b, isLoop, secondSubstack) {
|
|||
b.isLoop = true;
|
||||
this.activeThread.stack.push(b); // remember the block that started the substack
|
||||
}
|
||||
if(!secondSubstack) {
|
||||
if (!secondSubstack) {
|
||||
this.activeThread.nextBlock = b.substack;
|
||||
} else {
|
||||
this.activeThread.nextBlock = b.substack2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ var Reporter = function(data) {
|
|||
this.el = null; // jQuery Element for the outer box
|
||||
this.valueEl = null; // jQ element containing the reporter value
|
||||
this.slider = null; // slider jQ element
|
||||
}
|
||||
};
|
||||
|
||||
Reporter.prototype.attach = function(scene) {
|
||||
switch (this.mode) {
|
||||
|
@ -47,7 +47,7 @@ Reporter.prototype.attach = function(scene) {
|
|||
// Temporarily, set the value to sliderMin until an update
|
||||
this.slider = $('<input type="range" min="' + this.sliderMin +
|
||||
'" max="' + this.sliderMax + '" step="1" value="' +
|
||||
this.sliderMin + '" data-target="' + this.target +
|
||||
this.sliderMin + '" data-target="' + this.target +
|
||||
'" data-var="' + this.param + '">');
|
||||
this.slider.change(this.changeSlider);
|
||||
this.el.append('<br>');
|
||||
|
@ -68,13 +68,13 @@ Reporter.prototype.attach = function(scene) {
|
|||
this.valueEl.css('background-color', 'rgb(' + cR + ',' + cG + ',' + cB + ')');
|
||||
this.el.css('display', this.visible ? 'inline-block' : 'none');
|
||||
scene.append(this.el);
|
||||
}
|
||||
};
|
||||
|
||||
Reporter.prototype.update = function() {
|
||||
this.el.css('display', this.visible ? 'inline-block' : 'none');
|
||||
if (!this.visible) return;
|
||||
|
||||
var newValue ='';
|
||||
|
||||
var newValue = '';
|
||||
var target = runtime.spriteNamed(this.target);
|
||||
switch (this.cmd) {
|
||||
case 'getVar:':
|
||||
|
@ -104,20 +104,21 @@ Reporter.prototype.update = function() {
|
|||
break;
|
||||
}
|
||||
this.valueEl.html(newValue);
|
||||
if (this.mode == 3)
|
||||
if (this.mode == 3) {
|
||||
this.slider.val(parseInt(newValue));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reporter.prototype.updateLayer = function() {
|
||||
this.el.css('z-index', this.z);
|
||||
}
|
||||
};
|
||||
|
||||
Reporter.prototype.changeSlider = function() {
|
||||
var newValue = parseInt($(this).val());
|
||||
var target = runtime.spriteNamed($(this).attr('data-target'));
|
||||
var variable = $(this).attr('data-var');
|
||||
target.variables[variable] = newValue;
|
||||
}
|
||||
};
|
||||
|
||||
var List = function(data) {
|
||||
this.contents = data.contents;
|
||||
|
@ -171,7 +172,7 @@ List.prototype.attach = function(scene) {
|
|||
this.el.height(this.height);
|
||||
this.el.css('z-index', this.z);
|
||||
this.el.css('display', this.visible ? 'inline-block' : 'none');
|
||||
}
|
||||
};
|
||||
|
||||
List.prototype.update = function(){
|
||||
this.el.css('display', this.visible ? 'inline-block' : 'none');
|
||||
|
@ -191,4 +192,4 @@ List.prototype.update = function(){
|
|||
|
||||
List.prototype.updateLayer = function() {
|
||||
this.el.css('z-index', this.z);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -35,8 +35,8 @@ var Runtime = function() {
|
|||
this.audioPlaying = [];
|
||||
this.notesPlaying = [];
|
||||
this.projectLoaded = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Initializer for the drawing and audio contexts.
|
||||
Runtime.prototype.init = function() {
|
||||
this.scene = $('#container');
|
||||
|
@ -44,8 +44,8 @@ Runtime.prototype.init = function() {
|
|||
this.audioContext = new AudioContext();
|
||||
this.audioGain = this.audioContext.createGainNode();
|
||||
this.audioGain.connect(runtime.audioContext.destination);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Load start waits for the stage and the sprites to be loaded, without
|
||||
// hanging the browser. When the loading is finished, we begin the step
|
||||
// and animate methods.
|
||||
|
@ -69,7 +69,7 @@ Runtime.prototype.loadStart = function() {
|
|||
$('#info').html("Loaded!");
|
||||
setInterval(this.step, 33);
|
||||
this.projectLoaded = true;
|
||||
}
|
||||
};
|
||||
|
||||
Runtime.prototype.greenFlag = function() {
|
||||
if (this.projectLoaded) {
|
||||
|
@ -78,7 +78,7 @@ Runtime.prototype.greenFlag = function() {
|
|||
interp.primitiveTable.timerReset();
|
||||
this.startGreenFlags();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Runtime.prototype.stopAll = function() {
|
||||
interp.activeThread = new Thread(null);
|
||||
|
@ -86,10 +86,11 @@ Runtime.prototype.stopAll = function() {
|
|||
stopAllSounds();
|
||||
// Hide reporters
|
||||
for (var s = 0; s < runtime.sprites.length; s++) {
|
||||
if (typeof runtime.sprites[s].hideBubble == 'function')
|
||||
if (typeof runtime.sprites[s].hideBubble == 'function') {
|
||||
runtime.sprites[s].hideBubble();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Step method for execution - called every 33 milliseconds
|
||||
Runtime.prototype.step = function() {
|
||||
|
@ -97,8 +98,8 @@ Runtime.prototype.step = function() {
|
|||
for (var r = 0; r < runtime.reporters.length; r++) {
|
||||
runtime.reporters[r].update();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Stack functions -- push and remove stacks
|
||||
// to be run by the interpreter as threads.
|
||||
Runtime.prototype.allStacksDo = function(f) {
|
||||
|
@ -106,25 +107,25 @@ Runtime.prototype.allStacksDo = function(f) {
|
|||
var stack;
|
||||
for (var i = runtime.sprites.length-1; i >= 0; i--) {
|
||||
var o = runtime.sprites[i];
|
||||
if(typeof(o) == 'object' && o.constructor == Sprite) {
|
||||
if (typeof(o) == 'object' && o.constructor == Sprite) {
|
||||
$.each(o.stacks, function(index, stack) {
|
||||
f(stack, o);
|
||||
});
|
||||
}
|
||||
}
|
||||
$.each(stage.stacks, function(index, stack) {
|
||||
f(stack, stage);
|
||||
f(stack, stage);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Hat triggers
|
||||
Runtime.prototype.startGreenFlags = function() {
|
||||
function startIfGreenFlag(stack, target) {
|
||||
if (stack.op == 'whenGreenFlag') interp.toggleThread(stack, target);
|
||||
}
|
||||
this.allStacksDo(startIfGreenFlag);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Runtime.prototype.startKeyHats = function(ch) {
|
||||
var keyName = null;
|
||||
if (('A'.charCodeAt(0) <= ch) && (ch <= 'Z'.charCodeAt(0)) ||
|
||||
|
@ -140,44 +141,59 @@ Runtime.prototype.startKeyHats = function(ch) {
|
|||
if (ch == 32) keyName = "space";
|
||||
|
||||
if (keyName == null) return;
|
||||
var startMatchingKeyHats = function (stack, target) {
|
||||
var startMatchingKeyHats = function(stack, target) {
|
||||
if ((stack.op == "whenKeyPressed") && (stack.args[0] == keyName)) {
|
||||
// Only start the stack if it is not already running
|
||||
if (!interp.isRunning(stack))
|
||||
if (!interp.isRunning(stack)) {
|
||||
interp.toggleThread(stack, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
runtime.allStacksDo(startMatchingKeyHats);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Runtime.prototype.startClickedHats = function(sprite) {
|
||||
function startIfClicked(stack, target) {
|
||||
if(target == sprite && stack.op == "whenClicked") {
|
||||
if(!interp.isRunning(stack))
|
||||
interp.toggleThread(stack, target);
|
||||
if (target == sprite && stack.op == "whenClicked" && !interp.isRunning(stack)) {
|
||||
interp.toggleThread(stack, target);
|
||||
}
|
||||
}
|
||||
runtime.allStacksDo(startIfClicked);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Returns true if a key is pressed.
|
||||
Runtime.prototype.keyIsDown = function(ch) {
|
||||
return this.keysDown[ch] || false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Sprite named -- returns one of the sprites on the stage.
|
||||
Runtime.prototype.spriteNamed = function(n) {
|
||||
if (n == 'Stage') return this.stage;
|
||||
var selected_sprite = null;
|
||||
$.each(this.sprites, function(index, s) {
|
||||
if (s.objName == n) {
|
||||
selected_sprite = s;
|
||||
selected_sprite = s;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return selected_sprite;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Runtime.prototype.getTimeString = function(which) {
|
||||
// Return local time properties.
|
||||
var now = new Date();
|
||||
switch (which) {
|
||||
case 'hour': return now.getHours();
|
||||
case 'minute': return now.getMinutes();
|
||||
case 'second': return now.getSeconds();
|
||||
case 'year': return now.getFullYear(); // four digit year (e.g. 2012)
|
||||
case 'month': return now.getMonth() + 1; // 1-12
|
||||
case 'date': return now.getDate(); // 1-31
|
||||
case 'day of week': return now.getDay() + 1; // 1-7, where 1 is Sunday
|
||||
}
|
||||
return ''; // shouldn't happen
|
||||
};
|
||||
|
||||
// Reassigns z-indices for layer functions
|
||||
Runtime.prototype.reassignZ = function(target, move) {
|
||||
var sprites = this.sprites;
|
||||
|
@ -189,18 +205,18 @@ Runtime.prototype.reassignZ = function(target, move) {
|
|||
sprites.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (move == null) {
|
||||
// Move to the front
|
||||
this.sprites.splice(this.sprites.length, 0, target);
|
||||
} else if (oldIndex - move >= 0 && oldIndex - move < this.sprites.length+1) {
|
||||
} else if (oldIndex - move >= 0 && oldIndex - move < this.sprites.length + 1) {
|
||||
// Move to the new position
|
||||
this.sprites.splice(oldIndex - move, 0, target);
|
||||
} else {
|
||||
// No change is required
|
||||
this.sprites.splice(oldIndex, 0, target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Renumber the z-indices
|
||||
var newZ = 1;
|
||||
$.each(this.sprites, function(index, sprite) {
|
||||
|
@ -208,4 +224,4 @@ Runtime.prototype.reassignZ = function(target, move) {
|
|||
sprite.updateLayer();
|
||||
newZ++;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,58 +23,58 @@
|
|||
'use strict';
|
||||
|
||||
var runtime, interp, io, iosAudioActive = false;
|
||||
$(function () {
|
||||
$(function() {
|
||||
runtime = new Runtime();
|
||||
runtime.init();
|
||||
|
||||
|
||||
$(window).keydown(function(e) {
|
||||
runtime.keysDown[e.which] = true;
|
||||
runtime.startKeyHats(e.which);
|
||||
});
|
||||
|
||||
|
||||
$(window).keyup(function(e) {
|
||||
delete runtime.keysDown[e.which];
|
||||
});
|
||||
|
||||
|
||||
// Update the project ID field
|
||||
$('#project_id').val(project_id);
|
||||
|
||||
|
||||
// Go project button behavior
|
||||
$('#go_project').click(function() {
|
||||
window.location = "#" + parseInt($('#project_id').val());
|
||||
window.location.reload(true);
|
||||
});
|
||||
|
||||
|
||||
// Green flag behavior
|
||||
$('#trigger_green_flag, #greenSlide').click(function() {
|
||||
$('#greenSlide').css('display', 'none');
|
||||
runtime.greenFlag()
|
||||
});
|
||||
|
||||
|
||||
// Stop button behavior
|
||||
$('#trigger_stop').click(function() {
|
||||
runtime.stopAll();
|
||||
});
|
||||
|
||||
|
||||
// Canvas container mouse events
|
||||
$('#container').mousedown(function(e) {
|
||||
runtime.mouseDown = true;
|
||||
//e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
$('#container').mouseup(function(e) {
|
||||
runtime.mouseDown = false;
|
||||
//e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
$('#container').mousemove(function(e) {
|
||||
var absX = e.pageX - this.offsetLeft;
|
||||
var absY = e.pageY - this.offsetTop;
|
||||
runtime.mousePos = [absX-240, -absY+180];
|
||||
});
|
||||
|
||||
|
||||
// Touch events - EXPERIMENTAL
|
||||
$(window).bind('touchstart', function(e) {
|
||||
$(window).bind('touchstart', function(e) {
|
||||
// On iOS, we need to activate the Web Audio API
|
||||
// with an empty sound play on the first touch event.
|
||||
if (!iosAudioActive) {
|
||||
|
@ -85,8 +85,8 @@ $(function () {
|
|||
isource.noteOn(0);
|
||||
iosAudioActive = true;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$('#container').bind('touchstart', function(e) {
|
||||
runtime.mouseDown = true;
|
||||
});
|
||||
|
@ -94,14 +94,14 @@ $(function () {
|
|||
$('#container').bind('touchend', function(e) {
|
||||
runtime.mouseDown = true;
|
||||
});
|
||||
|
||||
|
||||
$('#container').bind('touchmove', function(e) {
|
||||
var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
|
||||
var absX = touch.pageX - this.offsetLeft;
|
||||
var absY = touch.pageY - this.offsetTop;
|
||||
runtime.mousePos = [absX-240, -absY+180];
|
||||
});
|
||||
|
||||
|
||||
// Border touch events - EXPERIMENTAL
|
||||
$('#left').bind('touchstart touchmove', function(e) { runtime.keysDown[37] = true; runtime.startKeyHats(37); });
|
||||
$('#left').bind('touchend', function(e) { delete runtime.keysDown[37]; });
|
||||
|
@ -111,11 +111,11 @@ $(function () {
|
|||
$('#right').bind('touchend', function(e) { delete runtime.keysDown[39]; });
|
||||
$('#down').bind('touchstart touchmove', function(e) { runtime.keysDown[40] = true; runtime.startKeyHats(40); });
|
||||
$('#down').bind('touchend', function(e) { delete runtime.keysDown[40]; });
|
||||
|
||||
|
||||
// Load the interpreter and primitives
|
||||
interp = new Interpreter();
|
||||
interp.initPrims();
|
||||
|
||||
|
||||
// Load the requested project and go!
|
||||
io = new IO();
|
||||
io.loadProject(project_id);
|
||||
|
|
168
js/Sprite.js
168
js/Sprite.js
|
@ -22,12 +22,12 @@
|
|||
'use strict';
|
||||
|
||||
var Sprite = function(data) {
|
||||
if(!this.data) {
|
||||
if (!this.data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
// Public variables used for Scratch-accessible data.
|
||||
this.visible = (typeof(this.data.visible) == "undefined") ? true : data.visible;
|
||||
this.visible = typeof(this.data.visible) == "undefined" ? true : data.visible;
|
||||
|
||||
this.scratchX = data.scratchX || 0;
|
||||
this.scratchY = data.scratchY || 0;
|
||||
|
@ -94,8 +94,8 @@ var Sprite = function(data) {
|
|||
|
||||
// Stacks to be pushed to the interpreter and run
|
||||
this.stacks = [];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Attaches a Sprite (<img>) to a Scratch scene
|
||||
Sprite.prototype.attach = function(scene) {
|
||||
// Create textures and materials for each of the costumes.
|
||||
|
@ -109,7 +109,7 @@ Sprite.prototype.attach = function(scene) {
|
|||
sprite.costumesLoaded += 1;
|
||||
sprite.updateCostume();
|
||||
|
||||
$(sprite.textures[c]).css('display', (sprite.currentCostumeIndex == c) ? 'inline' : 'none');
|
||||
$(sprite.textures[c]).css('display', sprite.currentCostumeIndex == c ? 'inline' : 'none');
|
||||
$(sprite.textures[c]).css('position', 'absolute').css('left', '0px').css('top', '0px');
|
||||
$(sprite.textures[c]).bind('dragstart', function(evt) { evt.preventDefault(); })
|
||||
.bind('selectstart', function(evt) { evt.preventDefault(); })
|
||||
|
@ -137,24 +137,23 @@ Sprite.prototype.attach = function(scene) {
|
|||
this.talkBubbleStyler = $('<div class="bubble-say"></div>');
|
||||
this.talkBubble.append(this.talkBubbleBox);
|
||||
this.talkBubble.append(this.talkBubbleStyler);
|
||||
|
||||
|
||||
runtime.scene.append(this.talkBubble);
|
||||
}
|
||||
};
|
||||
|
||||
// Load sounds from the server and buffer them
|
||||
Sprite.prototype.loadSounds = function() {
|
||||
var spr = this;
|
||||
$.each(this.sounds, function (index, sound) {
|
||||
$.each(this.sounds, function(index, sound) {
|
||||
io.soundRequest(sound, spr);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// True when all the costumes have been loaded
|
||||
Sprite.prototype.isLoaded = function() {
|
||||
return (this.costumesLoaded == this.costumes.length
|
||||
&& this.soundsLoaded == Object.keys(this.sounds).length);
|
||||
}
|
||||
|
||||
return this.costumesLoaded == this.costumes.length && this.soundsLoaded == Object.keys(this.sounds).length;
|
||||
};
|
||||
|
||||
// Step methods
|
||||
Sprite.prototype.showCostume = function(costume) {
|
||||
if (costume < 0) {
|
||||
|
@ -167,34 +166,34 @@ Sprite.prototype.showCostume = function(costume) {
|
|||
this.currentCostumeIndex = costume;
|
||||
}
|
||||
this.updateCostume();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.indexOfCostumeNamed = function(name) {
|
||||
for(var i in this.costumes) {
|
||||
for (var i in this.costumes) {
|
||||
var c = this.costumes[i];
|
||||
if(c['costumeName'] == name) {
|
||||
if (c['costumeName'] == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.showCostumeNamed = function(name) {
|
||||
var index = this.indexOfCostumeNamed(name);
|
||||
if(!index) return;
|
||||
if (!index) return;
|
||||
this.showCostume(index);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.updateCostume = function() {
|
||||
if(!this.textures[this.currentCostumeIndex]) {
|
||||
if (!this.textures[this.currentCostumeIndex]) {
|
||||
this.currentCostumeIndex = 0;
|
||||
}
|
||||
$(this.mesh).css('display', 'none');
|
||||
this.mesh = this.textures[this.currentCostumeIndex];
|
||||
this.updateVisible();
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.onClick = function(evt) {
|
||||
// TODO - needs work!!
|
||||
var boxOffset = $('#container').offset();
|
||||
|
@ -238,34 +237,35 @@ Sprite.prototype.onClick = function(evt) {
|
|||
$(underElement).click();
|
||||
$(this.mesh).show();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.setVisible = function(v) {
|
||||
this.visible = v;
|
||||
this.updateVisible();
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.updateLayer = function() {
|
||||
$(this.mesh).css('z-index', this.z);
|
||||
if (this.talkBubble) this.talkBubble.css('z-index', this.z);
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.updateVisible = function() {
|
||||
$(this.mesh).css('display', (this.visible) ? 'inline' : 'none');
|
||||
if (this.talkBubbleOn)
|
||||
this.talkBubble.css('display', (this.visible) ? 'inline-block' : 'none');
|
||||
}
|
||||
$(this.mesh).css('display', this.visible ? 'inline' : 'none');
|
||||
if (this.talkBubbleOn) {
|
||||
this.talkBubble.css('display', this.visible ? 'inline-block' : 'none');
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.updateTransform = function() {
|
||||
var texture = this.textures[this.currentCostumeIndex];
|
||||
var resolution = this.costumes[this.currentCostumeIndex].bitmapResolution || 1;
|
||||
|
||||
|
||||
var drawWidth = texture.width * this.scale / resolution;
|
||||
var drawHeight = texture.height * this.scale / resolution;
|
||||
|
||||
|
||||
var rotationCenterX = this.costumes[this.currentCostumeIndex].rotationCenterX;
|
||||
var rotationCenterY = this.costumes[this.currentCostumeIndex].rotationCenterY;
|
||||
|
||||
|
||||
var drawX = this.scratchX + (480 / 2) - rotationCenterX;
|
||||
var drawY = -this.scratchY + (360 / 2) - rotationCenterY;
|
||||
|
||||
|
@ -275,26 +275,26 @@ Sprite.prototype.updateTransform = function() {
|
|||
// sign to the X scale.
|
||||
}
|
||||
|
||||
$(this.mesh).css('transform',
|
||||
$(this.mesh).css('transform',
|
||||
'translatex(' + drawX + 'px) \
|
||||
translatey(' + drawY + 'px) \
|
||||
rotate(' + this.rotation + 'deg) \
|
||||
scaleX(' + scaleXprepend + (this.scale / resolution) + ') scaleY(' + (this.scale / resolution) + ')');
|
||||
$(this.mesh).css('-moz-transform',
|
||||
$(this.mesh).css('-moz-transform',
|
||||
'translatex(' + drawX + 'px) \
|
||||
translatey(' + drawY + 'px) \
|
||||
rotate(' + this.rotation + 'deg) \
|
||||
scaleX(' + scaleXprepend + this.scale + ') scaleY(' + this.scale / resolution + ')');
|
||||
$(this.mesh).css('-webkit-transform',
|
||||
$(this.mesh).css('-webkit-transform',
|
||||
'translatex(' + drawX + 'px) \
|
||||
translatey(' + drawY + 'px) \
|
||||
rotate(' + this.rotation + 'deg) \
|
||||
scaleX(' + scaleXprepend + (this.scale / resolution) + ') scaleY(' + (this.scale / resolution) + ')');
|
||||
|
||||
$(this.mesh).css('-webkit-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
$(this.mesh).css('-moz-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
$(this.mesh).css('-ms-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
$(this.mesh).css('-o-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
|
||||
$(this.mesh).css('-webkit-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
$(this.mesh).css('-moz-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
$(this.mesh).css('-ms-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
$(this.mesh).css('-o-transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
$(this.mesh).css('transform-origin', rotationCenterX + 'px ' + rotationCenterY + 'px');
|
||||
|
||||
// Don't forget to update the talk bubble.
|
||||
|
@ -303,9 +303,9 @@ Sprite.prototype.updateTransform = function() {
|
|||
this.talkBubble.css('left', xy[0] + 'px');
|
||||
this.talkBubble.css('top', xy[1] + 'px');
|
||||
}
|
||||
|
||||
this.updateLayer();
|
||||
}
|
||||
|
||||
this.updateLayer();
|
||||
};
|
||||
|
||||
Sprite.prototype.getTalkBubbleXY = function() {
|
||||
var texture = this.textures[this.currentCostumeIndex];
|
||||
|
@ -316,7 +316,7 @@ Sprite.prototype.getTalkBubbleXY = function() {
|
|||
var drawX = this.scratchX + (480 / 2) - rotationCenterX;
|
||||
var drawY = -this.scratchY + (360 / 2) - rotationCenterY;
|
||||
return [drawX + drawWidth, drawY - drawHeight / 2];
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.showBubble = function(text, type) {
|
||||
var xy = this.getTalkBubbleXY();
|
||||
|
@ -329,30 +329,34 @@ Sprite.prototype.showBubble = function(text, type) {
|
|||
|
||||
this.talkBubbleStyler.removeClass('bubble-say');
|
||||
this.talkBubbleStyler.removeClass('bubble-think');
|
||||
if (type == 'say') this.talkBubbleStyler.addClass('bubble-say');
|
||||
else if (type == 'think') this.talkBubbleStyler.addClass('bubble-think');
|
||||
|
||||
if (this.visible)
|
||||
if (type == 'say') {
|
||||
this.talkBubbleStyler.addClass('bubble-say');
|
||||
} else if (type == 'think') {
|
||||
this.talkBubbleStyler.addClass('bubble-think');
|
||||
}
|
||||
|
||||
if (this.visible) {
|
||||
this.talkBubble.css('display', 'inline-block');
|
||||
}
|
||||
this.talkBubbleBox.html(text);
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.hideBubble = function() {
|
||||
this.talkBubbleOn = false;
|
||||
this.talkBubble.css('display', 'none');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.setXY = function(x, y) {
|
||||
this.scratchX = x;
|
||||
this.scratchY = y;
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.setDirection = function(d) {
|
||||
var rotation;
|
||||
d = d % 360
|
||||
if (d < 0) d += 360;
|
||||
this.direction = (d > 180) ? d - 360 : d;
|
||||
this.direction = d > 180 ? d - 360 : d;
|
||||
if (this.rotationStyle == 'normal') {
|
||||
rotation = (this.direction - 90) % 360;
|
||||
} else if (this.rotationStyle == 'leftRight') {
|
||||
|
@ -367,23 +371,23 @@ Sprite.prototype.setDirection = function(d) {
|
|||
}
|
||||
this.rotation = rotation;
|
||||
this.updateTransform();
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.setRotationStyle = function(r) {
|
||||
this.rotationStyle = r;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.getSize = function() {
|
||||
return this.scale * 100;
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.setSize = function(percent) {
|
||||
var newScale = percent / 100.0;
|
||||
newScale = Math.max(0.05, Math.min(newScale, 100));
|
||||
this.scale = newScale;
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Move functions
|
||||
Sprite.prototype.keepOnStage = function() {
|
||||
var x = this.scratchX + 240;
|
||||
|
@ -398,7 +402,7 @@ Sprite.prototype.keepOnStage = function() {
|
|||
if (myBox.top > edgeBox.bottom) y -= myBox.top - edgeBox.bottom;
|
||||
this.scratchX = x - 240;
|
||||
this.scratchY = 180 - y;
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.getRect = function() {
|
||||
var cImg = this.textures[this.currentCostumeIndex];
|
||||
|
@ -406,35 +410,38 @@ Sprite.prototype.getRect = function() {
|
|||
var y = 180 - this.scratchY - (cImg.height/2.0);
|
||||
var myBox = new Rectangle(x, y, cImg.width, cImg.height);
|
||||
return myBox;
|
||||
}
|
||||
|
||||
// Pen functions
|
||||
};
|
||||
|
||||
// Pen functions
|
||||
Sprite.prototype.setPenColor = function(c) {
|
||||
var hsv = Color.rgb2hsv(c);
|
||||
this.penHue = (200 * hsv[0]) / 360 ;
|
||||
this.penShade = 50 * hsv[2]; // not quite right; doesn't account for saturation
|
||||
this.penColorCache = c;
|
||||
}
|
||||
Sprite.prototype.setPenHue = function(n) {
|
||||
};
|
||||
|
||||
Sprite.prototype.setPenHue = function(n) {
|
||||
this.penHue = n % 200;
|
||||
if (this.penHue < 0) this.penHue += 200;
|
||||
this.updateCachedPenColor();
|
||||
}
|
||||
Sprite.prototype.setPenShade = function(n) {
|
||||
};
|
||||
|
||||
Sprite.prototype.setPenShade = function(n) {
|
||||
this.penShade = n % 200;
|
||||
if (this.penShade < 0) this.penShade += 200;
|
||||
this.updateCachedPenColor();
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.updateCachedPenColor = function() {
|
||||
var c = Color.fromHSV((this.penHue * 180.0) / 100.0, 1, 1);
|
||||
var shade = (this.penShade > 100) ? 200 - this.penShade : this.penShade; // range 0..100
|
||||
var shade = this.penShade > 100 ? 200 - this.penShade : this.penShade; // range 0..100
|
||||
if (shade < 50) {
|
||||
this.penColorCache = Color.mixRGB(0, c, (10 + shade) / 60.0);
|
||||
} else {
|
||||
this.penColorCache = Color.mixRGB(c, 0xFFFFFF, (shade - 50) / 60);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Sprite.prototype.stamp = function(canvas, opacity) {
|
||||
var drawWidth = this.textures[this.currentCostumeIndex].width * this.scale;
|
||||
var drawHeight = this.textures[this.currentCostumeIndex].height * this.scale;
|
||||
|
@ -447,12 +454,13 @@ Sprite.prototype.stamp = function(canvas, opacity) {
|
|||
canvas.drawImage(this.mesh, -drawWidth/2, -drawHeight/2, drawWidth, drawHeight);
|
||||
canvas.restore();
|
||||
canvas.globalAlpha = 1;
|
||||
}
|
||||
};
|
||||
|
||||
Sprite.prototype.soundNamed = function(name) {
|
||||
if (name in this.sounds && this.sounds[name].buffer)
|
||||
if (name in this.sounds && this.sounds[name].buffer) {
|
||||
return this.sounds[name];
|
||||
else if (name in runtime.stage.sounds && runtime.stage.sounds[name].buffer)
|
||||
} else if (name in runtime.stage.sounds && runtime.stage.sounds[name].buffer) {
|
||||
return runtime.stage.sounds[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
25
js/Stage.js
25
js/Stage.js
|
@ -35,31 +35,28 @@ var Stage = function(data) {
|
|||
this.lineCache = this.lineCanvas.getContext('2d');
|
||||
|
||||
Sprite.call(this, data);
|
||||
}
|
||||
};
|
||||
|
||||
Stage.prototype = Object.create(Sprite.prototype);
|
||||
Stage.prototype.constructor = Stage;
|
||||
|
||||
|
||||
Stage.prototype.attachPenLayer = function(scene) {
|
||||
if (this.penLayerLoaded)
|
||||
return;
|
||||
if (this.penLayerLoaded) return;
|
||||
this.penLayerLoaded = true;
|
||||
$(this.lineCanvas).css('position', 'absolute');
|
||||
$(this.lineCanvas).css('z-index', '-1');
|
||||
scene.append(this.lineCanvas);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Stage.prototype.isLoaded = function() {
|
||||
return (this.penLayerLoaded
|
||||
&& this.costumesLoaded == this.costumes.length
|
||||
&& this.soundsLoaded == Object.keys(this.sounds).length);
|
||||
}
|
||||
|
||||
return this.penLayerLoaded && this.costumesLoaded == this.costumes.length && this.soundsLoaded == Object.keys(this.sounds).length;
|
||||
};
|
||||
|
||||
// Pen functions
|
||||
Stage.prototype.clearPenStrokes = function() {
|
||||
this.lineCache.clearRect(0, 0, 480, 360);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Stage.prototype.stroke = function(from, to, width, color) {
|
||||
this.lineCache.lineWidth = width;
|
||||
this.lineCache.lineCap = 'round';
|
||||
|
@ -69,4 +66,4 @@ Stage.prototype.stroke = function(from, to, width, color) {
|
|||
this.lineCache.lineTo(to[0] + 240.5, 180.5 - to[1]);
|
||||
this.lineCache.strokeStyle = 'rgb(' + (color >> 16) + ',' + (color >> 8 & 255) + ',' + (color & 255) + ')';
|
||||
this.lineCache.stroke();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var LooksPrims = function() {}
|
||||
var LooksPrims = function() {};
|
||||
|
||||
LooksPrims.prototype.addPrimsTo = function(primTable) {
|
||||
primTable["show"] = this.primShow;
|
||||
|
@ -42,28 +42,28 @@ LooksPrims.prototype.addPrimsTo = function(primTable) {
|
|||
primTable["changeGraphicEffect:by:"] = this.primChangeEffect;
|
||||
primTable["setGraphicEffect:to:"] = this.primSetEffect;
|
||||
primTable["filterReset"] = this.primClearEffects;
|
||||
|
||||
|
||||
primTable["say:"] = function(b) { showBubble(b, 'say'); };
|
||||
primTable["say:duration:elapsed:from:"] = function(b) { showBubbleAndWait(b, 'say'); };
|
||||
primTable["think:"] = function(b) { showBubble(b, 'think'); };
|
||||
primTable["think:duration:elapsed:from:"] = function(b) { showBubbleAndWait(b, 'think'); };
|
||||
}
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primShow = function(b) {
|
||||
interp.targetSprite().setVisible(true);
|
||||
interp.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primHide = function(b) {
|
||||
interp.targetSprite().setVisible(false);
|
||||
interp.targetSprite().setVisible(false);
|
||||
interp.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primNextCostume = function(b) {
|
||||
interp.targetSprite().showCostume(interp.targetSprite().currentCostumeIndex + 1);
|
||||
interp.redraw();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primShowCostume = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null) return;
|
||||
|
@ -88,8 +88,8 @@ LooksPrims.prototype.primShowCostume = function(b) {
|
|||
}
|
||||
}
|
||||
if (s.visible) interp.redraw();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primStartScene = function(b) {
|
||||
var s = runtime.stage;
|
||||
var arg = interp.arg(b, 0);
|
||||
|
@ -113,55 +113,55 @@ LooksPrims.prototype.primStartScene = function(b) {
|
|||
}
|
||||
}
|
||||
if (s.visible) interp.redraw();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primCostumeNum = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
return (s == null) ? 1 : s.currentCostumeIndex + 1;
|
||||
}
|
||||
|
||||
return s == null ? 1 : s.currentCostumeIndex + 1;
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primChangeSize = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null) return;
|
||||
s.setSize(s.getSize() + interp.arg(b, 0));
|
||||
if (s.visible) interp.redraw();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primSetSize = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null) return;
|
||||
s.setSize(interp.arg(b, 0));
|
||||
if (s.visible) interp.redraw();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primSize = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null) return 100;
|
||||
return s.getSize();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primGoFront = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
runtime.reassignZ(s, null);
|
||||
if(s.visible) interp.redraw();
|
||||
}
|
||||
|
||||
if (s.visible) interp.redraw();
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primGoBack = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
runtime.reassignZ(s, interp.arg(b, 0));
|
||||
if(s.visible) interp.redraw();
|
||||
}
|
||||
|
||||
LooksPrims.prototype.primChangeEffect = function(b) {}
|
||||
if (s.visible) interp.redraw();
|
||||
};
|
||||
|
||||
LooksPrims.prototype.primSetEffect = function(b) {}
|
||||
LooksPrims.prototype.primChangeEffect = function(b) {};
|
||||
|
||||
LooksPrims.prototype.primClearEffects = function(b) {}
|
||||
LooksPrims.prototype.primSetEffect = function(b) {};
|
||||
|
||||
LooksPrims.prototype.primClearEffects = function(b) {};
|
||||
|
||||
var showBubble = function(b, type) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.showBubble(interp.arg(b, 0), type);
|
||||
}
|
||||
};
|
||||
|
||||
var showBubbleAndWait = function(b, type) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -175,4 +175,4 @@ var showBubbleAndWait = function(b, type) {
|
|||
} else {
|
||||
if (interp.checkTimer()) s.hideBubble();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var MotionAndPenPrims = function() {}
|
||||
var MotionAndPenPrims = function() {};
|
||||
|
||||
MotionAndPenPrims.prototype.addPrimsTo = function(primTable) {
|
||||
primTable['forward:'] = this.primMove;
|
||||
|
@ -26,19 +26,19 @@ MotionAndPenPrims.prototype.addPrimsTo = function(primTable) {
|
|||
primTable["gotoX:y:"] = this.primGoTo;
|
||||
primTable["gotoSpriteOrMouse:"] = this.primGoToSpriteOrMouse;
|
||||
primTable["glideSecs:toX:y:elapsed:from:"] = this.primGlide;
|
||||
|
||||
|
||||
primTable["changeXposBy:"] = this.primChangeX;
|
||||
primTable["xpos:"] = this.primSetX;
|
||||
primTable["changeYposBy:"] = this.primChangeY;
|
||||
primTable["ypos:"] = this.primSetY;
|
||||
|
||||
|
||||
primTable["bounceOffEdge"] = this.primBounceOffEdge;
|
||||
primTable["setRotationStyle"] = this.primSetRotationStyle;
|
||||
|
||||
|
||||
primTable["xpos"] = this.primXPosition;
|
||||
primTable["ypos"] = this.primYPosition;
|
||||
primTable["heading"] = this.primDirection;
|
||||
|
||||
|
||||
primTable["clearPenTrails"] = this.primClear;
|
||||
primTable["putPenDown"] = this.primPenDown;
|
||||
primTable["putPenUp"] = this.primPenUp;
|
||||
|
@ -49,63 +49,62 @@ MotionAndPenPrims.prototype.addPrimsTo = function(primTable) {
|
|||
primTable["changePenShadeBy:"] = this.primChangePenShade;
|
||||
primTable["penSize:"] = this.primSetPenSize;
|
||||
primTable["changePenSizeBy:"] = this.primChangePenSize;
|
||||
|
||||
|
||||
primTable["stampCostume"] = this.primStamp;
|
||||
primTable["stampTransparent"] = this.primStampTransparent;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primMove = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var radians = ((Math.PI * (90 - s.direction)) / 180);
|
||||
var radians = (90 - s.direction) * Math.PI / 180;
|
||||
var d = interp.arg(b, 0);
|
||||
|
||||
moveSpriteTo(s, s.scratchX + (d * Math.cos(radians)),
|
||||
s.scratchY + (d * Math.sin(radians)));
|
||||
if(s.visible) interp.redraw();
|
||||
}
|
||||
moveSpriteTo(s, s.scratchX + d * Math.cos(radians), s.scratchY + d * Math.sin(radians));
|
||||
if (s.visible) interp.redraw();
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primTurnLeft = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var d = s.direction - interp.arg(b, 0);
|
||||
s.setDirection(d);
|
||||
if(s.visible) interp.redraw();
|
||||
}
|
||||
if (s.visible) interp.redraw();
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primTurnRight = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var d = s.direction + interp.arg(b, 0);
|
||||
s.setDirection(d);
|
||||
if(s.visible) interp.redraw();
|
||||
}
|
||||
if (s.visible) interp.redraw();
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetDirection = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
s.setDirection(interp.arg(b, 0));
|
||||
if(s.visible) interp.redraw();
|
||||
}
|
||||
if (s.visible) interp.redraw();
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primPointTowards = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var p = mouseOrSpritePosition(interp.arg(b, 0));
|
||||
if ((s == null) || (p == null)) return;
|
||||
if (s == null || p == null) return;
|
||||
var dx = p.x - s.scratchX;
|
||||
var dy = p.y - s.scratchY;
|
||||
var angle = 90 - ((Math.atan2(dy, dx) * 180) / Math.PI);
|
||||
var angle = 90 - Math.atan2(dy, dx) * 180 / Math.PI;
|
||||
s.setDirection(angle);
|
||||
if (s.visible) interp.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primGoTo = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) moveSpriteTo(s, interp.arg(b, 0), interp.arg(b, 1));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primGoToSpriteOrMouse = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var p = mouseOrSpritePosition(interp.arg(b, 0));
|
||||
if ((s == null) || (p == null)) return;
|
||||
if (s == null || p == null) return;
|
||||
moveSpriteTo(s, p.x, p.y);
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primGlide = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -119,44 +118,43 @@ MotionAndPenPrims.prototype.primGlide = function(b) {
|
|||
return;
|
||||
}
|
||||
// record state: [0]start msecs, [1]duration, [2]startX, [3]startY, [4]endX, [5]endY
|
||||
interp.activeThread.tmpObj =
|
||||
[interp.currentMSecs, 1000 * secs, s.scratchX, s.scratchY, destX, destY];
|
||||
interp.activeThread.tmpObj = [interp.currentMSecs, 1000 * secs, s.scratchX, s.scratchY, destX, destY];
|
||||
interp.startTimer(secs);
|
||||
} else {
|
||||
var state = interp.activeThread.tmpObj;
|
||||
if (!interp.checkTimer()) {
|
||||
// in progress: move to intermediate position along path
|
||||
var frac = (interp.currentMSecs - state[0]) / state[1];
|
||||
var newX = state[2] + (frac * (state[4] - state[2]));
|
||||
var newY = state[3] + (frac * (state[5] - state[3]));
|
||||
var newX = state[2] + frac * (state[4] - state[2]);
|
||||
var newY = state[3] + frac * (state[5] - state[3]);
|
||||
moveSpriteTo(s, newX, newY);
|
||||
} else {
|
||||
// finished: move to final position and clear state
|
||||
moveSpriteTo(s, state[4], state[5]);
|
||||
interp.activeThread.tmpObj = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primChangeX = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) moveSpriteTo(s, s.scratchX + interp.arg(b, 0), s.scratchY);
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetX = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) moveSpriteTo(s, interp.arg(b, 0), s.scratchY);
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primChangeY = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) moveSpriteTo(s, s.scratchX, s.scratchY + interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetY = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) moveSpriteTo(s, s.scratchX, interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primBounceOffEdge = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -164,7 +162,7 @@ MotionAndPenPrims.prototype.primBounceOffEdge = function(b) {
|
|||
if (!turnAwayFromEdge(s)) return;
|
||||
ensureOnStageOnBounce(s);
|
||||
if (s.visible) interp.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetRotationStyle = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -175,95 +173,94 @@ MotionAndPenPrims.prototype.primSetRotationStyle = function(b) {
|
|||
else if (request == 'left-right') rotationStyle = 'leftRight';
|
||||
else if (request == 'none') rotationStyle = 'none';
|
||||
s.setRotationStyle(rotationStyle);
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primXPosition = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
return (s != null) ? s.scratchX : 0;
|
||||
}
|
||||
return s != null ? s.scratchX : 0;
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primYPosition = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
return (s != null) ? s.scratchY : 0;
|
||||
}
|
||||
return s != null ? s.scratchY : 0;
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primDirection = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
return (s != null) ? s.direction : 0;
|
||||
}
|
||||
return s != null ? s.direction : 0;
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primClear = function(b) {
|
||||
runtime.stage.clearPenStrokes();
|
||||
interp.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primPenDown = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.penIsDown = true;
|
||||
stroke(s, s.scratchX, s.scratchY, s.scratchX + 0.2, s.scratchY + 0.2);
|
||||
interp.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primPenUp = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.penIsDown = false;
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetPenColor = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.setPenColor(interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetPenHue = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.setPenHue(interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primChangePenHue = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.setPenHue(s.penHue + interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetPenShade = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.setPenShade(interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primChangePenShade = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.setPenShade(s.penShade + interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primSetPenSize = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var w = Math.max(0, Math.min(interp.arg(b, 0), 100));
|
||||
if (s != null) s.penWidth = w;
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primChangePenSize = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var w = Math.max(0, Math.min(s.penWidth + interp.arg(b, 0), 100));
|
||||
if (s != null) s.penWidth = w;
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primStamp = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
s.stamp(runtime.stage.lineCache, 100);
|
||||
}
|
||||
};
|
||||
|
||||
MotionAndPenPrims.prototype.primStampTransparent = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var transparency = Math.max(0, Math.min(interp.arg(b, 0), 100));
|
||||
var alpha = 100 - transparency;
|
||||
s.stamp(runtime.stage.lineCache, alpha);
|
||||
}
|
||||
};
|
||||
|
||||
// Helpers
|
||||
var stroke = function(s, oldX, oldY, newX, newY) {
|
||||
runtime.stage.stroke([oldX, oldY],
|
||||
[newX, newY], s.penWidth, s.penColorCache);
|
||||
runtime.stage.stroke([oldX, oldY], [newX, newY], s.penWidth, s.penColorCache);
|
||||
interp.redraw();
|
||||
}
|
||||
};
|
||||
|
||||
var mouseOrSpritePosition = function(arg) {
|
||||
if (arg == "_mouse_") {
|
||||
|
@ -275,7 +272,7 @@ var mouseOrSpritePosition = function(arg) {
|
|||
return new Point(s.scratchX, s.scratchY);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var moveSpriteTo = function(s, newX, newY) {
|
||||
var oldX = s.scratchX;
|
||||
|
@ -283,8 +280,8 @@ var moveSpriteTo = function(s, newX, newY) {
|
|||
s.setXY(newX, newY);
|
||||
s.keepOnStage();
|
||||
if (s.penIsDown) stroke(s, oldX, oldY, s.scratchX, s.scratchY);
|
||||
if ((s.penIsDown) || (s.visible)) interp.redraw();
|
||||
}
|
||||
if (s.penIsDown || s.visible) interp.redraw();
|
||||
};
|
||||
|
||||
var turnAwayFromEdge = function(s) {
|
||||
// turn away from the nearest edge if it's close enough; otherwise do nothing
|
||||
|
@ -298,23 +295,23 @@ var turnAwayFromEdge = function(s) {
|
|||
var d4 = Math.max(0, 360 - r.bottom);
|
||||
// find the nearest edge
|
||||
var e = 0, minDist = 100000;
|
||||
if (d1 < minDist) { minDist = d1; e = 1 }
|
||||
if (d2 < minDist) { minDist = d2; e = 2 }
|
||||
if (d3 < minDist) { minDist = d3; e = 3 }
|
||||
if (d4 < minDist) { minDist = d4; e = 4 }
|
||||
if (d1 < minDist) { minDist = d1; e = 1; }
|
||||
if (d2 < minDist) { minDist = d2; e = 2; }
|
||||
if (d3 < minDist) { minDist = d3; e = 3; }
|
||||
if (d4 < minDist) { minDist = d4; e = 4; }
|
||||
if (minDist > 0) return false; // not touching to any edge
|
||||
// point away from nearest edge
|
||||
var radians = ((90 - s.direction) * Math.PI) / 180;
|
||||
var radians = (90 - s.direction) * Math.PI / 180;
|
||||
var dx = Math.cos(radians);
|
||||
var dy = -Math.sin(radians);
|
||||
if (e == 1) { dx = Math.max(0.2, Math.abs(dx)) }
|
||||
if (e == 2) { dy = Math.max(0.2, Math.abs(dy)) }
|
||||
if (e == 3) { dx = 0 - Math.max(0.2, Math.abs(dx)) }
|
||||
if (e == 4) { dy = 0 - Math.max(0.2, Math.abs(dy)) }
|
||||
var newDir = ((180 * Math.atan2(dy, dx)) / Math.PI) + 90;
|
||||
if (e == 1) { dx = Math.max(0.2, Math.abs(dx)); }
|
||||
if (e == 2) { dy = Math.max(0.2, Math.abs(dy)); }
|
||||
if (e == 3) { dx = 0 - Math.max(0.2, Math.abs(dx)); }
|
||||
if (e == 4) { dy = 0 - Math.max(0.2, Math.abs(dy)); }
|
||||
var newDir = Math.atan2(dy, dx) * 180 / Math.PI + 90;
|
||||
s.direction = newDir;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var ensureOnStageOnBounce = function(s) {
|
||||
var r = s.getRect();
|
||||
|
@ -326,4 +323,4 @@ var ensureOnStageOnBounce = function(s) {
|
|||
if (r.bottom > 360) {
|
||||
moveSpriteTo(s, s.scratchX, s.scratchY + (r.bottom - 360));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,57 +26,57 @@ var Primitives = function() {}
|
|||
|
||||
Primitives.prototype.addPrimsTo = function(primTable) {
|
||||
// Math primitives
|
||||
primTable["+"] = function(b) { return interp.arg(b, 0) + interp.arg(b, 1) };
|
||||
primTable["-"] = function(b) { return interp.arg(b, 0) - interp.arg(b, 1) };
|
||||
primTable["*"] = function(b) { return interp.arg(b, 0) * interp.arg(b, 1) };
|
||||
primTable["/"] = function(b) { return interp.arg(b, 0) / interp.arg(b, 1) };
|
||||
primTable["%"] = function(b) { return interp.arg(b, 0) % interp.arg(b, 1) };
|
||||
primTable["+"] = function(b) { return interp.arg(b, 0) + interp.arg(b, 1); };
|
||||
primTable["-"] = function(b) { return interp.arg(b, 0) - interp.arg(b, 1); };
|
||||
primTable["*"] = function(b) { return interp.arg(b, 0) * interp.arg(b, 1); };
|
||||
primTable["/"] = function(b) { return interp.arg(b, 0) / interp.arg(b, 1); };
|
||||
primTable["%"] = function(b) { return interp.arg(b, 0) % interp.arg(b, 1); };
|
||||
primTable["randomFrom:to:"] = this.primRandom;
|
||||
primTable["<"] = function(b) { return (interp.arg(b, 0) < interp.arg(b, 1)) };
|
||||
primTable["="] = function(b) { return (interp.arg(b, 0) == interp.arg(b, 1)) };
|
||||
primTable[">"] = function(b) { return (interp.arg(b, 0) > interp.arg(b, 1)) };
|
||||
primTable["&"] = function(b) { return interp.arg(b, 0) && interp.arg(b, 1) };
|
||||
primTable["|"] = function(b) { return interp.arg(b, 0) || interp.arg(b, 1) };
|
||||
primTable["<"] = function(b) { return (interp.arg(b, 0) < interp.arg(b, 1)); };
|
||||
primTable["="] = function(b) { return (interp.arg(b, 0) == interp.arg(b, 1)); };
|
||||
primTable[">"] = function(b) { return (interp.arg(b, 0) > interp.arg(b, 1)); };
|
||||
primTable["&"] = function(b) { return interp.arg(b, 0) && interp.arg(b, 1); };
|
||||
primTable["|"] = function(b) { return interp.arg(b, 0) || interp.arg(b, 1); };
|
||||
primTable["not"] = function(b) { return !interp.arg(b, 0) };
|
||||
primTable["abs"] = function(b) { return Math.abs(interp.arg(b, 0)) };
|
||||
primTable["sqrt"] = function(b) { return Math.sqrt(interp.arg(b, 0)) };
|
||||
|
||||
primTable["\\\\"] = this.primModulo;
|
||||
primTable["rounded"] = function(b) { return Math.round(interp.arg(b, 0)) };
|
||||
primTable["rounded"] = function(b) { return Math.round(interp.arg(b, 0)); };
|
||||
primTable["computeFunction:of:"] = this.primMathFunction;
|
||||
|
||||
|
||||
// String primitives
|
||||
primTable["concatenate:with:"] = function(b) { return "" + interp.arg(b, 0) + interp.arg(b, 1) };
|
||||
primTable["concatenate:with:"] = function(b) { return "" + interp.arg(b, 0) + interp.arg(b, 1); };
|
||||
primTable["letter:of:"] = this.primLetterOf;
|
||||
primTable["stringLength:"] = function(b) { return interp.arg(b, 0).length };
|
||||
|
||||
primTable["stringLength:"] = function(b) { return interp.arg(b, 0).length; };
|
||||
|
||||
new VarListPrims().addPrimsTo(primTable);
|
||||
new MotionAndPenPrims().addPrimsTo(primTable);
|
||||
new LooksPrims().addPrimsTo(primTable);
|
||||
new SensingPrims().addPrimsTo(primTable);
|
||||
new SoundPrims().addPrimsTo(primTable);
|
||||
}
|
||||
|
||||
|
||||
Primitives.prototype.primRandom = function(b) {
|
||||
var n1 = interp.arg(b, 0);
|
||||
var n2 = interp.arg(b, 1);
|
||||
var low = (n1 <= n2) ? n1 : n2;
|
||||
var hi = (n1 <= n2) ? n2 : n1;
|
||||
if(low == hi) return low;
|
||||
var low = n1 <= n2 ? n1 : n2;
|
||||
var hi = n1 <= n2 ? n2 : n1;
|
||||
if (low == hi) return low;
|
||||
// if both low and hi are ints, truncate the result to an int
|
||||
if ((Math.floor(low) == low) && (Math.floor(hi) == hi)) {
|
||||
return low + Math.floor(Math.random() * ((hi + 1) - low));
|
||||
if (Math.floor(low) == low && Math.floor(hi) == hi) {
|
||||
return low + Math.floor(Math.random() * (hi + 1 - low));
|
||||
}
|
||||
return (Math.random() * (hi - low)) + low;
|
||||
return Math.random() * (hi - low) + low;
|
||||
}
|
||||
|
||||
|
||||
Primitives.prototype.primLetterOf = function(b) {
|
||||
var s = interp.arg(b, 1);
|
||||
var i = interp.arg(b, 0) - 1;
|
||||
if ((i < 0) || (i >= s.length)) return "";
|
||||
if (i < 0 || i >= s.length) return "";
|
||||
return s.charAt(i);
|
||||
}
|
||||
|
||||
|
||||
Primitives.prototype.primModulo = function(b) {
|
||||
var modulus = interp.arg(b, 1);
|
||||
var n = interp.arg(b, 0) % modulus;
|
||||
|
@ -90,14 +90,14 @@ Primitives.prototype.primMathFunction = function(b) {
|
|||
switch(op) {
|
||||
case "abs": return Math.abs(n);
|
||||
case "sqrt": return Math.sqrt(n);
|
||||
case "sin": return Math.sin((Math.PI * n) / 180);
|
||||
case "cos": return Math.cos((Math.PI * n) / 180);
|
||||
case "tan": return Math.tan((Math.PI * n) / 180);
|
||||
case "asin": return (Math.asin(n) * 180) / Math.PI;
|
||||
case "acos": return (Math.acos(n) * 180) / Math.PI;
|
||||
case "atan": return (Math.atan(n) * 180) / Math.PI;
|
||||
case "sin": return Math.sin(n * Math.PI / 180);
|
||||
case "cos": return Math.cos(n * Math.PI / 180);
|
||||
case "tan": return Math.tan(n * Math.PI / 180);
|
||||
case "asin": return Math.asin(n) * 180 / Math.PI;
|
||||
case "acos": return Math.acos(n) * 180 / Math.PI;
|
||||
case "atan": return Math.atan(n) * 180 / Math.PI;
|
||||
case "ln": return Math.log(n);
|
||||
case "log": return Math.log(n) / Math.LN10;
|
||||
case "log": return Math.log(n) / Math.LN10;
|
||||
case "e ^": return Math.exp(n);
|
||||
case "10 ^": return Math.exp(n * Math.LN10);
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var SensingPrims = function() {}
|
||||
var SensingPrims = function() {};
|
||||
|
||||
SensingPrims.prototype.addPrimsTo = function(primTable) {
|
||||
primTable['touching:'] = this.primTouching;
|
||||
primTable['touchingColor:'] = this.primTouchingColor;
|
||||
primTable['color:sees:'] = this.primColorTouchingColor;
|
||||
|
||||
|
||||
primTable['keyPressed:'] = this.primKeyPressed;
|
||||
primTable['mousePressed'] = function(b) { return runtime.mouseDown; };
|
||||
primTable['mouseX'] = function(b) { return runtime.mousePos[0]; };
|
||||
|
@ -30,29 +30,29 @@ SensingPrims.prototype.addPrimsTo = function(primTable) {
|
|||
|
||||
primTable['getAttribute:of:'] = this.primGetAttribute;
|
||||
|
||||
primTable['timeAndDate'] = this.primTimeDate;
|
||||
primTable['timeAndDate'] = function(b){ return runtime.getTimeString(interp.arg(b, 0)); };
|
||||
primTable['timestamp'] = this.primTimestamp;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primTouching = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null || !s.visible) return false;
|
||||
|
||||
|
||||
var arg = interp.arg(b, 0);
|
||||
if (arg == '_edge_') {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
|
||||
if (arg == '_mouse_') {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
|
||||
var s2 = runtime.spriteNamed(arg);
|
||||
if (s2 == null || !s2.visible) return false;
|
||||
|
||||
|
||||
return spriteHitTest(s, s2);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primTouchingColor = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null || !s.visible) return false;
|
||||
|
@ -60,8 +60,8 @@ SensingPrims.prototype.primTouchingColor = function(b) {
|
|||
var color = interp.arg(b, 0);
|
||||
|
||||
return stageColorHitTest(s, color);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primColorTouchingColor = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null || !s.visible) return false;
|
||||
|
@ -70,8 +70,8 @@ SensingPrims.prototype.primColorTouchingColor = function(b) {
|
|||
var stageColor = interp.arg(b, 1);
|
||||
|
||||
return stageColorByColorHitTest(s, myColor, stageColor);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var spriteHitTest = function(a, b) {
|
||||
var hitCanvas = document.createElement('canvas');
|
||||
hitCanvas.width = 480;
|
||||
|
@ -91,8 +91,8 @@ var spriteHitTest = function(a, b) {
|
|||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var stageColorHitTest = function(target, color) {
|
||||
var r, g, b;
|
||||
r = (color >> 16);
|
||||
|
@ -123,18 +123,18 @@ var stageColorHitTest = function(target, color) {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var stageColorByColorHitTest = function(target, myColor, otherColor) {
|
||||
var threshold_acceptable = function(a, b, c, x, y, z) {
|
||||
diff_a = Math.abs(a-x);
|
||||
diff_b = Math.abs(b-y);
|
||||
diff_c = Math.abs(c-z);
|
||||
var diff_a = Math.abs(a-x);
|
||||
var diff_b = Math.abs(b-y);
|
||||
var diff_c = Math.abs(c-z);
|
||||
if (diff_a + diff_b + diff_c < 100) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
var targetCanvas = document.createElement('canvas');
|
||||
targetCanvas.width = 480;
|
||||
targetCanvas.height = 360;
|
||||
|
@ -157,23 +157,23 @@ var stageColorByColorHitTest = function(target, myColor, otherColor) {
|
|||
var hitCanvas = document.createElement('canvas');
|
||||
hitCanvas.width = 480;
|
||||
hitCanvas.height = 360;
|
||||
hitCtx = hitCanvas.getContext('2d');
|
||||
var hitCtx = hitCanvas.getContext('2d');
|
||||
$.each(runtime.sprites, function(i, sprite) {
|
||||
if (sprite != target)
|
||||
if (sprite != target) {
|
||||
sprite.stamp(hitCtx, 100);
|
||||
}
|
||||
});
|
||||
|
||||
var hitData = hitCtx.getImageData(0, 0, hitCanvas.width, hitCanvas.height).data;
|
||||
var pxCount = targetData.length;
|
||||
for (var i = 0; i < pxCount; i += 4) {
|
||||
if (threshold_acceptable(targetData[i], targetData[i+1], targetData[i+2], mr, mg, mb)
|
||||
&& threshold_acceptable(hitData[i], hitData[i+1], hitData[i+2], or, og, ob)) {
|
||||
if (threshold_acceptable(targetData[i], targetData[i+1], targetData[i+2], mr, mg, mb) && threshold_acceptable(hitData[i], hitData[i+1], hitData[i+2], or, og, ob)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primKeyPressed = function(b) {
|
||||
var key = interp.arg(b, 0);
|
||||
var ch = key.charCodeAt(0);
|
||||
|
@ -184,8 +184,8 @@ SensingPrims.prototype.primKeyPressed = function(b) {
|
|||
if (key == "down arrow") ch = 40;
|
||||
if (key == "space") ch = 32;
|
||||
return (typeof(runtime.keysDown[ch]) != 'undefined');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primDistanceTo = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
var p = mouseOrSpritePosition(interp.arg(b, 0));
|
||||
|
@ -193,8 +193,8 @@ SensingPrims.prototype.primDistanceTo = function(b) {
|
|||
var dx = p.x - s.scratchX;
|
||||
var dy = p.y - s.scratchY;
|
||||
return Math.sqrt((dx * dx) + (dy * dy));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primGetAttribute = function(b) {
|
||||
var attr = interp.arg(b, 0);
|
||||
var targetSprite = runtime.spriteNamed(interp.arg(b, 1));
|
||||
|
@ -207,7 +207,7 @@ SensingPrims.prototype.primGetAttribute = function(b) {
|
|||
if (attr == 'size') return targetSprite.getSize();
|
||||
if (attr == 'volume') return targetSprite.volume;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primTimeDate = function(b) {
|
||||
var dt = interp.arg(b, 0);
|
||||
|
@ -220,13 +220,16 @@ SensingPrims.prototype.primTimeDate = function(b) {
|
|||
if (dt == 'minute') return now.getMinutes();
|
||||
if (dt == 'second') return now.getSeconds();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
SensingPrims.prototype.primTimestamp = function(b) {
|
||||
var now = new Date(), epoch = new Date(2000,0,1), dst = now.getTimezoneOffset() - epoch.getTimezoneOffset(), msSince = now.getTime() - epoch.getTime();
|
||||
msSince += (now.getTimezoneOffset() - dst) * 60000;
|
||||
return msSince / 86400000;
|
||||
}
|
||||
var now = new Date();
|
||||
var epoch = new Date(2000, 0, 1);
|
||||
var dst = now.getTimezoneOffset() - epoch.getTimezoneOffset();
|
||||
var msSince = now.getTime() - epoch.getTime();
|
||||
msSince += (now.getTimezoneOffset() - dst) * 60000;
|
||||
return msSince / 86400000;
|
||||
};
|
||||
|
||||
// Helpers
|
||||
SensingPrims.prototype.mouseOrSpritePosition = function(arg) {
|
||||
|
@ -239,4 +242,4 @@ SensingPrims.prototype.mouseOrSpritePosition = function(arg) {
|
|||
return new Point(s.scratchX, s.scratchY);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var SoundPrims = function() {}
|
||||
var SoundPrims = function() {};
|
||||
|
||||
SoundPrims.prototype.addPrimsTo = function(primTable) {
|
||||
primTable['playSound:'] = this.primPlaySound;
|
||||
|
@ -34,7 +34,7 @@ SoundPrims.prototype.addPrimsTo = function(primTable) {
|
|||
primTable['changeTempoBy:'] = function(b) { runtime.stage.data.tempoBPM = runtime.stage.data.tempoBPM + interp.arg(b, 0); };
|
||||
primTable['setTempoTo:'] = function(b) { runtime.stage.data.tempoBPM = interp.arg(b, 0); };
|
||||
primTable['tempo'] = function(b) { return runtime.stage.data.tempoBPM; };
|
||||
}
|
||||
};
|
||||
|
||||
var playSound = function(snd) {
|
||||
if (snd.source) {
|
||||
|
@ -42,11 +42,11 @@ var playSound = function(snd) {
|
|||
snd.source.noteOff(0);
|
||||
snd.source = null;
|
||||
}
|
||||
|
||||
|
||||
snd.source = runtime.audioContext.createBufferSource();
|
||||
snd.source.buffer = snd.buffer;
|
||||
snd.source.connect(runtime.audioGain);
|
||||
|
||||
|
||||
// Track the sound's completion state
|
||||
snd.source.done = false;
|
||||
snd.source.finished = function() {
|
||||
|
@ -64,7 +64,7 @@ var playSound = function(snd) {
|
|||
runtime.audioPlaying.push(snd);
|
||||
snd.source.noteOn(0);
|
||||
return snd.source;
|
||||
}
|
||||
};
|
||||
|
||||
var playDrum = function(drum, secs, client) {
|
||||
var player = SoundBank.getDrumPlayer(drum, secs);
|
||||
|
@ -81,9 +81,9 @@ var playDrum = function(drum, secs, client) {
|
|||
runtime.notesPlaying.splice(i, 1);
|
||||
}
|
||||
}
|
||||
window.setTimeout(source.finished, secs * 1000);
|
||||
window.setTimeout(source.finished, secs * 1000);
|
||||
return player;
|
||||
}
|
||||
};
|
||||
|
||||
var playNote = function(instrument, midiKey, secs, client) {
|
||||
var player = SoundBank.getNotePlayer(instrument, midiKey);
|
||||
|
@ -99,9 +99,9 @@ var playNote = function(instrument, midiKey, secs, client) {
|
|||
runtime.notesPlaying.splice(i, 1);
|
||||
}
|
||||
}
|
||||
window.setTimeout(source.finished, secs * 1000);
|
||||
window.setTimeout(source.finished, secs * 1000);
|
||||
return player;
|
||||
}
|
||||
};
|
||||
|
||||
var stopAllSounds = function() {
|
||||
var oldPlaying = runtime.audioPlaying;
|
||||
|
@ -121,14 +121,14 @@ var stopAllSounds = function() {
|
|||
oldPlaying[s].finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primPlaySound = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null) return;
|
||||
var snd = s.soundNamed(interp.arg(b, 0));
|
||||
if (snd != null) playSound(snd);
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primPlaySoundUntilDone = function(b) {
|
||||
var activeThread = interp.activeThread;
|
||||
|
@ -145,11 +145,11 @@ SoundPrims.prototype.primPlaySoundUntilDone = function(b) {
|
|||
} else {
|
||||
interp.yield = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var beatsToSeconds = function(beats) {
|
||||
return (beats * 60) / runtime.stage.data.tempoBPM;
|
||||
}
|
||||
return beats * 60 / runtime.stage.data.tempoBPM;
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primPlayNote = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -162,7 +162,7 @@ SoundPrims.prototype.primPlayNote = function(b) {
|
|||
} else {
|
||||
interp.checkTimer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primPlayDrum = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -175,7 +175,7 @@ SoundPrims.prototype.primPlayDrum = function(b) {
|
|||
} else {
|
||||
interp.checkTimer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primPlayRest = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -186,28 +186,28 @@ SoundPrims.prototype.primPlayRest = function(b) {
|
|||
} else {
|
||||
interp.checkTimer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primSetInstrument = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.instrument = interp.arg(b, 0);
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primStopAllSounds = function(b) {
|
||||
stopAllSounds();
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primChangeVolume = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.volume += interp.arg(b, 0);
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primSetVolume = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s != null) s.volume = interp.arg(b, 0);
|
||||
}
|
||||
};
|
||||
|
||||
SoundPrims.prototype.primVolume = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
return (s != null) ? s.volume : 0;
|
||||
}
|
||||
return s != null ? s.volume : 0;
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ VarListPrims.prototype.addPrimsTo = function(primTable) {
|
|||
primTable['lineCountOfList:'] = this.primListLength;
|
||||
primTable['getLine:ofList:'] = this.primListGetLine;
|
||||
primTable['list:contains:'] = this.primListContains;
|
||||
}
|
||||
};
|
||||
|
||||
// Variable primitive implementations
|
||||
|
||||
|
@ -42,21 +42,23 @@ VarListPrims.prototype.primReadVar = function(b) {
|
|||
var s = interp.targetSprite();
|
||||
if (s == null) return;
|
||||
var targetVar = interp.arg(b, 0);
|
||||
if (targetVar in s.variables)
|
||||
if (targetVar in s.variables) {
|
||||
return s.variables[targetVar];
|
||||
else if (targetVar in runtime.stage.variables)
|
||||
} else if (targetVar in runtime.stage.variables) {
|
||||
return runtime.stage.variables[targetVar];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primSetVar = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
if (s == null) return;
|
||||
var targetVar = interp.arg(b, 0);
|
||||
if (targetVar in s.variables)
|
||||
if (targetVar in s.variables) {
|
||||
s.variables[targetVar] = interp.arg(b, 1);
|
||||
else if (targetVar in runtime.stage.variables)
|
||||
} else if (targetVar in runtime.stage.variables) {
|
||||
runtime.stage.variables[targetVar] = interp.arg(b, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primChangeVar = function(b) {
|
||||
var s = interp.targetSprite();
|
||||
|
@ -69,7 +71,7 @@ VarListPrims.prototype.primChangeVar = function(b) {
|
|||
runtime.stage.variables[targetVar] = parseFloat(runtime.stage.variables[targetVar]);
|
||||
runtime.stage.variables[targetVar] += interp.arg(b, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primHideVar = function(b) {
|
||||
var targetVar = interp.arg(b, 0);
|
||||
|
@ -79,7 +81,7 @@ VarListPrims.prototype.primHideVar = function(b) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primShowVar = function(b) {
|
||||
var targetVar = interp.arg(b, 0);
|
||||
|
@ -89,11 +91,11 @@ VarListPrims.prototype.primShowVar = function(b) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// List primitive implementations
|
||||
|
||||
// Take a list name and target sprite and return the JS list itself
|
||||
// Take a list name and target sprite and return the JS list itself
|
||||
var findList = function(targetSprite, listName) {
|
||||
if (targetSprite == null) targetSprite = runtime.stage;
|
||||
if (listName in targetSprite.lists) {
|
||||
|
@ -102,20 +104,20 @@ var findList = function(targetSprite, listName) {
|
|||
return runtime.stage.lists[listName].contents;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primReadList = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 0));
|
||||
if (list) {
|
||||
var allOne = list.map(function(val){return val.length}).reduce(function(old,val){return old+val},0)===list.length;
|
||||
return list.join(allOne?'':' ');
|
||||
var allOne = list.map(function(val) { return val.length; }).reduce(function(old,val) { return old + val; }, 0) === list.length;
|
||||
return list.join(allOne ? '' : ' ');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primListAppend = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 1));
|
||||
if (list) list.push(interp.arg(b, 0));
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primListDeleteLine = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 1));
|
||||
|
@ -124,41 +126,42 @@ VarListPrims.prototype.primListDeleteLine = function(b) {
|
|||
if (line == 'all' || list.length == 0) list.length = 0;
|
||||
else if (line == 'last') list.splice(list.length - 1, 1);
|
||||
else if (parseInt(line) - 1 in list) list.splice(parseInt(line) - 1, 1);
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primListInsertAt = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 2));
|
||||
if (!list) return;
|
||||
var newItem = interp.arg(b, 0);
|
||||
|
||||
|
||||
var position = interp.arg(b, 1);
|
||||
if (position == 'last') position = list.length;
|
||||
else if (position == 'random') position = Math.round(Math.random() * list.length);
|
||||
else position = parseInt(position) - 1;
|
||||
if (position > list.length) return;
|
||||
|
||||
|
||||
list.splice(position, 0, newItem);
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primListSetLine = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 1));
|
||||
if (!list) return;
|
||||
var newItem = interp.arg(b, 2);
|
||||
var position = interp.arg(b, 0);
|
||||
|
||||
|
||||
if (position == 'last') position = list.length - 1;
|
||||
else if (position == 'random') position = Math.floor(Math.random() * list.length);
|
||||
else position = parseInt(position) - 1;
|
||||
|
||||
if (position > list.length - 1) return;
|
||||
|
||||
list[position] = newItem;
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primListLength = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 0));
|
||||
if (!list) return 0;
|
||||
return list.length;
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primListGetLine = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 1));
|
||||
|
@ -169,7 +172,7 @@ VarListPrims.prototype.primListGetLine = function(b) {
|
|||
else if (line == 'last') line = list.length;
|
||||
else if (list.length < line) return 0;
|
||||
return list[line - 1];
|
||||
}
|
||||
};
|
||||
|
||||
VarListPrims.prototype.primListContains = function(b) {
|
||||
var list = findList(interp.targetSprite(), interp.arg(b, 0));
|
||||
|
@ -177,4 +180,4 @@ VarListPrims.prototype.primListContains = function(b) {
|
|||
var searchItem = interp.arg(b, 1);
|
||||
if (parseFloat(searchItem) == searchItem) searchItem = parseFloat(searchItem);
|
||||
return $.inArray(searchItem, list) > -1;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -65,11 +65,11 @@ var NotePlayer = function(wavFileData, originalPitch, loopStart, loopEnd, env) {
|
|||
if (env) {
|
||||
this.attackEnd = env[0] * 44.100;
|
||||
if (this.attackEnd > 0) this.attackRate = Math.pow(33000, 1 / this.attackEnd);
|
||||
this.holdEnd = this.attackEnd + (env[1] * 44.100);
|
||||
this.holdEnd = this.attackEnd + env[1] * 44.100;
|
||||
var decayCount = env[2] * 44100;
|
||||
this.decayRate = (decayCount == 0) ? 1 : Math.pow(33000, -1 / decayCount);
|
||||
this.decayRate = decayCount == 0 ? 1 : Math.pow(33000, -1 / decayCount);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NotePlayer.prototype = Object.create(SoundDecoder.prototype);
|
||||
NotePlayer.prototype.constructor = NotePlayer;
|
||||
|
@ -79,14 +79,14 @@ NotePlayer.prototype.setNoteAndDuration = function(midiKey, secs) {
|
|||
var pitch = 440 * Math.pow(2, (midiKey - 69) / 12); // midi key 69 is A (440 Hz)
|
||||
this.stepSize = pitch / (2 * this.originalPitch); // adjust for original sampling rate of 22050
|
||||
this.setDuration(secs);
|
||||
}
|
||||
};
|
||||
|
||||
NotePlayer.prototype.setDuration = function(secs) {
|
||||
this.samplesSinceStart = 0;
|
||||
this.samplesRemaining = 44100 * secs;
|
||||
if (!this.isLooped) this.samplesRemaining = Math.min(this.samplesRemaining, this.endOffset / this.stepSize);
|
||||
this.envelopeValue = (this.attackEnd > 0) ? 1 / 33000 : 1;
|
||||
}
|
||||
this.envelopeValue = this.attackEnd > 0 ? 1 / 33000 : 1;
|
||||
};
|
||||
|
||||
NotePlayer.prototype.interpolatedSample = function() {
|
||||
if (this.samplesRemaining-- <= 0) { this.noteFinished(); return 0; }
|
||||
|
@ -99,11 +99,11 @@ NotePlayer.prototype.interpolatedSample = function() {
|
|||
var frac = this.index - i;
|
||||
var curr = this.rawSample(i);
|
||||
var next = this.rawSample(i + 1);
|
||||
var sample = (curr + (frac * (next - curr))) / 100000; // xxx 32000; attenuate...
|
||||
var sample = (curr + frac * (next - curr)) / 100000; // xxx 32000; attenuate...
|
||||
if (this.samplesRemaining < 1000) sample *= (this.samplesRemaining / 1000.0); // relaase phease
|
||||
this.updateEnvelope();
|
||||
return this.envelopeValue * sample;
|
||||
}
|
||||
};
|
||||
|
||||
NotePlayer.prototype.rawSample = function(sampleIndex) {
|
||||
if (sampleIndex >= this.endOffset) {
|
||||
|
@ -112,8 +112,8 @@ NotePlayer.prototype.rawSample = function(sampleIndex) {
|
|||
}
|
||||
var byteIndex = 2 * sampleIndex;
|
||||
var result = (this.soundData[byteIndex + 1] << 8) + this.soundData[byteIndex];
|
||||
return (result <= 32767) ? result : result - 65536;
|
||||
}
|
||||
return result <= 32767 ? result : result - 65536;
|
||||
};
|
||||
|
||||
NotePlayer.prototype.updateEnvelope = function() {
|
||||
// Compute envelopeValue for the current sample.
|
||||
|
@ -125,4 +125,4 @@ NotePlayer.prototype.updateEnvelope = function() {
|
|||
} else if (this.samplesSinceStart > this.holdEnd) {
|
||||
if (this.decayRate < 1) this.envelopeValue *= this.decayRate;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -178,24 +178,24 @@ SoundBank.getNotePlayer = function(instNum, midiKey) {
|
|||
// Return a NotePlayer for the given Scratch 2.0 instrument number (1..21)
|
||||
// and MIDI key (0..127). If the instrument is out of range, use 1.
|
||||
var r = SoundBank.getNoteRecord(instNum - 1, midiKey);
|
||||
var env = (r.length > 5) ? r[5] : null;
|
||||
var env = r.length > 5 ? r[5] : null;
|
||||
return new NotePlayer(Instr.samples[r[1]], SoundBank.pitchForKey(r[2]), r[3], r[4], env);
|
||||
}
|
||||
};
|
||||
|
||||
SoundBank.getNoteRecord = function(instNum, midiKey) {
|
||||
// Get a note record for the given instrument number.
|
||||
if ((instNum < 0) || (instNum >= SoundBank.instruments.length)) instNum = 0;
|
||||
if (instNum < 0 || instNum >= SoundBank.instruments.length) instNum = 0;
|
||||
var keyRanges = SoundBank.instruments[instNum];
|
||||
for (var r = 0; r < keyRanges.length; r++) {
|
||||
var topOfKeyRange = keyRanges[r][0];
|
||||
if (midiKey <= topOfKeyRange) return keyRanges[r];
|
||||
}
|
||||
return keyRanges[keyRanges.length - 1]; // return the note record for the top key range.
|
||||
}
|
||||
};
|
||||
|
||||
SoundBank.pitchForKey = function(midiKey) {
|
||||
return 440 * Math.pow(2, (midiKey - 69) / 12); // midi key 69 is A=440 Hz
|
||||
}
|
||||
};
|
||||
|
||||
SoundBank.getDrumPlayer = function(drumNum, secs) {
|
||||
// Return a NotePlayer for the given drum number.
|
||||
|
@ -210,6 +210,4 @@ SoundBank.getDrumPlayer = function(drumNum, secs) {
|
|||
var player = new NotePlayer(Instr.samples[entry[0]], SoundBank.pitchForKey(60), loopStart, loopEnd, env);
|
||||
player.setNoteAndDuration(60 + entry[1], 0);
|
||||
return player;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
var SoundDecoder = function(wavFileData) {
|
||||
this.scratchSound = null;
|
||||
|
||||
|
||||
this.soundData = null;
|
||||
this.startOffset = 0;
|
||||
this.endOffset = 0;
|
||||
|
@ -37,10 +37,10 @@ var SoundDecoder = function(wavFileData) {
|
|||
this.thisSample = 0;
|
||||
|
||||
// decoder state
|
||||
this.sample = 0;
|
||||
this.sample = 0;
|
||||
this.index = 0;
|
||||
this.lastByte = -1; // -1 indicates that there is no saved lastByte
|
||||
|
||||
|
||||
this.nextSample = 0;
|
||||
|
||||
this.info = null;
|
||||
|
@ -61,12 +61,12 @@ var SoundDecoder = function(wavFileData) {
|
|||
if (info.bitsPerSample == 16) this.getSample = this.getSample16Uncompressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SoundDecoder.prototype.noteFinished = function() {
|
||||
// Called by subclasses to force ending condition to be true in writeSampleData()
|
||||
this.bytePosition = this.endOffset;
|
||||
}
|
||||
};
|
||||
|
||||
// Used for Notes and Drums - Web Audio API ScriptProcessorNodes use this
|
||||
// as a callback function to fill the buffers with sample data.
|
||||
|
@ -78,7 +78,7 @@ SoundDecoder.prototype.writeSampleData = function(evt) {
|
|||
var n = this.interpolatedSample();
|
||||
output[i] = n;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// For pre-caching the samples of WAV sounds
|
||||
// Return a full list of samples generated by the decoder.
|
||||
|
@ -90,7 +90,7 @@ SoundDecoder.prototype.getAllSamples = function() {
|
|||
smp = this.interpolatedSample();
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
};
|
||||
|
||||
// Provide the next sample for the buffer
|
||||
SoundDecoder.prototype.interpolatedSample = function() {
|
||||
|
@ -101,11 +101,9 @@ SoundDecoder.prototype.interpolatedSample = function() {
|
|||
this.fraction -= 1.0;
|
||||
}
|
||||
if (this.nextSample == null) { return null; }
|
||||
var out = (this.fraction == 0) ?
|
||||
this.thisSample :
|
||||
this.thisSample + (this.fraction * (this.nextSample - this.thisSample));
|
||||
return (out) / 32768.0;
|
||||
}
|
||||
var out = this.fraction == 0 ? this.thisSample : this.thisSample + this.fraction * (this.nextSample - this.thisSample);
|
||||
return out / 32768.0;
|
||||
};
|
||||
|
||||
// 16-bit samples, big-endian
|
||||
SoundDecoder.prototype.getSample16Uncompressed = function() {
|
||||
|
@ -119,13 +117,13 @@ SoundDecoder.prototype.getSample16Uncompressed = function() {
|
|||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// 8-bit samples, uncompressed
|
||||
SoundDecoder.prototype.getSample8Uncompressed = function() {
|
||||
if (this.bytePosition >= this.info.sampleDataSize) return null;
|
||||
return (this.soundData[this.bytePosition++] - 128) << 8;
|
||||
}
|
||||
};
|
||||
|
||||
/*SoundDecoder.prototype.updateVolume = function() {
|
||||
if (this.client == null) {
|
||||
|
@ -149,13 +147,13 @@ SoundDecoder.stepTable = [
|
|||
12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
];
|
||||
|
||||
SoundDecoder.prototype.getSampleADPCM = function() {
|
||||
SoundDecoder.prototype.getSampleADPCM = function() {
|
||||
// Decompress sample data using the IMA ADPCM algorithm.
|
||||
// Note: Handles only one channel, 4-bits/sample.
|
||||
// Note: Handles only one channel, 4-bits/sample.
|
||||
var step = 0, code = 0, delta = 0;
|
||||
|
||||
if (((this.bytePosition % this.adpcmBlockSize) == 0) && (this.lastByte < 0)) { // read block header
|
||||
if (this.bytePosition > (this.info.sampleDataSize - 4)) return null;
|
||||
|
||||
if (this.bytePosition % this.adpcmBlockSize == 0 && this.lastByte < 0) { // read block header
|
||||
if (this.bytePosition > this.info.sampleDataSize - 4) return null;
|
||||
this.sample = (this.soundData[this.bytePosition + 1] << 8) + this.soundData[this.bytePosition];
|
||||
if (this.sample > 32767) this.sample -= 65536;
|
||||
this.index = this.soundData[this.bytePosition + 2];
|
||||
|
@ -184,7 +182,7 @@ SoundDecoder.prototype.getSampleADPCM = function() {
|
|||
if (this.index > 88) this.index = 88;
|
||||
if (this.index < 0) this.index = 0;
|
||||
// compute and output sample
|
||||
this.sample += ((code & 8) ? -delta : delta);
|
||||
this.sample += code & 8 ? -delta : delta;
|
||||
if (this.sample > 32767) this.sample = 32767;
|
||||
if (this.sample < -32768) this.sample = -32768;
|
||||
return this.sample;
|
||||
|
|
|
@ -19,11 +19,10 @@
|
|||
|
||||
var WAVFile = function() {};
|
||||
|
||||
|
||||
WAVFile.decode = function(waveData) {
|
||||
// Decode the given WAV file data and return an Object with the format and sample data.
|
||||
var result = {};
|
||||
|
||||
|
||||
var data = new OffsetBuffer(waveData);
|
||||
|
||||
// read WAVE File Header
|
||||
|
@ -31,11 +30,11 @@ WAVFile.decode = function(waveData) {
|
|||
var totalSize = data.readInt();
|
||||
if (data.getLength() != (totalSize + 8)) console.log("WAVFile: bad RIFF size; ignoring");
|
||||
if (data.readString(4) != 'WAVE') { console.log("WAVFile: not a WAVE file"); return; }
|
||||
|
||||
|
||||
// read format chunk
|
||||
var formatChunk = WAVFile.extractChunk('fmt ', data);
|
||||
if (formatChunk.getLength() < 16) { console.log("WAVFile: format chunk is too small"); return; }
|
||||
|
||||
|
||||
var encoding = formatChunk.readShort();
|
||||
result.encoding = encoding;
|
||||
result.channels = formatChunk.readShort();
|
||||
|
@ -48,7 +47,7 @@ WAVFile.decode = function(waveData) {
|
|||
var sampleDataStartAndSize = WAVFile.dataChunkStartAndSize(data);
|
||||
result.sampleDataStart = sampleDataStartAndSize[0];
|
||||
result.sampleDataSize = sampleDataStartAndSize[1];
|
||||
|
||||
|
||||
// handle various encodings
|
||||
if (encoding == 1) {
|
||||
if (!((result.bitsPerSample == 8) || (result.bitsPerSample == 16))) {
|
||||
|
@ -75,8 +74,7 @@ WAVFile.decode = function(waveData) {
|
|||
return;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
WAVFile.extractChunk = function(desiredType, data) {
|
||||
// Return the contents of the first chunk of the given type or an empty OffsetBuffer if it is not found.
|
||||
|
@ -93,8 +91,7 @@ WAVFile.extractChunk = function(desiredType, data) {
|
|||
}
|
||||
}
|
||||
return new OffsetBuffer(new ArrayBuffer());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
WAVFile.dataChunkStartAndSize = function(data) {
|
||||
// Return an array with the starting offset and size of the first chunk of the given type.
|
||||
|
@ -110,4 +107,4 @@ WAVFile.dataChunkStartAndSize = function(data) {
|
|||
}
|
||||
}
|
||||
return [0, 0]; // chunk not found; bad wave file
|
||||
}
|
||||
};
|
||||
|
|
116
js/util/Color.js
116
js/util/Color.js
|
@ -20,70 +20,70 @@
|
|||
Color = function() {};
|
||||
|
||||
Color.fromHSV = function(h, s, v) {
|
||||
var r, g, b;
|
||||
h = h % 360;
|
||||
if (h < 0) h += 360;
|
||||
s = Math.max(0, Math.min(s, 1));
|
||||
v = Math.max(0, Math.min(v, 1));
|
||||
var r, g, b;
|
||||
h = h % 360;
|
||||
if (h < 0) h += 360;
|
||||
s = Math.max(0, Math.min(s, 1));
|
||||
v = Math.max(0, Math.min(v, 1));
|
||||
|
||||
var i = Math.floor(h / 60);
|
||||
var f = (h / 60) - i;
|
||||
var p = v * (1 - s);
|
||||
var q = v * (1 - (s * f));
|
||||
var t = v * (1 - (s * (1 - f)));
|
||||
if (i == 0) { r = v; g = t; b = p; }
|
||||
else if (i == 1) { r = q; g = v; b = p; }
|
||||
else if (i == 2) { r = p; g = v; b = t; }
|
||||
else if (i == 3) { r = p; g = q; b = v; }
|
||||
else if (i == 4) { r = t; g = p; b = v; }
|
||||
else if (i == 5) { r = v; g = p; b = q; }
|
||||
r = Math.floor(r * 255);
|
||||
g = Math.floor(g * 255);
|
||||
b = Math.floor(b * 255);
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
var i = Math.floor(h / 60);
|
||||
var f = (h / 60) - i;
|
||||
var p = v * (1 - s);
|
||||
var q = v * (1 - s * f);
|
||||
var t = v * (1 - s * (1 - f));
|
||||
if (i == 0) { r = v; g = t; b = p; }
|
||||
else if (i == 1) { r = q; g = v; b = p; }
|
||||
else if (i == 2) { r = p; g = v; b = t; }
|
||||
else if (i == 3) { r = p; g = q; b = v; }
|
||||
else if (i == 4) { r = t; g = p; b = v; }
|
||||
else if (i == 5) { r = v; g = p; b = q; }
|
||||
r = Math.floor(r * 255);
|
||||
g = Math.floor(g * 255);
|
||||
b = Math.floor(b * 255);
|
||||
return (r << 16) | (g << 8) | b;
|
||||
};
|
||||
|
||||
Color.rgb2hsv = function(rgb) {
|
||||
var h, s, v, x, f, i;
|
||||
var r = ((rgb >> 16) & 255) / 255;
|
||||
var g = ((rgb >> 8) & 255) / 255;
|
||||
var b = (rgb & 255) / 255;
|
||||
x = Math.min(Math.min(r, g), b);
|
||||
v = Math.max(Math.max(r, g), b);
|
||||
if (x == v) return [0, 0, v]; // gray; hue arbitrarily reported as zero
|
||||
f = (r == x) ? g - b : ((g == x) ? b - r : r - g);
|
||||
i = (r == x) ? 3 : ((g == x) ? 5 : 1);
|
||||
h = ((i - (f / (v - x))) * 60) % 360;
|
||||
s = (v - x) / v;
|
||||
return [h, s, v];
|
||||
}
|
||||
var h, s, v, x, f, i;
|
||||
var r = ((rgb >> 16) & 255) / 255;
|
||||
var g = ((rgb >> 8) & 255) / 255;
|
||||
var b = (rgb & 255) / 255;
|
||||
x = Math.min(Math.min(r, g), b);
|
||||
v = Math.max(Math.max(r, g), b);
|
||||
if (x == v) return [0, 0, v]; // gray; hue arbitrarily reported as zero
|
||||
f = r == x ? g - b : g == x ? b - r : r - g;
|
||||
i = r == x ? 3 : g == x ? 5 : 1;
|
||||
h = ((i - f / (v - x)) * 60) % 360;
|
||||
s = (v - x) / v;
|
||||
return [h, s, v];
|
||||
};
|
||||
|
||||
Color.scaleBrightness = function(rgb, scale) {
|
||||
var hsv = Color.rgb2hsv(rgb);
|
||||
scale = Math.max(0, Math.min(scale, 1));
|
||||
return Color.fromHSV(hsv[0], hsv[1], scale * hsv[2]);
|
||||
}
|
||||
var hsv = Color.rgb2hsv(rgb);
|
||||
scale = Math.max(0, Math.min(scale, 1));
|
||||
return Color.fromHSV(hsv[0], hsv[1], scale * hsv[2]);
|
||||
};
|
||||
|
||||
Color.mixRGB = function(rgb1, rgb2, fraction) {
|
||||
// Mix rgb1 with rgb2. 0 gives all rgb1, 1 gives rbg2, .5 mixes them 50/50.
|
||||
if (fraction <= 0) return rgb1;
|
||||
if (fraction >= 1) return rgb2;
|
||||
var r1 = (rgb1 >> 16) & 255;
|
||||
var g1 = (rgb1 >> 8) & 255;
|
||||
var b1 = rgb1 & 255
|
||||
var r2 = (rgb2 >> 16) & 255;
|
||||
var g2 = (rgb2 >> 8) & 255;
|
||||
var b2 = rgb2 & 255
|
||||
var r = ((fraction * r2) + ((1.0 - fraction) * r1)) & 255;
|
||||
var g = ((fraction * g2) + ((1.0 - fraction) * g1)) & 255;
|
||||
var b = ((fraction * b2) + ((1.0 - fraction) * b1)) & 255;
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
// Mix rgb1 with rgb2. 0 gives all rgb1, 1 gives rbg2, .5 mixes them 50/50.
|
||||
if (fraction <= 0) return rgb1;
|
||||
if (fraction >= 1) return rgb2;
|
||||
var r1 = (rgb1 >> 16) & 255;
|
||||
var g1 = (rgb1 >> 8) & 255;
|
||||
var b1 = rgb1 & 255
|
||||
var r2 = (rgb2 >> 16) & 255;
|
||||
var g2 = (rgb2 >> 8) & 255;
|
||||
var b2 = rgb2 & 255
|
||||
var r = ((fraction * r2) + ((1.0 - fraction) * r1)) & 255;
|
||||
var g = ((fraction * g2) + ((1.0 - fraction) * g1)) & 255;
|
||||
var b = ((fraction * b2) + ((1.0 - fraction) * b1)) & 255;
|
||||
return (r << 16) | (g << 8) | b;
|
||||
};
|
||||
|
||||
Color.random = function() {
|
||||
// return a random color
|
||||
var h = 360 * Math.random();
|
||||
var s = 0.7 + (0.3 * Math.random());
|
||||
var v = 0.6 + (0.4 * Math.random());
|
||||
return Color.fromHSV(h, s, v);
|
||||
}
|
||||
// return a random color
|
||||
var h = 360 * Math.random();
|
||||
var s = 0.7 + (0.3 * Math.random());
|
||||
var v = 0.6 + (0.4 * Math.random());
|
||||
return Color.fromHSV(h, s, v);
|
||||
};
|
||||
|
|
|
@ -19,63 +19,63 @@
|
|||
var OffsetBuffer = function(data) {
|
||||
this.offset = 0;
|
||||
this.ab = data;
|
||||
}
|
||||
};
|
||||
|
||||
// Read various datatypes from the ArrayBuffer, seeking the offset.
|
||||
OffsetBuffer.prototype.readString = function(length) {
|
||||
var str = this.ab2str(this.ab.slice(this.offset, this.offset + length));
|
||||
this.offset += length;
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
OffsetBuffer.prototype.readInt = function() {
|
||||
var num = this.ab2int(this.ab.slice(this.offset, this.offset + 4));
|
||||
this.offset += 4;
|
||||
return num;
|
||||
}
|
||||
};
|
||||
|
||||
OffsetBuffer.prototype.readUint = function() {
|
||||
var num = this.ab2uint(this.ab.slice(this.offset, this.offset + 4));
|
||||
this.offset += 4;
|
||||
return num;
|
||||
}
|
||||
};
|
||||
|
||||
OffsetBuffer.prototype.readShort = function() {
|
||||
var num = this.ab2short(this.ab.slice(this.offset, this.offset + 2));
|
||||
this.offset += 2;
|
||||
return num;
|
||||
}
|
||||
};
|
||||
|
||||
OffsetBuffer.prototype.readBytes = function(length) {
|
||||
var bytes = this.ab.slice(this.offset, this.offset + length);
|
||||
this.offset += length;
|
||||
return bytes;
|
||||
}
|
||||
};
|
||||
|
||||
// Length of the internal buffer
|
||||
OffsetBuffer.prototype.getLength = function() {
|
||||
return this.ab.byteLength;
|
||||
}
|
||||
};
|
||||
|
||||
// Number of bytes remaining from the current offset
|
||||
OffsetBuffer.prototype.bytesAvailable = function() {
|
||||
return (this.getLength() - this.offset);
|
||||
}
|
||||
return this.getLength() - this.offset;
|
||||
};
|
||||
|
||||
// ArrayBuffer -> JS type conversion methods
|
||||
OffsetBuffer.prototype.ab2str = function(buf) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buf));
|
||||
}
|
||||
};
|
||||
|
||||
// These create Javascript Numbers
|
||||
OffsetBuffer.prototype.ab2int = function(buf) {
|
||||
return new Int32Array(buf)[0];
|
||||
}
|
||||
};
|
||||
|
||||
OffsetBuffer.prototype.ab2uint = function(buf) {
|
||||
return new Uint32Array(buf)[0];
|
||||
}
|
||||
};
|
||||
|
||||
OffsetBuffer.prototype.ab2short = function(buf) {
|
||||
return new Int16Array(buf)[0];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
var Point = function(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
};
|
||||
|
||||
var Rectangle = function(x, y, width, height) {
|
||||
this.x = x;
|
||||
|
@ -27,10 +27,8 @@ var Rectangle = function(x, y, width, height) {
|
|||
this.right = x + width;
|
||||
this.top = y;
|
||||
this.bottom = y + height;
|
||||
}
|
||||
};
|
||||
|
||||
Rectangle.prototype.intersects = function(other) {
|
||||
return !(this.left > other.right ||
|
||||
this.right < other.left ||
|
||||
this.top > other.bottom ||
|
||||
this.bottom < other.top);
|
||||
}
|
||||
return !(this.left > other.right || this.right < other.left || this.top > other.bottom || this.bottom < other.top);
|
||||
};
|
||||
|
|
|
@ -21,40 +21,40 @@ var Timer = function() {
|
|||
var trials = [];
|
||||
var last_trial = 0;
|
||||
var start_time = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Timer.prototype.time = function() {
|
||||
return (new Date()).getTime();
|
||||
}
|
||||
return Date.now();
|
||||
};
|
||||
|
||||
Timer.prototype.start = function() {
|
||||
start_time = this.time();
|
||||
}
|
||||
};
|
||||
|
||||
Timer.prototype.stop = function() {
|
||||
end = this.time();
|
||||
last_trial = end - start_time;
|
||||
trials.push(last_trial);
|
||||
}
|
||||
};
|
||||
|
||||
Timer.prototype.count = function() {
|
||||
return trials.length;
|
||||
}
|
||||
};
|
||||
|
||||
Timer.prototype.average = function() {
|
||||
sum = 0;
|
||||
for(i = 0; i < this.count(); i++) {
|
||||
for (i = 0; i < this.count(); i++) {
|
||||
sum += trials[i];
|
||||
}
|
||||
return sum / this.count();
|
||||
}
|
||||
};
|
||||
|
||||
Timer.prototype.print = function(element) {
|
||||
text = "Trial: " + last_trial + "ms" +
|
||||
text = "Trial: " + last_trial + "ms" +
|
||||
"<br />\nTrials: " + this.count() + ", Avg: " + this.average() + "ms";
|
||||
if(element) {
|
||||
if (element) {
|
||||
$(element).html(text);
|
||||
} else {
|
||||
console.log(text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Reference in a new issue