2013-10-28 20:00:20 +00:00
// Copyright (C) 2013 Massachusetts Institute of Technology
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// Scratch HTML5 Player
// Sprite.js
// Tim Mickel, July 2011 - March 2012
// The Sprite provides the interface and implementation for Scratch sprite-control
'use strict' ;
var Sprite = function ( data ) {
2013-11-01 22:44:51 -04:00
if ( ! this . data ) {
2013-10-28 20:00:20 +00:00
this . data = data ;
}
// Public variables used for Scratch-accessible data.
2013-11-01 22:44:51 -04:00
this . visible = typeof ( this . data . visible ) == "undefined" ? true : data . visible ;
2013-10-28 20:00:20 +00:00
this . scratchX = data . scratchX || 0 ;
this . scratchY = data . scratchY || 0 ;
this . scale = data . scale || 1.0 ;
this . direction = data . direction || 90 ;
this . rotation = ( data . direction - 90 ) || 0 ;
this . rotationStyle = data . rotationStyle || 'normal' ;
this . isFlipped = data . direction < 0 && data . rotationStyle == 'leftRight' ;
this . costumes = data . costumes || [ ] ;
2014-07-25 10:03:05 +01:00
this . currentCostumeIndex = Math . floor ( data . currentCostumeIndex ) || 0 ;
2013-10-28 20:00:20 +00:00
this . previousCostumeIndex = - 1 ;
this . objName = data . objName || '' ;
this . variables = { } ;
if ( data . variables ) {
for ( var i = 0 ; i < data . variables . length ; i ++ ) {
this . variables [ data . variables [ i ] [ 'name' ] ] = data . variables [ i ] [ 'value' ] ;
}
}
this . lists = { } ;
if ( data . lists ) {
for ( var i = 0 ; i < data . lists . length ; i ++ ) {
this . lists [ data . lists [ i ] [ 'listName' ] ] = data . lists [ i ] ;
}
}
// Used for the pen
this . penIsDown = false ;
this . penWidth = 1 ;
this . penHue = 120 ; // blue
this . penShade = 50 ; // full brightness and saturation
this . penColorCache = 0x0000FF ;
// Used for layering
if ( ! this . z ) this . z = io . getCount ( ) ;
// HTML element for the talk bubbles
this . talkBubble = null ;
this . talkBubbleBox = null ;
this . talkBubbleStyler = null ;
this . talkBubbleOn = false ;
2014-03-08 00:46:59 -07:00
// HTML element for the ask bubbles
this . askInput = null ;
this . askInputField = null ;
this . askInputButton = null ;
this . askInputOn = false ;
2013-10-28 20:00:20 +00:00
// Internal variables used for rendering meshes.
this . textures = [ ] ;
this . materials = [ ] ;
this . geometries = [ ] ;
this . mesh = null ;
// Sound buffers and data
this . sounds = { } ;
if ( data . sounds ) {
for ( var i = 0 ; i < data . sounds . length ; i ++ ) {
this . sounds [ data . sounds [ i ] [ 'soundName' ] ] = data . sounds [ i ] ;
}
}
this . soundsLoaded = 0 ;
this . instrument = 1 ;
2013-11-03 07:10:45 -05:00
// Image effects
2013-11-14 23:44:00 -05:00
this . filters = {
color : 0 ,
fisheye : 0 ,
whirl : 0 ,
pixelate : 0 ,
mosaic : 0 ,
brightness : 0 ,
ghost : 0
} ;
2013-11-02 10:19:44 -04:00
2013-10-28 20:00:20 +00:00
// Incremented when images are loaded by the browser.
this . costumesLoaded = 0 ;
// Stacks to be pushed to the interpreter and run
this . stacks = [ ] ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
// Attaches a Sprite (<img>) to a Scratch scene
Sprite . prototype . attach = function ( scene ) {
// Create textures and materials for each of the costumes.
for ( var c in this . costumes ) {
this . textures [ c ] = document . createElement ( 'img' ) ;
$ ( this . textures [ c ] )
. load ( [ this , c ] , function ( evo ) {
var sprite = evo . handleObj . data [ 0 ] ;
var c = evo . handleObj . data [ 1 ] ;
sprite . costumesLoaded += 1 ;
sprite . updateCostume ( ) ;
2013-11-01 22:44:51 -04:00
$ ( sprite . textures [ c ] ) . css ( 'display' , sprite . currentCostumeIndex == c ? 'inline' : 'none' ) ;
2013-10-28 20:00:20 +00:00
$ ( sprite . textures [ c ] ) . css ( 'position' , 'absolute' ) . css ( 'left' , '0px' ) . css ( 'top' , '0px' ) ;
$ ( sprite . textures [ c ] ) . bind ( 'dragstart' , function ( evt ) { evt . preventDefault ( ) ; } )
2014-04-09 01:17:50 -07:00
. bind ( 'selectstart' , function ( evt ) { evt . preventDefault ( ) ; } )
. bind ( 'touchend' , function ( evt ) { sprite . onClick ( evt ) ; $ ( this ) . addClass ( 'touched' ) ; } )
. click ( function ( evt ) {
if ( ! $ ( this ) . hasClass ( 'touched' ) ) {
sprite . onClick ( evt ) ;
} else {
$ ( this ) . removeClass ( 'touched' ) ;
}
2013-10-28 20:00:20 +00:00
} ) ;
scene . append ( $ ( sprite . textures [ c ] ) ) ;
} )
2014-05-20 16:29:34 +01:00
. attr ( {
2014-06-01 09:20:19 +01:00
'crossOrigin' : 'anonymous' ,
2014-05-20 16:29:34 +01:00
'src' : io . asset _base + this . costumes [ c ] . baseLayerMD5 + io . asset _suffix
} ) ;
2013-10-28 20:00:20 +00:00
}
this . mesh = this . textures [ this . currentCostumeIndex ] ;
this . updateLayer ( ) ;
this . updateVisible ( ) ;
this . updateTransform ( ) ;
2014-03-08 00:46:59 -07:00
if ( ! this . isStage ) {
2014-04-09 01:17:50 -07:00
this . talkBubble = $ ( '<div class="bubble-container"></div>' ) ;
this . talkBubble . css ( 'display' , 'none' ) ;
this . talkBubbleBox = $ ( '<div class="bubble"></div>' ) ;
this . talkBubbleStyler = $ ( '<div class="bubble-say"></div>' ) ;
this . talkBubble . append ( this . talkBubbleBox ) ;
this . talkBubble . append ( this . talkBubbleStyler ) ;
2014-03-08 00:46:59 -07:00
}
this . askInput = $ ( '<div class="ask-container"></div>' ) ;
this . askInput . css ( 'display' , 'none' ) ;
this . askInputField = $ ( '<div class="ask-field"></div>' ) ;
2014-03-09 11:59:38 -06:00
this . askInputTextField = $ ( '<input type="text" class="ask-text-field"></input>' ) ;
2014-03-08 00:46:59 -07:00
this . askInputField . append ( this . askInputTextField ) ;
this . askInputButton = $ ( '<div class="ask-button"></div>' ) ;
2014-03-09 11:59:38 -06:00
this . bindDoAskButton ( ) ;
2014-03-08 00:46:59 -07:00
this . askInput . append ( this . askInputField ) ;
this . askInput . append ( this . askInputButton ) ;
2013-11-01 22:44:51 -04:00
2013-10-28 20:00:20 +00:00
runtime . scene . append ( this . talkBubble ) ;
2014-03-08 00:46:59 -07:00
runtime . scene . append ( this . askInput ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
// Load sounds from the server and buffer them
Sprite . prototype . loadSounds = function ( ) {
2014-04-09 01:17:50 -07:00
var self = this ;
2013-11-01 22:44:51 -04:00
$ . each ( this . sounds , function ( index , sound ) {
2014-04-09 01:17:50 -07:00
io . soundRequest ( sound , self ) ;
2013-10-28 20:00:20 +00:00
} ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
// True when all the costumes have been loaded
Sprite . prototype . isLoaded = function ( ) {
2013-11-01 22:44:51 -04:00
return this . costumesLoaded == this . costumes . length && this . soundsLoaded == Object . keys ( this . sounds ) . length ;
} ;
2013-10-28 20:00:20 +00:00
// Step methods
Sprite . prototype . showCostume = function ( costume ) {
if ( costume < 0 ) {
costume += this . costumes . length ;
}
if ( ! this . textures [ costume ] ) {
this . currentCostumeIndex = 0 ;
}
else {
this . currentCostumeIndex = costume ;
}
this . updateCostume ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . indexOfCostumeNamed = function ( name ) {
2013-11-01 22:44:51 -04:00
for ( var i in this . costumes ) {
2013-10-28 20:00:20 +00:00
var c = this . costumes [ i ] ;
2013-11-01 22:44:51 -04:00
if ( c [ 'costumeName' ] == name ) {
2013-10-28 20:00:20 +00:00
return i ;
}
}
return null ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . showCostumeNamed = function ( name ) {
var index = this . indexOfCostumeNamed ( name ) ;
2013-11-01 22:44:51 -04:00
if ( ! index ) return ;
2013-10-28 20:00:20 +00:00
this . showCostume ( index ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . updateCostume = function ( ) {
2013-11-01 22:44:51 -04:00
if ( ! this . textures [ this . currentCostumeIndex ] ) {
2013-10-28 20:00:20 +00:00
this . currentCostumeIndex = 0 ;
}
$ ( this . mesh ) . css ( 'display' , 'none' ) ;
this . mesh = this . textures [ this . currentCostumeIndex ] ;
this . updateVisible ( ) ;
this . updateTransform ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . onClick = function ( evt ) {
// TODO - needs work!!
2013-11-04 18:52:33 -08:00
// We don't need boxOffset anymore.
var mouseX = runtime . mousePos [ 0 ] + 240 ;
var mouseY = 180 - runtime . mousePos [ 1 ] ;
2013-10-28 20:00:20 +00:00
2014-09-06 02:35:11 -05:00
var canv = document . createElement ( 'canvas' ) ;
canv . width = 480 ;
canv . height = 360 ;
var ctx = canv . getContext ( '2d' ) ;
var drawWidth = this . textures [ this . currentCostumeIndex ] . width ;
var drawHeight = this . textures [ this . currentCostumeIndex ] . height ;
var scale = this . scale / ( this . costumes [ this . currentCostumeIndex ] . bitmapResolution || 1 ) ;
var rotationCenterX = this . costumes [ this . currentCostumeIndex ] . rotationCenterX ;
var rotationCenterY = this . costumes [ this . currentCostumeIndex ] . rotationCenterY ;
ctx . translate ( 240 + this . scratchX , 180 - this . scratchY ) ;
ctx . rotate ( this . rotation * Math . PI / 180.0 ) ;
ctx . scale ( scale , scale ) ;
ctx . translate ( - rotationCenterX , - rotationCenterY ) ;
ctx . drawImage ( this . mesh , 0 , 0 ) ;
var alpha ;
try {
2014-02-19 22:53:32 -05:00
var idata = ctx . getImageData ( mouseX , mouseY , 1 , 1 ) . data ;
2014-09-06 02:35:11 -05:00
alpha = idata [ 3 ] ;
} catch ( e ) {
// as of 2014-09-06, WebKit/Safari still throws a security exception trying to read
// image data from a canvas after any svg has been draw to it. Version 7.0.6 (9537.78.2)
// WebKit-nightly (SVN-r173347) does not have this issue.
alpha = 1 ;
2013-10-28 20:00:20 +00:00
}
if ( alpha > 0 ) {
// Start clicked hats if the pixel is non-transparent
runtime . startClickedHats ( this ) ;
} else {
// Otherwise, move back a layer and trigger the click event
$ ( this . mesh ) . hide ( ) ;
2014-02-19 22:53:32 -05:00
var bb = $ ( '#container' ) [ 0 ] . getBoundingClientRect ( ) ;
var underElement = document . elementFromPoint ( bb . left + mouseX , bb . top + mouseY ) ;
2013-10-28 20:00:20 +00:00
$ ( underElement ) . click ( ) ;
$ ( this . mesh ) . show ( ) ;
}
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . setVisible = function ( v ) {
2014-04-09 01:17:50 -07:00
this . visible = v ;
this . updateVisible ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . updateLayer = function ( ) {
$ ( this . mesh ) . css ( 'z-index' , this . z ) ;
2014-03-10 21:04:56 -06:00
if ( this . talkBubble ) this . talkBubble . css ( 'z-index' , this . z ) ;
2014-03-08 00:46:59 -07:00
if ( this . askInput ) this . askInput . css ( 'z-index' , this . z ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . updateVisible = function ( ) {
2013-11-01 22:44:51 -04:00
$ ( this . mesh ) . css ( 'display' , this . visible ? 'inline' : 'none' ) ;
2014-03-08 00:46:59 -07:00
if ( this . talkBubbleOn ) this . talkBubble . css ( 'display' , this . visible ? 'inline-block' : 'none' ) ;
if ( this . askInputOn ) this . askInput . css ( 'display' , this . visible ? 'inline-block' : 'none' ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . updateTransform = function ( ) {
var texture = this . textures [ this . currentCostumeIndex ] ;
var resolution = this . costumes [ this . currentCostumeIndex ] . bitmapResolution || 1 ;
2013-11-01 22:44:51 -04:00
2013-10-28 20:00:20 +00:00
var drawWidth = texture . width * this . scale / resolution ;
var drawHeight = texture . height * this . scale / resolution ;
2013-11-01 22:44:51 -04:00
2013-10-28 20:00:20 +00:00
var rotationCenterX = this . costumes [ this . currentCostumeIndex ] . rotationCenterX ;
var rotationCenterY = this . costumes [ this . currentCostumeIndex ] . rotationCenterY ;
2013-11-01 22:44:51 -04:00
2013-10-28 20:00:20 +00:00
var drawX = this . scratchX + ( 480 / 2 ) - rotationCenterX ;
var drawY = - this . scratchY + ( 360 / 2 ) - rotationCenterY ;
var scaleXprepend = '' ;
if ( this . isFlipped ) {
scaleXprepend = '-' ; // For a leftRight flip, we add a minus
// sign to the X scale.
}
2014-04-09 01:17:50 -07:00
$ ( 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' ,
'translatex(' + drawX + 'px) ' +
'translatey(' + drawY + 'px) ' +
'rotate(' + this . rotation + 'deg) ' +
'scaleX(' + scaleXprepend + this . scale + ') scaleY(' + this . scale / resolution + ')'
) ;
$ ( this . mesh ) . css (
'-webkit-transform' ,
'translatex(' + drawX + 'px) ' +
'translatey(' + drawY + 'px) ' +
'rotate(' + this . rotation + 'deg) ' +
'scaleX(' + scaleXprepend + ( this . scale / resolution ) + ') scaleY(' + ( this . scale / resolution ) + ')'
) ;
2013-11-01 22:44:51 -04:00
$ ( 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' ) ;
2013-10-28 20:00:20 +00:00
$ ( this . mesh ) . css ( 'transform-origin' , rotationCenterX + 'px ' + rotationCenterY + 'px' ) ;
// Don't forget to update the talk bubble.
if ( this . talkBubble ) {
var xy = this . getTalkBubbleXY ( ) ;
this . talkBubble . css ( 'left' , xy [ 0 ] + 'px' ) ;
this . talkBubble . css ( 'top' , xy [ 1 ] + 'px' ) ;
}
2013-11-01 22:44:51 -04:00
this . updateLayer ( ) ;
} ;
2013-10-28 20:00:20 +00:00
2013-11-14 23:44:00 -05:00
Sprite . prototype . updateFilters = function ( ) {
$ ( this . mesh ) . css ( 'opacity' , 1 - this . filters . ghost / 100 ) ;
2014-04-09 01:17:50 -07:00
$ ( this . mesh ) . css (
'-webkit-filter' ,
'hue-rotate(' + ( this . filters . color * 1.8 ) + 'deg) ' +
'brightness(' + ( this . filters . brightness < 0 ? this . filters . brightness / 100 + 1 : Math . min ( 2.5 , this . filters . brightness * . 015 + 1 ) ) + ')'
) ;
2013-11-14 23:44:00 -05:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . getTalkBubbleXY = function ( ) {
var texture = this . textures [ this . currentCostumeIndex ] ;
var drawWidth = texture . width * this . scale ;
var drawHeight = texture . height * this . scale ;
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 ;
return [ drawX + drawWidth , drawY - drawHeight / 2 ] ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . showBubble = function ( text , type ) {
var xy = this . getTalkBubbleXY ( ) ;
this . talkBubbleOn = true ;
this . talkBubble . css ( 'z-index' , this . z ) ;
this . talkBubble . css ( 'left' , xy [ 0 ] + 'px' ) ;
this . talkBubble . css ( 'top' , xy [ 1 ] + 'px' ) ;
2014-03-09 11:59:38 -06:00
this . talkBubbleBox . removeClass ( 'say-think-border' ) ;
this . talkBubbleBox . removeClass ( 'ask-border' ) ;
2014-04-09 01:17:50 -07:00
2013-10-28 20:00:20 +00:00
this . talkBubbleStyler . removeClass ( 'bubble-say' ) ;
this . talkBubbleStyler . removeClass ( 'bubble-think' ) ;
2014-03-08 00:46:59 -07:00
this . talkBubbleStyler . removeClass ( 'bubble-ask' ) ;
2013-11-01 22:44:51 -04:00
if ( type == 'say' ) {
2014-03-08 00:46:59 -07:00
this . talkBubbleBox . addClass ( 'say-think-border' ) ;
2013-11-01 22:44:51 -04:00
this . talkBubbleStyler . addClass ( 'bubble-say' ) ;
} else if ( type == 'think' ) {
2014-03-08 00:46:59 -07:00
this . talkBubbleBox . addClass ( 'say-think-border' ) ;
2013-11-01 22:44:51 -04:00
this . talkBubbleStyler . addClass ( 'bubble-think' ) ;
2014-03-08 00:46:59 -07:00
} else if ( type == 'doAsk' ) {
this . talkBubbleBox . addClass ( 'ask-border' ) ;
this . talkBubbleStyler . addClass ( 'bubble-ask' ) ;
2013-11-01 22:44:51 -04:00
}
if ( this . visible ) {
2013-10-28 20:00:20 +00:00
this . talkBubble . css ( 'display' , 'inline-block' ) ;
2013-11-01 22:44:51 -04:00
}
2013-10-28 20:00:20 +00:00
this . talkBubbleBox . html ( text ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . hideBubble = function ( ) {
this . talkBubbleOn = false ;
this . talkBubble . css ( 'display' , 'none' ) ;
2013-11-01 22:44:51 -04:00
} ;
2014-03-08 00:46:59 -07:00
Sprite . prototype . showAsk = function ( ) {
this . askInputOn = true ;
this . askInput . css ( 'z-index' , this . z ) ;
this . askInput . css ( 'left' , '15px' ) ;
this . askInput . css ( 'right' , '15px' ) ;
this . askInput . css ( 'bottom' , '7px' ) ;
this . askInput . css ( 'height' , '25px' ) ;
if ( this . visible ) {
this . askInput . css ( 'display' , 'inline-block' ) ;
this . askInputTextField . focus ( ) ;
}
} ;
Sprite . prototype . hideAsk = function ( ) {
this . askInputOn = false ;
2014-03-09 11:59:38 -06:00
this . askInputTextField . val ( '' ) ;
2014-03-08 00:46:59 -07:00
this . askInput . css ( 'display' , 'none' ) ;
} ;
2014-03-09 11:59:38 -06:00
Sprite . prototype . bindDoAskButton = function ( ) {
2014-04-09 01:17:50 -07:00
var self = this ;
this . askInputButton . on ( "keypress click" , function ( e ) {
var eType = e . type ;
if ( eType === 'click' || ( eType === 'keypress' && e . which === 13 ) ) {
var stage = interp . targetStage ( ) ;
stage . askAnswer = $ ( self . askInputTextField ) . val ( ) ;
self . hideBubble ( ) ;
self . hideAsk ( ) ;
interp . activeThread . paused = false ;
}
} ) ;
2014-03-09 11:59:38 -06:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . setXY = function ( x , y ) {
this . scratchX = x ;
this . scratchY = y ;
this . updateTransform ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . setDirection = function ( d ) {
var rotation ;
d = d % 360
if ( d < 0 ) d += 360 ;
2013-11-01 22:44:51 -04:00
this . direction = d > 180 ? d - 360 : d ;
2013-10-28 20:00:20 +00:00
if ( this . rotationStyle == 'normal' ) {
rotation = ( this . direction - 90 ) % 360 ;
} else if ( this . rotationStyle == 'leftRight' ) {
if ( ( ( this . direction - 90 ) % 360 ) >= 0 ) {
this . isFlipped = false ;
} else {
this . isFlipped = true ;
}
rotation = 0 ;
} else {
rotation = 0 ;
}
this . rotation = rotation ;
this . updateTransform ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . setRotationStyle = function ( r ) {
this . rotationStyle = r ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . getSize = function ( ) {
return this . scale * 100 ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . setSize = function ( percent ) {
var newScale = percent / 100.0 ;
newScale = Math . max ( 0.05 , Math . min ( newScale , 100 ) ) ;
this . scale = newScale ;
this . updateTransform ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
// Move functions
Sprite . prototype . keepOnStage = function ( ) {
var x = this . scratchX + 240 ;
var y = 180 - this . scratchY ;
var myBox = this . getRect ( ) ;
var inset = - Math . min ( 18 , Math . min ( myBox . width , myBox . height ) / 2 ) ;
var edgeBox = new Rectangle ( inset , inset , 480 - ( 2 * inset ) , 360 - ( 2 * inset ) ) ;
if ( myBox . intersects ( edgeBox ) ) return ; // sprite is sufficiently on stage
if ( myBox . right < edgeBox . left ) x += edgeBox . left - myBox . right ;
if ( myBox . left > edgeBox . right ) x -= myBox . left - edgeBox . right ;
if ( myBox . bottom < edgeBox . top ) y += edgeBox . top - myBox . bottom ;
if ( myBox . top > edgeBox . bottom ) y -= myBox . top - edgeBox . bottom ;
this . scratchX = x - 240 ;
this . scratchY = 180 - y ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . getRect = function ( ) {
var cImg = this . textures [ this . currentCostumeIndex ] ;
var x = this . scratchX + 240 - ( cImg . width / 2.0 ) ;
var y = 180 - this . scratchY - ( cImg . height / 2.0 ) ;
var myBox = new Rectangle ( x , y , cImg . width , cImg . height ) ;
return myBox ;
2013-11-01 22:44:51 -04:00
} ;
// Pen functions
2013-10-28 20:00:20 +00:00
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 ;
2013-11-01 22:44:51 -04:00
} ;
Sprite . prototype . setPenHue = function ( n ) {
2013-10-28 20:00:20 +00:00
this . penHue = n % 200 ;
if ( this . penHue < 0 ) this . penHue += 200 ;
this . updateCachedPenColor ( ) ;
2013-11-01 22:44:51 -04:00
} ;
Sprite . prototype . setPenShade = function ( n ) {
2013-10-28 20:00:20 +00:00
this . penShade = n % 200 ;
if ( this . penShade < 0 ) this . penShade += 200 ;
this . updateCachedPenColor ( ) ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . updateCachedPenColor = function ( ) {
var c = Color . fromHSV ( ( this . penHue * 180.0 ) / 100.0 , 1 , 1 ) ;
2013-11-01 22:44:51 -04:00
var shade = this . penShade > 100 ? 200 - this . penShade : this . penShade ; // range 0..100
2013-10-28 20:00:20 +00:00
if ( shade < 50 ) {
this . penColorCache = Color . mixRGB ( 0 , c , ( 10 + shade ) / 60.0 ) ;
} else {
this . penColorCache = Color . mixRGB ( c , 0xFFFFFF , ( shade - 50 ) / 60 ) ;
}
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . stamp = function ( canvas , opacity ) {
2014-07-25 11:43:59 +01:00
var resolution = this . costumes [ this . currentCostumeIndex ] . bitmapResolution || 1 ;
var drawWidth = this . textures [ this . currentCostumeIndex ] . width ;
var drawHeight = this . textures [ this . currentCostumeIndex ] . height ;
2013-10-28 20:00:20 +00:00
var drawX = this . scratchX + ( 480 / 2 ) ;
var drawY = - this . scratchY + ( 360 / 2 ) ;
2014-07-25 11:43:59 +01:00
var rotationCenterX = this . costumes [ this . currentCostumeIndex ] . rotationCenterX ;
var rotationCenterY = this . costumes [ this . currentCostumeIndex ] . rotationCenterY ;
2013-10-28 20:00:20 +00:00
canvas . globalAlpha = opacity / 100.0 ;
canvas . save ( ) ;
canvas . translate ( drawX , drawY ) ;
2014-07-25 11:43:59 +01:00
canvas . scale ( this . scale / resolution , this . scale / resolution ) ;
2013-10-28 20:00:20 +00:00
canvas . rotate ( this . rotation * Math . PI / 180.0 ) ;
2014-07-25 11:43:59 +01:00
canvas . drawImage ( this . mesh , - rotationCenterX , - rotationCenterY , drawWidth , drawHeight ) ;
2013-10-28 20:00:20 +00:00
canvas . restore ( ) ;
canvas . globalAlpha = 1 ;
2013-11-01 22:44:51 -04:00
} ;
2013-10-28 20:00:20 +00:00
Sprite . prototype . soundNamed = function ( name ) {
2013-11-01 22:44:51 -04:00
if ( name in this . sounds && this . sounds [ name ] . buffer ) {
2013-10-28 20:00:20 +00:00
return this . sounds [ name ] ;
2013-11-01 22:44:51 -04:00
} else if ( name in runtime . stage . sounds && runtime . stage . sounds [ name ] . buffer ) {
2013-10-28 20:00:20 +00:00
return runtime . stage . sounds [ name ] ;
2013-11-01 22:44:51 -04:00
}
2013-10-28 20:00:20 +00:00
return null ;
2013-11-01 22:44:51 -04:00
} ;
2013-11-02 18:16:18 -04:00
Sprite . prototype . resetFilters = function ( ) {
2013-11-14 23:44:00 -05:00
this . filters = {
color : 0 ,
fisheye : 0 ,
whirl : 0 ,
pixelate : 0 ,
mosaic : 0 ,
brightness : 0 ,
ghost : 0
} ;
this . updateFilters ( ) ;
} ;