diff --git a/.gitignore b/.gitignore index d5fd3c1..34d99e1 100755 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules/ .DS_Store lab/audio/ lab/filter/ -static/items/rube/*-backups \ No newline at end of file +static/items/rube/*-backups +build/ diff --git a/README.md b/README.md index cae17cf..956e9b6 100755 --- a/README.md +++ b/README.md @@ -2,8 +2,10 @@ chuck.js ======== Physical JavaScript Action Browser Multiplayer Game - it will be awesome! - -[![Foo](http://25.media.tumblr.com/8249dcd3bdb176686421d1914937db1c/tumblr_mzc1ejGxNC1ry8awho1_400.png)](http://chuck-game.tumblr.com/ "Screenshot of chuck.js - click to visit our development blog!") + + + + Follow the development at http://chuck-game.tumblr.com/ diff --git a/app/Bootstrap/HttpServer.js b/app/Bootstrap/HttpServer.js index e74a46a..a1293b2 100755 --- a/app/Bootstrap/HttpServer.js +++ b/app/Bootstrap/HttpServer.js @@ -1,92 +1,74 @@ define([ - 'http', - 'node-static', - 'Server/Api' + 'express', + 'http', + 'path', + 'Server/Api', + 'fs' ], -function (http, nodeStatic, Api) { +function (express, http, path, Api, fs) { + + "use strict"; function HttpServer (options, coordinator) { options.port = options.port || 1234; - options.caching = typeof options.caching != 'undefined' ? options.caching : 3600; options.rootDirectory = options.rootDirectory || './'; - this.server = null; this.api = new Api(coordinator); - + this.app = express(); + this.server = http.createServer(this.app); this.init(options); } HttpServer.prototype.init = function (options) { var self = this; + var app = this.app; - var fileServer = new nodeStatic.Server(options.rootDirectory, { cache: options.caching }); + // Serve static files + app.use('/static', express.static(path.join(options.rootDirectory, 'static'))); + app.use('/app', express.static(path.join(options.rootDirectory, 'app'))); - this.server = http.createServer( - function (req, res) { - - var fullBody = ''; - req.addListener('data', function(chunk) { // doesn't work on Jeenas computer without this - fullBody += chunk.toString(); - }); + // Serve index.html at root + app.get('/', function(req, res) { + res.sendFile(path.resolve(options.rootDirectory, 'static/html/index.html')); + }); - req.addListener('error', function(err) { - console.log(''); - }); - - - req.addListener('end', function () { - - switch(true) { - case req.url == '/': - fileServer.serveFile('./static/html/index.html', 200, {}, req, res); - console.checkpoint('HTTP Server serves index'); - break; - - case req.url == '/game.html': - fileServer.serveFile('./static/html/game.html', 200, {}, req, res); - console.checkpoint('HTTP Server serves game'); - break; - - case req.url == '/client.js': - fileServer.serveFile('./client.js', 200, {}, req, res); - break; - - case req.url == '/require.js': - fileServer.serveFile('./node_modules/requirejs/require.js', 200, {}, req, res); - break; - - case req.url == '/screenfull.js': - fileServer.serveFile('./node_modules/screenfull/dist/screenfull.js', 200, {}, req, res); - break; - - case req.url == '/api': - self.api.handleCall(fullBody); - var status = self.api.isError ? 400 : 200; - res.writeHead(status, {"Content-Type": self.api.getContentType()}); - res.end(self.api.getOutput()); - self.api.isError = false; - break; - - case new RegExp(/^\/app/).test(req.url): - fileServer.serve(req, res, function () { - self.handleFileError(res) - }); - break; - - case new RegExp(/^\/static/).test(req.url): - fileServer.serve(req, res, function () { - self.handleFileError(res) - }); - break; - - default: - self.handleFileError(res); - break; - } - }); + // Serve client.js and minified version + app.get('/client.js', function(req, res) { + if (process.env.NODE_ENV === 'production' && fs.existsSync(path.resolve(options.rootDirectory, 'build/client.min.js'))) { + res.sendFile(path.resolve(options.rootDirectory, 'build/client.min.js')); + } else { + res.sendFile(path.resolve(options.rootDirectory, 'client.js')); } - ); + }); + app.get('/client.min.js', function(req, res) { + res.sendFile(path.resolve(options.rootDirectory, 'build/client.min.js')); + }); + + // Serve require.js, screenfull.js, chart.js from node_modules + app.get('/require.js', function(req, res) { + res.sendFile(path.resolve(options.rootDirectory, 'node_modules/requirejs/require.js')); + }); + app.get('/screenfull.js', function(req, res) { + res.sendFile(path.resolve(options.rootDirectory, 'static/vendor/screenfull.js')); + }); + app.get('/chart.js', function(req, res) { + // Chart.js v4 uses 'dist/chart.umd.js' + res.sendFile(path.resolve(options.rootDirectory, 'node_modules/chart.js/dist/chart.umd.js')); + }); + + // API endpoint + app.post('/api', express.text({type: '*/*'}), function(req, res) { + self.api.handleCall(req.body); + var status = self.api.isError ? 400 : 200; + res.status(status).type(self.api.getContentType()).send(self.api.getOutput()); + self.api.isError = false; + }); + + // 404 handler + app.use(function(req, res) { + res.status(404).send('

404 not ... found

'); + }); this.server.once('error', function(err) { if(err.code == 'EADDRINUSE') { @@ -97,7 +79,6 @@ function (http, nodeStatic, Api) { }); this.server.listen(options.port); - console.checkpoint('start HTTP server'); } @@ -105,10 +86,5 @@ function (http, nodeStatic, Api) { return this.server; } - HttpServer.prototype.handleFileError = function (res) { - res.writeHead(404, {'Content-Type': 'text/html'}); - res.end('

404 not ... found

'); - } - return HttpServer; }); \ No newline at end of file diff --git a/app/Bootstrap/Socket.js b/app/Bootstrap/Socket.js index 460ac73..c9a652e 100755 --- a/app/Bootstrap/Socket.js +++ b/app/Bootstrap/Socket.js @@ -4,29 +4,23 @@ define([ function (io) { - function Socket (server, options, coordinator) { - options.logLevel = typeof options.logLevel != 'undefined' - ? options.logLevel - : 0; - - this.coordinator = coordinator; - this.socket = io.listen(server); + "use strict"; + function Socket (server, options, coordinator) { + this.coordinator = coordinator; + this.io = io(server, { + // No more 'log level' or 'transports' in v4 + // Add any v4-compatible options here if needed + }); this.init(options); } Socket.prototype.init = function (options) { - var self = this; - this.socket.configure('development', function () { - this.set('log level', options.logLevel); - }); - - this.socket.on('connection', function (user) { + this.io.on('connection', function (socket) { console.checkpoint('socket receiving connection'); - self.onConnection(user); + self.onConnection(socket); }); - console.checkpoint('start Socket Listener'); } diff --git a/app/Game/Asset/RubeDoll.json b/app/Game/Asset/RubeDoll.json new file mode 100644 index 0000000..0ea7dc3 --- /dev/null +++ b/app/Game/Asset/RubeDoll.json @@ -0,0 +1,783 @@ +{ + "allowSleep" : true, + "autoClearForces" : true, + "body" : + [ + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.04692989960312843, + 0.04692989960312843, + -0.04693000018596649, + -0.04693000018596649 + ], + "y" : + [ + -0.1895969957113266, + 0.1895969957113266, + 0.1895969957113266, + -0.1895969957113266 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0004525936674326658, + "massData-center" : + { + "x" : -5.029141902923584e-08, + "y" : 0 + }, + "massData-mass" : 0.03559110686182976, + "name" : "upperLeftArm", + "position" : + { + "x" : -0.1165359988808632, + "y" : 0.9012569785118103 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture2", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.1366278976202011, + 0.1366278976202011, + -0.1360991001129150, + -0.1360991001129150 + ], + "y" : + [ + -0.3788780868053436, + 0.3830279111862183, + 0.3830279111862183, + -0.3788780868053436 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.01134084537625313, + "massData-center" : + { + "x" : 0.0002643987536430359, + "y" : 0.002074912190437317 + }, + "massData-mass" : 0.2077923566102982, + "name" : "chest", + "position" : + { + "x" : 0.0007875636219978333, + "y" : 0.7995355725288391 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "circle" : + { + "center" : + { + "x" : -0.01561669446527958, + "y" : 0.004700659774243832 + }, + "radius" : 0.2268356680870056 + }, + "density" : 0.2204959988594055, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture1" + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0009264730615541339, + "massData-center" : + { + "x" : -0.01561669446527958, + "y" : 0.004700659774243832 + }, + "massData-mass" : 0.03564291819930077, + "name" : "head", + "position" : + { + "x" : 0.02309736609458923, + "y" : 1.497289657592773 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.04693000018596649, + 0.04693000018596649, + -0.04693005979061127, + -0.04693005979061127 + ], + "y" : + [ + -0.1159216761589050, + 0.1159217953681946, + 0.1159217953681946, + -0.1159216761589050 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0001134485355578363, + "massData-center" : + { + "x" : -2.980232238769531e-08, + "y" : 5.960464477539062e-08 + }, + "massData-mass" : 0.02176084183156490, + "name" : "lowerRightArm", + "position" : + { + "x" : 0.1183081120252609, + "y" : 0.6842151284217834 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.07039500027894974, + 0.07039500027894974, + -0.07039500027894974, + -0.07039500027894974 + ], + "y" : + [ + -0.09294360131025314, + 0.1277720034122467, + 0.1277720034122467, + -0.09294360131025314 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0001869037223514169, + "massData-center" : + { + "x" : 0, + "y" : 0.01741420105099678 + }, + "massData-mass" : 0.03107454814016819, + "name" : "lowerLeftLeg", + "position" : + { + "x" : -0.03829947486519814, + "y" : 0.09799569845199585 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.04693000763654709, + 0.04693000763654709, + -0.04692991077899933, + -0.04692991077899933 + ], + "y" : + [ + -0.1159216761589050, + 0.1159217953681946, + 0.1159217953681946, + -0.1159216761589050 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0001134483172791079, + "massData-center" : + { + "x" : 4.842877032729120e-08, + "y" : 5.960464477539062e-08 + }, + "massData-mass" : 0.02176081016659737, + "name" : "lowerLeftArm", + "position" : + { + "x" : -0.1165381968021393, + "y" : 0.6842151284217834 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.04693000018596649, + 0.04693000018596649, + -0.04693010076880455, + -0.04693010076880455 + ], + "y" : + [ + -0.1895969957113266, + 0.1895969957113266, + 0.1895969957113266, + -0.1895969957113266 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0004525947733782232, + "massData-center" : + { + "x" : -5.029141902923584e-08, + "y" : 0 + }, + "massData-mass" : 0.03559118881821632, + "name" : "upperRightArm", + "position" : + { + "x" : 0.1183080002665520, + "y" : 0.9012569785118103 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.07039496302604675, + 0.07039496302604675, + -0.07039486616849899, + -0.07039486616849899 + ], + "y" : + [ + -0.1890522241592407, + 0.1890524625778198, + 0.1890524625778198, + -0.1890522241592407 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0007221315754577518, + "massData-center" : + { + "x" : 4.842877388000488e-08, + "y" : 1.192092895507812e-07 + }, + "massData-mass" : 0.05323329567909241, + "name" : "upperRightLeg", + "position" : + { + "x" : 0.03859551250934601, + "y" : 0.3325110077857971 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.07039505988359451, + 0.07039505988359451, + -0.07039495557546616, + -0.07039495557546616 + ], + "y" : + [ + -0.1890522241592407, + 0.1890524625778198, + 0.1890524625778198, + -0.1890522241592407 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.000722132739610970, + "massData-center" : + { + "x" : 5.215406417846680e-08, + "y" : 1.192092824453539e-07 + }, + "massData-mass" : 0.05323336645960808, + "name" : "upperLeftLeg", + "position" : + { + "x" : -0.03829947486519814, + "y" : 0.3325110077857971 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.07039500027894974, + 0.07039500027894974, + -0.07039490342140198, + -0.07039490342140198 + ], + "y" : + [ + -0.09294389933347702, + 0.1276109963655472, + 0.1276109963655472, + -0.09294389933347702 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0001864968799054623, + "massData-center" : + { + "x" : 4.842877032729120e-08, + "y" : 0.01733354665338993 + }, + "massData-mass" : 0.03105190023779869, + "name" : "lowerRightLeg", + "position" : + { + "x" : 0.03859551250934601, + "y" : 0.09799569845199585 + }, + "type" : 2 + } + ], + "collisionbitplanes" : + { + "names" : + [ + "bitplane1", + "bitplane2", + "bitplane3", + "bitplane4", + "bitplane5", + "bitplane6", + "bitplane7", + "bitplane8", + "bitplane9", + "bitplane10", + "bitplane11", + "bitplane12", + "bitplane13", + "bitplane14", + "bitplane15", + "bitplane16", + "bitplane17", + "bitplane18", + "bitplane19", + "bitplane20", + "bitplane21", + "bitplane22", + "bitplane23", + "bitplane24", + "bitplane25", + "bitplane26", + "bitplane27", + "bitplane28", + "bitplane29", + "bitplane30", + "bitplane31", + "bitplane32" + ] + }, + "continuousPhysics" : true, + "gravity" : + { + "x" : 0, + "y" : -10 + }, + "joint" : + [ + + { + "anchorA" : + { + "x" : 0.0003538504242897034, + "y" : 0.07107692956924438 + }, + "anchorB" : + { + "x" : 0.0003536641597747803, + "y" : -0.1459649801254272 + }, + "bodyA" : 3, + "bodyB" : 6, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : 0.01745329238474369, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint1", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.919862151145935 + }, + + { + "anchorA" : + { + "x" : -0.0007802955806255341, + "y" : -0.1484909951686859 + }, + "anchorB" : + { + "x" : -0.0007801018655300140, + "y" : 0.08614099025726318 + }, + "bodyA" : 8, + "bodyB" : 4, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : 0.01745329238474369, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint7", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 2.443460941314697 + }, + + { + "anchorA" : + { + "x" : -0.004979588091373444, + "y" : -0.1506859958171844 + }, + "anchorB" : + { + "x" : -0.005973689258098602, + "y" : 0.08482310175895691 + }, + "bodyA" : 7, + "bodyB" : 9, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : 0.01745329238474369, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint8", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 2.443460941314697 + }, + + { + "anchorA" : + { + "x" : -0.06799955666065216, + "y" : -0.3021813035011292 + }, + "anchorB" : + { + "x" : -0.02891255542635918, + "y" : 0.1648437976837158 + }, + "bodyA" : 1, + "bodyB" : 8, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -0.7853981852531433, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint6", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.570796370506287 + }, + + { + "anchorA" : + { + "x" : 0.06260262429714203, + "y" : -0.3029872477054596 + }, + "anchorB" : + { + "x" : 0.02294230461120605, + "y" : 0.1640380322933197 + }, + "bodyA" : 1, + "bodyB" : 7, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -0.7853981852531433, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint5", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.570796370506287 + }, + + { + "anchorA" : + { + "x" : 0.1179294362664223, + "y" : 0.2464744448661804 + }, + "anchorB" : + { + "x" : 0.0004089996218681335, + "y" : 0.1447530388832092 + }, + "bodyA" : 1, + "bodyB" : 6, + "enableLimit" : false, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -2.268928050994873, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint2", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 3.141592741012573 + }, + + { + "anchorA" : + { + "x" : -0.1188285648822784, + "y" : 0.2521644234657288 + }, + "anchorB" : + { + "x" : -0.001505002379417419, + "y" : 0.1504430174827576 + }, + "bodyA" : 1, + "bodyB" : 0, + "enableLimit" : false, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -2.268928050994873, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint3", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 3.141592741012573 + }, + + { + "anchorA" : + { + "x" : 0.0008554458618164062, + "y" : -0.1461489796638489 + }, + "anchorB" : + { + "x" : 0.0008557140827178955, + "y" : 0.07089227437973022 + }, + "bodyA" : 0, + "bodyB" : 5, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -1.919862151145935, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint4", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 0.01745329238474369 + }, + + { + "anchorA" : + { + "x" : 0.02210754156112671, + "y" : 0.4607425332069397 + }, + "anchorB" : + { + "x" : -0.02544101513922215, + "y" : -0.2591779232025146 + }, + "bodyA" : 1, + "bodyB" : 2, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -0.6981316804885864, + "maxMotorTorque" : 0, + "motorSpeed" : 0, + "name" : "joint9", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.221730470657349 + } + ], + "positionIterations" : 3, + "stepsPerSecond" : 60.0, + "subStepping" : false, + "velocityIterations" : 8, + "warmStarting" : true +} diff --git a/app/Game/Channel/Channel.js b/app/Game/Channel/Channel.js index 094cef0..db3b3a6 100755 --- a/app/Game/Channel/Channel.js +++ b/app/Game/Channel/Channel.js @@ -3,11 +3,13 @@ "Lib/Utilities/NotificationCenter", "Game/Channel/User", "Lib/Utilities/Protocol/Helper", - "Lib/Utilities/Options", + "Lib/Utilities/OptionsHelper", "Game/Config/Settings" ], - function (GameController, Nc, User, ProtocolHelper, Options, Settings) { + function (GameController, nc, User, ProtocolHelper, optionsHelper, Settings) { + + "use strict"; function Channel (pipeToServer, options) { @@ -17,24 +19,25 @@ this.users = {}; this.pipeToServer = pipeToServer; this.levelListIndex = -1; + this.gameController = null; - this.options = options = Options.merge(options, { + this.options = options = optionsHelper.merge(options, { levelUids: Settings.CHANNEL_DEFAULT_LEVELS }); // Notification Center - Nc.on(Nc.ns.channel.events.round.end, this.onEndRound, this); - Nc.on(Nc.ns.channel.events.controlCommand.channel, function (message) { + nc.on(nc.ns.channel.events.round.end, this.onEndRound, this); + nc.on(nc.ns.channel.events.controlCommand.channel, function (message) { ProtocolHelper.applyCommand(message.data, self); }); - Nc.on(Nc.ns.channel.to.client.gameCommand.broadcast, this.broadcastGameCommand, this); - Nc.on(Nc.ns.channel.to.client.controlCommand.broadcast, this.broadcastControlCommand, this); - //Nc.on(Nc.ns.channel.to.client.gameCommand.broadcastExcept, this.broadcastGameCommandExcept, this); - //Nc.on(Nc.ns.channel.to.client.controlCommand.broadcastExcept, this.broadcastControlCommandExcept, this); + nc.on(nc.ns.channel.to.client.gameCommand.broadcast, this.broadcastGameCommand, this); + nc.on(nc.ns.channel.to.client.controlCommand.broadcast, this.broadcastControlCommand, this); + //nc.on(nc.ns.channel.to.client.gameCommand.broadcastExcept, this.broadcastGameCommandExcept, this); + //nc.on(nc.ns.channel.to.client.controlCommand.broadcastExcept, this.broadcastControlCommandExcept, this); this.beginRound(); - console.checkpoint('channel ' + this.name + ' created'); + console.checkpoint("channel " + this.name + " created"); setTimeout(function() { if(Object.keys(self.users).length < 1) { @@ -53,7 +56,7 @@ if(this.gameController) { this.gameController.destroy(); - delete this.gameController; + this.gameController = null; } var gameControllerOptions = { @@ -79,6 +82,7 @@ Channel.prototype.onEndRound = function() { var self = this; + this.gameController.endRound(); this.broadcastControlCommand("endRound", true); console.checkpoint("End Round (" + this.name + ") - Begin Round in " + Settings.CHANNEL_END_ROUND_TIME + " seconds"); @@ -99,23 +103,23 @@ }; if(!this.gameController.level || !this.gameController.level.isLoaded) { - var token = Nc.on(Nc.ns.core.game.events.level.loaded, function() { + var token = nc.on(nc.ns.core.game.events.level.loaded, function() { self.sendJoinSuccess(options); - this.users[options.id].sendControlCommand("beginRound", clientGameControllerOptions); - Nc.off(token); + self.users[options.id].sendControlCommand("beginRound", clientGameControllerOptions); + nc.off(token); }); } else { - self.sendJoinSuccess(options); + this.sendJoinSuccess(options); this.users[options.id].sendControlCommand("beginRound", clientGameControllerOptions); } - } + }; Channel.prototype.sendJoinSuccess = function(options) { var user = new User(options.id, options); var joinedUsers = []; for(var userId in this.users) { - joinedUsers.push(this.users[userId].options) + joinedUsers.push(this.users[userId].options); } var levelUid = null; @@ -125,23 +129,22 @@ this.users[user.id] = user; - var options = { + options = { user: user.options, joinedUsers: joinedUsers, levelUid: levelUid }; - //Nc.trigger('user/' + user.id + "/joinSuccess", options); + //nc.trigger("user/" + user.id + "/joinSuccess", options); user.sendControlCommand("joinSuccess", options); - Nc.trigger(Nc.ns.channel.events.user.joined, user); + nc.trigger(nc.ns.channel.events.user.joined, user); this.broadcastControlCommandExcept("userJoined", user.options, user); }; Channel.prototype.onReleaseUser = function (userId) { var self = this; - var user = this.users[userId]; - Nc.trigger(Nc.ns.channel.events.user.left, userId); + nc.trigger(nc.ns.channel.events.user.left, userId); delete this.users[userId]; this.broadcastControlCommand("userLeft", userId); @@ -158,7 +161,7 @@ } }, Settings.CHANNEL_DESTRUCTION_TIME * 1000); } - } + }; Channel.prototype.destroy = function() { console.checkpoint("channel (" + this.name + ") destroyed"); @@ -172,7 +175,7 @@ for(var id in this.users) { this.users[id].sendControlCommand(command, options); } - } + }; Channel.prototype.broadcastControlCommandExcept = function (command, options, exceptUser) { for(var id in this.users) { @@ -180,13 +183,13 @@ this.users[id].sendControlCommand(command, options); } } - } + }; Channel.prototype.broadcastGameCommand = function (command, options) { for(var id in this.users) { this.users[id].sendGameCommand(command, options); } - } + }; Channel.prototype.broadcastGameCommandExcept = function (command, options, exceptUser) { for(var id in this.users) { @@ -194,7 +197,7 @@ this.users[id].sendGameCommand(command, options); } } - } + }; return Channel; diff --git a/app/Game/Channel/Collision/Detector.js b/app/Game/Channel/Collision/Detector.js index 8576da9..4d81552 100755 --- a/app/Game/Channel/Collision/Detector.js +++ b/app/Game/Channel/Collision/Detector.js @@ -4,6 +4,8 @@ define([ function (Parent) { + "use strict"; + function Detector () { Parent.call(this); } diff --git a/app/Game/Channel/Control/PlayerController.js b/app/Game/Channel/Control/PlayerController.js index c8e5c6e..c9e55d3 100755 --- a/app/Game/Channel/Control/PlayerController.js +++ b/app/Game/Channel/Control/PlayerController.js @@ -5,7 +5,9 @@ define([ "Game/Config/Settings" ], -function(Parent, Nc, Parser, Settings) { +function(Parent, nc, Parser, Settings) { + + "use strict"; function PlayerController(player) { @@ -33,31 +35,34 @@ function(Parent, Nc, Parser, Settings) { }; PlayerController.prototype.handActionRequest = function(options) { - if (options) this.player.handActionRequest(options.x, options.y); + options.x = parseFloat(options.x) || 0.0; + options.y = parseFloat(options.y) || 0.0; + options.av = parseFloat(options.av) || 0.0; + if (options) this.player.handActionRequest(options); }; PlayerController.prototype.suicide = function() { this.player.suicide(); }; - PlayerController.prototype.mePositionStateUpdate = function(update) { + PlayerController.prototype.mePositionStateOverride = function(update) { - if(!this.player.doll) { - console.warn('me state update, even though doll does not exist'); - return; + if(!this.player.isSpawned()) { + // if someone still falls but is dead on the server already + return; } var difference = { x: Math.abs(update.p.x - this.player.doll.body.GetPosition().x), y: Math.abs(update.p.y - this.player.doll.body.GetPosition().y) - } + }; - if(difference.x < Settings.PUNKBUSTER_DIFFERENCE_METERS - && difference.y < Settings.PUNKBUSTER_DIFFERENCE_METERS) { + if(difference.x < Settings.PUNKBUSTER_DIFFERENCE_METERS && + difference.y < Settings.PUNKBUSTER_DIFFERENCE_METERS) { this.player.doll.updatePositionState(update); } else { // HARD UPDATE FOR SELF - console.log(this.player.user.options.nickname + ' is cheating.') + console.log(this.player.user.options.nickname + " is cheating."); var body = this.player.doll.body; @@ -66,7 +71,7 @@ function(Parent, Nc, Parser, Settings) { lv: body.GetLinearVelocity() }; - Nc.trigger(Nc.ns.channel.to.client.user.gameCommand.send + this.player.id, 'positionStateReset', options); + nc.trigger(nc.ns.channel.to.client.user.gameCommand.send + this.player.id, "positionStateReset", options); } }; diff --git a/app/Game/Channel/GameController.js b/app/Game/Channel/GameController.js index eb7e013..4f5f5b5 100755 --- a/app/Game/Channel/GameController.js +++ b/app/Game/Channel/GameController.js @@ -2,39 +2,40 @@ define([ "Game/Core/GameController", "Game/Channel/Physics/Engine", "Game/Config/Settings", - "Game/Channel/Control/PlayerController", "Lib/Utilities/RequestAnimFrame", "Lib/Utilities/NotificationCenter", "Lib/Vendor/Box2D", "Game/Channel/Player", "Game/Channel/GameObjects/GameObject", "Game/Channel/GameObjects/Doll", - "Game/Channel/GameObjects/Items/RagDoll" + "Game/Channel/GameObjects/Items/RubeDoll" ], -function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, Nc, Box2D, Player, GameObject, Doll, RagDoll) { +function (Parent, PhysicsEngine, Settings, requestAnimFrame, nc, Box2D, Player, GameObject, Doll, RubeDoll) { + + "use strict"; function GameController (options) { this.animationTimeout = null; this.worldUpdateTimeout = null; this.spawnTimeouts = []; + this.roundHasEnded = false; Parent.call(this, options); this.ncTokens = this.ncTokens.concat([ - Nc.on(Nc.ns.channel.events.user.joined, this.onUserJoined, this), - Nc.on(Nc.ns.channel.events.user.left, this.onUserLeft, this), - Nc.on(Nc.ns.channel.events.user.level.reset, this.onResetLevel, this), - Nc.on(Nc.ns.channel.events.user.client.ready, this.onClientReady, this), - Nc.on(Nc.ns.core.game.events.level.loaded, this.onLevelLoaded, this), - Nc.on(Nc.ns.channel.events.game.player.killed, this.onPlayerKilled, this), + nc.on(nc.ns.channel.events.user.joined, this.onUserJoined, this), + nc.on(nc.ns.channel.events.user.left, this.onUserLeft, this), + nc.on(nc.ns.channel.events.user.level.reset, this.onResetLevel, this), + nc.on(nc.ns.channel.events.user.client.ready, this.onClientReady, this), + nc.on(nc.ns.core.game.events.level.loaded, this.onLevelLoaded, this), + nc.on(nc.ns.channel.events.game.player.killed, this.onPlayerKilled, this), ]); console.checkpoint('starting game controller for channel (' + options.channelName + ')'); } - GameController.prototype = Object.create(Parent.prototype); GameController.prototype.update = function () { @@ -57,15 +58,28 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N this.createPlayer(user); } + GameController.prototype.onUserLeft = function (userId) { + var player = this.players[userId]; + this.clearItemsOfPlayerFingerPrints(player); + Parent.prototype.onUserLeft.call(this, userId); + }; + + GameController.prototype.clearItemsOfPlayerFingerPrints = function(player) { + nc.trigger(nc.ns.channel.events.game.player.clearFingerPrints, player); + }; + GameController.prototype.createPlayer = function(user) { - var player = Parent.prototype.createPlayer.call(this, user); - player.setPlayerController(new PlayerController(player)) + + var revealedGameController = { + isInBetweenRounds: this.isInBetweenRounds.bind(this) + }; + var player = Parent.prototype.createPlayer.call(this, user, revealedGameController); user.setPlayer(player); }; GameController.prototype.onPlayerKilled = function(player, killedByPlayer) { if(killedByPlayer.stats.score >= this.options.scoreLimit) { - Nc.trigger(Nc.ns.channel.events.round.end); + nc.trigger(nc.ns.channel.events.round.end); } else { this.spawnPlayer(player, Settings.RESPAWN_TIME); } @@ -81,8 +95,6 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N var spawnTimeout = setTimeout(function() { player.spawn(spawnPoint.x, spawnPoint.y); - // put it into - self.gameObjects.animated.push(player); var options = { id: player.id, @@ -90,7 +102,7 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N y: spawnPoint.y }; - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, "spawnPlayer", options); + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "spawnPlayer", options); var i = self.spawnTimeouts.indexOf(spawnTimeout); self.spawnTimeouts.splice(i, 1); @@ -105,17 +117,18 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N var update = this.getWorldUpdateObject(false); if(Object.getOwnPropertyNames(update).length > 0) { - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, 'worldUpdate', update); + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "worldUpdate", update); } this.worldUpdateTimeout = setTimeout(this.updateWorld.bind(this), Settings.NETWORK_UPDATE_INTERVAL); - } + }; GameController.prototype.getWorldUpdateObject = function(getSleeping) { getSleeping = getSleeping || false; var update = {}; +/* var body = this.physicsEngine.world.GetBodyList(); do { if((getSleeping || body.IsAwake()) && body.GetType() === Box2D.Dynamics.b2Body.b2_dynamicBody) { @@ -123,22 +136,33 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N if (userData instanceof GameObject) { var gameObject = userData; - - update[gameObject.uid] = { - p: body.GetPosition(), - a: body.GetAngle(), - lv: body.GetLinearVelocity(), - av: body.GetAngularVelocity() - }; - - if(gameObject instanceof Doll) { - update[gameObject.uid].as = gameObject.getActionState(); - update[gameObject.uid].laxy = gameObject.lookAtXY; + var updateData = gameObject.getUpdateData(); + + if (updateData) { + update[gameObject.uid] = updateData; } } } } while (body = body.GetNext()); +*/ + + for (var uid in this.worldUpdateObjects) { + + var gameObject = this.worldUpdateObjects[uid]; + + if (!(gameObject instanceof GameObject)) { + console.warn('Cant find object ' + uid + ' in worldUpdateObjects pool (channel side), here is the object:'); + console.log(gameObject); + continue; + } + + var updateData = gameObject.getUpdateData(getSleeping); + + if (updateData) { + update[gameObject.uid] = updateData; + } + } return update; }; @@ -147,7 +171,7 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N var spawnedPlayers = []; for(var id in this.players) { var player = this.players[id]; - if(player.isSpawned) { + if(player.isSpawned()) { var options = { id: id, @@ -166,23 +190,36 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N return spawnedPlayers; }; - GameController.prototype.getRuntimeItems = function() { - var objects = [] + GameController.prototype._getRuntimeItems = function() { - for (var i = 0; i < this.gameObjects.animated.length; i++) { - if(this.gameObjects.animated[i] instanceof RagDoll) { - var object = this.gameObjects.animated[i]; - var options = object.options; - options.x = object.getPosition().x; - options.y = object.getPosition().y; - objects.push({ - uid: object.uid, - options: object.options - }); + var runtimeItems = []; + for (var uid in this.worldUpdateObjects) { + if(this.worldUpdateObjects[uid] instanceof RubeDoll) { + var object = this.worldUpdateObjects[uid]; + runtimeItems.push(object); } - }; + } + return runtimeItems; + }; - return objects; + GameController.prototype.gatherRuntimeItemsForWorldUpdate = function() { + var infos = []; + var runtimeItems = this._getRuntimeItems(); + + // On the other side this is using the level.createItem mechanism to + // create the RubeDoll from its ItemSettings + for (var i = 0; i < runtimeItems.length; i++) { + var object = runtimeItems[i]; + var options = object.options; + options.x = object.getPosition().x; + options.y = object.getPosition().y; + infos.push({ + uid: object.uid, + options: object.options + }); + } + + return infos; }; GameController.prototype.onClientReady = function(userId) { @@ -191,21 +228,34 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N var options = { spawnedPlayers: this.getSpawnedPlayersAndTheirPositions(), worldUpdate: this.getWorldUpdateObject(true), - runtimeItems: this.getRuntimeItems(), + runtimeItems: this.gatherRuntimeItemsForWorldUpdate(), userId: userId - } + }; - Nc.trigger(Nc.ns.channel.to.client.user.gameCommand.send + userId, "clientReadyResponse", options); + nc.trigger(nc.ns.channel.to.client.user.gameCommand.send + userId, "clientReadyResponse", options); this.spawnPlayer(player, 0); }; + GameController.prototype.endRound = function() { + this.roundHasEnded = true; + + for(var id in this.players) { + this.players[id].setInBetweenRounds(true); + } + }; + + GameController.prototype.isInBetweenRounds = function() { + return this.roundHasEnded; + }; + + // FIXME: remove this method GameController.prototype.onResetLevel = function(userId) { console.log('OH NO!!! ON RESET LEVEL IS CALLED AND RESPAWNES PLAYERS'); Parent.prototype.onResetLevel.call(this); - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, "resetLevel", true); + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "resetLevel", true); for (var key in this.players) { this.spawnPlayer(this.players[key]); } @@ -219,6 +269,11 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N clearTimeout(this.spawnTimeouts[i]); }; + var runtimeItems = this._getRuntimeItems(); + for (var i = 0; i < runtimeItems.length; i++) { + runtimeItems[i].destroy(); + } + Parent.prototype.destroy.call(this); }; diff --git a/app/Game/Channel/GameObjects/Doll.js b/app/Game/Channel/GameObjects/Doll.js index ed5022b..572beac 100755 --- a/app/Game/Channel/GameObjects/Doll.js +++ b/app/Game/Channel/GameObjects/Doll.js @@ -2,10 +2,13 @@ define([ "Game/Core/GameObjects/Doll", "Game/Channel/GameObjects/Item", "Lib/Vendor/Box2D", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Assert" ], -function (Parent, Item, Box2D, Nc) { +function (Parent, Item, Box2D, nc, Assert) { + + "use strict"; function Doll(physicsEngine, uid, player) { Parent.call(this, physicsEngine, uid, player); @@ -13,12 +16,14 @@ function (Parent, Item, Box2D, Nc) { Doll.prototype = Object.create(Parent.prototype); - Doll.prototype.findCloseItem = function(x, y) { + Doll.prototype.findCloseItem = function(x) { + + var self = this; function findItem(array) { for (var i = 0; i < array.length; i++) { var item = array[i]; - if(item.isGrabbingAllowed(this.player)) { + if(item.isGrabbingAllowed(self.player)) { return item; } } @@ -29,7 +34,7 @@ function (Parent, Item, Box2D, Nc) { } else { return findItem(this.reachableItems.right); } - } + }; Doll.prototype.onImpact = function(isColliding, fixture) { var self = this; @@ -42,47 +47,73 @@ function (Parent, Item, Box2D, Nc) { var item = otherBody.GetUserData(); if(item instanceof Item) { var itemVelocity = item.body.GetLinearVelocity(); - var itemMass = item.body.GetMass(); + //var itemMass = item.body.GetMass(); var ownVelocity = this.body.GetLinearVelocity(); var b2Math = Box2D.Common.Math.b2Math; - - var absItemVelocity = b2Math.AbsV(itemVelocity) - var max = 1; + var absItemVelocity = b2Math.AbsV(itemVelocity); + var min = 1; + var damage = 0; - if(absItemVelocity.x > max || absItemVelocity.y > max) { + if(absItemVelocity.x > min || absItemVelocity.y > min) { if(item.lastMoved && item.lastMoved.player != this.player) { - var damageVector = b2Math.SubtractVV(itemVelocity, ownVelocity); - damageVector.Abs(); - damageVector.Multiply(0.7); - damageVector.Multiply(itemMass * 1.3); - var damage = damageVector.Length(); - damage *= item.options.danger ? item.options.danger : 1; - var player = item.lastMoved.player; + var collision = b2Math.SubtractVV(itemVelocity, ownVelocity); + // Tested max velocity banana: 50 + var velocityDamage = collision.Length() / 50; + + // Max weight of piano: 15 + var weightDamage = item.options.weight / 15; + + // Max danger of knife: 3 + var dangerDamage = item.options.danger / 3; + + // + 0.5 and / 2: offsetting for lower velocity impact + // * 300: tested imperically by throwing piano from deadly height + // * 80: tested imperically by throwing knife fast + damage = (velocityDamage + 0.5) * (weightDamage * 300 + dangerDamage * 80) / 2; + + var lastMovedPlayer = item.lastMoved.player; var callback = function() { - self.player.addDamage(damage, player); - } + self.player.addDamage(damage, lastMovedPlayer, item); + }; - Nc.trigger(Nc.ns.channel.engine.worldQueue.add, callback) + nc.trigger(nc.ns.channel.engine.worldQueue.add, callback); } } - item.setLastMovedBy(this.player); + // only set lastMovedBy if player wasn't hurt by collision + if (damage === 0) { + item.setLastMovedBy(this.player); + } } } } - } + }; Doll.prototype.updatePositionState = function(update) { if(!this.isAnotherPlayerNearby()) { + Assert.number(update.p.x, update.p.y); + Assert.number(update.lv.x, update.lv.y); this.body.SetAwake(true); this.body.SetPosition(update.p); this.body.SetLinearVelocity(update.lv); } }; + + Doll.prototype.getUpdateData = function(getSleeping) { + + var updateData = Parent.prototype.getUpdateData.call(this, getSleeping); + + if(updateData) { + updateData.as = this.getActionState(); + updateData.laxy = this.lookAtXY; + } + + return updateData; + }; return Doll; diff --git a/app/Game/Channel/GameObjects/GameObject.js b/app/Game/Channel/GameObjects/GameObject.js index 03b2a9b..5d51afb 100755 --- a/app/Game/Channel/GameObjects/GameObject.js +++ b/app/Game/Channel/GameObjects/GameObject.js @@ -1,9 +1,39 @@ define([ - "Game/Core/GameObjects/GameObject" + "Game/Core/GameObjects/GameObject", + "Lib/Vendor/Box2D" ], -function(Parent) { +function (Parent, Box2D) { - return Parent; + "use strict"; + function GameObject(physicsEngine, uid) { + Parent.call(this, physicsEngine, uid); + } + + GameObject.prototype = Object.create(Parent.prototype); + + GameObject.prototype.getUpdateData = function(getSleeping) { + + if (!this.body) { + return null; + } + + if (this.body.GetType() === Box2D.Dynamics.b2Body.b2_staticBody) { + return null; + } + + if (!getSleeping && !this.body.IsAwake()) { + return null; + } + + return { + p: this.body.GetPosition(), + a: this.body.GetAngle(), + lv: this.body.GetLinearVelocity(), + av: this.body.GetAngularVelocity() + }; + } + + return GameObject; }); \ No newline at end of file diff --git a/app/Game/Channel/GameObjects/Item.js b/app/Game/Channel/GameObjects/Item.js index dcf28ef..0bdb334 100755 --- a/app/Game/Channel/GameObjects/Item.js +++ b/app/Game/Channel/GameObjects/Item.js @@ -1,17 +1,27 @@ define([ - "Game/Core/GameObjects/Item" + "Game/Core/GameObjects/Item", + "Lib/Utilities/NotificationCenter", ], -function (Parent) { +function (Parent, nc) { + + "use strict"; function Item(physicsEngine, uid, options) { Parent.call(this, physicsEngine, uid, options); this.heldByPlayers = []; this.lastMoved = null; + + this.ncTokens = (this.ncTokens || []).concat([ + nc.on(nc.ns.channel.events.game.player.clearFingerPrints, this.clearOfPlayerFingerPrints, this) + ]); } Item.prototype = Object.create(Parent.prototype); + Item.prototype.getLastMovedBy = function() { + return this.lastMoved; + } Item.prototype.setLastMovedBy = function(player) { @@ -19,14 +29,14 @@ function (Parent) { this.lastMoved = { player: player, timestamp: new Date() - } + }; } else { this.lastMoved = null; } }; - Item.prototype.isGrabbingAllowed = function(player) { - return this.heldByPlayers.length == 0; + Item.prototype.isGrabbingAllowed = function(player) { // jshint unused:false + return this.heldByPlayers.length === 0; }; Item.prototype.beingGrabbed = function(player) { @@ -38,7 +48,7 @@ function (Parent) { } }; - Item.prototype.isReleasingAllowed = function(player) { + Item.prototype.isReleasingAllowed = function(player) { // jshint unused:false return true; }; @@ -54,6 +64,13 @@ function (Parent) { } }; + Item.prototype.clearOfPlayerFingerPrints = function(player) { + if (this.getLastMovedBy() && this.getLastMovedBy().player === player) { + console.checkpoint('Removing fingerprints from ' + this.options.image); + this.setLastMovedBy(null); + } + }; + Item.prototype.onCollisionChange = function(isColliding, fixture) { if(isColliding) { @@ -79,8 +96,7 @@ function (Parent) { } } } - } - + }; return Item; diff --git a/app/Game/Channel/GameObjects/Items/RagDoll.js b/app/Game/Channel/GameObjects/Items/RagDoll.js index 938e229..1c02792 100644 --- a/app/Game/Channel/GameObjects/Items/RagDoll.js +++ b/app/Game/Channel/GameObjects/Items/RagDoll.js @@ -4,7 +4,9 @@ define([ "Lib/Utilities/NotificationCenter" ], -function (Parent, Settings, Nc) { +function (Parent, Settings, nc) { + + "use strict"; function RagDoll(physicsEngine, uid, options) { this.scheduledForDestruction = false; @@ -33,7 +35,7 @@ function (Parent, Settings, Nc) { var self = this; this.scheduledForDestruction = true; this.destructionTimeout = setTimeout(function() { - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, 'removeGameObject', { + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, 'removeGameObject', { type: 'animated', uid: self.uid }); diff --git a/app/Game/Channel/GameObjects/Items/Rube.js b/app/Game/Channel/GameObjects/Items/Rube.js deleted file mode 100644 index f663984..0000000 --- a/app/Game/Channel/GameObjects/Items/Rube.js +++ /dev/null @@ -1,9 +0,0 @@ -define([ - "Game/Core/GameObjects/Items/Rube" -], - -function (Parent) { - - return Parent; - -}); \ No newline at end of file diff --git a/app/Game/Channel/GameObjects/Items/RubeDoll.js b/app/Game/Channel/GameObjects/Items/RubeDoll.js new file mode 100644 index 0000000..c03199e --- /dev/null +++ b/app/Game/Channel/GameObjects/Items/RubeDoll.js @@ -0,0 +1,79 @@ +define([ + "Game/Core/GameObjects/Items/RubeDoll", + "Game/Config/Settings", + "Lib/Utilities/NotificationCenter" +], + +function (Parent, Settings, nc) { + + "use strict"; + + function RubeDoll(physicsEngine, uid, options) { + this.scheduledForDestruction = false; + this.destructionTimeout = null; + + Parent.call(this, physicsEngine, uid, options); + } + + RubeDoll.prototype = Object.create(Parent.prototype); + + RubeDoll.prototype.beingGrabbed = function(player) { + Parent.prototype.beingGrabbed.call(this, player); + if(this.scheduledForDestruction) { + clearTimeout(this.destructionTimeout); + } + }; + + RubeDoll.prototype.beingReleased = function(player) { + Parent.prototype.beingReleased.call(this, player); + if(this.scheduledForDestruction) { + this.delayedDestroy(); + } + }; + + RubeDoll.prototype.delayedDestroy = function() { + var self = this; + this.scheduledForDestruction = true; + this.destructionTimeout = setTimeout(function() { + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, 'removeGameObject', { + type: 'animated', + uid: self.uid + }); + self.destroy(); + }, Settings.RAGDOLL_DESTRUCTION_TIME * 1000); + }; + + RubeDoll.prototype.getUpdateData = function(getSleeping) { + var updateData = Parent.prototype.getUpdateData.call(this, getSleeping); + + // if parent is asleep it sends null, to do no update + if(!updateData) { + return updateData; + } + + // adding limb update data + var limbUpdateData = {}; + + for(var name in this.limbs) { + limbUpdateData[name] = { + p: this.limbs[name].GetPosition(), + a: this.limbs[name].GetAngle(), + lv: this.limbs[name].GetLinearVelocity(), + av: this.limbs[name].GetAngularVelocity() + }; + } + updateData['limbs'] = limbUpdateData; + + return updateData; + } + + RubeDoll.prototype.destroy = function() { + if(this.scheduledForDestruction) { + clearTimeout(this.destructionTimeout); + } + Parent.prototype.destroy.call(this); + }; + + return RubeDoll; + +}); \ No newline at end of file diff --git a/app/Game/Channel/GameObjects/SpectatorDoll.js b/app/Game/Channel/GameObjects/SpectatorDoll.js index b89f262..058fca7 100644 --- a/app/Game/Channel/GameObjects/SpectatorDoll.js +++ b/app/Game/Channel/GameObjects/SpectatorDoll.js @@ -4,6 +4,8 @@ define([ function (Parent) { + "use strict"; + return Parent; }); \ No newline at end of file diff --git a/app/Game/Channel/Loader/Level.js b/app/Game/Channel/Loader/Level.js index f0c9818..4271cd3 100755 --- a/app/Game/Channel/Loader/Level.js +++ b/app/Game/Channel/Loader/Level.js @@ -4,10 +4,12 @@ define([ "fs" ], -function (Parent, Settings, fs) { +function (Parent, Settings, FileSystem) { - function Level (uid, engine, gameObjects) { - Parent.call(this, uid, engine, gameObjects); + "use strict"; + + function Level (uid, engine) { + Parent.call(this, uid, engine); } Level.prototype = Object.create(Parent.prototype); @@ -15,7 +17,7 @@ function (Parent, Settings, fs) { Level.prototype.loadLevelDataFromPath = function (path, callback) { // overwriting parent - fs.readFile(path, "utf8", function (err, data) { + FileSystem.readFile(path, "utf8", function (err, data) { if (err) throw err; callback(JSON.parse(data)); }); diff --git a/app/Game/Channel/Loader/TiledLevel.js b/app/Game/Channel/Loader/TiledLevel.js index e23fbd3..ad96bfd 100644 --- a/app/Game/Channel/Loader/TiledLevel.js +++ b/app/Game/Channel/Loader/TiledLevel.js @@ -4,6 +4,8 @@ define([ function (Parent) { + "use strict"; + return Parent; }); \ No newline at end of file diff --git a/app/Game/Channel/PipeToServer.js b/app/Game/Channel/PipeToServer.js index 2c007e5..c25e34d 100755 --- a/app/Game/Channel/PipeToServer.js +++ b/app/Game/Channel/PipeToServer.js @@ -1,55 +1,147 @@ define([ "Lib/Utilities/NotificationCenter", - "Game/Channel/Channel" + "Game/Channel/Channel", + "Game/Config/Settings", + "fs" ], -function (Nc, Channel) { +function (nc, Channel, Settings, fs) { + + "use strict"; function PipeToServer (process) { - - var self = this; - this.channel = null; this.process = process; + this.recordingFileName = null; - Nc.on(Nc.ns.channel.to.server.controlCommand.send, this.send, this); + nc.on(nc.ns.channel.to.server.controlCommand.send, this.send, this); - process.on('message', function (message, handle) { + process.on("message", this.onProcessMessage.bind(this)); + } - if(message.data.hasOwnProperty('CREATE')) { - self.channel = new Channel(self, message.data.options); - } else if (message.data.hasOwnProperty('KILL')) { - self.channel.destroy(); - } else { - self.onMessage(message); + PipeToServer.prototype.onProcessMessage = function (message, handle) { // jshint unused:false + + if(message.data.hasOwnProperty("CREATE")) { + this.channel = new Channel(this, message.data.options); + + message.data.options.playingFileName = Settings.CHANNEL_PLAY_RECORDING; + + if(message.data.options.playingFileName) { + var self = this; + setTimeout(function() { + console.log(message.data.options.playingFileName); + self.play(message.data.options.playingFileName); + }, 2000); // giving channel time to set everything up + } + + if(Settings.CHANNEL_RECORD_SESSION) { + + this.recordingFileName = Settings.CHANNEL_RECORDING_PATH + + this.channel.name + + "-" + + (new Date()).toISOString() + + "-" + + message.data.options.levelUids.join("_") + + ".log"; + + if(!fs.existsSync(Settings.CHANNEL_RECORDING_PATH)) { + fs.mkdirSync(Settings.CHANNEL_RECORDING_PATH); + } + + setInterval(this.recordWorldUpdate.bind(this), 1000); + console.checkpoint("Started recording to: " + this.recordingFileName); } - }); - } + } else if (message.data.hasOwnProperty("KILL")) { + this.channel.destroy(); + } else { + this.onMessage(message); + + if(Settings.CHANNEL_RECORD_SESSION && this.channel && this.channel.name) { + var m = JSON.stringify(message); + var timestamp = Date.now(); + var line = "m" + timestamp + m + "\n"; + + fs.appendFile(this.recordingFileName, line, function (err) { + if (err) throw err; + }); + } + } + + }; PipeToServer.prototype.send = function (recipient, data) { var message = { recipient: recipient, data: data - } + }; this.process.send(message); }; PipeToServer.prototype.onMessage = function (message) { switch(message.recipient) { - case 'channel': - Nc.trigger(Nc.ns.channel.events.controlCommand.channel, message); + case "channel": + nc.trigger(nc.ns.channel.events.controlCommand.channel, message); break; default: - Nc.trigger(Nc.ns.channel.events.controlCommand.user + message.recipient, message); + nc.trigger(nc.ns.channel.events.controlCommand.user + message.recipient, message); break; } - } + }; + + PipeToServer.prototype.play = function(playingFileName) { + var self = this; + var data = fs.readFileSync(Settings.CHANNEL_RECORDING_PATH + playingFileName); + var lines = data.toString().split("\n"); + var start = 0; + for (var i = 0; i < lines.length; i++) { + // bind message variable + (function() { + var line = lines[i]; + if(line.length > 0) { + var type = line.substring(0, 1); + var time = parseInt(line.substring(1, Date.now().toString().length + 1), 10); + if(i === 0) { + start = time; + } + time -= start; + + var jsonString = line.substring(Date.now().toString().length + 1); + var message = JSON.parse(jsonString); + + setTimeout(function() { + console.log(line); + + if(type == "m") { + self.onProcessMessage(message, null); + } else if(type == "w") { + if(self.channel.gameController) { + self.channel.gameController.onWorldUpdate(message); + } + } + }, time); + } + })(); + } + }; + + PipeToServer.prototype.recordWorldUpdate = function() { + if(this.channel.gameController) { + var update = this.channel.gameController.getWorldUpdateObject(true); + var worldUpdate = JSON.stringify(update); + var timestamp = Date.now(); + var line = "w" + timestamp + worldUpdate + "\n"; + + fs.appendFile(this.recordingFileName, line, function (err) { + if (err) throw err; + }); + } + }; PipeToServer.prototype.destroy = function() { - this.send('coordinator', {destroy:this.channel.name}); + this.send("coordinator", {destroy:this.channel.name}); this.process.exit(0); }; diff --git a/app/Game/Channel/Player.js b/app/Game/Channel/Player.js index 00f806a..7c32014 100755 --- a/app/Game/Channel/Player.js +++ b/app/Game/Channel/Player.js @@ -1,17 +1,22 @@ define([ "Game/Core/Player", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Game/Channel/Control/PlayerController" ], -function (Parent, Nc) { +function (Parent, nc, PlayerController) { + + "use strict"; - function Player(id, physicsEngine, user) { - Parent.call(this, id, physicsEngine, user); + function Player(id, physicsEngine, user, revealedGameController) { + Parent.call(this, id, physicsEngine, user, revealedGameController); + + this.playerController = new PlayerController(this); } Player.prototype = Object.create(Parent.prototype); - Player.prototype.handActionRequest = function(x, y) { + Player.prototype.handActionRequest = function(options) { if(!this.doll) return false; var item = null; @@ -20,30 +25,26 @@ function (Parent, Nc) { if (isHolding) { item = this.holdingItem; } else { - item = this.doll.findCloseItem(x, y); + item = this.doll.findCloseItem(options.x); } if(item) { - this.handAction(x, y, isHolding, item); + this.handAction(options, isHolding, item); } - } + }; - Player.prototype.handAction = function(x, y, isHolding, item) { + Player.prototype.handAction = function(options, isHolding, item) { - var options = { - playerId: this.id, - itemUid: item.uid - } + options.playerId = this.id; + options.itemUid = item.uid; if (isHolding) { // throw if(item.isReleasingAllowed()) { - this.throw(x, y, item); + this.throw(options, item); options.action = "throw"; - options.x = x; - options.y = y; - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, "handActionResponse", options); + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "handActionResponse", options); } } else { // grab @@ -51,26 +52,34 @@ function (Parent, Nc) { this.grab(item); options.action = "grab"; - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, "handActionResponse", options); + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "handActionResponse", options); } } }; Player.prototype.suicide = function() { - this.addDamage(100, this); + if(this.isSpawned()) { + this.addDamage(100, this, null); + } }; - Player.prototype.addDamage = function(damage, enemy) { + Player.prototype.addDamage = function(damage, enemy, byItem) { + + // Prevent stats change (kills) after round has ended + if (this.revealedGameController.isInBetweenRounds()) { + return; + } + this.stats.health -= damage; if(this.stats.health < 0) this.stats.health = 0; - + if(this.stats.health <= 0) { if(enemy != this) enemy.score(); - this.kill(enemy); - } else { - this.broadcastStats(); + this.kill(enemy, byItem); } + + this.broadcastStats(enemy); }; Player.prototype.spawn = function(x, y) { @@ -79,19 +88,19 @@ function (Parent, Nc) { this.broadcastStats(); }; - Player.prototype.kill = function(killedByPlayer) { + Player.prototype.kill = function(killedByPlayer, byItem) { this.stats.deaths++; var ragDollId = this.stats.deaths; Parent.prototype.kill.call(this, killedByPlayer, ragDollId); - this.broadcastStats(); - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, "playerKill", { + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "playerKill", { playerId: this.id, killedByPlayerId: killedByPlayer.id, - ragDollId: ragDollId + ragDollId: ragDollId, + item: byItem ? byItem.options.name : "Suicide" }); - Nc.trigger(Nc.ns.channel.events.game.player.killed, this, killedByPlayer); // sends endround + nc.trigger(nc.ns.channel.events.game.player.killed, this, killedByPlayer); // sends endround if(this.ragDoll) { this.ragDoll.delayedDestroy(); @@ -100,17 +109,21 @@ function (Parent, Nc) { Player.prototype.score = function() { this.stats.score++; - this.broadcastStats(); }; - Player.prototype.broadcastStats = function() { - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, "updateStats", { + Player.prototype.broadcastStats = function(enemy) { + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "updateStats", { playerId: this.id, stats: this.stats }); + + if(enemy && enemy != this) { + nc.trigger(nc.ns.channel.to.client.gameCommand.broadcast, "updateStats", { + playerId: enemy.id, + stats: enemy.stats + }); + } }; - - return Player; diff --git a/app/Game/Channel/User.js b/app/Game/Channel/User.js index 1bc4b60..fe8b56e 100755 --- a/app/Game/Channel/User.js +++ b/app/Game/Channel/User.js @@ -5,7 +5,7 @@ define([ "Lib/Utilities/Protocol/Parser", ], -function(Parent, Nc, ProtocolHelper, ProtocolParser) { +function(Parent, nc, ProtocolHelper, ProtocolParser) { function User(id, options) { Parent.call(this, id, options); @@ -14,15 +14,15 @@ function(Parent, Nc, ProtocolHelper, ProtocolParser) { this.isReady = false; var self = this; - Nc.on(Nc.ns.channel.to.client.user.controlCommand.joinSuccess + this.id, function(options) { + nc.on(nc.ns.channel.to.client.user.controlCommand.joinSuccess + this.id, function(options) { self.sendControlCommand("joinSuccess", options); }); - Nc.on(Nc.ns.channel.events.controlCommand.user + this.id, function(message) { + nc.on(nc.ns.channel.events.controlCommand.user + this.id, function(message) { ProtocolHelper.applyCommand(message.data, self); }); - Nc.on(Nc.ns.channel.to.client.user.gameCommand.send + this.id, function(command, options) { + nc.on(nc.ns.channel.to.client.user.gameCommand.send + this.id, function(command, options) { self.sendGameCommand(command, options); }); @@ -44,10 +44,10 @@ function(Parent, Nc, ProtocolHelper, ProtocolParser) { } // FIXME: move this to Protocol helper as a function if(command.hasOwnProperty("resetLevel")) { - Nc.trigger(Nc.ns.channel.events.user.level.reset, this.id); + nc.trigger(nc.ns.channel.events.user.level.reset, this.id); } else if(command.hasOwnProperty("clientReady")) { this.isReady = true; - Nc.trigger(Nc.ns.channel.events.user.client.ready, this.id); + nc.trigger(nc.ns.channel.events.user.client.ready, this.id); } else { this.player.playerController.applyCommand(command); } @@ -61,7 +61,17 @@ function(Parent, Nc, ProtocolHelper, ProtocolParser) { var recipient = this.id; var data = ProtocolHelper.encodeCommand(command, options); - Nc.trigger(Nc.ns.channel.to.server.controlCommand.send, recipient, data); + /** + * Listen for beginRound control command + * to set client to be unready again + * so it can load its new level without getting + * any gameCommands like worldUpdate + */ + if(command == "beginRound") { + this.isReady = false; + } + + nc.trigger(nc.ns.channel.to.server.controlCommand.send, recipient, data); }; User.prototype.sendGameCommand = function(command, options) { diff --git a/app/Game/Client/AudioPlayer.js b/app/Game/Client/AudioPlayer.js new file mode 100644 index 0000000..851cbbb --- /dev/null +++ b/app/Game/Client/AudioPlayer.js @@ -0,0 +1,38 @@ +define([ +], + +function () { + + "use strict"; + + function AudioPlayer(path) { + this.audio = new Audio(path); + this.audio.loop = true; + this.audio.volume = 0.1; + + this.audio.addEventListener('timeupdate', function(){ + var buffer = 1 + if(this.currentTime > this.duration - buffer) { + this.currentTime = 1 + this.play() + } + }, false); + } + + AudioPlayer.prototype.play = function() { + //this.audio.play(); + //this.doPlay = true; + } + + AudioPlayer.prototype.stop = function() { + //this.audio.stop(); + //this.doPlay = false; + }; + + AudioPlayer.prototype.destroy = function() { + this.stop(); + }; + + return AudioPlayer; + +}); \ No newline at end of file diff --git a/app/Game/Client/Collision/Detector.js b/app/Game/Client/Collision/Detector.js index 20e16a4..ab540f2 100755 --- a/app/Game/Client/Collision/Detector.js +++ b/app/Game/Client/Collision/Detector.js @@ -4,6 +4,8 @@ define([ function (Parent) { + "use strict"; + function Detector () { Parent.call(this); } diff --git a/app/Game/Client/Control/Input.js b/app/Game/Client/Control/Input.js new file mode 100755 index 0000000..b163541 --- /dev/null +++ b/app/Game/Client/Control/Input.js @@ -0,0 +1,21 @@ +define([ + "Lib/Utilities/NotificationCenter" +], + +function (nc) { + + function Input(playerController) { + this.playerController = playerController; + this.x = null; + this.y = null + } + + Input.prototype.onXyChange = function(x, y) { + this.x = x; + this.y = y; + this.playerController.setXY(this.x, this.y); + } + + return Input; + +}); \ No newline at end of file diff --git a/app/Game/Client/Control/Input/MouseInput.js b/app/Game/Client/Control/Input/MouseInput.js deleted file mode 100755 index ee66551..0000000 --- a/app/Game/Client/Control/Input/MouseInput.js +++ /dev/null @@ -1,47 +0,0 @@ -define([ - "Game/Client/Control/Input/XyInput", - "Game/Client/View/DomController", - "Game/Config/Settings", - "Lib/Utilities/NotificationCenter" -], - -function (Parent, DomController, Settings, Nc) { - - function MouseInput() { - Parent.call(this); - - this.init(); - } - - MouseInput.prototype = Object.create(Parent.prototype); - - MouseInput.prototype.init = function() { - - var canvas = null; - var self = this; - canvas = DomController.getCanvas(); - - canvas.onmousemove = function(e){ - - // -1 +1 +1 +1 xy scaling should - // -1 -1 +1 -1 be like this - - var x = (((e.clientX - this.offsetLeft) / Settings.STAGE_WIDTH) * 2) - 1; - var y = (((Settings.STAGE_HEIGHT - (e.clientY - this.offsetTop)) / Settings.STAGE_HEIGHT) * 2) -1; - - self.onXyChange(x, y); - } - - canvas.onmousedown = function(e) { - - var x = (((e.clientX - this.offsetLeft) / Settings.STAGE_WIDTH) * 2) - 1; - var y = (((Settings.STAGE_HEIGHT - (e.clientY - this.offsetTop)) / Settings.STAGE_HEIGHT) * 2) -1; - - Nc.trigger(Nc.ns.client.input.handAction.request, x, y); - } - }; - - - return MouseInput; - -}); diff --git a/app/Game/Client/Control/Input/XyInput.js b/app/Game/Client/Control/Input/XyInput.js deleted file mode 100755 index 47864d4..0000000 --- a/app/Game/Client/Control/Input/XyInput.js +++ /dev/null @@ -1,20 +0,0 @@ -define([ - "Lib/Utilities/NotificationCenter" -], - -function (Nc) { - - function XyInput() { - this.x = null; - this.y = null - } - - XyInput.prototype.onXyChange = function(x, y) { - this.x = x; - this.y = y; - Nc.trigger(Nc.ns.client.input.xy.change, x, y); - } - - return XyInput; - -}); \ No newline at end of file diff --git a/app/Game/Client/Control/Input/GamepadInput.js b/app/Game/Client/Control/Inputs/Gamepad.js similarity index 90% rename from app/Game/Client/Control/Input/GamepadInput.js rename to app/Game/Client/Control/Inputs/Gamepad.js index 9ddef91..9b10803 100644 --- a/app/Game/Client/Control/Input/GamepadInput.js +++ b/app/Game/Client/Control/Inputs/Gamepad.js @@ -1,10 +1,11 @@ define([ - "Game/Client/Control/Input/XyInput", - "Game/Config/Settings", - "Lib/Utilities/NotificationCenter" + "Game/Client/Control/Input", + "Game/Config/Settings" ], -function (Parent, Settings, Nc) { +function (Parent, Settings) { + + "use strict"; function GamepadInput(playerController) { this.playerController = playerController; @@ -36,7 +37,7 @@ function (Parent, Settings, Nc) { var y = -this.gamepad.axes[3]; // Looking direction - this.playerController.xyInput.onXyChange(x, y); + this.onXyChange(x, y); // Pointer finger holding item var holdingPressure = this.gamepad.axes[5]; diff --git a/app/Game/Client/Control/Inputs/KeyboardAndMouse.js b/app/Game/Client/Control/Inputs/KeyboardAndMouse.js new file mode 100644 index 0000000..50af8a1 --- /dev/null +++ b/app/Game/Client/Control/Inputs/KeyboardAndMouse.js @@ -0,0 +1,231 @@ +define([ + "Game/Client/Control/Input", + "Game/Client/Control/KeyboardInput", + "Game/Client/View/DomController", + "Game/Config/Settings", + "Game/Client/Control/Swiper" +], + +function (Parent, KeyboardInput, domController, Settings, Swiper) { + + "use strict"; + + function KeyboardAndMouse(playerController) { + Parent.call(this); + + this.x = 0; + this.y = 0; + this.mousePosition = {x:0,y:0}; + this.modifier = false; + this.swiper = null; + this.lastLookDirection = 1; + + this.keys = { + w:87, + a:65, + s:83, + d:68, + + f:70, + g:71, + k:75, + + up: 38, + left: 37, + down: 40, + right: 39, + + space: 32, + tab: 9, + shift: 16, + + plus: 187, + plusfx: 171, + minus: 189, + minusfx: 173, + zero: 48 + }; + + this.playerController = playerController; + this.keyboardInit(); + this.mouseInit(); + } + + KeyboardAndMouse.prototype = Object.create(Parent.prototype); + + KeyboardAndMouse.prototype.keyboardInit = function() { + + this.keyboardInput = new KeyboardInput(); + var self = this; + + function bind2Pc(methodName) { + return self.playerController[methodName].bind(self.playerController); + } + + this.keyboardInput.registerKey(this.keys.a, this.moveLeft.bind(this), this.stop.bind(this)); + this.keyboardInput.registerKey(this.keys.left, this.moveLeft.bind(this), this.stop.bind(this)); + this.keyboardInput.registerKey(this.keys.d, this.moveRight.bind(this), this.stop.bind(this)); + this.keyboardInput.registerKey(this.keys.right, this.moveRight.bind(this), this.stop.bind(this)); + this.keyboardInput.registerKey(this.keys.w, bind2Pc('jump'), bind2Pc('jumpStop')); + this.keyboardInput.registerKey(this.keys.up, bind2Pc('jump'), bind2Pc('jumpStop')); + this.keyboardInput.registerKey(this.keys.space, bind2Pc('jump'), bind2Pc('jumpStop')); + this.keyboardInput.registerKey(this.keys.plus, bind2Pc('zoomIn')); + this.keyboardInput.registerKey(this.keys.plusfx, bind2Pc('zoomIn')); + this.keyboardInput.registerKey(this.keys.minus, bind2Pc('zoomOut')); + this.keyboardInput.registerKey(this.keys.minusfx, bind2Pc('zoomOut')); + this.keyboardInput.registerKey(this.keys.zero, bind2Pc('zoomReset')); + this.keyboardInput.registerKey(this.keys.tab, bind2Pc('showInfo'), bind2Pc('hideInfo')); + this.keyboardInput.registerKey(this.keys.k, bind2Pc('suicide')); + + this.keyboardInput.registerKey( + this.keys.shift, + this.activateModifier.bind(this), + this.deactivateModifier.bind(this) + ); + }; + + KeyboardAndMouse.prototype.moveLeft = function() { + if (!this.modifier) { + this.lastLookDirection = -1; + this.onXyChange(this.lastLookDirection * Settings.VIEWPORT_LOOK_AHEAD, 0); + } + this.playerController.moveLeft(); + }; + + KeyboardAndMouse.prototype.moveRight = function() { + if (!this.modifier) { + this.lastLookDirection = 1; + this.onXyChange(this.lastLookDirection * Settings.VIEWPORT_LOOK_AHEAD, 0); + } + this.playerController.moveRight(); + }; + + KeyboardAndMouse.prototype.stop = function(e) { + + var isMovingLeft = this.keyboardInput.getKeyByKeyCode(this.keys.a).getActive() + || this.keyboardInput.getKeyByKeyCode(this.keys.left).getActive(); + + var isMovingRight = this.keyboardInput.getKeyByKeyCode(this.keys.d).getActive() + || this.keyboardInput.getKeyByKeyCode(this.keys.right).getActive(); + + if (isMovingLeft) { + this.moveLeft(); + return; + } + + if (isMovingRight) { + this.moveRight(); + return; + } + + this.playerController.stop(); + }; + + KeyboardAndMouse.prototype.mouseInit = function() { + var canvas = domController.getCanvas(); + var self = this; + + canvas.onmousedown = function(e) { + self.mousePosition = {x:0,y:0}; + + if(!self.playerController.player.isHoldingSomething()) { + var options = { + x: self.x, + y: self.y, + av: 0 + }; + self.playerController.handActionRequest(options); + } else { + self.swiper = new Swiper(); + + self.draw({ + movementX: 0, + movementY: 0 + }); + } + } + + canvas.onmousemove = function(e) { + if (self.swiper) { + self.draw(e); + } else if(self.modifier) { + self.updateViewport(e); + } + } + + canvas.onmouseup = function(e) { + if(self.swiper) { + var options = self.swiper.swipeEnd(e.x, e.y); + self.playerController.handActionRequest(options); + self.swiper = null; + } + } + }; + + KeyboardAndMouse.prototype.updateViewport = function(e) { + // -1 +1 +1 +1 xy scaling should + // -1 -1 +1 -1 be like this + + var movementX = e.movementX || + e.mozMovementX || + e.webkitMovementX || + 0; + + var movementY = e.movementY || + e.mozMovementY || + e.webkitMovementY || + 0; + + this.x += movementX / Settings.VIEWPORT_SPEED_FACTOR; + if(this.x > 1) { + this.x = 1; + } + if(this.x < -1) { + this.x = -1; + }; + + this.y -= movementY / Settings.VIEWPORT_SPEED_FACTOR; + if(this.y > 1) { + this.y = 1; + } + if(this.y < -1) { + this.y = -1; + } + + this.lastLookDirection = this.x >= 0 ? 1 : -1; + + this.onXyChange(this.x, this.y); + }; + + KeyboardAndMouse.prototype.draw = function(e) { + var movementX = e.movementX || + e.mozMovementX || + e.webkitMovementX || + 0; + + var movementY = e.movementY || + e.mozMovementY || + e.webkitMovementY || + 0; + + this.mousePosition.x += movementX; + this.mousePosition.y += movementY; + + this.swiper.swipe(this.mousePosition.x, -this.mousePosition.y); + }; + + KeyboardAndMouse.prototype.activateModifier = function() { + this.modifier = true; + this.playerController.activateModifier(); + }; + + KeyboardAndMouse.prototype.deactivateModifier = function() { + this.modifier = false; + this.x = this.lastLookDirection * Settings.VIEWPORT_LOOK_AHEAD; + this.y = 0; + this.onXyChange(this.x, this.y); + this.playerController.deactivateModifier(); + }; + return KeyboardAndMouse; + +}); \ No newline at end of file diff --git a/app/Game/Client/Control/Key.js b/app/Game/Client/Control/Key.js index 2377406..724ea3d 100755 --- a/app/Game/Client/Control/Key.js +++ b/app/Game/Client/Control/Key.js @@ -1,4 +1,9 @@ - define(function () { +define([ +], + +function () { + + "use strict"; function Key () { this._active = false; diff --git a/app/Game/Client/Control/KeyboardInput.js b/app/Game/Client/Control/KeyboardInput.js index 665d1aa..3c95baf 100755 --- a/app/Game/Client/Control/KeyboardInput.js +++ b/app/Game/Client/Control/KeyboardInput.js @@ -4,50 +4,47 @@ define([ function (Key) { - function KeyboardInput (playerController) { - - this._registry = {}; - this._playerController = playerController; - + function KeyboardInput () { + this.registry = {}; this.init(); } KeyboardInput.prototype.init = function () { // Using window is ok here because it only runs in the browser - window.onkeydown = this._onKeyDown.bind(this); - window.onkeyup = this._onKeyUp.bind(this); + window.onkeydown = this.onKeyDown.bind(this); + window.onkeyup = this.onKeyUp.bind(this); } KeyboardInput.prototype.registerKey = function (keyCode, onKeyDown, onKeyUp) { var key = new Key(); if(onKeyDown) key.setKeyDownFunction(onKeyDown); if(onKeyUp) key.setKeyUpFunction(onKeyUp); - this._registry[keyCode] = key; + this.registry[keyCode] = key; } - KeyboardInput.prototype._getKeyByKeyCode = function (keyCode) { - return this._registry[keyCode]; + KeyboardInput.prototype.getKeyByKeyCode = function (keyCode) { + return this.registry[keyCode]; } - KeyboardInput.prototype._onKeyDown = function (e) { - var key = this._getKeyByKeyCode(e.keyCode); + KeyboardInput.prototype.onKeyDown = function (e) { + var key = this.getKeyByKeyCode(e.keyCode); if (key && !key.getActive()) { var callback = key.getKeyDownFunction(); - if(callback) this._playerController[callback](); key.setActive(true); + if(callback) callback(); } // Prevent tab from changing focus if(e.keyCode == 9) return false; } - KeyboardInput.prototype._onKeyUp = function (e) { - var key = this._getKeyByKeyCode(e.keyCode); + KeyboardInput.prototype.onKeyUp = function (e) { + var key = this.getKeyByKeyCode(e.keyCode); if (key && key.getActive()) { var callback = key.getKeyUpFunction(); - if(callback) this._playerController[callback](); key.setActive(false); + if(callback) callback(); } // Prevent tab from changing focus diff --git a/app/Game/Client/Control/PlayerController.js b/app/Game/Client/Control/PlayerController.js index 2125dfa..82db175 100755 --- a/app/Game/Client/Control/PlayerController.js +++ b/app/Game/Client/Control/PlayerController.js @@ -1,137 +1,124 @@ define([ "Game/Core/Control/PlayerController", - "Game/Client/Control/KeyboardInput", - "Game/Client/Control/Input/MouseInput", "Lib/Utilities/NotificationCenter", - "Game/Client/Control/Input/GamepadInput", + "Game/Client/Control/Inputs/KeyboardAndMouse", + "Game/Client/Control/Inputs/Gamepad", + "Game/Client/PointerLockManager" ], -function (Parent, KeyboardInput, MouseInput, Nc, GamepadInput) { +function (Parent, nc, KeyboardAndMouse, Gamepad, pointerLockManager) { + + "use strict"; function PlayerController (me) { Parent.call(this, me); - this.keyboardInput = new KeyboardInput(this); - this.xyInput = new MouseInput(this); - this.gamepadInput = new GamepadInput(this); - - this.ncTokens = [ - Nc.on(Nc.ns.client.input.xy.change, this.setXY, this), - Nc.on(Nc.ns.client.input.handAction.request, this.handActionRequest, this) - ]; - - var keys = { - w:87, - a:65, - s:83, - d:68, - - f:70, - g:71, - k:75, - - up: 38, - left: 37, - down: 40, - right: 39, - - space: 32, - - tab: 9 - } - - this.init(keys); + this.keyboardAndMouse = new KeyboardAndMouse(this); + this.gamepad = new Gamepad(this); } PlayerController.prototype = Object.create(Parent.prototype); PlayerController.prototype.update = function() { + Parent.prototype.update.call(this); - this.gamepadInput.update(); + this.gamepad.update(); }; - PlayerController.prototype.init = function (keys) { - - this.keyboardInput.registerKey(keys.a, 'moveLeft', 'stop'); - this.keyboardInput.registerKey(keys.left, 'moveLeft', 'stop'); - - this.keyboardInput.registerKey(keys.d, 'moveRight', 'stop'); - this.keyboardInput.registerKey(keys.right, 'moveRight', 'stop'); - - this.keyboardInput.registerKey(keys.w, 'jump', 'jumpStop'); - this.keyboardInput.registerKey(keys.up, 'jump', 'jumpStop'); - this.keyboardInput.registerKey(keys.space, 'jump', 'jumpStop'); - - this.keyboardInput.registerKey(keys.tab, 'showInfo', 'hideInfo'); - - this.keyboardInput.registerKey(keys.f, 'handActionLeft'); - this.keyboardInput.registerKey(keys.g, 'handActionRight'); - - this.keyboardInput.registerKey(keys.k, 'suicide'); - } - PlayerController.prototype.moveLeft = function () { + if (!this.isPlayerInputAllowed()) return; Parent.prototype.moveLeft.call(this); - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, 'moveLeft'); + nc.trigger(nc.ns.client.to.server.gameCommand.send, 'moveLeft'); } PlayerController.prototype.moveRight = function () { + if (!this.isPlayerInputAllowed()) return; Parent.prototype.moveRight.call(this); - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, 'moveRight'); + nc.trigger(nc.ns.client.to.server.gameCommand.send, 'moveRight'); } + // always allow to stop, to prevent endless running PlayerController.prototype.stop = function () { Parent.prototype.stop.call(this); - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, 'stop'); + nc.trigger(nc.ns.client.to.server.gameCommand.send, 'stop'); } PlayerController.prototype.jump = function () { + if (!this.isPlayerInputAllowed()) return; Parent.prototype.jump.call(this); - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, 'jump'); + nc.trigger(nc.ns.client.to.server.gameCommand.send, 'jump'); } + // always allow to stop. PlayerController.prototype.jumpStop = function () { Parent.prototype.jumpStop.call(this); - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, 'jumpStop'); + nc.trigger(nc.ns.client.to.server.gameCommand.send, 'jumpStop'); } PlayerController.prototype.setXY = function(x, y) { + if (!this.isPlayerInputAllowed()) return; var options = {x:x, y:y}; Parent.prototype.lookAt.call(this, options); - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, 'lookAt', options); - }; - - PlayerController.prototype.handActionLeft = function() { - this.handActionRequest(-0.5, 0.5); - }; - - PlayerController.prototype.handActionRight = function() { - this.handActionRequest(0.5, 0.5); + nc.trigger(nc.ns.client.to.server.gameCommand.send, 'lookAt', options); }; PlayerController.prototype.suicide = function() { - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, "suicide"); + if (!this.isPlayerInputAllowed()) return; + nc.trigger(nc.ns.client.to.server.gameCommand.send, "suicide"); }; - PlayerController.prototype.handActionRequest = function(x, y) { - var options = {x:x, y:y}; - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, "handActionRequest", options); + PlayerController.prototype.handActionRequest = function(options) { + if (!this.isPlayerInputAllowed()) return; + nc.trigger(nc.ns.client.to.server.gameCommand.send, "handActionRequest", options); }; PlayerController.prototype.showInfo = function() { - Nc.trigger(Nc.ns.client.game.gameInfo.toggle, true); + if (!this.isPlayerInputAllowed()) return; + nc.trigger(nc.ns.client.game.gameStats.toggle, true); }; PlayerController.prototype.hideInfo = function() { - Nc.trigger(Nc.ns.client.game.gameInfo.toggle, false); + if (!this.isPlayerInputAllowed()) return; + nc.trigger(nc.ns.client.game.gameStats.toggle, false); }; - PlayerController.prototype.destroy = function() { - Nc.offAll(this.ncTokens); - Parent.prototype.destroy.call(this); + PlayerController.prototype.zoomIn = function() { + if (!this.isPlayerInputAllowed()) return; + nc.trigger(nc.ns.client.game.zoomIn, true); }; + PlayerController.prototype.zoomOut = function() { + if (!this.isPlayerInputAllowed()) return; + nc.trigger(nc.ns.client.game.zoomOut, false); + }; + PlayerController.prototype.zoomReset = function() { + if (!this.isPlayerInputAllowed()) return; + nc.trigger(nc.ns.client.game.zoomReset, false); + }; + + PlayerController.prototype.activateModifier = function() { + if (!this.isPlayerInputAllowed()) return; + Parent.prototype.activateModifier.call(this); + nc.trigger(nc.ns.client.to.server.gameCommand.send, "activateModifier"); + }; + + PlayerController.prototype.deactivateModifier = function() { + if (!this.isPlayerInputAllowed()) return; + Parent.prototype.deactivateModifier.call(this); + nc.trigger(nc.ns.client.to.server.gameCommand.send, "deactivateModifier"); + }; + + /* + * Client overwrite - allow player input if PointerLock is locked to canvas + * and is not in between games + */ + PlayerController.prototype.isPlayerInputAllowed = function() { + return pointerLockManager.isLocked() + && Parent.prototype.isPlayerInputAllowed.call(this); + }; + + return PlayerController; }); \ No newline at end of file diff --git a/app/Game/Client/Control/Swiper.js b/app/Game/Client/Control/Swiper.js new file mode 100644 index 0000000..7e0909f --- /dev/null +++ b/app/Game/Client/Control/Swiper.js @@ -0,0 +1,175 @@ +define([ + "Lib/Utilities/NotificationCenter", +], + +function (nc) { + + var MAX_LENGTH = 200; + var MIN_LENGTH = 5; + + function Swiper() { + this.points = []; + this.angleSum = 0; + this.lengthSum = 0; + this.finished = false; + } + + Swiper.prototype.swipe = function(x, y) { + + if(this.finished) return; + + var points = this.points; + + // Check if swipe is long enough to count + if(points.length >= 1) { + var lastPoint = points[points.length-1]; + var a = Math.abs(x) - Math.abs(lastPoint.x); + var b = Math.abs(y) - Math.abs(lastPoint.y); + var currentLength = Math.sqrt(a * a + b * b); // Pythagoras -> hypotenuse + + if(currentLength < MIN_LENGTH) return; + } + + points.push({x:x, y:y}); + + if(points.length >= 2) { + + this.updateLengthSum(currentLength); + + if(this.points.length >= 3) { + this.updateAngleSum(currentLength); + } + } + + var i = points.length - 1; + nc.trigger(nc.ns.client.view.swiper.swipe, points[i].x, points[i].y); + } + + Swiper.prototype.updateLengthSum = function(currentLength) { + var points = this.points; + var i = points.length - 1; + + var lengthSum = this.lengthSum + Math.abs(currentLength); + if(lengthSum > MAX_LENGTH) { + + var diff = lengthSum - MAX_LENGTH; + var ratio = (currentLength - diff) / currentLength; + + // Offset triangle to origin + var x0 = points[i].x - points[i-1].x; + var y0 = points[i].y - points[i-1].y; + + // Multiply with ratio and offset back + var clippedX = x0 * ratio + points[i-1].x; + var clippedY = y0 * ratio + points[i-1].y; + + points[i].x = clippedX; + points[i].y = clippedY; + + this.finished = true; + + lengthSum = MAX_LENGTH; + } + + this.lengthSum = lengthSum; + }; + + Swiper.prototype.updateAngleSum = function(currentLength) { + + var points = this.points; + var i = points.length - 1; + + // last two lines + var vectors = [ + { + x: points[i].x - points[i-1].x, + y: points[i].y - points[i-1].y + }, + { + x: points[i-2].x - points[i-1].x, + y: points[i-2].y - points[i-1].y + } + ]; + + var yx = vectors[0].y * vectors[1].x; + var xy = vectors[0].x * vectors[1].y; + var direction = 0; + if(yx > xy) { + direction = -1; + } else if (yx < xy) { + direction = 1; + } + + var dotProduct = vectors[0].x + * vectors[1].x + + vectors[0].y + * vectors[1].y; + + var lastLength = Math.sqrt( + Math.pow(vectors[1].x, 2) + + Math.pow(vectors[1].y, 2) + ); + + var angle = 180 - (Math.acos((dotProduct / (currentLength * lastLength)) % 1) * 180 / Math.PI); + angle *= direction; + + if(!isNaN(parseFloat(angle)) && direction != 0) { + this.angleSum += angle; + } + }; + + Swiper.prototype.swipeEnd = function(x, y) { + var angularVelocity = this.angleSum; + var length = this.lengthSum; + + if (this.points.length < 1) { + return { + x: 0, + y: 0, + av: 0 + } + } + + var p0x = this.points[0].x; + var p0y = this.points[0].y; + var sumx = 0; + var sumy = 0; + + nc.trigger(nc.ns.client.view.swiper.end); + + for(var i=0, count = this.points.length; i < count; i++) { + var p = this.points[i]; + sumx += p.x - p0x; + sumy += p.y - p0y; + } + + var direction = { + x: sumx / count, + y: sumy / count + }; + + var larger = Math.abs(direction.x) > Math.abs(direction.y) ? direction.x : direction.y; + direction.x /= Math.abs(larger); + direction.y /= Math.abs(larger); + + this.angleSum = 0; + this.lengthSum = 0; + this.points = []; + this.finished = false; + + return { + x: direction.x * length / 100, + y: direction.y * length / 100, + av: angularVelocity / 100 + } + } + + Swiper.prototype.destroy = function() { + for (var i = 0; i < this.ncTokens.length; i++) { + nc.off(this.ncTokens[i]); + }; + }; + + return Swiper; + +}); \ No newline at end of file diff --git a/app/Game/Client/GameController.js b/app/Game/Client/GameController.js index 9cb1ca6..9d85885 100755 --- a/app/Game/Client/GameController.js +++ b/app/Game/Client/GameController.js @@ -11,16 +11,16 @@ define([ "Game/Client/GameObjects/Doll", "Game/Client/View/DomController", "Lib/Utilities/Protocol/Helper", - "Game/Client/Me" + "Game/Client/Me", + "Game/Client/AudioPlayer", + "Game/Client/PointerLockManager", + "Lib/Utilities/Assert", + "Lib/Utilities/Exception" ], -function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, requestAnimFrame, Settings, GameObject, Doll, DomController, ProtocolHelper, Me) { +function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, nc, requestAnimFrame, Settings, GameObject, Doll, domController, ProtocolHelper, Me, AudioPlayer, pointerLockManager, Assert, Exception) { - if (!window.cancelAnimationFrame) { - window.cancelAnimationFrame = function(id) { - clearTimeout(id); - }; - } + "use strict"; function GameController (options) { @@ -28,11 +28,12 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque this.view = ViewManager.createView(); this.me = null; this.animationRequestId = null; + this.audioPlayer = null; Parent.call(this, options); this.ncTokens = this.ncTokens.concat([ - Nc.on(Nc.ns.client.game.gameInfo.toggle, this.toggleInfo, this) + nc.on(nc.ns.client.game.gameStats.toggle, this.toggleGameStats, this) ]); } @@ -40,60 +41,52 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque GameController.prototype.getMe = function () { return this.me; - } + }; GameController.prototype.update = function () { Parent.prototype.update.call(this); - DomController.statsBegin(); - this.animationRequestId = requestAnimFrame(this.update.bind(this)); - this.physicsEngine.update(); if(this.me) { this.me.update(); - this.mePositionStateUpdate(); + this.mePositionStateOverride(); } - for (var i = 0; i < this.gameObjects.animated.length; i++) { - this.gameObjects.animated[i].render(); - } + nc.trigger(nc.ns.client.game.events.render); this.view.render(); + domController.fpsStep(); + }; - DomController.statsEnd(); - } - - GameController.prototype.mePositionStateUpdate = function() { - if(this.me.isPositionStateUpdateNeeded()) { - Nc.trigger(Nc.ns.client.to.server.gameCommand.send, "mePositionStateUpdate", this.me.getPositionStateUpdate()); + GameController.prototype.mePositionStateOverride = function() { + if(this.me.isPositionStateOverrideNeeded()) { + nc.trigger( + nc.ns.client.to.server.gameCommand.send, + "mePositionStateOverride", + this.me.getPositionStateOverride() + ); } }; GameController.prototype.onClientReadyResponse = function(options) { - - if (options.worldUpdate) { - this.onWorldUpdate(options.worldUpdate); - } + var i; if (options.runtimeItems) { - for (var i = 0; i < options.runtimeItems.length; i++) { + for (i = 0; i < options.runtimeItems.length; i++) { + var itemDef = options.runtimeItems[i]; - var alreadyExists = false; - for (var i = 0; i < this.gameObjects.animated.length; i++) { - if(this.gameObjects.animated[i].uid == itemDef.uid) { - alreadyExists = true; - break; - } - }; - - if(!alreadyExists) { - var item = this.level.createItem(itemDef.uid, itemDef.options); + if(!this.getItemByUid(itemDef.uid)) { + // When creating from synchronization we need to bring it into level format (px) + itemDef.options.x *= Settings.RATIO; + itemDef.options.y *= Settings.RATIO; + this.level.createItem(itemDef.uid, itemDef.options); + console.log("Creating runtime Item: ", itemDef.options.name, itemDef.uid) } - }; + } } this.setMe(); @@ -101,43 +94,51 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque this.clientIsReady = true; // needs to stay before onSpawnPlayer if (options.spawnedPlayers) { - for(var i = 0; i < options.spawnedPlayers.length; i++) { + for(i = 0; i < options.spawnedPlayers.length; i++) { this.onSpawnPlayer(options.spawnedPlayers[i]); } } + + if (options.worldUpdate) { // needs to stay after onSpawnPlayer otherwise others doll will not be there + this.onWorldUpdate(options.worldUpdate); + } + + //this.audioPlayer = new AudioPlayer(Settings.AUDIO_PATH + "city.mp3"); + //this.audioPlayer.play(); }; - GameController.prototype.onWorldUpdate = function (updateData) { - var body = this.physicsEngine.world.GetBodyList(); - do { - var userData = body.GetUserData(); - if (userData instanceof GameObject) { - var gameObject = userData; - if(updateData[gameObject.uid]) { - var update = updateData[gameObject.uid]; + /* - if (gameObject instanceof Doll) { - if(gameObject === this.me.doll) { - this.me.setLastServerPositionState(update); - if(!this.me.acceptPositionStateUpdateFromServer()) { - continue; // this is to ignore own doll updates from world update - } - } - gameObject.setActionState(update.as); - gameObject.lookAt(update.laxy.x, update.laxy.y); - } + TODO : + - remove this + - overwrite setUpdateData inside client / Me with an empty function - body.SetAwake(true); - body.SetPosition(update.p); - body.SetAngle(update.a); - body.SetLinearVelocity(update.lv); - body.SetAngularVelocity(update.av); - } + GameController.prototype.onWorldUpdateGameObject = function(body, gameObject, update) { + if(gameObject === this.me.doll) { + this.me.setLastServerPositionState(update); + if(!this.me.acceptPositionStateUpdateFromServer()) { + return; // this is to ignore own doll updates from world update } - - } while (body = body.GetNext()); + } + Parent.prototype.onWorldUpdateGameObject.call(this, body, gameObject, update); + }; + */ + + GameController.prototype.onRemoveGameObject = function(options) { + + }; + + GameController.prototype.updateGameObject = function (gameObject, gameObjectUpdate) { + if(gameObject === this.me.doll) { + this.me.setLastServerPositionState(gameObjectUpdate); + if(!this.me.acceptPositionStateUpdateFromServer()) { + return; // this is to ignore own doll updates from world update + } + } + + Parent.prototype.updateGameObject.call(this, gameObject, gameObjectUpdate); } GameController.prototype.createMe = function(user) { @@ -146,9 +147,8 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque }; GameController.prototype.setMe = function() { - this.me.setPlayerController(new PlayerController(this.me)); this.view.setMe(this.me); - } + }; GameController.prototype.onGameCommand = function(message) { ProtocolHelper.applyCommand(message, this); @@ -174,78 +174,35 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque playerId: playerId }); } - } + }; GameController.prototype.onHandActionResponse = function(options) { var player = this.players[options.playerId]; - - var item = null; - for (var i = 0; i < this.gameObjects.animated.length; i++) { - var currentItem = this.gameObjects.animated[i]; - if(currentItem.uid == options.itemUid) { - item = currentItem; - break; - } - }; + var item = this.getItemByUid(options.itemUid); if(item) { if(options.action == "throw") { - player.throw(options.x, options.y, item); + player.throw(options, item); } else if(options.action == "grab") { player.grab(item); } } else { - console.warn("Item for joint can not be found locally. " + options.itemUid) + console.warn("Item for joint can not be found locally. " + options.itemUid); } }; GameController.prototype.onUpdateStats = function(options) { var player = this.players[options.playerId]; + if(!player) { + throw new Exception("No player with id: " + options.playerId); + } player.setStats(options.stats); - // FIXME: move to canvas later - if(player == this.me) { - DomController.setHealth(player.stats.health); - } - }; - - GameController.prototype.onPlayerKill = function(options) { - var player = this.players[options.playerId]; - var killedByPlayer = this.players[options.killedByPlayerId]; - player.kill(killedByPlayer, options.ragDollId); - }; - - GameController.prototype.onPositionStateReset = function(options) { - this.me.resetPositionState(options); - }; - - GameController.prototype.onRemoveGameObject = function(options) { - var object = null; - for (var i = 0; i < this.gameObjects[options.type].length; i++) { - if(this.gameObjects[options.type][i].uid == options.uid) { - object = this.gameObjects[options.type][i]; - break; - } - } - if(object) { - //this.onGameObjectRemove(options.type, object); - object.destroy(); - } else { - console.warn("GameObject for removal can not be found locally. " + options.uid); - } - }; - - GameController.prototype.loadLevel = function (path) { - Parent.prototype.loadLevel.call(this, path); - } - - GameController.prototype.toggleInfo = function(show) { - var playersArray = []; for (var key in this.players) { playersArray.push(this.players[key]); - }; + } var sortedPlayers = playersArray.sort(function(a,b) { if(a.stats.score > b.stats.score) return -1; @@ -257,48 +214,66 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque return 0; }); - function pad(string, max, alignLeft) { - string = string.substring(0, max - 1); + nc.trigger(nc.ns.client.view.gameStats.update, sortedPlayers); + }; - var spaces = new Array( max - string.length + 1 ).join(" "); - if(alignLeft) { - return string + spaces; - } else { - return spaces + string; - } - } + GameController.prototype.onPlayerKill = function(options) { + var player = this.players[options.playerId]; + var killedByPlayer = this.players[options.killedByPlayerId]; + player.kill(killedByPlayer, options.ragDollId); - var string = "" + - pad("#", 2, false) + " " + - pad("Name", 12, true) + - pad("Score", 6, false) + - pad("Deaths", 7, false) + - pad("Health", 7, false) + - "\n-----------------------------------\n"; + nc.trigger(nc.ns.client.view.gameStats.kill, { + victim: { + name: player.user.options.nickname, + isMe: player === this.me + }, + killer: { + name: killedByPlayer.user.options.nickname, + isMe: killedByPlayer === this.me + }, + item: options.item + }); + }; - var lines = []; - sortedPlayers.forEach(function(player, i) { - var name = player.getNickname(); - lines.push( - pad("" + (i + 1) + ".", 2, false) + " " + - pad(name, 12, true) + - pad("" + player.stats.score, 6, false) + - pad("" + player.stats.deaths, 7, false) + - pad("" + parseInt(player.stats.health, 10), 7, false) - ); - }, this); + GameController.prototype.onPositionStateReset = function(options) { + this.me.resetPositionState(options); + }; - string += lines.join("\n"); + GameController.prototype.loadLevel = function (path) { + Parent.prototype.loadLevel.call(this, path); + }; - this.view.toggleInfo(show, string); + GameController.prototype.onLevelLoaded = function () { + pointerLockManager.update(null, {start:true}); + }; + + GameController.prototype.toggleGameStats = function(show) { + nc.trigger(nc.ns.client.view.gameStats.toggle, show); + }; + + GameController.prototype.beginRound = function() { + this.me.setInBetweenRounds(false); + }; + + GameController.prototype.endRound = function() { + this.me.setInBetweenRounds(true); + this.toggleGameStats(true); }; GameController.prototype.destroy = function() { + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + cancelAnimationFrame(this.animationRequestId); Parent.prototype.destroy.call(this); + //this.audioPlayer.destroy(); + this.view.destroy(); }; diff --git a/app/Game/Client/GameObjects/Doll.js b/app/Game/Client/GameObjects/Doll.js index 7671555..a5ea6b4 100755 --- a/app/Game/Client/GameObjects/Doll.js +++ b/app/Game/Client/GameObjects/Doll.js @@ -2,12 +2,17 @@ define([ "Game/Core/GameObjects/Doll", "Game/Config/Settings", "Lib/Utilities/NotificationCenter", - "Lib/Utilities/Exception" + "Lib/Utilities/Exception", + "Lib/Utilities/ColorConverter", + "Game/Client/View/Abstract/Layer", ], -function (Parent, Settings, Nc, Exception) { +function (Parent, Settings, nc, Exception, ColorConverter, Layer) { + + "use strict"; - function Doll(physicsEngine, uid, player) { + function Doll(physicsEngine, uid, player) { + this.layerId = Layer.ID.SPAWN; this.animationDef = { "stand": [1,1], "walk": [2,28], @@ -20,31 +25,71 @@ function (Parent, Settings, Nc, Exception) { "run": [104,126] } - this.animatedMeshes = {}; + this.lastStep = Date.now(); + + this.animatedMeshesContainer = { + withArms: {}, + withoutArms: {} + }; + this.animatedMeshes = this.animatedMeshesContainer.withArms; this.headMesh = null; + this.holdingArmMesh = null; + + var converter = new ColorConverter(); + this.primaryColor = converter.getColorByName(player.getNickname()); Parent.call(this, physicsEngine, uid, player); } Doll.prototype = Object.create(Parent.prototype); - Doll.prototype.setActionState = function(state) { + Doll.prototype.setActionState = function(state, force) { - if(this.actionState == state) return; + if(!force && this.actionState == state) return; if(!state) throw new Exception("action state is undefined"); if(this.animatedMeshes[this.actionState]) { - Nc.trigger(Nc.ns.client.view.mesh.update, this.animatedMeshes[this.actionState], { visible: false }); + nc.trigger( + nc.ns.client.view.mesh.update, + this.layerId, + this.animatedMeshesContainer.withArms[this.actionState], + { visible: false } + ); + nc.trigger( + nc.ns.client.view.mesh.update, + this.layerId, + this.animatedMeshesContainer.withoutArms[this.actionState], + { visible: false } + ); } Parent.prototype.setActionState.call(this, state); - Nc.trigger(Nc.ns.client.view.mesh.update, this.animatedMeshes[this.actionState], { visible: true }); + nc.trigger( + nc.ns.client.view.mesh.update, + this.layerId, + this.animatedMeshes[this.actionState], + { + visible: true, + xScale: this.lookDirection + } + ); } Doll.prototype.createMesh = function() { + var self = this; + + var setShirtColor = function (mesh) { + nc.trigger(nc.ns.client.view.mesh.addFilter, self.layerId, mesh, 'colorRangeReplace', { + minColor: 0x3b4a31, + maxColor: 0x657f54, + newColor: self.primaryColor, + brightnessOffset: 0.56 + }); + } + // Body var padF = function(n) { @@ -55,53 +100,101 @@ function (Parent, Settings, Nc, Exception) { var self = this; - for (var key in this.animationDef) { - var start = this.animationDef[key][0]; - var end = this.animationDef[key][1]; + var arms = ["withArms", "withoutArms"]; + for (var j = 0; j < arms.length; j++) { + var arm = arms[j]; + for (var key in this.animationDef) { + var start = this.animationDef[key][0]; + var end = this.animationDef[key][1]; - var texturePaths = []; - for (var i = start; i <= end; i++) { - texturePaths.push( - Settings.GRAPHICS_PATH - + Settings.GRAPHICS_SUBPATH_CHARACTERS - + this.characterName - //+ "/Animation/WithoutArms/ChuckAnimationsWithoutArms0" - + "/Animation/WithArms/ChuckAnimations0" - + padF(i) - + ".png" - ); + var texturePaths = []; + for (var i = start; i <= end; i++) { + + /* + // Multiple File Animations + texturePaths.push( + Settings.GRAPHICS_PATH + + Settings.GRAPHICS_SUBPATH_CHARACTERS + + this.characterName + + "/Animation/" + arm.toUpperCaseFirstChar() + "/ChuckAnimations0" + + padF(i) + + ".png" + ); + */ + + // Single File Animations (animation names from chuck_sheet.json, use option fromFrame=true) + texturePaths.push( + "Chuck" + arm.toUpperCaseFirstChar() + "0" + padF(i) + ".png" + ); + } + + + var callback = function(mesh) { + self.animatedMeshesContainer[arm][key] = mesh; + nc.trigger(nc.ns.client.view.mesh.add, self.layerId, mesh); + + setShirtColor(mesh); + }; + + nc.trigger(nc.ns.client.view.animatedMesh.create, this.layerId, texturePaths, callback, { + visible: false, + pivot: { + x: 0, + y: 40 * 4 + }, + xScale: 0.25, + yScale: 0.25, + anchor: { + x: 0.5, + y: 0 + }, + fromFrame: true + }); } - var callback = function(mesh) { - self.animatedMeshes[key] = mesh; - Nc.trigger(Nc.ns.client.view.mesh.add, mesh); - }; - - Nc.trigger(Nc.ns.client.view.animatedMesh.create, texturePaths, callback, { - visible: false, - pivot: { - x: 35/2 * 4, - y: 40 * 4 - }, - width: 35, - height: 40 - }); - } + }; // Head var texturePath = Settings.GRAPHICS_PATH + "Characters/Chuck/head.png"; var callback = function (mesh) { self.headMesh = mesh; - Nc.trigger(Nc.ns.client.view.mesh.add, mesh); + nc.trigger(nc.ns.client.view.mesh.add, self.layerId, mesh); } - Nc.trigger(Nc.ns.client.view.mesh.create, texturePath, callback, { + nc.trigger(nc.ns.client.view.mesh.create, this.layerId, texturePath, callback, { pivot: { x: 5, y: 12 }, width: 10, - height: 12 + height: 12, + anchor: { + x: 0, + y: 0 + } + }); + + // Holding arm + + texturePath = Settings.GRAPHICS_PATH + "Characters/Chuck/holdingArm.png"; + var callback = function (mesh) { + self.holdingArmMesh = mesh; + nc.trigger(nc.ns.client.view.mesh.add, self.layerId, mesh); + setShirtColor(mesh); + } + nc.trigger(nc.ns.client.view.mesh.create, this.layerId, texturePath, callback, { + visible: false, + pivot: { + //x: 35/2 * 4, + x: 0, + y: 40 * 4 + }, + width: 35, + height: 40, + anchor: { + x: 0.5, + y: 0 + } }); } @@ -113,18 +206,28 @@ function (Parent, Settings, Nc, Exception) { if(oldLookDirection != this.lookDirection) { for(var key in this.animatedMeshes) { - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.animatedMeshes[key], { xScale: this.lookDirection } ); } + + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, + this.holdingArmMesh, + { + xScale: this.lookDirection + } + ); } var angle = Math.atan2(this.lookAtXY.x, this.lookAtXY.y) / 2 - 0.7855 * this.lookDirection; // 0.7855 = 45° - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.headMesh, { xScale: this.lookDirection, @@ -133,35 +236,70 @@ function (Parent, Settings, Nc, Exception) { ); } + Doll.prototype.grab = function(item) { + Parent.prototype.grab.call(this, item); + this.animatedMeshes = this.animatedMeshesContainer.withoutArms; + this.setActionState(this.actionState, true); + nc.trigger(nc.ns.client.view.mesh.update, this.layerId, this.holdingArmMesh, { visible: true }); + }; + + Doll.prototype.throw = function(item, options) { + Parent.prototype.throw.call(this, item, options); + this.animatedMeshes = this.animatedMeshesContainer.withArms; + this.setActionState(this.actionState, true); + nc.trigger(nc.ns.client.view.mesh.update, this.layerId, this.holdingArmMesh, { visible: false }); + }; Doll.prototype.destroy = function () { for (var key in this.animatedMeshes) { - Nc.trigger(Nc.ns.client.view.mesh.remove, this.animatedMeshes[key]); + nc.trigger(nc.ns.client.view.mesh.remove, this.layerId, this.animatedMeshes[key]); } - Nc.trigger(Nc.ns.client.view.mesh.remove, this.headMesh); + nc.trigger(nc.ns.client.view.mesh.remove, this.layerId, this.headMesh); Parent.prototype.destroy.call(this); } Doll.prototype.render = function() { if(this.actionState) { - Nc.trigger(Nc.ns.client.view.mesh.update, + + var stepLength = (Date.now() - this.lastStep); + this.lastStep = Date.now(); + + // compare current framerate to wanted and get factor + // (stepLength / 60) * 2 + // * 2 to scale to flash fps + + var factor = stepLength / 30; + + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.animatedMeshes[this.actionState], { x: this.body.GetPosition().x * Settings.RATIO, y: this.body.GetPosition().y * Settings.RATIO, - rotation: this.body.GetAngle() + animationSpeed: factor + //rotation: this.body.GetAngle() } ); - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.headMesh, { x: this.body.GetPosition().x * Settings.RATIO, y: this.body.GetPosition().y * Settings.RATIO - this.height + this.headHeight } ) + + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, + this.holdingArmMesh, + { + x: this.body.GetPosition().x * Settings.RATIO, + y: this.body.GetPosition().y * Settings.RATIO + } + ) } } diff --git a/app/Game/Client/GameObjects/GameObject.js b/app/Game/Client/GameObjects/GameObject.js index 577830d..f193bbc 100755 --- a/app/Game/Client/GameObjects/GameObject.js +++ b/app/Game/Client/GameObjects/GameObject.js @@ -4,7 +4,9 @@ define([ "Lib/Utilities/NotificationCenter" ], -function (Parent, Exception, Nc) { +function (Parent, Exception, nc) { + + "use strict"; function GameObject(physicsEngine, uid) { Parent.call(this, physicsEngine, uid); @@ -25,7 +27,7 @@ function (Parent, Exception, Nc) { GameObject.prototype.createMesh = function() { throw new Exception('Abstract method GameObject.createMesh not overwritten'); }; - + return GameObject; }); \ No newline at end of file diff --git a/app/Game/Client/GameObjects/Item.js b/app/Game/Client/GameObjects/Item.js index 71bc2e7..f798416 100755 --- a/app/Game/Client/GameObjects/Item.js +++ b/app/Game/Client/GameObjects/Item.js @@ -1,13 +1,21 @@ define([ "Game/Core/GameObjects/Item", "Game/Config/Settings", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Game/Client/View/Abstract/Layer" ], -function (Parent, Settings, Nc) { +function (Parent, Settings, nc, Layer) { + + "use strict"; function Item(physicsEngine, uid, options) { + this.layerId = Layer.ID.ITEM; Parent.call(this, physicsEngine, uid, options); + + this.ncTokens = this.ncTokens.concat([ + nc.on(nc.ns.client.game.events.render, this.render, this) + ]); } Item.prototype = Object.create(Parent.prototype); @@ -22,10 +30,11 @@ function (Parent, Settings, Nc) { var callback = function(mesh) { self.mesh = mesh; - Nc.trigger(Nc.ns.client.view.mesh.add, mesh); + nc.trigger(nc.ns.client.view.mesh.add, self.layerId, mesh); } - Nc.trigger(Nc.ns.client.view.mesh.create, + nc.trigger(nc.ns.client.view.mesh.create, + this.layerId, texturePath, callback, { @@ -40,13 +49,14 @@ function (Parent, Settings, Nc) { }; Item.prototype.destroy = function() { - Nc.trigger(Nc.ns.client.view.mesh.remove, this.mesh); + nc.trigger(nc.ns.client.view.mesh.remove, this.layerId, this.mesh); Parent.prototype.destroy.call(this); }; Item.prototype.render = function() { - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.mesh, { x: this.body.GetPosition().x * Settings.RATIO, @@ -62,7 +72,8 @@ function (Parent, Settings, Nc) { Parent.prototype.flip.call(this, direction); if(oldFlipDirection != direction) { - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.mesh, { xScale: direction diff --git a/app/Game/Client/GameObjects/Items/RagDoll.js b/app/Game/Client/GameObjects/Items/RagDoll.js index 70d4117..73e0b83 100644 --- a/app/Game/Client/GameObjects/Items/RagDoll.js +++ b/app/Game/Client/GameObjects/Items/RagDoll.js @@ -2,12 +2,16 @@ define([ "Game/Core/GameObjects/Items/RagDoll", "Game/Core/GameObjects/Item", "Game/Config/Settings", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Game/Client/View/Abstract/Layer" ], -function (Parent, CoreItem, Settings, Nc) { +function (Parent, CoreItem, Settings, nc, Layer) { + + "use strict"; function RagDoll(physicsEngine, uid, options) { + this.layerId = Layer.ID.SPAWN; this.limbMeshes = {}; this.baseMeshName = "chest"; this.characterName = "Chuck"; @@ -36,10 +40,11 @@ function (Parent, CoreItem, Settings, Nc) { self.limbMeshes[name] = mesh; } - Nc.trigger(Nc.ns.client.view.mesh.add, mesh); + nc.trigger(nc.ns.client.view.mesh.add, self.layerId, mesh); } - Nc.trigger(Nc.ns.client.view.mesh.create, + nc.trigger(nc.ns.client.view.mesh.create, + this.layerId, texturePath + name + ".png", callback, { @@ -59,7 +64,8 @@ function (Parent, CoreItem, Settings, Nc) { if(this.limbs) { for(var name in this.limbMeshes) { if(this.limbs[name]) { - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.limbMeshes[name], { x: this.limbs[name].GetPosition().x * Settings.RATIO, @@ -79,7 +85,8 @@ function (Parent, CoreItem, Settings, Nc) { CoreItem.prototype.flip.call(this, direction); if(oldFlipDirection != direction) { - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.mesh, { xScale: direction @@ -87,7 +94,8 @@ function (Parent, CoreItem, Settings, Nc) { ); for (var name in this.limbMeshes) { - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.limbMeshes[name], { xScale: direction @@ -100,7 +108,7 @@ function (Parent, CoreItem, Settings, Nc) { RagDoll.prototype.destroy = function() { for (var name in this.limbMeshes) { - Nc.trigger(Nc.ns.client.view.mesh.remove, this.limbMeshes[name]); + nc.trigger(nc.ns.client.view.mesh.remove, this.layerId, this.limbMeshes[name]); }; Parent.prototype.destroy.call(this); diff --git a/app/Game/Client/GameObjects/Items/Rube.js b/app/Game/Client/GameObjects/Items/Rube.js deleted file mode 100644 index e029668..0000000 --- a/app/Game/Client/GameObjects/Items/Rube.js +++ /dev/null @@ -1,26 +0,0 @@ -define([ - "Game/Core/GameObjects/Items/Rube" -], - -function (Parent) { - - function Rube(physicsEngine, uid, options) { - Parent.call(this, physicsEngine, uid, options); - } - - Rube.prototype = Object.create(Parent.prototype); - - Rube.prototype.createMesh = function() { - }; - - Rube.prototype.destroy = function() { - }; - - Rube.prototype.render = function() { - } - - Rube.prototype.flip = function(direction) { - }; - - return Rube; -}); \ No newline at end of file diff --git a/app/Game/Client/GameObjects/Items/RubeDoll.js b/app/Game/Client/GameObjects/Items/RubeDoll.js new file mode 100644 index 0000000..6c6034b --- /dev/null +++ b/app/Game/Client/GameObjects/Items/RubeDoll.js @@ -0,0 +1,254 @@ +define([ + "Game/Core/GameObjects/Items/RubeDoll", + "Game/Client/View/Abstract/Layer", + "Game/Config/Settings", + "Lib/Utilities/NotificationCenter", +], + +function (Parent, Layer, Settings, nc) { + + "use strict"; + + function RubeDoll(physicsEngine, uid, options) { + + this.primaryColor = options.primaryColor; + + var limbOptions = {}; + + limbOptions.chest = { + width: 6, + height: 18, + x: 0, + y: 0 + }; + + limbOptions.head = { + width: 10, + height: 12, + x: 0, + y: - limbOptions.chest.height / 2 - 7 + }; + + limbOptions.upperLeftLeg = { + width: 5, + height: 8, + x: -2, + y: limbOptions.chest.height / 2 + }; + + limbOptions.upperRightLeg = { + width: 5, + height: 8, + x: 2, + y: limbOptions.chest.height / 2 + }; + + limbOptions.lowerLeftLeg = { + width: 5, + height: 4, + x: -2, + y: limbOptions.chest.height / 2 + limbOptions.upperLeftLeg.height + }; + + limbOptions.lowerRightLeg = { + width: 5, + height: 4, + x: 2, + y: limbOptions.chest.height / 2 + limbOptions.upperRightLeg.height + }; + + + + limbOptions.upperLeftArm = { + width: 4, + height: 8, + x: -2, + y: -limbOptions.chest.height / 2 + }; + + limbOptions.upperRightArm = { + width: 4, + height: 8, + x: 2, + y: -limbOptions.chest.height / 2 + }; + + limbOptions.lowerLeftArm = { + width: 4, + height: 5, + x: -2, + y: -limbOptions.chest.height / 2 + limbOptions.upperLeftArm.height + }; + + limbOptions.lowerRightArm = { + width: 4, + height: 5, + x: 2, + y: -limbOptions.chest.height / 2 + limbOptions.upperRightArm.height + }; + + this.limbOptions = limbOptions; + + this.layerId = Layer.ID.SPAWN; + this.limbMeshes = {}; + this.baseMeshName = "chest"; + this.characterName = "Chuck"; + this.lastFlipDirection = -options.direction || 1; + + Parent.call(this, physicsEngine, uid, options); + } + + RubeDoll.prototype = Object.create(Parent.prototype); + + RubeDoll.prototype.createMesh = function() { + + this.createLimbMesh("lowerRightLeg"); + this.createLimbMesh("upperRightLeg"); + this.createLimbMesh("lowerRightArm"); + this.createLimbMesh("upperRightArm"); + + this.createLimbMesh("chest"); + this.createLimbMesh("head"); + + this.createLimbMesh("lowerLeftLeg"); + this.createLimbMesh("upperLeftLeg"); + this.createLimbMesh("lowerLeftArm"); + this.createLimbMesh("upperLeftArm"); + + }; + + RubeDoll.prototype.createLimbMesh = function(name) { + var self = this; + var texturePath = Settings.GRAPHICS_PATH + + Settings.GRAPHICS_SUBPATH_CHARACTERS + "" + + this.characterName + '/'; + + + var callback = function(mesh) { + if(name == self.baseMeshName) { + self.mesh = mesh; + } + + self.limbMeshes[name] = mesh; + + nc.trigger(nc.ns.client.view.mesh.add, self.layerId, mesh); + + // setting shirt color + nc.trigger(nc.ns.client.view.mesh.addFilter, self.layerId, mesh, "colorRangeReplace", { + minColor: 0x3b4a31, + maxColor: 0x6d855d, + newColor: self.primaryColor, + brightnessOffset: 0.56 + }); + }; + + nc.trigger(nc.ns.client.view.mesh.create, + this.layerId, + texturePath + name + ".png", + callback, + { + width: this.limbOptions[name].width, + height: this.limbOptions[name].height, + pivot: { + x: this.limbOptions[name].width / 2, + y: this.limbOptions[name].height / 2 + } + } + ); + }; + + RubeDoll.prototype.destroy = function() { + + for (var name in this.limbMeshes) { + nc.trigger(nc.ns.client.view.mesh.remove, this.layerId, this.limbMeshes[name]); + }; + + Parent.prototype.destroy.call(this); + }; + + RubeDoll.prototype.render = function() { + //Parent.prototype.render.call(this); + + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, + this.mesh, + { + x: this.body.GetPosition().x * Settings.RATIO, + y: this.body.GetPosition().y * Settings.RATIO, + rotation: this.body.GetAngle() + } + ); + + if(this.limbs) { + for(var name in this.limbMeshes) { + if(this.limbs[name]) { + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, + this.limbMeshes[name], + { + x: this.limbs[name].GetPosition().x * Settings.RATIO, + y: this.limbs[name].GetPosition().y * Settings.RATIO, + rotation: this.limbs[name].GetAngle() + } + ); + } + } + } + }; + + RubeDoll.prototype.flip = function(direction) { + + Parent.prototype.flip.call(this, direction); + + // flipping depth of right body side arm/leg images with left + if (this.lastFlipDirection != direction) { // FIXME : this is a bit broken. + + this.lastFlipDirection = direction; + + nc.trigger(nc.ns.client.view.mesh.swapMeshIndexes, + this.layerId, + this.limbMeshes["lowerRightLeg"], + this.limbMeshes["lowerLeftLeg"] + ); + nc.trigger(nc.ns.client.view.mesh.swapMeshIndexes, + this.layerId, + this.limbMeshes["upperRightLeg"], + this.limbMeshes["upperLeftLeg"] + ); + nc.trigger(nc.ns.client.view.mesh.swapMeshIndexes, + this.layerId, + this.limbMeshes["lowerRightArm"], + this.limbMeshes["lowerLeftArm"] + ); + nc.trigger(nc.ns.client.view.mesh.swapMeshIndexes, + this.layerId, + this.limbMeshes["upperRightArm"], + this.limbMeshes["upperLeftArm"] + ); + + // swap short images + nc.trigger(nc.ns.client.view.mesh.swapMeshes, + this.layerId, + this.limbMeshes["upperRightLeg"], + this.limbMeshes["upperLeftLeg"] + ); + } + + // x flipping has to happen after (see swapMeshes) + if(this.limbs) { + for(var name in this.limbMeshes) { + if(this.limbs[name]) { + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, + this.limbMeshes[name], + { + xScale: direction, + } + ); + } + } + } + }; + + return RubeDoll; +}); \ No newline at end of file diff --git a/app/Game/Client/GameObjects/SpectatorDoll.js b/app/Game/Client/GameObjects/SpectatorDoll.js index faf141a..4d1fbb1 100644 --- a/app/Game/Client/GameObjects/SpectatorDoll.js +++ b/app/Game/Client/GameObjects/SpectatorDoll.js @@ -3,6 +3,8 @@ define([ ], function (Parent) { + + "use strict"; function SpectatorDoll(physicsEngine, uid) { Parent.call(this, physicsEngine, uid); diff --git a/app/Game/Client/GameObjects/Tile.js b/app/Game/Client/GameObjects/Tile.js index 4319c4d..1ca9984 100755 --- a/app/Game/Client/GameObjects/Tile.js +++ b/app/Game/Client/GameObjects/Tile.js @@ -1,12 +1,16 @@ define([ "Game/Core/GameObjects/Tile", "Game/Config/Settings", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Game/Client/View/Abstract/Layer" ], -function (Parent, Settings, Nc) { +function (Parent, Settings, nc, Layer) { + + "use strict"; function Tile(physicsEngine, uid, options) { + this.layerId = Layer.ID.TILE; Parent.call(this, physicsEngine, uid, options); } @@ -27,10 +31,11 @@ function (Parent, Settings, Nc) { var callback = function(mesh) { self.mesh = mesh; - Nc.trigger(Nc.ns.client.view.mesh.add, mesh); + nc.trigger(nc.ns.client.view.mesh.add, self.layerId, mesh); } - Nc.trigger(Nc.ns.client.view.mesh.create, + nc.trigger(nc.ns.client.view.mesh.create, + this.layerId, texturePath, callback, { @@ -45,13 +50,14 @@ function (Parent, Settings, Nc) { }; Tile.prototype.destroy = function() { - Nc.trigger(Nc.ns.client.view.mesh.remove, this.mesh); + nc.trigger(nc.ns.client.view.mesh.remove, this.layerId, this.mesh); Parent.prototype.destroy.call(this); }; Tile.prototype.render = function() { - Nc.trigger(Nc.ns.client.view.mesh.update, + nc.trigger(nc.ns.client.view.mesh.update, + this.layerId, this.mesh, { x: this.body.GetPosition().x * Settings.RATIO, diff --git a/app/Game/Client/Loader/Level.js b/app/Game/Client/Loader/Level.js index f732196..832d9f0 100755 --- a/app/Game/Client/Loader/Level.js +++ b/app/Game/Client/Loader/Level.js @@ -2,13 +2,20 @@ define([ "Game/Core/Loader/Level", "Game/Config/Settings", "Lib/Utilities/NotificationCenter", - "Lib/Vendor/Pixi" + "Lib/Vendor/Pixi", + "Game/Client/View/Abstract/Layer" ], -function (Parent, Settings, Nc, PIXI) { +function (Parent, Settings, nc, PIXI, AbstractLayer) { + + "use strict"; function Level (uid, engine, gameObjects) { Parent.call(this, uid, engine, gameObjects); + this.levelSize = { + width: 0, + height: 0 + } } Level.prototype = Object.create(Parent.prototype); @@ -17,13 +24,13 @@ function (Parent, Settings, Nc, PIXI) { var self = this; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { - if(xhr.readyState == 4) { - if(xhr.status == 200) { - self.loadAssets(JSON.parse(xhr.responseText), callback); - } else { - console.error("Ajax error: " + xhr.status + " " + xhr.statusText) - } + if(xhr.readyState == 4) { + if(xhr.status == 200) { + self.loadAssets(JSON.parse(xhr.responseText), callback); + } else { + console.error("Ajax error: " + xhr.status + " " + xhr.statusText) } + } } xhr.open("GET", path, true); xhr.send(null); @@ -39,7 +46,7 @@ function (Parent, Settings, Nc, PIXI) { loader.onComplete = function() { callback(levelData); }; loader.onProgress = function() { var progress = parseInt(100 / numPaths * ++count, 10) + 1; - Nc.trigger(Nc.ns.client.view.preloadBar.update, progress); + nc.trigger(nc.ns.client.view.preloadBar.update, progress); } loader.load(); }; @@ -55,28 +62,42 @@ function (Parent, Settings, Nc, PIXI) { return n; } - var characterNames = ["Chuck"]; - var animationSets = ["WithArms"];//, "WithArms"]; + /* + // Single File Animations Preloading + var animationSets = ["WithArms", "WithoutArms"]; var addition = ""; + for (var i = 0; i < characterNames.length; i++) { var characterName = characterNames[i]; for (var j = 1; j <= 126; j++) { for (var k = 0; k < animationSets.length; k++) { var animationSet = animationSets[k]; - addition = animationSet == "WithoutArms" ? "WithoutArms" : ""; paths.push( Settings.GRAPHICS_PATH + Settings.GRAPHICS_SUBPATH_CHARACTERS + characterName + "/Animation/" + animationSet - + "/ChuckAnimations" + addition + "0" + + "/ChuckAnimations0" + padF(j) + ".png" ); }; }; }; + */ + + var characterNames = ["Chuck"]; + var characterName = characterNames[0]; + + paths.push( + Settings.GRAPHICS_PATH + + Settings.GRAPHICS_SUBPATH_CHARACTERS + + characterName + + "/Animation/" + + "/TexturePacker" + + "/chuck_sheet.json" + ); paths.push( Settings.GRAPHICS_PATH @@ -88,5 +109,24 @@ function (Parent, Settings, Nc, PIXI) { return paths; }; + Level.prototype.setupLayer = function(options, behind, referenceId) { + + Parent.prototype.setupLayer.call(this, options, behind, referenceId); + var parallaxSpeed = 0.0; // default parallax + if (options.properties && options.properties.parallaxSpeed) { + parallaxSpeed = parseFloat(options.properties.parallaxSpeed); + } + nc.trigger( + nc.ns.client.view.layer.createAndInsert, + options.layerId, + { + parallaxSpeed: parallaxSpeed, + levelSize: this.levelSize + }, + behind, + referenceId + ); + }; + return Level; }); \ No newline at end of file diff --git a/app/Game/Client/Loader/TiledLevel.js b/app/Game/Client/Loader/TiledLevel.js index d5cff31..df451d8 100644 --- a/app/Game/Client/Loader/TiledLevel.js +++ b/app/Game/Client/Loader/TiledLevel.js @@ -1,16 +1,32 @@ define([ "Game/Core/Loader/TiledLevel", - "Game/Config/Settings" + "Game/Config/Settings", + "Lib/Utilities/NotificationCenter", ], -function (Parent, Settings) { +function (Parent, Settings, nc) { + + "use strict"; - function TiledLevel(uid, engine, gameObjects) { - Parent.call(this, uid, engine, gameObjects); + function TiledLevel(uid, engine) { + this.layerId = "background"; + Parent.call(this, uid, engine); } TiledLevel.prototype = Object.create(Parent.prototype); - + + TiledLevel.prototype.setup = function(levelData) { + var tilesLayerData = this.getLayer(levelData, "tiles"); + this.levelSize = { + width: tilesLayerData.width * Settings.TILE_SIZE, + height: tilesLayerData.height * Settings.TILE_SIZE + }; + + nc.trigger(nc.ns.client.view.layer.levelSizeUpdate, this.levelSize); + + Parent.prototype.setup.call(this, levelData); + }; + TiledLevel.prototype.getAssetPaths = function(levelData) { var paths = Parent.prototype.getAssetPaths.call(this, levelData); @@ -40,12 +56,100 @@ function (Parent, Settings) { paths.push(texturePath); }; - // FIXME: Get background image + for (var i = 0; i < levelData.layers.length; i++) { + var layer = levelData.layers[i]; + if (layer.type == "imagelayer") { + paths.push(Settings.MAPS_PATH + layer.image); + } + }; return paths; - } - + + TiledLevel.prototype.setupLayer = function(options, behind, referenceId) { + + var self = this; + + Parent.prototype.setupLayer.call(this, options, behind, referenceId); + + // So far only one image per layer is possible because of Tiled editor + if (options.type == "imagelayer") { + + var texturePath = Settings.MAPS_PATH + options.image; + + var callback = function(mesh) { + nc.trigger(nc.ns.client.view.mesh.add, options.layerId, mesh); + nc.trigger(nc.ns.client.view.mesh.update, options.layerId, mesh, { + x: 0,//self.levelData.width * Settings.TILE_SIZE / 2, + y: 0,//self.levelData.height * Settings.TILE_SIZE / 2, + pivot: { + x: mesh.texture.width / 2, + y: mesh.texture.height / 2 + }, + xScale: 1, + yScale: 1 + }); + } + + nc.trigger(nc.ns.client.view.mesh.create, + options.layerId, + texturePath, + callback, + { + alpha: options.opacity + } + ); + } + + // Adding tiles without collision + else if (options.type == "tilelayer" && options.name != "tiles") { + this.createNonCollidingTiles(options); + } + + }; + + + TiledLevel.prototype.createNonCollidingTiles = function(options) { + + var data = options.data; + var tilesOptions = []; + for (var i = 0; i < data.length; i++) { + + var gid = data[i]; + if(gid === 0) continue; + + var imagePath = this.getTileImagePath(gid); + var parts = imagePath.split("/"); + var tileType = parts[parts.length - 1].split(".")[0].split("") + + var callback = function(mesh) { + nc.trigger(nc.ns.client.view.mesh.add, options.layerId, mesh); + } + + nc.trigger(nc.ns.client.view.mesh.create, + options.layerId, + Settings.MAPS_PATH + imagePath, + callback, + { + width: Settings.TILE_SIZE, + height: Settings.TILE_SIZE, + x: (i % options.width) * Settings.TILE_SIZE, + y: parseInt(i / options.width , 10) * Settings.TILE_SIZE, + } + ); + } + } + + TiledLevel.prototype.getLayer = function(levelData, name) { + for (var i = 0; i < levelData.layers.length; i++) { + if(levelData.layers[i].name === name) { + return levelData.layers[i]; + } + } + + throw "Layer '" + name + "' not found."; + }; + return TiledLevel; }); \ No newline at end of file diff --git a/app/Game/Client/Me.js b/app/Game/Client/Me.js index e0555ae..a904c04 100644 --- a/app/Game/Client/Me.js +++ b/app/Game/Client/Me.js @@ -1,28 +1,60 @@ define([ "Game/Client/Player", - "Game/Config/Settings" + "Game/Config/Settings", + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Assert", + "Game/Client/Control/PlayerController", ], -function (Parent, Settings) { +function (Parent, Settings, nc, Assert, PlayerController) { + + "use strict"; function Me(id, physicsEngine, user) { Parent.call(this, id, physicsEngine, user); + // View uses this to calculate center position + this.lookAtXY = { + x: Settings.VIEWPORT_LOOK_AHEAD, + y: 0 + }; + this.lastServerPositionState = { p: { x: 0, y: 0 } }; + + this.arrowMesh = null; + this.createAndAddArrow(); + this.playerController = new PlayerController(this); } Me.prototype = Object.create(Parent.prototype); + + Me.prototype.lookAt = function(x, y) { + this.lookAtXY = { + x: x, + y: y + }; + + Parent.prototype.lookAt.call(this, x, y); + }; + + Me.prototype.getLookAt = function() { + return { + x: this.lookAtXY.x, + y: this.lookAtXY.y + }; + }; Me.prototype.setLastServerPositionState = function(update) { this.lastServerPositionState = update; }; - Me.prototype.isPositionStateUpdateNeeded = function() { + // Checks if client should send out its position to server + Me.prototype.isPositionStateOverrideNeeded = function() { if(!this.doll) { return false; @@ -35,20 +67,20 @@ function (Parent, Settings) { var difference = { x: Math.abs(this.lastServerPositionState.p.x - this.doll.body.GetPosition().x), y: Math.abs(this.lastServerPositionState.p.y - this.doll.body.GetPosition().y) - } + }; - if(difference.x > Settings.ME_STATE_MAX_DIFFERENCE_METERS - || difference.y > Settings.ME_STATE_MAX_DIFFERENCE_METERS) { + if(difference.x > Settings.ME_STATE_MAX_DIFFERENCE_METERS || + difference.y > Settings.ME_STATE_MAX_DIFFERENCE_METERS) { return true; } return false; }; - Me.prototype.getPositionStateUpdate = function() { + Me.prototype.getPositionStateOverride = function() { return { p: this.doll.body.GetPosition().Copy(), lv: this.doll.body.GetLinearVelocity().Copy() - } + }; }; Me.prototype.acceptPositionStateUpdateFromServer = function() { @@ -57,10 +89,39 @@ function (Parent, Settings) { }; Me.prototype.resetPositionState = function(options) { + Assert.number(options.p.x, options.p.y); + Assert.number(options.lv.x, options.lv.y); this.doll.body.SetPosition(options.p); this.doll.body.SetLinearVelocity(options.lv); }; + Me.prototype.createAndAddArrow = function() { + var self = this; + + var position = this.getPosition(); + + var options = { + x: position.x * Settings.RATIO, + y: position.y * Settings.RATIO, + }; + + var callback = function(arrowMesh) { + self.arrowMesh = arrowMesh; + }; + nc.trigger(nc.ns.client.view.playerArrow.createAndAdd, callback, options); + }; + + Me.prototype.render = function() { + + Parent.prototype.render.call(this); + + var position = this.getPosition(); + var options = { + x: position.x * Settings.RATIO, + y: position.y * Settings.RATIO, + }; + nc.trigger(nc.ns.client.view.playerArrow.update, this.arrowMesh, options); + }; + return Me; - }); \ No newline at end of file diff --git a/app/Game/Client/Networker.js b/app/Game/Client/Networker.js index c45448d..4e27382 100755 --- a/app/Game/Client/Networker.js +++ b/app/Game/Client/Networker.js @@ -7,9 +7,14 @@ define([ "Game/Client/View/DomController" ], -function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { +function (ProtocolHelper, GameController, User, nc, Settings, domController) { - function Networker (socketLink) { + "use strict"; + + function Networker (socketLink, channelName, nickname) { + + this.channelName = channelName; + this.nickname = nickname; this.socketLink = socketLink; this.gameController = null; this.users = {}; @@ -20,33 +25,47 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { var self = this; this.socketLink.on('message', function (message) { var m = JSON.parse(message) - if(Settings.NETWORK_LOG_INCOMING) { - if (message.indexOf('worldUpdate') == -1 && message.indexOf('pong') == -1) { + if(Settings.NETWORK_LOG_INCOMING) { + var shouldBeFiltered = false; + var keyword; + + for (var i = 0; i < Settings.NETWORK_LOG_FILTER.length; i++) { + keyword = Settings.NETWORK_LOG_FILTER[i]; + if(message.search(keyword) != -1) { + shouldBeFiltered = true; + break; + } + }; + + if(!shouldBeFiltered) { console.log('INCOMING', message); } - } + + ProtocolHelper.applyCommand(message, self); }); - Nc.on(Nc.ns.client.to.server.gameCommand.send, this.sendGameCommand, this); - Nc.on(Nc.ns.core.game.events.level.loaded, this.onLevelLoaded, this); + nc.on(nc.ns.client.to.server.gameCommand.send, this.sendGameCommand, this); + nc.on(nc.ns.core.game.events.level.loaded, this.onLevelLoaded, this); + + domController.setNick(nickname); } // Socket callbacks Networker.prototype.onConnect = function () { console.log('connected.') - var channel = JSON.parse(localStorage["channel"]); - var player = JSON.parse(localStorage["player"]); - if(channel.name) { + if(this.channelName) { var options = { - channelName: channel.name, - nickname: player.nickname + channelName: this.channelName, + nickname: this.nickname } this.sendCommand('join', options); + domController.setConnected(true); } else { + alert("Error: no channel name"); window.location.href = "/"; } } @@ -55,7 +74,7 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { //if(this.gameController) this.gameController.destruct(); //this.gameController = null; console.log('disconnected. game destroyed. no auto-reconnect'); - document.body.style.backgroundColor = '#aaaaaa'; + domController.setConnected(false); } Networker.prototype.onJoinSuccess = function (options) { @@ -74,18 +93,21 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { } Networker.prototype.onJoinError = function(options) { - // alert(options.message); + alert(options.message); window.location.href = "/"; }; Networker.prototype.onLevelLoaded = function() { + /* this.gameController.createMe(this.users[this.meUserId]); for (var userId in this.users) { if(this.meUserId != userId) { this.gameController.createPlayer(this.users[userId]); } - } + }*/ + + this.gameController.onLevelLoaded(); this.sendGameCommand("clientReady"); }; @@ -107,23 +129,18 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { this.socketLink.send(message); if(Settings.NETWORK_LOG_OUTGOING) { - if(Settings.NETWORK_LOG_FILTER.length > 0) { + var shouldBeFiltered = false; + var keyword; - var shouldBeFiltered = false; - var keyword; - - for (var i = 0; i < Settings.NETWORK_LOG_FILTER.length; i++) { - keyword = Settings.NETWORK_LOG_FILTER[i]; - if(message.search(keyword) != -1) { - shouldBeFiltered = true; - break; - } - }; - - if(!shouldBeFiltered) { - console.log('OUTGOING', message); + for (var i = 0; i < Settings.NETWORK_LOG_FILTER.length; i++) { + keyword = Settings.NETWORK_LOG_FILTER[i]; + if(message.search(keyword) != -1) { + shouldBeFiltered = true; + break; } - } else { + }; + + if(!shouldBeFiltered) { console.log('OUTGOING', message); } } @@ -157,12 +174,16 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { } Networker.prototype.onGameCommand = function(message) { - this.gameController.onGameCommand(message); + if (this.gameController) { + this.gameController.onGameCommand(message); + } else { + console.warn("Networker.onGameCommand: this.gameController is undefined", message); + } } Networker.prototype.onPong = function(timestamp) { var ping = (Date.now() - parseInt(timestamp, 10)); - DomController.setPing(ping); + domController.setPing(ping); setTimeout(this.ping.bind(this), 1000); }; @@ -170,14 +191,23 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { if(this.gameController) { this.gameController.destroy(); - delete this.gameController; } this.gameController = new GameController(options); + + this.gameController.createMe(this.users[this.meUserId]); + + for (var userId in this.users) { + if(this.meUserId != userId) { + this.gameController.createPlayer(this.users[userId]); + } + } + + this.gameController.beginRound(); }; Networker.prototype.onEndRound = function() { - this.gameController.toggleInfo(true); + this.gameController.endRound(); }; return Networker; diff --git a/app/Game/Client/Physics/Engine.js b/app/Game/Client/Physics/Engine.js index b368571..6e93609 100755 --- a/app/Game/Client/Physics/Engine.js +++ b/app/Game/Client/Physics/Engine.js @@ -3,17 +3,21 @@ define([ "Game/Config/Settings", "Game/Client/View/DomController", "Lib/Vendor/Box2D", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Game/Client/View/Pixi/DebugDraw", + "Game/Client/View/Pixi/Layers/Debug" ], -function (Parent, Settings, DomController, Box2D, Nc) { +function (Parent, Settings, domController, Box2D, nc, DebugDraw, debugLayer) { + + "use strict"; function Engine () { Parent.call(this); this.debugMode = false; - Nc.on(Nc.ns.client.view.debugMode.toggle, this.onToggleDebugMode, this); + nc.on(nc.ns.client.view.debugMode.toggle, this.onToggleDebugMode, this); } Engine.prototype = Object.create(Parent.prototype); @@ -21,19 +25,19 @@ function (Parent, Settings, DomController, Box2D, Nc) { Engine.prototype.onToggleDebugMode = function(debugMode) { this.debugMode = debugMode; - if(this.debugMode && !this.debugDraw) { + if(!this.debugDraw) { this.setupDebugDraw(); } + + debugLayer.container.visible = this.debugMode; }; Engine.prototype.setupDebugDraw = function () { - var debugSprite = DomController.getDebugCanvas().getContext("2d"); - // set debug draw - this.debugDraw = new Box2D.Dynamics.b2DebugDraw(); + this.debugDraw = new DebugDraw(); - this.debugDraw.SetSprite(debugSprite); + this.debugDraw.SetSprite(debugLayer.graphics); this.debugDraw.SetDrawScale(Settings.RATIO); this.debugDraw.SetFillAlpha(0.5); this.debugDraw.SetLineThickness(1.0); @@ -49,7 +53,7 @@ function (Parent, Settings, DomController, Box2D, Nc) { ); this.world.SetDebugDraw(this.debugDraw); - } + }; Engine.prototype.update = function () { Parent.prototype.update.call(this); @@ -57,7 +61,7 @@ function (Parent, Settings, DomController, Box2D, Nc) { if(this.debugMode) { this.world.DrawDebugData(); } - } + }; return Engine; -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/app/Game/Client/Player.js b/app/Game/Client/Player.js index 5fb7ce7..f7484cc 100755 --- a/app/Game/Client/Player.js +++ b/app/Game/Client/Player.js @@ -4,15 +4,21 @@ define([ "Game/Config/Settings" ], -function (Parent, Nc, Settings) { +function (Parent, nc, Settings) { + + "use strict"; - function Player(id, physicsEngine, user) { + function Player(id, physicsEngine, user, isMe) { Parent.call(this, id, physicsEngine, user); - this.playerInfoView = null; - this.playerInfoViewVisibleTimeout = null; - this.playerInfoViewVisible = false; + this.healthBarView = null; + this.healthBarViewVisibleTimeout = null; + this.healthBarViewVisible = false; this.initHealthBar(); + + this.ncTokens = (this.ncTokens || []).concat([ + nc.on(nc.ns.client.game.events.render, this.render, this) + ]); } Player.prototype = Object.create(Parent.prototype); @@ -29,36 +35,36 @@ function (Parent, Nc, Settings) { Player.prototype.initHealthBar = function() { var self = this; - this.playerInfoViewVisible = false; + this.healthBarViewVisible = false; var options = { x: 100, y: 100, healthFactor: this.stats.health / 100, - visible: this.playerInfoViewVisible + visible: this.healthBarViewVisible }; - var callback = function(playerInfoView) { - self.playerInfoView = playerInfoView; + var callback = function(healthBarView) { + self.healthBarView = healthBarView; } - Nc.trigger(Nc.ns.client.view.playerInfo.createAndAdd, callback, options); + nc.trigger(nc.ns.client.view.healthBar.createAndAdd, callback, options); }; Player.prototype.onHealthChange = function() { if(this.stats.health != 100) { - this.setPlayerInfoVisible(true); + this.setHealthBarVisible(true); } }; Player.prototype.spawn = function(x, y) { Parent.prototype.spawn.call(this, x, y); - this.setPlayerInfoVisible(false); + this.setHealthBarVisible(false); }; - Player.prototype.setPlayerInfoVisible = function(visible) { + Player.prototype.setHealthBarVisible = function(visible) { var self = this; - this.playerInfoViewVisible = visible; - if(this.playerInfoViewVisibleTimeout) clearTimeout(this.playerInfoViewVisibleTimeout); + this.healthBarViewVisible = visible; + if(this.healthBarViewVisibleTimeout) clearTimeout(this.healthBarViewVisibleTimeout); if(visible) { var position = this.getPosition(); @@ -67,44 +73,45 @@ function (Parent, Nc, Settings) { x: position.x * Settings.RATIO, y: position.y * Settings.RATIO, healthFactor: this.stats.health / 100, - visible: this.playerInfoViewVisible + visible: this.healthBarViewVisible }; - Nc.trigger(Nc.ns.client.view.playerInfo.update, this.playerInfoView, options); + nc.trigger(nc.ns.client.view.healthBar.update, this.healthBarView, options); - this.playerInfoViewVisibleTimeout = setTimeout(function() { - self.playerInfoViewVisible = false; - Nc.trigger(Nc.ns.client.view.playerInfo.update, self.playerInfoView, {visible: self.playerInfoViewVisible}); + this.healthBarViewVisibleTimeout = setTimeout(function() { + self.healthBarViewVisible = false; + nc.trigger(nc.ns.client.view.healthBar.update, self.healthBarView, {visible: self.healthBarViewVisible}); }, Settings.HEALTH_DISPLAY_TIME * 1000); } else { - Nc.trigger(Nc.ns.client.view.playerInfo.update, this.playerInfoView, {visible: this.playerInfoViewVisible}); + nc.trigger(nc.ns.client.view.healthBar.update, this.healthBarView, {visible: this.healthBarViewVisible}); } }; - Player.prototype.getNickname = function() { - return this.user.options.nickname; - }; - Player.prototype.render = function() { if(this.doll) { this.doll.render(); } - if(this.playerInfoViewVisible) { + if(this.healthBarViewVisible) { var position = this.getPosition(); var options = { healthFactor: this.stats.health / 100, x: position.x * Settings.RATIO, y: position.y * Settings.RATIO, } - Nc.trigger(Nc.ns.client.view.playerInfo.update, this.playerInfoView, options); + nc.trigger(nc.ns.client.view.healthBar.update, this.healthBarView, options); } }; + Player.prototype.isHoldingSomething = function() { + return !!this.holdingItem; + }; + Player.prototype.destroy = function() { - clearTimeout(this.playerInfoViewVisibleTimeout); - Nc.trigger(Nc.ns.client.view.playerInfo.remove, this.playerInfoView); + clearTimeout(this.healthBarViewVisibleTimeout); + nc.trigger(nc.ns.client.view.healthBar.remove, this.healthBarView); + nc.off(this.ncTokens); Parent.prototype.destroy.call(this); }; diff --git a/app/Game/Client/PointerLockManager.js b/app/Game/Client/PointerLockManager.js new file mode 100644 index 0000000..ff0ac96 --- /dev/null +++ b/app/Game/Client/PointerLockManager.js @@ -0,0 +1,53 @@ +define([ + "Lib/Utilities/QuerySelector", + "Lib/Utilities/NotificationCenter" +], + +function (qs, nc) { + + "use strict"; + + function PointerLockManager() { + this.canvas = qs.$("#canvas"); + + this.listeners = []; + + if (!document) { + throw new Error("Using PointerLockManager, but window.document is not defined."); + } + + document.addEventListener('pointerlockchange', this.update.bind(this), false); + document.addEventListener('mozpointerlockchange', this.update.bind(this), false); + document.addEventListener('webkitpointerlockchange', this.update.bind(this), false); + + this.ncTokens = [ + nc.on(nc.ns.client.pointerLock.request, this.request, this) + ]; + } + + PointerLockManager.prototype.request = function() { + + var canvas = this.canvas; + + canvas.requestPointerLock = canvas.requestPointerLock || + canvas.mozRequestPointerLock || + canvas.webkitRequestPointerLock; + + // Ask the browser to lock the pointer + canvas.requestPointerLock(); + } + + // called by the browser event and others + PointerLockManager.prototype.update = function(e, options) { + options = options ? options : {}; + nc.trigger(nc.ns.client.pointerLock.change, this.isLocked(), options); + }; + + PointerLockManager.prototype.isLocked = function() { + return document.pointerLockElement === this.canvas || + document.mozPointerLockElement === this.canvas || + document.webkitPointerLockElement === this.canvas; + }; + + return new PointerLockManager(); +}); \ No newline at end of file diff --git a/app/Game/Client/View/Abstract/Layer.js b/app/Game/Client/View/Abstract/Layer.js new file mode 100644 index 0000000..5e1521e --- /dev/null +++ b/app/Game/Client/View/Abstract/Layer.js @@ -0,0 +1,67 @@ +define([ + "Lib/Utilities/Abstract", + "Lib/Utilities/NotificationCenter" +], + +function (Abstract, nc) { + + "use strict"; + + function Layer(name, options) { + this.name = name; + this.parallaxSpeed = options.parallaxSpeed || 0; + this.zoom = { + current: window.innerWidth / 600, + target: window.innerWidth / 600 + }; + this.position = { + current: { x: 0, y: 0}, + target: { x: 0, y: 0} + }; + + if(options.levelSize) { + this.position.current.x = -options.levelSize.width / 2; + this.position.current.y = -options.levelSize.height / 2; + } + + this.ncTokens = []; + } + + Object.defineProperty(Layer, 'ID', { + value: { + TILE: 'tile', + ITEM: 'item', + SPAWN: 'spawnpoints' + } + }); + + Abstract.prototype.addMethod.call(Layer, 'show'); + Abstract.prototype.addMethod.call(Layer, 'hide'); + Abstract.prototype.addMethod.call(Layer, 'createMesh', ['texturePath', 'callback', 'options']); + Abstract.prototype.addMethod.call(Layer, 'createAnimatedMesh', ['texturePaths', 'callback', 'options']); + Abstract.prototype.addMethod.call(Layer, 'addMesh', ['mesh']); + Abstract.prototype.addMethod.call(Layer, 'removeMesh', ['mesh']); + Abstract.prototype.addMethod.call(Layer, 'updateMesh', ['mesh', 'options']); + Abstract.prototype.addMethod.call(Layer, 'render', ['centerPosition']); + + Layer.prototype.getName = function() { + return this.name; + }; + + Layer.prototype.setPosition = function(centerPosition) { + this.position.target.x = centerPosition.x; + this.position.target.y = centerPosition.y; + }; + + Layer.prototype.setZoom = function(z) { + this.zoom.target = z; + }; + + Layer.prototype.destroy = function() { + for (var i = 0; i < this.ncTokens.length; i++) { + nc.off(this.ncTokens[i]); + }; + }; + + return Layer; +}); \ No newline at end of file diff --git a/app/Game/Client/View/Abstract/View.js b/app/Game/Client/View/Abstract/View.js new file mode 100755 index 0000000..a226801 --- /dev/null +++ b/app/Game/Client/View/Abstract/View.js @@ -0,0 +1,106 @@ +define([ + "Lib/Utilities/Abstract", + "Game/Client/View/DomController", + "Game/Config/Settings", + "Lib/Utilities/Exception", + "Lib/Utilities/NotificationCenter" +], + +function (Abstract, domController, Settings, Exception, nc) { + + "use strict"; + + function AbstractView () { + this.me = null; + this.canvas = null; + this.debugMode = false; + + this.ncTokens = [ + nc.on(nc.ns.client.view.display.change, this.onDisplaySizeChange, this), + nc.on(nc.ns.client.view.debugMode.toggle, this.onToggleDebugMode, this), + + nc.on(nc.ns.client.game.zoomIn, this.onZoomIn, this), + nc.on(nc.ns.client.game.zoomOut, this.onZoomOut, this), + nc.on(nc.ns.client.game.zoomReset, this.onZoomReset, this), + + nc.on(nc.ns.client.view.preloadBar.update, this.onUpdateLoader, this), + ]; + } + + Abstract.prototype.addMethod.call(AbstractView, 'render'); + Abstract.prototype.addMethod.call(AbstractView, 'addFilter', ['mesh', 'options']); + Abstract.prototype.addMethod.call(AbstractView, 'removeFilter', ['mesh', 'options']); + Abstract.prototype.addMethod.call(AbstractView, 'setCameraPosition', ['x', 'y']); + Abstract.prototype.addMethod.call(AbstractView, 'onZoomIn'); + Abstract.prototype.addMethod.call(AbstractView, 'onZoomOut'); + Abstract.prototype.addMethod.call(AbstractView, 'onZoomReset'); + Abstract.prototype.addMethod.call(AbstractView, 'toggleInfo', ['show', 'string']); + Abstract.prototype.addMethod.call(AbstractView, 'onUpdateLoader', ['progress']); + + AbstractView.prototype.isWebGlEnabled = function () { + try { + return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); + } catch(e) { + return false; + } + } + + AbstractView.prototype.initCanvas = function (canvas) { + this.canvas = canvas; + domController.initCanvas(canvas); + } + + AbstractView.prototype.setMe = function(player) { + this.me = player; + }; +/* + AbstractView.prototype.calculateCameraPosition = function() { + var reference = this.me.getPosition(); + var pos = {}; + + pos.x = reference.x; + pos.y = reference.y; + + pos.x = pos.x * Settings.RATIO; + pos.y = -(pos.y * Settings.RATIO); + + pos.x += this.me.playerController.xyInput.x * Settings.STAGE_WIDTH / 4; + pos.y += this.me.playerController.xyInput.y * Settings.STAGE_HEIGHT / 4; + + return pos; + }; +*/ + AbstractView.prototype.onDisplaySizeChange = function(isFullScreen) { + +/* + if (!isFullScreen) { + Settings.STAGE_WIDTH = 600; + Settings.STAGE_HEIGHT = 400; + } else { + // FIXME: Create FIXME meme (dumb and dumber) + // FIXME: don't overwrite Settings + Settings.STAGE_WIDTH = window.innerWidth; + Settings.STAGE_HEIGHT = window.innerHeight; + } + */ + + Settings.STAGE_WIDTH = window.innerWidth; + Settings.STAGE_HEIGHT = window.innerHeight; + }; + + AbstractView.prototype.onToggleDebugMode = function(debugMode) { + if(debugMode) { + //this.setCameraPosition(-Settings.STAGE_WIDTH / 2, -Settings.STAGE_HEIGHT / 2); + } + + this.debugMode = debugMode; + }; + + AbstractView.prototype.destroy = function() { + for (var i = 0; i < this.ncTokens.length; i++) { + nc.off(this.ncTokens[i]); + }; + }; + + return AbstractView; +}); \ No newline at end of file diff --git a/app/Game/Client/View/DomController.js b/app/Game/Client/View/DomController.js index 89fb78e..90d5f52 100755 --- a/app/Game/Client/View/DomController.js +++ b/app/Game/Client/View/DomController.js @@ -1,91 +1,168 @@ define([ - 'Game/Config/Settings', - 'Lib/Utilities/NotificationCenter', - "Lib/Vendor/Stats", - "Lib/Vendor/Screenfull" + "Game/Config/Settings", + "Lib/Utilities/NotificationCenter", + "Lib/Vendor/Screenfull", + "Game/Client/View/Graph", + "Game/Client/PointerLockManager" ], -function (Settings, Nc, Stats, Screenfull) { +function (Settings, nc, Screenfull, Graph, pointerLockManager) { + + "use strict"; function DomController() { - this.canvas = document.getElementById("canvas"); - this.debugCanvas = null; + this.canvas = null; this.stats = null; this.ping = null; + this.nickContainer = null; + this.fpsContainer = null; + this.devToolsContainer = null; + this.frames = 0; - Nc.on(Nc.ns.client.view.events.ready, this.initDevTools, this); + this.canvas = document.getElementById("canvas"); + + this.initDevTools(); } DomController.prototype.initDevTools = function() { - var self = this; + var li, button, label; - // create dev tools container - this.devToolsContainer = document.createElement("div"); - this.devToolsContainer.id = "devtools"; - document.body.appendChild(this.devToolsContainer); + this.devToolsContainer = document.getElementById("menuBar"); - // create Fullscreen - var p = document.createElement("p"); - var button = document.createElement("button"); - button.innerHTML = "Fullscreen"; + // create back to menu button + li = document.createElement("li"); + li.id = "back-to-menu"; + button = document.createElement("button"); + button.innerHTML = "Menu"; button.onclick = function() { - if(Screenfull.enabled) { - Screenfull.request(self.canvas); - } - } - p.appendChild(button); - this.devToolsContainer.appendChild(p); + window.location.href="/"; + }; + li.appendChild(button); + this.devToolsContainer.appendChild(li); - window.onresize = function() { - if(Screenfull.enabled) { - Nc.trigger(Nc.ns.client.view.fullscreen.change, Screenfull.isFullscreen); + // create user name + li = document.createElement("li"); + label = document.createElement("label"); + label.appendChild(document.createTextNode("?")); + li.appendChild(label); + this.devToolsContainer.appendChild(li); + this.nickContainer = label; + + + // create fps label with updater + li = document.createElement("li"); + label = document.createElement("label"); + label.id = "label-fps"; + li.appendChild(label); + this.devToolsContainer.appendChild(li); + this.fpsContainer = label; + +/* + // create new fps meter + li = document.createElement("li"); + var fpsCanvas = document.createElement("canvas"); + fpsCanvas.id = "graph-fps"; + fpsCanvas.width = "100"; + fpsCanvas.height = "27"; + li.appendChild(fpsCanvas); + this.devToolsContainer.appendChild(li); + + this.fpsGraph = new Graph(fpsCanvas.getContext("2d"), true); + + this.fpsGraph.onUpdate(function(value){ + self.fpsContainer.innerHTML = "FPS:" + value; + + var color, + alpha = 0.8; + + if (value >= 50) { + color = "rgba(136, 209, 018, " + alpha + ")"; + } else if (value > 25) { + color = "rgba(204, 114, 018, " + alpha + ")"; + } else { + color = "rgba(224, 018, 018, " + 1 + ")"; } - } + + return color; + }); + + // create new ping meter + li = document.createElement("li"); + var pingCanvas = document.createElement("canvas"); + pingCanvas.id = "graph-fps"; + pingCanvas.width = "100"; + pingCanvas.height = "27"; + li.appendChild(pingCanvas); + this.devToolsContainer.appendChild(li); + + this.pingGraph = new Graph(pingCanvas.getContext("2d"), false, { + scaleOverride: false, + scaleStartValue: 0, + scaleStepWidth: 0, + scaleSteps: 0 + }); +*/ + + setInterval(function() { + self.fpsContainer.innerHTML = "FPS:" + self.frames; + self.frames = 0; + }, 1000); // create Ping: container - this.ping = document.createElement("span"); - this.devToolsContainer.appendChild(this.ping); + li = document.createElement("li"); + this.ping = document.createElement("label"); + li.appendChild(this.ping); + this.devToolsContainer.appendChild(li); - // create FPS stats - this.stats = new Stats(); - this.stats.setMode(0); - this.devToolsContainer.appendChild(this.stats.domElement); // create debug mode - var label = document.createElement("label"); + li = document.createElement("li"); + label = document.createElement("label"); var checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.onclick = function(e) { - Nc.trigger(Nc.ns.client.view.debugMode.toggle, e.target.checked); - self.getDebugCanvas().style.display = e.target.checked ? "" : "none"; - } + nc.trigger(nc.ns.client.view.debugMode.toggle, e.target.checked); + }; label.appendChild(checkbox); label.appendChild(document.createTextNode("Debug")); - this.devToolsContainer.appendChild(label); + li.appendChild(label); + this.devToolsContainer.appendChild(li); - // create health - this.health = document.createElement("span"); - this.health.innerHTML = "Health: 100"; - p = document.createElement("p"); - p.appendChild(this.health); - this.devToolsContainer.appendChild(p); + + // create Fullscreen + li = document.createElement("li"); + li.id = "fullscreen"; + button = document.createElement("button"); + button.innerHTML = "Fullscreen"; + button.onclick = function() { + if(Screenfull.enabled) { + pointerLockManager.request(); + Screenfull.request(self.canvas); + } + }; + li.appendChild(button); + this.devToolsContainer.appendChild(li); + + + // FIXME : isn't this a weird place for this? + window.onresize = function() { + nc.trigger(nc.ns.client.view.display.change); + }; }; - DomController.prototype.statsBegin = function() { - if(this.stats) { - this.stats.begin(); - } + DomController.prototype.setNick = function (nick) { + this.nickContainer.innerHTML = nick; }; - DomController.prototype.statsEnd = function() { - if(this.stats) { - this.stats.end(); - } + DomController.prototype.fpsStep = function() { + this.frames++; + // this.fpsGraph.step(); }; DomController.prototype.setPing = function(ping) { - this.ping.innerHTML = "Ping: " + ping; + this.ping.innerHTML = "Ping:" + ping; + // this.pingGraph.addValue(ping); }; DomController.prototype.getCanvasContainer = function () { @@ -96,31 +173,32 @@ function (Settings, Nc, Stats, Screenfull) { } else { throw 'Canvas Container missing: #' + Settings.CANVAS_DOM_ID; } - } + }; DomController.prototype.getCanvas = function () { return this.canvas; - } + }; DomController.prototype.initCanvas = function (canvas) { - Nc.trigger(Nc.ns.client.view.fullscreen.change, Screenfull.isFullscreen); - } + nc.trigger(nc.ns.client.view.display.change, Screenfull.isFullscreen); + }; - DomController.prototype.getDebugCanvas = function () { - - if(!this.debugCanvas) { - var canvas = document.createElement('canvas'); - canvas.width = Settings.STAGE_WIDTH; - canvas.height = Settings.STAGE_HEIGHT; - this.debugCanvas = canvas; - this.getCanvasContainer().appendChild(canvas); + DomController.prototype.setConnected = function(connected) { + if(connected) { + document.body.style.backgroundColor = ''; + } else { + document.body.style.backgroundColor = '#aaaaaa'; + this.ping.innerHTML = "Disconnected. ".replace(/ /g, ' '); + this.ping.style.color = "#ff0000"; + /* + self = this; + setTimeout(function(){self.ping.innerHTML = "Reload Page...".replace(/ /g, ' ');}, 3000); + setTimeout(function(){self.ping.innerHTML = "Reload in 3...".replace(/ /g, ' ');}, 6000); + setTimeout(function(){self.ping.innerHTML = "Reload in 2...".replace(/ /g, ' ');}, 7000); + setTimeout(function(){self.ping.innerHTML = "Reload in 1...".replace(/ /g, ' ');}, 8000); + setTimeout(function(){self.ping.innerHTML = "Reload now. ".replace(/ /g, ' '); location.reload(); }, 9000); + */ } - - return this.debugCanvas; - } - - DomController.prototype.setHealth = function(health) { - this.health.innerHTML = "Health: " + parseInt(health, 10); }; diff --git a/app/Game/Client/View/Graph.js b/app/Game/Client/View/Graph.js new file mode 100644 index 0000000..28edb30 --- /dev/null +++ b/app/Game/Client/View/Graph.js @@ -0,0 +1,99 @@ +define([ + "Lib/Vendor/Chart" +], + +function (Chart) { + + "use strict"; + + function Graph(ctx, isStepCounter, newOptions) { + + var numberOfGraphBars = 25; + + var empty = new Array(numberOfGraphBars); + for (var i = empty.length - 1; i >= 0; i--) empty[i] = -1; + + var data = { + labels: empty, + datasets: [ + { + label: "no label", + fillColor: "rgba(220,220,220,1)", + strokeColor: "rgba(220,220,220,1)", + highlightFill: "rgba(220,220,220,1)", + data: empty + }, + ]}; + + var options = { + showScale: false, + scaleShowLabels: false, + showTooltips: false, + animation: false, + scaleBeginAtZero : true, + scaleShowGridLines : false, + scaleShowHorizontalLines: true, + scaleShowVerticalLines: true, + barShowStroke : false, + barStrokeWidth : 0, + barValueSpacing : 0, + barDatasetSpacing : 0, + responsive: false, + scaleBackdropPaddingY : 10, + scaleOverride: true, + scaleStartValue: 0, + scaleStepWidth: 1, + scaleSteps: 60 + } + + if (newOptions) { + if (newOptions.scaleOverride) options.scaleOverride = newOptions.scaleOverride; + if (newOptions.scaleStartValue) options.scaleStartValue = newOptions.scaleStartValue; + if (newOptions.scaleSteps) options.scaleSteps = newOptions.scaleSteps; + if (newOptions.scaleStepWidth) options.scaleStepWidth = newOptions.scaleStepWidth; + } + + this.chart = new Chart(ctx).Bar(data, options); + this.stepCounter = 0; + this.currentValue = 0; + this.updateFunction = function(value){}; + + var self = this; + + if (isStepCounter) { + setInterval(function(){ + self.addValue(null); + }, 1000); + } + + } + + Graph.prototype.addValue = function (value) { + + value = value ? value : this.stepCounter; + + this.chart.addData( [value], "" ); + this.currentValue = value; + this.stepCounter = 0; + var color = this.updateFunction(this.currentValue); + color = color ? color : "rgba(136, 209, 018, 1)"; // green + this.chart.datasets[0].bars[this.chart.datasets[0].bars.length-1].fillColor = color; + this.chart.removeData(); + this.chart.update(); + } + + Graph.prototype.step = function() { + this.stepCounter++; + }; + + Graph.prototype.getCurrentValue = function() { + return this.currentValue; + }; + + Graph.prototype.onUpdate = function(f) { + this.updateFunction = f; + }; + + return Graph; + +}); \ No newline at end of file diff --git a/app/Game/Client/View/LayerManager.js b/app/Game/Client/View/LayerManager.js new file mode 100644 index 0000000..2e2d34f --- /dev/null +++ b/app/Game/Client/View/LayerManager.js @@ -0,0 +1,183 @@ +define([ + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Exception", + "Game/Client/View/Pixi/Layer" +], + +function (nc, Exception, Layer) { + + "use strict"; + + function LayerManager(container, me) { + this.layers = []; + this.container = container; + + this.ncTokens = [ + nc.on(nc.ns.client.view.layer.createAndInsert, this.createAndInsert, this), + nc.on(nc.ns.client.view.mesh.create, this.createMesh, this), + nc.on(nc.ns.client.view.animatedMesh.create, this.createAnimatedMesh, this), + nc.on(nc.ns.client.view.mesh.add, this.addMesh, this), + nc.on(nc.ns.client.view.mesh.remove, this.removeMesh, this), + nc.on(nc.ns.client.view.mesh.update, this.updateMesh, this), + nc.on(nc.ns.client.view.mesh.addFilter, this.addFilter, this), + nc.on(nc.ns.client.view.mesh.removeFilter, this.removeFilter, this), + nc.on(nc.ns.client.view.mesh.swapMeshIndexes, this.swapMeshIndexes, this), + nc.on(nc.ns.client.view.mesh.swapMeshes, this.swapMeshes, this) + ]; + } + + + LayerManager.prototype.render = function(centerPosition, zoom) { + for (var i = 0; i < this.layers.length; i++) { + var layer = this.layers[i]; + layer.render(centerPosition, zoom); + } + }; + + /* + * If no referenceId is given, the layer is inserted in the far background (behind=true) + * or in the foreground (behind=false/null) + */ + LayerManager.prototype.createAndInsert = function(id, options, behind, referenceId) { + var layer = new Layer(id, options); + this.insert(layer, behind, referenceId); + }; + + LayerManager.prototype.insert = function(newlayer, behind, referenceId) { + var referenceIndex = -1; + behind = !!behind; + + if (referenceId) { + for(var i = 0; i < this.layers.length; i++) { + var layer = this.layers[i]; + + if (layer.getName() === referenceId) { + referenceIndex = i; + break; + } + } + if (referenceIndex === -1) { + throw new Exception('Reference Layer (' + referenceId + ') could not be found'); + } + } else { + referenceIndex = behind ? 0 : this.container.children.length; + } + + var layerIndex = behind ? referenceIndex : referenceIndex + 1; + + this.layers.splice(layerIndex, 0, newlayer); + + this.rearrangeLayers(); + }; + + LayerManager.prototype.rearrangeLayers = function() { + + var layer; + + for (var i = this.layers.length - 1; i >= 0; i--) { + layer = this.layers[i]; + if (this.container.children.indexOf(layer.getContainer()) !== -1) { + this.container.removeChild(layer.getContainer()); + } + }; + + if (this.container.children.length !== 0) { + console.warn('Unmanaged stuff in container... ', this.container.children); + //throw new Exception('Unmanaged dirt in container... '); + } + + for (var i = 0; i < this.layers.length; i++) { + layer = this.layers[i]; + this.container.addChildAt(layer.getContainer(), i); + }; + }; + + LayerManager.prototype.getLayerById = function(id) { + for (var i = 0; i < this.layers.length; i++) { + var layer = this.layers[i]; + if (layer.getName() === id) { + return layer; + } + }; + return null; + }; + + + + + /* Delegate methods */ + + LayerManager.prototype.delegate = function() { + var methodName = arguments[0]; + var layerId = arguments[1]; + var layer = this.getLayerById(layerId); + + if (!layer) { + throw new Exception('Layer (' + layerId + ') does not exist.'); + } + + var args = arguments; + Array.prototype.splice.call(args, 0, 2); + + layer[methodName].apply(layer, args); + }; + + LayerManager.prototype.createMesh = function() { + var args = arguments; + Array.prototype.splice.call(args, 0, 0, 'createMesh') + + this.delegate.apply(this, args); + }; + + LayerManager.prototype.createAnimatedMesh = function() { + Array.prototype.splice.call(arguments, 0, 0, 'createAnimatedMesh') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.addMesh = function() { + Array.prototype.splice.call(arguments, 0, 0, 'addMesh') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.removeMesh = function() { + Array.prototype.splice.call(arguments, 0, 0, 'removeMesh') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.updateMesh = function() { + Array.prototype.splice.call(arguments, 0, 0, 'updateMesh') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.addFilter = function() { + Array.prototype.splice.call(arguments, 0, 0, 'addFilter') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.removeFilter = function() { + Array.prototype.splice.call(arguments, 0, 0, 'removeFilter') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.swapMeshIndexes = function() { + Array.prototype.splice.call(arguments, 0, 0, 'swapMeshIndexes') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.swapMeshes = function() { + Array.prototype.splice.call(arguments, 0, 0, 'swapMeshes') + this.delegate.apply(this, arguments); + }; + + LayerManager.prototype.destroy = function() { + for (var i = 0; i < this.ncTokens.length; i++) { + nc.off(this.ncTokens[i]); + }; + for (var i = this.layers.length - 1; i >= 0; i--) { + var layer = this.layers[i]; + layer.destroy(); + }; + }; + + return LayerManager; +}); \ No newline at end of file diff --git a/app/Game/Client/View/Mesh.js b/app/Game/Client/View/Mesh.js new file mode 100644 index 0000000..9290593 --- /dev/null +++ b/app/Game/Client/View/Mesh.js @@ -0,0 +1,22 @@ +define([ + 'Game/Config/Settings', + 'Lib/Utilities/NotificationCenter', + "Lib/Vendor/Stats", + "Lib/Vendor/Screenfull" +], + +function (Settings, nc, Stats, Screenfull) { + + "use strict"; + + function Mesh() { + + } + + Mesh.prototype.render = function() { + + }; + + return Mesh; + +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/ColorRangeReplaceFilter.js b/app/Game/Client/View/Pixi/ColorRangeReplaceFilter.js new file mode 100644 index 0000000..70045ea --- /dev/null +++ b/app/Game/Client/View/Pixi/ColorRangeReplaceFilter.js @@ -0,0 +1,91 @@ +define([ + "Lib/Vendor/Pixi" +], + +function (PIXI) { + + "use strict"; + + var Parent = PIXI.AbstractFilter; + + function ColorRangeReplaceFilter() { + Parent.call(this); + + this.passes = [this]; + + // set the uniforms + + this.uniforms = { + minColor: {type: '3fv', value: [0,0,0]}, + maxColor: {type: '3fv', value: [0,0,0]}, + newColor: {type: '3fv', value: [0,0,0]}, + brightnessOffset: {type: '1f', value: 0} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'uniform vec3 minColor;', + 'uniform vec3 maxColor;', + 'uniform vec3 newColor;', + 'uniform float brightnessOffset;', + + 'void main(void) {', + ' vec4 pixel = texture2D(uSampler, vTextureCoord);', + ' if(pixel.x >= minColor.x && pixel.y >= minColor.y && pixel.z >= minColor.z', + ' && pixel.w == 1.0', + ' && pixel.x <= maxColor.x && pixel.y <= maxColor.y && pixel.z <= maxColor.z) {', + + ' pixel.rgb = mix(pixel.rgb, vec3(0.2126*pixel.r + 0.7152*pixel.g + 0.0722*pixel.b), 1.0);', // desaturate to gray + ' pixel.rgb += brightnessOffset;', + ' pixel.rgb *= newColor;', + ' }', + ' gl_FragColor = pixel;', + '}' + ]; + } + + ColorRangeReplaceFilter.prototype = Object.create(Parent.prototype); + ColorRangeReplaceFilter.prototype.constructor = ColorRangeReplaceFilter; + + + Object.defineProperty(ColorRangeReplaceFilter.prototype, 'minColor', { + get: function() { + return PIXI.rgb2hex(this.uniforms.minColor.value); + }, + set: function(value) { + this.uniforms.minColor.value = PIXI.hex2rgb(value); + } + }); + + Object.defineProperty(ColorRangeReplaceFilter.prototype, 'maxColor', { + get: function() { + return PIXI.rgb2hex(this.uniforms.maxColor.value); + }, + set: function(value) { + this.uniforms.maxColor.value = PIXI.hex2rgb(value); + } + }); + + Object.defineProperty(ColorRangeReplaceFilter.prototype, 'newColor', { + get: function() { + return PIXI.rgb2hex(this.uniforms.newColor.value); + }, + set: function(value) { + this.uniforms.newColor.value = PIXI.hex2rgb(value); + } + }); + + Object.defineProperty(ColorRangeReplaceFilter.prototype, 'brightnessOffset', { + get: function() { + return this.uniforms.brightnessOffset.value; + }, + set: function(value) { + this.uniforms.brightnessOffset.value = value; + } + }); + + return ColorRangeReplaceFilter; +}); diff --git a/app/Game/Client/View/Pixi/DebugDraw.js b/app/Game/Client/View/Pixi/DebugDraw.js new file mode 100644 index 0000000..a45a621 --- /dev/null +++ b/app/Game/Client/View/Pixi/DebugDraw.js @@ -0,0 +1,179 @@ +define([ + "Lib/Vendor/Box2D" +], + +function (Box2D) { + + "use strict"; + + var Parent = Box2D.Dynamics.b2DebugDraw; + + function DebugDraw() { + Parent.call(this); + this.m_drawScale = 1; + } + + DebugDraw.prototype = Object.create(Parent.prototype); + + DebugDraw.prototype.setColor = function(color) { + this.m_ctx.debugColor = color.color; + this.m_ctx.debugFillAlpha = this.m_fillAlpha; + this.m_ctx.lineStyle(1, this.m_ctx.debugColor, this.m_alpha); + }; + + DebugDraw.prototype.SetSprite = function(sprite) { + this.m_ctx = sprite; + + this.m_sprite = { + graphics: { + clear: function () { + sprite.clear(); + sprite.lineStyle(1, 0xffffff, 0.8); + } + } + }; + + this.m_ctx.beginPath = function() { + this.beginFill(this.debugColor, this.debugFillAlpha); + }; + + this.m_ctx.closePath = function() { + this.endFill(); + }; + + this.m_ctx.fill = function() { + this.endFill(); + }; + + this.m_ctx.stroke = function() { + // do nothing + }; + + this.m_ctx.arc = function(x, y, radius, startingAngle, endingAngle, counterClockwise) { + this.drawCircle(x, y, radius); + } + + }; + + DebugDraw.prototype.DrawPolygon = function (vertices, vertexCount, color) { + this.setColor(color); + Parent.prototype.DrawPolygon.call(this, arguments); + }; + + DebugDraw.prototype.DrawSolidPolygon = function (vertices, vertexCount, color) { + this.setColor(color); + Parent.prototype.DrawSolidPolygon.apply(this, arguments); + }; + + DebugDraw.prototype.DrawCircle = function (center, radius, color) { + this.setColor(color); + Parent.prototype.DrawCircle.apply(this, arguments); + }; + + DebugDraw.prototype.DrawSolidCircle = function (center, radius, axis, color) { + this.setColor(color); + Parent.prototype.DrawSolidCircle.apply(this, arguments); + }; + + DebugDraw.prototype.DrawSegment = function (p1, p2, color) { + this.setColor(color); + Parent.prototype.DrawSegment.apply(this, arguments); + }; + + DebugDraw.prototype.DrawTransform = function (xf) { + this.setColor(0xff0000); + Parent.prototype.DrawTransform.apply(this, arguments); + }; + + +/* + DebugDraw.prototype.DrawPolygon = function (vertices, vertexCount, color) { + if (!vertexCount) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.moveTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + for (var i = 1; i < vertexCount; i++) { + s.lineTo(vertices[i].x * drawScale, vertices[i].y * drawScale); + } + s.lineTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + s.closePath(); + s.stroke(); + }; + + DebugDraw.prototype.DrawSolidPolygon = function (vertices, vertexCount, color) { + if (!vertexCount) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.fillStyle = this._color(color.color, this.m_fillAlpha); + s.moveTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + for (var i = 1; i < vertexCount; i++) { + s.lineTo(vertices[i].x * drawScale, vertices[i].y * drawScale); + } + s.lineTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + s.closePath(); + s.fill(); + s.stroke(); + }; + + DebugDraw.prototype.DrawCircle = function (center, radius, color) { + if (!radius) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.arc(center.x * drawScale, center.y * drawScale, radius * drawScale, 0, Math.PI * 2, true); + s.closePath(); + s.stroke(); + }; + + DebugDraw.prototype.DrawSolidCircle = function (center, radius, axis, color) { + if (!radius) return; + var s = this.m_ctx, + drawScale = this.m_drawScale, + cx = center.x * drawScale, + cy = center.y * drawScale; + s.moveTo(0, 0); + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.fillStyle = this._color(color.color, this.m_fillAlpha); + s.arc(cx, cy, radius * drawScale, 0, Math.PI * 2, true); + s.moveTo(cx, cy); + s.lineTo((center.x + axis.x * radius) * drawScale, (center.y + axis.y * radius) * drawScale); + s.closePath(); + s.fill(); + s.stroke(); + }; + + DebugDraw.prototype.DrawSegment = function (p1, p2, color) { + var s = this.m_ctx, + drawScale = this.m_drawScale; + s.strokeStyle = this._color(color.color, this.m_alpha); + s.beginPath(); + s.moveTo(p1.x * drawScale, p1.y * drawScale); + s.lineTo(p2.x * drawScale, p2.y * drawScale); + s.closePath(); + s.stroke(); + }; + + DebugDraw.prototype.DrawTransform = function (xf) { + var s = this.m_ctx, + drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(0xff0000, this.m_alpha); + s.moveTo(xf.position.x * drawScale, xf.position.y * drawScale); + s.lineTo((xf.position.x + this.m_xformScale * xf.R.col1.x) * drawScale, (xf.position.y + this.m_xformScale * xf.R.col1.y) * drawScale); + + s.strokeStyle = this._color(0xff00, this.m_alpha); + s.moveTo(xf.position.x * drawScale, xf.position.y * drawScale); + s.lineTo((xf.position.x + this.m_xformScale * xf.R.col2.x) * drawScale, (xf.position.y + this.m_xformScale * xf.R.col2.y) * drawScale); + s.closePath(); + s.stroke(); + }; +*/ + return DebugDraw; + +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/GameStats.js b/app/Game/Client/View/Pixi/GameStats.js new file mode 100644 index 0000000..55d520c --- /dev/null +++ b/app/Game/Client/View/Pixi/GameStats.js @@ -0,0 +1,206 @@ +define([ + "Lib/Vendor/Pixi", + "Lib/Utilities/NotificationCenter", + "Game/Config/Settings", + "Lib/Utilities/ColorConverter" +], + +function (PIXI, nc, Settings, ColorConverter) { + + "use strict"; + + function GameStats(view) { + + this.style = { + borderWidth: 3, + padding: 20, + colors: { + background: 0x000000, + text: "red", + headline: "#880000", + border: 0xAA0000 + }, + line: { + height: 16, + spacing: 5 + }, + fontSize: 12 + }; + + this.view = view; + + this.container = new PIXI.DisplayObjectContainer(); + + var blurFilter = new PIXI.BlurFilter(); + blurFilter.blurX = 12; + blurFilter.blurY = 12; + var grayFilter = new PIXI.GrayFilter(); + grayFilter.gray = 0.85; + this.filters = [blurFilter, grayFilter]; + + this.background = new PIXI.Graphics(); + this.background.alpha = 0.7; + this.container.addChild(this.background); + + this.dialog = new PIXI.DisplayObjectContainer(); + this.container.addChild(this.dialog); + + this.graphics = new PIXI.Graphics(); + + /* + gameContainer + filters + container + background + dialog + graphics + playerColor + headline + line + */ + + this.container.visible = false; + this.sortedPlayers = []; + + this.ncTokens = [ + nc.on(nc.ns.client.view.gameStats.toggle, this.toggle, this), + nc.on(nc.ns.client.view.gameStats.update, this.update, this) + ]; + } + + GameStats.prototype.getInfoContainer = function() { + return this.container; + }; + + GameStats.prototype.toggle = function(show) { + if(show) { + this.redraw(); + // show stats with filters + this.container.visible = true; + + this.view.addFilters(this.filters); + this.filters.forEach(function(filter) { filter.dirty = true; }); + } else { + this.container.visible = false; + this.view.removeFilters(this.filters); + } + } + + GameStats.prototype.update = function(sortedPlayers) { + this.sortedPlayers = sortedPlayers; + this.redraw(); + }; + + GameStats.prototype.redraw = function() { + this.background.clear(); + this.graphics.clear(); + this.dialog.removeChildren(); + + // redraw background + this.background.beginFill(this.style.colors.background); + this.background.drawRect(0, 0, Settings.STAGE_WIDTH, Settings.STAGE_HEIGHT); + this.background.endFill(); + + // redraw text and graphics + + var string = "" + + " #".pad(7, true) + " " + + "Name".pad(12, true) + + "Score".pad(6, false) + + "Deaths".pad(7, false) + + "Health".pad(9, false) + " "; + + var line = new PIXI.Text(string, { + font: "normal " + this.style.fontSize + "px 'Joystix'", + fill: this.style.colors.headline + }); + + line.position = new PIXI.Point(0, 0); + this.dialog.addChild(line); + + this.drawPlayers(this.sortedPlayers); + + var x = Settings.STAGE_WIDTH / 2 - this.dialog.getBounds().width / 2, + y = Settings.STAGE_HEIGHT / 2 - (this.sortedPlayers.length + 1) * (this.style.line.height + this.style.line.spacing) / 2; + this.dialog.position = new PIXI.Point(x, y); + + this.dialog.addChild(this.graphics); + }; + + GameStats.prototype.drawPlayers = function(sortedPlayers) { + sortedPlayers.forEach(function(player, i) { + + this.drawPlayer(player, i + 1); + this.drawPlayerGraphics(player, i + 1) + + }, this); + }; + + GameStats.prototype.drawPlayer = function(player, i) { + var string = (i + ". ").pad(7, false) + " " + + player.getNickname().pad(12, true) + + ("" + player.stats.score).pad(6, false) + + ("" + player.stats.deaths).pad(7, false) + + ("" /* + parseInt(player.stats.health, 10)*/).pad(9, false) + " "; + + var line = new PIXI.Text(string, { + font: "normal " + this.style.fontSize + "px 'Joystix'", + fill: this.style.colors.text + }); + + line.position = new PIXI.Point( + 0, + i * (this.style.line.height + this.style.line.spacing) + ); + + this.dialog.addChild(line); + }; + + GameStats.prototype.drawPlayerGraphics = function(player, i) { + var converter = new ColorConverter(); + + // draw shirt color + this.graphics.beginFill(converter.getColorByName(player.getNickname())); + this.graphics.drawRect( + 50, + i * (this.style.line.height + this.style.line.spacing), + this.style.line.height, + this.style.line.height + ); + + // draw health bar + var height = this.style.line.height / 2, + width = height * 7, + borderWidth = 2, + offsetX = 360, + offsetY = (i * (this.style.line.height + this.style.line.spacing)) + ((this.style.line.height - height) / 2); + + this.graphics.beginFill(0x000000); + this.graphics.drawRect(offsetX, offsetY, width, height); + this.graphics.endFill(); + + if(player.stats.health > 0) { + var color = player.stats.health / 100 < Settings.CRITICAL_HEALTH_THRESHOLD + ? 0xFF0000 + : 0x00FF00; + + this.graphics.beginFill(color); + this.graphics.drawRect( + offsetX + borderWidth, + offsetY + borderWidth, + width * player.stats.health / 100 - 2 * borderWidth, + height - 2 * borderWidth + ); + this.graphics.endFill(); + } + }; + + GameStats.prototype.destroy = function() { + for (var i = 0; i < this.ncTokens.length; i++) { + nc.off(this.ncTokens[i]); + }; + }; + + return GameStats; + +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/Layer.js b/app/Game/Client/View/Pixi/Layer.js new file mode 100644 index 0000000..5a44988 --- /dev/null +++ b/app/Game/Client/View/Pixi/Layer.js @@ -0,0 +1,322 @@ +define([ + "Game/Client/View/Abstract/Layer", + "Lib/Vendor/Pixi", + "Game/Client/View/Pixi/ColorRangeReplaceFilter", + "Game/Config/Settings", + "Lib/Utilities/ColorConverter", + "Lib/Utilities/NotificationCenter" +], + +function (Parent, PIXI, ColorRangeReplaceFilter, Settings, ColorConverter, nc) { + + "use strict"; + + function Layer (name, options) { + Parent.call(this, name, options); + this.container = new PIXI.DisplayObjectContainer(); + this.container.x = 0; + this.container.y = 0; + this.static = false; + this.levelSize = { + width: 0, + height: 0 + } + + this.ncTokens = this.ncTokens.concat([ + nc.on(nc.ns.client.view.layer.levelSizeUpdate, this.onLevelSizeUpdate, this) + ]); + + if (Settings.SHOW_LAYER_INFO) { + + var self = this; + var g = new PIXI.Graphics(); + var converter = new ColorConverter(); + var c = converter.getColorByName(name); + var fontSize = 12; + var textOptions = { + font: "normal " + fontSize + "px 'Joystix'", + fill: "#" + c.toString(16), + }; + + var t = new PIXI.Text(name, textOptions); + + var y = 0; + switch (name) { + case "ghost": y++; + case "item": y++; + case "tile": y++; + case "spawn": y=y; + } + + t.position = new PIXI.Point(0, fontSize * y); + + g.lineStyle (1, c, 1); + g.drawRect (0, 0, 100 + y, 100 + y); + + setTimeout(function(){ + self.container.addChild(t); + self.container.addChild(g); + }, 500); + } + } + + Layer.prototype = Object.create(Parent.prototype); + + Layer.prototype.onLevelSizeUpdate = function(levelSize) { + this.levelSize = levelSize; + }; + + Layer.prototype.getAvailableMeshFilters = function() { + return { + "blur": PIXI.BlurFilter, + "desaturate": PIXI.GrayFilter, + "pixelate": PIXI.PixelateFilter, + "colorRangeReplace": ColorRangeReplaceFilter, + }; + }; + + Layer.prototype.getContainer = function() { + return this.container; + }; + + Layer.prototype.show = function() { + this.container.visible = true; + }; + + Layer.prototype.hide = function() { + this.container.visible = false; + }; + + Layer.prototype.addMesh = function(mesh) { + this.container.addChild(mesh); + }; + + Layer.prototype.removeMesh = function(mesh) { + this.container.removeChild(mesh); + }; + + Layer.prototype.swapMeshIndexes = function(meshA, meshB) { + var indexA = this.container.getChildIndex(meshA); + var indexB = this.container.getChildIndex(meshB); + + this.container.setChildIndex(meshA, indexB); + this.container.setChildIndex(meshB, indexA); + }; + + Layer.prototype.swapMeshes = function(meshA, meshB) { + var textureA = meshA.texture; + var textureB = meshB.texture; + + meshA.setTexture(textureB); + meshA.onTextureUpdate(); + meshA.scale.x = 1; + meshA.scale.y = 1; + + meshB.setTexture(textureA); + meshB.onTextureUpdate(); + meshB.scale.x = 1; + meshB.scale.y = 1; + + }; + + Layer.prototype.createMesh = function (texturePath, callback, options) { + + var texture = (options && options.fromFrame) + ? PIXI.Texture.fromFrame(texturePath) + : PIXI.Texture.fromImage(texturePath); + + + var mesh = new PIXI.Sprite(texture); + + if(options) this.updateMesh(mesh, options); + + callback(mesh); + }; + + Layer.prototype.createAnimatedMesh = function (texturePaths, callback, options) { + var textures = []; + for (var i = 0; i < texturePaths.length; i++) { + + var texture = (options && options.fromFrame) + ? PIXI.Texture.fromFrame(texturePaths[i]) + : PIXI.Texture.fromImage(texturePaths[i]); + + texture.width = options.width; + texture.height = options.height; + //PIXI.texturesToUpdate.push(texture); + textures.push(texture); + } + + var mesh = new PIXI.MovieClip(textures); + if(options) this.updateMesh(mesh, options); + + mesh.animationSpeed = 0.5; + + mesh.play(); + + callback(mesh); + } + + Layer.prototype.updateMesh = function(mesh, options) { + if (options.x) mesh.position.x = options.x; + if (options.y) mesh.position.y = options.y; + if (options.rotation) mesh.rotation = options.rotation; + if (options.alpha) mesh.alpha = options.alpha; + if (options.width) mesh.width = options.width; + if (options.height) mesh.height = options.height; + if (options.xScale) mesh.width = Math.abs(mesh.width) * options.xScale; + if (options.yScale) mesh.scale.y = options.yScale; + if (options.visible === true || options.visible === false) mesh.visible = options.visible; + if (options.pivot) mesh.pivot = new PIXI.Point(options.pivot.x, options.pivot.y); + if (options.anchor) mesh.anchor = options.anchor; + if (options.animationSpeed) mesh.animationSpeed = options.animationSpeed; + }; + + Layer.prototype.addFilter = function(mesh, filterName, options) { + + // use game container if mesh null + if(mesh === null) { + + } + + if (!this.getAvailableMeshFilters().hasOwnProperty(filterName)) { + throw new Exception('Filter ' + filterName + ' is not available'); + } + + var MeshFilter = this.getAvailableMeshFilters()[filterName]; + var filter = new MeshFilter(); + + switch (filterName) { + case 'desaturate': + if (options.amount) filter.gray = options.amount; + break; + + case 'blur': + if (options.blurX) filter.blurX = options.blurX; + if (options.blurY) filter.blurY = options.blurY; + break; + + case 'colorRangeReplace': + if (options.minColor) filter.minColor = options.minColor; + if (options.maxColor) filter.maxColor = options.maxColor; + if (options.newColor) filter.newColor = options.newColor; + if (options.brightnessOffset) filter.brightnessOffset = options.brightnessOffset; + break; + + case 'pixelate': + if (options.sizeX) filter.size.x = options.sizeX; + if (options.sizeY) filter.size.y = options.sizeY; + break; + + default: + break; + } + + var filters = mesh.filters; + + if(!filters) { + filters = []; + } else { + // ensure uniqueness of filter by name + this.removeFilter(mesh, filterName); + } + + filters.push(filter); + mesh.filters = filters; + }; + + Layer.prototype.removeFilter = function(mesh, filterName) { + + var filters = mesh.filters; + + if(!filters) { + return; + } + + // FIXME this should throw an error i think since "options" is not defined here + // maybe we never actually call this method? + var MeshFilter = this.getAvailableMeshFilters()[options.filter]; + + filters = filters.filter(function(filter){ + return !filter instanceof MeshFilter; + }); + + mesh.filters = filter; + }; + + Layer.prototype.render = function(centerPosition, zoom) { + this.setPosition(centerPosition); + + this.setZoom(zoom); + + // Zoom + var zoomStep = (this.zoom.target - this.zoom.current) * Settings.CAMERA_GLIDE / 100; + this.zoom.current += zoomStep; + this.container.scale.x = this.zoom.current; + this.container.scale.y = this.container.scale.x; + + + /* + // we would need another zoom state, + // to separate fixed zooming (by window size) + // and user zoom by +/-/0 keys + + // this snippet would zoom the layers by its parallax + // so further away layers would not zoom as much. + + var zoomParallax = this.parallaxSpeed == 0 + ? 1 + : this.parallaxSpeed < 0 + ? (1 + this.parallaxSpeed) + : this.parallaxSpeed + 1000; + + var newZoomTarget = 1 + Math.log(this.zoom.target+2) * (zoomParallax) + console.log(newZoomTarget) + + this.container.scale.x = newZoomTarget; + this.container.scale.y = newZoomTarget; + + // Later, this would need to be added as well: + this.container.x *= newZoomTarget; + this.container.y *= newZoomTarget; + */ + + + // Position + + if (!this.static) { + var posXStep = (this.position.target.x - this.position.current.x) * Settings.CAMERA_GLIDE / 100; + this.position.current.x += posXStep; + + var posYStep = (this.position.target.y - this.position.current.y) * Settings.CAMERA_GLIDE / 100; + this.position.current.y += posYStep; + + // Add here to set 0,0 not in the center of the map but the level origin in the top left + // FIXME: use a different kind of flag than "name" + if (this.name == "spawn" + || this.name == "tile" + || this.name == "item" + || this.name == "ghost" + || this.name == "swiper" + || this.parallaxSpeed == 0) { + + this.container.x = this.position.current.x; + this.container.y = this.position.current.y; + + } else { + var x = this.position.current.x + this.levelSize.width / 2; + this.container.x = x - x * -this.parallaxSpeed; + var y = this.position.current.y + this.levelSize.height / 2; + this.container.y = y - y * -this.parallaxSpeed; + } + + this.container.x *= this.zoom.current; + this.container.y *= this.zoom.current; + + this.container.x += Settings.STAGE_WIDTH / 2; + this.container.y += Settings.STAGE_HEIGHT / 2; + } + }; + + return Layer; +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/Layers/Debug.js b/app/Game/Client/View/Pixi/Layers/Debug.js new file mode 100644 index 0000000..c5b650e --- /dev/null +++ b/app/Game/Client/View/Pixi/Layers/Debug.js @@ -0,0 +1,20 @@ +define([ + "Game/Client/View/Pixi/Layer", + "Lib/Vendor/Pixi", +], + +function (Parent, PIXI) { + + "use strict"; + + function Debug() { + Parent.call(this, "debug", 0.00000001); + + this.graphics = new PIXI.Graphics(); + this.container.addChild(this.graphics); + } + + Debug.prototype = Object.create(Parent.prototype); + + return new Debug(); +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/Layers/Ghost.js b/app/Game/Client/View/Pixi/Layers/Ghost.js new file mode 100644 index 0000000..8260f6f --- /dev/null +++ b/app/Game/Client/View/Pixi/Layers/Ghost.js @@ -0,0 +1,119 @@ +define([ + "Game/Client/View/Pixi/Layer", + "Lib/Vendor/Pixi", + "Lib/Utilities/NotificationCenter", + "Game/Config/Settings" +], + +function (Parent, PIXI, nc, Settings) { + + "use strict"; + + function Ghost() { + Parent.call(this, "ghost", {parallaxSpeed: 0}); + + this.ncTokens = this.ncTokens.concat([ + nc.on(nc.ns.client.view.layer.levelSizeUpdate, this.onLevelSizeUpdate, this), + nc.on(nc.ns.client.view.playerArrow.createAndAdd, this.onCreateAndAddPlayerArrow, this), + nc.on(nc.ns.client.view.playerArrow.update, this.onUpdatePlayerArrow, this), + nc.on(nc.ns.client.view.healthBar.createAndAdd, this.onCreateAndAddHealthBar, this), + nc.on(nc.ns.client.view.healthBar.update, this.onUpdateHealthBar, this), + nc.on(nc.ns.client.view.healthBar.remove, this.onRemoveHealthBar, this), + ]); + + } + + Ghost.prototype = Object.create(Parent.prototype); + + Ghost.prototype.onLevelSizeUpdate = function(levelSize) { + this.position.current.x = -levelSize.width / 2; + this.position.current.y = -levelSize.height / 2; + }; + + Ghost.prototype.onCreateAndAddPlayerArrow = function(callback, options) { + + var arrow = new PIXI.Graphics(); + arrow.visible = false; + this.container.addChild(arrow); + + var width = 10, + height = 10; + + arrow.beginFill(0xffffff, 0.4); + arrow.lineStyle(0, 0x000000); + arrow.moveTo(0, 0); + arrow.lineTo(width, 0); + arrow.lineTo(width / 2, height); + arrow.endFill(); + arrow.pivot = new PIXI.Point(width/2, height/2); + arrow.visible = true; + + this.onUpdatePlayerArrow(arrow, options); + + callback(arrow); + }; + + Ghost.prototype.onUpdatePlayerArrow = function(arrow, options) { + + var offsetX = 0, + offsetY = -55, + x = offsetX + options.x, + y = offsetY + options.y; + + var target = new PIXI.Point(x, y); + + arrow.position.x += (target.x -arrow.position.x) * Settings.ARROW_GLIDE / 1.5 / 100; + arrow.position.y += (target.y -arrow.position.y) * Settings.ARROW_GLIDE / 100; + + var angle = -Math.atan2(arrow.position.x - x, arrow.position.y - options.y); + angle += 0.785398163 * 4; + + arrow.rotation = angle; + }; + + // Player Info + + Ghost.prototype.onCreateAndAddHealthBar = function(callback, options) { + var healthBar = new PIXI.Graphics(); + this.container.addChild(healthBar); + + this.onUpdateHealthBar(healthBar, options); + callback(healthBar); + }; + + Ghost.prototype.onUpdateHealthBar = function(healthBar, options) { + var width = 14, + height = 2, + borderWidth = 1, + offsetX = -8, + offsetY = -52; + + if(typeof options.healthFactor != 'undefined') { + healthBar.clear(); + + healthBar.beginFill(0x000000); + healthBar.lineStyle(borderWidth, 0x000000); + healthBar.drawRect(0, 0, width, height); + healthBar.endFill(); + + if(options.healthFactor > 0) { + var color = 0x00FF00; + if(options.healthFactor < Settings.CRITICAL_HEALTH_THRESHOLD) color = 0xFF0000; + healthBar.beginFill(color); + healthBar.lineStyle(0, 0x000000); + healthBar.drawRect(borderWidth, borderWidth, width * options.healthFactor, height); + healthBar.endFill(); + } + } + + if (options.x && options.y) healthBar.position = new PIXI.Point(offsetX + options.x, offsetY + options.y); + if (options.visible === true || options.visible === false) healthBar.visible = options.visible; + }; + + Ghost.prototype.onRemoveHealthBar = function(healthBar) { + this.container.removeChild(healthBar); + }; + + return Ghost; + +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/Layers/Messages.js b/app/Game/Client/View/Pixi/Layers/Messages.js new file mode 100644 index 0000000..a9a547c --- /dev/null +++ b/app/Game/Client/View/Pixi/Layers/Messages.js @@ -0,0 +1,63 @@ +define([ + "Game/Client/View/Pixi/Layer", + "Lib/Vendor/Pixi", + "Lib/Utilities/NotificationCenter", + "Game/Config/Settings" +], + +function (Parent, PIXI, nc, Settings) { + + "use strict"; + + function Messages() { + Parent.call(this, "messages", {parallaxSpeed:-1}); + + this.ncTokens = this.ncTokens.concat([ + nc.on(nc.ns.client.view.gameStats.kill, this.onKill, this) + ]); + + this.mainTextOptions = { + font: "normal 22px 'Joystix'", + fill: "#cc0000", + stroke: "rgba(0,0,0,0.8)", + strokeThickness: 6 + }; + + this.mainText = new PIXI.Text("", this.mainTextOptions); + this.container.addChild(this.mainText); + this.mainText.visible = false; + } + + Messages.prototype = Object.create(Parent.prototype); + + Messages.prototype.onKill = function(options) { + + var killer = options.killer.isMe ? "You" : options.killer.name; + + var victim = options.victim.isMe + ? options.killer.isMe + ? "Yourself" + : "You" + : options.victim.name; + + var text = killer + " killed " + victim + " with " + options.item; + + this.mainText.setText(text); + this.mainText.setStyle(this.mainTextOptions); + this.mainText.position = new PIXI.Point(-this.mainText.width / 2, (Settings.STAGE_HEIGHT / 4) -this.mainText.height / 2); + this.mainText.visible = true; + + var self = this; + setTimeout(function(){ + self.mainText.visible = false; + }, Settings.SCORE_MESSAGE_TIMEOUT); + + } + + Messages.prototype.render = function(centerPosition, zoom) { + Parent.prototype.render.call(this, centerPosition, 1); + } + + return Messages; + +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/Layers/Swiper.js b/app/Game/Client/View/Pixi/Layers/Swiper.js new file mode 100644 index 0000000..9c89d95 --- /dev/null +++ b/app/Game/Client/View/Pixi/Layers/Swiper.js @@ -0,0 +1,57 @@ +define([ + "Game/Client/View/Pixi/Layer", + "Lib/Vendor/Pixi", + "Lib/Utilities/NotificationCenter", + "Game/Config/Settings" +], + +function (Parent, PIXI, nc, Settings) { + + function Swiper() { + Parent.call(this, "swiper", {parallaxSpeed:0}); + + this.static = true; + + this.ncTokens = this.ncTokens.concat([ + nc.on(nc.ns.client.view.swiper.swipe, this.swipe, this), + nc.on(nc.ns.client.view.swiper.end, this.end, this) + ]); + + this.sprite = new PIXI.Graphics(); + this.container.addChild(this.sprite); + + this.end(); + } + + Swiper.prototype = Object.create(Parent.prototype); + + Swiper.prototype.swipe = function(x, y) { + var offset = { + x: Settings.STAGE_WIDTH / 2 / this.zoom.current, + y: Settings.STAGE_HEIGHT / 2 / this.zoom.current, + } + + this.sprite.moveTo(offset.x + this.last.x, offset.y + this.last.y); + + this.last.x = x; + this.last.y = -y; + + this.sprite.lineTo(offset.x + this.last.x, offset.y + this.last.y); + }; + + Swiper.prototype.end = function(x, y) { + this.sprite.clear(); + + this.sprite.lineStyle(2, 0xffffff); + this.sprite.alpha = 0.5; + + this.last = { + x: 0, + y: 0 + } + }; + + + return Swiper; + +}); \ No newline at end of file diff --git a/app/Game/Client/View/Pixi/View.js b/app/Game/Client/View/Pixi/View.js new file mode 100755 index 0000000..d7c7f11 --- /dev/null +++ b/app/Game/Client/View/Pixi/View.js @@ -0,0 +1,317 @@ +define([ + "Game/Client/View/Abstract/View", + "Game/Client/View/DomController", + "Lib/Vendor/Pixi", + "Game/Config/Settings", + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Exception", + "Game/Client/View/Pixi/GameStats", + "Game/Client/View/LayerManager", + "Game/Client/View/Pixi/Layers/Ghost", + "Game/Client/View/Pixi/Layers/Swiper", + "Game/Client/PointerLockManager", + "Game/Client/View/Pixi/Layers/Debug", + "Game/Client/View/Pixi/Layers/Messages" +], + +function (Parent, domController, PIXI, Settings, nc, Exception, GameStats, LayerManager, Ghost, Swiper, pointerLockManager, Debug, Messages) { + + "use strict"; + + function PixiView () { + + Parent.call(this); + + this.xyz = Math.random() + + this.layerManager = null; + this.stage = null; + this.container = null; + this.infoContainer = null; + this.loader = null; + this.currentZoom = Settings.ZOOM_DEFAULT; + this.clickToEnable = null; + + this.init(); + + this.ncTokens = this.ncTokens.concat([ + nc.on(nc.ns.client.pointerLock.change, this.onPointerLockChange, this), + nc.on(nc.ns.core.game.events.level.loaded, this.showDefaultLayers, this) + ]); + + PIXI.scaleModes.DEFAULT = PIXI.scaleModes.NEAREST; + } + + PixiView.prototype = Object.create(Parent.prototype); + + PixiView.prototype.init = function () { + + var rendererOptions = { + view: domController.getCanvas(), + antialiasing: false, + transparent: false, + resolution: 1 + } + + if(Settings.USE_WEBGL) { + + PIXI.WebGLRenderer.glContextId = 0; + this.renderer = new PIXI.WebGLRenderer(Settings.STAGE_WIDTH, Settings.STAGE_HEIGHT, rendererOptions); + console.log('WebGLRenderer'); + + } else { + + this.renderer = new PIXI.CanvasRenderer(Settings.STAGE_WIDTH, Settings.STAGE_HEIGHT, rendererOptions); + console.warn('CanvasRenderer - not using WebGL!'); + + } + + this.onDisplaySizeChange(false); + + this.stage = new PIXI.Stage(0x333333); + + this.container = new PIXI.DisplayObjectContainer(); + this.stage.addChild(this.container); + + this.layerManager = new LayerManager(this.container, this.me); + + this.initLoader(); + + this.initCanvas(this.renderer.view); + + this.initPointerLockView(); + + // Tab Overlay (not using layer manager, cause of filters) + this.gameStats = new GameStats(this); + this.stage.addChild(this.gameStats.getInfoContainer()); + + this.ghostLayer = new Ghost(); + this.ghostLayer.hide(); + this.layerManager.insert(this.ghostLayer, false); + + this.swiperLayer = new Swiper(); + this.swiperLayer.hide() + this.layerManager.insert(this.swiperLayer, false); + + this.debugLayer = Debug; + this.debugLayer.hide(); + this.layerManager.insert(this.debugLayer, false); + + this.messagesLayer = new Messages(); + this.messagesLayer.hide(); + this.layerManager.insert(this.messagesLayer, false); + + this.render(); + } + + PixiView.prototype.showDefaultLayers = function() { + this.ghostLayer.show(); + this.swiperLayer.show() + this.debugLayer.show(); + this.messagesLayer.show(); + }; + + PixiView.prototype.render = function () { + + if (this.me) { + this.layerManager.render(this.calculateCenterPosition(), this.currentZoom); + } + + this.renderer.render(this.stage); + } + + PixiView.prototype.initPointerLockView = function() { + if (!Settings.ENABLE_POINTER_LOCK_FILTER) return; + + var blurFilter = new PIXI.BlurFilter(); + blurFilter.blurX = 42 * this.currentZoom; + blurFilter.blurY = 42 * this.currentZoom; + + var pixelFilter = new PIXI.PixelateFilter(); + pixelFilter.pixelSize = 10 * this.currentZoom ; + + var grayFilter = new PIXI.GrayFilter(); + grayFilter.gray = 0.99; + this.pointerLockFilters = [pixelFilter, grayFilter]; + + this.clickToEnable = new PIXI.Text("Click to start playing."); + this.clickToEnable.visible = false; + this.stage.addChild(this.clickToEnable) + }; + + PixiView.prototype.onPointerLockChange = function(isLocked, options) { + if (!Settings.ENABLE_POINTER_LOCK_FILTER) return; + + if(isLocked) { + this.removeFilters(this.pointerLockFilters); + + this.clickToEnable.visible = false; + this.onZoomReset(); + } else { + + if(!options || options.start !== true) { + this.clickToEnable.setText("Click to continue playing."); + } + + this.clickToEnable.setStyle({ + font: "normal " + (14 * this.currentZoom) + "px 'Joystix'", + fill: "#ffffff", + stroke: "rgba(0,0,0,0.8)", + strokeThickness: 6 * this.currentZoom + }); + + this.addFilters(this.pointerLockFilters); + this.pointerLockFilters.forEach(function(filter) { filter.dirty = true; }); + + this.clickToEnable.position = new PIXI.Point(Settings.STAGE_WIDTH / 2 - this.clickToEnable.width / 2, Settings.STAGE_HEIGHT / 2 - this.clickToEnable.height / 2) + this.clickToEnable.visible = true; + + this.onZoomReset(); + this.currentZoom *= 0.9; + } + }; + + PixiView.prototype.removeFilters = function(filters) { + + if(this.container && this.container.filters && this.container.filters.length) { + for (var i = this.container.filters.length - 1; i >= 0; i--) { + + for (var j = filters.length - 1; j >= 0; j--) { + if (filters[j] === this.container.filters[i]) { + this.container.filters.splice(i, 1); + } + } + } + + // weird bug, filters.length cant be 0, must be set to null + if(this.container.filters.length < 1) { + this.container.filters = null; + } + } + + }; + + PixiView.prototype.addFilters = function(filters) { + if (filters.length < 1) return; + if (!this.container) { + return; + } + + if (!this.container.filters) { + /* + * slice does a copy, which is important here - + * otherwise this.pointerLockFilters will be manipulated too on remove. + */ + this.container.filters = filters.slice(); + return; + } + + for (var i = 0; i < filters.length; i++) { + this.container.filters.push(filters[i]); + } + }; + + PixiView.prototype.calculateCenterPosition = function() { + var target = this.me.getHeadPosition(); + + var centerPosition = {x: target.x, y: target.y}; + centerPosition.x *= Settings.RATIO * -1; + centerPosition.y *= Settings.RATIO * -1; + + var lookAt = this.me.getLookAt(); + centerPosition.x -= lookAt.x * 600 / 4; + centerPosition.y += lookAt.y * 400 / 4; + + return centerPosition; + }; + + PixiView.prototype.onDisplaySizeChange = function(isFullScreen) { + Parent.prototype.onDisplaySizeChange.call(this, isFullScreen); + + this.renderer.resize(window.innerWidth, window.innerHeight); + this.currentZoom = window.innerWidth / 600; + + pointerLockManager.update(null, {}); // only to reposition clickToEnable text + }; + + PixiView.prototype.initLoader = function() { + this.loader = new PIXI.Graphics(); + this.stage.addChild(this.loader); + this.onUpdateLoader(0); + }; + + PixiView.prototype.onUpdateLoader = function(progress) { + var width = 200, + height = 5, + borderWidth = 1; + + if(progress < 100) { + this.loader.clear(); + + this.loader.beginFill(0x000000); + this.loader.lineStyle(borderWidth, 0x000000); + this.loader.drawRect(0, 0, width, height); + this.loader.endFill(); + + if(progress > 0) { + var color = 0xFF0FA3; + this.loader.beginFill(color); + this.loader.lineStyle(0, 0x000000); + this.loader.drawRect(borderWidth, borderWidth, width * progress / 100, height); + this.loader.endFill(); + } + + this.loader.position = new PIXI.Point( + Settings.STAGE_WIDTH / 2 - width / 2 - borderWidth, + Settings.STAGE_HEIGHT / 2 - height / 2 - borderWidth + ); + } + + this.loader.visible = progress < 100; + }; + + PixiView.prototype.onZoomIn = function() { + if(this.currentZoom + Settings.ZOOM_FACTOR <= Settings.ZOOM_MAX) { + this.currentZoom += Settings.ZOOM_FACTOR; + } + }; + + PixiView.prototype.onZoomOut = function() { + //if(this.currentZoom - Settings.ZOOM_FACTOR > window.innerWidth / 600) { + this.currentZoom -= Settings.ZOOM_FACTOR; + //} + }; + + PixiView.prototype.onZoomReset = function() { + this.currentZoom = window.innerWidth / 600; + }; + + PixiView.prototype.getTexturesFromFrame = function(textureNames) { + + var textures = []; + + for (var i = 0; i < textureNames.length; i++) { + textures.push(PIXI.Texture.fromFrame(textureNames[i])); + }; + + return textures; + }; + + PixiView.prototype.destroy = function() { + + this.layerManager.destroy(); // also calls all layers destroy + + for (var i = 0; i < this.stage.children.length; i++) { + this.stage.removeChild(this.stage.children[i]); + } + + this.renderer.render(this.stage); + + this.renderer.destroy(); + delete this.renderer; + + Parent.prototype.destroy.call(this); + }; + + return PixiView; +}); diff --git a/app/Game/Client/View/Views/ThreeView.js b/app/Game/Client/View/Three/View.js similarity index 94% rename from app/Game/Client/View/Views/ThreeView.js rename to app/Game/Client/View/Three/View.js index d24963f..e653802 100755 --- a/app/Game/Client/View/Views/ThreeView.js +++ b/app/Game/Client/View/Three/View.js @@ -1,11 +1,12 @@ define([ - "Game/Client/View/Views/AbstractView", - "Game/Client/View/DomController", + "Game/Client/View/Abstract/View", "Lib/Vendor/Three", "Game/Config/Settings" ], -function (Parent, DomController, Three, Settings) { +function (Parent, Three, Settings) { + + "use strict"; function ThreeView () { Parent.call(this); diff --git a/app/Game/Client/View/ViewManager.js b/app/Game/Client/View/ViewManager.js index 3e3da85..f935357 100755 --- a/app/Game/Client/View/ViewManager.js +++ b/app/Game/Client/View/ViewManager.js @@ -1,22 +1,24 @@ define([ "Game/Config/Settings", "Lib/Utilities/Exception", - "Game/Client/View/Views/AbstractView", - //"Game/Client/View/Views/ThreeView", - "Game/Client/View/Views/PixiView", + "Game/Client/View/Abstract/View", + //"Game/Client/View/Three/View", + "Game/Client/View/Pixi/View", "Lib/Utilities/NotificationCenter" ], -function (Settings, Exception, AbstractView, PixiView, Nc) { +function (Settings, Exception, AbstractView, PixiView, nc) { + + "use strict"; var ViewManager = {}; ViewManager.createView = function() { var view = null switch(Settings.VIEW_CONTROLLER) { - case 'Three': - view = new ThreeView(); - break; + //case 'Three': + // view = new ThreeView(); + // break; case 'Pixi': view = new PixiView(); break; @@ -36,8 +38,6 @@ function (Settings, Exception, AbstractView, PixiView, Nc) { throw new Exception("In the view", Settings.VIEW_CONTROLLER + 'View', "this.setCanvas(canvas) has not been called with a valid HTMLCanvasElement!"); } - Nc.trigger(Nc.ns.client.view.events.ready, view); - return view; } diff --git a/app/Game/Client/View/Views/AbstractView.js b/app/Game/Client/View/Views/AbstractView.js deleted file mode 100755 index 8afb1ba..0000000 --- a/app/Game/Client/View/Views/AbstractView.js +++ /dev/null @@ -1,165 +0,0 @@ -define([ - "Game/Client/View/DomController", - "Game/Config/Settings", - "Lib/Utilities/Exception", - "Lib/Utilities/NotificationCenter" -], - -function (DomController, Settings, Exception, Nc) { - - function AbstractView () { - this.me = null; - this.canvas = null; - this.debugMode = false; - - this.ncTokens = [ - Nc.on(Nc.ns.client.view.mesh.create, this.createMesh, this), - Nc.on(Nc.ns.client.view.animatedMesh.create, this.createAnimatedMesh, this), - Nc.on(Nc.ns.client.view.mesh.add, this.addMesh, this), - Nc.on(Nc.ns.client.view.mesh.remove, this.removeMesh, this), - Nc.on(Nc.ns.client.view.mesh.update, this.updateMesh, this), - - Nc.on(Nc.ns.client.view.fullscreen.change, this.onFullscreenChange, this), - Nc.on(Nc.ns.client.view.debugMode.toggle, this.onToggleDebugMode, this), - - Nc.on(Nc.ns.client.view.playerInfo.createAndAdd, this.onCreateAndAddPlayerInfo, this), - Nc.on(Nc.ns.client.view.playerInfo.update, this.onUpdatePlayerInfo, this), - Nc.on(Nc.ns.client.view.playerInfo.remove, this.onRemovePlayerInfo, this), - - Nc.on(Nc.ns.client.view.preloadBar.update, this.onUpdateLoader, this), - ]; - - - } - - AbstractView.prototype.isWebGlEnabled = function () { - try { - return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); - } catch(e) { - return false; - } - } - - AbstractView.prototype.initCanvas = function (canvas) { - - this.canvas = canvas; - DomController.initCanvas(canvas); - } - - AbstractView.prototype.loadPlayerMesh = function(player) { - throw new Exception('Abstract Function loadPlayerMesh not overwritten'); - }; - - AbstractView.prototype.loadMeshes = function(objects) { - throw new Exception('Abstract Function loadMeshes not overwritten'); - }; - - AbstractView.prototype.render = function () { - throw new Exception('Abstract Function render not overwritten'); - } - - AbstractView.prototype.createMesh = function (texturePath, callback, options) { - throw new Exception('Abstract Function createMesh not overwritten'); - } - - AbstractView.prototype.createAnimatedMesh = function (texturePaths, callback, options) { - throw new Exception('Abstract Function createAnimatedMesh not overwritten'); - } - - AbstractView.prototype.addMesh = function(mesh) { - throw new Exception('Abstract Function addMesh not overwritten'); - }; - - AbstractView.prototype.removeMesh = function(mesh) { - throw new Exception('Abstract Function removeMesh not overwritten'); - }; - - AbstractView.prototype.updateMesh = function(mesh, options) { - throw new Exception('Abstract Function updateMesh not overwritten'); - }; - - AbstractView.prototype.setMe = function(player) { - this.me = player; - }; - - AbstractView.prototype.addPlayer = function(player) { - throw new Exception('Abstract Function addPlayer not overwritten'); - }; - - AbstractView.prototype.removPlayer = function(player) { - throw new Exception('Abstract Function removPlayer not overwritten'); - }; - - AbstractView.prototype.setCameraPosition = function (x, y) { - throw new Exception('Abstract Function setCameraPosition not overwritten'); - } - - AbstractView.prototype.calculateCameraPosition = function() { - var reference = this.me.getPosition(); - var pos = {}; - - pos.x = reference.x; - pos.y = reference.y; - - pos.x = pos.x * Settings.RATIO; - pos.y = -(pos.y * Settings.RATIO); - - pos.x += this.me.playerController.xyInput.x * Settings.STAGE_WIDTH / 4; - pos.y += this.me.playerController.xyInput.y * Settings.STAGE_HEIGHT / 4; - - return pos; - }; - - AbstractView.prototype.setCameraZoom = function (z) { - throw new Exception('Abstract Function setCameraZoom not overwritten'); - }; - - AbstractView.prototype.onFullscreenChange = function(isFullScreen) { - - if (!isFullScreen) { - Settings.STAGE_WIDTH = 600; - Settings.STAGE_HEIGHT = 400; - } else { - // FIXME: Create FIXME meme (dumb and dumber) - // FIXME: don't overwrite Settings - Settings.STAGE_WIDTH = window.innerWidth; - Settings.STAGE_HEIGHT = window.innerHeight; - } - }; - - AbstractView.prototype.onToggleDebugMode = function(debugMode) { - if(debugMode) { - this.setCameraPosition(-Settings.STAGE_WIDTH / 2, -Settings.STAGE_HEIGHT / 2); - } - - this.debugMode = debugMode; - }; - - AbstractView.prototype.toggleInfo = function(show, string) { - throw new Exception('Abstract Function showInfo not overwritten'); - }; - - AbstractView.prototype.onCreateAndAddPlayerInfo = function(options) { - throw new Exception('Abstract Function onCreateAndAddPlayerInfo not overwritten'); - }; - - AbstractView.prototype.onUpdatePlayerInfo = function(playerInfo, options) { - throw new Exception('Abstract Function onUpdatePlayerInfo not overwritten'); - }; - - AbstractView.prototype.onRemovePlayerInfo = function(playerInfo) { - throw new Exception('Abstract Function onRemovePlayerInfo not overwritten'); - }; - - AbstractView.prototype.onUpdateLoader = function(progress) { - throw new Exception('Abstract Function onUpdateLoader not overwritten'); - }; - - AbstractView.prototype.destroy = function() { - for (var i = 0; i < this.ncTokens.length; i++) { - Nc.off(this.ncTokens[i]); - }; - }; - - return AbstractView; -}); \ No newline at end of file diff --git a/app/Game/Client/View/Views/PixiView.js b/app/Game/Client/View/Views/PixiView.js deleted file mode 100755 index d35b18a..0000000 --- a/app/Game/Client/View/Views/PixiView.js +++ /dev/null @@ -1,320 +0,0 @@ -define([ - "Game/Client/View/Views/AbstractView", - "Game/Client/View/DomController", - "Lib/Vendor/Pixi", - "Game/Config/Settings", - "Lib/Utilities/NotificationCenter" -], - -function (Parent, DomController, PIXI, Settings, Nc) { - - function PixiView () { - - Parent.call(this); - - this.movableObjects = []; - this.stage = null; - this.container = null; - this.infoContainer = null; - this.infoFilters = []; - this.infoBox = null; - this.loader = null; - this.init(); - this.pixi = PIXI; - - PIXI.scaleModes.DEFAULT = PIXI.scaleModes.NEAREST; - } - - - PixiView.prototype = Object.create(Parent.prototype); - - PixiView.prototype.init = function () { - - var transparent = false; - var antialias = true; - var canvas = DomController.getCanvas(); - - if(Settings.USE_WEBGL) { - this.renderer = new PIXI.WebGLRenderer(Settings.STAGE_WIDTH, Settings.STAGE_HEIGHT, canvas, transparent, antialias); - console.log('WebGLRenderer') - } else { - this.renderer = new PIXI.CanvasRenderer(Settings.STAGE_WIDTH, Settings.STAGE_HEIGHT, canvas, transparent, antialias); - console.log('CanvasRenderer - not using WebGL!') - } - - this.stage = new PIXI.Stage(0x333333); - - this.initCamera(); - this.initInfo(); - this.initLoader(); - - this.initCanvas(this.renderer.view); - } - - PixiView.prototype.render = function () { - if(this.me) { - var pos = this.calculateCameraPosition(); - this.setCameraPosition(pos.x, pos.y); - } - - this.renderer.render(this.stage); - } - - // Meshes - - PixiView.prototype.addMesh = function(mesh) { - this.container.addChild(mesh); - }; - - PixiView.prototype.removeMesh = function(mesh) { - this.container.removeChild(mesh); - }; - - PixiView.prototype.createMesh = function (texturePath, callback, options) { - - var texture = PIXI.Texture.fromImage(texturePath); - - var mesh = new PIXI.Sprite(texture); - - if(options) this.updateMesh(mesh, options); - - callback(mesh); - } - - PixiView.prototype.createAnimatedMesh = function (texturePaths, callback, options) { - var textures = []; - for (var i = 0; i < texturePaths.length; i++) { - var texture = PIXI.Texture.fromImage(texturePaths[i]); - texture.width = options.width; - texture.height = options.height; - PIXI.texturesToUpdate.push(texture); - textures.push(texture); - } - - var mesh = new PIXI.MovieClip(textures); - if(options) this.updateMesh(mesh, options); - - mesh.animationSpeed = 0.5; - - mesh.play(); - - callback(mesh); - } - - PixiView.prototype.updateMesh = function(mesh, options) { - if (options.x) mesh.position.x = options.x; - if (options.y) mesh.position.y = options.y; - if (options.rotation) mesh.rotation = options.rotation; - if (options.width) mesh.width = options.width; - if (options.height) mesh.height = options.height; - if (options.xScale) mesh.width = Math.abs(mesh.width) * options.xScale; - if (options.yScale) mesh.scale.y = options.yScale; - if (options.visible === true || options.visible === false) mesh.visible = options.visible; - if (options.pivot) mesh.pivot = new PIXI.Point(options.pivot.x, options.pivot.y); - } - - // Camera - - PixiView.prototype.initCamera = function () { - this.container = new PIXI.DisplayObjectContainer(); - this.stage.addChild(this.container); - } - - PixiView.prototype.calculateCameraPosition = function() { - var zoom = this.container.scale.x; - - var target = this.me.getHeadPosition(); - target.x *= -Settings.RATIO * zoom; - target.y *= -Settings.RATIO * zoom; - - target.x -= this.me.playerController.xyInput.x * Settings.STAGE_WIDTH / 4; - target.y += this.me.playerController.xyInput.y * Settings.STAGE_HEIGHT / 4; - - var pos = this.getCameraPosition(); - - pos.x += (target.x -pos.x) * Settings.CAMERA_GLIDE / 100; - pos.y += (target.y -pos.y) * Settings.CAMERA_GLIDE / 100; - - return pos; - }; - - PixiView.prototype.setCameraPosition = function (x, y) { - if(!this.debugMode) { - this.container.position.x = x + Settings.STAGE_WIDTH / 2; - this.container.position.y = y + Settings.STAGE_HEIGHT / 2; - } - }; - - PixiView.prototype.getCameraPosition = function () { - var pos = this.container.position; - - pos.x = pos.x - Settings.STAGE_WIDTH / 2; - pos.y = pos.y - Settings.STAGE_HEIGHT / 2; - - return pos; - }; - - PixiView.prototype.setCameraZoom = function (z) { - this.container.scale.x = z; - this.container.scale.y = z; - - }; - - PixiView.prototype.onFullscreenChange = function(isFullScreen) { - Parent.prototype.onFullscreenChange.call(this, isFullScreen); - - if(isFullScreen) { - this.renderer.resize(window.innerWidth, window.innerHeight); - this.setCameraZoom(window.innerWidth / 600); - } else { - this.renderer.resize(600, 400); - this.setCameraZoom(1); - } - }; - - // Info Overlay - - PixiView.prototype.initInfo = function() { - this.infoContainer = new PIXI.DisplayObjectContainer(); - this.stage.addChild(this.infoContainer); - - var blurFilter = new PIXI.BlurFilter(); - blurFilter.blurX = 12; - blurFilter.blurY = 12; - var grayFilter = new PIXI.GrayFilter(); - grayFilter.gray = 0.85; - this.infoFilters = [blurFilter, grayFilter]; - - this.infoText = new PIXI.Text("", {font: "normal 20px monospace", fill: "red", align: "center"}); - this.infoBox = new PIXI.Graphics(); - this.infoBox.alpha = 0.7; - - this.infoContainer.addChild(this.infoBox); - this.infoContainer.addChild(this.infoText); - - this.infoContainer.visible = false; - }; - - PixiView.prototype.toggleInfo = function(show, string) { - if(show) { - this.infoText.setText(string); - this.infoText.updateText(); - this.infoText.dirty = false; - - var x = Settings.STAGE_WIDTH / 2 - this.infoText.width / 2, - y = Settings.STAGE_HEIGHT / 2 - this.infoText.height / 2; - this.infoText.position = new PIXI.Point(x, y); - - var borderWidth = 3; - var padding = 20; - this.infoBox.clear(); - this.infoBox.beginFill(0x000000); - this.infoBox.lineStyle(borderWidth, 0xAA0000); - this.infoBox.drawRect(0, 0, this.infoText.width - borderWidth + 2 * padding * 2, this.infoText.height - borderWidth + 2 * padding); - this.infoBox.endFill(); - this.infoBox.position.x = this.infoText.position.x + borderWidth/2 - padding * 2; - this.infoBox.position.y = this.infoText.position.y + borderWidth/2 - padding; - - this.infoContainer.visible = true; - this.container.filters = this.infoFilters; - this.infoFilters.forEach(function(filter) { filter.dirty = true; }); - } else { - this.infoText.setText("..."); - this.infoContainer.visible = false; - this.container.filters = null; - } - }; - - // Player Info - - PixiView.prototype.onCreateAndAddPlayerInfo = function(callback, options) { - var playerInfo = new PIXI.Graphics(); - this.container.addChild(playerInfo); - - this.onUpdatePlayerInfo(playerInfo, options); - - callback(playerInfo); - }; - - PixiView.prototype.onUpdatePlayerInfo = function(playerInfo, options) { - var width = 14, - height = 2, - borderWidth = 1, - offsetX = -8, - offsetY = -52; - - if(typeof options.healthFactor != 'undefined') { - playerInfo.clear(); - - playerInfo.beginFill(0x000000); - playerInfo.lineStyle(borderWidth, 0x000000); - playerInfo.drawRect(0, 0, width, height); - playerInfo.endFill(); - - if(options.healthFactor > 0) { - var color = 0x00FF00; - if(options.healthFactor < 0.30) color = 0xFF0000; - playerInfo.beginFill(color); - playerInfo.lineStyle(0, 0x000000); - playerInfo.drawRect(borderWidth, borderWidth, width * options.healthFactor, height); - playerInfo.endFill(); - } - } - - if (options.x && options.y) playerInfo.position = new PIXI.Point(offsetX + options.x, offsetY + options.y); - if (options.visible === true || options.visible === false) playerInfo.visible = options.visible; - }; - - PixiView.prototype.onRemovePlayerInfo = function(playerInfo) { - this.container.removeChild(playerInfo); - }; - - PixiView.prototype.initLoader = function() { - this.loader = new PIXI.Graphics(); - this.stage.addChild(this.loader); - this.onUpdateLoader(0); - }; - - PixiView.prototype.onUpdateLoader = function(progress) { - var width = 200, - height = 5, - borderWidth = 1; - - if(progress < 100) { - this.loader.clear(); - - this.loader.beginFill(0x000000); - this.loader.lineStyle(borderWidth, 0x000000); - this.loader.drawRect(0, 0, width, height); - this.loader.endFill(); - - if(progress > 0) { - var color = 0xFF0FA3; - this.loader.beginFill(color); - this.loader.lineStyle(0, 0x000000); - this.loader.drawRect(borderWidth, borderWidth, width * progress / 100, height); - this.loader.endFill(); - } - - this.loader.position = new PIXI.Point( - Settings.STAGE_WIDTH / 2 - width / 2 - borderWidth, - Settings.STAGE_HEIGHT / 2 - height / 2 - borderWidth - ); - } - - this.loader.visible = progress < 100; - }; - - PixiView.prototype.destroy = function() { - - for (var i = 0; i < this.stage.children.length; i++) { - this.stage.removeChild(this.stage.children[i]); - } - - this.renderer.render(this.stage); - - Parent.prototype.destroy.call(this); - }; - - return PixiView; -}); diff --git a/app/Game/Config/ItemSettings.js b/app/Game/Config/ItemSettings.js index e01e953..8724c0a 100644 --- a/app/Game/Config/ItemSettings.js +++ b/app/Game/Config/ItemSettings.js @@ -1,7 +1,14 @@ -define(function() { +define([ +], + +function () { + + "use strict"; var ItemSettings = { + // weight is a number between 0.1 for very light and 10 for very heavy + "Default": { "category": "", @@ -15,7 +22,7 @@ define(function() { "rotation": "0", "bounce": "0", "grabAngle": "-1.5", - "danger": "1", + "danger": "0", "bodyType": "dynamic", }, @@ -25,7 +32,7 @@ define(function() { "image": "chest.png", "type": "ragdoll", - "weight": "5", + "weight": "7", "width": "6", "height": "12", @@ -200,7 +207,7 @@ define(function() { "category": "kitchen", "image": "fridge.gif", - "weight": "10", + "weight": "5", "width": "31", "height": "53", @@ -213,11 +220,12 @@ define(function() { "category": "kitchen", "image": "microwave.gif", - "weight": "3.6", + "weight": "4.6", "width": "19", "height": "12", "grabAngle": "-0.1", + "danger": "2", }, "Coffeemachine": @@ -240,12 +248,24 @@ define(function() { "height": "9", }, + "Table": + { + "category": "kitchen", + "image": "table.gif", + + "weight": "3.5", + "width": "60", + "height": "21", + + "grabAngle": "-0.2", + }, + "Banana": { "category": "kitchen", "image": "banana.gif", - "weight": "1", + "weight": "3", "width": "5", "height": "9", @@ -315,6 +335,45 @@ define(function() { "grabAngle": "-0.2", }, + "Couch": + { + "category": "livingroom", + "image": "couch.gif", + + "weight": "7", + "width": "50", + "height": "29", + "bounce": "3", + + "grabAngle": "-0.2", + }, + + "Cactus": + { + "category": "livingroom", + "image": "cactus.gif", + + "weight": "2.5", + "width": "17", + "height": "31", + + "danger": "1.9", + + "grabAngle": "-0.2", + }, + + "Piano": + { + "category": "livingroom", + "image": "piano.gif", + + "weight": "10", + "width": "66", + "height": "48", + + "grabAngle": "-0.2", + }, + "Bible": { "category": "livingroom", @@ -352,21 +411,19 @@ define(function() { }, - - - "Rube": + "RubeDoll": { "category": "kitchen", "image": "banana.gif", - - // "type": "rube", - "weight": "1", - "width": "5", + + "weight": "3", + "width": "15", "height": "9", - "grabAngle": "0.5", + "type": "rubedoll", + "grabAngle": "0.001", // seems to be a bug, that 0 does not work! } - } + }; return ItemSettings; }); \ No newline at end of file diff --git a/app/Game/Config/Settings.js b/app/Game/Config/Settings.js index d856841..10cf76f 100755 --- a/app/Game/Config/Settings.js +++ b/app/Game/Config/Settings.js @@ -1,50 +1,68 @@ -define(function() { + define([ +], + +function () { + + "use strict"; var Settings = { STAGE_WIDTH: 600, STAGE_HEIGHT: 400, + ZOOM_FACTOR: 0.8, + ZOOM_DEFAULT: 1, + ZOOM_MAX: 10, // BOX2D INITIALATORS BOX2D_WORLD_AABB_SIZE: 3000, BOX2D_ALLOW_SLEEP: true, BOX2D_GRAVITY: 26, - BOX2D_VELOCITY_ITERATIONS: 5, - BOX2D_POSITION_ITERATIONS: 5, + BOX2D_VELOCITY_ITERATIONS: 20, + BOX2D_POSITION_ITERATIONS: 10, // 200/100 created problems (awful teleporting when repositioning joints) BOX2D_TIME_STEP: 1 / 60, // PATHS - GRAPHICS_PATH: 'static/img/', - GRAPHICS_SUBPATH_ITEMS: 'Items/', - GRAPHICS_SUBPATH_CHARACTERS: 'Characters/', - GRAPHICS_SUBPATH_TILES: 'Tiles/', - MAPS_PATH: 'static/maps/tiled/', + GRAPHICS_PATH: "static/img/", + GRAPHICS_SUBPATH_ITEMS: "Items/", + GRAPHICS_SUBPATH_CHARACTERS: "Characters/", + GRAPHICS_SUBPATH_TILES: "Tiles/", + MAPS_PATH: "static/maps/tiled/", + AUDIO_PATH: "static/sounds/", + CHANNEL_RECORDING_PATH: "recordings/", + CHANNEL_PLAY_RECORDING: false, //"Varberg-2015-03-15T23:10:29.316Z-stones.log", RATIO: 21, //35 // original tile size is 25 but we want it to resize to 20 ORIGINAL_TILE_SIZE: 25, TILE_SIZE: 20, CAMERA_IS_ORTHOGRAPHIC: true, - CAMERA_GLIDE: 12, // % of the way per frame - VIEW_CONTROLLER: 0 ? 'Three' : 'Pixi', + CAMERA_GLIDE: 6, // % of the way per frame + VIEW_CONTROLLER: 0 ? "Three" : "Pixi", + ARROW_GLIDE: 30, // % of the way per frame + SHOW_LAYER_INFO: false, + ENABLE_POINTER_LOCK_FILTER: true, // GAME PLAY WALK_SPEED: 4, RUN_SPEED: 8, FLY_SPEED: 6.2, - JUMP_SPEED: 20, - JUMP_STOP_DAMPING_FACTOR: 0.5, - MAX_THROW_FORCE: 18 * 3.5, - MAX_THROW_ANGULAR_VELOCITY: 0, + JUMP_SPEED: 16, + JUMP_STOP_DAMPING_FACTOR: 0.4, + MAX_THROW_FORCE: 28, + MAX_THROW_ANGULAR_VELOCITY: 3, MAX_RUNNING_WEIGHT: 9, RESPAWN_TIME: 5, HEALTH_DISPLAY_TIME: 2, - RAGDOLL_DESTRUCTION_TIME: 20, + CRITICAL_HEALTH_THRESHOLD: 0.3, + RAGDOLL_DESTRUCTION_TIME: 10, + VIEWPORT_SPEED_FACTOR: 640, + VIEWPORT_LOOK_AHEAD: 0.1, + SCORE_MESSAGE_TIMEOUT: 3500, // restitution: bouncyness, friction: rubbing, density: mass TILE_FRICTION: 0.99, TILE_RESTITUTION: 0.1, - PLAYER_DENSITY: 3.68, + PLAYER_DENSITY: 12.2, //3.68, PLAYER_FRICTION: 5, PLAYER_MOTION_FRICTION: 0.1, PLAYER_RESTITUTION: 0.0, @@ -56,27 +74,29 @@ define(function() { ITEM_LINEAR_DAMPING: 0.02, // BROWSER - CANVAS_DOM_ID: 'canvasContainer', - IS_BROWSER_ENVIRONMENT: typeof window !== 'undefined', + CANVAS_DOM_ID: "canvasContainer", + IS_BROWSER_ENVIRONMENT: typeof window !== "undefined", USE_WEBGL: true, // NETWORKING - NETWORK_UPDATE_INTERVAL: 70, - CHANNEL_DESTRUCTION_TIME: 30, + NETWORK_UPDATE_INTERVAL: 70, // in milliseconds NETWORK_LOG_INCOMING: false, NETWORK_LOG_OUTGOING: false, - NETWORK_LOG_FILTER: ['ping', 'pong', 'worldUpdate', 'lookAt'], + NETWORK_LOG_FILTER: ["ping", "pong", "worldUpdate", "lookAt"], // CHANNEL - CHANNEL_END_ROUND_TIME: 4, //10, - CHANNEL_DEFAULT_MAX_USERS: 40, - CHANNEL_DEFAULT_SCORE_LIMIT: 10, - CHANNEL_DEFAULT_LEVELS: ['stones2', 'debug', 'stones2', 'debug'], + CHANNEL_MAX_USERS: 20, + CHANNEL_DESTRUCTION_TIME: 0.5 * 60, + CHANNEL_END_ROUND_TIME: 20, //10, + CHANNEL_DEFAULT_MAX_USERS: 10, + CHANNEL_DEFAULT_SCORE_LIMIT: 5, + CHANNEL_DEFAULT_LEVELS: ["debug"], + CHANNEL_RECORD_SESSION: false, // ME STATE ME_STATE_MAX_DIFFERENCE_METERS: 1, PUNKBUSTER_DIFFERENCE_METERS: 1 - } + }; Settings.TILE_RATIO = Settings.ORIGINAL_TILE_SIZE / Settings.TILE_SIZE; diff --git a/app/Game/Core/Collision/Detector.js b/app/Game/Core/Collision/Detector.js index e4994f5..3849541 100755 --- a/app/Game/Core/Collision/Detector.js +++ b/app/Game/Core/Collision/Detector.js @@ -4,6 +4,8 @@ define([ function (Box2D) { + "use strict"; + function Detector () { this.listener = new Box2D.Dynamics.b2ContactListener(); this.listener.BeginContact = this.beginContact.bind(this); diff --git a/app/Game/Core/Control/PlayerController.js b/app/Game/Core/Control/PlayerController.js index 97189cb..d264152 100755 --- a/app/Game/Core/Control/PlayerController.js +++ b/app/Game/Core/Control/PlayerController.js @@ -1,20 +1,21 @@ -define(function () { +define([ +], + +function () { function PlayerController (player) { - this.player = player; - - this._shift; - this._isJumping; this._walkingDirectionStatus = 0; } PlayerController.prototype.moveLeft = function () { + if(!this.isPlayerInputAllowed()) return; this.player.move(-1); this._walkingDirectionStatus = -1; } PlayerController.prototype.moveRight = function () { + if(!this.isPlayerInputAllowed()) return; this.player.move(1); this._walkingDirectionStatus = 1; } @@ -25,7 +26,7 @@ define(function () { } PlayerController.prototype.jump = function () { - this._isJumping = true; + if(!this.isPlayerInputAllowed()) return; this.player.jump(); } @@ -34,15 +35,31 @@ define(function () { } PlayerController.prototype.lookAt = function (options) { + if(!this.isPlayerInputAllowed()) return; if(options) this.player.lookAt(options.x, options.y); } + PlayerController.prototype.activateModifier = function() { + if (!this.isPlayerInputAllowed()) return; + this.player.activateModifier(); + }; + + PlayerController.prototype.deactivateModifier = function() { + if (!this.isPlayerInputAllowed()) return; + this.player.deactivateModifier(); + }; + PlayerController.prototype.update = function () { if(this._walkingDirectionStatus != 0) { this.player.move(this._walkingDirectionStatus); } } + // Default behaviour - may be needed later? + PlayerController.prototype.isPlayerInputAllowed = function() { + return true; + }; + PlayerController.prototype.destroy = function() { // extend if necessary }; diff --git a/app/Game/Core/GameController.js b/app/Game/Core/GameController.js index e84747b..caea9c3 100755 --- a/app/Game/Core/GameController.js +++ b/app/Game/Core/GameController.js @@ -4,24 +4,28 @@ define([ "Game/" + GLOBALS.context + "/Player", "Lib/Utilities/NotificationCenter", "Game/" + GLOBALS.context + "/GameObjects/Doll", + "Game/" + GLOBALS.context + "/GameObjects/GameObject", + "Game/" + GLOBALS.context + "/GameObjects/Item", + "Lib/Utilities/Assert", ], -function (PhysicsEngine, TiledLevel, Player, Nc, Doll) { +function (PhysicsEngine, TiledLevel, Player, nc, Doll, GameObject, Item, Assert) { + + "use strict"; function GameController (options) { this.options = options; this.players = {}; this.level = null; - this.gameObjects = null; - this.resetGameObjects(); + this.worldUpdateObjects = {}; this.physicsEngine = new PhysicsEngine(); this.physicsEngine.setCollisionDetector(); this.ncTokens = [ - Nc.on(Nc.ns.core.game.gameObject.add, this.onGameObjectAdd, this), - Nc.on(Nc.ns.core.game.gameObject.remove, this.onGameObjectRemove, this) + nc.on(nc.ns.core.game.worldUpdateObjects.add, this.onWorldUpdateObjectAdd, this), + nc.on(nc.ns.core.game.worldUpdateObjects.remove, this.onWorldUpdateObjectRemove, this) ]; this.loadLevel(options.levelUid); @@ -33,46 +37,61 @@ function (PhysicsEngine, TiledLevel, Player, Nc, Doll) { // extend for both sides if necessary }; - GameController.prototype.resetGameObjects = function() { - this.gameObjects = { - fixed: [], - animated: [] - }; + GameController.prototype.onWorldUpdateObjectAdd = function(object) { + this.worldUpdateObjects[object.uid] = object; }; - GameController.prototype.onGameObjectAdd = function(type, object) { - this.gameObjects[type].push(object); - }; - - GameController.prototype.onGameObjectRemove = function(type, object) { - var i = this.gameObjects[type].indexOf(object); - if(i>=0) this.gameObjects[type].splice(i, 1); + GameController.prototype.onWorldUpdateObjectRemove = function(object) { + delete this.worldUpdateObjects[object.uid]; }; GameController.prototype.getPhysicsEngine = function () { return this.physicsEngine; - } + }; + + GameController.prototype.getItemByUid = function(uid) { + // FIXME : maybe divide this into a dedicated item pool? + return this.worldUpdateObjects[uid]; + }; GameController.prototype.loadLevel = function (levelUid) { if (this.level) { this.level.destroy(); - this.resetGameObjects(); + this.worldUpdateObjects = {}; } - this.level = new TiledLevel(levelUid, this.physicsEngine, this.gameObjects); + this.level = new TiledLevel(levelUid, this.physicsEngine); + }; + + /* + * This is now in core, because the recorder/player + * uses the world update mechanism on the channel side + */ + GameController.prototype.onWorldUpdate = function (updateData) { + + for (var uid in updateData) { + + var gameObject = this.worldUpdateObjects[uid]; + + if (!(gameObject instanceof GameObject)) { + console.warn('Can\'t find object ' + uid + ' in worldUpdateObjects pool:', Object.keys(this.worldUpdateObjects)); + continue; + } + + this.updateGameObject(gameObject, updateData[uid]); + } + }; + + + GameController.prototype.updateGameObject = function(gameObject, gameObjectUpdate) { + gameObject.setUpdateData(gameObjectUpdate); } GameController.prototype.onResetLevel = function() { this.loadLevel(this.level.uid); }; - /* - GameController.prototype.userJoined = function (user) { - this.players[user.id] = this.createPlayer(user); - } - */ - GameController.prototype.onUserLeft = function (userId) { var player = this.players[userId]; if(!player) { @@ -82,47 +101,34 @@ function (PhysicsEngine, TiledLevel, Player, Nc, Doll) { player.destroy(); delete this.players[userId]; - } + }; - GameController.prototype.createPlayer = function(user) { - var player = new Player(user.id, this.physicsEngine, user); + GameController.prototype.createPlayer = function(user, revealedGameController) { + var player = new Player(user.id, this.physicsEngine, user, revealedGameController); this.players[user.id] = player; return player; }; + GameController.prototype.destroy = function () { for(var player in this.players) { - // this.players[player].destroy(); - - // FIXME: - // commented out for now, because players are in gameObjects array. - // try using a real gameobject for the health bar + this.players[player].destroy(); } - for (var i = 0; i < this.ncTokens.length; i++) { - Nc.off(this.ncTokens[i]); - }; + // FIXME ns.client in core? + nc.trigger(nc.ns.client.game.events.destroy); - /* - * Contents of gameObject: Players, Items, Tiles, RagDolls - * No Dolls. - */ - - for (var key in this.gameObjects) { - for (var i = 0; i < this.gameObjects[key].length; i++) { - var gameObject = this.gameObjects[key][i]; - - gameObject.destroy(); - }; - }; - - this.gameObjects = { - fixed: [], - animated: [] - }; + // Testing after destroy if worldUpdateObjects is empty + // events.game.destroy -> gameobjects.destroy() -> nc.trigger(worldUpdateObjects.remove) + if(Object.keys(this.worldUpdateObjects).length > 0) { + console.warn('Not all worldUpdateObjects have been removed... ', Object.keys(this.worldUpdateObjects)); + } this.physicsEngine.destroy(); - } + this.worldUpdateObjects = null; + + nc.off(this.ncTokens); + }; return GameController; }); diff --git a/app/Game/Core/GameObjects/Doll.js b/app/Game/Core/GameObjects/Doll.js index 1847956..32c3e05 100755 --- a/app/Game/Core/GameObjects/Doll.js +++ b/app/Game/Core/GameObjects/Doll.js @@ -1,13 +1,17 @@ define([ "Game/" + GLOBALS.context + "/GameObjects/GameObject", + "Lib/Utilities/Exception", "Lib/Vendor/Box2D", "Game/Config/Settings", "Game/" + GLOBALS.context + "/Collision/Detector", "Game/" + GLOBALS.context + "/GameObjects/Item", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Assert" ], -function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { +function (Parent, Exception, Box2D, Settings, CollisionDetector, Item, nc, Assert) { + + "use strict"; function Doll (physicsEngine, uid, player) { @@ -24,8 +28,8 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { this.standing = false; this.moveDirection = 0; this.lookDirection = 0; - this.legs; - this.footSensor; + this.legs = null; + this.footSensor = null; this.actionState = null; this.lookAtXY = { x:0, y:0 }; this.reachableItems = { @@ -36,11 +40,12 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { this.holdingJoint = null; this.holdingItem = null; - this.ragDoll = {head: null, body: null}; + this.ragDoll = {head: null, body: null}; // FIXME: wtf is this? can we remove it? this.createFixtures(); this.body.SetActive(false); + nc.trigger(nc.ns.core.game.worldUpdateObjects.add, this); } Doll.prototype = Object.create(Parent.prototype); @@ -56,6 +61,10 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { }; Doll.prototype.createFixtures = function () { + Assert.number(this.width, this.height); + Assert.number(this.reachDistance); + Assert.number(this.areaSize); + var self = this; var fixtureDef = new Box2D.Dynamics.b2FixtureDef(); @@ -64,13 +73,15 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { fixtureDef.restitution = Settings.PLAYER_RESTITUTION; var headShape = new Box2D.Collision.Shapes.b2CircleShape(); - headShape.SetRadius(this.width / 2 / Settings.RATIO); + var radius = this.width / 2 / Settings.RATIO; + headShape.SetRadius(radius); + headShape.SetLocalPosition(new Box2D.Common.Math.b2Vec2(0, -(this.height - (this.width / 2)) / Settings.RATIO)); fixtureDef.shape = headShape; fixtureDef.isSensor = false; fixtureDef.userData = { onCollisionChange: this.onImpact.bind(this) - } + }; this.body.CreateFixture(fixtureDef); @@ -103,7 +114,7 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { fixtureDef.userData = { onCollisionChange: this.onFootSensorDetection.bind(this) - } + }; this.footSensor = this.body.CreateFixture(fixtureDef); @@ -122,7 +133,7 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { onCollisionChange: function(isColliding, fixture) { self.onFixtureWithinReach(isColliding, "left", fixture); } - } + }; this.body.CreateFixture(fixtureDef); var grabSensorRightShape = new Box2D.Collision.Shapes.b2PolygonShape(); @@ -141,7 +152,7 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { onCollisionChange: function(isColliding, fixture) { self.onFixtureWithinReach(isColliding, "right", fixture); } - } + }; this.body.CreateFixture(fixtureDef); @@ -160,7 +171,7 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { fixtureDef.userData = { onCollisionChange: function(isColliding, fixture) { - var userData = fixture.GetBody().GetUserData() + var userData = fixture.GetBody().GetUserData(); if(userData instanceof Doll) { var doll = userData; var i = self.nearbyDolls.indexOf(doll); @@ -175,28 +186,29 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { } } } - } + }; this.body.CreateFixture(fixtureDef); - } + }; Doll.prototype.setActionState = function(state) { this.actionState = state; - } + }; Doll.prototype.getActionState = function() { return this.actionState; - } + }; Doll.prototype.isWalking = function() { return ["walk", "walkback", "run"].indexOf(this.actionState) >= 0; - } + }; Doll.prototype.spawn = function (x, y) { + Assert.number(x, y); this.body.SetPosition(new Box2D.Common.Math.b2Vec2(x / Settings.RATIO, y / Settings.RATIO)); this.body.SetActive(true); this.setActionState("fall"); - } + }; Doll.prototype.getHeadPosition = function() { var pos = this.body.GetPosition(); @@ -209,19 +221,21 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { Doll.prototype.setFriction = function (friction) { if(!friction) friction = -1; + Assert.number(friction); + if (this.legs.GetFriction() != friction) { this.legs.SetFriction(friction); } - } + }; - Doll.prototype.move = function (direction) { + Doll.prototype.move = function (direction, modifierActivated) { this.moveDirection = direction; var speed; var isHoldingHeavyItem = this.holdingItem && this.holdingItem.options.weight > Settings.MAX_RUNNING_WEIGHT; switch(true) { - case direction == this.lookDirection && this.isStanding() && !isHoldingHeavyItem: + case direction == this.lookDirection && this.isStanding() && !isHoldingHeavyItem && !modifierActivated: speed = Settings.RUN_SPEED; break; @@ -242,13 +256,15 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { this.setFriction(Settings.PLAYER_MOTION_FRICTION); this.body.SetAwake(true); + + Assert.number(speed, direction); var vector = new Box2D.Common.Math.b2Vec2(speed * direction, this.body.GetLinearVelocity().y); this.body.SetLinearVelocity(vector); if(this.isStanding()) { if(this.moveDirection == this.lookDirection) { - if(isHoldingHeavyItem) { + if(isHoldingHeavyItem || modifierActivated) { this.setActionState("walk"); } else { this.setActionState("run"); @@ -258,7 +274,7 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { this.setActionState("walkback"); } } - } + }; Doll.prototype.stop = function () { this.moveDirection = 0; @@ -270,20 +286,20 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { vector.x *= Settings.JUMP_STOP_DAMPING_FACTOR; this.body.SetLinearVelocity(vector); } - } + }; Doll.prototype.jump = function () { if (this.isStanding()) { this.body.SetAwake(true); + var vector = new Box2D.Common.Math.b2Vec2(0, -Settings.JUMP_SPEED); this.body.SetLinearVelocity(vector); this.setStanding(false); - this.setActionState("jump"); } - } + }; Doll.prototype.jumpStop = function () { if (!this.isStanding() ) { @@ -294,17 +310,17 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { this.body.SetLinearVelocity(vector); } } - } + }; Doll.prototype.setStanding = function (isStanding) { if (this.standing == isStanding) return; this.standing = isStanding; if(isStanding) this.setActionState("stand"); - } + }; Doll.prototype.isStanding = function () { return this.standing; - } + }; Doll.prototype.lookAt = function(x, y) { var oldLookDirection = this.lookDirection; @@ -338,9 +354,12 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { } var bodyPosition = this.body.GetPosition(); + + Assert.number(this.width, this.height); + Assert.number(this.lookDirection); var handPosition = new Box2D.Common.Math.b2Vec2( bodyPosition.x + ((this.width / 2 / Settings.RATIO) * this.lookDirection), - bodyPosition.y - this.height / 3 * 2 / Settings.RATIO // 2/3 of the body height + bodyPosition.y - this.height / 4 * 2 / Settings.RATIO // 2/3 of the body height ); this.holdingItem.reposition(handPosition, this.lookDirection); @@ -352,19 +371,31 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { } }; - Doll.prototype.throw = function(item, x, y) { - this.body.GetWorld().DestroyJoint(this.holdingJoint); + Doll.prototype.throw = function(item, options) { + if(this.holdingJoint) { + this.body.GetWorld().DestroyJoint(this.holdingJoint); + } else { + // log stack if we called throw without a holdingJoint + var w = new Error("Throwing without a holdingJoint"); + console.error(w.message + "\n" + w.stack); + } + this.holdingJoint = null; this.holdingItem = null; - item.throw(x, y); + var dollVelocity = { + x: this.body.GetLinearVelocity().x, + y: this.body.GetLinearVelocity().y + }; + + item.throw(options, dollVelocity); }; Doll.prototype.isAnotherPlayerNearby = function() { return this.nearbyDolls.length > 0; }; - Doll.prototype.onFootSensorDetection = function(isColliding, fixture) { + Doll.prototype.onFootSensorDetection = function(isColliding, fixture) { // jshint unused:false var self = this; @@ -401,9 +432,9 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { self.setStanding(false); } } - } + }; - Doll.prototype.onImpact = function(isColliding, fixture) { + Doll.prototype.onImpact = function(isColliding, fixture) { // jshint unused:false // overwrite if necessary }; @@ -421,7 +452,7 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { this.reachableItems[side].splice(i, 1); } } - } + }; Doll.prototype.getVelocities = function() { return { @@ -432,7 +463,7 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { Doll.prototype.update = function() { - if (this.body.GetLinearVelocity().x == 0 && this.isWalking()) { + if (this.body.GetLinearVelocity().x === 0 && this.isWalking()) { this.stop(); } @@ -441,7 +472,16 @@ function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { } }; + Doll.prototype.setUpdateData = function(update) { + + Parent.prototype.setUpdateData.call(this, update); + + this.setActionState(update.as); + this.lookAt(update.laxy.x, update.laxy.y); + }; + Doll.prototype.destroy = function() { + nc.trigger(nc.ns.core.game.worldUpdateObjects.remove, this); Parent.prototype.destroy.call(this); }; diff --git a/app/Game/Core/GameObjects/GameObject.js b/app/Game/Core/GameObjects/GameObject.js index 20553a1..a1eb12d 100755 --- a/app/Game/Core/GameObjects/GameObject.js +++ b/app/Game/Core/GameObjects/GameObject.js @@ -1,16 +1,24 @@ define([ "Lib/Vendor/Box2D", - "Lib/Utilities/Exception" + "Lib/Utilities/Exception", + "Lib/Utilities/Assert", + "Lib/Utilities/NotificationCenter" ], -function (Box2D, Exception) { +function (Box2D, Exception, Assert, nc) { + + "use strict"; function GameObject(physicsEngine, uid) { this.uid = uid; var def = this.getBodyDef(); def.userData = this; - this.body = physicsEngine.getWorld().CreateBody(def); + this.body = physicsEngine.createBody(def); + + this.ncTokens = (this.ncTokens || []).concat([ + nc.on(nc.ns.client.game.events.destroy, this.destroy, this) + ]); } GameObject.prototype.getBodyDef = function() { @@ -18,11 +26,14 @@ function (Box2D, Exception) { }; GameObject.prototype.destroy = function() { + if(this.body instanceof Box2D.Dynamics.b2Body) { this.body.GetWorld().DestroyBody(this.body); } else { throw new Exception("can not destroy body"); } + + nc.off(this.ncTokens); }; GameObject.prototype.getBody = function() { @@ -32,6 +43,20 @@ function (Box2D, Exception) { GameObject.prototype.getPosition = function() { return this.body.GetPosition().Copy(); }; + + GameObject.prototype.setUpdateData = function(update) { + + Assert.number(update.p.x, update.p.y); + Assert.number(update.a); + Assert.number(update.lv.x, update.lv.y); + Assert.number(update.av); + + this.body.SetAwake(true); + this.body.SetPosition(update.p); + this.body.SetAngle(update.a); + this.body.SetLinearVelocity(update.lv); + this.body.SetAngularVelocity(update.av); + }; return GameObject; diff --git a/app/Game/Core/GameObjects/Item.js b/app/Game/Core/GameObjects/Item.js index dea0e5f..4e25fb0 100644 --- a/app/Game/Core/GameObjects/Item.js +++ b/app/Game/Core/GameObjects/Item.js @@ -1,13 +1,16 @@ define([ "Game/" + GLOBALS.context + "/GameObjects/GameObject", "Lib/Vendor/Box2D", - "Lib/Utilities/Options", + "Lib/Utilities/OptionsHelper", "Game/Config/Settings", "Lib/Utilities/Exception", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Assert" ], -function (Parent, Box2D, Options, Settings, Exception, Nc) { +function (Parent, Box2D, optionsHelper, Settings, Exception, nc, Assert) { + + "use strict"; function Item(physicsEngine, uid, options) { @@ -23,25 +26,28 @@ function (Parent, Box2D, Options, Settings, Exception, Nc) { y: parseFloat(options.y) }; - this.options = Options.merge(floatOptions, options); + this.options = optionsHelper.merge(floatOptions, options); if(!this.options.category) { // FIXME add more validation - console.warn('item category empty (' + this.options.name + ')' ); + //console.warn('item category empty (' + this.options.name + ')' ); } Parent.call(this, physicsEngine, uid); this.createFixture(); this.body.ResetMassData(); this.flipDirection = 1; + if (this.body.GetMass() < 1) { + this.body.SetBullet(true); + } - Nc.trigger(Nc.ns.core.game.gameObject.add, 'animated', this); + nc.trigger(nc.ns.core.game.worldUpdateObjects.add, this); } Item.prototype = Object.create(Parent.prototype); Item.prototype.getBodyDef = function() { - + Assert.number(this.options.x, this.options.y); var bodyDef = new Box2D.Dynamics.b2BodyDef(); bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody; bodyDef.position.x = this.options.x / Settings.RATIO; @@ -49,14 +55,18 @@ function (Parent, Box2D, Options, Settings, Exception, Nc) { bodyDef.angle = 0; return bodyDef; - } + }; Item.prototype.getFixtureDef = function() { + Assert.number(this.options.width, this.options.height); + Assert.number(this.options.weight); + Assert.number(this.options.bounce); + var itemShape; var w = this.options.width / Settings.RATIO; var h = this.options.height / Settings.RATIO; - if(this.options.type == 'circle'){ + if(this.options.type == "circle") { var r = (w + h) / 4 ; itemShape = new Box2D.Collision.Shapes.b2CircleShape(); itemShape.SetRadius(r); @@ -66,25 +76,19 @@ function (Parent, Box2D, Options, Settings, Exception, Nc) { itemShape.SetAsOrientedBox(w / 2, h / 2, new Box2D.Common.Math.b2Vec2(0, -(h/2))); } - var fixtureDef = new Box2D.Dynamics.b2FixtureDef(); fixtureDef.shape = itemShape; - var offset = 4, - factor = 80; - var density = ((this.options.weight + offset) / this.options.width / this.options.height) * factor; - fixtureDef.density = density; + fixtureDef.density = this.options.weight; fixtureDef.friction = Settings.ITEM_FRICTION; - fixtureDef.restitution = this.options.bounce - ? this.options.bounce / 10 - : Settings.ITEM_RESTITUTION; + fixtureDef.restitution = this.options.bounce ? this.options.bounce / 10 : Settings.ITEM_RESTITUTION; fixtureDef.isSensor = false; fixtureDef.userData = { onCollisionChange: this.onCollisionChange.bind(this) - } + }; return fixtureDef; }; @@ -92,7 +96,7 @@ function (Parent, Box2D, Options, Settings, Exception, Nc) { Item.prototype.createFixture = function () { var fixtureDef = this.getFixtureDef(); this.body.CreateFixture(fixtureDef); - } + }; Item.prototype.flip = function(direction) { this.flipDirection = direction; @@ -100,48 +104,61 @@ function (Parent, Box2D, Options, Settings, Exception, Nc) { // FIXME: implement body flip if necessary }; - Item.prototype.beingGrabbed = function(player) { + Item.prototype.beingGrabbed = function(player) { // jshint unused:false // overwrite if necessary }; - Item.prototype.beingReleased = function(player) { + Item.prototype.beingReleased = function(player) { // jshint unused:false // overwrite if necessary }; - Item.prototype.onCollisionChange = function(isColliding, fixture, info) { + Item.prototype.onCollisionChange = function(isColliding, fixture, info) { // jshint unused:false // overwrite if necessary }; Item.prototype.reposition = function(handPosition, direction) { + Assert.number(handPosition.x, handPosition.y); + Assert.number(direction); + Assert.number(this.options.width); + Assert.number(this.options.grabAngle); + this.body.SetAwake(true); var position = new Box2D.Common.Math.b2Vec2( handPosition.x + ((this.options.width / Settings.RATIO / 2) * direction), handPosition.y - ) + ); this.body.SetPosition(position); + this.body.SetAngle((this.options.grabAngle || 0.0) * direction); this.flip(direction); - this.body.SetAngle((this.options.grabAngle || 0) * direction); }; Item.prototype.getGrabPoint = function() { return this.body.GetWorldCenter(); }; - Item.prototype.throw = function(x, y) { - var body = this.body; + Item.prototype.throw = function(options, carrierVelocity) { + this.accelerateBody(this.body, options, carrierVelocity); + }; + + Item.prototype.accelerateBody = function(body, options, carrierVelocity) { + Assert.number(this.options.weight); + Assert.number(carrierVelocity.x, carrierVelocity.y); + Assert.number(options.x, options.y); + Assert.number(options.av); + body.SetAwake(true); - var vector = new Box2D.Common.Math.b2Vec2( - x * Settings.MAX_THROW_FORCE / this.options.weight, - -y * Settings.MAX_THROW_FORCE / this.options.weight - ); - this.body.SetLinearVelocity(vector); + var x = options.x * Settings.MAX_THROW_FORCE / this.options.weight + carrierVelocity.x; + var y = -options.y * Settings.MAX_THROW_FORCE / this.options.weight + carrierVelocity.y; + var vector = new Box2D.Common.Math.b2Vec2(x, y); + body.SetLinearVelocity(vector); - body.SetAngularVelocity(Settings.MAX_THROW_ANGULAR_VELOCITY * x); + var av = -options.av * Settings.MAX_THROW_ANGULAR_VELOCITY; + body.SetAngularVelocity(av); }; Item.prototype.destroy = function() { - Nc.trigger(Nc.ns.core.game.gameObject.remove, 'animated', this); + nc.trigger(nc.ns.core.game.worldUpdateObjects.remove, this); Parent.prototype.destroy.call(this); }; diff --git a/app/Game/Core/GameObjects/Items/RagDoll.js b/app/Game/Core/GameObjects/Items/RagDoll.js index ea7ca36..48faba6 100644 --- a/app/Game/Core/GameObjects/Items/RagDoll.js +++ b/app/Game/Core/GameObjects/Items/RagDoll.js @@ -2,10 +2,15 @@ define([ "Game/" + GLOBALS.context + "/GameObjects/Item", "Lib/Vendor/Box2D", "Game/Config/Settings", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Assert", + "Lib/Utilities/OptionsHelper", + "Game/Config/ItemSettings", ], -function (Parent, Box2D, Settings, Nc) { +function (Parent, Box2D, Settings, nc, Assert, optionsHelper, ItemSettings) { + + "use strict"; function RagDoll(physicsEngine, uid, options) { @@ -90,18 +95,17 @@ function (Parent, Box2D, Settings, Nc) { - - - - + // FIXME + var ragdollOptions = optionsHelper.merge(ItemSettings.RagDoll, ItemSettings.Default); + options = optionsHelper.merge(options, ragdollOptions); Parent.call(this, physicsEngine, uid, options); //this.createSensor(); this.limbs = {}; - this.addHead(); + this.addLimb( "upperLeftLeg", this.body, @@ -133,7 +137,6 @@ function (Parent, Box2D, Settings, Nc) { - this.addLimb( "upperLeftArm", this.body, @@ -161,6 +164,7 @@ function (Parent, Box2D, Settings, Nc) { 0, options.limbs.upperRightArm.height / 2 ); + } RagDoll.prototype = Object.create(Parent.prototype); @@ -181,10 +185,14 @@ function (Parent, Box2D, Settings, Nc) { var bodyDef = Parent.prototype.getBodyDef.call(this); bodyDef.linearDamping = Settings.PLAYER_LINEAR_DAMPING; bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody; + bodyDef.position.y -= this.options.height / 2 / Settings.RATIO; // position it on top of ground + return bodyDef; }; RagDoll.prototype.getFixtureDef = function() { + Assert.number(this.options.limbs.chest.width, this.options.limbs.chest.height); + var fixtureDef = Parent.prototype.getFixtureDef.call(this); fixtureDef.density = Settings.PLAYER_DENSITY; fixtureDef.friction = Settings.PLAYER_FRICTION; @@ -204,6 +212,8 @@ function (Parent, Box2D, Settings, Nc) { }; RagDoll.prototype.createSensor = function() { + Assert.number(this.options.width, this.options.height); + var w = this.options.width / Settings.RATIO; var h = this.options.height / Settings.RATIO; @@ -216,14 +226,18 @@ function (Parent, Box2D, Settings, Nc) { fixtureDef.userData = { onCollisionChange: this.onCollisionChange.bind(this) - } + }; this.body.CreateFixture(fixtureDef); }; RagDoll.prototype.addHead = function() { + Assert.number(this.options.x, this.options.y); + Assert.number(this.options.limbs.head.x, this.options.limbs.head.y); + Assert.number(this.options.limbs.head.width); + var x = this.options.x + this.options.limbs.head.x, - y = this.options.y + this.options.limbs.head.y; + y = this.options.y + this.options.limbs.head.y - this.options.height / 2; // position it on top of ground; var bodyDef = new Box2D.Dynamics.b2BodyDef(); bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody; @@ -268,8 +282,13 @@ function (Parent, Box2D, Settings, Nc) { }; RagDoll.prototype.addLimb = function(name, connectTo, xOffset, yOffset) { + Assert.number(xOffset, yOffset); + Assert.number(this.options.x, this.options.y); + Assert.number(this.options.limbs[name].x, this.options.limbs[name].y); + Assert.number(this.options.limbs[name].width, this.options.limbs[name].height); + var x = this.options.x + this.options.limbs[name].x, - y = this.options.y + this.options.limbs[name].y; + y = this.options.y + this.options.limbs[name].y - this.options.height / 2; // position it on top of ground;; var bodyDef = new Box2D.Dynamics.b2BodyDef(); bodyDef.linearDamping = Settings.PLAYER_LINEAR_DAMPING; @@ -320,6 +339,10 @@ function (Parent, Box2D, Settings, Nc) { }; RagDoll.prototype.reposition = function(handPosition, direction) { + Assert.number(this.options.limbs.head.x, this.options.limbs.head.y); + Assert.number(this.options.grabAngle); + Assert.number(direction); + Parent.prototype.reposition.call(this, handPosition, direction); var chestPosition = this.body.GetPosition(); @@ -327,30 +350,25 @@ function (Parent, Box2D, Settings, Nc) { var position = new Box2D.Common.Math.b2Vec2( chestPosition.x + this.options.limbs.head.x / Settings.RATIO, chestPosition.y + this.options.limbs.head.y / Settings.RATIO - ) + ); this.limbs.head.SetPosition(position); - this.limbs.head.SetAngle((this.options.grabAngle || 0) * direction); + this.limbs.head.SetAngle((this.options.grabAngle || 0.0) * direction); }; - RagDoll.prototype.throw = function(x, y) { - Parent.prototype.throw.call(this, x, y); - - var limbDampingFactor = 1; + RagDoll.prototype.throw = function(options, carrierVelocity) { + Parent.prototype.throw.call(this, options, carrierVelocity); for(var name in this.limbs) { var body = this.limbs[name]; - body.SetAwake(true); - var vector = new Box2D.Common.Math.b2Vec2( - x * Settings.MAX_THROW_FORCE * limbDampingFactor / this.options.weight, - -y * Settings.MAX_THROW_FORCE * limbDampingFactor / this.options.weight - ); - body.SetLinearVelocity(vector); - // body.SetAngularVelocity(Settings.MAX_THROW_ANGULAR_VELOCITY * x); + this.accelerateBody(body, options, carrierVelocity); } }; RagDoll.prototype.setVelocities = function(options) { + Assert.number(options.linearVelocity.x, options.linearVelocity.y); + Assert.number(options.angularVelocity); + this.body.SetLinearVelocity(options.linearVelocity); this.body.SetAngularVelocity(options.angularVelocity); for(var name in this.limbs) { @@ -360,7 +378,6 @@ function (Parent, Box2D, Settings, Nc) { RagDoll.prototype.destroy = function() { - Nc.trigger(Nc.ns.core.game.gameObject.remove, 'animated', this); var world = this.body.GetWorld(); for (var name in this.limbs) { diff --git a/app/Game/Core/GameObjects/Items/RagDoll2.js b/app/Game/Core/GameObjects/Items/RagDoll2.js index 8ebedff..75fd89e 100644 --- a/app/Game/Core/GameObjects/Items/RagDoll2.js +++ b/app/Game/Core/GameObjects/Items/RagDoll2.js @@ -5,6 +5,8 @@ define([ ], function (Parent, Box2D, Settings) { + + "use strict"; function RagDoll(physicsEngine, uid, options) { Parent.call(this, physicsEngine, uid, options); diff --git a/app/Game/Core/GameObjects/Items/Rube.js b/app/Game/Core/GameObjects/Items/Rube.js deleted file mode 100644 index c5c66c5..0000000 --- a/app/Game/Core/GameObjects/Items/Rube.js +++ /dev/null @@ -1,1408 +0,0 @@ -define([ - "Game/" + GLOBALS.context + "/GameObjects/Item", - "Lib/Vendor/RubeLoader", - "Lib/Vendor/Box2D", - "Game/Config/Settings" -], - -function (Parent, RubeLoader, Box2D, Settings ) { - - // Fixme - make this loadable - var __ragdollJson; - - function Rube(physicsEngine, uid, options) { - - this.rubeLoader = null; - this.body = null; - - Parent.call(this, physicsEngine, uid, options); - var world = physicsEngine.getWorld(); - world.DestroyBody(this.body); - - var json = __ragdollJson; - - this.rubeLoader = new RubeLoader(json, world); - var scene = this.rubeLoader.getScene(); - - for (var i in scene.bodies) { - var body = scene.bodies[i]; - var position = body.GetPosition().Copy(); - position.Add(new Box2D.Common.Math.b2Vec2( - options.x / Settings.RATIO, - options.y / Settings.RATIO - )); - body.SetPosition(position); - - if(body.name == "chest"){ - this.body = body; - } - } - - var def = this.body.GetDefinition(); - def.userData = this; - this.body.SetUserData(this); - } - - Rube.prototype = Object.create(Parent.prototype); - - Rube.prototype.flip = function(direction) { - Parent.prototype.flip.call(this, direction); - // Extend - }; - - __ragdollJson = - -{ - "allowSleep" : true, - "autoClearForces" : true, - "body" : - [ - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.05748672783374786, - 0.05748672783374786, - -0.05748683214187622, - -0.05748683214187622 - ], - "y" : - [ - -0.2322469353675842, - 0.2322462797164917, - 0.2322462797164917, - -0.2322469353675842 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.001019014045596123, - "massData-center" : - { - "x" : -5.215406062575312e-08, - "y" : -3.278255462646484e-07 - }, - "massData-mass" : 0.05340443924069405, - "name" : "upperArmLeft", - "position" : - { - "x" : -0.1699507087469101, - "y" : 1.113796472549438 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture0", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.1718577891588211, - 0.1684816330671310, - 0.001688212156295776, - -0.1718577295541763, - -0.1718577295541763, - 0.001460619270801544 - ], - "y" : - [ - -0.3928470611572266, - 0.4921868443489075, - 0.4921868443489075, - 0.3841522336006165, - -0.4204435348510742, - -0.4519201517105103 - ] - } - } - }, - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture2", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.1679489463567734, - 0.1679489463567734, - -0.004204027354717255, - -0.004204027354717255 - ], - "y" : - [ - 0.4449140429496765, - 0.6170670390129089, - 0.6170670390129089, - 0.4449140429496765 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.03228222951292992, - "massData-center" : - { - "x" : 0.008858840912580490, - "y" : 0.06282533705234528 - }, - "massData-mass" : 0.3355117142200470, - "name" : "chest", - "position" : - { - "x" : -0.05338868126273155, - "y" : 0.9620395302772522 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "circle" : - { - "center" : - { - "x" : -0.007499951869249344, - "y" : 0.003749847412109375 - }, - "radius" : 0.2746430933475494 - }, - "density" : 0.2204959988594055, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture1" - }, - - { - "circle" : - { - "center" : - { - "x" : -0.03327952325344086, - "y" : -0.1384725570678711 - }, - "radius" : 0.2485582530498505 - }, - "density" : 0.2204959988594055, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture1" - } - ], - "linearVelocity" : 0, - "massData-I" : 0.004164268728345633, - "massData-center" : - { - "x" : -0.01910765282809734, - "y" : -0.06028826907277107 - }, - "massData-mass" : 0.09504657238721848, - "name" : "head", - "position" : - { - "x" : 0.04257059469819069, - "y" : 1.812389135360718 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.05748683214187622, - 0.05748683214187622, - -0.05748690664768219, - -0.05748690664768219 - ], - "y" : - [ - -0.1419981122016907, - 0.1419981718063354, - 0.1419981718063354, - -0.1419981122016907 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.0002554289239924401, - "massData-center" : - { - "x" : -3.725290298461914e-08, - "y" : 2.980232238769531e-08 - }, - "massData-mass" : 0.03265211358666420, - "name" : "lowerArmRight", - "position" : - { - "x" : 0.1177217364311218, - "y" : 0.8479318022727966 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.1415265351533890, - 0.1415265351533890, - -0.08457186818122864, - -0.08457186818122864 - ], - "y" : - [ - -0.1143886670470238, - -0.05680520832538605, - -0.05680520832538605, - -0.1143886670470238 - ] - } - } - }, - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.08623030036687851, - 0.08623030036687851, - -0.08623020350933075, - -0.08623020350933075 - ], - "y" : - [ - -0.1138511821627617, - 0.1565139442682266, - 0.1565139442682266, - -0.1138511821627617 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.0005858240183442831, - "massData-center" : - { - "x" : 0.006215983536094427, - "y" : -0.002008607611060143 - }, - "massData-mass" : 0.05964682996273041, - "name" : "lowerLegLeft", - "position" : - { - "x" : -0.08319067955017090, - "y" : 0.1298431605100632 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.05748684704303741, - 0.05748684704303741, - -0.05748672783374786, - -0.05748672783374786 - ], - "y" : - [ - -0.1419981122016907, - 0.1419981718063354, - 0.1419981718063354, - -0.1419981122016907 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.0002554284874349833, - "massData-center" : - { - "x" : 5.960464477539062e-08, - "y" : 2.980232238769531e-08 - }, - "massData-mass" : 0.03265206888318062, - "name" : "lowerArmLeft", - "position" : - { - "x" : -0.1699528992176056, - "y" : 0.8479318022727966 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.05748683214187622, - 0.05748683214187622, - -0.05748690664768219, - -0.05748690664768219 - ], - "y" : - [ - -0.2322469353675842, - 0.2322462797164917, - 0.2322462797164917, - -0.2322469353675842 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.001019015791825950, - "massData-center" : - { - "x" : -3.725290298461914e-08, - "y" : -3.278255462646484e-07 - }, - "massData-mass" : 0.05340452119708061, - "name" : "upperArmRight", - "position" : - { - "x" : 0.1177217364311218, - "y" : 1.113796472549438 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.08623021841049194, - 0.08623021841049194, - -0.08623008430004120, - -0.08623008430004120 - ], - "y" : - [ - -0.2315792292356491, - 0.2315795421600342, - 0.2315795421600342, - -0.2315792292356491 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.001625877106562257, - "massData-center" : - { - "x" : 6.705522537231445e-08, - "y" : 1.564621925354004e-07 - }, - "massData-mass" : 0.07987650483846664, - "name" : "upperLegRight", - "position" : - { - "x" : 0.03142313286662102, - "y" : 0.4171121716499329 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.08623032271862030, - 0.08623032271862030, - -0.08623017370700836, - -0.08623017370700836 - ], - "y" : - [ - -0.2315792292356491, - 0.2315795421600342, - 0.2315795421600342, - -0.2315792292356491 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.001625879434868693, - "massData-center" : - { - "x" : 7.450580596923828e-08, - "y" : 1.564621925354004e-07 - }, - "massData-mass" : 0.07987659424543381, - "name" : "upperLegLeft", - "position" : - { - "x" : -0.08319067955017090, - "y" : 0.4171121716499329 - }, - "type" : 2 - }, - - { - "angle" : 0, - "angularVelocity" : 0, - "awake" : true, - "fixture" : - [ - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.08623021841049194, - 0.08623021841049194, - -0.08623008430004120, - -0.08623008430004120 - ], - "y" : - [ - -0.1138515025377274, - 0.1563164740800858, - 0.1563164740800858, - -0.1138515025377274 - ] - } - } - }, - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "name" : "fixture3", - "polygon" : - { - "vertices" : - { - "x" : - [ - 0.1415264606475830, - 0.1415264606475830, - -0.08457189798355103, - -0.08457189798355103 - ], - "y" : - [ - -0.1143886670470238, - -0.05680520832538605, - -0.05680520832538605, - -0.1143886670470238 - ] - } - } - } - ], - "linearVelocity" : 0, - "massData-I" : 0.0005849063745699823, - "massData-center" : - { - "x" : 0.006219535600394011, - "y" : -0.002099231118336320 - }, - "massData-mass" : 0.05961277708411217, - "name" : "lowerLegRight", - "position" : - { - "x" : 0.03142313286662102, - "y" : 0.1298431605100632 - }, - "type" : 2 - } - ], - "collisionbitplanes" : - { - "names" : - [ - "bitplane1", - "bitplane2", - "bitplane3", - "bitplane4", - "bitplane5", - "bitplane6", - "bitplane7", - "bitplane8", - "bitplane9", - "bitplane10", - "bitplane11", - "bitplane12", - "bitplane13", - "bitplane14", - "bitplane15", - "bitplane16", - "bitplane17", - "bitplane18", - "bitplane19", - "bitplane20", - "bitplane21", - "bitplane22", - "bitplane23", - "bitplane24", - "bitplane25", - "bitplane26", - "bitplane27", - "bitplane28", - "bitplane29", - "bitplane30", - "bitplane31", - "bitplane32" - ] - }, - "continuousPhysics" : true, - "gravity" : - { - "x" : 0, - "y" : -10 - }, - "image" : - [ - - { - "aspectScale" : 1, - "body" : 9, - "center" : - { - "x" : 0.02911517955362797, - "y" : -0.0009155124425888062 - }, - "corners" : - { - "x" : - [ - -0.08536797016859055, - 0.1435983330011368, - 0.1435983330011368, - -0.08536797016859055 - ], - "y" : - [ - -0.1153986603021622, - -0.1153986603021622, - 0.1135676354169846, - 0.1135676354169846 - ] - }, - "file" : "../../img/Characters/Chuck/lowerLeftLeg.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.08536797016859055, - -0.1153986603021622, - 0.1435983330011368, - -0.1153986603021622, - 0.1435983330011368, - 0.1135676354169846, - -0.08536797016859055, - 0.1135676354169846 - ], - "name" : "image5", - "opacity" : 1, - "scale" : 0.2289662957191467 - }, - - { - "aspectScale" : 1, - "body" : 7, - "center" : - { - "x" : -0.02732392773032188, - "y" : 0.02671334147453308 - }, - "corners" : - { - "x" : - [ - -0.1425068378448486, - 0.08785898983478546, - 0.08785898983478546, - -0.1425068378448486 - ], - "y" : - [ - -0.2324482202529907, - -0.2324482202529907, - 0.2858749032020569, - 0.2858749032020569 - ] - }, - "file" : "../../img/Characters/Chuck/upperRightLeg.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.1425068378448486, - -0.2324482202529907, - 0.08785898983478546, - -0.2324482202529907, - 0.08785898983478546, - 0.2858749032020569, - -0.1425068378448486, - 0.2858749032020569 - ], - "name" : "image6", - "opacity" : 1, - "scale" : 0.5183231234550476 - }, - - { - "aspectScale" : 1, - "body" : 6, - "center" : - { - "x" : 0.0003027096390724182, - "y" : 0.0006600618362426758 - }, - "corners" : - { - "x" : - [ - -0.05836960300803185, - 0.05897502228617668, - 0.05897502228617668, - -0.05836960300803185 - ], - "y" : - [ - -0.2340291887521744, - -0.2340291887521744, - 0.2353493124246597, - 0.2353493124246597 - ] - }, - "file" : "../../img/Characters/Chuck/upperLeftArm.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.05836960300803185, - -0.2340291887521744, - 0.05897502228617668, - -0.2340291887521744, - 0.05897502228617668, - 0.2353493124246597, - -0.05836960300803185, - 0.2353493124246597 - ], - "name" : "image4", - "opacity" : 1, - "renderOrder" : 1, - "scale" : 0.4693785011768341 - }, - - { - "aspectScale" : 1, - "body" : 3, - "center" : - { - "x" : 0.0007003694772720337, - "y" : 0.001779437065124512 - }, - "corners" : - { - "x" : - [ - -0.05596264451742172, - 0.05736338347196579, - 0.05736338347196579, - -0.05596264451742172 - ], - "y" : - [ - -0.1398780941963196, - -0.1398780941963196, - 0.1434369683265686, - 0.1434369683265686 - ] - }, - "file" : "../../img/Characters/Chuck/lowerLeftArm.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.05596264451742172, - -0.1398780941963196, - 0.05736338347196579, - -0.1398780941963196, - 0.05736338347196579, - 0.1434369683265686, - -0.05596264451742172, - 0.1434369683265686 - ], - "name" : "image3", - "opacity" : 1, - "renderOrder" : 1, - "scale" : 0.2833150625228882 - }, - - { - "aspectScale" : 1, - "body" : 1, - "center" : - { - "x" : -0.0008481591939926147, - "y" : -0.001265347003936768 - }, - "corners" : - { - "x" : - [ - -0.1698881536722183, - 0.1681918352842331, - 0.1681918352842331, - -0.1698881536722183 - ], - "y" : - [ - -0.480212002992630, - -0.480212002992630, - 0.4776813089847565, - 0.4776813089847565 - ] - }, - "file" : "../../img/Characters/Chuck/chest.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.1698881536722183, - -0.480212002992630, - 0.1681918352842331, - -0.480212002992630, - 0.1681918352842331, - 0.4776813089847565, - -0.1698881536722183, - 0.4776813089847565 - ], - "name" : "image2", - "opacity" : 1, - "renderOrder" : 5, - "scale" : 0.9578933119773865 - }, - - { - "aspectScale" : 1, - "body" : 8, - "center" : - { - "x" : 0.003173574805259705, - "y" : -0.001172244548797607 - }, - "corners" : - { - "x" : - [ - -0.1414211541414261, - 0.1477683037519455, - 0.1477683037519455, - -0.1414211541414261 - ], - "y" : - [ - -0.2325238138437271, - -0.2325238138437271, - 0.2301793247461319, - 0.2301793247461319 - ] - }, - "file" : "../../img/Characters/Chuck/upperLeftLeg.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.1414211541414261, - -0.2325238138437271, - 0.1477683037519455, - -0.2325238138437271, - 0.1477683037519455, - 0.2301793247461319, - -0.1414211541414261, - 0.2301793247461319 - ], - "name" : "image6", - "opacity" : 1, - "renderOrder" : 6, - "scale" : 0.4627031385898590 - }, - - { - "aspectScale" : 1, - "body" : 4, - "center" : - { - "x" : 0.02851789817214012, - "y" : -0.0009155124425888062 - }, - "corners" : - { - "x" : - [ - -0.08596524596214294, - 0.1430010497570038, - 0.1430010497570038, - -0.08596524596214294 - ], - "y" : - [ - -0.1153986603021622, - -0.1153986603021622, - 0.1135676354169846, - 0.1135676354169846 - ] - }, - "file" : "../../img/Characters/Chuck/lowerLeftLeg.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.08596524596214294, - -0.1153986603021622, - 0.1430010497570038, - -0.1153986603021622, - 0.1430010497570038, - 0.1135676354169846, - -0.08596524596214294, - 0.1135676354169846 - ], - "name" : "image5", - "opacity" : 1, - "renderOrder" : 6, - "scale" : 0.2289662957191467 - }, - - { - "aspectScale" : 1, - "body" : 2, - "center" : - { - "x" : 0.01975236460566521, - "y" : -0.07194232940673828 - }, - "corners" : - { - "x" : - [ - -0.2679373621940613, - 0.3074420690536499, - 0.3074420690536499, - -0.2679373621940613 - ], - "y" : - [ - -0.4171699881553650, - -0.4171699881553650, - 0.2732853293418884, - 0.2732853293418884 - ] - }, - "file" : "../../img/Characters/Chuck/head.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.2679373621940613, - -0.4171699881553650, - 0.3074420690536499, - -0.4171699881553650, - 0.3074420690536499, - 0.2732853293418884, - -0.2679373621940613, - 0.2732853293418884 - ], - "name" : "image1", - "opacity" : 1, - "renderOrder" : 6, - "scale" : 0.6904553174972534 - }, - - { - "aspectScale" : 1, - "body" : 0, - "center" : - { - "x" : 0.002138927578926086, - "y" : 0.0006600618362426758 - }, - "corners" : - { - "x" : - [ - -0.05653338506817818, - 0.06081124022603035, - 0.06081124022603035, - -0.05653338506817818 - ], - "y" : - [ - -0.2340291887521744, - -0.2340291887521744, - 0.2353493124246597, - 0.2353493124246597 - ] - }, - "file" : "../../img/Characters/Chuck/upperLeftArm.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.05653338506817818, - -0.2340291887521744, - 0.06081124022603035, - -0.2340291887521744, - 0.06081124022603035, - 0.2353493124246597, - -0.05653338506817818, - 0.2353493124246597 - ], - "name" : "image4", - "opacity" : 1, - "renderOrder" : 8, - "scale" : 0.4693785011768341 - }, - - { - "aspectScale" : 1, - "body" : 5, - "center" : - { - "x" : 0.002538725733757019, - "y" : 0.001779437065124512 - }, - "corners" : - { - "x" : - [ - -0.05412428826093674, - 0.05920173972845078, - 0.05920173972845078, - -0.05412428826093674 - ], - "y" : - [ - -0.1398780941963196, - -0.1398780941963196, - 0.1434369683265686, - 0.1434369683265686 - ] - }, - "file" : "../../img/Characters/Chuck/lowerLeftArm.png", - "filter" : 0, - "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], - "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], - "glVertexPointer" : - [ - -0.05412428826093674, - -0.1398780941963196, - 0.05920173972845078, - -0.1398780941963196, - 0.05920173972845078, - 0.1434369683265686, - -0.05412428826093674, - 0.1434369683265686 - ], - "name" : "image3", - "opacity" : 1, - "renderOrder" : 8, - "scale" : 0.2833150625228882 - } - ], - "joint" : - [ - - { - "anchorA" : - { - "x" : 0.001047849655151367, - "y" : -0.1790249347686768 - }, - "anchorB" : - { - "x" : 0.001048207283020020, - "y" : 0.08683943748474121 - }, - "bodyA" : 0, - "bodyB" : 5, - "enableLimit" : true, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : 0, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint4", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 1.919862151145935 - }, - - { - "anchorA" : - { - "x" : -0.1165831685066223, - "y" : 0.3330366015434265 - }, - "anchorB" : - { - "x" : -2.135336399078369e-05, - "y" : 0.1812803745269775 - }, - "bodyA" : 1, - "bodyB" : 0, - "enableLimit" : false, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : -2.268928050994873, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint3", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 3.141592741012573 - }, - - { - "anchorA" : - { - "x" : 0.07454992830753326, - "y" : 0.5068108439445496 - }, - "anchorB" : - { - "x" : -0.02141102217137814, - "y" : -0.3435407876968384 - }, - "bodyA" : 1, - "bodyB" : 2, - "enableLimit" : true, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : -1.221730470657349, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint0", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 0.6981316804885864 - }, - - { - "anchorA" : - { - "x" : 0.1367489844560623, - "y" : -0.3606387376785278 - }, - "anchorB" : - { - "x" : 0.05056380107998848, - "y" : 0.1842886805534363 - }, - "bodyA" : 1, - "bodyB" : 7, - "enableLimit" : true, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : -0.6981316804885864, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint5", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 1.919862151145935 - }, - - { - "anchorA" : - { - "x" : -0.08329562842845917, - "y" : -0.3541148304939270 - }, - "anchorB" : - { - "x" : -0.05503869056701660, - "y" : 0.1909851431846619 - }, - "bodyA" : 1, - "bodyB" : 8, - "enableLimit" : true, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : -0.6981316804885864, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint6", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 1.919862151145935 - }, - - { - "anchorA" : - { - "x" : 0.1710196435451508, - "y" : 0.3308989405632019 - }, - "anchorB" : - { - "x" : -9.131431579589844e-05, - "y" : 0.1791421175003052 - }, - "bodyA" : 1, - "bodyB" : 6, - "enableLimit" : false, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : -2.268928050994873, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint2", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 3.141592741012573 - }, - - { - "anchorA" : - { - "x" : 0.0004334598779678345, - "y" : 0.08706557750701904 - }, - "anchorB" : - { - "x" : 0.0004332214593887329, - "y" : -0.1787990331649780 - }, - "bodyA" : 3, - "bodyB" : 6, - "enableLimit" : true, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : 0, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint1", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 1.919862151145935 - }, - - { - "anchorA" : - { - "x" : 0.002425249665975571, - "y" : -0.1845821887254715 - }, - "anchorB" : - { - "x" : 0.002425376325845718, - "y" : 0.1026860624551773 - }, - "bodyA" : 7, - "bodyB" : 9, - "enableLimit" : true, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : -2.268928050994873, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint8", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 0 - }, - - { - "anchorA" : - { - "x" : -0.0009558200836181641, - "y" : -0.1818936169147491 - }, - "anchorB" : - { - "x" : -0.0009555891156196594, - "y" : 0.1055182516574860 - }, - "bodyA" : 8, - "bodyB" : 4, - "enableLimit" : true, - "enableMotor" : false, - "jointSpeed" : 0, - "lowerLimit" : -2.268928050994873, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint7", - "refAngle" : 0, - "type" : "revolute", - "upperLimit" : 0 - } - ], - "positionIterations" : 3, - "stepsPerSecond" : 60.0, - "subStepping" : false, - "velocityIterations" : 8, - "warmStarting" : true -} - - - - - - - return Rube; - -}); \ No newline at end of file diff --git a/app/Game/Core/GameObjects/Items/RubeDoll.js b/app/Game/Core/GameObjects/Items/RubeDoll.js new file mode 100644 index 0000000..77b511d --- /dev/null +++ b/app/Game/Core/GameObjects/Items/RubeDoll.js @@ -0,0 +1,223 @@ +define([ + "Game/" + GLOBALS.context + "/GameObjects/Item", + "Lib/Vendor/RubeLoader", + "Lib/Vendor/Box2D", + "Game/Config/Settings", + "Lib/Utilities/Assert", + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Matrix", + "json!Game/Asset/RubeDoll.json" // using requirejs json loader plugin +], + +function (Parent, RubeLoader, Box2D, Settings, Assert, nc, Matrix, RubeDollJson) { + + "use strict"; + + function RubeDoll(physicsEngine, uid, options) { + Assert.number(options.x, options.y); + + this.rubeLoader = null; + this.body = null; + this.limbs = {}; + this.joints = null; + this.limits = []; + + var chest = null; + this.rubeLoader = new RubeLoader(RubeDollJson, physicsEngine.getWorldForRubeLoader()); + + this.loadRubeDollFromScene(options); + + Parent.call(this, physicsEngine, uid, options); + physicsEngine.destroyBody(this.body); + this.body = this.limbs.chest; + delete this.limbs.chest; + + this.body.SetUserData(this); + + this.flip(options.direction || 1); + } + + RubeDoll.prototype = Object.create(Parent.prototype); + + RubeDoll.prototype.loadRubeDollFromScene = function(options) { + var scene = this.rubeLoader.getScene(); + + + + for (var i in scene.bodies) { + var body = scene.bodies[i]; + var position = body.GetPosition().Copy(); + position.Add(new Box2D.Common.Math.b2Vec2( + options.x / Settings.RATIO, + options.y / Settings.RATIO + )); + body.SetPosition(position); + this.limbs[body.name] = body; + + // code snipped possibly needed for filtering between doll and rubedoll while holding + //var filterData = new Box2D.Dynamics.b2FilterData(); + //filterData.groupIndex = -66; + //if(body.name != "head" && body.name != "chest") { + // for (var fixture = body.GetFixtureList(); fixture; fixture = fixture.GetNext()) { + // fixture.SetFilterData(filterData); + // } + //} + } + + this.joints = scene.joints; + + var count = 0; + for (var i in this.joints) { + this.limits[i] = { + lower: this.joints[i].GetLowerLimit(), + upper: this.joints[i].GetUpperLimit(), + }; +/* + this.joints[i].EnableLimit(false); + + if(count < 4 && this.joints[i] instanceof Box2D.Dynamics.Joints.b2RevoluteJoint) { + console.log(i); + } else { + body.GetWorld().DestroyJoint(this.joints[i]); + } + count++; + */ + } + }; + + RubeDoll.prototype.getFixtureDef = function() { + var fixtureDef = new Box2D.Dynamics.b2FixtureDef(); + fixtureDef.shape = new Box2D.Collision.Shapes.b2CircleShape(); + return fixtureDef; + }; + + RubeDoll.prototype.flip = function(direction) { + var oldFlipDirection = this.flipDirection; + + Parent.prototype.flip.call(this, direction); + + if(oldFlipDirection != direction) { + + for (var i in this.joints) { + var joint = this.joints[i]; + var limits = this.limits[i]; + + if (joint instanceof Box2D.Dynamics.Joints.b2RevoluteJoint) { + + if (direction > 0) { + joint.SetLimits(limits.lower, limits.upper); + continue; + } + + var a1 = limits.lower * -1; + var a2 = limits.upper * -1; + + if (a2 > a1) { + joint.SetLimits(a1, a2); + } else { + joint.SetLimits(a2, a1); + } + + // joint.SetAngle(joint.GetAngle() * -1); + } + } + } + }; + + RubeDoll.prototype.reposition = function(handPosition, direction) { + var oldPosition = this.getPosition(); + var oldAngle = this.body.GetAngle(); + var oldDirection = this.flipDirection; + + // calls flip() at the end of Parent reposition() + Parent.prototype.reposition.call(this, handPosition, direction); + + var differenceAngle = oldAngle - this.body.GetAngle(); + + //this.body.SetLinearVelocity(new Box2D.Common.Math.b2Vec2(0, 0)); + + var offset = Box2D.Common.Math.b2Math.SubtractVV(this.getPosition(), oldPosition); + var grabAngle = (this.options.grabAngle || 0.001); + + for(var key in this.limbs) { + var limb = this.limbs[key]; + + // Setting position offset first (floor to hand) + var position = limb.GetPosition().Copy(); + position.Add(offset); + limb.SetPosition(position); + + // grabing local point to "rotate" around (x, y position transform only) + var localPoint = this.body.GetLocalPoint(limb.GetPosition().Copy()); + + // create rotation matrix from chest rotation difference + var mat = Box2D.Common.Math.b2Mat22.FromAngle(differenceAngle); + + // matrix multiplication with local limb position + position = Box2D.Common.Math.b2Math.MulTMV(mat, localPoint); + + // translating back to global position + var globalPoint = this.body.GetWorldPoint(position); + limb.SetPosition(globalPoint); + + // relative limb rotating by chest rotation difference + var d = (oldDirection == direction) ? -1 : 1; + limb.SetAngle((limb.GetAngle() - differenceAngle) * d); + + //limb.SetType(Box2D.Dynamics.b2Body.b2_staticBody); + //limb.SetLinearVelocity(new Box2D.Common.Math.b2Vec2(0, 0)); + } + }; + + RubeDoll.prototype.setVelocities = function(options) { + Assert.number(options.linearVelocity.x, options.linearVelocity.y); + Assert.number(options.angularVelocity); + + this.body.SetLinearVelocity(options.linearVelocity); + this.body.SetAngularVelocity(options.angularVelocity); + for(var name in this.limbs) { + this.limbs[name].SetLinearVelocity(options.linearVelocity); + } + }; + + RubeDoll.prototype.getPosition = function() { + return this.body.GetPosition().Copy(); + }; + + RubeDoll.prototype.getHeadPosition = function() { + return this.limbs.head.GetPosition().Copy(); + }; + + RubeDoll.prototype.setUpdateData = function(update) { + + Parent.prototype.setUpdateData.call(this, update); + +/* + for(var name in update.limbs) { + Assert.number(update.limbs[name].p.x, update.limbs[name].p.y); + Assert.number(update.limbs[name].a); + Assert.number(update.limbs[name].lv.x, update.limbs[name].lv.y); + Assert.number(update.limbs[name].av); + + this.limbs[name].SetAwake(true); + this.limbs[name].SetPosition(update.limbs[name].p); + this.limbs[name].SetAngle(update.limbs[name].a); + this.limbs[name].SetLinearVelocity(update.limbs[name].lv); + this.limbs[name].SetAngularVelocity(update.limbs[name].av); + } + */ + } + + RubeDoll.prototype.destroy = function() { + + var world = this.body.GetWorld(); + + for (var name in this.limbs) { + world.DestroyBody(this.limbs[name]); + } + + Parent.prototype.destroy.call(this); + }; + + return RubeDoll; +}); \ No newline at end of file diff --git a/app/Game/Core/GameObjects/Items/Skateboard.js b/app/Game/Core/GameObjects/Items/Skateboard.js index 67fc387..1e12df6 100755 --- a/app/Game/Core/GameObjects/Items/Skateboard.js +++ b/app/Game/Core/GameObjects/Items/Skateboard.js @@ -1,30 +1,125 @@ define([ "Game/" + GLOBALS.context + "/GameObjects/Item", "Lib/Vendor/Box2D", - "Game/Config/Settings" + "Game/Config/Settings", + "Lib/Utilities/Assert" ], -function (Parent, Box2D, Settings) { +function (Parent, Box2D, Settings, Assert) { + + "use strict"; function Skateboard(physicsEngine, uid, options) { Parent.call(this, physicsEngine, uid, options); + } + + Skateboard.prototype = Object.create(Parent.prototype); + + Skateboard.prototype.createFixture = function () { + Assert.number(this.options.width, this.options.height); + Assert.number(this.options.weight); + + var deckShape = new Box2D.Collision.Shapes.b2PolygonShape(); + var w = this.options.width / Settings.RATIO; + var h = 2 / Settings.RATIO; + deckShape.SetAsOrientedBox(w / 2, h / 2, new Box2D.Common.Math.b2Vec2(0, -(4.5 / Settings.RATIO))); + + var fixtureDef = new Box2D.Dynamics.b2FixtureDef(); + fixtureDef.shape = deckShape; + + var offset = 4, + factor = 80; + var density = ((this.options.weight + offset) / this.options.width / this.options.height) * factor; + fixtureDef.density = density; + fixtureDef.friction = Settings.ITEM_FRICTION; + fixtureDef.restitution = 0.2; + fixtureDef.isSensor = false; + + this.body.CreateFixture(fixtureDef); + + + this.addWheel( + -8, + -2.5 + ); + + this.addWheel( + 7, + -2.5 + ); + + }; + + Skateboard.prototype.addWheel = function(x, y) { + Assert.number(x, y); + + var bodyDef = new Box2D.Dynamics.b2BodyDef(); + bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody; + bodyDef.position.x = x / Settings.RATIO; + bodyDef.position.y = y / Settings.RATIO; + bodyDef.angle = 0; + + var wheelShape = new Box2D.Collision.Shapes.b2CircleShape(); + wheelShape.SetRadius(2.5 / Settings.RATIO); + wheelShape.SetLocalPosition(new Box2D.Common.Math.b2Vec2(x / Settings.RATIO, y / Settings.RATIO)); + + + var fixtureDef = new Box2D.Dynamics.b2FixtureDef(); + var offset = 4, + factor = 80; + var density = ((0.1 + offset) / 3 / 3) * factor; + fixtureDef.density = density; + fixtureDef.shape = wheelShape; + fixtureDef.restitution = 0.2; + fixtureDef.isSensor = false; + fixtureDef.friction = 0.0005; + + this.body.CreateFixture(fixtureDef); + }; + + Skateboard.prototype.flip = function(direction) { + this.flipDirection = direction; + }; + + return Skateboard; + +}); + + +/* +define([ + "Game/" + GLOBALS.context + "/GameObjects/Item", + "Lib/Vendor/Box2D", + "Game/Config/Settings", + "Lib/Utilities/Assert" +], + +function (Parent, Box2D, Settings, Assert) { + + "use strict"; + + function Skateboard(physicsEngine, uid, options) { + + Parent.call(this, physicsEngine, uid, options); this.wheels = [ - this.addWheel( - options.x + 8, - options.y - 1.5 - ), - this.addWheel( - options.x - 8, - options.y - 1.5 - ) + this.addWheel( + options.x + 8, + options.y - 1.5 + ), + this.addWheel( + options.x - 8, + options.y - 1.5 + ) ]; } Skateboard.prototype = Object.create(Parent.prototype); Skateboard.prototype.createFixture = function () { + Assert.number(this.options.width, this.options.height); + Assert.number(this.options.weight); var deckShape = new Box2D.Collision.Shapes.b2PolygonShape(); var w = this.options.width / Settings.RATIO; @@ -43,17 +138,18 @@ function (Parent, Box2D, Settings) { fixtureDef.isSensor = false; this.body.CreateFixture(fixtureDef); - } + }; Skateboard.prototype.addWheel = function(x, y) { + Assert.number(x, y); - var bodyDef = new Box2D.Dynamics.b2BodyDef(); + var bodyDef = new Box2D.Dynamics.b2BodyDef(); bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody; bodyDef.position.x = x / Settings.RATIO; bodyDef.position.y = y / Settings.RATIO; bodyDef.angle = 0; - var wheelShape = new Box2D.Collision.Shapes.b2CircleShape(); + var wheelShape = new Box2D.Collision.Shapes.b2CircleShape(); wheelShape.SetRadius(1.5 / Settings.RATIO); wheelShape.SetLocalPosition(new Box2D.Common.Math.b2Vec2(0, 0)); @@ -64,18 +160,23 @@ function (Parent, Box2D, Settings) { fixtureDef.density = density; fixtureDef.shape = wheelShape; fixtureDef.isSensor = false; + fixtureDef.friction = 0; var wheelBody = this.body.GetWorld().CreateBody(bodyDef); wheelBody.CreateFixture(fixtureDef); - var revoluteJointDef = new Box2D.Dynamics.Joints.b2RevoluteJointDef(); - revoluteJointDef.enableMotor = false; + //var revoluteJointDef = new Box2D.Dynamics.Joints.b2RevoluteJointDef(); + var revoluteJointDef = new Box2D.Dynamics.Joints.b2WeldJointDef(); + //revoluteJointDef.enableMotor = false; - revoluteJointDef.Initialize(this.body, wheelBody, wheelBody.GetWorldCenter()); - this.body.GetWorld().CreateJoint(revoluteJointDef); - // FIXME this means, that we will have bodies in the world, which must not be - // updated (wheels) because they are always connected to a body which will be updated. + + revoluteJointDef.Initialize(this.body, wheelBody, wheelBody.GetWorldCenter()); + var j = this.body.GetWorld().CreateJoint(revoluteJointDef); + + + // FIXME this means, that we will have bodies in the world, which must not be + // updated (wheels) because they are always connected to a body which will be updated. return wheelBody; }; @@ -85,18 +186,13 @@ function (Parent, Box2D, Settings) { // FIXME: implement body flip if necessary }; - Skateboard.prototype.throw = function(x, y) { - Parent.prototype.throw.call(this, x, y); + Skateboard.prototype.throw = function(options, carrierVelocity) { + Parent.prototype.throw.call(this, options, carrierVelocity); for (var i = 0; i < this.wheels.length; i++) { var body = this.wheels[i]; - body.SetAwake(true); - var vector = new Box2D.Common.Math.b2Vec2( - x * Settings.MAX_THROW_FORCE / this.options.weight, - -y * Settings.MAX_THROW_FORCE / this.options.weight - ); - body.SetLinearVelocity(vector); + this.accelerateBody(body, options, carrierVelocity); } }; @@ -110,4 +206,5 @@ function (Parent, Box2D, Settings) { return Skateboard; -}); \ No newline at end of file +}); +*/ \ No newline at end of file diff --git a/app/Game/Core/GameObjects/SpectatorDoll.js b/app/Game/Core/GameObjects/SpectatorDoll.js index f86bba6..912314a 100644 --- a/app/Game/Core/GameObjects/SpectatorDoll.js +++ b/app/Game/Core/GameObjects/SpectatorDoll.js @@ -4,9 +4,11 @@ define([ ], function (Parent, Box2D) { + + "use strict"; function SpectatorDoll(physicsEngine, uid, player) { - Parent.call(this, physicsEngine, uid); + //Parent.call(this, physicsEngine, uid); } SpectatorDoll.prototype = Object.create(Parent.prototype); @@ -31,6 +33,9 @@ function (Parent, Box2D) { SpectatorDoll.prototype.update = function() { }; + + SpectatorDoll.prototype.destroy = function() { + }; return SpectatorDoll; diff --git a/app/Game/Core/GameObjects/Tile.js b/app/Game/Core/GameObjects/Tile.js index 2f483db..dee3a1a 100755 --- a/app/Game/Core/GameObjects/Tile.js +++ b/app/Game/Core/GameObjects/Tile.js @@ -3,22 +3,25 @@ define([ "Lib/Vendor/Box2D", "Game/Config/Settings", "Lib/Utilities/Exception", - "Lib/Utilities/NotificationCenter" + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Assert" ], -function (Parent, Box2D, Settings, Exception, Nc) { +function (Parent, Box2D, Settings, Exception, nc, Assert) { + + "use strict"; function Tile(physicsEngine, uid, options) { this.options = options; Parent.call(this, physicsEngine, uid); this.createPhysicTile(this.options); - - Nc.trigger(Nc.ns.core.game.gameObject.add, 'fixed', this); } Tile.prototype = Object.create(Parent.prototype); Tile.prototype.getBodyDef = function() { + Assert.number(this.options.x, this.options.y); + Assert.number(this.options.r); var bodyDef = new Box2D.Dynamics.b2BodyDef(); bodyDef.type = Box2D.Dynamics.b2Body.b2_staticBody; @@ -27,7 +30,7 @@ function (Parent, Box2D, Settings, Exception, Nc) { bodyDef.angle = (this.options.r || 0) * 90 * Math.PI / 180; return bodyDef; - } + }; Tile.prototype.createPhysicTile = function (tile) { var vertices = this.createVertices(tile); @@ -41,7 +44,7 @@ function (Parent, Box2D, Settings, Exception, Nc) { fixtureDef.restitution = Settings.TILE_RESTITUTION; fixtureDef.isSensor = false; this.body.CreateFixture(fixtureDef); - } + }; Tile.prototype.createVertices = function (tile) { var vs = []; @@ -102,22 +105,17 @@ function (Parent, Box2D, Settings, Exception, Nc) { default: throw new Exception("Tile Creation - no shape given"); - break; } return vs; - } + }; Tile.prototype.mkArg = function (multiplier) { return Settings.TILE_SIZE / 2 / Settings.RATIO * multiplier; - } + }; Tile.prototype.addVec = function (vs, m1, m2) { return vs.push(new Box2D.Common.Math.b2Vec2(this.mkArg(m1), this.mkArg(m2))); - } - - Tile.prototype.destroy = function() { - Nc.trigger(Nc.ns.core.game.gameObject.remove, 'fixed', this); }; return Tile; diff --git a/app/Game/Core/Loader/Level.js b/app/Game/Core/Loader/Level.js index e20b6ab..83cabde 100755 --- a/app/Game/Core/Loader/Level.js +++ b/app/Game/Core/Loader/Level.js @@ -2,14 +2,17 @@ define([ "Game/Config/Settings", "Lib/Vendor/Box2D", "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Abstract", "Game/" + GLOBALS.context + "/Collision/Detector", "Game/" + GLOBALS.context + "/GameObjects/Tile", "Game/" + GLOBALS.context + "/GameObjects/Item", "Game/" + GLOBALS.context + "/GameObjects/Items/Skateboard", "Game/" + GLOBALS.context + "/GameObjects/Items/RagDoll", - "Game/" + GLOBALS.context + "/GameObjects/Items/Rube" + "Game/" + GLOBALS.context + "/GameObjects/Items/RubeDoll" -], function (Settings, Box2D, Nc, CollisionDetector, Tile, Item, Skateboard, RagDoll, Rube) { +], function (Settings, Box2D, nc, Abstract, CollisionDetector, Tile, Item, Skateboard, RagDoll, RubeDoll) { + + "use strict"; function Level (uid, engine) { this.uid = uid; @@ -17,87 +20,83 @@ define([ this.levelObject = null; this.isLoaded = false; this.load(this.uid); + this.spawnPoints = null; } Level.prototype.load = function (uid) { var self = this; - var path = Settings.MAPS_PATH + uid + ".json" - this.loadLevelDataFromPath(path, function(levelData) { - self.levelData = levelData; - self.createTiles(); - self.createItems(); - self.isLoaded = true; - Nc.trigger(Nc.ns.core.game.events.level.loaded); + // FIXME: check if theres a security hazard here (user injected path) + var path = Settings.MAPS_PATH + uid + ".json"; + this.loadLevelDataFromPath(path, function (levelData) { + self.setup(levelData); }); - } + }; + + Level.prototype.setup = function(levelData) { // jshint unused:false + this.isLoaded = true; + nc.trigger(nc.ns.core.game.events.level.loaded); + }; + + Level.prototype.createItems = function(options) { + for (var i = 0; i < options.length; i++) { + var uid = "item-" + i; + this.createItem(uid, options[i]); + } + }; Level.prototype.createItem = function(uid, options) { + switch(options.type) { - case 'skateboard': + case "skateboard": return new Skateboard(this.engine, uid, options); - case 'ragdoll': + case "ragdoll": return new RagDoll(this.engine, uid, options); - case 'rube': - return new Rube(this.engine, uid, options); + case "rubedoll": + return new RubeDoll(this.engine, uid, options); default: return new Item(this.engine, uid, options); } }; + Level.prototype.createTiles = function(options) { + for (var i = 0; i < options.length; i++) { + new Tile(this.engine, "tile-" + i, options[i]); + } + }; + + Level.prototype.createSpawnPoints = function(points) { + this.spawnPoints = points; + }; + + Level.prototype.setupLayer = function(options, behind, referenceId) { // jshint unused:false + // will be extended (so far only in client) + }; + + + Level.prototype.createContainer = function(options) { // jshint unused:false + // nothing to do here yet, in the future perhaps synchronize day/night graphics + }; + Level.prototype.getRandomSpawnPoint = function() { - throw new Error("Level not loaded."); + if(!this.spawnPoints) { + return { + x: 150 + Math.random() * 300, + y: -500 + }; + } + + var size = this.spawnPoints.length; + var object = this.spawnPoints[parseInt(Math.random() * (size -1), 10)]; + return { - x: 150 + Math.random() * 300, - y: -500 + x: object.x / Settings.TILE_RATIO, + y: object.y / Settings.TILE_RATIO }; }; Level.prototype.destroy = function () { - /* - for (var key in this.gameObjects) { - for (var i = 0; i < this.gameObjects[key].length; i++) { - this.gameObjects[key][i].destroy(); - } - } - */ this.isLoaded = false; - } - -/* Extended by TiledLevel - Level.prototype.createTiles = function () { - - if (!this.levelData || !this.levelData.tiles || this.levelData.tiles.length < 1) { - throw "Level: Can't create physic tiles, no tiles found"; - } - - var tiles = this.levelData.tiles; - - for (var i = 0; i < tiles.length; i++) { - var options = tiles[i]; - //options.m = this.tileAtPositionExists(options.x, options.y - 1) ? "Soil" : "GrassSoil"; - options.m = "Soil"; - //this.gameObjects.fixed.push( - new Tile(this.engine, "tile-" + i, options); - //); - } - } -*/ - -/* Extended by TiledLevel - Level.prototype.createItems = function() { - if (!this.levelData || !this.levelData.items) { - return; - } - var items = this.levelData.items; - - for (var i = 0; i < items.length; i++) { - var options = items[i]; - var uid = "item-" + i; - var item = this.createItem(uid, options); - //this.gameObjects.animated.push(item); - }; }; -*/ return Level; -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/app/Game/Core/Loader/TiledLevel.js b/app/Game/Core/Loader/TiledLevel.js index 9ffe666..16834b0 100755 --- a/app/Game/Core/Loader/TiledLevel.js +++ b/app/Game/Core/Loader/TiledLevel.js @@ -3,115 +3,191 @@ define([ "Game/Config/Settings", "Game/Config/ItemSettings", "Lib/Vendor/Box2D", - "Lib/Utilities/Options", + "Lib/Utilities/OptionsHelper", "Lib/Utilities/Exception", + "Lib/Utilities/NotificationCenter", + "Lib/Utilities/Assert", + "Game/Client/View/Abstract/Layer", "Game/" + GLOBALS.context + "/Collision/Detector", "Game/" + GLOBALS.context + "/GameObjects/Tile", "Game/" + GLOBALS.context + "/GameObjects/Item", "Game/" + GLOBALS.context + "/GameObjects/Items/Skateboard", -], function (Parent, Settings, ItemSettings, Box2D, Options, Exception, CollisionDetector, Tile, Item, Skateboard) { +], function (Parent, Settings, ItemSettings, Box2D, optionsHelper, Exception, nc, Assert, AbstractLayer, CollisionDetector, Tile, Item, Skateboard) { - // Public + "use strict"; + function TiledLevel (path, engine) { + this.layerMapping = { + tiles: this.createTiles.bind(this), + items: this.createItems.bind(this), + spawnpoints: this.createSpawnPoints.bind(this) + //collision: this.createTiles.bind(this), collision renamed to tiles + }; + this.levelData = null; Parent.call(this, path, engine); } TiledLevel.prototype = Object.create(Parent.prototype); - TiledLevel.prototype.createTiles = function () { - if (!this.levelData) { - throw "Level: Can't create level, nothing found"; + + TiledLevel.prototype.setup = function(levelData) { + this.levelData = levelData; + + var spawnpointsExists = levelData.layers.some(function(o) { + return o.name == AbstractLayer.ID.SPAWN; + }); + + if (!spawnpointsExists) { + console.warn("No layerMapping for level file layer: " + layerOptions.name); + return; } - var collisionLayer = this.getLayer(this.levelData, "collision"); - - if(collisionLayer) { - - for (var i = 0; i < collisionLayer.data.length; i++) { - - var gid = collisionLayer.data[i]; - if(gid === 0) continue; - - var imagePath = this.getTileImagePath(gid); - - - var parts = imagePath.split("/"); - var tileType = parts[parts.length - 1].split(".")[0].split(""); - - // FIXME rename s to shape, r to rotation etc. - - var options = { - s: parseInt(tileType[0], 10), - r: parseInt(tileType[1], 10), - t: imagePath, - x: i % collisionLayer.width, - y: parseInt(i / collisionLayer.height , 10) - } - - //this.gameObjects.fixed.push( - new Tile(this.engine, "tile-" + i, options); - //); + function getLayerId(name, i) { + var mapping = { + tiles: AbstractLayer.ID.TILE, + items: AbstractLayer.ID.ITEM, + spawnpoints: AbstractLayer.ID.SPAWN + }; + if(mapping[name]) { + return mapping[name]; } - } else { - console.warn("Level: No collision Layer given"); + return "layer-" + i + "-" + name; } - } - TiledLevel.prototype.createItems = function() { - var objects = this.getLayer(this.levelData, "items").objects; + var spawnpointsFound = false, + lastLayerId = null; + // from spawnpoints to background + for (var i = levelData.layers.length - 1; i >= 0; i--) { + var layerOptions = levelData.layers[i]; + layerOptions.z = i; + layerOptions.layerId = getLayerId(layerOptions.name, i); + + if (layerOptions.name == AbstractLayer.ID.SPAWN) { + spawnpointsFound = true; + } + + if (!spawnpointsFound) { + continue; + } + + // Setting up spawnpoints and then all layers behind it + this.setupLayer(layerOptions, true, lastLayerId); + + if(this.layerMapping[layerOptions.name]) { + this.layerMapping[layerOptions.name](layerOptions); + } + + lastLayerId = layerOptions.layerId; + } + + spawnpointsFound = false; // reset (used in mkLayer) + lastLayerId = AbstractLayer.ID.SPAWN; + + + // from spawnpoints to foreground + for (var i = 0; i < levelData.layers.length; i++) { + var layerOptions = levelData.layers[i]; + layerOptions.z = i; + layerOptions.layerId = getLayerId(layerOptions.name, i); + + if (layerOptions.name == AbstractLayer.ID.SPAWN) { + spawnpointsFound = true; + continue; + } + + if (!spawnpointsFound) { + continue; + } + + // Setting up all layers before + this.setupLayer(layerOptions, false, lastLayerId); + + if(this.layerMapping[layerOptions.name]) { + this.layerMapping[layerOptions.name](layerOptions); + } + + lastLayerId = layerOptions.layerId; + } + + + Parent.prototype.setup.call(this, levelData); + }; + + TiledLevel.prototype.createTiles = function(options) { + + var data = options.data; + var tilesOptions = []; + for (var i = 0; i < data.length; i++) { + + var gid = data[i]; + if(gid === 0) continue; + + var imagePath = this.getTileImagePath(gid); + + var parts = imagePath.split("/"); + var tileType = parts[parts.length - 1].split(".")[0].split(""); + + // FIXME rename s to shape, r to rotation etc. + + var tileOptions = { + s: parseInt(tileType[0], 10), + r: parseInt(tileType[1], 10), + t: imagePath, + x: i % options.width, + y: parseInt(i / options.width , 10) + }; + + tilesOptions.push(tileOptions); + } + + Parent.prototype.createTiles.call(this, tilesOptions); + }; + + TiledLevel.prototype.createItems = function(options) { + var objects = options.objects; + var itemsOptions = []; for (var i = 0; i < objects.length; i++) { - var object = objects[i]; + options = this.gatherOptions(objects[i]); + itemsOptions.push(options); + } - var options = this.gatherOptions(object); + Parent.prototype.createItems.call(this, itemsOptions); + }; - var uid = "item-" + i; - var item = this.createItem(uid, options); - //this.gameObjects.animated.push(item); - }; + TiledLevel.prototype.createSpawnPoints = function(options) { + var points = options.objects.map(function(o) { + return { x: o.x, y: o.y }; + }); + + Parent.prototype.createSpawnPoints.call(this, points); }; TiledLevel.prototype.gatherOptions = function(tiledObject) { - var options = {}; - + var options = this.getDefaultItemSettingsByName(tiledObject.name); options.name = tiledObject.name; - options.rotation = tiledObject.rotation; - options.width = tiledObject.width / Settings.TILE_RATIO; - options.height = tiledObject.height / Settings.TILE_RATIO; - options.x = (tiledObject.x + tiledObject.width / 2) / Settings.TILE_RATIO; + options.x = (tiledObject.x + options.width / 2) / Settings.TILE_RATIO; options.y = (tiledObject.y + options.height / 2) / Settings.TILE_RATIO; - - if (!options.width) options.width = undefined; - if (!options.height) options.height = undefined; - - var defaultOptions = this.getDefaultItemSettingsByName(options.name); - - options = Options.merge(options, defaultOptions); - //options = Options.merge(tiledObject.properties, options); + // FIXME: check RAD vs. DEG for options.rotation = tiledObject.rotation; return options; }; - TiledLevel.prototype.getDefaultItemSettingsByName = function(name) { if(!name) { - throw new Exception('Item name cannot be be empty'); + throw new Exception("Item name cannot be be empty"); } if(ItemSettings[name] === undefined) { - throw new Exception('Item name (' + name + ') cannot be found in item list'); + throw new Exception("Item name (" + name + ") cannot be found in item list"); } - var options = ItemSettings.Default; - - options = Options.merge(ItemSettings[name], options); - - return options; + return optionsHelper.merge(ItemSettings[name], ItemSettings.Default); }; TiledLevel.prototype.getTileImagePath = function(gid) { @@ -122,35 +198,7 @@ define([ return tileset.tiles["" + (gid - offset)].image; } } - } - - TiledLevel.prototype.getRandomSpawnPoint = function() { - if(!this.levelData) { - return Parent.prototype.getRandomSpawnPoint.call(this); - } else { - - var spawnLayer = this.getLayer(this.levelData, "spawnpoints"); - - var size = spawnLayer.objects.length; - var object = spawnLayer.objects[parseInt(Math.random() * (size -1), 10)]; - - return { - x: object.x / Settings.TILE_RATIO, - y: object.y / Settings.TILE_RATIO - } - - } - }; - - TiledLevel.prototype.getLayer = function(levelData, name) { - for (var i = 0; i < levelData.layers.length; i++) { - if(levelData.layers[i].name === name) { - return levelData.layers[i]; - } - } - - throw "Layer '" + name + "' not found."; }; return TiledLevel; -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/app/Game/Core/Physics/Engine.js b/app/Game/Core/Physics/Engine.js index 8eeb92c..4ec27b2 100755 --- a/app/Game/Core/Physics/Engine.js +++ b/app/Game/Core/Physics/Engine.js @@ -5,7 +5,9 @@ define([ "Lib/Utilities/NotificationCenter" ], -function (Settings, Box2D, CollisionDetector, Nc) { +function (Settings, Box2D, CollisionDetector, nc) { + + "use strict"; function Engine () { this.world = new Box2D.Dynamics.b2World( @@ -13,33 +15,30 @@ function (Settings, Box2D, CollisionDetector, Nc) { Settings.BOX2D_ALLOW_SLEEP ); this.world.SetWarmStarting(true); - this.ground = null; this.lastStep = Date.now(); this.worldQueue = []; this.ncTokens = [ - Nc.on(Nc.ns.channel.engine.worldQueue.add, this.addToWorldQueue, this) + nc.on(nc.ns.channel.engine.worldQueue.add, this.addToWorldQueue, this) ]; } - Engine.prototype.getWorld = function () { - return this.world; - } - - Engine.prototype.getGround = function () { - return this.ground; - } - Engine.prototype.setCollisionDetector = function () { var detector = new CollisionDetector(); this.world.SetContactListener(detector.getListener()); } + Engine.prototype.getWorldForRubeLoader = function() { + return this.world; + }; + Engine.prototype.createBody = function (bodyDef) { - var body = this.world.CreateBody(bodyDef); - if(!this.ground) this.ground = body; - return body; + return this.world.CreateBody(bodyDef); + } + + Engine.prototype.destroyBody = function (body) { + return this.world.DestroyBody(body); } Engine.prototype.addToWorldQueue = function(callback) { @@ -63,8 +62,8 @@ function (Settings, Box2D, CollisionDetector, Nc) { } Engine.prototype.destroy = function() { + nc.offAll(this.ncTokens); delete this.world; - Nc.offAll(this.ncTokens); }; diff --git a/app/Game/Core/Player.js b/app/Game/Core/Player.js index 9543c02..996facb 100755 --- a/app/Game/Core/Player.js +++ b/app/Game/Core/Player.js @@ -1,16 +1,19 @@ define([ "Game/" + GLOBALS.context + "/GameObjects/Doll", + "Game/" + GLOBALS.context + "/Control/PlayerController", "Game/Config/Settings", "Lib/Utilities/NotificationCenter", "Lib/Utilities/Exception", + "Lib/Utilities/ColorConverter", "Game/" + GLOBALS.context + "/GameObjects/SpectatorDoll", - "Game/" + GLOBALS.context + "/GameObjects/Items/RagDoll" + "Game/" + GLOBALS.context + "/GameObjects/Items/RubeDoll" ], +function (Doll, PlayerController, Settings, nc, Exception, ColorConverter, SpectatorDoll, RubeDoll) { -function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { + "use strict"; - function Player (id, physicsEngine, user) { + function Player (id, physicsEngine, user, revealedGameController) { this.stats = { health: 100, deaths: 0, @@ -19,18 +22,23 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { this.user = user; this.physicsEngine = physicsEngine; - this.playerController = null; + this.playerController = null; // pre-initialise with null, because client/players don't get one this.doll; this.id = id; - this.isSpawned = false; + this.spawned = false; this.holdingItem = null; + this.inBetweenRounds = true; this.spectatorDoll = new SpectatorDoll(this.physicsEngine, "spectatorDoll-" + this.id, this); - - Nc.trigger(Nc.ns.core.game.gameObject.add, 'animated', this); + this.revealedGameController = revealedGameController; + this.modifierActivated = false; } + Player.prototype.getNickname = function() { + return this.user.options.nickname; + }; + Player.prototype.getActiveDoll = function() { - if(this.isSpawned) { + if(this.spawned) { return this.doll; } else if (this.ragDoll) { return this.ragDoll; @@ -41,9 +49,13 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { Player.prototype.spawn = function (x, y) { this.doll = new Doll(this.physicsEngine, "doll-" + this.id, this); this.doll.spawn(x, y); - this.isSpawned = true; + this.spawned = true; } + Player.prototype.isSpawned = function() { + return this.spawned; + }; + Player.prototype.getPosition = function () { return this.getActiveDoll().getPosition(); } @@ -54,54 +66,73 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { Player.prototype.move = function (direction) { - if(!this.isSpawned) return false; - this.doll.move(direction); + if(!this.spawned) return false; + this.doll.move(direction, this.modifierActivated); } Player.prototype.stop = function () { - if(!this.isSpawned) return false; + if(!this.spawned) return false; this.doll.stop(); } Player.prototype.jump = function () { - if(!this.isSpawned) return false; + if(!this.spawned) return false; this.doll.jump(); } Player.prototype.jumpStop = function () { - if(!this.isSpawned) return false; + if(!this.spawned) return false; this.doll.jumpStop(); } Player.prototype.lookAt = function (x, y) { - if(!this.isSpawned) return false; + if(!this.spawned) return false; // FIXME implement spectator movement here this.doll.lookAt(x, y); } + Player.prototype.activateModifier = function () { + if(!this.spawned) return false; + this.modifierActivated = true; + } + + Player.prototype.deactivateModifier = function () { + if(!this.spawned) return false; + this.modifierActivated = false; + } + Player.prototype.grab = function(item) { - if(!this.isSpawned) return false; + if(!this.spawned) return false; this.doll.grab(item); item.beingGrabbed(this); this.holdingItem = item; }; - Player.prototype.throw = function(x, y, item) { - if(!this.isSpawned) return false; - this.doll.throw(item, x, y); + Player.prototype.throw = function(options, item) { + if(!this.spawned) return false; + this.doll.throw(item, options); item.beingReleased(this); this.holdingItem = null; }; Player.prototype.kill = function(killedByPlayer, ragDollId) { - if(!this.isSpawned) return false; + if(!this.spawned) return false; // FIXME: do something better then just respawn in GameController if(this.holdingItem) { - this.throw(0, 0, this.holdingItem) + var options = { + x: 0, + y: 0, + av: 0 + }; + this.throw(options, this.holdingItem) } // prepare for creating the ragdoll + + var converter = new ColorConverter(); + var primaryColor = converter.getColorByName(this.getNickname()); + var options = { x: this.getPosition().x * Settings.RATIO, y: this.getPosition().y * Settings.RATIO, @@ -110,21 +141,23 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { image: "chest.png", name: "RagDoll", rotation: 0, - type: "ragdoll", + type: "rubedoll", weight: 3, width: 5, - height: 12 + height: 12, + primaryColor: primaryColor, + direction: this.doll.lookDirection }; - var ragDoll = new RagDoll(this.physicsEngine, "ragDoll-" + this.id + "-" + ragDollId, options); - ragDoll.setVelocities(this.doll.getVelocities()); + var rubeDoll = new RubeDoll(this.physicsEngine, "rubeDoll-" + this.id + "-" + ragDollId, options); + rubeDoll.setVelocities(this.doll.getVelocities()); - this.isSpawned = false; + this.spawned = false; this.doll.destroy(); this.doll = null; - this.ragDoll = ragDoll; + this.ragDoll = rubeDoll; }; Player.prototype.update = function () { @@ -140,16 +173,23 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { Player.prototype.destroy = function () { - Nc.trigger(Nc.ns.core.game.gameObject.remove, 'animated', this); + // FIXME add destroy nc hook if(this.holdingItem) { - this.throw(0, 0, this.holdingItem); + var options = { + x: 0, + y: 0, + av: 0 + }; + this.throw(options, this.holdingItem); } this.spectatorDoll.destroy(); + // doll destoys itself at the end cause its a gameobject + // but on userLeft, the player has to destroy it. if(this.doll) { - this.doll.destroy(); + this.doll.destroy(); } if(this.playerController) { @@ -157,9 +197,13 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { } } - Player.prototype.setPlayerController = function(playerController) { - this.playerController = playerController; - } + Player.prototype.setInBetweenRounds = function(inBetweenRounds) { + this.inBetweenRounds = inBetweenRounds; + }; + + Player.prototype.isInBetweenRounds = function() { + return this.inBetweenRounds; + }; return Player; }); diff --git a/app/Game/Core/User.js b/app/Game/Core/User.js index de3264f..e2eda24 100755 --- a/app/Game/Core/User.js +++ b/app/Game/Core/User.js @@ -1,4 +1,7 @@ -define(function () { +define([ +], + +function () { function User (id, options) { this.id = id; diff --git a/app/Lib/Utilities/Abstract.js b/app/Lib/Utilities/Abstract.js new file mode 100644 index 0000000..c68e8ae --- /dev/null +++ b/app/Lib/Utilities/Abstract.js @@ -0,0 +1,20 @@ +define([ + "Lib/Utilities/Exception" +], + +function (Exception) { + + "use strict"; + + function Abstract() { + } + + Abstract.prototype.addMethod = function(methodName, params) { + this.prototype[methodName] = function() { + throw new Exception("Abstract method", this, methodName + "(" + params.join(', ') + ") not overwritten."); + } + } + + return Abstract; + +}); \ No newline at end of file diff --git a/app/Lib/Utilities/Assert.js b/app/Lib/Utilities/Assert.js new file mode 100644 index 0000000..0a89ed7 --- /dev/null +++ b/app/Lib/Utilities/Assert.js @@ -0,0 +1,21 @@ +define([ + "Lib/Utilities/Exception" +], + +function (Exception) { + + "use strict"; + + var Assert = {}; + + Assert.number = function() { + for (var i = 0; i < arguments.length; i++) { + if(isNaN(parseFloat(arguments[i]))) { + throw new Exception("Assert: not a number ", JSON.stringify(arguments)); + } + } + }; + + return Assert; + +}); \ No newline at end of file diff --git a/app/Lib/Utilities/Channel/Extensions.js b/app/Lib/Utilities/Channel/Extensions.js new file mode 100644 index 0000000..269e880 --- /dev/null +++ b/app/Lib/Utilities/Channel/Extensions.js @@ -0,0 +1,22 @@ +define([ + "Lib/Utilities/Core/Extensions" +], + +function (Parent) { + + //"use strict"; + + console.checkpoint = function (s) { + console.log(" \033[32mbeep - \033[0m" + s); + } + + console.warn = function (s) { + console.log(" \033[33mwarn - \033[0m" + s); + } + + console.error = function (s) { + console.log(" \033[31merror - \033[0m" + s); + } + + +}); \ No newline at end of file diff --git a/app/Lib/Utilities/Client/Extensions.js b/app/Lib/Utilities/Client/Extensions.js new file mode 100644 index 0000000..07e65ab --- /dev/null +++ b/app/Lib/Utilities/Client/Extensions.js @@ -0,0 +1,8 @@ +define([ + "Lib/Utilities/Core/Extensions" +], + +function (Parent) { + + "use strict"; +}); \ No newline at end of file diff --git a/app/Lib/Utilities/ColorConverter.js b/app/Lib/Utilities/ColorConverter.js new file mode 100644 index 0000000..6332a74 --- /dev/null +++ b/app/Lib/Utilities/ColorConverter.js @@ -0,0 +1,83 @@ +define([ + "Lib/Vendor/CryptoJS" +], + +function (CryptoJS) { + + "use strict"; + + function ColorConverter() { + this.sin = 0; + var palette = [ + 0x634c72, + 0x724c5e, + 0x787950, + 0x507971, + 0x506a79, + 0x8c423c, + 0x557e4a, + 0x436785, + 0xa62423, + 0x427f87, + 0x472e1a, + 0x4d667c, + 0x2a3c49, + 0x7c7e2b, + 0x3b3c21, + 0x263c27, + 0x7e897e, + 0xb55014, + 0x978c32, + 0x739137, + 0x46824f, + 0x19b0b4, + 0x1c1eb1, + 0xccb206, + 0x433e20, + 0x201a13, + 0x145396, + 0x313d08, + 0xb7a345, + 0xdc168a, + 0x310505, + 0x051631, + ]; + /* + var element, color; + + var start = 5; + var step = 4; + var max = 16; + + for(var r=start; r>>2]|=(c[b>>>2]>>>24-8*(b%4)&255)<<24-8*((d+b)%4);else if(65535>>2]=c[b>>>2];else e.push.apply(e,c);this.sigBytes+=a;return this},clamp:function(){var a=this.words,e=this.sigBytes;a[e>>>2]&=4294967295<<32-8*(e%4);a.length=o.ceil(e/4)},clone:function(){var a= +n.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var e=[],c=0;c>>2]>>>24-8*(d%4)&255;c.push((b>>>4).toString(16));c.push((b&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d>>3]|=parseInt(a.substr(d,2),16)<<24-4*(d%8);return j.create(c,b/2)}},p=k.Latin1={stringify:function(a){for(var b= +a.words,a=a.sigBytes,c=[],d=0;d>>2]>>>24-8*(d%4)&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<24-8*(d%4);return j.create(c,b)}},h=k.Utf8={stringify:function(a){try{return decodeURIComponent(escape(p.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return p.parse(unescape(encodeURIComponent(a)))}},b=m.BufferedBlockAlgorithm=n.extend({reset:function(){this._data=j.create(); +this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,c=b.words,d=b.sigBytes,f=this.blockSize,i=d/(4*f),i=a?o.ceil(i):o.max((i|0)-this._minBufferSize,0),a=i*f,d=o.min(4*a,d);if(a){for(var h=0;h>>32-d)+f}function l(b,f,a,e,c,d,g){b=b+(f&e|a&~e)+c+g;return(b<>>32-d)+f}function m(b,f,a,e,c,d,g){b=b+(f^a^e)+c+g;return(b<>>32-d)+f}function n(b,f,a,e,c,d,g){b=b+(a^(f|~e))+c+g;return(b<>>32-d)+f}var j=CryptoJS,k=j.lib,r=k.WordArray,k=k.Hasher,p=j.algo,h=[];(function(){for(var b=0;64>b;b++)h[b]=4294967296*o.abs(o.sin(b+1))|0})();p=p.MD5=k.extend({_doReset:function(){this._hash=r.create([1732584193,4023233417, +2562383102,271733878])},_doProcessBlock:function(b,f){for(var a=0;16>a;a++){var e=f+a,c=b[e];b[e]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360}for(var e=this._hash.words,c=e[0],d=e[1],g=e[2],i=e[3],a=0;64>a;a+=4)16>a?(c=q(c,d,g,i,b[f+a],7,h[a]),i=q(i,c,d,g,b[f+a+1],12,h[a+1]),g=q(g,i,c,d,b[f+a+2],17,h[a+2]),d=q(d,g,i,c,b[f+a+3],22,h[a+3])):32>a?(c=l(c,d,g,i,b[f+(a+1)%16],5,h[a]),i=l(i,c,d,g,b[f+(a+6)%16],9,h[a+1]),g=l(g,i,c,d,b[f+(a+11)%16],14,h[a+2]),d=l(d,g,i,c,b[f+a%16],20,h[a+3])):48>a?(c= +m(c,d,g,i,b[f+(3*a+5)%16],4,h[a]),i=m(i,c,d,g,b[f+(3*a+8)%16],11,h[a+1]),g=m(g,i,c,d,b[f+(3*a+11)%16],16,h[a+2]),d=m(d,g,i,c,b[f+(3*a+14)%16],23,h[a+3])):(c=n(c,d,g,i,b[f+3*a%16],6,h[a]),i=n(i,c,d,g,b[f+(3*a+7)%16],10,h[a+1]),g=n(g,i,c,d,b[f+(3*a+14)%16],15,h[a+2]),d=n(d,g,i,c,b[f+(3*a+5)%16],21,h[a+3]));e[0]=e[0]+c|0;e[1]=e[1]+d|0;e[2]=e[2]+g|0;e[3]=e[3]+i|0},_doFinalize:function(){var b=this._data,f=b.words,a=8*this._nDataBytes,e=8*b.sigBytes;f[e>>>5]|=128<<24-e%32;f[(e+64>>>9<<4)+14]=(a<<8|a>>> +24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(f.length+1);this._process();b=this._hash.words;for(f=0;4>f;f++)a=b[f],b[f]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360}});j.MD5=k._createHelper(p);j.HmacMD5=k._createHmacHelper(p)})(Math); + + return CryptoJS; + +}); \ No newline at end of file diff --git a/app/Lib/Vendor/Pixi/pixi.dev.1.5.1.js b/app/Lib/Vendor/Pixi/pixi.dev.1.5.1.js new file mode 100644 index 0000000..c7dd126 --- /dev/null +++ b/app/Lib/Vendor/Pixi/pixi.dev.1.5.1.js @@ -0,0 +1,14221 @@ +/** + * @license + * pixi.js - v1.5.1 + * Copyright (c) 2012-2014, Mat Groves + * http://goodboydigital.com/ + * + * Compiled: 2014-02-13 + * + * pixi.js is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license.php + */ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +(function(){ + + var root = this; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * @module PIXI + */ +var PIXI = PIXI || {}; + +/* +* +* This file contains a lot of pixi consts which are used across the rendering engine +* @class Consts +*/ +PIXI.WEBGL_RENDERER = 0; +PIXI.CANVAS_RENDERER = 1; + +// useful for testing against if your lib is using pixi. +PIXI.VERSION = "v1.5.1"; + +// the various blend modes supported by pixi +PIXI.blendModes = { + NORMAL:0, + ADD:1, + MULTIPLY:2, + SCREEN:3, + OVERLAY:4, + DARKEN:5, + LIGHTEN:6, + COLOR_DODGE:7, + COLOR_BURN:8, + HARD_LIGHT:9, + SOFT_LIGHT:10, + DIFFERENCE:11, + EXCLUSION:12, + HUE:13, + SATURATION:14, + COLOR:15, + LUMINOSITY:16 +}; + +// the scale modes +PIXI.scaleModes = { + DEFAULT:0, + LINEAR:0, + NEAREST:1 +}; + +// interaction frequency +PIXI.INTERACTION_FREQUENCY = 30; +PIXI.AUTO_PREVENT_DEFAULT = true; + +PIXI.RAD_TO_DEG = 180 / Math.PI; +PIXI.DEG_TO_RAD = Math.PI / 180; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. + * + * @class Point + * @constructor + * @param x {Number} position of the point on the x axis + * @param y {Number} position of the point on the y axis + */ +PIXI.Point = function(x, y) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; +}; + +/** + * Creates a clone of this point + * + * @method clone + * @return {Point} a copy of the point + */ +PIXI.Point.prototype.clone = function() +{ + return new PIXI.Point(this.x, this.y); +}; + +// constructor +PIXI.Point.prototype.constructor = PIXI.Point; + +PIXI.Point.prototype.set = function(x, y) +{ + this.x = x || 0; + this.y = y || ( (y !== 0) ? this.x : 0 ) ; +}; + + +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height. + * + * @class Rectangle + * @constructor + * @param x {Number} The X coord of the upper-left corner of the rectangle + * @param y {Number} The Y coord of the upper-left corner of the rectangle + * @param width {Number} The overall width of this rectangle + * @param height {Number} The overall height of this rectangle + */ +PIXI.Rectangle = function(x, y, width, height) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; +}; + +/** + * Creates a clone of this Rectangle + * + * @method clone + * @return {Rectangle} a copy of the rectangle + */ +PIXI.Rectangle.prototype.clone = function() +{ + return new PIXI.Rectangle(this.x, this.y, this.width, this.height); +}; + +/** + * Checks whether the x and y coordinates passed to this function are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coords are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + var x1 = this.x; + if(x >= x1 && x <= x1 + this.width) + { + var y1 = this.y; + + if(y >= y1 && y <= y1 + this.height) + { + return true; + } + } + + return false; +}; + +// constructor +PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; + +PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); +/** + * @author Adrien Brault + */ + +/** + * @class Polygon + * @constructor + * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be + * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the + * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are + * Numbers. + */ +PIXI.Polygon = function(points) +{ + //if points isn't an array, use arguments as the array + if(!(points instanceof Array)) + points = Array.prototype.slice.call(arguments); + + //if this is a flat array of numbers, convert it to points + if(typeof points[0] === 'number') { + var p = []; + for(var i = 0, il = points.length; i < il; i+=2) { + p.push( + new PIXI.Point(points[i], points[i + 1]) + ); + } + + points = p; + } + + this.points = points; +}; + +/** + * Creates a clone of this polygon + * + * @method clone + * @return {Polygon} a copy of the polygon + */ +PIXI.Polygon.prototype.clone = function() +{ + var points = []; + for (var i=0; i y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + + if(intersect) inside = !inside; + } + + return inside; +}; + +// constructor +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + +/** + * @author Chad Engler + */ + +/** + * The Circle object can be used to specify a hit area for displayObjects + * + * @class Circle + * @constructor + * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this circle + * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this circle + * @param radius {Number} The radius of the circle + */ +PIXI.Circle = function(x, y, radius) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +}; + +/** + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon + */ +PIXI.Circle.prototype.clone = function() +{ + return new PIXI.Circle(this.x, this.y, this.radius); +}; + +/** + * Checks whether the x, and y coordinates passed to this function are contained within this circle + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this polygon + */ +PIXI.Circle.prototype.contains = function(x, y) +{ + if(this.radius <= 0) + return false; + + var dx = (this.x - x), + dy = (this.y - y), + r2 = this.radius * this.radius; + + dx *= dx; + dy *= dy; + + return (dx + dy <= r2); +}; + +// constructor +PIXI.Circle.prototype.constructor = PIXI.Circle; + + +/** + * @author Chad Engler + */ + +/** + * The Ellipse object can be used to specify a hit area for displayObjects + * + * @class Ellipse + * @constructor + * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this ellipse + * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse + */ +PIXI.Ellipse = function(x, y, width, height) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; +}; + +/** + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse + */ +PIXI.Ellipse.prototype.clone = function() +{ + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); +}; + +/** + * Checks whether the x and y coordinates passed to this function are contained within this ellipse + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coords are within this ellipse + */ +PIXI.Ellipse.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + //normalize the coords to an ellipse with center 0,0 + var normx = ((x - this.x) / this.width), + normy = ((y - this.y) / this.height); + + normx *= normx; + normy *= normy; + + return (normx + normy <= 1); +}; + +/** +* Returns the framing rectangle of the ellipse as a PIXI.Rectangle object +* +* @method getBounds +* @return {Rectangle} the framing rectangle +*/ +PIXI.Ellipse.prototype.getBounds = function() +{ + return new PIXI.Rectangle(this.x, this.y, this.width, this.height); +}; + +// constructor +PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.determineMatrixArrayType = function() { + return (typeof Float32Array !== 'undefined') ? Float32Array : Array; +}; + +/* +* @class Matrix2 +* The Matrix2 class will choose the best type of array to use between +* a regular javascript Array and a Float32Array if the latter is available +* +*/ +PIXI.Matrix2 = PIXI.determineMatrixArrayType(); + +/* +* @class Matrix +* The Matrix class is now an object, which makes it a lot faster, +* here is a representation of it : +* | a | b | tx| +* | c | c | ty| +* | 0 | 0 | 1 | +* +*/ +PIXI.Matrix = function() +{ + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.tx = 0; + this.ty = 0; +}; + +/** + * Creates a pixi matrix object based on the array given as a parameter + * + * @method fromArray + * @param array {Array} The array that the matrix will be filled with + */ +PIXI.Matrix.prototype.fromArray = function(array) +{ + this.a = array[0]; + this.b = array[1]; + this.c = array[3]; + this.d = array[4]; + this.tx = array[2]; + this.ty = array[5]; +}; + +/** + * Creates an array from the current Matrix object + * + * @method toArray + * @param transpose {Boolean} Whether we need to transpose the matrix or not + * @return array {Array} the newly created array which contains the matrix + */ +PIXI.Matrix.prototype.toArray = function(transpose) +{ + if(!this.array) this.array = new Float32Array(9); + var array = this.array; + + if(transpose) + { + this.array[0] = this.a; + this.array[1] = this.c; + this.array[2] = 0; + this.array[3] = this.b; + this.array[4] = this.d; + this.array[5] = 0; + this.array[6] = this.tx; + this.array[7] = this.ty; + this.array[8] = 1; + } + else + { + this.array[0] = this.a; + this.array[1] = this.b; + this.array[2] = this.tx; + this.array[3] = this.c; + this.array[4] = this.d; + this.array[5] = this.ty; + this.array[6] = 0; + this.array[7] = 0; + this.array[8] = 1; + } + + return array;//[this.a, this.b, this.tx, this.c, this.d, this.ty, 0, 0, 1]; +}; + +PIXI.identityMatrix = new PIXI.Matrix(); +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The base class for all objects that are rendered on the screen. + * + * @class DisplayObject + * @constructor + */ +PIXI.DisplayObject = function() +{ + /** + * The coordinate of the object relative to the local coordinates of the parent. + * + * @property position + * @type Point + */ + this.position = new PIXI.Point(); + + /** + * The scale factor of the object. + * + * @property scale + * @type Point + */ + this.scale = new PIXI.Point(1,1);//{x:1, y:1}; + + /** + * The pivot point of the displayObject that it rotates around + * + * @property pivot + * @type Point + */ + this.pivot = new PIXI.Point(0,0); + + /** + * The rotation of the object in radians. + * + * @property rotation + * @type Number + */ + this.rotation = 0; + + /** + * The opacity of the object. + * + * @property alpha + * @type Number + */ + this.alpha = 1; + + /** + * The visibility of the object. + * + * @property visible + * @type Boolean + */ + this.visible = true; + + /** + * This is the defined area that will pick up mouse / touch events. It is null by default. + * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children) + * + * @property hitArea + * @type Rectangle|Circle|Ellipse|Polygon + */ + this.hitArea = null; + + /** + * This is used to indicate if the displayObject should display a mouse hand cursor on rollover + * + * @property buttonMode + * @type Boolean + */ + this.buttonMode = false; + + /** + * Can this object be rendered + * + * @property renderable + * @type Boolean + */ + this.renderable = false; + + /** + * [read-only] The display object container that contains this display object. + * + * @property parent + * @type DisplayObjectContainer + * @readOnly + */ + this.parent = null; + + /** + * [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage. + * + * @property stage + * @type Stage + * @readOnly + */ + this.stage = null; + + /** + * [read-only] The multiplied alpha of the displayObject + * + * @property worldAlpha + * @type Number + * @readOnly + */ + this.worldAlpha = 1; + + /** + * [read-only] Whether or not the object is interactive, do not toggle directly! use the `interactive` property + * + * @property _interactive + * @type Boolean + * @readOnly + * @private + */ + this._interactive = false; + + /** + * This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true + * + * @property defaultCursor + * @type String + * + */ + this.defaultCursor = 'pointer'; + + /** + * [read-only] Current transform of the object based on world (parent) factors + * + * @property worldTransform + * @type Mat3 + * @readOnly + * @private + */ + this.worldTransform = new PIXI.Matrix(); + + /** + * [NYI] Unknown + * + * @property color + * @type Array<> + * @private + */ + this.color = []; + + /** + * [NYI] Holds whether or not this object is dynamic, for rendering optimization + * + * @property dynamic + * @type Boolean + * @private + */ + this.dynamic = true; + + // cached sin rotation and cos rotation + this._sr = 0; + this._cr = 1; + + /** + * The area the filter is applied to + * + * @property filterArea + * @type Rectangle + */ + this.filterArea = new PIXI.Rectangle(0,0,1,1); + + /** + * The original, cached bounds of the object + * + * @property _bounds + * @type Rectangle + * @private + */ + this._bounds = new PIXI.Rectangle(0, 0, 1, 1); + /** + * The most up-to-date bounds of the object + * + * @property _currentBounds + * @type Rectangle + * @private + */ + this._currentBounds = null; + /** + * The original, cached mask of the object + * + * @property _currentBounds + * @type Rectangle + * @private + */ + this._mask = null; + + /* + * MOUSE Callbacks + */ + + /** + * A callback that is used when the users clicks on the displayObject with their mouse + * @method click + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user clicks the mouse down over the sprite + * @method mousedown + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse that was over the displayObject + * for this callback to be fired the mouse must have been pressed down over the displayObject + * @method mouseup + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse that was over the displayObject but is no longer over the displayObject + * for this callback to be fired, The touch must have started over the displayObject + * @method mouseupoutside + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the users mouse rolls over the displayObject + * @method mouseover + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the users mouse leaves the displayObject + * @method mouseout + * @param interactionData {InteractionData} + */ + + + /* + * TOUCH Callbacks + */ + + /** + * A callback that is used when the users taps on the sprite with their finger + * basically a touch version of click + * @method tap + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user touches over the displayObject + * @method touchstart + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases a touch over the displayObject + * @method touchend + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the touch that was over the displayObject + * for this callback to be fired, The touch must have started over the sprite + * @method touchendoutside + * @param interactionData {InteractionData} + */ +}; + +// constructor +PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; + +/** + * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default + * Instead of using this function you can now simply set the interactive property to true or false + * + * @method setInteractive + * @param interactive {Boolean} + * @deprecated Simply set the `interactive` property directly + */ +PIXI.DisplayObject.prototype.setInteractive = function(interactive) +{ + this.interactive = interactive; +}; + +/** + * Indicates if the sprite will have touch and mouse interactivity. It is false by default + * + * @property interactive + * @type Boolean + * @default false + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { + get: function() { + return this._interactive; + }, + set: function(value) { + this._interactive = value; + + // TODO more to be done here.. + // need to sort out a re-crawl! + if(this.stage)this.stage.dirty = true; + } +}); + +/** + * [read-only] Indicates if the sprite is globaly visible. + * + * @property worldVisible + * @type Boolean + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', { + get: function() { + var item = this; + + do + { + if(!item.visible)return false; + item = item.parent; + } + while(item); + + return true; + } +}); + +/** + * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it. + * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping. + * To remove a mask, set this property to null. + * + * @property mask + * @type Graphics + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { + get: function() { + return this._mask; + }, + set: function(value) { + + if(this._mask)this._mask.isMask = false; + this._mask = value; + if(this._mask)this._mask.isMask = true; + } +}); + +/** + * Sets the filters for the displayObject. + * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer. + * To remove filters simply set this property to 'null' + * @property filters + * @type Array An array of filters + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + if(value) + { + // now put all the passes in one place.. + var passes = []; + for (var i = 0; i < value.length; i++) + { + var filterPasses = value[i].passes; + for (var j = 0; j < filterPasses.length; j++) + { + passes.push(filterPasses[j]); + } + } + + // TODO change this as it is legacy + this._filterBlock = {target:this, filterPasses:passes}; + } + + this._filters = value; + } +}); + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.DisplayObject.prototype.updateTransform = function() +{ + // TODO OPTIMIZE THIS!! with dirty + if(this.rotation !== this.rotationCache) + { + + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); + } + + // var localTransform = this.localTransform//.toArray(); + var parentTransform = this.parent.worldTransform;//.toArray(); + var worldTransform = this.worldTransform;//.toArray(); + var px = this.pivot.x; + var py = this.pivot.y; + + var a00 = this._cr * this.scale.x, + a01 = -this._sr * this.scale.y, + a10 = this._sr * this.scale.x, + a11 = this._cr * this.scale.y, + a02 = this.position.x - a00 * px - py * a01, + a12 = this.position.y - a11 * py - px * a10, + b00 = parentTransform.a, b01 = parentTransform.b, + b10 = parentTransform.c, b11 = parentTransform.d; + + worldTransform.a = b00 * a00 + b01 * a10; + worldTransform.b = b00 * a01 + b01 * a11; + worldTransform.tx = b00 * a02 + b01 * a12 + parentTransform.tx; + + worldTransform.c = b10 * a00 + b11 * a10; + worldTransform.d = b10 * a01 + b11 * a11; + worldTransform.ty = b10 * a02 + b11 * a12 + parentTransform.ty; + + this.worldAlpha = this.alpha * this.parent.worldAlpha; +}; + +/** + * Retrieves the bounds of the displayObject as a rectangle object + * + * @method getBounds + * @return {Rectangle} the rectangular bounding area + */ +PIXI.DisplayObject.prototype.getBounds = function( matrix ) +{ + matrix = matrix;//just to get passed js hinting (and preserve inheritance) + return PIXI.EmptyRectangle; +}; + +/** + * Retrieves the local bounds of the displayObject as a rectangle object + * + * @method getLocalBounds + * @return {Rectangle} the rectangular bounding area + */ +PIXI.DisplayObject.prototype.getLocalBounds = function() +{ + //var matrixCache = this.worldTransform; + + return this.getBounds(PIXI.identityMatrix);///PIXI.EmptyRectangle(); +}; + +/** + * Sets the object's stage reference, the stage this object is connected to + * + * @method setStageReference + * @param stage {Stage} the stage that the object will have as its current stage reference + */ +PIXI.DisplayObject.prototype.setStageReference = function(stage) +{ + this.stage = stage; + if(this._interactive)this.stage.dirty = true; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + +/** +* Renders the object using the Canvas renderer +* +* @method _renderCanvas +* @param renderSession {RenderSession} +* @private +*/ +PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + +/** + * The position of the displayObject on the x axis relative to the local coordinates of the parent. + * + * @property x + * @type Number + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'x', { + get: function() { + return this.position.x; + }, + set: function(value) { + this.position.x = value; + } +}); + +/** + * The position of the displayObject on the y axis relative to the local coordinates of the parent. + * + * @property y + * @type Number + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'y', { + get: function() { + return this.position.y; + }, + set: function(value) { + this.position.y = value; + } +}); + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * A DisplayObjectContainer represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class DisplayObjectContainer + * @extends DisplayObject + * @constructor + */ +PIXI.DisplayObjectContainer = function() +{ + PIXI.DisplayObject.call( this ); + + /** + * [read-only] The array of children of this container. + * + * @property children + * @type Array + * @readOnly + */ + this.children = []; +}; + +// constructor +PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); +PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; + +/** + * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ + + /* +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { + get: function() { + return this.scale.x * this.getLocalBounds().width; + }, + set: function(value) { + this.scale.x = value / (this.getLocalBounds().width/this.scale.x); + this._width = value; + } +}); +*/ + +/** + * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ + +/* +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { + get: function() { + return this.scale.y * this.getLocalBounds().height; + }, + set: function(value) { + this.scale.y = value / (this.getLocalBounds().height/this.scale.y); + this._height = value; + } +}); +*/ + +/** + * Adds a child to the container. + * + * @method addChild + * @param child {DisplayObject} The DisplayObject to add to the container + */ +PIXI.DisplayObjectContainer.prototype.addChild = function(child) +{ + this.addChildAt(child, this.children.length); +}; + +/** + * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown + * + * @method addChildAt + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + */ +PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) +{ + if(index >= 0 && index <= this.children.length) + { + if(child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + if(this.stage)child.setStageReference(this.stage); + } + else + { + throw new Error(child + ' The index '+ index +' supplied is out of bounds ' + this.children.length); + } +}; + +/** + * [NYI] Swaps the depth of 2 displayObjects + * + * @method swapChildren + * @param child {DisplayObject} + * @param child2 {DisplayObject} + * @private + */ +PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) +{ + if(child === child2) { + return; + } + + var index1 = this.children.indexOf(child); + var index2 = this.children.indexOf(child2); + + if(index1 < 0 || index2 < 0) { + throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); + } + + this.children[index1] = child2; + this.children[index2] = child; + +}; + +/** + * Returns the child at the specified index + * + * @method getChildAt + * @param index {Number} The index to get the child from + */ +PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) +{ + if(index >= 0 && index < this.children.length) + { + return this.children[index]; + } + else + { + throw new Error('The supplied DisplayObjects must be a child of the caller ' + this); + } +}; + +/** + * Removes a child from the container. + * + * @method removeChild + * @param child {DisplayObject} The DisplayObject to remove + */ +PIXI.DisplayObjectContainer.prototype.removeChild = function(child) +{ + var index = this.children.indexOf( child ); + if ( index !== -1 ) + { + // update the stage reference.. + if(this.stage)child.removeStageReference(); + + child.parent = undefined; + this.children.splice( index, 1 ); + } + else + { + throw new Error(child + ' The supplied DisplayObject must be a child of the caller ' + this); + } +}; + + +/** +* Removes all the children +* +* @method removeAll +* NOT tested yet +*/ +/* PIXI.DisplayObjectContainer.prototype.removeAll = function() +{ + + + for(var i = 0 , j = this.children.length; i < j; i++) + { + this.removeChild(this.children[i]); + } + +}; +*/ +/* + * Updates the container's childrens transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.DisplayObjectContainer.prototype.updateTransform = function() +{ + //this._currentBounds = null; + + if(!this.visible)return; + + PIXI.DisplayObject.prototype.updateTransform.call( this ); + + for(var i=0,j=this.children.length; i childMaxX ? maxX : childMaxX; + maxY = maxY > childMaxY ? maxY : childMaxY; + } + + if(!childVisible) + return PIXI.EmptyRectangle; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.y = minY; + bounds.width = maxX - minX; + bounds.height = maxY - minY; + + // TODO: store a reference so that if this function gets called again in the render cycle we do not have to recalculate + //this._currentBounds = bounds; + + return bounds; +}; + +PIXI.DisplayObjectContainer.prototype.getLocalBounds = function() +{ + var matrixCache = this.worldTransform; + + this.worldTransform = PIXI.identityMatrix; + + for(var i=0,j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Sprite.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(!this.visible || this.alpha <= 0)return; + + var i,j; + + // do a quick check to see if this element has a mask or a filter. + if(this._mask || this._filters) + { + var spriteBatch = renderSession.spriteBatch; + + if(this._mask) + { + spriteBatch.stop(); + renderSession.maskManager.pushMask(this.mask, renderSession); + spriteBatch.start(); + } + + if(this._filters) + { + spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + + // add this sprite to the batch + spriteBatch.render(this); + + // now loop through the children and make sure they get rendered + for(i=0,j=this.children.length; i} an array of {Texture} objects that make up the animation + */ +PIXI.MovieClip = function(textures) +{ + PIXI.Sprite.call(this, textures[0]); + + /** + * The array of textures that make up the animation + * + * @property textures + * @type Array + */ + this.textures = textures; + + /** + * The speed that the MovieClip will play at. Higher is faster, lower is slower + * + * @property animationSpeed + * @type Number + * @default 1 + */ + this.animationSpeed = 1; + + /** + * Whether or not the movie clip repeats after playing. + * + * @property loop + * @type Boolean + * @default true + */ + this.loop = true; + + /** + * Function to call when a MovieClip finishes playing + * + * @property onComplete + * @type Function + */ + this.onComplete = null; + + /** + * [read-only] The MovieClips current frame index (this may not have to be a whole number) + * + * @property currentFrame + * @type Number + * @default 0 + * @readOnly + */ + this.currentFrame = 0; + + /** + * [read-only] Indicates if the MovieClip is currently playing + * + * @property playing + * @type Boolean + * @readOnly + */ + this.playing = false; +}; + +// constructor +PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); +PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; + +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + +/** + * Stops the MovieClip + * + * @method stop + */ +PIXI.MovieClip.prototype.stop = function() +{ + this.playing = false; +}; + +/** + * Plays the MovieClip + * + * @method play + */ +PIXI.MovieClip.prototype.play = function() +{ + this.playing = true; +}; + +/** + * Stops the MovieClip and goes to a specific frame + * + * @method gotoAndStop + * @param frameNumber {Number} frame index to stop at + */ +PIXI.MovieClip.prototype.gotoAndStop = function(frameNumber) +{ + this.playing = false; + this.currentFrame = frameNumber; + var round = (this.currentFrame + 0.5) | 0; + this.setTexture(this.textures[round % this.textures.length]); +}; + +/** + * Goes to a specific frame and begins playing the MovieClip + * + * @method gotoAndPlay + * @param frameNumber {Number} frame index to start at + */ +PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) +{ + this.currentFrame = frameNumber; + this.playing = true; +}; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.MovieClip.prototype.updateTransform = function() +{ + PIXI.Sprite.prototype.updateTransform.call(this); + + if(!this.playing)return; + + this.currentFrame += this.animationSpeed; + + var round = (this.currentFrame + 0.5) | 0; + + if(this.loop || round < this.textures.length) + { + this.setTexture(this.textures[round % this.textures.length]); + } + else if(round >= this.textures.length) + { + this.gotoAndStop(this.textures.length - 1); + if(this.onComplete) + { + this.onComplete(); + } + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.FilterBlock = function() +{ + this.visible = true; + this.renderable = true; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Text Object will create a line(s) of text. To split a line you can use '\n' + * or add a wordWrap property set to true and and wordWrapWidth property with a value + * in the style object + * + * @class Text + * @extends Sprite + * @constructor + * @param text {String} The copy that you would like the text to display + * @param [style] {Object} The style parameters + * @param [style.font] {String} default 'bold 20px Arial' The style and size of the font + * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * @param [style.stroke] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap, it needs wordWrap to be set to true + */ +PIXI.Text = function(text, style) +{ + /** + * The canvas element that everything is drawn to + * + * @property canvas + * @type HTMLCanvasElement + */ + this.canvas = document.createElement('canvas'); + + /** + * The canvas 2d context that everything is drawn with + * @property context + * @type HTMLCanvasElement 2d Context + */ + this.context = this.canvas.getContext('2d'); + + PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas)); + + this.setText(text); + this.setStyle(style); + + this.updateText(); + this.dirty = false; +}; + +// constructor +PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); +PIXI.Text.prototype.constructor = PIXI.Text; + +/** + * Set the style of the text + * + * @method setStyle + * @param [style] {Object} The style parameters + * @param [style.font='bold 20pt Arial'] {String} The style and size of the font + * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * @param [style.stroke='black'] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap + */ +PIXI.Text.prototype.setStyle = function(style) +{ + style = style || {}; + style.font = style.font || 'bold 20pt Arial'; + style.fill = style.fill || 'black'; + style.align = style.align || 'left'; + style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136 + style.strokeThickness = style.strokeThickness || 0; + style.wordWrap = style.wordWrap || false; + style.wordWrapWidth = style.wordWrapWidth || 100; + this.style = style; + this.dirty = true; +}; + +/** + * Set the copy for the text object. To split a line you can use '\n' + * + * @method setText + * @param {String} text The copy that you would like the text to display + */ +PIXI.Text.prototype.setText = function(text) +{ + this.text = text.toString() || ' '; + this.dirty = true; + +}; + +/** + * Renders text and updates it when needed + * + * @method updateText + * @private + */ +PIXI.Text.prototype.updateText = function() +{ + this.context.font = this.style.font; + + var outputText = this.text; + + // word wrap + // preserve original text + if(this.style.wordWrap)outputText = this.wordWrap(this.text); + + //split text into lines + var lines = outputText.split(/(?:\r\n|\r|\n)/); + + //calculate text width + var lineWidths = []; + var maxLineWidth = 0; + for (var i = 0; i < lines.length; i++) + { + var lineWidth = this.context.measureText(lines[i]).width; + lineWidths[i] = lineWidth; + maxLineWidth = Math.max(maxLineWidth, lineWidth); + } + this.canvas.width = maxLineWidth + this.style.strokeThickness; + + //calculate text height + var lineHeight = this.determineFontHeight('font: ' + this.style.font + ';') + this.style.strokeThickness; + this.canvas.height = lineHeight * lines.length; + + if(navigator.isCocoonJS) this.context.clearRect(0,0,this.canvas.width,this.canvas.height); + + //set canvas text styles + this.context.fillStyle = this.style.fill; + this.context.font = this.style.font; + + this.context.strokeStyle = this.style.stroke; + this.context.lineWidth = this.style.strokeThickness; + + this.context.textBaseline = 'top'; + + //draw lines line by line + for (i = 0; i < lines.length; i++) + { + var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); + + if(this.style.align === 'right') + { + linePosition.x += maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + linePosition.x += (maxLineWidth - lineWidths[i]) / 2; + } + + if(this.style.stroke && this.style.strokeThickness) + { + this.context.strokeText(lines[i], linePosition.x, linePosition.y); + } + + if(this.style.fill) + { + this.context.fillText(lines[i], linePosition.x, linePosition.y); + } + } + + this.updateTexture(); +}; + +/** + * Updates texture size based on canvas size + * + * @method updateTexture + * @private + */ +PIXI.Text.prototype.updateTexture = function() +{ + this.texture.baseTexture.width = this.canvas.width; + this.texture.baseTexture.height = this.canvas.height; + this.texture.frame.width = this.canvas.width; + this.texture.frame.height = this.canvas.height; + + this._width = this.canvas.width; + this._height = this.canvas.height; + + this.requiresUpdate = true; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Text.prototype._renderWebGL = function(renderSession) +{ + if(this.requiresUpdate) + { + this.requiresUpdate = false; + PIXI.updateWebGLTexture(this.texture.baseTexture, renderSession.gl); + } + + PIXI.Sprite.prototype._renderWebGL.call(this, renderSession); +}; + +/** + * Updates the transform of this object + * + * @method updateTransform + * @private + */ +PIXI.Text.prototype.updateTransform = function() +{ + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + PIXI.Sprite.prototype.updateTransform.call(this); +}; + +/* + * http://stackoverflow.com/users/34441/ellisbben + * great solution to the problem! + * returns the height of the given font + * + * @method determineFontHeight + * @param fontStyle {Object} + * @private + */ +PIXI.Text.prototype.determineFontHeight = function(fontStyle) +{ + // build a little reference dictionary so if the font style has been used return a + // cached version... + var result = PIXI.Text.heightCache[fontStyle]; + + if(!result) + { + var body = document.getElementsByTagName('body')[0]; + var dummy = document.createElement('div'); + var dummyText = document.createTextNode('M'); + dummy.appendChild(dummyText); + dummy.setAttribute('style', fontStyle + ';position:absolute;top:0;left:0'); + body.appendChild(dummy); + + result = dummy.offsetHeight; + PIXI.Text.heightCache[fontStyle] = result; + + body.removeChild(dummy); + } + + return result; +}; + +/** + * Applies newlines to a string to have it optimally fit into the horizontal + * bounds set by the Text object's wordWrapWidth property. + * + * @method wordWrap + * @param text {String} + * @private + */ +PIXI.Text.prototype.wordWrap = function(text) +{ + // Greedy wrapping algorithm that will wrap words as the line grows longer + // than its horizontal bounds. + var result = ''; + var lines = text.split('\n'); + for (var i = 0; i < lines.length; i++) + { + var spaceLeft = this.style.wordWrapWidth; + var words = lines[i].split(' '); + for (var j = 0; j < words.length; j++) + { + var wordWidth = this.context.measureText(words[j]).width; + var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; + if(wordWidthWithSpace > spaceLeft) + { + // Skip printing the newline if it's the first word of the line that is + // greater than the word wrap width. + if(j > 0) + { + result += '\n'; + } + result += words[j] + ' '; + spaceLeft = this.style.wordWrapWidth - wordWidth; + } + else + { + spaceLeft -= wordWidthWithSpace; + result += words[j] + ' '; + } + } + + if (i < lines.length-1) + { + result += '\n'; + } + } + return result; +}; + +/** + * Destroys this text object + * + * @method destroy + * @param destroyTexture {Boolean} + */ +PIXI.Text.prototype.destroy = function(destroyTexture) +{ + if(destroyTexture) + { + this.texture.destroy(); + } + +}; + +PIXI.Text.heightCache = {}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Text Object will create a line(s) of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n' + * You can generate the fnt files using + * http://www.angelcode.com/products/bmfont/ for windows or + * http://www.bmglyph.com/ for mac. + * + * @class BitmapText + * @extends DisplayObjectContainer + * @constructor + * @param text {String} The copy that you would like the text to display + * @param style {Object} The style parameters + * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + */ +PIXI.BitmapText = function(text, style) +{ + PIXI.DisplayObjectContainer.call(this); + + this._pool = []; + + this.setText(text); + this.setStyle(style); + this.updateText(); + this.dirty = false; +}; + +// constructor +PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.BitmapText.prototype.constructor = PIXI.BitmapText; + +/** + * Set the copy for the text object + * + * @method setText + * @param text {String} The copy that you would like the text to display + */ +PIXI.BitmapText.prototype.setText = function(text) +{ + this.text = text || ' '; + this.dirty = true; +}; + +/** + * Set the style of the text + * style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) + * [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * + * @method setStyle + * @param style {Object} The style parameters, contained as properties of an object + */ +PIXI.BitmapText.prototype.setStyle = function(style) +{ + style = style || {}; + style.align = style.align || 'left'; + this.style = style; + + var font = style.font.split(' '); + this.fontName = font[font.length - 1]; + this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size; + + this.dirty = true; + this.tint = style.tint; +}; + +/** + * Renders text and updates it when needed + * + * @method updateText + * @private + */ +PIXI.BitmapText.prototype.updateText = function() +{ + var data = PIXI.BitmapText.fonts[this.fontName]; + var pos = new PIXI.Point(); + var prevCharCode = null; + var chars = []; + var maxLineWidth = 0; + var lineWidths = []; + var line = 0; + var scale = this.fontSize / data.size; + + + for(var i = 0; i < this.text.length; i++) + { + var charCode = this.text.charCodeAt(i); + if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i))) + { + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + line++; + + pos.x = 0; + pos.y += data.lineHeight; + prevCharCode = null; + continue; + } + + var charData = data.chars[charCode]; + if(!charData) continue; + + if(prevCharCode && charData[prevCharCode]) + { + pos.x += charData.kerning[prevCharCode]; + } + chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)}); + pos.x += charData.xAdvance; + + prevCharCode = charCode; + } + + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + + var lineAlignOffsets = []; + for(i = 0; i <= line; i++) + { + var alignOffset = 0; + if(this.style.align === 'right') + { + alignOffset = maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + alignOffset = (maxLineWidth - lineWidths[i]) / 2; + } + lineAlignOffsets.push(alignOffset); + } + + var lenChildren = this.children.length; + var lenChars = chars.length; + var tint = this.tint || 0xFFFFFF; + for(i = 0; i < lenChars; i++) + { + var c = i < lenChildren ? this.children[i] : this._pool.pop(); // get old child if have. if not - take from pool. + + if (c) c.setTexture(chars[i].texture); // check if got one before. + else c = new PIXI.Sprite(chars[i].texture); // if no create new one. + + c.position.x = (chars[i].position.x + lineAlignOffsets[chars[i].line]) * scale; + c.position.y = chars[i].position.y * scale; + c.scale.x = c.scale.y = scale; + c.tint = tint; + if (!c.parent) this.addChild(c); + } + + // remove unnecessary children. + // and put their into the pool. + while(this.children.length > lenChars) + { + var child = this.getChildAt(this.children.length - 1); + this._pool.push(child); + this.removeChild(child); + } + + + /** + * [read-only] The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @property textWidth + * @type Number + */ + this.textWidth = maxLineWidth * scale; + + /** + * [read-only] The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @property textHeight + * @type Number + */ + this.textHeight = (pos.y + data.lineHeight) * scale; +}; + +/** + * Updates the transform of this object + * + * @method updateTransform + * @private + */ +PIXI.BitmapText.prototype.updateTransform = function() +{ + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); +}; + +PIXI.BitmapText.fonts = {}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * Holds all information related to an Interaction event + * + * @class InteractionData + * @constructor + */ +PIXI.InteractionData = function() +{ + /** + * This point stores the global coords of where the touch/mouse event happened + * + * @property global + * @type Point + */ + this.global = new PIXI.Point(); + + // this is here for legacy... but will remove + this.local = new PIXI.Point(); + + /** + * The target Sprite that was interacted with + * + * @property target + * @type Sprite + */ + this.target = null; + + /** + * When passed to an event handler, this will be the original DOM Event that was captured + * + * @property originalEvent + * @type Event + */ + this.originalEvent = null; +}; + +/** + * This will return the local coordinates of the specified displayObject for this InteractionData + * + * @method getLocalPosition + * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off + * @return {Point} A point containing the coordinates of the InteractionData position relative to the DisplayObject + */ +PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +{ + var worldTransform = displayObject.worldTransform; + var global = this.global; + + // do a cheeky transform to get the mouse coords; + var a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, + a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, + id = 1 / (a00 * a11 + a01 * -a10); + // set the mouse coords... + return new PIXI.Point(a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, + a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id); +}; + +// constructor +PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + /** + * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive + * if its interactive parameter is set to true + * This manager also supports multitouch. + * + * @class InteractionManager + * @constructor + * @param stage {Stage} The stage to handle interactions + */ +PIXI.InteractionManager = function(stage) +{ + /** + * a reference to the stage + * + * @property stage + * @type Stage + */ + this.stage = stage; + + /** + * the mouse data + * + * @property mouse + * @type InteractionData + */ + this.mouse = new PIXI.InteractionData(); + + /** + * an object that stores current touches (InteractionData) by id reference + * + * @property touchs + * @type Object + */ + this.touchs = {}; + + // helpers + this.tempPoint = new PIXI.Point(); + + /** + * + * @property mouseoverEnabled + * @type Boolean + * @default + */ + this.mouseoverEnabled = true; + + /** + * tiny little interactiveData pool ! + * + * @property pool + * @type Array + */ + this.pool = []; + + /** + * An array containing all the iterative items from the our interactive tree + * @property interactiveItems + * @type Array + * @private + * + */ + this.interactiveItems = []; + + /** + * Our canvas + * @property interactionDOMElement + * @type HTMLCanvasElement + * @private + */ + this.interactionDOMElement = null; + + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; + + /** + * The css style of the cursor that is being used + * @property currentCursorStyle + * @type String + * + */ + this.currentCursorStyle = 'inherit'; + + /** + * Is set to true when the mouse is moved out of the canvas + * @property mouseOut + * @type Boolean + * + */ + this.mouseOut = false; +}; + +// constructor +PIXI.InteractionManager.prototype.constructor = PIXI.InteractionManager; + +/** + * Collects an interactive sprite recursively to have their interactions managed + * + * @method collectInteractiveSprite + * @param displayObject {DisplayObject} the displayObject to collect + * @param iParent {DisplayObject} the display object's parent + * @private + */ +PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) +{ + var children = displayObject.children; + var length = children.length; + + // make an interaction tree... {item.__interactiveParent} + for (var i = length-1; i >= 0; i--) + { + var child = children[i]; + + // push all interactive bits + if(child.interactive) + { + iParent.interactiveChildren = true; + //child.__iParent = iParent; + this.interactiveItems.push(child); + + if(child.children.length > 0) + { + this.collectInteractiveSprite(child, child); + } + } + else + { + child.__iParent = null; + + if(child.children.length > 0) + { + this.collectInteractiveSprite(child, iParent); + } + } + + } +}; + +/** + * Sets the target for event delegation + * + * @method setTarget + * @param target {WebGLRenderer|CanvasRenderer} the renderer to bind events to + * @private + */ +PIXI.InteractionManager.prototype.setTarget = function(target) +{ + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + this.setTargetDomElement( target.view ); + } + + +}; + + +/** + * Sets the DOM element which will receive mouse/touch events. This is useful for when you have other DOM + * elements on top of the renderers Canvas element. With this you'll be able to delegate another DOM element + * to receive those events + * + * @method setTargetDomElement + * @param domElement {DOMElement} the DOM element which will receive mouse and touch events + * @private + */ +PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) +{ + + this.removeEvents(); + + + if (window.navigator.msPointerEnabled) + { + // time to remove some of that zoom in ja.. + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; + + // DO some window specific touch! + } + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); + + document.body.addEventListener('mouseup', this.onMouseUp, true); +}; + + +PIXI.InteractionManager.prototype.removeEvents = function() +{ + if(!this.interactionDOMElement)return; + + this.interactionDOMElement.style['-ms-content-zooming'] = ''; + this.interactionDOMElement.style['-ms-touch-action'] = ''; + + this.interactionDOMElement.removeEventListener('mousemove', this.onMouseMove, true); + this.interactionDOMElement.removeEventListener('mousedown', this.onMouseDown, true); + this.interactionDOMElement.removeEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + this.interactionDOMElement.removeEventListener('touchstart', this.onTouchStart, true); + this.interactionDOMElement.removeEventListener('touchend', this.onTouchEnd, true); + this.interactionDOMElement.removeEventListener('touchmove', this.onTouchMove, true); + + this.interactionDOMElement = null; + + document.body.removeEventListener('mouseup', this.onMouseUp, true); +}; + +/** + * updates the state of interactive objects + * + * @method update + * @private + */ +PIXI.InteractionManager.prototype.update = function() +{ + if(!this.target)return; + + // frequency of 30fps?? + var now = Date.now(); + var diff = now - this.last; + diff = (diff * PIXI.INTERACTION_FREQUENCY ) / 1000; + if(diff < 1)return; + this.last = now; + + var i = 0; + + // ok.. so mouse events?? + // yes for now :) + // OPTIMISE - how often to check?? + if(this.dirty) + { + this.dirty = false; + + var len = this.interactiveItems.length; + + for (i = 0; i < len; i++) { + this.interactiveItems[i].interactiveChildren = false; + } + + this.interactiveItems = []; + + if(this.stage.interactive)this.interactiveItems.push(this.stage); + // go through and collect all the objects that are interactive.. + this.collectInteractiveSprite(this.stage, this.stage); + } + + // loop through interactive objects! + var length = this.interactiveItems.length; + var cursor = 'inherit'; + var over = false; + + for (i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + // OPTIMISATION - only calculate every time if the mousemove function exists.. + // OK so.. does the object have any other interactive functions? + // hit-test the clip! + // if(item.mouseover || item.mouseout || item.buttonMode) + // { + // ok so there are some functions so lets hit test it.. + item.__hit = this.hitTest(item, this.mouse); + this.mouse.target = item; + // ok so deal with interactions.. + // looks like there was a hit! + if(item.__hit && !over) + { + if(item.buttonMode) cursor = item.defaultCursor; + + if(!item.interactiveChildren)over = true; + + if(!item.__isOver) + { + if(item.mouseover)item.mouseover(this.mouse); + item.__isOver = true; + } + } + else + { + if(item.__isOver) + { + // roll out! + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } + } + + if( this.currentCursorStyle !== cursor ) + { + this.currentCursorStyle = cursor; + this.interactionDOMElement.style.cursor = cursor; + } +}; + +/** + * Is called when the mouse moves across the renderer element + * + * @method onMouseMove + * @param event {Event} The DOM event of the mouse moving + * @private + */ +PIXI.InteractionManager.prototype.onMouseMove = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + // TODO optimize by not check EVERY TIME! maybe half as often? // + var rect = this.interactionDOMElement.getBoundingClientRect(); + + this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); + this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); + + var length = this.interactiveItems.length; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mousemove) + { + //call the function! + item.mousemove(this.mouse); + } + } +}; + +/** + * Is called when the mouse button is pressed down on the renderer element + * + * @method onMouseDown + * @param event {Event} The DOM event of a mouse button being pressed down + * @private + */ +PIXI.InteractionManager.prototype.onMouseDown = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + + if(PIXI.AUTO_PREVENT_DEFAULT)this.mouse.originalEvent.preventDefault(); + + // loop through interaction tree... + // hit test each item! -> + // get interactive items under point?? + //stage.__i + var length = this.interactiveItems.length; + + // while + // hit test + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mousedown || item.click) + { + item.__mouseIsDown = true; + item.__hit = this.hitTest(item, this.mouse); + + if(item.__hit) + { + //call the function! + if(item.mousedown)item.mousedown(this.mouse); + item.__isDown = true; + + // just the one! + if(!item.interactiveChildren)break; + } + } + } +}; + +/** + * Is called when the mouse button is moved out of the renderer element + * + * @method onMouseOut + * @param event {Event} The DOM event of a mouse button being moved out + * @private + */ +PIXI.InteractionManager.prototype.onMouseOut = function() +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = 'inherit'; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } + + this.mouseOut = true; + + // move the mouse to an impossible position + this.mouse.global.x = -10000; + this.mouse.global.y = -10000; +}; + +/** + * Is called when the mouse button is released on the renderer element + * + * @method onMouseUp + * @param event {Event} The DOM event of a mouse button being released + * @private + */ +PIXI.InteractionManager.prototype.onMouseUp = function(event) +{ + + this.mouse.originalEvent = event || window.event; //IE uses window.event + + var length = this.interactiveItems.length; + var up = false; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + item.__hit = this.hitTest(item, this.mouse); + + if(item.__hit && !up) + { + //call the function! + if(item.mouseup) + { + item.mouseup(this.mouse); + } + if(item.__isDown) + { + if(item.click)item.click(this.mouse); + } + + if(!item.interactiveChildren)up = true; + } + else + { + if(item.__isDown) + { + if(item.mouseupoutside)item.mouseupoutside(this.mouse); + } + } + + item.__isDown = false; + //} + } +}; + +/** + * Tests if the current mouse coordinates hit a sprite + * + * @method hitTest + * @param item {DisplayObject} The displayObject to test for a hit + * @param interactionData {InteractionData} The interactionData object to update in the case there is a hit + * @private + */ +PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) +{ + var global = interactionData.global; + + if( !item.worldVisible )return false; + + // temp fix for if the element is in a non visible + + var isSprite = (item instanceof PIXI.Sprite), + worldTransform = item.worldTransform, + a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, + a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, + id = 1 / (a00 * a11 + a01 * -a10), + x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, + y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; + + interactionData.target = item; + + //a sprite or display object with a hit area defined + if(item.hitArea && item.hitArea.contains) { + if(item.hitArea.contains(x, y)) { + //if(isSprite) + interactionData.target = item; + + return true; + } + + return false; + } + // a sprite with no hitarea defined + else if(isSprite) + { + var width = item.texture.frame.width, + height = item.texture.frame.height, + x1 = -width * item.anchor.x, + y1; + + if(x > x1 && x < x1 + width) + { + y1 = -height * item.anchor.y; + + if(y > y1 && y < y1 + height) + { + // set the target property if a hit is true! + interactionData.target = item; + return true; + } + } + } + + var length = item.children.length; + + for (var i = 0; i < length; i++) + { + var tempItem = item.children[i]; + var hit = this.hitTest(tempItem, interactionData); + if(hit) + { + // hmm.. TODO SET CORRECT TARGET? + interactionData.target = item; + return true; + } + } + + return false; +}; + +/** + * Is called when a touch is moved across the renderer element + * + * @method onTouchMove + * @param event {Event} The DOM event of a touch moving across the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchMove = function(event) +{ + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + var touchData; + var i = 0; + + for (i = 0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; + + // update the touch position + touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); + touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + } + + var length = this.interactiveItems.length; + for (i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + if(item.touchmove) + item.touchmove(touchData); + } +}; + +/** + * Is called when a touch is started on the renderer element + * + * @method onTouchStart + * @param event {Event} The DOM event of a touch starting on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchStart = function(event) +{ + var rect = this.interactionDOMElement.getBoundingClientRect(); + + if(PIXI.AUTO_PREVENT_DEFAULT)event.preventDefault(); + + var changedTouches = event.changedTouches; + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + + var touchData = this.pool.pop(); + if(!touchData)touchData = new PIXI.InteractionData(); + + touchData.originalEvent = event || window.event; + + this.touchs[touchEvent.identifier] = touchData; + touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); + touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + + var length = this.interactiveItems.length; + + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + + if(item.touchstart || item.tap) + { + item.__hit = this.hitTest(item, touchData); + + if(item.__hit) + { + //call the function! + if(item.touchstart)item.touchstart(touchData); + item.__isDown = true; + item.__touchData = touchData; + + if(!item.interactiveChildren)break; + } + } + } + } +}; + +/** + * Is called when a touch is ended on the renderer element + * + * @method onTouchEnd + * @param event {Event} The DOM event of a touch ending on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchEnd = function(event) +{ + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + var touchData = this.touchs[touchEvent.identifier]; + var up = false; + touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); + touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + + var length = this.interactiveItems.length; + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + var itemTouchData = item.__touchData; // <-- Here! + item.__hit = this.hitTest(item, touchData); + + if(itemTouchData === touchData) + { + // so this one WAS down... + touchData.originalEvent = event || window.event; + // hitTest?? + + if(item.touchend || item.tap) + { + if(item.__hit && !up) + { + if(item.touchend)item.touchend(touchData); + if(item.__isDown) + { + if(item.tap)item.tap(touchData); + } + + if(!item.interactiveChildren)up = true; + } + else + { + if(item.__isDown) + { + if(item.touchendoutside)item.touchendoutside(touchData); + } + } + + item.__isDown = false; + } + + item.__touchData = null; + + } + /* + else + { + + } + */ + } + // remove the touch.. + this.pool.push(touchData); + this.touchs[touchEvent.identifier] = null; + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Stage represents the root of the display tree. Everything connected to the stage is rendered + * + * @class Stage + * @extends DisplayObjectContainer + * @constructor + * @param backgroundColor {Number} the background color of the stage, you have to pass this in is in hex format + * like: 0xFFFFFF for white + * + * Creating a stage is a mandatory process when you use Pixi, which is as simple as this : + * var stage = new PIXI.Stage(0xFFFFFF); + * where the parameter given is the background colour of the stage, in hex + * you will use this stage instance to add your sprites to it and therefore to the renderer + * Here is how to add a sprite to the stage : + * stage.addChild(sprite); + */ +PIXI.Stage = function(backgroundColor) +{ + PIXI.DisplayObjectContainer.call( this ); + + /** + * [read-only] Current transform of the object based on world (parent) factors + * + * @property worldTransform + * @type Mat3 + * @readOnly + * @private + */ + this.worldTransform = new PIXI.Matrix(); + + /** + * Whether or not the stage is interactive + * + * @property interactive + * @type Boolean + */ + this.interactive = true; + + /** + * The interaction manage for this stage, manages all interactive activity on the stage + * + * @property interactive + * @type InteractionManager + */ + this.interactionManager = new PIXI.InteractionManager(this); + + /** + * Whether the stage is dirty and needs to have interactions updated + * + * @property dirty + * @type Boolean + * @private + */ + this.dirty = true; + + //the stage is its own stage + this.stage = this; + + //optimize hit detection a bit + this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); + + this.setBackgroundColor(backgroundColor); +}; + +// constructor +PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Stage.prototype.constructor = PIXI.Stage; + +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements on top of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +}; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; +}; + +/** + * Converts a color as an [R, G, B] array to a hex number + * + * @method rgb2hex + * @param rgb {Array} + */ +PIXI.rgb2hex = function(rgb) { + return ((rgb[0]*255 << 16) + (rgb[1]*255 << 8) + rgb[2]*255); +}; + +/** + * A polyfill for Function.prototype.bind + * + * @method bind + */ +if (typeof Function.prototype.bind !== 'function') { + Function.prototype.bind = (function () { + var slice = Array.prototype.slice; + return function (thisArg) { + var target = this, boundArgs = slice.call(arguments, 1); + + if (typeof target !== 'function') throw new TypeError(); + + function bound() { + var args = boundArgs.concat(slice.call(arguments)); + target.apply(this instanceof bound ? this : thisArg, args); + } + + bound.prototype = (function F(proto) { + if (proto) F.prototype = proto; + if (!(this instanceof F)) return new F(); + })(target.prototype); + + return bound; + }; + })(); +} + +/** + * A wrapper for ajax requests to be handled cross browser + * + * @class AjaxRequest + * @constructor + */ +PIXI.AjaxRequest = function() +{ + var activexmodes = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.3.0', 'Microsoft.XMLHTTP']; //activeX versions to check for in IE + + if (window.ActiveXObject) + { //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken) + for (var i=0; i 0 && (number & (number - 1)) === 0) // see: http://goo.gl/D9kPj + return number; + else + { + var result = 1; + while (result < number) result <<= 1; + return result; + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * https://github.com/mrdoob/eventtarget.js/ + * THankS mr DOob! + */ + +/** + * Adds event emitter functionality to a class + * + * @class EventTarget + * @example + * function MyEmitter() { + * PIXI.EventTarget.call(this); //mixes in event target stuff + * } + * + * var em = new MyEmitter(); + * em.emit({ type: 'eventName', data: 'some data' }); + */ +PIXI.EventTarget = function () { + + /** + * Holds all the listeners + * + * @property listeneners + * @type Object + */ + var listeners = {}; + + /** + * Adds a listener for a specific event + * + * @method addEventListener + * @param type {string} A string representing the event type to listen for. + * @param listener {function} The callback function that will be fired when the event occurs + */ + this.addEventListener = this.on = function ( type, listener ) { + + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + } + + }; + + /** + * Fires the event, ie pretends that the event has happened + * + * @method dispatchEvent + * @param event {Event} the event object + */ + this.dispatchEvent = this.emit = function ( event ) { + + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + + } + + }; + + /** + * Removes the specified listener that was assigned to the specified event type + * + * @method removeEventListener + * @param type {string} A string representing the event type which will have its listener removed + * @param listener {function} The callback function that was be fired when the event occured + */ + this.removeEventListener = this.off = function ( type, listener ) { + + var index = listeners[ type ].indexOf( listener ); + + if ( index !== - 1 ) { + + listeners[ type ].splice( index, 1 ); + + } + + }; + + /** + * Removes all the listeners that were active for the specified event type + * + * @method removeAllEventListeners + * @param type {string} A string representing the event type which will have all its listeners removed + */ + this.removeAllEventListeners = function( type ) { + var a = listeners[type]; + if (a) + a.length = 0; + }; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * @class autoDetectRenderer + * @static + * @param width=800 {Number} the width of the renderers view + * @param height=600 {Number} the height of the renderers view + * @param [view] {Canvas} the canvas to use as a view, optional + * @param [transparent=false] {Boolean} the transparency of the render view, default false + * @param [antialias=false] {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + */ +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) +{ + if(!width)width = 800; + if(!height)height = 600; + + // BORROWED from Mr Doob (mrdoob.com) + var webgl = ( function () { try { + var canvas = document.createElement( 'canvas' ); + return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); + } catch( e ) { + return false; + } + } )(); + + + if( webgl ) + { + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); + } + + return new PIXI.CanvasRenderer(width, height, view, transparent); +}; + +/* + PolyK library + url: http://polyk.ivank.net + Released under MIT licence. + + Copyright (c) 2012 Ivan Kuckir + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + This is an amazing lib! + + slightly modified by Mat Groves (matgroves.com); +*/ + +/** + * Based on the Polyk library http://polyk.ivank.net released under MIT licence. + * This is an amazing lib! + * slightly modified by Mat Groves (matgroves.com); + * @class PolyK + * + */ +PIXI.PolyK = {}; + +/** + * Triangulates shapes for webGL graphic fills + * + * @method Triangulate + * + */ +PIXI.PolyK.Triangulate = function(p) +{ + var sign = true; + + var n = p.length >> 1; + if(n < 3) return []; + + var tgs = []; + var avl = []; + for(var i = 0; i < n; i++) avl.push(i); + + i = 0; + var al = n; + while(al > 3) + { + var i0 = avl[(i+0)%al]; + var i1 = avl[(i+1)%al]; + var i2 = avl[(i+2)%al]; + + var ax = p[2*i0], ay = p[2*i0+1]; + var bx = p[2*i1], by = p[2*i1+1]; + var cx = p[2*i2], cy = p[2*i2+1]; + + var earFound = false; + if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) + { + earFound = true; + for(var j = 0; j < al; j++) + { + var vi = avl[j]; + if(vi === i0 || vi === i1 || vi === i2) continue; + + if(PIXI.PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) { + earFound = false; + break; + } + } + } + + if(earFound) + { + tgs.push(i0, i1, i2); + avl.splice((i+1)%al, 1); + al--; + i = 0; + } + else if(i++ > 3*al) + { + // need to flip flip reverse it! + // reset! + if(sign) + { + tgs = []; + avl = []; + for(i = 0; i < n; i++) avl.push(i); + + i = 0; + al = n; + + sign = false; + } + else + { + window.console.log("PIXI Warning: shape too complex to fill"); + return []; + } + } + } + + tgs.push(avl[0], avl[1], avl[2]); + return tgs; +}; + +/** + * Checks whether a point is within a triangle + * + * @method _PointInTriangle + * @param px {Number} x coordinate of the point to test + * @param py {Number} y coordinate of the point to test + * @param ax {Number} x coordinate of the a point of the triangle + * @param ay {Number} y coordinate of the a point of the triangle + * @param bx {Number} x coordinate of the b point of the triangle + * @param by {Number} y coordinate of the b point of the triangle + * @param cx {Number} x coordinate of the c point of the triangle + * @param cy {Number} y coordinate of the c point of the triangle + * @private + */ +PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy) +{ + var v0x = cx-ax; + var v0y = cy-ay; + var v1x = bx-ax; + var v1y = by-ay; + var v2x = px-ax; + var v2y = py-ay; + + var dot00 = v0x*v0x+v0y*v0y; + var dot01 = v0x*v1x+v0y*v1y; + var dot02 = v0x*v2x+v0y*v2y; + var dot11 = v1x*v1x+v1y*v1y; + var dot12 = v1x*v2x+v1y*v2y; + + var invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + var u = (dot11 * dot02 - dot01 * dot12) * invDenom; + var v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + // Check if point is in triangle + return (u >= 0) && (v >= 0) && (u + v < 1); +}; + +/** + * Checks whether a shape is convex + * + * @method _convex + * + * @private + */ +PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign) +{ + return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +// TODO Alvin and Mat +// Should we eventually create a Utils class ? +// Or just move this file to the pixi.js file ? +PIXI.initDefaultShaders = function() +{ + + // PIXI.stripShader = new PIXI.StripShader(); +// PIXI.stripShader.init(); + +}; + +PIXI.CompileVertexShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); +}; + +PIXI.CompileFragmentShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); +}; + +PIXI._CompileShader = function(gl, shaderSrc, shaderType) +{ + var src = shaderSrc.join("\n"); + var shader = gl.createShader(shaderType); + gl.shaderSource(shader, src); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + window.console.log(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; + +PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) +{ + var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); + var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc); + + var shaderProgram = gl.createProgram(); + + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + window.console.log("Could not initialise shaders"); + } + + return shaderProgram; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Richard Davey http://www.photonstorm.com @photonstorm + */ + +/** +* @class PixiShader +* @constructor +*/ +PIXI.PixiShader = function(gl) +{ + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * @property {any} program - The WebGL program. + */ + this.program = null; + + /** + * @property {array} fragmentSrc - The fragment shader. + */ + this.fragmentSrc = [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]; + + + /** + * @property {number} textureCount - A local texture counter for multi-texture shaders. + */ + this.textureCount = 0; + + this.attributes = []; + + this.init(); +}; + +/** +* Initialises the shader +* @method init +* +*/ +PIXI.PixiShader.prototype.init = function() +{ + + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc); + + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.dimensions = gl.getUniformLocation(program, 'dimensions'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if(this.colorAttribute === -1) + { + this.colorAttribute = 2; + } + + this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; + + // End worst hack eva // + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); + } + + this.initUniforms(); + + this.program = program; +}; + +/** +* Initialises the shader uniform values. +* Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ +* http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf +* +* @method initUniforms +*/ +PIXI.PixiShader.prototype.initUniforms = function() +{ + this.textureCount = 1; + var gl = this.gl; + var uniform; + + for (var key in this.uniforms) + { + uniform = this.uniforms[key]; + + var type = uniform.type; + + if (type === 'sampler2D') + { + uniform._init = false; + + if (uniform.value !== null) + { + this.initSampler2D(uniform); + } + } + else if (type === 'mat2' || type === 'mat3' || type === 'mat4') + { + // These require special handling + uniform.glMatrix = true; + uniform.glValueLength = 1; + + if (type === 'mat2') + { + uniform.glFunc = gl.uniformMatrix2fv; + } + else if (type === 'mat3') + { + uniform.glFunc = gl.uniformMatrix3fv; + } + else if (type === 'mat4') + { + uniform.glFunc = gl.uniformMatrix4fv; + } + } + else + { + // GL function reference + uniform.glFunc = gl['uniform' + type]; + + if (type === '2f' || type === '2i') + { + uniform.glValueLength = 2; + } + else if (type === '3f' || type === '3i') + { + uniform.glValueLength = 3; + } + else if (type === '4f' || type === '4i') + { + uniform.glValueLength = 4; + } + else + { + uniform.glValueLength = 1; + } + } + } + +}; + +/** +* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) +* +* @method initSampler2D +*/ +PIXI.PixiShader.prototype.initSampler2D = function(uniform) +{ + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + return; + } + + var gl = this.gl; + + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTexture); + + // Extended texture data + if (uniform.textureData) + { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) + { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) + { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else + { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } + + gl.uniform1i(uniform.uniformLocation, this.textureCount); + + uniform._init = true; + + this.textureCount++; + +}; + +/** +* Updates the shader uniform values. +* +* @method syncUniforms +*/ +PIXI.PixiShader.prototype.syncUniforms = function() +{ + this.textureCount = 1; + var uniform; + var gl = this.gl; + + // This would probably be faster in an array and it would guarantee key order + for (var key in this.uniforms) + { + + uniform = this.uniforms[key]; + + if (uniform.glValueLength === 1) + { + if (uniform.glMatrix === true) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); + } + else + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); + } + } + else if (uniform.glValueLength === 2) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); + } + else if (uniform.glValueLength === 3) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); + } + else if (uniform.glValueLength === 4) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); + } + else if (uniform.type === 'sampler2D') + { + if (uniform._init) + { + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl)); + gl.uniform1i(uniform.uniformLocation, this.textureCount); + this.textureCount++; + } + else + { + this.initSampler2D(uniform); + } + } + } + +}; + +/** +* Destroys the shader +* @method destroy +* +*/ +PIXI.PixiShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attributes = null; +}; + +/** +* +* @property defaultVertexSrc +* @type String +*/ +PIXI.PixiShader.defaultVertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec2 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = vec4(color * aColor.x, aColor.x);', + '}' +]; + + + + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Richard Davey http://www.photonstorm.com @photonstorm + */ + +/** +* @class PixiFastShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.PixiFastShader = function(gl) +{ + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * @property {any} program - The WebGL program. + */ + this.program = null; + + /** + * @property {array} fragmentSrc - The fragment shader. + */ + this.fragmentSrc = [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying float vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]; + + /** + * @property {array} vertexSrc - The vertex shader + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying float vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ]; + + + /** + * @property {number} textureCount - A local texture counter for multi-texture shaders. + */ + this.textureCount = 0; + + + this.init(); +}; + +/** +* Initialises the shader +* @method init +* +*/ +PIXI.PixiFastShader.prototype.init = function() +{ + + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.dimensions = gl.getUniformLocation(program, 'dimensions'); + this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); + + this.aScale = gl.getAttribLocation(program, 'aScale'); + this.aRotation = gl.getAttribLocation(program, 'aRotation'); + + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + + + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its somthing to do with the current state of the gl context. + // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if(this.colorAttribute === -1) + { + this.colorAttribute = 2; + } + + this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; + + // End worst hack eva // + + + this.program = program; +}; + +/** +* Destroys the shader +* @method destroy +* +*/ +PIXI.PixiFastShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attributes = null; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.StripShader = function() +{ + /** + * @property {any} program - The WebGL program. + */ + this.program = null; + + /** + * @property {array} fragmentSrc - The fragment shader. + */ + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying float vColor;', + 'uniform float alpha;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));', + ' gl_FragColor = gl_FragColor * alpha;', + '}' + ]; + + /** + * @property {array} fragmentSrc - The fragment shader. + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'varying vec2 vTextureCoord;', + 'uniform vec2 offsetVector;', + 'varying float vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = aColor;', + '}' + ]; +}; + +/** +* Initialises the shader +* @method init +* +*/ +PIXI.StripShader.prototype.init = function() +{ + + var gl = PIXI.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class PrimitiveShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.PrimitiveShader = function(gl) +{ + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * @property {any} program - The WebGL program. + */ + this.program = null; + + /** + * @property fragmentSrc + * @type Array + */ + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ]; + + /** + * @property vertexSrc + * @type Array + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform vec3 tint;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ]; + + this.init(); +}; + +/** +* Initialises the shader +* @method init +* +*/ +PIXI.PrimitiveShader.prototype.init = function() +{ + + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.tintColor = gl.getUniformLocation(program, 'tint'); + + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + this.attributes = [this.aVertexPosition, this.colorAttribute]; + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** +* Destroys the shader +* @method destroy +* +*/ +PIXI.PrimitiveShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attribute = null; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A set of functions used by the webGL renderer to draw the primitive graphics data + * + * @class WebGLGraphics + * @private + * @static + */ +PIXI.WebGLGraphics = function() +{ + +}; + +/** + * Renders the graphics object + * + * @static + * @private + * @method renderGraphics + * @param graphics {Graphics} + * @param renderSession {Object} + */ +PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projection, offset) +{ + var gl = renderSession.gl; + var projection = renderSession.projection, + offset = renderSession.offset, + shader = renderSession.shaderManager.primitiveShader; + + if(!graphics._webGL[gl.id])graphics._webGL[gl.id] = {points:[], indices:[], lastIndex:0, + buffer:gl.createBuffer(), + indexBuffer:gl.createBuffer()}; + + var webGL = graphics._webGL[gl.id]; + + if(graphics.dirty) + { + graphics.dirty = false; + + if(graphics.clearDirty) + { + graphics.clearDirty = false; + + webGL.lastIndex = 0; + webGL.points = []; + webGL.indices = []; + + } + + PIXI.WebGLGraphics.updateGraphics(graphics, gl); + } + + renderSession.shaderManager.activatePrimitiveShader(); + + // This could be speeded up for sure! + + // set the matrix transform + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + + gl.uniform1f(shader.alpha, graphics.worldAlpha); + gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); + + gl.drawElements(gl.TRIANGLE_STRIP, webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + + renderSession.shaderManager.deactivatePrimitiveShader(); + + // return to default shader... +// PIXI.activateShader(PIXI.defaultShader); +}; + +/** + * Updates the graphics object + * + * @static + * @private + * @method updateGraphics + * @param graphicsData {Graphics} The graphics object to update + * @param gl {WebGLContext} the current WebGL drawing context + */ +PIXI.WebGLGraphics.updateGraphics = function(graphics, gl) +{ + var webGL = graphics._webGL[gl.id]; + + for (var i = webGL.lastIndex; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + if(data.type === PIXI.Graphics.POLY) + { + if(data.fill) + { + if(data.points.length>3) + PIXI.WebGLGraphics.buildPoly(data, webGL); + } + + if(data.lineWidth > 0) + { + PIXI.WebGLGraphics.buildLine(data, webGL); + } + } + else if(data.type === PIXI.Graphics.RECT) + { + PIXI.WebGLGraphics.buildRectangle(data, webGL); + } + else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) + { + PIXI.WebGLGraphics.buildCircle(data, webGL); + } + } + + webGL.lastIndex = graphics.graphicsData.length; + + + + webGL.glPoints = new Float32Array(webGL.points); + + gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); + gl.bufferData(gl.ARRAY_BUFFER, webGL.glPoints, gl.STATIC_DRAW); + + webGL.glIndicies = new Uint16Array(webGL.indices); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, webGL.glIndicies, gl.STATIC_DRAW); +}; + +/** + * Builds a rectangle to draw + * + * @static + * @private + * @method buildRectangle + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) +{ + // --- // + // need to convert points to a nice regular data + // + var rectData = graphicsData.points; + var x = rectData[0]; + var y = rectData[1]; + var width = rectData[2]; + var height = rectData[3]; + + + if(graphicsData.fill) + { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vertPos = verts.length/6; + + // start + verts.push(x, y); + verts.push(r, g, b, alpha); + + verts.push(x + width, y); + verts.push(r, g, b, alpha); + + verts.push(x , y + height); + verts.push(r, g, b, alpha); + + verts.push(x + width, y + height); + verts.push(r, g, b, alpha); + + // insert 2 dead triangles.. + indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3); + } + + if(graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = [x, y, + x + width, y, + x + width, y + height, + x, y + height, + x, y]; + + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a circle to draw + * + * @static + * @private + * @method buildCircle + * @param graphicsData {Graphics} The graphics object to draw + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) +{ + + // need to convert points to a nice regular data + var rectData = graphicsData.points; + var x = rectData[0]; + var y = rectData[1]; + var width = rectData[2]; + var height = rectData[3]; + + var totalSegs = 40; + var seg = (Math.PI * 2) / totalSegs ; + + var i = 0; + + if(graphicsData.fill) + { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + indices.push(vecPos); + + for (i = 0; i < totalSegs + 1 ; i++) + { + verts.push(x,y, r, g, b, alpha); + + verts.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height, + r, g, b, alpha); + + indices.push(vecPos++, vecPos++); + } + + indices.push(vecPos-1); + } + + if(graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = []; + + for (i = 0; i < totalSegs + 1; i++) + { + graphicsData.points.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height); + } + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a line to draw + * + * @static + * @private + * @method buildLine + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) +{ + // TODO OPTIMISE! + var i = 0; + + var points = graphicsData.points; + if(points.length === 0)return; + + // if the line width is an odd number add 0.5 to align to a whole pixel + if(graphicsData.lineWidth%2) + { + for (i = 0; i < points.length; i++) { + points[i] += 0.5; + } + } + + // get first and last point.. figure out the middle! + var firstPoint = new PIXI.Point( points[0], points[1] ); + var lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); + + // if the first point is the last point - gonna have issues :) + if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) + { + points.pop(); + points.pop(); + + lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); + + var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; + var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; + + points.unshift(midPointX, midPointY); + points.push(midPointX, midPointY); + } + + var verts = webGLData.points; + var indices = webGLData.indices; + var length = points.length / 2; + var indexCount = points.length; + var indexStart = verts.length/6; + + // DRAW the Line + var width = graphicsData.lineWidth / 2; + + // sort color + var color = PIXI.hex2rgb(graphicsData.lineColor); + var alpha = graphicsData.lineAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var px, py, p1x, p1y, p2x, p2y, p3x, p3y; + var perpx, perpy, perp2x, perp2y, perp3x, perp3y; + var a1, b1, c1, a2, b2, c2; + var denom, pdist, dist; + + p1x = points[0]; + p1y = points[1]; + + p2x = points[2]; + p2y = points[3]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + // start + verts.push(p1x - perpx , p1y - perpy, + r, g, b, alpha); + + verts.push(p1x + perpx , p1y + perpy, + r, g, b, alpha); + + for (i = 1; i < length-1; i++) + { + p1x = points[(i-1)*2]; + p1y = points[(i-1)*2 + 1]; + + p2x = points[(i)*2]; + p2y = points[(i)*2 + 1]; + + p3x = points[(i+1)*2]; + p3y = points[(i+1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + perp2x = -(p2y - p3y); + perp2y = p2x - p3x; + + dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); + perp2x /= dist; + perp2y /= dist; + perp2x *= width; + perp2y *= width; + + a1 = (-perpy + p1y) - (-perpy + p2y); + b1 = (-perpx + p2x) - (-perpx + p1x); + c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); + a2 = (-perp2y + p3y) - (-perp2y + p2y); + b2 = (-perp2x + p2x) - (-perp2x + p3x); + c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); + + denom = a1*b2 - a2*b1; + + if(Math.abs(denom) < 0.1 ) + { + + denom+=10.1; + verts.push(p2x - perpx , p2y - perpy, + r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy, + r, g, b, alpha); + + continue; + } + + px = (b1*c2 - b2*c1)/denom; + py = (a2*c1 - a1*c2)/denom; + + + pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); + + + if(pdist > 140 * 140) + { + perp3x = perpx - perp2x; + perp3y = perpy - perp2y; + + dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); + perp3x /= dist; + perp3y /= dist; + perp3x *= width; + perp3y *= width; + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x + perp3x, p2y +perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + indexCount++; + } + else + { + + verts.push(px , py); + verts.push(r, g, b, alpha); + + verts.push(p2x - (px-p2x), p2y - (py - p2y)); + verts.push(r, g, b, alpha); + } + } + + p1x = points[(length-2)*2]; + p1y = points[(length-2)*2 + 1]; + + p2x = points[(length-1)*2]; + p2y = points[(length-1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + verts.push(p2x - perpx , p2y - perpy); + verts.push(r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy); + verts.push(r, g, b, alpha); + + indices.push(indexStart); + + for (i = 0; i < indexCount; i++) + { + indices.push(indexStart++); + } + + indices.push(indexStart-1); +}; + +/** + * Builds a polygon to draw + * + * @static + * @private + * @method buildPoly + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) +{ + var points = graphicsData.points; + if(points.length < 6)return; + + // get first and last point.. figure out the middle! + var verts = webGLData.points; + var indices = webGLData.indices; + + var length = points.length / 2; + + // sort color + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var triangles = PIXI.PolyK.Triangulate(points); + + var vertPos = verts.length / 6; + + var i = 0; + + for (i = 0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vertPos); + indices.push(triangles[i] + vertPos); + indices.push(triangles[i+1] + vertPos); + indices.push(triangles[i+2] +vertPos); + indices.push(triangles[i+2] + vertPos); + } + + for (i = 0; i < length; i++) + { + verts.push(points[i * 2], points[i * 2 + 1], + r, g, b, alpha); + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.glContexts = []; // this is where we store the webGL contexts for easy access. + +/** + * the WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * should be used for browsers that support webGL. This Render works by automatically managing webGLBatch's. + * So no need for Sprite Batch's or Sprite Cloud's + * Dont forget to add the view to your DOM or you will not see anything :) + * + * @class WebGLRenderer + * @constructor + * @param width=0 {Number} the width of the canvas view + * @param height=0 {Number} the height of the canvas view + * @param view {HTMLCanvasElement} the canvas to use as a view, optional + * @param transparent=false {Boolean} If the render view is transparent, default false + * @param antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) + * + */ +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) +{ + if(!PIXI.defaultRenderer)PIXI.defaultRenderer = this; + + this.type = PIXI.WEBGL_RENDERER; + + // do a catch.. only 1 webGL renderer.. + /** + * Whether the render view is transparent + * + * @property transparent + * @type Boolean + */ + this.transparent = !!transparent; + + /** + * The width of the canvas view + * + * @property width + * @type Number + * @default 800 + */ + this.width = width || 800; + + /** + * The height of the canvas view + * + * @property height + * @type Number + * @default 600 + */ + this.height = height || 600; + + /** + * The canvas element that everything is drawn to + * + * @property view + * @type HTMLCanvasElement + */ + this.view = view || document.createElement( 'canvas' ); + this.view.width = this.width; + this.view.height = this.height; + + // deal with losing context.. + this.contextLost = this.handleContextLost.bind(this); + this.contextRestoredLost = this.handleContextRestored.bind(this); + + this.view.addEventListener('webglcontextlost', this.contextLost, false); + this.view.addEventListener('webglcontextrestored', this.contextRestoredLost, false); + + this.options = { + alpha: this.transparent, + antialias:!!antialias, // SPEED UP?? + premultipliedAlpha:!!transparent, + stencil:true + }; + + //try 'experimental-webgl' + try { + this.gl = this.view.getContext('experimental-webgl', this.options); + } catch (e) { + //try 'webgl' + try { + this.gl = this.view.getContext('webgl', this.options); + } catch (e2) { + // fail, not able to get a context + throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); + } + } + + var gl = this.gl; + this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++; + + PIXI.glContexts[this.glContextId] = gl; + + if(!PIXI.blendModesWebGL) + { + PIXI.blendModesWebGL = []; + + PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + PIXI.blendModesWebGL[PIXI.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + } + + + + + this.projection = new PIXI.Point(); + this.projection.x = this.width/2; + this.projection.y = -this.height/2; + + this.offset = new PIXI.Point(0, 0); + + this.resize(this.width, this.height); + this.contextLost = false; + + // time to create the render managers! each one focuses on managine a state in webGL + this.shaderManager = new PIXI.WebGLShaderManager(gl); // deals with managing the shader programs and their attribs + this.spriteBatch = new PIXI.WebGLSpriteBatch(gl); // manages the rendering of sprites + this.maskManager = new PIXI.WebGLMaskManager(gl); // manages the masks using the stencil buffer + this.filterManager = new PIXI.WebGLFilterManager(gl, this.transparent); // manages the filters + + this.renderSession = {}; + this.renderSession.gl = this.gl; + this.renderSession.drawCount = 0; + this.renderSession.shaderManager = this.shaderManager; + this.renderSession.maskManager = this.maskManager; + this.renderSession.filterManager = this.filterManager; + this.renderSession.spriteBatch = this.spriteBatch; + + + gl.useProgram(this.shaderManager.defaultShader.program); + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + + gl.enable(gl.BLEND); + gl.colorMask(true, true, true, this.transparent); +}; + +// constructor +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; + +/** + * Renders the stage to its webGL view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.WebGLRenderer.prototype.render = function(stage) +{ + if(this.contextLost)return; + + + // if rendering a new stage clear the batches.. + if(this.__stage !== stage) + { + if(stage.interactive)stage.interactionManager.removeEvents(); + + // TODO make this work + // dont think this is needed any more? + this.__stage = stage; + } + + // update any textures this includes uvs and uploading them to the gpu + PIXI.WebGLRenderer.updateTextures(); + + // update the scene graph + stage.updateTransform(); + + var gl = this.gl; + + // -- Does this need to be set every frame? -- // + //gl.colorMask(true, true, true, this.transparent); + gl.viewport(0, 0, this.width, this.height); + + // make sure we are bound to the main frame buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + if(this.transparent) + { + gl.clearColor(0, 0, 0, 0); + } + else + { + gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + } + + + gl.clear(gl.COLOR_BUFFER_BIT); + + this.renderDisplayObject( stage, this.projection ); + + // interaction + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } + else + { + if(stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = false; + stage.interactionManager.setTarget(this); + } + } + + /* + //can simulate context loss in Chrome like so: + this.view.onmousedown = function(ev) { + console.dir(this.gl.getSupportedExtensions()); + var ext = ( + gl.getExtension("WEBGL_scompressed_texture_s3tc") + // gl.getExtension("WEBGL_compressed_texture_s3tc") || + // gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || + // gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") + ); + console.dir(ext); + var loseCtx = this.gl.getExtension("WEBGL_lose_context"); + console.log("killing context"); + loseCtx.loseContext(); + setTimeout(function() { + console.log("restoring context..."); + loseCtx.restoreContext(); + }.bind(this), 1000); + }.bind(this); + */ +}; + +/** + * Renders a display Object + * + * @method renderDIsplayObject + * @param displayObject {DisplayObject} The DisplayObject to render + * @param projection {Point} The projection + * @param buffer {Array} a standard WebGL buffer + */ +PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer) +{ + // reset the render session data.. + this.renderSession.drawCount = 0; + this.renderSession.currentBlendMode = 9999; + + this.renderSession.projection = projection; + this.renderSession.offset = this.offset; + + // start the sprite batch + this.spriteBatch.begin(this.renderSession); + + // start the filter manager + this.filterManager.begin(this.renderSession, buffer); + + // render the scene! + displayObject._renderWebGL(this.renderSession); + + // finish the sprite batch + this.spriteBatch.end(); +}; + +/** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures + * @private + */ +PIXI.WebGLRenderer.updateTextures = function() +{ + var i = 0; + + //TODO break this out into a texture manager... + //for (i = 0; i < PIXI.texturesToUpdate.length; i++) + // PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + + + for (i=0; i < PIXI.Texture.frameUpdates.length; i++) + PIXI.WebGLRenderer.updateTextureFrame(PIXI.Texture.frameUpdates[i]); + + for (i = 0; i < PIXI.texturesToDestroy.length; i++) + PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); + + PIXI.texturesToUpdate.length = 0; + PIXI.texturesToDestroy.length = 0; + PIXI.Texture.frameUpdates.length = 0; +}; + +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) +{ + //TODO break this out into a texture manager... + + for (var i = texture._glTextures.length - 1; i >= 0; i--) + { + var glTexture = texture._glTextures[i]; + var gl = PIXI.glContexts[i]; + + if(gl && glTexture) + { + gl.deleteTexture(glTexture); + } + } + + texture._glTextures.length = 0; +}; + +/** + * + * @method updateTextureFrame + * @param texture {Texture} The texture to update the frame from + * @private + */ +PIXI.WebGLRenderer.updateTextureFrame = function(texture) +{ + texture.updateFrame = false; + + // now set the uvs. Figured that the uv data sits with a texture rather than a sprite. + // so uv data is stored on the texture itself + texture._updateWebGLuvs(); +}; + +/** + * resizes the webGL view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the webGL view + * @param height {Number} the new height of the webGL view + */ +PIXI.WebGLRenderer.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.view.width = width; + this.view.height = height; + + this.gl.viewport(0, 0, this.width, this.height); + + this.projection.x = this.width/2; + this.projection.y = -this.height/2; +}; + +/** + * Creates a WebGL texture + * + * @method createWebGLTexture + * @param texture {Texture} the texture to render + * @param gl {webglContext} the WebGL context + * @static + */ +PIXI.createWebGLTexture = function(texture, gl) +{ + + + if(texture.hasLoaded) + { + texture._glTextures[gl.id] = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + + // reguler... + + if(!texture._powerOf2) + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + } + + return texture._glTextures[gl.id]; +}; + +/** + * Updates a WebGL texture + * + * @method updateWebGLTexture + * @param texture {Texture} the texture to update + * @param gl {webglContext} the WebGL context + * @private + */ +PIXI.updateWebGLTexture = function(texture, gl) +{ + if( texture._glTextures[gl.id] ) + { + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + + // reguler... + + if(!texture._powerOf2) + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + } + +}; + +/** + * Handles a lost webgl context + * + * @method handleContextLost + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextLost = function(event) +{ + event.preventDefault(); + this.contextLost = true; +}; + +/** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextRestored = function() +{ + + //try 'experimental-webgl' + try { + this.gl = this.view.getContext('experimental-webgl', this.options); + } catch (e) { + //try 'webgl' + try { + this.gl = this.view.getContext('webgl', this.options); + } catch (e2) { + // fail, not able to get a context + throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); + } + } + + var gl = this.gl; + gl.id = PIXI.WebGLRenderer.glContextId ++; + + + + // need to set the context... + this.shaderManager.setContext(gl); + this.spriteBatch.setContext(gl); + this.maskManager.setContext(gl); + this.filterManager.setContext(gl); + + + this.renderSession.gl = this.gl; + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + + gl.enable(gl.BLEND); + gl.colorMask(true, true, true, this.transparent); + + this.gl.viewport(0, 0, this.width, this.height); + + for(var key in PIXI.TextureCache) + { + var texture = PIXI.TextureCache[key].baseTexture; + texture._glTextures = []; + } + + /** + * Whether the context was lost + * @property contextLost + * @type Boolean + */ + this.contextLost = false; + +}; + +/** + * Removes everything from the renderer (event listeners, spritebatch, etc...) + * + * @method destroy + */ +PIXI.WebGLRenderer.prototype.destroy = function() +{ + + // deal with losing context.. + + // remove listeners + this.view.removeEventListener('webglcontextlost', this.contextLost); + this.view.removeEventListener('webglcontextrestored', this.contextRestoredLost); + + PIXI.glContexts[this.glContextId] = null; + + this.projection = null; + this.offset = null; + + // time to create the render managers! each one focuses on managine a state in webGL + this.shaderManager.destroy(); + this.spriteBatch.destroy(); + this.maskManager.destroy(); + this.filterManager.destroy(); + + this.shaderManager = null; + this.spriteBatch = null; + this.maskManager = null; + this.filterManager = null; + + this.gl = null; + // + this.renderSession = null; +}; + + +PIXI.WebGLRenderer.glContextId = 0; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** +* @class WebGLMaskManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @private +*/ +PIXI.WebGLMaskManager = function(gl) +{ + this.maskStack = []; + this.maskPosition = 0; + + this.setContext(gl); +}; + +/** +* Sets the drawing context to the one given in parameter +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLMaskManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +/** +* Applies the Mask and adds it to the current filter stack +* @method pushMask +* @param maskData {Array} +* @param renderSession {RenderSession} +*/ +PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession) +{ + var gl = this.gl; + + if(this.maskStack.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS,1,1); + } + + // maskData.visible = false; + + this.maskStack.push(maskData); + + gl.colorMask(false, false, false, true); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + + PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL,0, this.maskStack.length); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); +}; + +/** +* Removes the last filter from the filter stack and doesn't return it +* @method popMask +* +* @param renderSession {RenderSession} an object containing all the useful parameters +*/ +PIXI.WebGLMaskManager.prototype.popMask = function(renderSession) +{ + var gl = this.gl; + + var maskData = this.maskStack.pop(); + + if(maskData) + { + gl.colorMask(false, false, false, false); + + //gl.stencilFunc(gl.ALWAYS,1,1); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + + PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL,0,this.maskStack.length); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + } + + if(this.maskStack.length === 0)gl.disable(gl.STENCIL_TEST); +}; + +/** +* Destroys the mask stack +* @method destroy +*/ +PIXI.WebGLMaskManager.prototype.destroy = function() +{ + this.maskStack = null; + this.gl = null; +}; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLShaderManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @private +*/ +PIXI.WebGLShaderManager = function(gl) +{ + + this.maxAttibs = 10; + this.attribState = []; + this.tempAttribState = []; + + for (var i = 0; i < this.maxAttibs; i++) { + this.attribState[i] = false; + } + + this.setContext(gl); + // the final one is used for the rendering strips + //this.stripShader = new PIXI.StripShader(gl); +}; + + +/** +* Initialises the context and the properties +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +* @param transparent {Boolean} Whether or not the drawing context should be transparent +*/ +PIXI.WebGLShaderManager.prototype.setContext = function(gl) +{ + this.gl = gl; + + // the next one is used for rendering primatives + this.primitiveShader = new PIXI.PrimitiveShader(gl); + + // this shader is used for the default sprite rendering + this.defaultShader = new PIXI.PixiShader(gl); + + // this shader is used for the fast sprite rendering + this.fastShader = new PIXI.PixiFastShader(gl); + + + this.activateShader(this.defaultShader); +}; + + +/** +* Takes the attributes given in parameters +* @method setAttribs +* @param attribs {Array} attribs +*/ +PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs) +{ + // reset temp state + + var i; + + for (i = 0; i < this.tempAttribState.length; i++) + { + this.tempAttribState[i] = false; + } + + // set the new attribs + for (i = 0; i < attribs.length; i++) + { + var attribId = attribs[i]; + this.tempAttribState[attribId] = true; + } + + var gl = this.gl; + + for (i = 0; i < this.attribState.length; i++) + { + + if(this.attribState[i] !== this.tempAttribState[i]) + { + this.attribState[i] = this.tempAttribState[i]; + + if(this.tempAttribState[i]) + { + gl.enableVertexAttribArray(i); + } + else + { + gl.disableVertexAttribArray(i); + } + } + } +}; + +/** +* Sets-up the given shader +* +* @method activateShader +* @param shader {Object} the shader that is going to be activated +*/ +PIXI.WebGLShaderManager.prototype.activateShader = function(shader) +{ + //if(this.currentShader == shader)return; + + this.currentShader = shader; + + this.gl.useProgram(shader.program); + this.setAttribs(shader.attributes); + +}; + +/** +* Triggers the primitive shader +* @method activatePrimitiveShader +*/ +PIXI.WebGLShaderManager.prototype.activatePrimitiveShader = function() +{ + var gl = this.gl; + + gl.useProgram(this.primitiveShader.program); + + this.setAttribs(this.primitiveShader.attributes); + +}; + +/** +* Disable the primitive shader +* @method deactivatePrimitiveShader +*/ +PIXI.WebGLShaderManager.prototype.deactivatePrimitiveShader = function() +{ + var gl = this.gl; + + gl.useProgram(this.defaultShader.program); + + this.setAttribs(this.defaultShader.attributes); +}; + +/** +* Destroys +* @method destroy +*/ +PIXI.WebGLShaderManager.prototype.destroy = function() +{ + this.attribState = null; + + this.tempAttribState = null; + + this.primitiveShader.destroy(); + + this.defaultShader.destroy(); + + this.fastShader.destroy(); + + this.gl = null; +}; + + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * + * Heavily inspired by LibGDX's WebGLSpriteBatch: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java + */ + + /** + * + * @class WebGLSpriteBatch + * @private + * @constructor + * @param gl {WebGLContext} the current WebGL drawing context + * + */ +PIXI.WebGLSpriteBatch = function(gl) +{ + + /** + * + * + * @property vertSize + * @type Number + */ + this.vertSize = 6; + + /** + * The number of images in the SpriteBatch before it flushes + * @property size + * @type Number + */ + this.size = 10000;//Math.pow(2, 16) / this.vertSize; + + //the total number of floats in our batch + var numVerts = this.size * 4 * this.vertSize; + //the total number of indices in our batch + var numIndices = this.size * 6; + + //vertex data + + /** + * Holds the vertices + * + * @property vertices + * @type Float32Array + */ + this.vertices = new Float32Array(numVerts); + + //index data + /** + * Holds the indices + * + * @property indices + * @type Uint16Array + */ + this.indices = new Uint16Array(numIndices); + + this.lastIndexCount = 0; + + for (var i=0, j=0; i < numIndices; i += 6, j += 4) + { + this.indices[i + 0] = j + 0; + this.indices[i + 1] = j + 1; + this.indices[i + 2] = j + 2; + this.indices[i + 3] = j + 0; + this.indices[i + 4] = j + 2; + this.indices[i + 5] = j + 3; + } + + + this.drawing = false; + this.currentBatchSize = 0; + this.currentBaseTexture = null; + + this.setContext(gl); +}; + +/** +* +* @method setContext +* +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) +{ + this.gl = gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // 65535 is max index, so 65535 / 6 = 10922. + + + //upload the index data + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + this.currentBlendMode = 99999; +}; + +/** +* +* @method begin +* +* @param renderSession {RenderSession} the RenderSession +*/ +PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession) +{ + this.renderSession = renderSession; + this.shader = this.renderSession.shaderManager.defaultShader; + + this.start(); +}; + +/** +* +* @method end +* +*/ +PIXI.WebGLSpriteBatch.prototype.end = function() +{ + this.flush(); +}; + +/** +* +* @method render +* +* @param sprite {Sprite} the sprite to render when using this spritebatch +*/ +PIXI.WebGLSpriteBatch.prototype.render = function(sprite) +{ + // check texture.. + if(sprite.texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + { + this.flush(); + this.currentBaseTexture = sprite.texture.baseTexture; + } + + + // check blend mode + if(sprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(sprite.blendMode); + } + + // get the uvs for the texture + var uvs = sprite._uvs || sprite.texture._uvs; + // if the uvs have not updated then no point rendering just yet! + if(!uvs)return; + + // get the sprites current alpha + var alpha = sprite.worldAlpha; + var tint = sprite.tint; + + var verticies = this.vertices; + + var width = sprite.texture.frame.width; + var height = sprite.texture.frame.height; + + // TODO trim?? + var aX = sprite.anchor.x; + var aY = sprite.anchor.y; + + var w0, w1, h0, h1; + + if (sprite.texture.trim) + { + // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. + var trim = sprite.texture.trim; + + w1 = trim.x - aX * trim.width; + w0 = w1 + width; + + h1 = trim.y - aY * trim.height; + h0 = h1 + height; + } + else + { + w0 = (width ) * (1-aX); + w1 = (width ) * -aX; + + h0 = height * (1-aY); + h1 = height * -aY; + } + + var index = this.currentBatchSize * 4 * this.vertSize; + + var worldTransform = sprite.worldTransform;//.toArray(); + + var a = worldTransform.a;//[0]; + var b = worldTransform.c;//[3]; + var c = worldTransform.b;//[1]; + var d = worldTransform.d;//[4]; + var tx = worldTransform.tx;//[2]; + var ty = worldTransform.ty;///[5]; + + // xy + verticies[index++] = a * w1 + c * h1 + tx; + verticies[index++] = d * h1 + b * w1 + ty; + // uv + verticies[index++] = uvs.x0; + verticies[index++] = uvs.y0; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h1 + tx; + verticies[index++] = d * h1 + b * w0 + ty; + // uv + verticies[index++] = uvs.x1; + verticies[index++] = uvs.y1; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h0 + tx; + verticies[index++] = d * h0 + b * w0 + ty; + // uv + verticies[index++] = uvs.x2; + verticies[index++] = uvs.y2; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w1 + c * h0 + tx; + verticies[index++] = d * h0 + b * w1 + ty; + // uv + verticies[index++] = uvs.x3; + verticies[index++] = uvs.y3; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // increment the batchsize + this.currentBatchSize++; + + +}; + +/** +* Renders a tilingSprite using the spriteBatch +* @method renderTilingSprite +* +* @param sprite {TilingSprite} the tilingSprite to render +*/ +PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) +{ + var texture = tilingSprite.tilingTexture; + + if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + { + this.flush(); + this.currentBaseTexture = texture.baseTexture; + } + + // check blend mode + if(tilingSprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(tilingSprite.blendMode); + } + + // set the textures uvs temporarily + // TODO create a separate texture so that we can tile part of a texture + + if(!tilingSprite._uvs)tilingSprite._uvs = new PIXI.TextureUvs(); + + var uvs = tilingSprite._uvs; + + tilingSprite.tilePosition.x %= texture.baseTexture.width; + tilingSprite.tilePosition.y %= texture.baseTexture.height; + + var offsetX = tilingSprite.tilePosition.x/texture.baseTexture.width; + var offsetY = tilingSprite.tilePosition.y/texture.baseTexture.height; + + var scaleX = (tilingSprite.width / texture.baseTexture.width) / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x); + var scaleY = (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y); + + uvs.x0 = 0 - offsetX; + uvs.y0 = 0 - offsetY; + + uvs.x1 = (1 * scaleX) - offsetX; + uvs.y1 = 0 - offsetY; + + uvs.x2 = (1 * scaleX) - offsetX; + uvs.y2 = (1 * scaleY) - offsetY; + + uvs.x3 = 0 - offsetX; + uvs.y3 = (1 *scaleY) - offsetY; + + // get the tilingSprites current alpha + var alpha = tilingSprite.worldAlpha; + var tint = tilingSprite.tint; + + var verticies = this.vertices; + + var width = tilingSprite.width; + var height = tilingSprite.height; + + // TODO trim?? + var aX = tilingSprite.anchor.x; // - tilingSprite.texture.trim.x + var aY = tilingSprite.anchor.y; //- tilingSprite.texture.trim.y + var w0 = width * (1-aX); + var w1 = width * -aX; + + var h0 = height * (1-aY); + var h1 = height * -aY; + + var index = this.currentBatchSize * 4 * this.vertSize; + + var worldTransform = tilingSprite.worldTransform; + + var a = worldTransform.a;//[0]; + var b = worldTransform.c;//[3]; + var c = worldTransform.b;//[1]; + var d = worldTransform.d;//[4]; + var tx = worldTransform.tx;//[2]; + var ty = worldTransform.ty;///[5]; + + // xy + verticies[index++] = a * w1 + c * h1 + tx; + verticies[index++] = d * h1 + b * w1 + ty; + // uv + verticies[index++] = uvs.x0; + verticies[index++] = uvs.y0; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h1 + tx; + verticies[index++] = d * h1 + b * w0 + ty; + // uv + verticies[index++] = uvs.x1; + verticies[index++] = uvs.y1; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h0 + tx; + verticies[index++] = d * h0 + b * w0 + ty; + // uv + verticies[index++] = uvs.x2; + verticies[index++] = uvs.y2; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w1 + c * h0 + tx; + verticies[index++] = d * h0 + b * w1 + ty; + // uv + verticies[index++] = uvs.x3; + verticies[index++] = uvs.y3; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // increment the batchs + this.currentBatchSize++; +}; + + +/** +* Renders the content and empties the current batch +* +* @method flush +* +*/ +PIXI.WebGLSpriteBatch.prototype.flush = function() +{ + // If the batch is length 0 then return as there is nothing to draw + if (this.currentBatchSize===0)return; + + var gl = this.gl; + + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id] || PIXI.createWebGLTexture(this.currentBaseTexture, gl)); + + // upload the verts to the buffer + + if(this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + } + else + { + var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } + + // var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + //gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); + + // then reset the batch! + this.currentBatchSize = 0; + + // increment the draw count + this.renderSession.drawCount++; +}; + +/** +* +* @method stop +* +*/ +PIXI.WebGLSpriteBatch.prototype.stop = function() +{ + this.flush(); +}; + +/** +* +* @method start +* +*/ +PIXI.WebGLSpriteBatch.prototype.start = function() +{ + var gl = this.gl; + + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); + + // set the pointers + var stride = this.vertSize * 4; + gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); + gl.vertexAttribPointer(this.shader.colorAttribute, 2, gl.FLOAT, false, stride, 4 * 4); + + // set the blend mode.. + if(this.currentBlendMode !== PIXI.blendModes.NORMAL) + { + this.setBlendMode(PIXI.blendModes.NORMAL); + } +}; + +/** +* Sets-up the given blendMode from WebGL's point of view +* @method setBlendMode +* +* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD +*/ +PIXI.WebGLSpriteBatch.prototype.setBlendMode = function(blendMode) +{ + this.flush(); + + this.currentBlendMode = blendMode; + + var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; + this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); +}; + +/** +* Destroys the SpriteBatch +* @method destroy +*/ +PIXI.WebGLSpriteBatch.prototype.destroy = function() +{ + + this.vertices = null; + this.indices = null; + + this.gl.deleteBuffer( this.vertexBuffer ); + this.gl.deleteBuffer( this.indexBuffer ); + + this.currentBaseTexture = null; + + this.gl = null; +}; + + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * + * Heavily inspired by LibGDX's WebGLSpriteBatch: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java + */ + +PIXI.WebGLFastSpriteBatch = function(gl) +{ + + + this.vertSize = 10; + this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize; + this.size = this.maxSize; + + //the total number of floats in our batch + var numVerts = this.size * 4 * this.vertSize; + //the total number of indices in our batch + var numIndices = this.maxSize * 6; + + //vertex data + this.vertices = new Float32Array(numVerts); + //index data + this.indices = new Uint16Array(numIndices); + + this.vertexBuffer = null; + this.indexBuffer = null; + + this.lastIndexCount = 0; + + for (var i=0, j=0; i < numIndices; i += 6, j += 4) + { + this.indices[i + 0] = j + 0; + this.indices[i + 1] = j + 1; + this.indices[i + 2] = j + 2; + this.indices[i + 3] = j + 0; + this.indices[i + 4] = j + 2; + this.indices[i + 5] = j + 3; + } + + this.drawing = false; + this.currentBatchSize = 0; + this.currentBaseTexture = null; + + this.currentBlendMode = 0; + this.renderSession = null; + + + this.shader = null; + + this.matrix = null; + + this.setContext(gl); +}; + +PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl) +{ + this.gl = gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // 65535 is max index, so 65535 / 6 = 10922. + + + //upload the index data + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + this.currentBlendMode = 99999; +}; + +PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession) +{ + this.renderSession = renderSession; + this.shader = this.renderSession.shaderManager.fastShader; + + this.matrix = spriteBatch.worldTransform.toArray(true); + + this.start(); +}; + +PIXI.WebGLFastSpriteBatch.prototype.end = function() +{ + this.flush(); +}; + + +PIXI.WebGLFastSpriteBatch.prototype.render = function(spriteBatch) +{ + + var children = spriteBatch.children; + var sprite = children[0]; + + // if the uvs have not updated then no point rendering just yet! + + // check texture. + if(!sprite.texture._uvs)return; + + this.currentBaseTexture = sprite.texture.baseTexture; + // check blend mode + if(sprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(sprite.blendMode); + } + + for(var i=0,j= children.length; i= this.size) + { + this.flush(); + } +}; + +PIXI.WebGLFastSpriteBatch.prototype.flush = function() +{ + + // If the batch is length 0 then return as there is nothing to draw + if (this.currentBatchSize===0)return; + + var gl = this.gl; + + // bind the current texture + + if(!this.currentBaseTexture._glTextures[gl.id])PIXI.createWebGLTexture(this.currentBaseTexture, gl); + + gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id]);// || PIXI.createWebGLTexture(this.currentBaseTexture, gl)); + + // upload the verts to the buffer + + + if(this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + } + else + { + var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } + + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); + + // then reset the batch! + this.currentBatchSize = 0; + + // increment the draw count + this.renderSession.drawCount++; +}; + + +PIXI.WebGLFastSpriteBatch.prototype.stop = function() +{ + this.flush(); +}; + +PIXI.WebGLFastSpriteBatch.prototype.start = function() +{ + var gl = this.gl; + + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); + + // set the matrix + gl.uniformMatrix3fv(this.shader.uMatrix, false, this.matrix); + + // set the pointers + var stride = this.vertSize * 4; + + gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(this.shader.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4); + gl.vertexAttribPointer(this.shader.aScale, 2, gl.FLOAT, false, stride, 4 * 4); + gl.vertexAttribPointer(this.shader.aRotation, 1, gl.FLOAT, false, stride, 6 * 4); + gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4); + gl.vertexAttribPointer(this.shader.colorAttribute, 1, gl.FLOAT, false, stride, 9 * 4); + + // set the blend mode.. + if(this.currentBlendMode !== PIXI.blendModes.NORMAL) + { + this.setBlendMode(PIXI.blendModes.NORMAL); + } +}; + +PIXI.WebGLFastSpriteBatch.prototype.setBlendMode = function(blendMode) +{ + this.flush(); + + this.currentBlendMode = blendMode; + + var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; + this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); +}; + + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLFilterManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @param transparent {Boolean} Whether or not the drawing context should be transparent +* @private +*/ +PIXI.WebGLFilterManager = function(gl, transparent) +{ + this.transparent = transparent; + + this.filterStack = []; + + this.offsetX = 0; + this.offsetY = 0; + + this.setContext(gl); +}; + +// API +/** +* Initialises the context and the properties +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLFilterManager.prototype.setContext = function(gl) +{ + this.gl = gl; + this.texturePool = []; + + this.initShaderBuffers(); +}; + +/** +* +* @method begin +* @param renderSession {RenderSession} +* @param buffer {ArrayBuffer} +*/ +PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer) +{ + this.renderSession = renderSession; + this.defaultShader = renderSession.shaderManager.defaultShader; + + var projection = this.renderSession.projection; + + this.width = projection.x * 2; + this.height = -projection.y * 2; + this.buffer = buffer; +}; + +/** +* Applies the filter and adds it to the current filter stack +* @method pushFilter +* @param filterBlock {Object} the filter that will be pushed to the current filter stack +*/ +PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) +{ + var gl = this.gl; + + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; + + + // filter program + // OPTIMISATION - the first filter is free if its a simple color change? + this.filterStack.push(filterBlock); + + var filter = filterBlock.filterPasses[0]; + + this.offsetX += filterBlock.target.filterArea.x; + this.offsetY += filterBlock.target.filterArea.y; + + var texture = this.texturePool.pop(); + if(!texture) + { + texture = new PIXI.FilterTexture(this.gl, this.width, this.height); + } + else + { + texture.resize(this.width, this.height); + } + + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + filterBlock.target.filterArea = filterBlock.target.getBounds(); + + var filterArea = filterBlock.target.filterArea; + + var padidng = filter.padding; + filterArea.x -= padidng; + filterArea.y -= padidng; + filterArea.width += padidng * 2; + filterArea.height += padidng * 2; + + // cap filter to screen size.. + if(filterArea.x < 0)filterArea.x = 0; + if(filterArea.width > this.width)filterArea.width = this.width; + if(filterArea.y < 0)filterArea.y = 0; + if(filterArea.height > this.height)filterArea.height = this.height; + + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + + // set view port + gl.viewport(0, 0, filterArea.width, filterArea.height); + + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; + + offset.x = -filterArea.x; + offset.y = -filterArea.y; + + // update projection + gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + + gl.colorMask(true, true, true, true); + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + filterBlock._glFilterTexture = texture; + +}; + + +/** +* Removes the last filter from the filter stack and doesn't return it +* @method popFilter +*/ +PIXI.WebGLFilterManager.prototype.popFilter = function() +{ + var gl = this.gl; + var filterBlock = this.filterStack.pop(); + var filterArea = filterBlock.target.filterArea; + var texture = filterBlock._glFilterTexture; + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; + + if(filterBlock.filterPasses.length > 1) + { + gl.viewport(0, 0, filterArea.width, filterArea.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = 0; + this.vertexArray[1] = filterArea.height; + + this.vertexArray[2] = filterArea.width; + this.vertexArray[3] = filterArea.height; + + this.vertexArray[4] = 0; + this.vertexArray[5] = 0; + + this.vertexArray[6] = filterArea.width; + this.vertexArray[7] = 0; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + // now set the uvs.. + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + var inputTexture = texture; + var outputTexture = this.texturePool.pop(); + if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height); + + // need to clear this FBO as it may have some left over elements from a previous filter. + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + for (var i = 0; i < filterBlock.filterPasses.length-1; i++) + { + var filterPass = filterBlock.filterPasses[i]; + + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); + + // draw texture.. + //filterPass.applyFilterPass(filterArea.width, filterArea.height); + this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); + + // swap the textures.. + var temp = inputTexture; + inputTexture = outputTexture; + outputTexture = temp; + } + + gl.enable(gl.BLEND); + + texture = inputTexture; + this.texturePool.push(outputTexture); + } + + var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; + + this.offsetX -= filterArea.x; + this.offsetY -= filterArea.y; + + + var sizeX = this.width; + var sizeY = this.height; + + var offsetX = 0; + var offsetY = 0; + + var buffer = this.buffer; + + // time to render the filters texture to the previous scene + if(this.filterStack.length === 0) + { + gl.colorMask(true, true, true, this.transparent); + } + else + { + var currentFilter = this.filterStack[this.filterStack.length-1]; + filterArea = currentFilter.target.filterArea; + + sizeX = filterArea.width; + sizeY = filterArea.height; + + offsetX = filterArea.x; + offsetY = filterArea.y; + + buffer = currentFilter._glFilterTexture.frameBuffer; + } + + + + // TODO need toremove thease global elements.. + projection.x = sizeX/2; + projection.y = -sizeY/2; + + offset.x = offsetX; + offset.y = offsetY; + + filterArea = filterBlock.target.filterArea; + + var x = filterArea.x-offsetX; + var y = filterArea.y-offsetY; + + // update the buffers.. + // make sure to flip the y! + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = x; + this.vertexArray[1] = y + filterArea.height; + + this.vertexArray[2] = x + filterArea.width; + this.vertexArray[3] = y + filterArea.height; + + this.vertexArray[4] = x; + this.vertexArray[5] = y; + + this.vertexArray[6] = x + filterArea.width; + this.vertexArray[7] = y; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + gl.viewport(0, 0, sizeX, sizeY); + // bind the buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + // apply! + this.applyFilterPass(filter, filterArea, sizeX, sizeY); + + // now restore the regular shader.. + gl.useProgram(this.defaultShader.program); + gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + + // return the texture to the pool + this.texturePool.push(texture); + filterBlock._glFilterTexture = null; +}; + + +/** +* Applies the filter to the specified area +* @method applyFilterPass +* @param filter {AbstractFilter} the filter that needs to be applied +* @param filterArea {texture} TODO - might need an update +* @param width {Number} the horizontal range of the filter +* @param height {Number} the vertical range of the filter +*/ +PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height) +{ + // use program + var gl = this.gl; + var shader = filter.shaders[gl.id]; + + if(!shader) + { + shader = new PIXI.PixiShader(gl); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shaders[gl.id] = shader; + } + + // set the shader + gl.useProgram(shader.program); + + gl.uniform2f(shader.projectionVector, width/2, -height/2); + gl.uniform2f(shader.offsetVector, 0,0); + + if(filter.uniforms.dimensions) + { + filter.uniforms.dimensions.value[0] = this.width;//width; + filter.uniforms.dimensions.value[1] = this.height;//height; + filter.uniforms.dimensions.value[2] = this.vertexArray[0]; + filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + } + + shader.syncUniforms(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderSession.drawCount++; +}; + +/** +* Initialises the shader buffers +* @method initShaderBuffers +*/ +PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() +{ + var gl = this.gl; + + // create some buffers + this.vertexBuffer = gl.createBuffer(); + this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + + // bind and upload the vertexs.. + // keep a reference to the vertexFloatData.. + this.vertexArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + this.vertexArray, + gl.STATIC_DRAW); + + + // bind and upload the uv buffer + this.uvArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + this.uvArray, + gl.STATIC_DRAW); + + this.colorArray = new Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + this.colorArray, + gl.STATIC_DRAW); + + // bind and upload the index + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array([0, 1, 2, 1, 3, 2]), + gl.STATIC_DRAW); +}; + +/** +* Destroys the filter and removes it from the filter stack +* @method destroy +*/ +PIXI.WebGLFilterManager.prototype.destroy = function() +{ + var gl = this.gl; + + this.filterStack = null; + + this.offsetX = 0; + this.offsetY = 0; + + // destroy textures + for (var i = 0; i < this.texturePool.length; i++) { + this.texturePool.destroy(); + } + + this.texturePool = null; + + //destroy buffers.. + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.uvBuffer); + gl.deleteBuffer(this.colorBuffer); + gl.deleteBuffer(this.indexBuffer); +}; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class FilterTexture +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @param width {Number} the horizontal range of the filter +* @param height {Number} the vertical range of the filter +* @private +*/ +PIXI.FilterTexture = function(gl, width, height) +{ + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + // next time to create a frame buffer and texture + this.frameBuffer = gl.createFramebuffer(); + this.texture = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); + + this.resize(width, height); +}; + + +/** +* Clears the filter texture +* @method clear +*/ +PIXI.FilterTexture.prototype.clear = function() +{ + var gl = this.gl; + + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); +}; + +/** + * Resizes the texture to the specified width and height + * + * @method resize + * @param width {Number} the new width of the texture + * @param height {Number} the new height of the texture + */ +PIXI.FilterTexture.prototype.resize = function(width, height) +{ + if(this.width === width && this.height === height) return; + + this.width = width; + this.height = height; + + var gl = this.gl; + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + +}; + +/** +* Destroys the filter texture +* @method destroy +*/ +PIXI.FilterTexture.prototype.destroy = function() +{ + var gl = this.gl; + gl.deleteFramebuffer( this.frameBuffer ); + gl.deleteTexture( this.texture ); + + this.frameBuffer = null; + this.texture = null; +}; + +/** + * @author Mat Groves + * + * + */ +/** + * A set of functions used to handle masking + * + * @class CanvasMaskManager + */ +PIXI.CanvasMaskManager = function() +{ + +}; + +/** + * This method adds it to the current stack of masks + * + * @method pushMask + * @param maskData the maskData that will be pushed + * @param context {Context2D} the 2d drawing method of the canvas + */ +PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, context) +{ + context.save(); + + var cacheAlpha = maskData.alpha; + var transform = maskData.worldTransform; + + context.setTransform(transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty); + + PIXI.CanvasGraphics.renderGraphicsMask(maskData, context); + + context.clip(); + + maskData.worldAlpha = cacheAlpha; +}; + +/** + * Restores the current drawing context to the state it was before the mask was applied + * + * @method popMask + * @param context {Context2D} the 2d drawing method of the canvas + */ +PIXI.CanvasMaskManager.prototype.popMask = function(context) +{ + context.restore(); +}; + +/** + * @author Mat Groves + * + * + */ + +/** + * @class CanvasTinter + * @constructor + * @static + */ +PIXI.CanvasTinter = function() +{ + /// this.textureCach +}; + +//PIXI.CanvasTinter.cachTint = true; + + +/** + * Basically this method just needs a sprite and a color and tints the sprite + * with the given color + * + * @method getTintedTexture + * @param sprite {Sprite} the sprite to tint + * @param color {Number} the color to use to tint the sprite with + */ +PIXI.CanvasTinter.getTintedTexture = function(sprite, color) +{ + + var texture = sprite.texture; + + color = PIXI.CanvasTinter.roundColor(color); + + var stringColor = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + texture.tintCache = texture.tintCache || {}; + + if(texture.tintCache[stringColor]) return texture.tintCache[stringColor]; + + // clone texture.. + var canvas = PIXI.CanvasTinter.canvas || document.createElement("canvas"); + + //PIXI.CanvasTinter.tintWithPerPixel(texture, stringColor, canvas); + + + PIXI.CanvasTinter.tintMethod(texture, color, canvas); + + if(PIXI.CanvasTinter.convertTintToImage) + { + // is this better? + var tintImage = new Image(); + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + PIXI.CanvasTinter.canvas = null; + + } + + return canvas; +}; + +/** + * Tint a texture using the "multiply" operation + * @method tintWithMultiply + * @param texture {texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + context.fillRect(0, 0, frame.width, frame.height); + + context.globalCompositeOperation = "multiply"; + + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + context.globalCompositeOperation = "destination-atop"; + + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); +}; + +/** + * Tint a texture using the "overlay" operation + * @method tintWithOverlay + * @param texture {texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithOverlay = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + + + context.globalCompositeOperation = "copy"; + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + context.fillRect(0, 0, frame.width, frame.height); + + context.globalCompositeOperation = "destination-atop"; + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + + //context.globalCompositeOperation = "copy"; + +}; + +/** + * Tint a texture pixel per pixel + * @method tintPerPixel + * @param texture {texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + context.globalCompositeOperation = "copy"; + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + var rgbValues = PIXI.hex2rgb(color); + var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2]; + + var pixelData = context.getImageData(0, 0, frame.width, frame.height); + + var pixels = pixelData.data; + + for (var i = 0; i < pixels.length; i += 4) + { + pixels[i+0] *= r; + pixels[i+1] *= g; + pixels[i+2] *= b; + } + + context.putImageData(pixelData, 0, 0); +}; + +/** + * Rounds the specified color according to the PIXI.CanvasTinter.cacheStepsPerColorChannel + * @method roundColor + * @param color {number} the color to round, should be a hex color + */ +PIXI.CanvasTinter.roundColor = function(color) +{ + var step = PIXI.CanvasTinter.cacheStepsPerColorChannel; + + var rgbValues = PIXI.hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return PIXI.rgb2hex(rgbValues); +}; + +/** + * + * Number of steps which will be used as a cap when rounding colors + * + * @property cacheStepsPerColorChannel + * @type Number + */ +PIXI.CanvasTinter.cacheStepsPerColorChannel = 8; +/** + * + * Number of steps which will be used as a cap when rounding colors + * + * @property convertTintToImage + * @type Boolean + */ +PIXI.CanvasTinter.convertTintToImage = false; + +/** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method + * + * @property canUseMultiply + * @type Boolean + */ +PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes(); + +PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel; + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * Dont forget to add the view to your DOM or you will not see anything :) + * + * @class CanvasRenderer + * @constructor + * @param width=800 {Number} the width of the canvas view + * @param height=600 {Number} the height of the canvas view + * @param [view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [transparent=false] {Boolean} the transparency of the render view, default false + */ +PIXI.CanvasRenderer = function(width, height, view, transparent) +{ + PIXI.defaultRenderer = PIXI.defaultRenderer || this; + + this.type = PIXI.CANVAS_RENDERER; + + /** + * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. + * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. + * + * @property clearBeforeRender + * @type Boolean + * @default + */ + this.clearBeforeRender = true; + + /** + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @property roundPixels + * @type Boolean + * @default + */ + this.roundPixels = false; + + /** + * Whether the render view is transparent + * + * @property transparent + * @type Boolean + */ + this.transparent = !!transparent; + + if(!PIXI.blendModesCanvas) + { + PIXI.blendModesCanvas = []; + + if(PIXI.canUseNewCanvasBlendModes()) + { + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "screen"; + PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "overlay"; + PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "darken"; + PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "lighten"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn"; + PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light"; + PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light"; + PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference"; + PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion"; + PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "hue"; + PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "color"; + PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity"; + } + else + { + // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough" + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over"; + } + } + + /** + * The width of the canvas view + * + * @property width + * @type Number + * @default 800 + */ + this.width = width || 800; + + /** + * The height of the canvas view + * + * @property height + * @type Number + * @default 600 + */ + this.height = height || 600; + + /** + * The canvas element that everything is drawn to + * + * @property view + * @type HTMLCanvasElement + */ + this.view = view || document.createElement( "canvas" ); + + /** + * The canvas 2d context that everything is drawn with + * @property context + * @type HTMLCanvasElement 2d Context + */ + this.context = this.view.getContext( "2d", { alpha: this.transparent } ); + + this.refresh = true; + // hack to enable some hardware acceleration! + //this.view.style["transform"] = "translatez(0)"; + + this.view.width = this.width; + this.view.height = this.height; + this.count = 0; + + /** + * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer + * @property CanvasMaskManager + * @type CanvasMaskManager + */ + this.maskManager = new PIXI.CanvasMaskManager(); + + /** + * The render session is just a bunch of parameter used for rendering + * @property renderSession + * @type Object + */ + this.renderSession = { + context: this.context, + maskManager: this.maskManager, + scaleMode: null, + smoothProperty: null + }; + + if("imageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "imageSmoothingEnabled"; + else if("webkitImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "webkitImageSmoothingEnabled"; + else if("mozImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "mozImageSmoothingEnabled"; + else if("oImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "oImageSmoothingEnabled"; +}; + +// constructor +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; + +/** + * Renders the stage to its canvas view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.CanvasRenderer.prototype.render = function(stage) +{ + // update textures if need be + PIXI.texturesToUpdate.length = 0; + PIXI.texturesToDestroy.length = 0; + + stage.updateTransform(); + + this.context.setTransform(1,0,0,1,0,0); + this.context.globalAlpha = 1; + + if (!this.transparent && this.clearBeforeRender) + { + this.context.fillStyle = stage.backgroundColorString; + this.context.fillRect(0, 0, this.width, this.height); + } + else if (this.transparent && this.clearBeforeRender) + { + this.context.clearRect(0, 0, this.width, this.height); + } + + this.renderDisplayObject(stage); + + // run interaction! + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } + + // remove frame updates.. + if(PIXI.Texture.frameUpdates.length > 0) + { + PIXI.Texture.frameUpdates.length = 0; + } +}; + +/** + * Resizes the canvas view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the canvas view + * @param height {Number} the new height of the canvas view + */ +PIXI.CanvasRenderer.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.view.width = width; + this.view.height = height; +}; + +/** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render + * @param context {Context2D} the context 2d method of the canvas + * @private + */ +PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) +{ + // no longer recursive! + //var transform; + //var context = this.context; + + this.renderSession.context = context || this.context; + displayObject._renderCanvas(this.renderSession); +}; + +/** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) +{ + var context = this.context; + var verticies = strip.verticies; + + var length = verticies.length/2; + this.count++; + + context.beginPath(); + for (var i=1; i < length-2; i++) + { + // draw some triangles! + var index = i*2; + + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; + var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = "#FF0000"; + context.fill(); + context.closePath(); +}; + +/** + * Renders a strip + * + * @method renderStrip + * @param strip {Strip} The Strip to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderStrip = function(strip) +{ + var context = this.context; + + // draw triangles!! + var verticies = strip.verticies; + var uvs = strip.uvs; + + var length = verticies.length/2; + this.count++; + + for (var i = 1; i < length-2; i++) + { + // draw some triangles! + var index = i*2; + + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; + var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; + + var u0 = uvs[index] * strip.texture.width, u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width; + var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height; + + context.save(); + context.beginPath(); + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + context.closePath(); + + context.clip(); + + // Compute matrix transform + var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2; + var deltaA = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2; + var deltaB = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2; + var deltaC = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2; + var deltaD = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2; + var deltaE = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2; + var deltaF = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2; + + context.transform(deltaA / delta, deltaD / delta, + deltaB / delta, deltaE / delta, + deltaC / delta, deltaF / delta); + + context.drawImage(strip.texture.baseTexture.source, 0, 0); + context.restore(); + } +}; + +/** + * Creates a Canvas element of the given size + * + * @method CanvasBuffer + * @param width {Number} the width for the newly created canvas + * @param height {Number} the height for the newly created canvas + * @static + * @private + */ +PIXI.CanvasBuffer = function(width, height) +{ + this.width = width; + this.height = height; + + this.canvas = document.createElement( "canvas" ); + this.context = this.canvas.getContext( "2d" ); + + this.canvas.width = width; + this.canvas.height = height; +}; + +/** + * Clears the canvas that was created by the CanvasBuffer class + * + * @method clear + * @private + */ +PIXI.CanvasBuffer.prototype.clear = function() +{ + this.context.clearRect(0,0, this.width, this.height); +}; + +/** + * Resizes the canvas that was created by the CanvasBuffer class to the specified width and height + * + * @method resize + * @param width {Number} the new width of the canvas + * @param height {Number} the new height of the canvas + * @private + */ + +PIXI.CanvasBuffer.prototype.resize = function(width, height) +{ + this.width = this.canvas.width = width; + this.height = this.canvas.height = height; +}; + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * A set of functions used by the canvas renderer to draw the primitive graphics data + * + * @class CanvasGraphics + */ +PIXI.CanvasGraphics = function() +{ + +}; + + +/* + * Renders the graphics object + * + * @static + * @private + * @method renderGraphics + * @param graphics {Graphics} the actual graphics object to render + * @param context {Context2D} the 2d drawing method of the canvas + */ +PIXI.CanvasGraphics.renderGraphics = function(graphics, context) +{ + var worldAlpha = graphics.worldAlpha; + var color = ''; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var points = data.points; + + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); + + context.lineWidth = data.lineWidth; + + if(data.type === PIXI.Graphics.POLY) + { + context.beginPath(); + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + else if(data.type === PIXI.Graphics.RECT) + { + + if(data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fillRect(points[0], points[1], points[2], points[3]); + + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeRect(points[0], points[1], points[2], points[3]); + } + + } + else if(data.type === PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + else if(data.type === PIXI.Graphics.ELIP) + { + + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var ellipseData = data.points; + + var w = ellipseData[2] * 2; + var h = ellipseData[3] * 2; + + var x = ellipseData[0] - w/2; + var y = ellipseData[1] - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + } +}; + +/* + * Renders a graphics mask + * + * @static + * @private + * @method renderGraphicsMask + * @param graphics {Graphics} the graphics which will be used as a mask + * @param context {Context2D} the context 2d method of the canvas + */ +PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) +{ + var len = graphics.graphicsData.length; + + if(len === 0) return; + + if(len > 1) + { + len = 1; + window.console.log('Pixi.js warning: masks in canvas can only mask using the first path in the graphics object'); + } + + for (var i = 0; i < 1; i++) + { + var data = graphics.graphicsData[i]; + var points = data.points; + + if(data.type === PIXI.Graphics.POLY) + { + context.beginPath(); + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + } + else if(data.type === PIXI.Graphics.RECT) + { + context.beginPath(); + context.rect(points[0], points[1], points[2], points[3]); + context.closePath(); + } + else if(data.type === PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.closePath(); + } + else if(data.type === PIXI.Graphics.ELIP) + { + + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + var ellipseData = data.points; + + var w = ellipseData[2] * 2; + var h = ellipseData[3] * 2; + + var x = ellipseData[0] - w/2; + var y = ellipseData[1] - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + context.closePath(); + } + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * It is important to know that with the webGL renderer only simple polygons can be filled at this stage + * Complex polygons will not be filled. Heres an example of a complex polygon: http://www.goodboydigital.com/wp-content/uploads/2013/06/complexPolygon.png + * + * @class Graphics + * @extends DisplayObjectContainer + * @constructor + */ +PIXI.Graphics = function() +{ + PIXI.DisplayObjectContainer.call( this ); + + this.renderable = true; + + /** + * The alpha of the fill of this graphics object + * + * @property fillAlpha + * @type Number + */ + this.fillAlpha = 1; + + /** + * The width of any lines drawn + * + * @property lineWidth + * @type Number + */ + this.lineWidth = 0; + + /** + * The color of any lines drawn + * + * @property lineColor + * @type String + */ + this.lineColor = "black"; + + /** + * Graphics data + * + * @property graphicsData + * @type Array + * @private + */ + this.graphicsData = []; + + + /** + * The tint applied to the graphic shape. This is a hex value + * + * @property tint + * @type Number + * @default 0xFFFFFF + */ + this.tint = 0xFFFFFF;// * Math.random(); + + /** + * The blend mode to be applied to the graphic shape + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ + this.blendMode = PIXI.blendModes.NORMAL; + + /** + * Current path + * + * @property currentPath + * @type Object + * @private + */ + this.currentPath = {points:[]}; + + /** + * Array containing some WebGL-related properties used by the WebGL renderer + * + * @property _webGL + * @type Array + * @private + */ + this._webGL = []; + + /** + * Whether this shape is being used as a mask + * + * @property isMask + * @type isMask + */ + this.isMask = false; + + /** + * The bounds of the graphic shape as rectangle object + * + * @property bounds + * @type Rectangle + */ + this.bounds = null; + + /** + * the bounds' padding used for bounds calculation + * + * @property bounds + * @type Number + */ + this.boundsPadding = 10; +}; + +// constructor +PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Graphics.prototype.constructor = PIXI.Graphics; + +/** + * If cacheAsBitmap is true the graphics object will then be rendered as if it was a sprite. + * This is useful if your graphics element does not change often as it will speed up the rendering of the object + * It is also usful as the graphics object will always be antialiased because it will be rendered using canvas + * Not recommended if you are constanly redrawing the graphics element. + * + * @property cacheAsBitmap + * @default false + * @type Boolean + * @private + */ +Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { + get: function() { + return this._cacheAsBitmap; + }, + set: function(value) { + this._cacheAsBitmap = value; + + if(this._cacheAsBitmap) + { + this._generateCachedSprite(); + } + else + { + this.destroyCachedSprite(); + this.dirty = true; + } + + } +}); + + +/** + * Specifies the line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method. + * + * @method lineStyle + * @param lineWidth {Number} width of the line to draw, will update the object's stored style + * @param color {Number} color of the line to draw, will update the object's stored style + * @param alpha {Number} alpha of the line to draw, will update the object's stored style + */ +PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) +{ + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.lineWidth = lineWidth || 0; + this.lineColor = color || 0; + this.lineAlpha = (arguments.length < 3) ? 1 : alpha; + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; + + this.graphicsData.push(this.currentPath); + + return this; +}; + +/** + * Moves the current drawing position to (x, y). + * + * @method moveTo + * @param x {Number} the X coordinate to move to + * @param y {Number} the Y coordinate to move to + */ +PIXI.Graphics.prototype.moveTo = function(x, y) +{ + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; + + this.currentPath.points.push(x, y); + + this.graphicsData.push(this.currentPath); + + return this; +}; + +/** + * Draws a line using the current line style from the current drawing position to (x, y); + * the current drawing position is then set to (x, y). + * + * @method lineTo + * @param x {Number} the X coordinate to draw to + * @param y {Number} the Y coordinate to draw to + */ +PIXI.Graphics.prototype.lineTo = function(x, y) +{ + this.currentPath.points.push(x, y); + this.dirty = true; + + return this; +}; + +/** + * Specifies a simple one-color fill that subsequent calls to other Graphics methods + * (such as lineTo() or drawCircle()) use when drawing. + * + * @method beginFill + * @param color {Number} the color of the fill + * @param alpha {Number} the alpha of the fill + */ +PIXI.Graphics.prototype.beginFill = function(color, alpha) +{ + + this.filling = true; + this.fillColor = color || 0; + this.fillAlpha = (arguments.length < 2) ? 1 : alpha; + + return this; +}; + +/** + * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method. + * + * @method endFill + */ +PIXI.Graphics.prototype.endFill = function() +{ + this.filling = false; + this.fillColor = null; + this.fillAlpha = 1; + + return this; +}; + +/** + * @method drawRect + * + * @param x {Number} The X coord of the top-left of the rectangle + * @param y {Number} The Y coord of the top-left of the rectangle + * @param width {Number} The width of the rectangle + * @param height {Number} The height of the rectangle + */ +PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) +{ + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, width, height], type:PIXI.Graphics.RECT}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; + + return this; +}; + +/** + * Draws a circle. + * + * @method drawCircle + * @param x {Number} The X coordinate of the center of the circle + * @param y {Number} The Y coordinate of the center of the circle + * @param radius {Number} The radius of the circle + */ +PIXI.Graphics.prototype.drawCircle = function( x, y, radius) +{ + + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; + + return this; +}; + +/** + * Draws an ellipse. + * + * @method drawEllipse + * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this ellipse + * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this ellipse + * @param width {Number} The width of the ellipse + * @param height {Number} The height of the ellipse + */ +PIXI.Graphics.prototype.drawEllipse = function( x, y, width, height) +{ + + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, width, height], type:PIXI.Graphics.ELIP}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; + + return this; +}; + +/** + * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings. + * + * @method clear + */ +PIXI.Graphics.prototype.clear = function() +{ + this.lineWidth = 0; + this.filling = false; + + this.dirty = true; + this.clearDirty = true; + this.graphicsData = []; + + this.bounds = null; //new PIXI.Rectangle(); + + return this; +}; + +/** + * Useful function that returns a texture of the graphics object that can then be used to create sprites + * This can be quite useful if your geometry is complicated and needs to be reused multiple times. + * + * @method generateTexture + * @return {Texture} a texture of the graphics object + */ +PIXI.Graphics.prototype.generateTexture = function() +{ + var bounds = this.getBounds(); + + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + canvasBuffer.context.translate(-bounds.x,-bounds.y); + + PIXI.CanvasGraphics.renderGraphics(this, canvasBuffer.context); + + return texture; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Graphics.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(this.visible === false || this.alpha === 0 || this.isMask === true)return; + + if(this._cacheAsBitmap) + { + + if(this.dirty) + { + this._generateCachedSprite(); + // we will also need to update the texture on the gpu too! + PIXI.updateWebGLTexture(this._cachedSprite.texture.baseTexture, renderSession.gl); + + this.dirty = false; + } + + PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + + return; + } + else + { + renderSession.spriteBatch.stop(); + + if(this._mask)renderSession.maskManager.pushMask(this.mask, renderSession); + if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock); + + // check blend mode + if(this.blendMode !== renderSession.spriteBatch.currentBlendMode) + { + renderSession.spriteBatch.currentBlendMode = this.blendMode; + var blendModeWebGL = PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode]; + renderSession.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + } + + PIXI.WebGLGraphics.renderGraphics(this, renderSession); + + // only render if it has children! + if(this.children.length) + { + renderSession.spriteBatch.start(); + + // simple render children! + for(var i=0, j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + return bounds; +}; + +/** + * Update the bounds of the object + * + * @method updateBounds + */ +PIXI.Graphics.prototype.updateBounds = function() +{ + + var minX = Infinity; + var maxX = -Infinity; + + var minY = Infinity; + var maxY = -Infinity; + + var points, x, y, w, h; + + for (var i = 0; i < this.graphicsData.length; i++) { + var data = this.graphicsData[i]; + var type = data.type; + var lineWidth = data.lineWidth; + + points = data.points; + + if(type === PIXI.Graphics.RECT) + { + x = points[0] - lineWidth/2; + y = points[1] - lineWidth/2; + w = points[2] + lineWidth; + h = points[3] + lineWidth; + + minX = x < minX ? x : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y < minY ? x : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else if(type === PIXI.Graphics.CIRC || type === PIXI.Graphics.ELIP) + { + x = points[0]; + y = points[1]; + w = points[2] + lineWidth/2; + h = points[3] + lineWidth/2; + + minX = x - w < minX ? x - w : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y - h < minY ? y - h : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else + { + // POLY + for (var j = 0; j < points.length; j+=2) + { + + x = points[j]; + y = points[j+1]; + minX = x-lineWidth < minX ? x-lineWidth : minX; + maxX = x+lineWidth > maxX ? x+lineWidth : maxX; + + minY = y-lineWidth < minY ? y-lineWidth : minY; + maxY = y+lineWidth > maxY ? y+lineWidth : maxY; + } + } + } + + var padding = this.boundsPadding; + this.bounds = new PIXI.Rectangle(minX - padding, minY - padding, (maxX - minX) + padding * 2, (maxY - minY) + padding * 2); +}; + + +/** + * Generates the cached sprite when the sprite has cacheAsBitmap = true + * + * @method _generateCachedSprite + * @private + */ +PIXI.Graphics.prototype._generateCachedSprite = function() +{ + var bounds = this.getLocalBounds(); + + if(!this._cachedSprite) + { + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + this._cachedSprite = new PIXI.Sprite(texture); + this._cachedSprite.buffer = canvasBuffer; + + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.buffer.resize(bounds.width, bounds.height); + } + + // leverage the anchor to account for the offset of the element + this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); + this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); + + // this._cachedSprite.buffer.context.save(); + this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y); + + PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context); + // this._cachedSprite.buffer.context.restore(); +}; + +PIXI.Graphics.prototype.destroyCachedSprite = function() +{ + this._cachedSprite.texture.destroy(true); + + // let the gc collect the unused sprite + // TODO could be object pooled! + this._cachedSprite = null; +}; + + +// SOME TYPES: +PIXI.Graphics.POLY = 0; +PIXI.Graphics.RECT = 1; +PIXI.Graphics.CIRC = 2; +PIXI.Graphics.ELIP = 3; + +/** + * @author Mat Groves http://matgroves.com/ + */ + + /** + * + * @class Strip + * @extends DisplayObjectContainer + * @constructor + * @param texture {Texture} The texture to use + * @param width {Number} the width + * @param height {Number} the height + * + */ +PIXI.Strip = function(texture, width, height) +{ + PIXI.DisplayObjectContainer.call( this ); + this.texture = texture; + this.blendMode = PIXI.blendModes.NORMAL; + + try + { + this.uvs = new Float32Array([0, 1, + 1, 1, + 1, 0, 0,1]); + + this.verticies = new Float32Array([0, 0, + 0,0, + 0,0, 0, + 0, 0]); + + this.colors = new Float32Array([1, 1, 1, 1]); + + this.indices = new Uint16Array([0, 1, 2, 3]); + } + catch(error) + { + this.uvs = [0, 1, + 1, 1, + 1, 0, 0,1]; + + this.verticies = [0, 0, + 0,0, + 0,0, 0, + 0, 0]; + + this.colors = [1, 1, 1, 1]; + + this.indices = [0, 1, 2, 3]; + } + + + /* + this.uvs = new Float32Array() + this.verticies = new Float32Array() + this.colors = new Float32Array() + this.indices = new Uint16Array() + */ + + this.width = width; + this.height = height; + + // load the texture! + if(texture.baseTexture.hasLoaded) + { + this.width = this.texture.frame.width; + this.height = this.texture.frame.height; + this.updateFrame = true; + } + else + { + this.onTextureUpdateBind = this.onTextureUpdate.bind(this); + this.texture.addEventListener( 'update', this.onTextureUpdateBind ); + } + + this.renderable = true; +}; + +// constructor +PIXI.Strip.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.Strip.prototype.constructor = PIXI.Strip; + +/* + * Sets the texture that the Strip will use + * + * @method setTexture + * @param texture {Texture} the texture that will be used + * @private + */ +PIXI.Strip.prototype.setTexture = function(texture) +{ + //TODO SET THE TEXTURES + //TODO VISIBILITY + + // stop current texture + this.texture = texture; + this.width = texture.frame.width; + this.height = texture.frame.height; + this.updateFrame = true; +}; + +/** + * When the texture is updated, this event will fire to update the scale and frame + * + * @method onTextureUpdate + * @param event + * @private + */ +PIXI.Strip.prototype.onTextureUpdate = function() +{ + this.updateFrame = true; +}; +/* @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * + * @class Rope + * @constructor + * @param texture {Texture} The texture to use + * @param points {Array} + * + */ +PIXI.Rope = function(texture, points) +{ + PIXI.Strip.call( this, texture ); + this.points = points; + + try + { + this.verticies = new Float32Array(points.length * 4); + this.uvs = new Float32Array(points.length * 4); + this.colors = new Float32Array(points.length * 2); + this.indices = new Uint16Array(points.length * 2); + } + catch(error) + { + this.verticies = new Array(points.length * 4); + this.uvs = new Array(points.length * 4); + this.colors = new Array(points.length * 2); + this.indices = new Array(points.length * 2); + } + + this.refresh(); +}; + + +// constructor +PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; + +/* + * Refreshes + * + * @method refresh + */ +PIXI.Rope.prototype.refresh = function() +{ + var points = this.points; + if(points.length < 1) return; + + var uvs = this.uvs; + + var lastPoint = points[0]; + var indices = this.indices; + var colors = this.colors; + + this.count-=0.2; + + + uvs[0] = 0; + uvs[1] = 1; + uvs[2] = 0; + uvs[3] = 1; + + colors[0] = 1; + colors[1] = 1; + + indices[0] = 0; + indices[1] = 1; + + var total = points.length, + point, index, amount; + + for (var i = 1; i < total; i++) + { + + point = points[i]; + index = i * 4; + // time to do some smart drawing! + amount = i / (total-1); + + if(i%2) + { + uvs[index] = amount; + uvs[index+1] = 0; + + uvs[index+2] = amount; + uvs[index+3] = 1; + + } + else + { + uvs[index] = amount; + uvs[index+1] = 0; + + uvs[index+2] = amount; + uvs[index+3] = 1; + } + + index = i * 2; + colors[index] = 1; + colors[index+1] = 1; + + index = i * 2; + indices[index] = index; + indices[index + 1] = index + 1; + + lastPoint = point; + } +}; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Rope.prototype.updateTransform = function() +{ + + var points = this.points; + if(points.length < 1)return; + + var lastPoint = points[0]; + var nextPoint; + var perp = {x:0, y:0}; + + this.count-=0.2; + + var verticies = this.verticies; + verticies[0] = lastPoint.x + perp.x; + verticies[1] = lastPoint.y + perp.y; //+ 200 + verticies[2] = lastPoint.x - perp.x; + verticies[3] = lastPoint.y - perp.y;//+200 + // time to do some smart drawing! + + var total = points.length, + point, index, ratio, perpLength, num; + + for (var i = 1; i < total; i++) + { + point = points[i]; + index = i * 4; + + if(i < points.length-1) + { + nextPoint = points[i+1]; + } + else + { + nextPoint = point; + } + + perp.y = -(nextPoint.x - lastPoint.x); + perp.x = nextPoint.y - lastPoint.y; + + ratio = (1 - (i / (total-1))) * 10; + + if(ratio > 1) ratio = 1; + + perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); + num = this.texture.height / 2; //(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + perp.x /= perpLength; + perp.y /= perpLength; + + perp.x *= num; + perp.y *= num; + + verticies[index] = point.x + perp.x; + verticies[index+1] = point.y + perp.y; + verticies[index+2] = point.x - perp.x; + verticies[index+3] = point.y - perp.y; + + lastPoint = point; + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); +}; +/* + * Sets the texture that the Rope will use + * + * @method setTexture + * @param texture {Texture} the texture that will be used + */ +PIXI.Rope.prototype.setTexture = function(texture) +{ + // stop current texture + this.texture = texture; + this.updateFrame = true; +}; + +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * A tiling sprite is a fast way of rendering a tiling image + * + * @class TilingSprite + * @extends DisplayObjectContainer + * @constructor + * @param texture {Texture} the texture of the tiling sprite + * @param width {Number} the width of the tiling sprite + * @param height {Number} the height of the tiling sprite + */ +PIXI.TilingSprite = function(texture, width, height) +{ + PIXI.Sprite.call( this, texture); + + /** + * The with of the tiling sprite + * + * @property width + * @type Number + */ + this.width = width || 100; + /** + * The height of the tiling sprite + * + * @property height + * @type Number + */ + this.height = height || 100; + + /** + * The scaling of the image that is being tiled + * + * @property tileScale + * @type Point + */ + this.tileScale = new PIXI.Point(1,1); + + /** + * A point that represents the scale of the texture object + * + * @property tileScaleOffset + * @type Point + */ + this.tileScaleOffset = new PIXI.Point(1,1); + + /** + * The offset position of the image that is being tiled + * + * @property tilePosition + * @type Point + */ + this.tilePosition = new PIXI.Point(0,0); + + + /** + * Whether this sprite is renderable or not + * + * @property renderable + * @type Boolean + * @default true + */ + this.renderable = true; + + /** + * The tint applied to the sprite. This is a hex value + * + * @property tint + * @type Number + * @default 0xFFFFFF + */ + this.tint = 0xFFFFFF; + + /** + * The blend mode to be applied to the sprite + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ + this.blendMode = PIXI.blendModes.NORMAL; +}; + +// constructor +PIXI.TilingSprite.prototype = Object.create(PIXI.Sprite.prototype); +PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite; + + +/** + * The width of the sprite, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.TilingSprite.prototype, 'width', { + get: function() { + return this._width; + }, + set: function(value) { + + this._width = value; + } +}); + +/** + * The height of the TilingSprite, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.TilingSprite.prototype, 'height', { + get: function() { + return this._height; + }, + set: function(value) { + this._height = value; + } +}); + +/** + * When the texture is updated, this event will be fired to update the scale and frame + * + * @method onTextureUpdate + * @param event + * @private + */ +PIXI.TilingSprite.prototype.onTextureUpdate = function() +{ + this.updateFrame = true; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.TilingSprite.prototype._renderWebGL = function(renderSession) +{ + + if(this.visible === false || this.alpha === 0)return; + + var i,j; + + if(this.mask || this.filters) + { + if(this.mask) + { + renderSession.spriteBatch.stop(); + renderSession.maskManager.pushMask(this.mask, renderSession); + renderSession.spriteBatch.start(); + } + + if(this.filters) + { + renderSession.spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + + if(!this.tilingTexture)this.generateTilingTexture(true); + else renderSession.spriteBatch.renderTilingSprite(this); + + // simple render children! + for(i=0,j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + +/** +* +* @method generateTilingTexture +* +* @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two +*/ +PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo) +{ + var texture = this.texture; + + if(!texture.baseTexture.hasLoaded)return; + + var baseTexture = texture.baseTexture; + var frame = texture.frame; + + var targetWidth, targetHeight; + + // check that the frame is the same size as the base texture. + + var isFrame = frame.width !== baseTexture.width || frame.height !== baseTexture.height; + + this.tilingTexture = texture; + + var newTextureRequired = false; + + if(!forcePowerOfTwo) + { + if(isFrame) + { + targetWidth = frame.width; + targetHeight = frame.height; + + newTextureRequired = true; + } + } + else + { + targetWidth = PIXI.getNextPowerOfTwo(texture.frame.width); + targetHeight = PIXI.getNextPowerOfTwo(texture.frame.height); + + if(frame.width !== targetWidth && frame.height !== targetHeight)newTextureRequired = true; + } + + if(newTextureRequired) + { + var canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight); + + canvasBuffer.context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + targetWidth, + targetHeight); + + this.tilingTexture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + this.tileScaleOffset.x = frame.width / targetWidth; + this.tileScaleOffset.y = frame.height / targetHeight; + } + + + this.tilingTexture.baseTexture._powerOf2 = true; +}; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi + * + * Awesome JS run time provided by EsotericSoftware + * https://github.com/EsotericSoftware/spine-runtimes + * + */ + +/* + * Awesome JS run time provided by EsotericSoftware + * + * https://github.com/EsotericSoftware/spine-runtimes + * + */ + + + +var spine = {}; + +spine.BoneData = function (name, parent) { + this.name = name; + this.parent = parent; +}; +spine.BoneData.prototype = { + length: 0, + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1 +}; + +spine.SlotData = function (name, boneData) { + this.name = name; + this.boneData = boneData; +}; +spine.SlotData.prototype = { + r: 1, g: 1, b: 1, a: 1, + attachmentName: null +}; + +spine.Bone = function (boneData, parent) { + this.data = boneData; + this.parent = parent; + this.setToSetupPose(); +}; +spine.Bone.yDown = false; +spine.Bone.prototype = { + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + m00: 0, m01: 0, worldX: 0, // a b x + m10: 0, m11: 0, worldY: 0, // c d y + worldRotation: 0, + worldScaleX: 1, worldScaleY: 1, + updateWorldTransform: function (flipX, flipY) { + var parent = this.parent; + if (parent != null) { + this.worldX = this.x * parent.m00 + this.y * parent.m01 + parent.worldX; + this.worldY = this.x * parent.m10 + this.y * parent.m11 + parent.worldY; + this.worldScaleX = parent.worldScaleX * this.scaleX; + this.worldScaleY = parent.worldScaleY * this.scaleY; + this.worldRotation = parent.worldRotation + this.rotation; + } else { + this.worldX = this.x; + this.worldY = this.y; + this.worldScaleX = this.scaleX; + this.worldScaleY = this.scaleY; + this.worldRotation = this.rotation; + } + var radians = this.worldRotation * Math.PI / 180; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + this.m00 = cos * this.worldScaleX; + this.m10 = sin * this.worldScaleX; + this.m01 = -sin * this.worldScaleY; + this.m11 = cos * this.worldScaleY; + if (flipX) { + this.m00 = -this.m00; + this.m01 = -this.m01; + } + if (flipY) { + this.m10 = -this.m10; + this.m11 = -this.m11; + } + if (spine.Bone.yDown) { + this.m10 = -this.m10; + this.m11 = -this.m11; + } + }, + setToSetupPose: function () { + var data = this.data; + this.x = data.x; + this.y = data.y; + this.rotation = data.rotation; + this.scaleX = data.scaleX; + this.scaleY = data.scaleY; + } +}; + +spine.Slot = function (slotData, skeleton, bone) { + this.data = slotData; + this.skeleton = skeleton; + this.bone = bone; + this.setToSetupPose(); +}; +spine.Slot.prototype = { + r: 1, g: 1, b: 1, a: 1, + _attachmentTime: 0, + attachment: null, + setAttachment: function (attachment) { + this.attachment = attachment; + this._attachmentTime = this.skeleton.time; + }, + setAttachmentTime: function (time) { + this._attachmentTime = this.skeleton.time - time; + }, + getAttachmentTime: function () { + return this.skeleton.time - this._attachmentTime; + }, + setToSetupPose: function () { + var data = this.data; + this.r = data.r; + this.g = data.g; + this.b = data.b; + this.a = data.a; + + var slotDatas = this.skeleton.data.slots; + for (var i = 0, n = slotDatas.length; i < n; i++) { + if (slotDatas[i] == data) { + this.setAttachment(!data.attachmentName ? null : this.skeleton.getAttachmentBySlotIndex(i, data.attachmentName)); + break; + } + } + } +}; + +spine.Skin = function (name) { + this.name = name; + this.attachments = {}; +}; +spine.Skin.prototype = { + addAttachment: function (slotIndex, name, attachment) { + this.attachments[slotIndex + ":" + name] = attachment; + }, + getAttachment: function (slotIndex, name) { + return this.attachments[slotIndex + ":" + name]; + }, + _attachAll: function (skeleton, oldSkin) { + for (var key in oldSkin.attachments) { + var colon = key.indexOf(":"); + var slotIndex = parseInt(key.substring(0, colon), 10); + var name = key.substring(colon + 1); + var slot = skeleton.slots[slotIndex]; + if (slot.attachment && slot.attachment.name == name) { + var attachment = this.getAttachment(slotIndex, name); + if (attachment) slot.setAttachment(attachment); + } + } + } +}; + +spine.Animation = function (name, timelines, duration) { + this.name = name; + this.timelines = timelines; + this.duration = duration; +}; +spine.Animation.prototype = { + apply: function (skeleton, time, loop) { + if (loop && this.duration) time %= this.duration; + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, 1); + }, + mix: function (skeleton, time, loop, alpha) { + if (loop && this.duration) time %= this.duration; + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, alpha); + } +}; + +spine.binarySearch = function (values, target, step) { + var low = 0; + var high = Math.floor(values.length / step) - 2; + if (!high) return step; + var current = high >>> 1; + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1) * step; + current = (low + high) >>> 1; + } +}; +spine.linearSearch = function (values, target, step) { + for (var i = 0, last = values.length - step; i <= last; i += step) + if (values[i] > target) return i; + return -1; +}; + +spine.Curves = function (frameCount) { + this.curves = []; // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... + this.curves.length = (frameCount - 1) * 6; +}; +spine.Curves.prototype = { + setLinear: function (frameIndex) { + this.curves[frameIndex * 6] = 0/*LINEAR*/; + }, + setStepped: function (frameIndex) { + this.curves[frameIndex * 6] = -1/*STEPPED*/; + }, + /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ + setCurve: function (frameIndex, cx1, cy1, cx2, cy2) { + var subdiv_step = 1 / 10/*BEZIER_SEGMENTS*/; + var subdiv_step2 = subdiv_step * subdiv_step; + var subdiv_step3 = subdiv_step2 * subdiv_step; + var pre1 = 3 * subdiv_step; + var pre2 = 3 * subdiv_step2; + var pre4 = 6 * subdiv_step2; + var pre5 = 6 * subdiv_step3; + var tmp1x = -cx1 * 2 + cx2; + var tmp1y = -cy1 * 2 + cy2; + var tmp2x = (cx1 - cx2) * 3 + 1; + var tmp2y = (cy1 - cy2) * 3 + 1; + var i = frameIndex * 6; + var curves = this.curves; + curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3; + curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3; + curves[i + 2] = tmp1x * pre4 + tmp2x * pre5; + curves[i + 3] = tmp1y * pre4 + tmp2y * pre5; + curves[i + 4] = tmp2x * pre5; + curves[i + 5] = tmp2y * pre5; + }, + getCurvePercent: function (frameIndex, percent) { + percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); + var curveIndex = frameIndex * 6; + var curves = this.curves; + var dfx = curves[curveIndex]; + if (!dfx/*LINEAR*/) return percent; + if (dfx == -1/*STEPPED*/) return 0; + var dfy = curves[curveIndex + 1]; + var ddfx = curves[curveIndex + 2]; + var ddfy = curves[curveIndex + 3]; + var dddfx = curves[curveIndex + 4]; + var dddfy = curves[curveIndex + 5]; + var x = dfx, y = dfy; + var i = 10/*BEZIER_SEGMENTS*/ - 2; + while (true) { + if (x >= percent) { + var lastX = x - dfx; + var lastY = y - dfy; + return lastY + (y - lastY) * (percent - lastX) / (x - lastX); + } + if (!i) break; + i--; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } +}; + +spine.RotateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, angle, ... + this.frames.length = frameCount * 2; +}; +spine.RotateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, angle) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = angle; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames, + amount; + + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 2]) { // Time is after last frame. + amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 2); + var lastFrameValue = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent); + + amount = frames[frameIndex + 1/*FRAME_VALUE*/] - lastFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + } +}; + +spine.TranslateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.TranslateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha; + bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 3); + var lastFrameX = frames[frameIndex - 2]; + var lastFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; + } +}; + +spine.ScaleTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.ScaleTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 3); + var lastFrameX = frames[frameIndex - 2]; + var lastFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.scaleX += (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.scaleY) * alpha; + } +}; + +spine.ColorTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, r, g, b, a, ... + this.frames.length = frameCount * 5; +}; +spine.ColorTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 5; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = r; + this.frames[frameIndex + 2] = g; + this.frames[frameIndex + 3] = b; + this.frames[frameIndex + 4] = a; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var slot = skeleton.slots[this.slotIndex]; + + if (time >= frames[frames.length - 5]) { // Time is after last frame. + var i = frames.length - 1; + slot.r = frames[i - 3]; + slot.g = frames[i - 2]; + slot.b = frames[i - 1]; + slot.a = frames[i]; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 5); + var lastFrameR = frames[frameIndex - 4]; + var lastFrameG = frames[frameIndex - 3]; + var lastFrameB = frames[frameIndex - 2]; + var lastFrameA = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); + + var r = lastFrameR + (frames[frameIndex + 1/*FRAME_R*/] - lastFrameR) * percent; + var g = lastFrameG + (frames[frameIndex + 2/*FRAME_G*/] - lastFrameG) * percent; + var b = lastFrameB + (frames[frameIndex + 3/*FRAME_B*/] - lastFrameB) * percent; + var a = lastFrameA + (frames[frameIndex + 4/*FRAME_A*/] - lastFrameA) * percent; + if (alpha < 1) { + slot.r += (r - slot.r) * alpha; + slot.g += (g - slot.g) * alpha; + slot.b += (b - slot.b) * alpha; + slot.a += (a - slot.a) * alpha; + } else { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + } + } +}; + +spine.AttachmentTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, ... + this.frames.length = frameCount; + this.attachmentNames = []; // time, ... + this.attachmentNames.length = frameCount; +}; +spine.AttachmentTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, attachmentName) { + this.frames[frameIndex] = time; + this.attachmentNames[frameIndex] = attachmentName; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = spine.binarySearch(frames, time, 1) - 1; + + var attachmentName = this.attachmentNames[frameIndex]; + skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); + } +}; + +spine.SkeletonData = function () { + this.bones = []; + this.slots = []; + this.skins = []; + this.animations = []; +}; +spine.SkeletonData.prototype = { + defaultSkin: null, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + if (slots[i].name == slotName) return slot[i]; + } + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].name == slotName) return i; + return -1; + }, + /** @return May be null. */ + findSkin: function (skinName) { + var skins = this.skins; + for (var i = 0, n = skins.length; i < n; i++) + if (skins[i].name == skinName) return skins[i]; + return null; + }, + /** @return May be null. */ + findAnimation: function (animationName) { + var animations = this.animations; + for (var i = 0, n = animations.length; i < n; i++) + if (animations[i].name == animationName) return animations[i]; + return null; + } +}; + +spine.Skeleton = function (skeletonData) { + this.data = skeletonData; + + this.bones = []; + for (var i = 0, n = skeletonData.bones.length; i < n; i++) { + var boneData = skeletonData.bones[i]; + var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)]; + this.bones.push(new spine.Bone(boneData, parent)); + } + + this.slots = []; + this.drawOrder = []; + for (i = 0, n = skeletonData.slots.length; i < n; i++) { + var slotData = skeletonData.slots[i]; + var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)]; + var slot = new spine.Slot(slotData, this, bone); + this.slots.push(slot); + this.drawOrder.push(slot); + } +}; +spine.Skeleton.prototype = { + x: 0, y: 0, + skin: null, + r: 1, g: 1, b: 1, a: 1, + time: 0, + flipX: false, flipY: false, + /** Updates the world transform for each bone. */ + updateWorldTransform: function () { + var flipX = this.flipX; + var flipY = this.flipY; + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].updateWorldTransform(flipX, flipY); + }, + /** Sets the bones and slots to their setup pose values. */ + setToSetupPose: function () { + this.setBonesToSetupPose(); + this.setSlotsToSetupPose(); + }, + setBonesToSetupPose: function () { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].setToSetupPose(); + }, + setSlotsToSetupPose: function () { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + slots[i].setToSetupPose(i); + }, + /** @return May return null. */ + getRootBone: function () { + return this.bones.length ? this.bones[0] : null; + }, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return slots[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return i; + return -1; + }, + setSkinByName: function (skinName) { + var skin = this.data.findSkin(skinName); + if (!skin) throw "Skin not found: " + skinName; + this.setSkin(skin); + }, + /** Sets the skin used to look up attachments not found in the {@link SkeletonData#getDefaultSkin() default skin}. Attachments + * from the new skin are attached if the corresponding attachment from the old skin was attached. + * @param newSkin May be null. */ + setSkin: function (newSkin) { + if (this.skin && newSkin) newSkin._attachAll(this, this.skin); + this.skin = newSkin; + }, + /** @return May be null. */ + getAttachmentBySlotName: function (slotName, attachmentName) { + return this.getAttachmentBySlotIndex(this.data.findSlotIndex(slotName), attachmentName); + }, + /** @return May be null. */ + getAttachmentBySlotIndex: function (slotIndex, attachmentName) { + if (this.skin) { + var attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment) return attachment; + } + if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; + }, + /** @param attachmentName May be null. */ + setAttachment: function (slotName, attachmentName) { + var slots = this.slots; + for (var i = 0, n = slots.size; i < n; i++) { + var slot = slots[i]; + if (slot.data.name == slotName) { + var attachment = null; + if (attachmentName) { + attachment = this.getAttachment(i, attachmentName); + if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; + } + slot.setAttachment(attachment); + return; + } + } + throw "Slot not found: " + slotName; + }, + update: function (delta) { + time += delta; + } +}; + +spine.AttachmentType = { + region: 0 +}; + +spine.RegionAttachment = function () { + this.offset = []; + this.offset.length = 8; + this.uvs = []; + this.uvs.length = 8; +}; +spine.RegionAttachment.prototype = { + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + width: 0, height: 0, + rendererObject: null, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + setUVs: function (u, v, u2, v2, rotate) { + var uvs = this.uvs; + if (rotate) { + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v2; + uvs[4/*X3*/] = u; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v; + uvs[0/*X1*/] = u2; + uvs[1/*Y1*/] = v2; + } else { + uvs[0/*X1*/] = u; + uvs[1/*Y1*/] = v2; + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v; + uvs[4/*X3*/] = u2; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v2; + } + }, + updateOffset: function () { + var regionScaleX = this.width / this.regionOriginalWidth * this.scaleX; + var regionScaleY = this.height / this.regionOriginalHeight * this.scaleY; + var localX = -this.width / 2 * this.scaleX + this.regionOffsetX * regionScaleX; + var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY; + var localX2 = localX + this.regionWidth * regionScaleX; + var localY2 = localY + this.regionHeight * regionScaleY; + var radians = this.rotation * Math.PI / 180; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + var localXCos = localX * cos + this.x; + var localXSin = localX * sin; + var localYCos = localY * cos + this.y; + var localYSin = localY * sin; + var localX2Cos = localX2 * cos + this.x; + var localX2Sin = localX2 * sin; + var localY2Cos = localY2 * cos + this.y; + var localY2Sin = localY2 * sin; + var offset = this.offset; + offset[0/*X1*/] = localXCos - localYSin; + offset[1/*Y1*/] = localYCos + localXSin; + offset[2/*X2*/] = localXCos - localY2Sin; + offset[3/*Y2*/] = localY2Cos + localXSin; + offset[4/*X3*/] = localX2Cos - localY2Sin; + offset[5/*Y3*/] = localY2Cos + localX2Sin; + offset[6/*X4*/] = localX2Cos - localYSin; + offset[7/*Y4*/] = localYCos + localX2Sin; + }, + computeVertices: function (x, y, bone, vertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00; + var m01 = bone.m01; + var m10 = bone.m10; + var m11 = bone.m11; + var offset = this.offset; + vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x; + vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y; + vertices[2/*X2*/] = offset[2/*X2*/] * m00 + offset[3/*Y2*/] * m01 + x; + vertices[3/*Y2*/] = offset[2/*X2*/] * m10 + offset[3/*Y2*/] * m11 + y; + vertices[4/*X3*/] = offset[4/*X3*/] * m00 + offset[5/*X3*/] * m01 + x; + vertices[5/*X3*/] = offset[4/*X3*/] * m10 + offset[5/*X3*/] * m11 + y; + vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x; + vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y; + } +} + +spine.AnimationStateData = function (skeletonData) { + this.skeletonData = skeletonData; + this.animationToMixTime = {}; +}; +spine.AnimationStateData.prototype = { + defaultMix: 0, + setMixByName: function (fromName, toName, duration) { + var from = this.skeletonData.findAnimation(fromName); + if (!from) throw "Animation not found: " + fromName; + var to = this.skeletonData.findAnimation(toName); + if (!to) throw "Animation not found: " + toName; + this.setMix(from, to, duration); + }, + setMix: function (from, to, duration) { + this.animationToMixTime[from.name + ":" + to.name] = duration; + }, + getMix: function (from, to) { + var time = this.animationToMixTime[from.name + ":" + to.name]; + return time ? time : this.defaultMix; + } +}; + +spine.AnimationState = function (stateData) { + this.data = stateData; + this.queue = []; +}; +spine.AnimationState.prototype = { + current: null, + previous: null, + currentTime: 0, + previousTime: 0, + currentLoop: false, + previousLoop: false, + mixTime: 0, + mixDuration: 0, + update: function (delta) { + this.currentTime += delta; + this.previousTime += delta; + this.mixTime += delta; + + if (this.queue.length > 0) { + var entry = this.queue[0]; + if (this.currentTime >= entry.delay) { + this._setAnimation(entry.animation, entry.loop); + this.queue.shift(); + } + } + }, + apply: function (skeleton) { + if (!this.current) return; + if (this.previous) { + this.previous.apply(skeleton, this.previousTime, this.previousLoop); + var alpha = this.mixTime / this.mixDuration; + if (alpha >= 1) { + alpha = 1; + this.previous = null; + } + this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); + } else + this.current.apply(skeleton, this.currentTime, this.currentLoop); + }, + clearAnimation: function () { + this.previous = null; + this.current = null; + this.queue.length = 0; + }, + _setAnimation: function (animation, loop) { + this.previous = null; + if (animation && this.current) { + this.mixDuration = this.data.getMix(this.current, animation); + if (this.mixDuration > 0) { + this.mixTime = 0; + this.previous = this.current; + this.previousTime = this.currentTime; + this.previousLoop = this.currentLoop; + } + } + this.current = animation; + this.currentLoop = loop; + this.currentTime = 0; + }, + /** @see #setAnimation(Animation, Boolean) */ + setAnimationByName: function (animationName, loop) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + this.setAnimation(animation, loop); + }, + /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0. + * @param animation May be null. */ + setAnimation: function (animation, loop) { + this.queue.length = 0; + this._setAnimation(animation, loop); + }, + /** @see #addAnimation(Animation, Boolean, Number) */ + addAnimationByName: function (animationName, loop, delay) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + this.addAnimation(animation, loop, delay); + }, + /** Adds an animation to be played delay seconds after the current or last queued animation. + * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ + addAnimation: function (animation, loop, delay) { + var entry = {}; + entry.animation = animation; + entry.loop = loop; + + if (!delay || delay <= 0) { + var previousAnimation = this.queue.length ? this.queue[this.queue.length - 1].animation : this.current; + if (previousAnimation != null) + delay = previousAnimation.duration - this.data.getMix(previousAnimation, animation) + (delay || 0); + else + delay = 0; + } + entry.delay = delay; + + this.queue.push(entry); + }, + /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ + isComplete: function () { + return !this.current || this.currentTime >= this.current.duration; + } +}; + +spine.SkeletonJson = function (attachmentLoader) { + this.attachmentLoader = attachmentLoader; +}; +spine.SkeletonJson.prototype = { + scale: 1, + readSkeletonData: function (root) { + /*jshint -W069*/ + var skeletonData = new spine.SkeletonData(), + boneData; + + // Bones. + var bones = root["bones"]; + for (var i = 0, n = bones.length; i < n; i++) { + var boneMap = bones[i]; + var parent = null; + if (boneMap["parent"]) { + parent = skeletonData.findBone(boneMap["parent"]); + if (!parent) throw "Parent bone not found: " + boneMap["parent"]; + } + boneData = new spine.BoneData(boneMap["name"], parent); + boneData.length = (boneMap["length"] || 0) * this.scale; + boneData.x = (boneMap["x"] || 0) * this.scale; + boneData.y = (boneMap["y"] || 0) * this.scale; + boneData.rotation = (boneMap["rotation"] || 0); + boneData.scaleX = boneMap["scaleX"] || 1; + boneData.scaleY = boneMap["scaleY"] || 1; + skeletonData.bones.push(boneData); + } + + // Slots. + var slots = root["slots"]; + for (i = 0, n = slots.length; i < n; i++) { + var slotMap = slots[i]; + boneData = skeletonData.findBone(slotMap["bone"]); + if (!boneData) throw "Slot bone not found: " + slotMap["bone"]; + var slotData = new spine.SlotData(slotMap["name"], boneData); + + var color = slotMap["color"]; + if (color) { + slotData.r = spine.SkeletonJson.toColor(color, 0); + slotData.g = spine.SkeletonJson.toColor(color, 1); + slotData.b = spine.SkeletonJson.toColor(color, 2); + slotData.a = spine.SkeletonJson.toColor(color, 3); + } + + slotData.attachmentName = slotMap["attachment"]; + + skeletonData.slots.push(slotData); + } + + // Skins. + var skins = root["skins"]; + for (var skinName in skins) { + if (!skins.hasOwnProperty(skinName)) continue; + var skinMap = skins[skinName]; + var skin = new spine.Skin(skinName); + for (var slotName in skinMap) { + if (!skinMap.hasOwnProperty(slotName)) continue; + var slotIndex = skeletonData.findSlotIndex(slotName); + var slotEntry = skinMap[slotName]; + for (var attachmentName in slotEntry) { + if (!slotEntry.hasOwnProperty(attachmentName)) continue; + var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]); + if (attachment != null) skin.addAttachment(slotIndex, attachmentName, attachment); + } + } + skeletonData.skins.push(skin); + if (skin.name == "default") skeletonData.defaultSkin = skin; + } + + // Animations. + var animations = root["animations"]; + for (var animationName in animations) { + if (!animations.hasOwnProperty(animationName)) continue; + this.readAnimation(animationName, animations[animationName], skeletonData); + } + + return skeletonData; + }, + readAttachment: function (skin, name, map) { + /*jshint -W069*/ + name = map["name"] || name; + + var type = spine.AttachmentType[map["type"] || "region"]; + + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); + attachment.x = (map["x"] || 0) * this.scale; + attachment.y = (map["y"] || 0) * this.scale; + attachment.scaleX = map["scaleX"] || 1; + attachment.scaleY = map["scaleY"] || 1; + attachment.rotation = map["rotation"] || 0; + attachment.width = (map["width"] || 32) * this.scale; + attachment.height = (map["height"] || 32) * this.scale; + attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; + } + + throw "Unknown attachment type: " + type; + }, + + readAnimation: function (name, map, skeletonData) { + /*jshint -W069*/ + var timelines = []; + var duration = 0; + var frameIndex, timeline, timelineName, valueMap, values, + i, n; + + var bones = map["bones"]; + for (var boneName in bones) { + if (!bones.hasOwnProperty(boneName)) continue; + var boneIndex = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) throw "Bone not found: " + boneName; + var boneMap = bones[boneName]; + + for (timelineName in boneMap) { + if (!boneMap.hasOwnProperty(timelineName)) continue; + values = boneMap[timelineName]; + if (timelineName == "rotate") { + timeline = new spine.RotateTimeline(values.length); + timeline.boneIndex = boneIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); + + } else if (timelineName == "translate" || timelineName == "scale") { + var timelineScale = 1; + if (timelineName == "scale") + timeline = new spine.ScaleTimeline(values.length); + else { + timeline = new spine.TranslateTimeline(values.length); + timelineScale = this.scale; + } + timeline.boneIndex = boneIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + var x = (valueMap["x"] || 0) * timelineScale; + var y = (valueMap["y"] || 0) * timelineScale; + timeline.setFrame(frameIndex, valueMap["time"], x, y); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); + + } else + throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; + } + } + var slots = map["slots"]; + for (var slotName in slots) { + if (!slots.hasOwnProperty(slotName)) continue; + var slotMap = slots[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + + for (timelineName in slotMap) { + if (!slotMap.hasOwnProperty(timelineName)) continue; + values = slotMap[timelineName]; + if (timelineName == "color") { + timeline = new spine.ColorTimeline(values.length); + timeline.slotIndex = slotIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + var color = valueMap["color"]; + var r = spine.SkeletonJson.toColor(color, 0); + var g = spine.SkeletonJson.toColor(color, 1); + var b = spine.SkeletonJson.toColor(color, 2); + var a = spine.SkeletonJson.toColor(color, 3); + timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); + + } else if (timelineName == "attachment") { + timeline = new spine.AttachmentTimeline(values.length); + timeline.slotIndex = slotIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + + } else + throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; + } + } + skeletonData.animations.push(new spine.Animation(name, timelines, duration)); + } +}; +spine.SkeletonJson.readCurve = function (timeline, frameIndex, valueMap) { + /*jshint -W069*/ + var curve = valueMap["curve"]; + if (!curve) return; + if (curve == "stepped") + timeline.curves.setStepped(frameIndex); + else if (curve instanceof Array) + timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); +}; +spine.SkeletonJson.toColor = function (hexString, colorIndex) { + if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString; + return parseInt(hexString.substring(colorIndex * 2, 2), 16) / 255; +}; + +spine.Atlas = function (atlasText, textureLoader) { + this.textureLoader = textureLoader; + this.pages = []; + this.regions = []; + + var reader = new spine.AtlasReader(atlasText); + var tuple = []; + tuple.length = 4; + var page = null; + while (true) { + var line = reader.readLine(); + if (line == null) break; + line = reader.trim(line); + if (!line.length) + page = null; + else if (!page) { + page = new spine.AtlasPage(); + page.name = line; + + page.format = spine.Atlas.Format[reader.readValue()]; + + reader.readTuple(tuple); + page.minFilter = spine.Atlas.TextureFilter[tuple[0]]; + page.magFilter = spine.Atlas.TextureFilter[tuple[1]]; + + var direction = reader.readValue(); + page.uWrap = spine.Atlas.TextureWrap.clampToEdge; + page.vWrap = spine.Atlas.TextureWrap.clampToEdge; + if (direction == "x") + page.uWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "y") + page.vWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat; + + textureLoader.load(page, line); + + this.pages.push(page); + + } else { + var region = new spine.AtlasRegion(); + region.name = line; + region.page = page; + + region.rotate = reader.readValue() == "true"; + + reader.readTuple(tuple); + var x = parseInt(tuple[0], 10); + var y = parseInt(tuple[1], 10); + + reader.readTuple(tuple); + var width = parseInt(tuple[0], 10); + var height = parseInt(tuple[1], 10); + + region.u = x / page.width; + region.v = y / page.height; + if (region.rotate) { + region.u2 = (x + height) / page.width; + region.v2 = (y + width) / page.height; + } else { + region.u2 = (x + width) / page.width; + region.v2 = (y + height) / page.height; + } + region.x = x; + region.y = y; + region.width = Math.abs(width); + region.height = Math.abs(height); + + if (reader.readTuple(tuple) == 4) { // split is optional + region.splits = [parseInt(tuple[0], 10), parseInt(tuple[1], 10), parseInt(tuple[2], 10), parseInt(tuple[3], 10)]; + + if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits + region.pads = [parseInt(tuple[0], 10), parseInt(tuple[1], 10), parseInt(tuple[2], 10), parseInt(tuple[3], 10)]; + + reader.readTuple(tuple); + } + } + + region.originalWidth = parseInt(tuple[0], 10); + region.originalHeight = parseInt(tuple[1], 10); + + reader.readTuple(tuple); + region.offsetX = parseInt(tuple[0], 10); + region.offsetY = parseInt(tuple[1], 10); + + region.index = parseInt(reader.readValue(), 10); + + this.regions.push(region); + } + } +}; +spine.Atlas.prototype = { + findRegion: function (name) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) + if (regions[i].name == name) return regions[i]; + return null; + }, + dispose: function () { + var pages = this.pages; + for (var i = 0, n = pages.length; i < n; i++) + this.textureLoader.unload(pages[i].rendererObject); + }, + updateUVs: function (page) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) { + var region = regions[i]; + if (region.page != page) continue; + region.u = region.x / page.width; + region.v = region.y / page.height; + if (region.rotate) { + region.u2 = (region.x + region.height) / page.width; + region.v2 = (region.y + region.width) / page.height; + } else { + region.u2 = (region.x + region.width) / page.width; + region.v2 = (region.y + region.height) / page.height; + } + } + } +}; + +spine.Atlas.Format = { + alpha: 0, + intensity: 1, + luminanceAlpha: 2, + rgb565: 3, + rgba4444: 4, + rgb888: 5, + rgba8888: 6 +}; + +spine.Atlas.TextureFilter = { + nearest: 0, + linear: 1, + mipMap: 2, + mipMapNearestNearest: 3, + mipMapLinearNearest: 4, + mipMapNearestLinear: 5, + mipMapLinearLinear: 6 +}; + +spine.Atlas.TextureWrap = { + mirroredRepeat: 0, + clampToEdge: 1, + repeat: 2 +}; + +spine.AtlasPage = function () {}; +spine.AtlasPage.prototype = { + name: null, + format: null, + minFilter: null, + magFilter: null, + uWrap: null, + vWrap: null, + rendererObject: null, + width: 0, + height: 0 +}; + +spine.AtlasRegion = function () {}; +spine.AtlasRegion.prototype = { + page: null, + name: null, + x: 0, y: 0, + width: 0, height: 0, + u: 0, v: 0, u2: 0, v2: 0, + offsetX: 0, offsetY: 0, + originalWidth: 0, originalHeight: 0, + index: 0, + rotate: false, + splits: null, + pads: null, +}; + +spine.AtlasReader = function (text) { + this.lines = text.split(/\r\n|\r|\n/); +}; +spine.AtlasReader.prototype = { + index: 0, + trim: function (value) { + return value.replace(/^\s+|\s+$/g, ""); + }, + readLine: function () { + if (this.index >= this.lines.length) return null; + return this.lines[this.index++]; + }, + readValue: function () { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + return this.trim(line.substring(colon + 1)); + }, + /** Returns the number of tuple values read (2 or 4). */ + readTuple: function (tuple) { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + var i = 0, lastMatch= colon + 1; + for (; i < 3; i++) { + var comma = line.indexOf(",", lastMatch); + if (comma == -1) { + if (!i) throw "Invalid line: " + line; + break; + } + tuple[i] = this.trim(line.substr(lastMatch, comma - lastMatch)); + lastMatch = comma + 1; + } + tuple[i] = this.trim(line.substring(lastMatch)); + return i + 1; + } +} + +spine.AtlasAttachmentLoader = function (atlas) { + this.atlas = atlas; +} +spine.AtlasAttachmentLoader.prototype = { + newAttachment: function (skin, type, name) { + switch (type) { + case spine.AttachmentType.region: + var region = this.atlas.findRegion(name); + if (!region) throw "Region not found in atlas: " + name + " (" + type + ")"; + var attachment = new spine.RegionAttachment(name); + attachment.rendererObject = region; + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + } + throw "Unknown attachment type: " + type; + } +} + +spine.Bone.yDown = true; +PIXI.AnimCache = {}; + +/** + * A class that enables the you to import and run your spine animations in pixi. + * Spine animation data needs to be loaded using the PIXI.AssetLoader or PIXI.SpineLoader before it can be used by this class + * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * + * @class Spine + * @extends DisplayObjectContainer + * @constructor + * @param url {String} The url of the spine anim file to be used + */ +PIXI.Spine = function (url) { + PIXI.DisplayObjectContainer.call(this); + + this.spineData = PIXI.AnimCache[url]; + + if (!this.spineData) { + throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); + } + + this.skeleton = new spine.Skeleton(this.spineData); + this.skeleton.updateWorldTransform(); + + this.stateData = new spine.AnimationStateData(this.spineData); + this.state = new spine.AnimationState(this.stateData); + + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.Spine.prototype.constructor = PIXI.Spine; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); + this.state.apply(this.skeleton); + this.skeleton.updateWorldTransform(); + + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } + + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; + } + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; + } + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.BaseTextureCache = {}; +PIXI.texturesToUpdate = []; +PIXI.texturesToDestroy = []; + +PIXI.BaseTextureCacheIdGenerator = 0; + +/** + * A texture stores the information that represents an image. All textures have a base texture + * + * @class BaseTexture + * @uses EventTarget + * @constructor + * @param source {String} the source object (image or canvas) + * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts + */ +PIXI.BaseTexture = function(source, scaleMode) +{ + PIXI.EventTarget.call( this ); + + /** + * [read-only] The width of the base texture set when the image has loaded + * + * @property width + * @type Number + * @readOnly + */ + this.width = 100; + + /** + * [read-only] The height of the base texture set when the image has loaded + * + * @property height + * @type Number + * @readOnly + */ + this.height = 100; + + /** + * The scale mode to apply when scaling this texture + * @property scaleMode + * @type PIXI.scaleModes + * @default PIXI.scaleModes.LINEAR + */ + this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; + + /** + * [read-only] Describes if the base texture has loaded or not + * + * @property hasLoaded + * @type Boolean + * @readOnly + */ + this.hasLoaded = false; + + /** + * The source that is loaded to create the texture + * + * @property source + * @type Image + */ + this.source = source; + + if(!source)return; + + if(this.source.complete || this.source.getContext) + { + this.hasLoaded = true; + this.width = this.source.width; + this.height = this.source.height; + + PIXI.texturesToUpdate.push(this); + } + else + { + + var scope = this; + this.source.onload = function() { + + scope.hasLoaded = true; + scope.width = scope.source.width; + scope.height = scope.source.height; + + // add it to somewhere... + PIXI.texturesToUpdate.push(scope); + scope.dispatchEvent( { type: 'loaded', content: scope } ); + }; + } + + this.imageUrl = null; + this._powerOf2 = false; + + //TODO will be used for futer pixi 1.5... + this.id = PIXI.BaseTextureCacheIdGenerator++; + + // used for webGL + this._glTextures = []; + +}; + +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; + +/** + * Destroys this base texture + * + * @method destroy + */ +PIXI.BaseTexture.prototype.destroy = function() +{ + if(this.imageUrl) + { + delete PIXI.BaseTextureCache[this.imageUrl]; + this.imageUrl = null; + this.source.src = null; + } + this.source = null; + PIXI.texturesToDestroy.push(this); +}; + +/** + * Changes the source image of the texture + * + * @method updateSourceImage + * @param newSrc {String} the path of the image + */ +PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) +{ + this.hasLoaded = false; + this.source.src = null; + this.source.src = newSrc; +}; + +/** + * Helper function that returns a base texture based on an image url + * If the image is not in the base texture cache it will be created and loaded + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @param crossorigin {Boolean} + * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts + * @return BaseTexture + */ +PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) +{ + var baseTexture = PIXI.BaseTextureCache[imageUrl]; + crossorigin = !crossorigin; + + if(!baseTexture) + { + // new Image() breaks tex loading in some versions of Chrome. + // See https://code.google.com/p/chromium/issues/detail?id=238071 + var image = new Image();//document.createElement('img'); + if (crossorigin) + { + image.crossOrigin = ''; + } + image.src = imageUrl; + baseTexture = new PIXI.BaseTexture(image, scaleMode); + baseTexture.imageUrl = imageUrl; + PIXI.BaseTextureCache[imageUrl] = baseTexture; + } + + return baseTexture; +}; + +PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode) +{ + if(!canvas._pixiId) + { + canvas._pixiId = 'canvas_' + PIXI.TextureCacheIdGenerator++; + } + + var baseTexture = PIXI.BaseTextureCache[canvas._pixiId]; + + if(!baseTexture) + { + baseTexture = new PIXI.BaseTexture(canvas, scaleMode); + PIXI.BaseTextureCache[canvas._pixiId] = baseTexture; + } + + return baseTexture; +}; + + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.TextureCache = {}; +PIXI.FrameCache = {}; + +PIXI.TextureCacheIdGenerator = 0; + +/** + * A texture stores the information that represents an image or part of an image. It cannot be added + * to the display list directly. To do this use PIXI.Sprite. If no frame is provided then the whole image is used + * + * @class Texture + * @uses EventTarget + * @constructor + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frame {Rectangle} The rectangle frame of the texture to show + */ +PIXI.Texture = function(baseTexture, frame) +{ + PIXI.EventTarget.call( this ); + + if(!frame) + { + this.noFrame = true; + frame = new PIXI.Rectangle(0,0,1,1); + } + + if(baseTexture instanceof PIXI.Texture) + baseTexture = baseTexture.baseTexture; + + /** + * The base texture of that this texture uses + * + * @property baseTexture + * @type BaseTexture + */ + this.baseTexture = baseTexture; + + /** + * The frame specifies the region of the base texture that this texture uses + * + * @property frame + * @type Rectangle + */ + this.frame = frame; + + /** + * The trim point + * + * @property trim + * @type Rectangle + */ + this.trim = null; + + this.scope = this; + + if(baseTexture.hasLoaded) + { + if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); + + this.setFrame(frame); + } + else + { + var scope = this; + baseTexture.addEventListener('loaded', function(){ scope.onBaseTextureLoaded(); }); + } +}; + +PIXI.Texture.prototype.constructor = PIXI.Texture; + +/** + * Called when the base texture is loaded + * + * @method onBaseTextureLoaded + * @param event + * @private + */ +PIXI.Texture.prototype.onBaseTextureLoaded = function() +{ + var baseTexture = this.baseTexture; + baseTexture.removeEventListener( 'loaded', this.onLoaded ); + + if(this.noFrame)this.frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); + + this.setFrame(this.frame); + + this.scope.dispatchEvent( { type: 'update', content: this } ); +}; + +/** + * Destroys this texture + * + * @method destroy + * @param destroyBase {Boolean} Whether to destroy the base texture as well + */ +PIXI.Texture.prototype.destroy = function(destroyBase) +{ + if(destroyBase) this.baseTexture.destroy(); +}; + +/** + * Specifies the rectangle region of the baseTexture + * + * @method setFrame + * @param frame {Rectangle} The frame of the texture to set it to + */ +PIXI.Texture.prototype.setFrame = function(frame) +{ + this.frame = frame; + this.width = frame.width; + this.height = frame.height; + + if(frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + { + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); + } + + this.updateFrame = true; + + PIXI.Texture.frameUpdates.push(this); + + + //this.dispatchEvent( { type: 'update', content: this } ); +}; + +PIXI.Texture.prototype._updateWebGLuvs = function() +{ + if(!this._uvs)this._uvs = new PIXI.TextureUvs(); + + var frame = this.frame; + var tw = this.baseTexture.width; + var th = this.baseTexture.height; + + this._uvs.x0 = frame.x / tw; + this._uvs.y0 = frame.y / th; + + this._uvs.x1 = (frame.x + frame.width) / tw; + this._uvs.y1 = frame.y / th; + + this._uvs.x2 = (frame.x + frame.width) / tw; + this._uvs.y2 = (frame.y + frame.height) / th; + + this._uvs.x3 = frame.x / tw; + this._uvs.y3 = (frame.y + frame.height) / th; +}; + +/** + * Helper function that returns a texture based on an image url + * If the image is not in the texture cache it will be created and loaded + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + * @return Texture + */ +PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode) +{ + var texture = PIXI.TextureCache[imageUrl]; + + if(!texture) + { + texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin, scaleMode)); + PIXI.TextureCache[imageUrl] = texture; + } + + return texture; +}; + +/** + * Helper function that returns a texture based on a frame id + * If the frame id is not in the texture cache an error will be thrown + * + * @static + * @method fromFrame + * @param frameId {String} The frame id of the texture + * @return Texture + */ +PIXI.Texture.fromFrame = function(frameId) +{ + var texture = PIXI.TextureCache[frameId]; + if(!texture) throw new Error('The frameId "' + frameId + '" does not exist in the texture cache '); + return texture; +}; + +/** + * Helper function that returns a texture based on a canvas element + * If the canvas is not in the texture cache it will be created and loaded + * + * @static + * @method fromCanvas + * @param canvas {Canvas} The canvas element source of the texture + * @return Texture + */ +PIXI.Texture.fromCanvas = function(canvas, scaleMode) +{ + var baseTexture = PIXI.BaseTexture.fromCanvas(canvas, scaleMode); + + return new PIXI.Texture( baseTexture ); + +}; + + +/** + * Adds a texture to the textureCache. + * + * @static + * @method addTextureToCache + * @param texture {Texture} + * @param id {String} the id that the texture will be stored against. + */ +PIXI.Texture.addTextureToCache = function(texture, id) +{ + PIXI.TextureCache[id] = texture; +}; + +/** + * Remove a texture from the textureCache. + * + * @static + * @method removeTextureFromCache + * @param id {String} the id of the texture to be removed + * @return {Texture} the texture that was removed + */ +PIXI.Texture.removeTextureFromCache = function(id) +{ + var texture = PIXI.TextureCache[id]; + PIXI.TextureCache[id] = null; + return texture; +}; + +// this is more for webGL.. it contains updated frames.. +PIXI.Texture.frameUpdates = []; + +PIXI.TextureUvs = function() +{ + this.x0 = 0; + this.y0 = 0; + + this.x1 = 0; + this.y1 = 0; + + this.x2 = 0; + this.y2 = 0; + + this.x3 = 0; + this.y4 = 0; + + +}; + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + A RenderTexture is a special texture that allows any pixi displayObject to be rendered to it. + + __Hint__: All DisplayObjects (exmpl. Sprites) that render on RenderTexture should be preloaded. + Otherwise black rectangles will be drawn instead. + + RenderTexture takes snapshot of DisplayObject passed to render method. If DisplayObject is passed to render method, position and rotation of it will be ignored. For example: + + var renderTexture = new PIXI.RenderTexture(800, 600); + var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); + sprite.position.x = 800/2; + sprite.position.y = 600/2; + sprite.anchor.x = 0.5; + sprite.anchor.y = 0.5; + renderTexture.render(sprite); + + Sprite in this case will be rendered to 0,0 position. To render this sprite at center DisplayObjectContainer should be used: + + var doc = new PIXI.DisplayObjectContainer(); + doc.addChild(sprite); + renderTexture.render(doc); // Renders to center of renderTexture + + * @class RenderTexture + * @extends Texture + * @constructor + * @param width {Number} The width of the render texture + * @param height {Number} The height of the render texture + */ +PIXI.RenderTexture = function(width, height, renderer) +{ + PIXI.EventTarget.call( this ); + + /** + * The with of the render texture + * + * @property width + * @type Number + */ + this.width = width || 100; + /** + * The height of the render texture + * + * @property height + * @type Number + */ + this.height = height || 100; + + /** + * The framing rectangle of the render texture + * + * @property frame + * @type Rectangle + */ + this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); + + /** + * The base texture object that this texture uses + * + * @property baseTexture + * @type BaseTexture + */ + this.baseTexture = new PIXI.BaseTexture(); + this.baseTexture.width = this.width; + this.baseTexture.height = this.height; + this.baseTexture._glTextures = []; + + this.baseTexture.hasLoaded = true; + + // each render texture can only belong to one renderer at the moment if its webGL + this.renderer = renderer || PIXI.defaultRenderer; + + if(this.renderer.type === PIXI.WEBGL_RENDERER) + { + var gl = this.renderer.gl; + + this.textureBuffer = new PIXI.FilterTexture(gl, this.width, this.height); + this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; + + this.render = this.renderWebGL; + this.projection = new PIXI.Point(this.width/2 , -this.height/2); + } + else + { + this.render = this.renderCanvas; + this.textureBuffer = new PIXI.CanvasBuffer(this.width, this.height); + this.baseTexture.source = this.textureBuffer.canvas; + } + + PIXI.Texture.frameUpdates.push(this); + + +}; + +PIXI.RenderTexture.prototype = Object.create(PIXI.Texture.prototype); +PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture; + +PIXI.RenderTexture.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.frame.width = this.width; + this.frame.height = this.height; + + if(this.renderer.type === PIXI.WEBGL_RENDERER) + { + this.projection.x = this.width / 2; + this.projection.y = -this.height / 2; + + var gl = this.renderer.gl; + gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTextures[gl.id]); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + else + { + this.textureBuffer.resize(this.width, this.height); + } + + PIXI.Texture.frameUpdates.push(this); +}; + +/** + * This function will draw the display object to the texture. + * + * @method renderWebGL + * @param displayObject {DisplayObject} The display object to render this texture on + * @param clear {Boolean} If true the texture will be cleared before the displayObject is drawn + * @private + */ +PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear) +{ + //TOOD replace position with matrix.. + var gl = this.renderer.gl; + + gl.colorMask(true, true, true, true); + + gl.viewport(0, 0, this.width, this.height); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer ); + + if(clear)this.textureBuffer.clear(); + + // THIS WILL MESS WITH HIT TESTING! + var children = displayObject.children; + + //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; + displayObject.worldTransform = PIXI.RenderTexture.tempMatrix; + // modify to flip... + displayObject.worldTransform.d = -1; + displayObject.worldTransform.ty = this.projection.y * -2; + + if(position) + { + displayObject.worldTransform.tx = position.x; + displayObject.worldTransform.ty -= position.y; + } + + for(var i=0,j=children.length; i} assetURLs an array of image/sprite sheet urls that you would like loaded + * supported. Supported image formats include 'jpeg', 'jpg', 'png', 'gif'. Supported + * sprite sheet data formats only include 'JSON' at this time. Supported bitmap font + * data formats include 'xml' and 'fnt'. + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.AssetLoader = function(assetURLs, crossorigin) +{ + PIXI.EventTarget.call(this); + + /** + * The array of asset URLs that are going to be loaded + * + * @property assetURLs + * @type Array + */ + this.assetURLs = assetURLs; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * Maps file extension to loader types + * + * @property loadersByType + * @type Object + */ + this.loadersByType = { + 'jpg': PIXI.ImageLoader, + 'jpeg': PIXI.ImageLoader, + 'png': PIXI.ImageLoader, + 'gif': PIXI.ImageLoader, + 'json': PIXI.JsonLoader, + 'atlas': PIXI.AtlasLoader, + 'anim': PIXI.SpineLoader, + 'xml': PIXI.BitmapFontLoader, + 'fnt': PIXI.BitmapFontLoader + }; +}; + +/** + * Fired when an item has loaded + * @event onProgress + */ + +/** + * Fired when all the assets have loaded + * @event onComplete + */ + +// constructor +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; + +/** + * Given a filename, returns its extension, wil + * + * @method _getDataType + * @param str {String} the name of the asset + */ +PIXI.AssetLoader.prototype._getDataType = function(str) +{ + var test = 'data:'; + //starts with 'data:' + var start = str.slice(0, test.length).toLowerCase(); + if (start === test) { + var data = str.slice(test.length); + + var sepIdx = data.indexOf(','); + if (sepIdx === -1) //malformed data URI scheme + return null; + + //e.g. 'image/gif;base64' => 'image/gif' + var info = data.slice(0, sepIdx).split(';')[0]; + + //We might need to handle some special cases here... + //standardize text/plain to 'txt' file extension + if (!info || info.toLowerCase() === 'text/plain') + return 'txt'; + + //User specified mime type, try splitting it by '/' + return info.split('/').pop().toLowerCase(); + } + + return null; +}; + +/** + * Starts loading the assets sequentially + * + * @method load + */ +PIXI.AssetLoader.prototype.load = function() +{ + var scope = this; + + function onLoad(evt) { + scope.onAssetLoaded(evt.loader); + } + + this.loadCount = this.assetURLs.length; + + for (var i=0; i < this.assetURLs.length; i++) + { + var fileName = this.assetURLs[i]; + //first see if we have a data URI scheme.. + var fileType = this._getDataType(fileName); + + //if not, assume it's a file URI + if (!fileType) + fileType = fileName.split('?').shift().split('.').pop().toLowerCase(); + + var Constructor = this.loadersByType[fileType]; + if(!Constructor) + throw new Error(fileType + ' is an unsupported file type'); + + var loader = new Constructor(fileName, this.crossorigin); + + loader.addEventListener('loaded', onLoad); + loader.load(); + } +}; + +/** + * Invoked after each file is loaded + * + * @method onAssetLoaded + * @private + */ +PIXI.AssetLoader.prototype.onAssetLoaded = function(loader) +{ + this.loadCount--; + this.dispatchEvent({ type: 'onProgress', content: this, loader: loader }); + if (this.onProgress) this.onProgress(loader); + + if (!this.loadCount) + { + this.dispatchEvent({type: 'onComplete', content: this}); + if(this.onComplete) this.onComplete(); + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The json file loader is used to load in JSON data and parse it + * When loaded this class will dispatch a 'loaded' event + * If loading fails this class will dispatch an 'error' event + * + * @class JsonLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.JsonLoader = function (url, crossorigin) { + PIXI.EventTarget.call(this); + + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ''); + + /** + * [read-only] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ + this.loaded = false; + +}; + +// constructor +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; + +/** + * Loads the JSON data + * + * @method load + */ +PIXI.JsonLoader.prototype.load = function () { + this.ajaxRequest = new PIXI.AjaxRequest(this.crossorigin); + var scope = this; + this.ajaxRequest.onreadystatechange = function () { + scope.onJSONLoaded(); + }; + + this.ajaxRequest.open('GET', this.url, true); + if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/json'); + this.ajaxRequest.send(null); +}; + +/** + * Invoke when JSON file is loaded + * + * @method onJSONLoaded + * @private + */ +PIXI.JsonLoader.prototype.onJSONLoaded = function () { + if (this.ajaxRequest.readyState === 4) { + if (this.ajaxRequest.status === 200 || window.location.protocol.indexOf('http') === -1) { + this.json = JSON.parse(this.ajaxRequest.responseText); + + if(this.json.frames) + { + // sprite sheet + var scope = this; + var textureUrl = this.baseUrl + this.json.meta.image; + var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); + var frameData = this.json.frames; + + this.texture = image.texture.baseTexture; + image.addEventListener('loaded', function() { + scope.onLoaded(); + }); + + for (var i in frameData) { + var rect = frameData[i].frame; + if (rect) { + PIXI.TextureCache[i] = new PIXI.Texture(this.texture, { + x: rect.x, + y: rect.y, + width: rect.w, + height: rect.h + }); + + // check to see ifthe sprite ha been trimmed.. + if (frameData[i].trimmed) { + + var texture = PIXI.TextureCache[i]; + + var actualSize = frameData[i].sourceSize; + var realSize = frameData[i].spriteSourceSize; + + texture.trim = new PIXI.Rectangle(realSize.x, realSize.y, actualSize.w, actualSize.h); + } + } + } + + image.load(); + + } + else if(this.json.bones) + { + // spine animation + var spineJsonParser = new spine.SkeletonJson(); + var skeletonData = spineJsonParser.readSkeletonData(this.json); + PIXI.AnimCache[this.url] = skeletonData; + this.onLoaded(); + } + else + { + this.onLoaded(); + } + } + else + { + this.onError(); + } + } +}; + +/** + * Invoke when json file loaded + * + * @method onLoaded + * @private + */ +PIXI.JsonLoader.prototype.onLoaded = function () { + this.loaded = true; + this.dispatchEvent({ + type: 'loaded', + content: this + }); +}; + +/** + * Invoke when error occured + * + * @method onError + * @private + */ +PIXI.JsonLoader.prototype.onError = function () { + this.dispatchEvent({ + type: 'error', + content: this + }); +}; +/** + * @author Martin Kelm http://mkelm.github.com + */ + +/** + * The atlas file loader is used to load in Atlas data and parse it + * When loaded this class will dispatch a 'loaded' event + * If loading fails this class will dispatch an 'error' event + * @class AtlasLoader + * @extends EventTarget + * @constructor + * @param {String} url the url of the JSON file + * @param {Boolean} crossorigin + */ + +PIXI.AtlasLoader = function (url, crossorigin) { + PIXI.EventTarget.call(this); + this.url = url; + this.baseUrl = url.replace(/[^\/]*$/, ''); + this.crossorigin = crossorigin; + this.loaded = false; + +}; + +// constructor +PIXI.AtlasLoader.constructor = PIXI.AtlasLoader; + + + /** + * Starts loading the JSON file + * + * @method load + */ +PIXI.AtlasLoader.prototype.load = function () { + this.ajaxRequest = new PIXI.AjaxRequest(); + this.ajaxRequest.onreadystatechange = this.onAtlasLoaded.bind(this); + + this.ajaxRequest.open('GET', this.url, true); + if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/json'); + this.ajaxRequest.send(null); +}; + +/** + * Invoke when JSON file is loaded + * @method onAtlasLoaded + * @private + */ +PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { + if (this.ajaxRequest.readyState === 4) { + if (this.ajaxRequest.status === 200 || window.location.href.indexOf('http') === -1) { + this.atlas = { + meta : { + image : [] + }, + frames : [] + }; + var result = this.ajaxRequest.responseText.split(/\r?\n/); + var lineCount = -3; + + var currentImageId = 0; + var currentFrame = null; + var nameInNextLine = false; + + var i = 0, + j = 0, + selfOnLoaded = this.onLoaded.bind(this); + + // parser without rotation support yet! + for (i = 0; i < result.length; i++) { + result[i] = result[i].replace(/^\s+|\s+$/g, ''); + if (result[i] === '') { + nameInNextLine = i+1; + } + if (result[i].length > 0) { + if (nameInNextLine === i) { + this.atlas.meta.image.push(result[i]); + currentImageId = this.atlas.meta.image.length - 1; + this.atlas.frames.push({}); + lineCount = -3; + } else if (lineCount > 0) { + if (lineCount % 7 === 1) { // frame name + if (currentFrame != null) { //jshint ignore:line + this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; + } + currentFrame = { name: result[i], frame : {} }; + } else { + var text = result[i].split(' '); + if (lineCount % 7 === 3) { // position + currentFrame.frame.x = Number(text[1].replace(',', '')); + currentFrame.frame.y = Number(text[2]); + } else if (lineCount % 7 === 4) { // size + currentFrame.frame.w = Number(text[1].replace(',', '')); + currentFrame.frame.h = Number(text[2]); + } else if (lineCount % 7 === 5) { // real size + var realSize = { + x : 0, + y : 0, + w : Number(text[1].replace(',', '')), + h : Number(text[2]) + }; + + if (realSize.w > currentFrame.frame.w || realSize.h > currentFrame.frame.h) { + currentFrame.trimmed = true; + currentFrame.realSize = realSize; + } else { + currentFrame.trimmed = false; + } + } + } + } + lineCount++; + } + } + + if (currentFrame != null) { //jshint ignore:line + this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; + } + + if (this.atlas.meta.image.length > 0) { + this.images = []; + for (j = 0; j < this.atlas.meta.image.length; j++) { + // sprite sheet + var textureUrl = this.baseUrl + this.atlas.meta.image[j]; + var frameData = this.atlas.frames[j]; + this.images.push(new PIXI.ImageLoader(textureUrl, this.crossorigin)); + + for (i in frameData) { + var rect = frameData[i].frame; + if (rect) { + PIXI.TextureCache[i] = new PIXI.Texture(this.images[j].texture.baseTexture, { + x: rect.x, + y: rect.y, + width: rect.w, + height: rect.h + }); + if (frameData[i].trimmed) { + PIXI.TextureCache[i].realSize = frameData[i].realSize; + // trim in pixi not supported yet, todo update trim properties if it is done ... + PIXI.TextureCache[i].trim.x = 0; + PIXI.TextureCache[i].trim.y = 0; + } + } + } + } + + this.currentImageId = 0; + for (j = 0; j < this.images.length; j++) { + this.images[j].addEventListener('loaded', selfOnLoaded); + } + this.images[this.currentImageId].load(); + + } else { + this.onLoaded(); + } + + } else { + this.onError(); + } + } +}; + +/** + * Invoke when json file has loaded + * @method onLoaded + * @private + */ +PIXI.AtlasLoader.prototype.onLoaded = function () { + if (this.images.length - 1 > this.currentImageId) { + this.currentImageId++; + this.images[this.currentImageId].load(); + } else { + this.loaded = true; + this.dispatchEvent({ + type: 'loaded', + content: this + }); + } +}; + +/** + * Invoke when error occured + * @method onError + * @private + */ +PIXI.AtlasLoader.prototype.onError = function () { + this.dispatchEvent({ + type: 'error', + content: this + }); +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The sprite sheet loader is used to load in JSON sprite sheet data + * To generate the data you can use http://www.codeandweb.com/texturepacker and publish in the 'JSON' format + * There is a free version so thats nice, although the paid version is great value for money. + * It is highly recommended to use Sprite sheets (also know as a 'texture atlas') as it means sprites can be batched and drawn together for highly increased rendering speed. + * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() + * This loader will load the image file that the Spritesheet points to as well as the data. + * When loaded this class will dispatch a 'loaded' event + * + * @class SpriteSheetLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.SpriteSheetLoader = function (url, crossorigin) { + /* + * i use texture packer to load the assets.. + * http://www.codeandweb.com/texturepacker + * make sure to set the format as 'JSON' + */ + PIXI.EventTarget.call(this); + + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ''); + + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = null; + + /** + * The frames of the sprite sheet + * + * @property frames + * @type Object + */ + this.frames = {}; +}; + +// constructor +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; + +/** + * This will begin loading the JSON file + * + * @method load + */ +PIXI.SpriteSheetLoader.prototype.load = function () { + var scope = this; + var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); + jsonLoader.addEventListener('loaded', function (event) { + scope.json = event.content.json; + scope.onLoaded(); + }); + jsonLoader.load(); +}; + +/** + * Invoke when all files are loaded (json and texture) + * + * @method onLoaded + * @private + */ +PIXI.SpriteSheetLoader.prototype.onLoaded = function () { + this.dispatchEvent({ + type: 'loaded', + content: this + }); +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The image loader class is responsible for loading images file formats ('jpeg', 'jpg', 'png' and 'gif') + * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() + * When loaded this class will dispatch a 'loaded' event + * + * @class ImageLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the image + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.ImageLoader = function(url, crossorigin) +{ + PIXI.EventTarget.call(this); + + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = PIXI.Texture.fromImage(url, crossorigin); + + /** + * if the image is loaded with loadFramedSpriteSheet + * frames will contain the sprite sheet frames + * + */ + this.frames = []; +}; + +// constructor +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; + +/** + * Loads image or takes it from cache + * + * @method load + */ +PIXI.ImageLoader.prototype.load = function() +{ + if(!this.texture.baseTexture.hasLoaded) + { + var scope = this; + this.texture.baseTexture.addEventListener('loaded', function() + { + scope.onLoaded(); + }); + } + else + { + this.onLoaded(); + } +}; + +/** + * Invoked when image file is loaded or it is already cached and ready to use + * + * @method onLoaded + * @private + */ +PIXI.ImageLoader.prototype.onLoaded = function() +{ + this.dispatchEvent({type: 'loaded', content: this}); +}; + +/** + * Loads image and split it to uniform sized frames + * + * + * @method loadFramedSpriteSheet + * @param frameWidth {Number} width of each frame + * @param frameHeight {Number} height of each frame + * @param textureName {String} if given, the frames will be cached in - format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y -1 ) + { + var args = [ + '%c %c %c Pixi.js ' + PIXI.VERSION + ' - ' + type + ' %c ' + ' %c ' + ' http://www.pixijs.com/ %c %c ♥%c♥%c♥ ', + 'background: #ff66a5', + 'background: #ff66a5', + 'color: #ff66a5; background: #030307;', + 'background: #ff66a5', + 'background: #ffc3dc', + 'background: #ff66a5', + 'color: #ff2424; background: #fff', + 'color: #ff2424; background: #fff', + 'color: #ff2424; background: #fff' + ]; + + console.log.apply(console, args); + } + else if (window['console']) + { + console.log('Pixi.js ' + PIXI.VERSION + ' - http://www.pixijs.com/'); + } + + PIXI.dontSayHello = true; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -111,16 +260,22 @@ PIXI.Point.prototype.clone = function() return new PIXI.Point(this.x, this.y); }; -// constructor -PIXI.Point.prototype.constructor = PIXI.Point; - +/** + * Sets the point to a new x and y position. + * If y is omitted, both x and y will be set to x. + * + * @method set + * @param [x=0] {Number} position of the point on the x axis + * @param [y=0] {Number} position of the point on the y axis + */ PIXI.Point.prototype.set = function(x, y) { this.x = x || 0; this.y = y || ( (y !== 0) ? this.x : 0 ) ; }; - +// constructor +PIXI.Point.prototype.constructor = PIXI.Point; /** * @author Mat Groves http://matgroves.com/ */ @@ -130,8 +285,8 @@ PIXI.Point.prototype.set = function(x, y) * * @class Rectangle * @constructor - * @param x {Number} The X coord of the upper-left corner of the rectangle - * @param y {Number} The Y coord of the upper-left corner of the rectangle + * @param x {Number} The X coordinate of the upper-left corner of the rectangle + * @param y {Number} The Y coordinate of the upper-left corner of the rectangle * @param width {Number} The overall width of this rectangle * @param height {Number} The overall height of this rectangle */ @@ -178,12 +333,12 @@ PIXI.Rectangle.prototype.clone = function() }; /** - * Checks whether the x and y coordinates passed to this function are contained within this Rectangle + * Checks whether the x and y coordinates given are contained within this Rectangle * * @method contains * @param x {Number} The X coordinate of the point to test * @param y {Number} The Y coordinate of the point to test - * @return {Boolean} Whether the x/y coords are within this Rectangle + * @return {Boolean} Whether the x/y coordinates are within this Rectangle */ PIXI.Rectangle.prototype.contains = function(x, y) { @@ -207,7 +362,7 @@ PIXI.Rectangle.prototype.contains = function(x, y) // constructor PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; -PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); +PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); /** * @author Adrien Brault */ @@ -215,7 +370,7 @@ PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); /** * @class Polygon * @constructor - * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, + * @param points* {Array(Point)|Array(Number)|Point...|Number...} This can be an array of Points that form the polygon, * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are @@ -224,21 +379,21 @@ PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); PIXI.Polygon = function(points) { //if points isn't an array, use arguments as the array - if(!(points instanceof Array)) - points = Array.prototype.slice.call(arguments); + if(!(points instanceof Array))points = Array.prototype.slice.call(arguments); //if this is a flat array of numbers, convert it to points - if(typeof points[0] === 'number') { + if(points[0] instanceof PIXI.Point) + { var p = []; - for(var i = 0, il = points.length; i < il; i+=2) { - p.push( - new PIXI.Point(points[i], points[i + 1]) - ); + for(var i = 0, il = points.length; i < il; i++) + { + p.push(points[i].x, points[i].y); } points = p; } + this.closed = true; this.points = points; }; @@ -250,11 +405,7 @@ PIXI.Polygon = function(points) */ PIXI.Polygon.prototype.clone = function() { - var points = []; - for (var i=0; i y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if(intersect) inside = !inside; @@ -285,7 +439,7 @@ PIXI.Polygon.prototype.contains = function(x, y) // constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - + /** * @author Chad Engler */ @@ -295,8 +449,8 @@ PIXI.Polygon.prototype.constructor = PIXI.Polygon; * * @class Circle * @constructor - * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this circle - * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this circle + * @param x {Number} The X coordinate of the center of this circle + * @param y {Number} The Y coordinate of the center of this circle * @param radius {Number} The radius of the circle */ PIXI.Circle = function(x, y, radius) @@ -327,7 +481,7 @@ PIXI.Circle = function(x, y, radius) * Creates a clone of this Circle instance * * @method clone - * @return {Circle} a copy of the polygon + * @return {Circle} a copy of the Circle */ PIXI.Circle.prototype.clone = function() { @@ -335,12 +489,12 @@ PIXI.Circle.prototype.clone = function() }; /** - * Checks whether the x, and y coordinates passed to this function are contained within this circle + * Checks whether the x and y coordinates given are contained within this circle * * @method contains * @param x {Number} The X coordinate of the point to test * @param y {Number} The Y coordinate of the point to test - * @return {Boolean} Whether the x/y coordinates are within this polygon + * @return {Boolean} Whether the x/y coordinates are within this Circle */ PIXI.Circle.prototype.contains = function(x, y) { @@ -357,10 +511,20 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); }; +/** +* Returns the framing rectangle of the circle as a PIXI.Rectangle object +* +* @method getBounds +* @return {Rectangle} the framing rectangle +*/ +PIXI.Circle.prototype.getBounds = function() +{ + return new PIXI.Rectangle(this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2); +}; + // constructor PIXI.Circle.prototype.constructor = PIXI.Circle; - /** * @author Chad Engler */ @@ -370,10 +534,10 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * * @class Ellipse * @constructor - * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this ellipse - * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this ellipse - * @param width {Number} The overall width of this ellipse - * @param height {Number} The overall height of this ellipse + * @param x {Number} The X coordinate of the center of the ellipse + * @param y {Number} The Y coordinate of the center of the ellipse + * @param width {Number} The half width of this ellipse + * @param height {Number} The half height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -418,7 +582,7 @@ PIXI.Ellipse.prototype.clone = function() }; /** - * Checks whether the x and y coordinates passed to this function are contained within this ellipse + * Checks whether the x and y coordinates given are contained within this ellipse * * @method contains * @param x {Number} The X coordinate of the point to test @@ -448,52 +612,178 @@ PIXI.Ellipse.prototype.contains = function(x, y) */ PIXI.Ellipse.prototype.getBounds = function() { - return new PIXI.Rectangle(this.x, this.y, this.width, this.height); + return new PIXI.Rectangle(this.x - this.width, this.y - this.height, this.width, this.height); }; // constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - + +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * The Rounded Rectangle object is an area defined by its position and has nice rounded corners, as indicated by its top-left corner point (x, y) and by its width and its height. + * + * @class RoundedRectangle + * @constructor + * @param x {Number} The X coordinate of the upper-left corner of the rounded rectangle + * @param y {Number} The Y coordinate of the upper-left corner of the rounded rectangle + * @param width {Number} The overall width of this rounded rectangle + * @param height {Number} The overall height of this rounded rectangle + * @param radius {Number} The overall radius of this corners of this rounded rectangle + */ +PIXI.RoundedRectangle = function(x, y, width, height, radius) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; + + /** + * @property radius + * @type Number + * @default 20 + */ + this.radius = radius || 20; +}; + +/** + * Creates a clone of this Rounded Rectangle + * + * @method clone + * @return {RoundedRectangle} a copy of the rounded rectangle + */ +PIXI.RoundedRectangle.prototype.clone = function() +{ + return new PIXI.RoundedRectangle(this.x, this.y, this.width, this.height, this.radius); +}; + +/** + * Checks whether the x and y coordinates given are contained within this Rounded Rectangle + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this Rounded Rectangle + */ +PIXI.RoundedRectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + var x1 = this.x; + if(x >= x1 && x <= x1 + this.width) + { + var y1 = this.y; + + if(y >= y1 && y <= y1 + this.height) + { + return true; + } + } + + return false; +}; + +// constructor +PIXI.RoundedRectangle.prototype.constructor = PIXI.RoundedRectangle; + + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ -PIXI.determineMatrixArrayType = function() { - return (typeof Float32Array !== 'undefined') ? Float32Array : Array; -}; - -/* -* @class Matrix2 -* The Matrix2 class will choose the best type of array to use between -* a regular javascript Array and a Float32Array if the latter is available -* -*/ -PIXI.Matrix2 = PIXI.determineMatrixArrayType(); - -/* -* @class Matrix -* The Matrix class is now an object, which makes it a lot faster, -* here is a representation of it : -* | a | b | tx| -* | c | c | ty| -* | 0 | 0 | 1 | -* -*/ +/** + * The Matrix class is now an object, which makes it a lot faster, + * here is a representation of it : + * | a | b | tx| + * | c | d | ty| + * | 0 | 0 | 1 | + * + * @class Matrix + * @constructor + */ PIXI.Matrix = function() { + /** + * @property a + * @type Number + * @default 1 + */ this.a = 1; + + /** + * @property b + * @type Number + * @default 0 + */ this.b = 0; + + /** + * @property c + * @type Number + * @default 0 + */ this.c = 0; + + /** + * @property d + * @type Number + * @default 1 + */ this.d = 1; + + /** + * @property tx + * @type Number + * @default 0 + */ this.tx = 0; + + /** + * @property ty + * @type Number + * @default 0 + */ this.ty = 0; }; /** - * Creates a pixi matrix object based on the array given as a parameter + * Creates a Matrix object based on the given array. The Element to Matrix mapping order is as follows: + * + * a = array[0] + * b = array[1] + * c = array[3] + * d = array[4] + * tx = array[2] + * ty = array[5] * * @method fromArray - * @param array {Array} The array that the matrix will be filled with + * @param array {Array} The array that the matrix will be populated from. */ PIXI.Matrix.prototype.fromArray = function(array) { @@ -506,52 +796,199 @@ PIXI.Matrix.prototype.fromArray = function(array) }; /** - * Creates an array from the current Matrix object + * Creates an array from the current Matrix object. * * @method toArray * @param transpose {Boolean} Whether we need to transpose the matrix or not - * @return array {Array} the newly created array which contains the matrix + * @return {Array} the newly created array which contains the matrix */ PIXI.Matrix.prototype.toArray = function(transpose) { - if(!this.array) this.array = new Float32Array(9); + if(!this.array) this.array = new PIXI.Float32Array(9); var array = this.array; if(transpose) { - this.array[0] = this.a; - this.array[1] = this.c; - this.array[2] = 0; - this.array[3] = this.b; - this.array[4] = this.d; - this.array[5] = 0; - this.array[6] = this.tx; - this.array[7] = this.ty; - this.array[8] = 1; + array[0] = this.a; + array[1] = this.b; + array[2] = 0; + array[3] = this.c; + array[4] = this.d; + array[5] = 0; + array[6] = this.tx; + array[7] = this.ty; + array[8] = 1; } else { - this.array[0] = this.a; - this.array[1] = this.b; - this.array[2] = this.tx; - this.array[3] = this.c; - this.array[4] = this.d; - this.array[5] = this.ty; - this.array[6] = 0; - this.array[7] = 0; - this.array[8] = 1; + array[0] = this.a; + array[1] = this.c; + array[2] = this.tx; + array[3] = this.b; + array[4] = this.d; + array[5] = this.ty; + array[6] = 0; + array[7] = 0; + array[8] = 1; } - return array;//[this.a, this.b, this.tx, this.c, this.d, this.ty, 0, 0, 1]; + return array; }; -PIXI.identityMatrix = new PIXI.Matrix(); +/** + * Get a new position with the current transformation applied. + * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering) + * + * @method apply + * @param pos {Point} The origin + * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) + * @return {Point} The new point, transformed through this matrix + */ +PIXI.Matrix.prototype.apply = function(pos, newPos) +{ + newPos = newPos || new PIXI.Point(); + + newPos.x = this.a * pos.x + this.c * pos.y + this.tx; + newPos.y = this.b * pos.x + this.d * pos.y + this.ty; + + return newPos; +}; + +/** + * Get a new position with the inverse of the current transformation applied. + * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input) + * + * @method applyInverse + * @param pos {Point} The origin + * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) + * @return {Point} The new point, inverse-transformed through this matrix + */ +PIXI.Matrix.prototype.applyInverse = function(pos, newPos) +{ + newPos = newPos || new PIXI.Point(); + + var id = 1 / (this.a * this.d + this.c * -this.b); + + newPos.x = this.d * id * pos.x + -this.c * id * pos.y + (this.ty * this.c - this.tx * this.d) * id; + newPos.y = this.a * id * pos.y + -this.b * id * pos.x + (-this.ty * this.a + this.tx * this.b) * id; + + return newPos; +}; + +/** + * Translates the matrix on the x and y. + * + * @method translate + * @param {Number} x + * @param {Number} y + * @return {Matrix} This matrix. Good for chaining method calls. + **/ +PIXI.Matrix.prototype.translate = function(x, y) +{ + this.tx += x; + this.ty += y; + + return this; +}; + +/** + * Applies a scale transformation to the matrix. + * + * @method scale + * @param {Number} x The amount to scale horizontally + * @param {Number} y The amount to scale vertically + * @return {Matrix} This matrix. Good for chaining method calls. + **/ +PIXI.Matrix.prototype.scale = function(x, y) +{ + this.a *= x; + this.d *= y; + this.c *= x; + this.b *= y; + this.tx *= x; + this.ty *= y; + + return this; +}; + + +/** + * Applies a rotation transformation to the matrix. + * @method rotate + * @param {Number} angle The angle in radians. + * @return {Matrix} This matrix. Good for chaining method calls. + **/ +PIXI.Matrix.prototype.rotate = function(angle) +{ + var cos = Math.cos( angle ); + var sin = Math.sin( angle ); + + var a1 = this.a; + var c1 = this.c; + var tx1 = this.tx; + + this.a = a1 * cos-this.b * sin; + this.b = a1 * sin+this.b * cos; + this.c = c1 * cos-this.d * sin; + this.d = c1 * sin+this.d * cos; + this.tx = tx1 * cos - this.ty * sin; + this.ty = tx1 * sin + this.ty * cos; + + return this; +}; + +/** + * Appends the given Matrix to this Matrix. + * + * @method append + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +PIXI.Matrix.prototype.append = function(matrix) +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + + this.a = matrix.a * a1 + matrix.b * c1; + this.b = matrix.a * b1 + matrix.b * d1; + this.c = matrix.c * a1 + matrix.d * c1; + this.d = matrix.c * b1 + matrix.d * d1; + + this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx; + this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty; + + return this; +}; + +/** + * Resets this Matix to an identity (default) matrix. + * + * @method identity + * @return {Matrix} This matrix. Good for chaining method calls. + */ +PIXI.Matrix.prototype.identity = function() +{ + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.tx = 0; + this.ty = 0; + + return this; +}; + +PIXI.identityMatrix = new PIXI.Matrix(); + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The base class for all objects that are rendered on the screen. + * This is an abstract class and should not be used on its own rather it should be extended. * * @class DisplayObject * @constructor @@ -670,7 +1107,7 @@ PIXI.DisplayObject = function() /** * This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true - * + * * @property defaultCursor * @type String * @@ -681,41 +1118,38 @@ PIXI.DisplayObject = function() * [read-only] Current transform of the object based on world (parent) factors * * @property worldTransform - * @type Mat3 + * @type Matrix * @readOnly * @private */ this.worldTransform = new PIXI.Matrix(); /** - * [NYI] Unknown + * cached sin rotation and cos rotation * - * @property color - * @type Array<> + * @property _sr + * @type Number * @private */ - this.color = []; + this._sr = 0; /** - * [NYI] Holds whether or not this object is dynamic, for rendering optimization + * cached sin rotation and cos rotation * - * @property dynamic - * @type Boolean + * @property _cr + * @type Number * @private */ - this.dynamic = true; - - // cached sin rotation and cos rotation - this._sr = 0; this._cr = 1; /** - * The area the filter is applied to + * The area the filter is applied to like the hitArea this is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle * * @property filterArea * @type Rectangle */ - this.filterArea = new PIXI.Rectangle(0,0,1,1); + this.filterArea = null;//new PIXI.Rectangle(0,0,1,1); /** * The original, cached bounds of the object @@ -725,6 +1159,7 @@ PIXI.DisplayObject = function() * @private */ this._bounds = new PIXI.Rectangle(0, 0, 1, 1); + /** * The most up-to-date bounds of the object * @@ -733,6 +1168,7 @@ PIXI.DisplayObject = function() * @private */ this._currentBounds = null; + /** * The original, cached mask of the object * @@ -742,36 +1178,29 @@ PIXI.DisplayObject = function() */ this._mask = null; + /** + * Cached internal flag. + * + * @property _cacheAsBitmap + * @type Boolean + * @private + */ + this._cacheAsBitmap = false; + + /** + * Cached internal flag. + * + * @property _cacheIsDirty + * @type Boolean + * @private + */ + this._cacheIsDirty = false; + + /* * MOUSE Callbacks */ - - /** - * A callback that is used when the users clicks on the displayObject with their mouse - * @method click - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user clicks the mouse down over the sprite - * @method mousedown - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases the mouse that was over the displayObject - * for this callback to be fired the mouse must have been pressed down over the displayObject - * @method mouseup - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases the mouse that was over the displayObject but is no longer over the displayObject - * for this callback to be fired, The touch must have started over the displayObject - * @method mouseupoutside - * @param interactionData {InteractionData} - */ - + /** * A callback that is used when the users mouse rolls over the displayObject * @method mouseover @@ -784,6 +1213,59 @@ PIXI.DisplayObject = function() * @param interactionData {InteractionData} */ + //Left button + /** + * A callback that is used when the users clicks on the displayObject with their mouse's left button + * @method click + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user clicks the mouse's left button down over the sprite + * @method mousedown + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's left button that was over the displayObject + * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject + * @method mouseup + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's left button that was over the displayObject but is no longer over the displayObject + * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject + * @method mouseupoutside + * @param interactionData {InteractionData} + */ + + //Right button + /** + * A callback that is used when the users clicks on the displayObject with their mouse's right button + * @method rightclick + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user clicks the mouse's right button down over the sprite + * @method rightdown + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's right button that was over the displayObject + * for this callback to be fired the mouse's right button must have been pressed down over the displayObject + * @method rightup + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's right button that was over the displayObject but is no longer over the displayObject + * for this callback to be fired, the mouse's right button must have been pressed down over the displayObject + * @method rightupoutside + * @param interactionData {InteractionData} + */ /* * TOUCH Callbacks @@ -819,19 +1301,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -/** - * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default - * Instead of using this function you can now simply set the interactive property to true or false - * - * @method setInteractive - * @param interactive {Boolean} - * @deprecated Simply set the `interactive` property directly - */ -PIXI.DisplayObject.prototype.setInteractive = function(interactive) -{ - this.interactive = interactive; -}; - /** * Indicates if the sprite will have touch and mouse interactivity. It is false by default * @@ -853,7 +1322,7 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { }); /** - * [read-only] Indicates if the sprite is globaly visible. + * [read-only] Indicates if the sprite is globally visible. * * @property worldVisible * @type Boolean @@ -898,12 +1367,14 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer. * To remove filters simply set this property to 'null' * @property filters - * @type Array An array of filters + * @type Array(Filter) */ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { return this._filters; }, + set: function(value) { if(value) @@ -927,6 +1398,36 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { } }); +/** + * Set if this display object is cached as a bitmap. + * This basically takes a snap shot of the display object as it is at that moment. It can provide a performance benefit for complex static displayObjects. + * To remove simply set this property to 'null' + * @property cacheAsBitmap + * @type Boolean + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'cacheAsBitmap', { + + get: function() { + return this._cacheAsBitmap; + }, + + set: function(value) { + + if(this._cacheAsBitmap === value)return; + + if(value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } +}); + /* * Updates the object transform for rendering * @@ -935,48 +1436,81 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { */ PIXI.DisplayObject.prototype.updateTransform = function() { - // TODO OPTIMIZE THIS!! with dirty - if(this.rotation !== this.rotationCache) - { + // create some matrix refs for easy access + var pt = this.parent.worldTransform; + var wt = this.worldTransform; - this.rotationCache = this.rotation; - this._sr = Math.sin(this.rotation); - this._cr = Math.cos(this.rotation); + // temporary matrix variables + var a, b, c, d, tx, ty; + + // so if rotation is between 0 then we can simplify the multiplication process.. + if(this.rotation % PIXI.PI_2) + { + // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes + if(this.rotation !== this.rotationCache) + { + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); + } + + // get the matrix values of the displayobject based on its transform properties.. + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; + + // check for pivot.. not often used so geared towards that fact! + if(this.pivot.x || this.pivot.y) + { + tx -= this.pivot.x * a + this.pivot.y * c; + ty -= this.pivot.x * b + this.pivot.y * d; + } + + // concat the parent matrix with the objects transform. + wt.a = a * pt.a + b * pt.c; + wt.b = a * pt.b + b * pt.d; + wt.c = c * pt.a + d * pt.c; + wt.d = c * pt.b + d * pt.d; + wt.tx = tx * pt.a + ty * pt.c + pt.tx; + wt.ty = tx * pt.b + ty * pt.d + pt.ty; + + + } + else + { + // lets do the fast version as we know there is no rotation.. + a = this.scale.x; + d = this.scale.y; + + tx = this.position.x - this.pivot.x * a; + ty = this.position.y - this.pivot.y * d; + + wt.a = a * pt.a; + wt.b = a * pt.b; + wt.c = d * pt.c; + wt.d = d * pt.d; + wt.tx = tx * pt.a + ty * pt.c + pt.tx; + wt.ty = tx * pt.b + ty * pt.d + pt.ty; } - // var localTransform = this.localTransform//.toArray(); - var parentTransform = this.parent.worldTransform;//.toArray(); - var worldTransform = this.worldTransform;//.toArray(); - var px = this.pivot.x; - var py = this.pivot.y; - - var a00 = this._cr * this.scale.x, - a01 = -this._sr * this.scale.y, - a10 = this._sr * this.scale.x, - a11 = this._cr * this.scale.y, - a02 = this.position.x - a00 * px - py * a01, - a12 = this.position.y - a11 * py - px * a10, - b00 = parentTransform.a, b01 = parentTransform.b, - b10 = parentTransform.c, b11 = parentTransform.d; - - worldTransform.a = b00 * a00 + b01 * a10; - worldTransform.b = b00 * a01 + b01 * a11; - worldTransform.tx = b00 * a02 + b01 * a12 + parentTransform.tx; - - worldTransform.c = b10 * a00 + b11 * a10; - worldTransform.d = b10 * a01 + b11 * a11; - worldTransform.ty = b10 * a02 + b11 * a12 + parentTransform.ty; - + // multiply the alphas.. this.worldAlpha = this.alpha * this.parent.worldAlpha; }; +// performance increase to avoid using call.. (10x faster) +PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform; + /** * Retrieves the bounds of the displayObject as a rectangle object * * @method getBounds + * @param matrix {Matrix} * @return {Rectangle} the rectangular bounding area */ -PIXI.DisplayObject.prototype.getBounds = function( matrix ) +PIXI.DisplayObject.prototype.getBounds = function(matrix) { matrix = matrix;//just to get passed js hinting (and preserve inheritance) return PIXI.EmptyRectangle; @@ -990,8 +1524,6 @@ PIXI.DisplayObject.prototype.getBounds = function( matrix ) */ PIXI.DisplayObject.prototype.getLocalBounds = function() { - //var matrixCache = this.worldTransform; - return this.getBounds(PIXI.identityMatrix);///PIXI.EmptyRectangle(); }; @@ -1007,11 +1539,159 @@ PIXI.DisplayObject.prototype.setStageReference = function(stage) if(this._interactive)this.stage.dirty = true; }; +/** + * Useful function that returns a texture of the displayObject object that can then be used to create sprites + * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. + * + * @method generateTexture + * @param resolution {Number} The resolution of the texture being generated + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used to generate the texture. + * @return {Texture} a texture of the graphics object + */ +PIXI.DisplayObject.prototype.generateTexture = function(resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + PIXI.DisplayObject._tempMatrix.tx = -bounds.x; + PIXI.DisplayObject._tempMatrix.ty = -bounds.y; + + renderTexture.render(this, PIXI.DisplayObject._tempMatrix); + + return renderTexture; +}; + +/** + * Generates and updates the cached sprite for this object. + * + * @method updateCache + */ +PIXI.DisplayObject.prototype.updateCache = function() +{ + this._generateCachedSprite(); +}; + +/** + * Calculates the global position of the display object + * + * @method toGlobal + * @param position {Point} The world origin to calculate from + * @return {Point} A point object representing the position of this object + */ +PIXI.DisplayObject.prototype.toGlobal = function(position) +{ + // don't need to u[date the lot + this.displayObjectUpdateTransform(); + return this.worldTransform.apply(position); +}; + +/** + * Calculates the local position of the display object relative to another point + * + * @method toLocal + * @param position {Point} The world origin to calculate from + * @param [from] {DisplayObject} The DisplayObject to calculate the global position from + * @return {Point} A point object representing the position of this object + */ +PIXI.DisplayObject.prototype.toLocal = function(position, from) +{ + // + if (from) + { + position = from.toGlobal(position); + } + + // don't need to u[date the lot + this.displayObjectUpdateTransform(); + return this.worldTransform.applyInverse(position); +}; + +/** + * Internal method. + * + * @method _renderCachedSprite + * @param renderSession {Object} The render session + * @private + */ +PIXI.DisplayObject.prototype._renderCachedSprite = function(renderSession) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if(renderSession.gl) + { + PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + } + else + { + PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + } +}; + +/** + * Internal method. + * + * @method _generateCachedSprite + * @private + */ +PIXI.DisplayObject.prototype._generateCachedSprite = function() +{ + this._cacheAsBitmap = false; + var bounds = this.getLocalBounds(); + + if(!this._cachedSprite) + { + var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0);//, renderSession.renderer); + + this._cachedSprite = new PIXI.Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + //REMOVE filter! + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + PIXI.DisplayObject._tempMatrix.tx = -bounds.x; + PIXI.DisplayObject._tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, PIXI.DisplayObject._tempMatrix, true); + + this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); + this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); + + this._filters = tempFilters; + + this._cacheAsBitmap = true; +}; + +/** +* Destroys the cached sprite. +* +* @method _destroyCachedSprite +* @private +*/ +PIXI.DisplayObject.prototype._destroyCachedSprite = function() +{ + if(!this._cachedSprite)return; + + this._cachedSprite.texture.destroy(true); + + // TODO could be object pooled! + this._cachedSprite = null; +}; + /** * Renders the object using the WebGL renderer * * @method _renderWebGL -* @param renderSession {RenderSession} +* @param renderSession {RenderSession} * @private */ PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) @@ -1025,7 +1705,7 @@ PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) * Renders the object using the Canvas renderer * * @method _renderCanvas -* @param renderSession {RenderSession} +* @param renderSession {RenderSession} * @private */ PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) @@ -1035,6 +1715,9 @@ PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) renderSession = renderSession; }; + +PIXI.DisplayObject._tempMatrix = new PIXI.Matrix(); + /** * The position of the displayObject on the x axis relative to the local coordinates of the parent. * @@ -1064,12 +1747,11 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'y', { this.position.y = value; } }); - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - /** * A DisplayObjectContainer represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. @@ -1086,34 +1768,47 @@ PIXI.DisplayObjectContainer = function() * [read-only] The array of children of this container. * * @property children - * @type Array + * @type Array(DisplayObject) * @readOnly */ this.children = []; + + // fast access to update transform.. + }; // constructor PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; + /** * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set * * @property width * @type Number */ - - /* Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { get: function() { return this.scale.x * this.getLocalBounds().width; }, set: function(value) { - this.scale.x = value / (this.getLocalBounds().width/this.scale.x); + + var width = this.getLocalBounds().width; + + if(width !== 0) + { + this.scale.x = value / width; + } + else + { + this.scale.x = 1; + } + + this._width = value; } }); -*/ /** * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set @@ -1121,28 +1816,37 @@ Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { * @property height * @type Number */ - -/* Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { get: function() { return this.scale.y * this.getLocalBounds().height; }, set: function(value) { - this.scale.y = value / (this.getLocalBounds().height/this.scale.y); + + var height = this.getLocalBounds().height; + + if(height !== 0) + { + this.scale.y = value / height ; + } + else + { + this.scale.y = 1; + } + this._height = value; } }); -*/ /** * Adds a child to the container. * * @method addChild * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. */ PIXI.DisplayObjectContainer.prototype.addChild = function(child) { - this.addChildAt(child, this.children.length); + return this.addChildAt(child, this.children.length); }; /** @@ -1151,6 +1855,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) * @method addChildAt * @param child {DisplayObject} The child to add * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. */ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) { @@ -1166,20 +1871,21 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) this.children.splice(index, 0, child); if(this.stage)child.setStageReference(this.stage); + + return child; } else { - throw new Error(child + ' The index '+ index +' supplied is out of bounds ' + this.children.length); + throw new Error(child + 'addChildAt: The index '+ index +' supplied is out of bounds ' + this.children.length); } }; /** - * [NYI] Swaps the depth of 2 displayObjects + * Swaps the position of 2 Display Objects within this container. * * @method swapChildren * @param child {DisplayObject} * @param child2 {DisplayObject} - * @private */ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) { @@ -1187,8 +1893,8 @@ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) return; } - var index1 = this.children.indexOf(child); - var index2 = this.children.indexOf(child2); + var index1 = this.getChildIndex(child); + var index2 = this.getChildIndex(child2); if(index1 < 0 || index2 < 0) { throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); @@ -1196,7 +1902,42 @@ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) this.children[index1] = child2; this.children[index2] = child; - + +}; + +/** + * Returns the index position of a child DisplayObject instance + * + * @method getChildIndex + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +PIXI.DisplayObjectContainer.prototype.getChildIndex = function(child) +{ + var index = this.children.indexOf(child); + if (index === -1) + { + throw new Error('The supplied DisplayObject must be a child of the caller'); + } + return index; +}; + +/** + * Changes the position of an existing child in the display object container + * + * @method setChildIndex + * @param child {DisplayObject} The child DisplayObject instance for which you want to change the index number + * @param index {Number} The resulting index number for the child display object + */ +PIXI.DisplayObjectContainer.prototype.setChildIndex = function(child, index) +{ + if (index < 0 || index >= this.children.length) + { + throw new Error('The supplied index is out of bounds'); + } + var currentIndex = this.getChildIndex(child); + this.children.splice(currentIndex, 1); //remove from old position + this.children.splice(index, 0, child); //add at new position }; /** @@ -1204,17 +1945,16 @@ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) * * @method getChildAt * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. */ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) { - if(index >= 0 && index < this.children.length) + if (index < 0 || index >= this.children.length) { - return this.children[index]; - } - else - { - throw new Error('The supplied DisplayObjects must be a child of the caller ' + this); + throw new Error('getChildAt: Supplied index '+ index +' does not exist in the child list, or the supplied DisplayObject must be a child of the caller'); } + return this.children[index]; + }; /** @@ -1222,55 +1962,83 @@ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) * * @method removeChild * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. */ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { var index = this.children.indexOf( child ); - if ( index !== -1 ) - { - // update the stage reference.. - if(this.stage)child.removeStageReference(); + if(index === -1)return; + + return this.removeChildAt( index ); +}; - child.parent = undefined; - this.children.splice( index, 1 ); +/** + * Removes a child from the specified index position. + * + * @method removeChildAt + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +PIXI.DisplayObjectContainer.prototype.removeChildAt = function(index) +{ + var child = this.getChildAt( index ); + if(this.stage) + child.removeStageReference(); + + child.parent = undefined; + this.children.splice( index, 1 ); + return child; +}; + +/** +* Removes all children from this container that are within the begin and end indexes. +* +* @method removeChildren +* @param beginIndex {Number} The beginning position. Default value is 0. +* @param endIndex {Number} The ending position. Default value is size of the container. +*/ +PIXI.DisplayObjectContainer.prototype.removeChildren = function(beginIndex, endIndex) +{ + var begin = beginIndex || 0; + var end = typeof endIndex === 'number' ? endIndex : this.children.length; + var range = end - begin; + + if (range > 0 && range <= end) + { + var removed = this.children.splice(begin, range); + for (var i = 0; i < removed.length; i++) { + var child = removed[i]; + if(this.stage) + child.removeStageReference(); + child.parent = undefined; + } + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; } else { - throw new Error(child + ' The supplied DisplayObject must be a child of the caller ' + this); + throw new Error( 'removeChildren: Range Error, numeric values are outside the acceptable range' ); } }; - -/** -* Removes all the children -* -* @method removeAll -* NOT tested yet -*/ -/* PIXI.DisplayObjectContainer.prototype.removeAll = function() -{ - - - for(var i = 0 , j = this.children.length; i < j; i++) - { - this.removeChild(this.children[i]); - } - -}; -*/ /* - * Updates the container's childrens transform for rendering + * Updates the transform on all children of this container for rendering * * @method updateTransform * @private */ PIXI.DisplayObjectContainer.prototype.updateTransform = function() { - //this._currentBounds = null; - if(!this.visible)return; - PIXI.DisplayObject.prototype.updateTransform.call( this ); + this.displayObjectUpdateTransform(); + + //PIXI.DisplayObject.prototype.updateTransform.call( this ); + + if(this._cacheAsBitmap)return; for(var i=0,j=this.children.length; i maxX ? x1 : maxX; - maxX = x2 > maxX ? x2 : maxX; - maxX = x3 > maxX ? x3 : maxX; - maxX = x4 > maxX ? x4 : maxX; + var x2 = a * w0 + c * h1 + tx; + var y2 = d * h1 + b * w0 + ty; - maxY = y1 > maxY ? y1 : maxY; - maxY = y2 > maxY ? y2 : maxY; - maxY = y3 > maxY ? y3 : maxY; - maxY = y4 > maxY ? y4 : maxY; + var x3 = a * w0 + c * h0 + tx; + var y3 = d * h0 + b * w0 + ty; + + var x4 = a * w1 + c * h0 + tx; + var y4 = d * h0 + b * w1 + ty; + + minX = x1 < minX ? x1 : minX; + minX = x2 < minX ? x2 : minX; + minX = x3 < minX ? x3 : minX; + minX = x4 < minX ? x4 : minX; + + minY = y1 < minY ? y1 : minY; + minY = y2 < minY ? y2 : minY; + minY = y3 < minY ? y3 : minY; + minY = y4 < minY ? y4 : minY; + + maxX = x1 > maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + } var bounds = this._bounds; @@ -1728,14 +2525,14 @@ PIXI.Sprite.prototype.getBounds = function(matrix) * Renders the object using the WebGL renderer * * @method _renderWebGL -* @param renderSession {RenderSession} +* @param renderSession {RenderSession} * @private */ PIXI.Sprite.prototype._renderWebGL = function(renderSession) { // if the sprite is not visible or the alpha is 0 then no need to render this element if(!this.visible || this.alpha <= 0)return; - + var i,j; // do a quick check to see if this element has a mask or a filter. @@ -1743,6 +2540,13 @@ PIXI.Sprite.prototype._renderWebGL = function(renderSession) { var spriteBatch = renderSession.spriteBatch; + // push filter first as we need to ensure the stencil buffer is correct for any masking + if(this._filters) + { + spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + if(this._mask) { spriteBatch.stop(); @@ -1750,12 +2554,6 @@ PIXI.Sprite.prototype._renderWebGL = function(renderSession) spriteBatch.start(); } - if(this._filters) - { - spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); - } - // add this sprite to the batch spriteBatch.render(this); @@ -1768,9 +2566,9 @@ PIXI.Sprite.prototype._renderWebGL = function(renderSession) // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); + if(this._mask)renderSession.maskManager.popMask(this._mask, renderSession); if(this._filters)renderSession.filterManager.popFilter(); - if(this._mask)renderSession.maskManager.popMask(renderSession); - + spriteBatch.start(); } else @@ -1782,141 +2580,127 @@ PIXI.Sprite.prototype._renderWebGL = function(renderSession) { this.children[i]._renderWebGL(renderSession); } - } - - //TODO check culling + } }; /** * Renders the object using the Canvas renderer * * @method _renderCanvas -* @param renderSession {RenderSession} +* @param renderSession {RenderSession} * @private */ PIXI.Sprite.prototype._renderCanvas = function(renderSession) { - // if the sprite is not visible or the alpha is 0 then no need to render this element - if(this.visible === false || this.alpha === 0)return; - - var frame = this.texture.frame; - var context = renderSession.context; - var texture = this.texture; + // If the sprite is not visible or the alpha is 0 then no need to render this element + if (this.visible === false || this.alpha === 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) return; - if(this.blendMode !== renderSession.currentBlendMode) + if (this.blendMode !== renderSession.currentBlendMode) { renderSession.currentBlendMode = this.blendMode; - context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; + renderSession.context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; } - if(this._mask) + if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession.context); + renderSession.maskManager.pushMask(this._mask, renderSession); } - - - //ignore null sources - if(frame && frame.width && frame.height && texture.baseTexture.source) + // Ignore null sources + if (this.texture.valid) { - context.globalAlpha = this.worldAlpha; + var resolution = this.texture.baseTexture.resolution / renderSession.resolution; - var transform = this.worldTransform; + renderSession.context.globalAlpha = this.worldAlpha; - // allow for trimming - + // If smoothingEnabled is supported and we need to change the smoothing property for this texture + if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) + { + renderSession.scaleMode = this.texture.baseTexture.scaleMode; + renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === PIXI.scaleModes.LINEAR); + } + + // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions + var dx = (this.texture.trim) ? this.texture.trim.x - this.anchor.x * this.texture.trim.width : this.anchor.x * -this.texture.frame.width; + var dy = (this.texture.trim) ? this.texture.trim.y - this.anchor.y * this.texture.trim.height : this.anchor.y * -this.texture.frame.height; + + // Allow for pixel rounding if (renderSession.roundPixels) { - context.setTransform(transform.a, transform.c, transform.b, transform.d, transform.tx || 0, transform.ty || 0); + renderSession.context.setTransform( + this.worldTransform.a, + this.worldTransform.b, + this.worldTransform.c, + this.worldTransform.d, + (this.worldTransform.tx * renderSession.resolution) | 0, + (this.worldTransform.ty * renderSession.resolution) | 0); + + dx = dx | 0; + dy = dy | 0; } else { - context.setTransform(transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty); + renderSession.context.setTransform( + this.worldTransform.a, + this.worldTransform.b, + this.worldTransform.c, + this.worldTransform.d, + this.worldTransform.tx * renderSession.resolution, + this.worldTransform.ty * renderSession.resolution); } + - //if smoothingEnabled is supported and we need to change the smoothing property for this texture - if(renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - context[renderSession.smoothProperty] = (renderSession.scaleMode === PIXI.scaleModes.LINEAR); - } - - if(this.tint !== 0xFFFFFF) + + if (this.tint !== 0xFFFFFF) { - - if(this.cachedTint !== this.tint) + if (this.cachedTint !== this.tint) { - // no point tinting an image that has not loaded yet! - if(!texture.baseTexture.hasLoaded)return; - this.cachedTint = this.tint; - - //TODO clean up caching - how to clean up the caches? + + // TODO clean up caching - how to clean up the caches? this.tintedTexture = PIXI.CanvasTinter.getTintedTexture(this, this.tint); - } - context.drawImage(this.tintedTexture, - 0, - 0, - frame.width, - frame.height, - (this.anchor.x) * -frame.width, - (this.anchor.y) * -frame.height, - frame.width, - frame.height); + renderSession.context.drawImage( + this.tintedTexture, + 0, + 0, + this.texture.crop.width, + this.texture.crop.height, + dx / resolution, + dy / resolution, + this.texture.crop.width / resolution, + this.texture.crop.height / resolution); } else { - - - - if(texture.trim) - { - var trim = texture.trim; - - context.drawImage(this.texture.baseTexture.source, - frame.x, - frame.y, - frame.width, - frame.height, - trim.x - this.anchor.x * trim.width, - trim.y - this.anchor.y * trim.height, - frame.width, - frame.height); - } - else - { - - context.drawImage(this.texture.baseTexture.source, - frame.x, - frame.y, - frame.width, - frame.height, - (this.anchor.x) * -frame.width, - (this.anchor.y) * -frame.height, - frame.width, - frame.height); - } - + renderSession.context.drawImage( + this.texture.baseTexture.source, + this.texture.crop.x, + this.texture.crop.y, + this.texture.crop.width, + this.texture.crop.height, + dx / resolution, + dy / resolution, + this.texture.crop.width / resolution, + this.texture.crop.height / resolution); } } // OVERWRITE - for(var i=0,j=this.children.length; i} an array of {Texture} objects that make up the animation + * @param textures {Array(Texture)} an array of {Texture} objects that make up the animation */ PIXI.MovieClip = function(textures) { @@ -2147,7 +2934,7 @@ PIXI.MovieClip = function(textures) * The array of textures that make up the animation * * @property textures - * @type Array + * @type Array(Texture) */ this.textures = textures; @@ -2211,13 +2998,12 @@ PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; * @readOnly */ Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { - get: function() { + get: function() { - return this.textures.length; - } + return this.textures.length; + } }); - /** * Stops the MovieClip * @@ -2272,7 +3058,7 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) */ PIXI.MovieClip.prototype.updateTransform = function() { - PIXI.Sprite.prototype.updateTransform.call(this); + this.displayObjectContainerUpdateTransform(); if(!this.playing)return; @@ -2280,6 +3066,8 @@ PIXI.MovieClip.prototype.updateTransform = function() var round = (this.currentFrame + 0.5) | 0; + this.currentFrame = this.currentFrame % this.textures.length; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -2293,26 +3081,84 @@ PIXI.MovieClip.prototype.updateTransform = function() } } }; - + +/** + * A short hand way of creating a movieclip from an array of frame ids + * + * @static + * @method fromFrames + * @param frames {Array} the array of frames ids the movieclip will use as its texture frames + */ +PIXI.MovieClip.fromFrames = function(frames) +{ + var textures = []; + + for (var i = 0; i < frames.length; i++) + { + textures.push(new PIXI.Texture.fromFrame(frames[i])); + } + + return new PIXI.MovieClip(textures); +}; + +/** + * A short hand way of creating a movieclip from an array of image ids + * + * @static + * @method fromImages + * @param frames {Array} the array of image ids the movieclip will use as its texture frames + */ +PIXI.MovieClip.fromImages = function(images) +{ + var textures = []; + + for (var i = 0; i < images.length; i++) + { + textures.push(new PIXI.Texture.fromImage(images[i])); + } + + return new PIXI.MovieClip(textures); +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - +/** + * A target and pass info object for filters. + * + * @class FilterBlock + * @constructor + */ PIXI.FilterBlock = function() { + /** + * The visible state of this FilterBlock. + * + * @property visible + * @type Boolean + */ this.visible = true; + + /** + * The renderable state of this FilterBlock. + * + * @property renderable + * @type Boolean + */ this.renderable = true; }; - + +PIXI.FilterBlock.prototype.constructor = PIXI.FilterBlock; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 + * Modified by Tom Slezakowski http://www.tomslezakowski.com @TomSlezakowski (24/03/2014) - Added dropShadowColor. */ /** - * A Text Object will create a line(s) of text. To split a line you can use '\n' - * or add a wordWrap property set to true and and wordWrapWidth property with a value - * in the style object + * A Text Object will create a line or multiple lines of text. To split a line you can use '\n' in your text string, + * or add a wordWrap property set to true and and wordWrapWidth property with a value in the style object. * * @class Text * @extends Sprite @@ -2326,6 +3172,10 @@ PIXI.FilterBlock = function() * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.dropShadow=false] {Boolean} Set a drop shadow for the text + * @param [style.dropShadowColor='#000000'] {String} A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param [style.dropShadowAngle=Math.PI/4] {Number} Set a angle of the drop shadow + * @param [style.dropShadowDistance=5] {Number} Set a distance of the drop shadow */ PIXI.Text = function(text, style) { @@ -2340,23 +3190,76 @@ PIXI.Text = function(text, style) /** * The canvas 2d context that everything is drawn with * @property context - * @type HTMLCanvasElement 2d Context + * @type HTMLCanvasElement */ this.context = this.canvas.getContext('2d'); + /** + * The resolution of the canvas. + * @property resolution + * @type Number + */ + this.resolution = 1; + PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas)); this.setText(text); this.setStyle(style); - this.updateText(); - this.dirty = false; }; // constructor PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); PIXI.Text.prototype.constructor = PIXI.Text; +/** + * The width of the Text, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.Text.prototype, 'width', { + get: function() { + + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + + return this.scale.x * this.texture.frame.width; + }, + set: function(value) { + this.scale.x = value / this.texture.frame.width; + this._width = value; + } +}); + +/** + * The height of the Text, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.Text.prototype, 'height', { + get: function() { + + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + + return this.scale.y * this.texture.frame.height; + }, + set: function(value) { + this.scale.y = value / this.texture.frame.height; + this._height = value; + } +}); + /** * Set the style of the text * @@ -2369,6 +3272,10 @@ PIXI.Text.prototype.constructor = PIXI.Text; * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap + * @param [style.dropShadow=false] {Boolean} Set a drop shadow for the text + * @param [style.dropShadowColor='#000000'] {String} A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param [style.dropShadowAngle=Math.PI/4] {Number} Set a angle of the drop shadow + * @param [style.dropShadowDistance=5] {Number} Set a distance of the drop shadow */ PIXI.Text.prototype.setStyle = function(style) { @@ -2380,21 +3287,26 @@ PIXI.Text.prototype.setStyle = function(style) style.strokeThickness = style.strokeThickness || 0; style.wordWrap = style.wordWrap || false; style.wordWrapWidth = style.wordWrapWidth || 100; + + style.dropShadow = style.dropShadow || false; + style.dropShadowAngle = style.dropShadowAngle || Math.PI / 6; + style.dropShadowDistance = style.dropShadowDistance || 4; + style.dropShadowColor = style.dropShadowColor || 'black'; + this.style = style; this.dirty = true; }; /** - * Set the copy for the text object. To split a line you can use '\n' + * Set the copy for the text object. To split a line you can use '\n'. * * @method setText - * @param {String} text The copy that you would like the text to display + * @param text {String} The copy that you would like the text to display */ PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || ' '; this.dirty = true; - }; /** @@ -2405,6 +3317,8 @@ PIXI.Text.prototype.setText = function(text) */ PIXI.Text.prototype.updateText = function() { + this.texture.baseTexture.resolution = this.resolution; + this.context.font = this.style.font; var outputText = this.text; @@ -2419,52 +3333,103 @@ PIXI.Text.prototype.updateText = function() //calculate text width var lineWidths = []; var maxLineWidth = 0; + var fontProperties = this.determineFontProperties(this.style.font); for (var i = 0; i < lines.length; i++) { var lineWidth = this.context.measureText(lines[i]).width; lineWidths[i] = lineWidth; maxLineWidth = Math.max(maxLineWidth, lineWidth); } - this.canvas.width = maxLineWidth + this.style.strokeThickness; + var width = maxLineWidth + this.style.strokeThickness; + if(this.style.dropShadow)width += this.style.dropShadowDistance; + + this.canvas.width = ( width + this.context.lineWidth ) * this.resolution; + //calculate text height - var lineHeight = this.determineFontHeight('font: ' + this.style.font + ';') + this.style.strokeThickness; - this.canvas.height = lineHeight * lines.length; + var lineHeight = fontProperties.fontSize + this.style.strokeThickness; + + var height = lineHeight * lines.length; + if(this.style.dropShadow)height += this.style.dropShadowDistance; + + this.canvas.height = height * this.resolution; + + this.context.scale( this.resolution, this.resolution); if(navigator.isCocoonJS) this.context.clearRect(0,0,this.canvas.width,this.canvas.height); - //set canvas text styles - this.context.fillStyle = this.style.fill; - this.context.font = this.style.font; + // used for debugging.. + //this.context.fillStyle ="#FF0000" + //this.context.fillRect(0, 0, this.canvas.width,this.canvas.height); + this.context.font = this.style.font; this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; + this.context.textBaseline = 'alphabetic'; + //this.context.lineJoin = 'round'; - this.context.textBaseline = 'top'; + var linePositionX; + var linePositionY; + if(this.style.dropShadow) + { + this.context.fillStyle = this.style.dropShadowColor; + + var xShadowOffset = Math.sin(this.style.dropShadowAngle) * this.style.dropShadowDistance; + var yShadowOffset = Math.cos(this.style.dropShadowAngle) * this.style.dropShadowDistance; + + for (i = 0; i < lines.length; i++) + { + linePositionX = this.style.strokeThickness / 2; + linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; + + if(this.style.align === 'right') + { + linePositionX += maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + linePositionX += (maxLineWidth - lineWidths[i]) / 2; + } + + if(this.style.fill) + { + this.context.fillText(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset); + } + + // if(dropShadow) + } + } + + //set canvas text styles + this.context.fillStyle = this.style.fill; + //draw lines line by line for (i = 0; i < lines.length; i++) { - var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); + linePositionX = this.style.strokeThickness / 2; + linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; if(this.style.align === 'right') { - linePosition.x += maxLineWidth - lineWidths[i]; + linePositionX += maxLineWidth - lineWidths[i]; } else if(this.style.align === 'center') { - linePosition.x += (maxLineWidth - lineWidths[i]) / 2; + linePositionX += (maxLineWidth - lineWidths[i]) / 2; } if(this.style.stroke && this.style.strokeThickness) { - this.context.strokeText(lines[i], linePosition.x, linePosition.y); + this.context.strokeText(lines[i], linePositionX, linePositionY); } if(this.style.fill) { - this.context.fillText(lines[i], linePosition.x, linePosition.y); + this.context.fillText(lines[i], linePositionX, linePositionY); } + + // if(dropShadow) } this.updateTexture(); @@ -2480,13 +3445,14 @@ PIXI.Text.prototype.updateTexture = function() { this.texture.baseTexture.width = this.canvas.width; this.texture.baseTexture.height = this.canvas.height; - this.texture.frame.width = this.canvas.width; - this.texture.frame.height = this.canvas.height; + this.texture.crop.width = this.texture.frame.width = this.canvas.width; + this.texture.crop.height = this.texture.frame.height = this.canvas.height; this._width = this.canvas.width; this._height = this.canvas.height; - this.requiresUpdate = true; + // update the dirty base textures + this.texture.baseTexture.dirty(); }; /** @@ -2498,63 +3464,140 @@ PIXI.Text.prototype.updateTexture = function() */ PIXI.Text.prototype._renderWebGL = function(renderSession) { - if(this.requiresUpdate) + if(this.dirty) { - this.requiresUpdate = false; - PIXI.updateWebGLTexture(this.texture.baseTexture, renderSession.gl); + this.resolution = renderSession.resolution; + + this.updateText(); + this.dirty = false; } PIXI.Sprite.prototype._renderWebGL.call(this, renderSession); }; /** - * Updates the transform of this object - * - * @method updateTransform - * @private - */ -PIXI.Text.prototype.updateTransform = function() +* Renders the object using the Canvas renderer +* +* @method _renderCanvas +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Text.prototype._renderCanvas = function(renderSession) { if(this.dirty) { + this.resolution = renderSession.resolution; + this.updateText(); this.dirty = false; } - - PIXI.Sprite.prototype.updateTransform.call(this); + + PIXI.Sprite.prototype._renderCanvas.call(this, renderSession); }; -/* - * http://stackoverflow.com/users/34441/ellisbben - * great solution to the problem! - * returns the height of the given font - * - * @method determineFontHeight - * @param fontStyle {Object} - * @private - */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +/** +* Calculates the ascent, descent and fontSize of a given fontStyle +* +* @method determineFontProperties +* @param fontStyle {Object} +* @private +*/ +PIXI.Text.prototype.determineFontProperties = function(fontStyle) { - // build a little reference dictionary so if the font style has been used return a - // cached version... - var result = PIXI.Text.heightCache[fontStyle]; + var properties = PIXI.Text.fontPropertiesCache[fontStyle]; - if(!result) + if(!properties) { - var body = document.getElementsByTagName('body')[0]; - var dummy = document.createElement('div'); - var dummyText = document.createTextNode('M'); - dummy.appendChild(dummyText); - dummy.setAttribute('style', fontStyle + ';position:absolute;top:0;left:0'); - body.appendChild(dummy); + properties = {}; + + var canvas = PIXI.Text.fontPropertiesCanvas; + var context = PIXI.Text.fontPropertiesContext; - result = dummy.offsetHeight; - PIXI.Text.heightCache[fontStyle] = result; + context.font = fontStyle; - body.removeChild(dummy); + var width = Math.ceil(context.measureText('|Mq').width); + var baseline = Math.ceil(context.measureText('M').width); + var height = 2 * baseline; + + baseline = baseline * 1.4 | 0; + + canvas.width = width; + canvas.height = height; + + context.fillStyle = '#f00'; + context.fillRect(0, 0, width, height); + + context.font = fontStyle; + + context.textBaseline = 'alphabetic'; + context.fillStyle = '#000'; + context.fillText('|MÉq', 0, baseline); + + var imagedata = context.getImageData(0, 0, width, height).data; + var pixels = imagedata.length; + var line = width * 4; + + var i, j; + + var idx = 0; + var stop = false; + + // ascent. scan from top to bottom until we find a non red pixel + for(i = 0; i < baseline; i++) + { + for(j = 0; j < line; j += 4) + { + if(imagedata[idx + j] !== 255) + { + stop = true; + break; + } + } + if(!stop) + { + idx += line; + } + else + { + break; + } + } + + properties.ascent = baseline - i; + + idx = pixels - line; + stop = false; + + // descent. scan from bottom to top until we find a non red pixel + for(i = height; i > baseline; i--) + { + for(j = 0; j < line; j += 4) + { + if(imagedata[idx + j] !== 255) + { + stop = true; + break; + } + } + if(!stop) + { + idx -= line; + } + else + { + break; + } + } + + properties.descent = i - baseline; + //TODO might need a tweak. kind of a temp fix! + properties.descent += 6; + properties.fontSize = properties.ascent + properties.descent; + + PIXI.Text.fontPropertiesCache[fontStyle] = properties; } - return result; + return properties; }; /** @@ -2579,7 +3622,7 @@ PIXI.Text.prototype.wordWrap = function(text) { var wordWidth = this.context.measureText(words[j]).width; var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; - if(wordWidthWithSpace > spaceLeft) + if(j === 0 || wordWidthWithSpace > spaceLeft) { // Skip printing the newline if it's the first word of the line that is // greater than the word wrap width. @@ -2587,13 +3630,13 @@ PIXI.Text.prototype.wordWrap = function(text) { result += '\n'; } - result += words[j] + ' '; + result += words[j]; spaceLeft = this.style.wordWrapWidth - wordWidth; } else { spaceLeft -= wordWidthWithSpace; - result += words[j] + ' '; + result += ' ' + words[j]; } } @@ -2606,28 +3649,48 @@ PIXI.Text.prototype.wordWrap = function(text) }; /** - * Destroys this text object - * - * @method destroy - * @param destroyTexture {Boolean} - */ -PIXI.Text.prototype.destroy = function(destroyTexture) +* Returns the bounds of the Text as a rectangle. The bounds calculation takes the worldTransform into account. +* +* @method getBounds +* @param matrix {Matrix} the transformation matrix of the Text +* @return {Rectangle} the framing rectangle +*/ +PIXI.Text.prototype.getBounds = function(matrix) { - if(destroyTexture) + if(this.dirty) { - this.texture.destroy(); + this.updateText(); + this.dirty = false; } + return PIXI.Sprite.prototype.getBounds.call(this, matrix); }; -PIXI.Text.heightCache = {}; - +/** + * Destroys this text object. + * + * @method destroy + * @param destroyBaseTexture {Boolean} whether to destroy the base texture as well + */ +PIXI.Text.prototype.destroy = function(destroyBaseTexture) +{ + // make sure to reset the the context and canvas.. dont want this hanging around in memory! + this.context = null; + this.canvas = null; + + this.texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); +}; + +PIXI.Text.fontPropertiesCache = {}; +PIXI.Text.fontPropertiesCanvas = document.createElement('canvas'); +PIXI.Text.fontPropertiesContext = PIXI.Text.fontPropertiesCanvas.getContext('2d'); + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * A Text Object will create a line(s) of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n' + * A BitmapText object will create a line or multiple lines of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n' in your string. * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. @@ -2644,11 +3707,42 @@ PIXI.BitmapText = function(text, style) { PIXI.DisplayObjectContainer.call(this); + /** + * [read-only] The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @property textWidth + * @type Number + * @readOnly + */ + this.textWidth = 0; + + /** + * [read-only] The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @property textHeight + * @type Number + * @readOnly + */ + this.textHeight = 0; + + /** + * @property _pool + * @type Array + * @private + */ this._pool = []; this.setText(text); this.setStyle(style); this.updateText(); + + /** + * The dirty state of this object. + * @property dirty + * @type Boolean + */ this.dirty = false; }; @@ -2657,10 +3751,10 @@ PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype) PIXI.BitmapText.prototype.constructor = PIXI.BitmapText; /** - * Set the copy for the text object + * Set the text string to be rendered. * * @method setText - * @param text {String} The copy that you would like the text to display + * @param text {String} The text that you would like displayed */ PIXI.BitmapText.prototype.setText = function(text) { @@ -2671,7 +3765,7 @@ PIXI.BitmapText.prototype.setText = function(text) /** * Set the style of the text * style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) - * [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single lines of text * * @method setStyle * @param style {Object} The style parameters, contained as properties of an object @@ -2706,11 +3800,11 @@ PIXI.BitmapText.prototype.updateText = function() var lineWidths = []; var line = 0; var scale = this.fontSize / data.size; - for(var i = 0; i < this.text.length; i++) { var charCode = this.text.charCodeAt(i); + if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i))) { lineWidths.push(pos.x); @@ -2724,12 +3818,14 @@ PIXI.BitmapText.prototype.updateText = function() } var charData = data.chars[charCode]; + if(!charData) continue; - if(prevCharCode && charData[prevCharCode]) + if(prevCharCode && charData.kerning[prevCharCode]) { pos.x += charData.kerning[prevCharCode]; } + chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)}); pos.x += charData.xAdvance; @@ -2740,6 +3836,7 @@ PIXI.BitmapText.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, pos.x); var lineAlignOffsets = []; + for(i = 0; i <= line; i++) { var alignOffset = 0; @@ -2757,6 +3854,7 @@ PIXI.BitmapText.prototype.updateText = function() var lenChildren = this.children.length; var lenChars = chars.length; var tint = this.tint || 0xFFFFFF; + for(i = 0; i < lenChars; i++) { var c = i < lenChildren ? this.children[i] : this._pool.pop(); // get old child if have. if not - take from pool. @@ -2780,23 +3878,7 @@ PIXI.BitmapText.prototype.updateText = function() this.removeChild(child); } - - /** - * [read-only] The width of the overall text, different from fontSize, - * which is defined in the style object - * - * @property textWidth - * @type Number - */ this.textWidth = maxLineWidth * scale; - - /** - * [read-only] The height of the overall text, different from fontSize, - * which is defined in the style object - * - * @property textHeight - * @type Number - */ this.textHeight = (pos.y + data.lineHeight) * scale; }; @@ -2818,11 +3900,11 @@ PIXI.BitmapText.prototype.updateTransform = function() }; PIXI.BitmapText.fonts = {}; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - + /** * Holds all information related to an Interaction event * @@ -2839,9 +3921,6 @@ PIXI.InteractionData = function() */ this.global = new PIXI.Point(); - // this is here for legacy... but will remove - this.local = new PIXI.Point(); - /** * The target Sprite that was interacted with * @@ -2864,24 +3943,31 @@ PIXI.InteractionData = function() * * @method getLocalPosition * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off + * @param [point] {Point} A Point object in which to store the value, optional (otherwise will create a new point) * @return {Point} A point containing the coordinates of the InteractionData position relative to the DisplayObject */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +PIXI.InteractionData.prototype.getLocalPosition = function(displayObject, point) { var worldTransform = displayObject.worldTransform; var global = this.global; // do a cheeky transform to get the mouse coords; - var a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, - a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, + var a00 = worldTransform.a, a01 = worldTransform.c, a02 = worldTransform.tx, + a10 = worldTransform.b, a11 = worldTransform.d, a12 = worldTransform.ty, id = 1 / (a00 * a11 + a01 * -a10); + + point = point || new PIXI.Point(); + + point.x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id; + point.y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; + // set the mouse coords... - return new PIXI.Point(a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, - a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id); + return point; }; // constructor -PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; +PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -2898,7 +3984,7 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; PIXI.InteractionManager = function(stage) { /** - * a reference to the stage + * A reference to the stage * * @property stage * @type Stage @@ -2906,7 +3992,7 @@ PIXI.InteractionManager = function(stage) this.stage = stage; /** - * the mouse data + * The mouse data * * @property mouse * @type InteractionData @@ -2914,18 +4000,21 @@ PIXI.InteractionManager = function(stage) this.mouse = new PIXI.InteractionData(); /** - * an object that stores current touches (InteractionData) by id reference + * An object that stores current touches (InteractionData) by id reference * - * @property touchs + * @property touches * @type Object */ - this.touchs = {}; + this.touches = {}; - // helpers + /** + * @property tempPoint + * @type Point + * @private + */ this.tempPoint = new PIXI.Point(); /** - * * @property mouseoverEnabled * @type Boolean * @default @@ -2933,8 +4022,8 @@ PIXI.InteractionManager = function(stage) this.mouseoverEnabled = true; /** - * tiny little interactiveData pool ! - * + * Tiny little interactiveData pool ! + * * @property pool * @type Array */ @@ -2945,7 +4034,6 @@ PIXI.InteractionManager = function(stage) * @property interactiveItems * @type Array * @private - * */ this.interactiveItems = []; @@ -2957,23 +4045,60 @@ PIXI.InteractionManager = function(stage) */ this.interactionDOMElement = null; - //this will make it so that you dont have to call bind all the time + //this will make it so that you don't have to call bind all the time + + /** + * @property onMouseMove + * @type Function + */ this.onMouseMove = this.onMouseMove.bind( this ); + + /** + * @property onMouseDown + * @type Function + */ this.onMouseDown = this.onMouseDown.bind(this); + + /** + * @property onMouseOut + * @type Function + */ this.onMouseOut = this.onMouseOut.bind(this); + + /** + * @property onMouseUp + * @type Function + */ this.onMouseUp = this.onMouseUp.bind(this); + /** + * @property onTouchStart + * @type Function + */ this.onTouchStart = this.onTouchStart.bind(this); + + /** + * @property onTouchEnd + * @type Function + */ this.onTouchEnd = this.onTouchEnd.bind(this); + + /** + * @property onTouchMove + * @type Function + */ this.onTouchMove = this.onTouchMove.bind(this); + /** + * @property last + * @type Number + */ this.last = 0; /** * The css style of the cursor that is being used * @property currentCursorStyle * @type String - * */ this.currentCursorStyle = 'inherit'; @@ -2981,9 +4106,17 @@ PIXI.InteractionManager = function(stage) * Is set to true when the mouse is moved out of the canvas * @property mouseOut * @type Boolean - * */ this.mouseOut = false; + + /** + * @property resolution + * @type Number + */ + this.resolution = 1; + + // used for hit testing + this._tempPoint = new PIXI.Point(); }; // constructor @@ -3003,27 +4136,25 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj var length = children.length; // make an interaction tree... {item.__interactiveParent} - for (var i = length-1; i >= 0; i--) + for (var i = length - 1; i >= 0; i--) { var child = children[i]; // push all interactive bits - if(child.interactive) + if (child._interactive) { iParent.interactiveChildren = true; //child.__iParent = iParent; this.interactiveItems.push(child); - if(child.children.length > 0) - { + if (child.children.length > 0) { this.collectInteractiveSprite(child, child); } } else { child.__iParent = null; - - if(child.children.length > 0) + if (child.children.length > 0) { this.collectInteractiveSprite(child, iParent); } @@ -3042,17 +4173,14 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj PIXI.InteractionManager.prototype.setTarget = function(target) { this.target = target; + this.resolution = target.resolution; - //check if the dom element has been set. If it has don't do anything - if( this.interactionDOMElement === null ) { + // Check if the dom element has been set. If it has don't do anything. + if (this.interactionDOMElement !== null) return; - this.setTargetDomElement( target.view ); - } - - + this.setTargetDomElement (target.view); }; - /** * Sets the DOM element which will receive mouse/touch events. This is useful for when you have other DOM * elements on top of the renderers Canvas element. With this you'll be able to delegate another DOM element @@ -3064,17 +4192,13 @@ PIXI.InteractionManager.prototype.setTarget = function(target) */ PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) { - this.removeEvents(); - if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. domElement.style['-ms-content-zooming'] = 'none'; domElement.style['-ms-touch-action'] = 'none'; - - // DO some window specific touch! } this.interactionDOMElement = domElement; @@ -3088,13 +4212,16 @@ PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) domElement.addEventListener('touchend', this.onTouchEnd, true); domElement.addEventListener('touchmove', this.onTouchMove, true); - document.body.addEventListener('mouseup', this.onMouseUp, true); + window.addEventListener('mouseup', this.onMouseUp, true); }; - +/** + * @method removeEvents + * @private + */ PIXI.InteractionManager.prototype.removeEvents = function() { - if(!this.interactionDOMElement)return; + if (!this.interactionDOMElement) return; this.interactionDOMElement.style['-ms-content-zooming'] = ''; this.interactionDOMElement.style['-ms-touch-action'] = ''; @@ -3110,7 +4237,7 @@ PIXI.InteractionManager.prototype.removeEvents = function() this.interactionDOMElement = null; - document.body.removeEventListener('mouseup', this.onMouseUp, true); + window.removeEventListener('mouseup', this.onMouseUp, true); }; /** @@ -3121,13 +4248,13 @@ PIXI.InteractionManager.prototype.removeEvents = function() */ PIXI.InteractionManager.prototype.update = function() { - if(!this.target)return; + if (!this.target) return; // frequency of 30fps?? var now = Date.now(); var diff = now - this.last; diff = (diff * PIXI.INTERACTION_FREQUENCY ) / 1000; - if(diff < 1)return; + if (diff < 1) return; this.last = now; var i = 0; @@ -3135,21 +4262,9 @@ PIXI.InteractionManager.prototype.update = function() // ok.. so mouse events?? // yes for now :) // OPTIMISE - how often to check?? - if(this.dirty) + if (this.dirty) { - this.dirty = false; - - var len = this.interactiveItems.length; - - for (i = 0; i < len; i++) { - this.interactiveItems[i].interactiveChildren = false; - } - - this.interactiveItems = []; - - if(this.stage.interactive)this.interactiveItems.push(this.stage); - // go through and collect all the objects that are interactive.. - this.collectInteractiveSprite(this.stage, this.stage); + this.rebuildInteractiveGraph(); } // loop through interactive objects! @@ -3164,43 +4279,77 @@ PIXI.InteractionManager.prototype.update = function() // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? // hit-test the clip! - // if(item.mouseover || item.mouseout || item.buttonMode) + // if (item.mouseover || item.mouseout || item.buttonMode) // { // ok so there are some functions so lets hit test it.. item.__hit = this.hitTest(item, this.mouse); this.mouse.target = item; // ok so deal with interactions.. // looks like there was a hit! - if(item.__hit && !over) + if (item.__hit && !over) { - if(item.buttonMode) cursor = item.defaultCursor; + if (item.buttonMode) cursor = item.defaultCursor; - if(!item.interactiveChildren)over = true; - - if(!item.__isOver) + if (!item.interactiveChildren) { - if(item.mouseover)item.mouseover(this.mouse); + over = true; + } + + if (!item.__isOver) + { + if (item.mouseover) + { + item.mouseover (this.mouse); + } item.__isOver = true; } } else { - if(item.__isOver) + if (item.__isOver) { // roll out! - if(item.mouseout)item.mouseout(this.mouse); + if (item.mouseout) + { + item.mouseout (this.mouse); + } item.__isOver = false; } } } - if( this.currentCursorStyle !== cursor ) + if (this.currentCursorStyle !== cursor) { this.currentCursorStyle = cursor; this.interactionDOMElement.style.cursor = cursor; } }; +/** + * @method rebuildInteractiveGraph + * @private + */ +PIXI.InteractionManager.prototype.rebuildInteractiveGraph = function() +{ + this.dirty = false; + + var len = this.interactiveItems.length; + + for (var i = 0; i < len; i++) { + this.interactiveItems[i].interactiveChildren = false; + } + + this.interactiveItems = []; + + if (this.stage.interactive) + { + this.interactiveItems.push(this.stage); + } + + // Go through and collect all the objects that are interactive.. + this.collectInteractiveSprite(this.stage, this.stage); +}; + /** * Is called when the mouse moves across the renderer element * @@ -3210,12 +4359,18 @@ PIXI.InteractionManager.prototype.update = function() */ PIXI.InteractionManager.prototype.onMouseMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + this.mouse.originalEvent = event; + // TODO optimize by not check EVERY TIME! maybe half as often? // var rect = this.interactionDOMElement.getBoundingClientRect(); - this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); - this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); + this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width) / this.resolution; + this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height) / this.resolution; var length = this.interactiveItems.length; @@ -3223,9 +4378,9 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { var item = this.interactiveItems[i]; - if(item.mousemove) + // Call the function! + if (item.mousemove) { - //call the function! item.mousemove(this.mouse); } } @@ -3240,9 +4395,17 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } - if(PIXI.AUTO_PREVENT_DEFAULT)this.mouse.originalEvent.preventDefault(); + this.mouse.originalEvent = event; + + if (PIXI.AUTO_PREVENT_DEFAULT) + { + this.mouse.originalEvent.preventDefault(); + } // loop through interaction tree... // hit test each item! -> @@ -3250,39 +4413,56 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) //stage.__i var length = this.interactiveItems.length; + var e = this.mouse.originalEvent; + var isRightButton = e.button === 2 || e.which === 3; + var downFunction = isRightButton ? 'rightdown' : 'mousedown'; + var clickFunction = isRightButton ? 'rightclick' : 'click'; + var buttonIsDown = isRightButton ? '__rightIsDown' : '__mouseIsDown'; + var isDown = isRightButton ? '__isRightDown' : '__isDown'; + // while // hit test for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(item.mousedown || item.click) + if (item[downFunction] || item[clickFunction]) { - item.__mouseIsDown = true; + item[buttonIsDown] = true; item.__hit = this.hitTest(item, this.mouse); - if(item.__hit) + if (item.__hit) { //call the function! - if(item.mousedown)item.mousedown(this.mouse); - item.__isDown = true; + if (item[downFunction]) + { + item[downFunction](this.mouse); + } + item[isDown] = true; // just the one! - if(!item.interactiveChildren)break; + if (!item.interactiveChildren) break; } } } }; /** - * Is called when the mouse button is moved out of the renderer element + * Is called when the mouse is moved out of the renderer element * * @method onMouseOut - * @param event {Event} The DOM event of a mouse button being moved out - * @private + * @param event {Event} The DOM event of a mouse being moved out + * @private */ -PIXI.InteractionManager.prototype.onMouseOut = function() +PIXI.InteractionManager.prototype.onMouseOut = function(event) { + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + this.mouse.originalEvent = event; + var length = this.interactiveItems.length; this.interactionDOMElement.style.cursor = 'inherit'; @@ -3290,10 +4470,13 @@ PIXI.InteractionManager.prototype.onMouseOut = function() for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(item.__isOver) + if (item.__isOver) { this.mouse.target = item; - if(item.mouseout)item.mouseout(this.mouse); + if (item.mouseout) + { + item.mouseout(this.mouse); + } item.__isOver = false; } } @@ -3314,42 +4497,62 @@ PIXI.InteractionManager.prototype.onMouseOut = function() */ PIXI.InteractionManager.prototype.onMouseUp = function(event) { + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } - this.mouse.originalEvent = event || window.event; //IE uses window.event + this.mouse.originalEvent = event; var length = this.interactiveItems.length; var up = false; + var e = this.mouse.originalEvent; + var isRightButton = e.button === 2 || e.which === 3; + + var upFunction = isRightButton ? 'rightup' : 'mouseup'; + var clickFunction = isRightButton ? 'rightclick' : 'click'; + var upOutsideFunction = isRightButton ? 'rightupoutside' : 'mouseupoutside'; + var isDown = isRightButton ? '__isRightDown' : '__isDown'; + for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - item.__hit = this.hitTest(item, this.mouse); - - if(item.__hit && !up) + if (item[clickFunction] || item[upFunction] || item[upOutsideFunction]) { - //call the function! - if(item.mouseup) + item.__hit = this.hitTest(item, this.mouse); + + if (item.__hit && !up) { - item.mouseup(this.mouse); + //call the function! + if (item[upFunction]) + { + item[upFunction](this.mouse); + } + if (item[isDown]) + { + if (item[clickFunction]) + { + item[clickFunction](this.mouse); + } + } + + if (!item.interactiveChildren) + { + up = true; + } } - if(item.__isDown) + else { - if(item.click)item.click(this.mouse); + if (item[isDown]) + { + if (item[upOutsideFunction]) item[upOutsideFunction](this.mouse); + } } - if(!item.interactiveChildren)up = true; + item[isDown] = false; } - else - { - if(item.__isDown) - { - if(item.mouseupoutside)item.mouseupoutside(this.mouse); - } - } - - item.__isDown = false; - //} } }; @@ -3365,66 +4568,77 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if( !item.worldVisible )return false; + if (!item.worldVisible) + { + return false; + } - // temp fix for if the element is in a non visible - - var isSprite = (item instanceof PIXI.Sprite), - worldTransform = item.worldTransform, - a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, - a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, - id = 1 / (a00 * a11 + a01 * -a10), - x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, - y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; + // map the global point to local space. + item.worldTransform.applyInverse(global, this._tempPoint); + + var x = this._tempPoint.x, + y = this._tempPoint.y, + i; interactionData.target = item; //a sprite or display object with a hit area defined - if(item.hitArea && item.hitArea.contains) { - if(item.hitArea.contains(x, y)) { - //if(isSprite) - interactionData.target = item; - - return true; - } - - return false; + if (item.hitArea && item.hitArea.contains) + { + return item.hitArea.contains(x, y); } // a sprite with no hitarea defined - else if(isSprite) + else if(item instanceof PIXI.Sprite) { - var width = item.texture.frame.width, - height = item.texture.frame.height, - x1 = -width * item.anchor.x, - y1; + var width = item.texture.frame.width; + var height = item.texture.frame.height; + var x1 = -width * item.anchor.x; + var y1; - if(x > x1 && x < x1 + width) + if (x > x1 && x < x1 + width) { y1 = -height * item.anchor.y; - if(y > y1 && y < y1 + height) + if (y > y1 && y < y1 + height) { // set the target property if a hit is true! - interactionData.target = item; return true; } } } + else if(item instanceof PIXI.Graphics) + { + var graphicsData = item.graphicsData; + for (i = 0; i < graphicsData.length; i++) + { + var data = graphicsData[i]; + if(!data.fill)continue; + + // only deal with fills.. + if(data.shape) + { + if(data.shape.contains(x, y)) + { + //interactionData.target = item; + return true; + } + } + } + } var length = item.children.length; - for (var i = 0; i < length; i++) + for (i = 0; i < length; i++) { var tempItem = item.children[i]; var hit = this.hitTest(tempItem, interactionData); - if(hit) + if (hit) { // hmm.. TODO SET CORRECT TARGET? interactionData.target = item; return true; } } - return false; }; @@ -3437,6 +4651,11 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; var touchData; @@ -3445,24 +4664,27 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) for (i = 0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; - touchData = this.touchs[touchEvent.identifier]; - touchData.originalEvent = event || window.event; + touchData = this.touches[touchEvent.identifier]; + touchData.originalEvent = event; // update the touch position - touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); - touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); - if(navigator.isCocoonJS) { + touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; + touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; + if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) + { + //Support for CocoonJS fullscreen scale modes touchData.global.x = touchEvent.clientX; touchData.global.y = touchEvent.clientY; } - } - var length = this.interactiveItems.length; - for (i = 0; i < length; i++) - { - var item = this.interactiveItems[i]; - if(item.touchmove) - item.touchmove(touchData); + for (var j = 0; j < this.interactiveItems.length; j++) + { + var item = this.interactiveItems[j]; + if (item.touchmove && item.__touchData && item.__touchData[touchEvent.identifier]) + { + item.touchmove(touchData); + } + } } }; @@ -3475,24 +4697,37 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + var rect = this.interactionDOMElement.getBoundingClientRect(); - if(PIXI.AUTO_PREVENT_DEFAULT)event.preventDefault(); - + if (PIXI.AUTO_PREVENT_DEFAULT) + { + event.preventDefault(); + } + var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.pool.pop(); - if(!touchData)touchData = new PIXI.InteractionData(); + if (!touchData) + { + touchData = new PIXI.InteractionData(); + } - touchData.originalEvent = event || window.event; + touchData.originalEvent = event; - this.touchs[touchEvent.identifier] = touchData; - touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); - touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); - if(navigator.isCocoonJS) { + this.touches[touchEvent.identifier] = touchData; + touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; + touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; + if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) + { + //Support for CocoonJS fullscreen scale modes touchData.global.x = touchEvent.clientX; touchData.global.y = touchEvent.clientY; } @@ -3503,18 +4738,19 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) { var item = this.interactiveItems[j]; - if(item.touchstart || item.tap) + if (item.touchstart || item.tap) { item.__hit = this.hitTest(item, touchData); - if(item.__hit) + if (item.__hit) { //call the function! - if(item.touchstart)item.touchstart(touchData); + if (item.touchstart)item.touchstart(touchData); item.__isDown = true; - item.__touchData = touchData; + item.__touchData = item.__touchData || {}; + item.__touchData[touchEvent.identifier] = touchData; - if(!item.interactiveChildren)break; + if (!item.interactiveChildren) break; } } } @@ -3530,18 +4766,24 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - //this.mouse.originalEvent = event || window.event; //IE uses window.event + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; - var touchData = this.touchs[touchEvent.identifier]; + var touchData = this.touches[touchEvent.identifier]; var up = false; - touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); - touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); - if(navigator.isCocoonJS) { + touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; + touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; + if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) + { + //Support for CocoonJS fullscreen scale modes touchData.global.x = touchEvent.clientX; touchData.global.y = touchEvent.clientY; } @@ -3550,54 +4792,53 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) for (var j = 0; j < length; j++) { var item = this.interactiveItems[j]; - var itemTouchData = item.__touchData; // <-- Here! - item.__hit = this.hitTest(item, touchData); - if(itemTouchData === touchData) + if (item.__touchData && item.__touchData[touchEvent.identifier]) { + + item.__hit = this.hitTest(item, item.__touchData[touchEvent.identifier]); + // so this one WAS down... - touchData.originalEvent = event || window.event; + touchData.originalEvent = event; // hitTest?? - if(item.touchend || item.tap) + if (item.touchend || item.tap) { - if(item.__hit && !up) + if (item.__hit && !up) { - if(item.touchend)item.touchend(touchData); - if(item.__isDown) + if (item.touchend) { - if(item.tap)item.tap(touchData); + item.touchend(touchData); + } + if (item.__isDown && item.tap) + { + item.tap(touchData); + } + if (!item.interactiveChildren) + { + up = true; } - - if(!item.interactiveChildren)up = true; } else { - if(item.__isDown) + if (item.__isDown && item.touchendoutside) { - if(item.touchendoutside)item.touchendoutside(touchData); + item.touchendoutside(touchData); } } item.__isDown = false; } - item.__touchData = null; - + item.__touchData[touchEvent.identifier] = null; } - /* - else - { - - } - */ } // remove the touch.. this.pool.push(touchData); - this.touchs[touchEvent.identifier] = null; + this.touches[touchEvent.identifier] = null; } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3626,7 +4867,7 @@ PIXI.Stage = function(backgroundColor) * [read-only] Current transform of the object based on world (parent) factors * * @property worldTransform - * @type Mat3 + * @type Matrix * @readOnly * @private */ @@ -3643,7 +4884,7 @@ PIXI.Stage = function(backgroundColor) /** * The interaction manage for this stage, manages all interactive activity on the stage * - * @property interactive + * @property interactionManager * @type InteractionManager */ this.interactionManager = new PIXI.InteractionManager(this); @@ -3661,7 +4902,7 @@ PIXI.Stage = function(backgroundColor) this.stage = this; //optimize hit detection a bit - this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); + this.stage.hitArea = new PIXI.Rectangle(0, 0, 100000, 100000); this.setBackgroundColor(backgroundColor); }; @@ -3724,16 +4965,16 @@ PIXI.Stage.prototype.setBackgroundColor = function(backgroundColor) }; /** - * This will return the point containing global coords of the mouse. + * This will return the point containing global coordinates of the mouse. * * @method getMousePosition - * @return {Point} The point containing the coords of the global InteractionData position. + * @return {Point} A point containing the coordinates of the global InteractionData position. */ PIXI.Stage.prototype.getMousePosition = function() { return this.interactionManager.mouse.global; }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3752,37 +4993,40 @@ PIXI.Stage.prototype.getMousePosition = function() * * @method requestAnimationFrame */ + /** * A polyfill for cancelAnimationFrame * * @method cancelAnimationFrame */ -var lastTime = 0; -var vendors = ['ms', 'moz', 'webkit', 'o']; -for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { - window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; - window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || - window[vendors[x] + 'CancelRequestAnimationFrame']; -} +(function(window) { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || + window[vendors[x] + 'CancelRequestAnimationFrame']; + } -if (!window.requestAnimationFrame) { - window.requestAnimationFrame = function(callback) { - var currTime = new Date().getTime(); - var timeToCall = Math.max(0, 16 - (currTime - lastTime)); - var id = window.setTimeout(function() { callback(currTime + timeToCall); }, - timeToCall); - lastTime = currTime + timeToCall; - return id; - }; -} + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } -if (!window.cancelAnimationFrame) { - window.cancelAnimationFrame = function(id) { - clearTimeout(id); - }; -} + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } -window.requestAnimFrame = window.requestAnimationFrame; + window.requestAnimFrame = window.requestAnimationFrame; +})(this); /** * Converts a hex color number to an [R, G, B] array @@ -3811,15 +5055,21 @@ PIXI.rgb2hex = function(rgb) { */ if (typeof Function.prototype.bind !== 'function') { Function.prototype.bind = (function () { - var slice = Array.prototype.slice; return function (thisArg) { - var target = this, boundArgs = slice.call(arguments, 1); + var target = this, i = arguments.length - 1, boundArgs = []; + if (i > 0) + { + boundArgs.length = i; + while (i--) boundArgs[i] = arguments[i + 1]; + } if (typeof target !== 'function') throw new TypeError(); function bound() { - var args = boundArgs.concat(slice.call(arguments)); - target.apply(this instanceof bound ? this : thisArg, args); + var i = arguments.length, args = new Array(i); + while (i--) args[i] = arguments[i]; + args = boundArgs.concat(args); + return target.apply(this instanceof bound ? this : thisArg, args); } bound.prototype = (function F(proto) { @@ -3900,6 +5150,7 @@ PIXI.unpackColorRGB = function(r, g, b)//r, g, b, a) */ PIXI.canUseNewCanvasBlendModes = function() { + if (typeof document === 'undefined') return false; var canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; @@ -3931,115 +5182,298 @@ PIXI.getNextPowerOfTwo = function(number) return result; } }; - + +PIXI.isPowerOfTwo = function(width, height) +{ + return (width > 0 && (width & (width - 1)) === 0 && height > 0 && (height & (height - 1)) === 0); + +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * https://github.com/mrdoob/eventtarget.js/ - * THankS mr DOob! + * @author Chad Engler https://github.com/englercj @Rolnaaba */ /** - * Adds event emitter functionality to a class + * Originally based on https://github.com/mrdoob/eventtarget.js/ from mr Doob. + * Currently takes inspiration from the nodejs EventEmitter, EventEmitter3, and smokesignals + */ + +/** + * Mixins event emitter functionality to a class * * @class EventTarget * @example - * function MyEmitter() { - * PIXI.EventTarget.call(this); //mixes in event target stuff - * } + * function MyEmitter() {} + * + * PIXI.EventTarget.mixin(MyEmitter.prototype); * * var em = new MyEmitter(); - * em.emit({ type: 'eventName', data: 'some data' }); + * em.emit('eventName', 'some data', 'some more data', {}, null, ...); */ -PIXI.EventTarget = function () { +PIXI.EventTarget = { + /** + * Backward compat from when this used to be a function + */ + call: function callCompat(obj) { + if(obj) { + obj = obj.prototype || obj; + PIXI.EventTarget.mixin(obj); + } + }, /** - * Holds all the listeners + * Mixes in the properties of the EventTarget prototype onto another object * - * @property listeneners - * @type Object + * @method mixin + * @param object {Object} The obj to mix into */ - var listeners = {}; + mixin: function mixin(obj) { + /** + * Return a list of assigned event listeners. + * + * @method listeners + * @param eventName {String} The events that should be listed. + * @return {Array} An array of listener functions + */ + obj.listeners = function listeners(eventName) { + this._listeners = this._listeners || {}; - /** - * Adds a listener for a specific event - * - * @method addEventListener - * @param type {string} A string representing the event type to listen for. - * @param listener {function} The callback function that will be fired when the event occurs - */ - this.addEventListener = this.on = function ( type, listener ) { + return this._listeners[eventName] ? this._listeners[eventName].slice() : []; + }; + /** + * Emit an event to all registered event listeners. + * + * @method emit + * @alias dispatchEvent + * @param eventName {String} The name of the event. + * @return {Boolean} Indication if we've emitted an event. + */ + obj.emit = obj.dispatchEvent = function emit(eventName, data) { + this._listeners = this._listeners || {}; - if ( listeners[ type ] === undefined ) { + //backwards compat with old method ".emit({ type: 'something' })" + if(typeof eventName === 'object') { + data = eventName; + eventName = eventName.type; + } - listeners[ type ] = []; + //ensure we are using a real pixi event + if(!data || data.__isEventObject !== true) { + data = new PIXI.Event(this, eventName, data); + } - } + //iterate the listeners + if(this._listeners && this._listeners[eventName]) { + var listeners = this._listeners[eventName].slice(0), + length = listeners.length, + fn = listeners[0], + i; - if ( listeners[ type ].indexOf( listener ) === - 1 ) { + for(i = 0; i < length; fn = listeners[++i]) { + //call the event listener + fn.call(this, data); - listeners[ type ].push( listener ); - } + //if "stopImmediatePropagation" is called, stop calling sibling events + if(data.stoppedImmediate) { + return this; + } + } - }; + //if "stopPropagation" is called then don't bubble the event + if(data.stopped) { + return this; + } + } - /** - * Fires the event, ie pretends that the event has happened - * - * @method dispatchEvent - * @param event {Event} the event object - */ - this.dispatchEvent = this.emit = function ( event ) { + //bubble this event up the scene graph + if(this.parent && this.parent.emit) { + this.parent.emit.call(this.parent, eventName, data); + } - if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + return this; + }; - return; + /** + * Register a new EventListener for the given event. + * + * @method on + * @alias addEventListener + * @param eventName {String} Name of the event. + * @param callback {Functon} fn Callback function. + */ + obj.on = obj.addEventListener = function on(eventName, fn) { + this._listeners = this._listeners || {}; - } + (this._listeners[eventName] = this._listeners[eventName] || []) + .push(fn); - for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + return this; + }; - listeners[ event.type ][ i ]( event ); + /** + * Add an EventListener that's only called once. + * + * @method once + * @param eventName {String} Name of the event. + * @param callback {Function} Callback function. + */ + obj.once = function once(eventName, fn) { + this._listeners = this._listeners || {}; - } + var self = this; + function onceHandlerWrapper() { + fn.apply(self.off(eventName, onceHandlerWrapper), arguments); + } + onceHandlerWrapper._originalHandler = fn; - }; + return this.on(eventName, onceHandlerWrapper); + }; - /** - * Removes the specified listener that was assigned to the specified event type - * - * @method removeEventListener - * @param type {string} A string representing the event type which will have its listener removed - * @param listener {function} The callback function that was be fired when the event occured - */ - this.removeEventListener = this.off = function ( type, listener ) { + /** + * Remove event listeners. + * + * @method off + * @alias removeEventListener + * @param eventName {String} The event we want to remove. + * @param callback {Function} The listener that we need to find. + */ + obj.off = obj.removeEventListener = function off(eventName, fn) { + this._listeners = this._listeners || {}; - var index = listeners[ type ].indexOf( listener ); + if(!this._listeners[eventName]) + return this; - if ( index !== - 1 ) { + var list = this._listeners[eventName], + i = fn ? list.length : 0; - listeners[ type ].splice( index, 1 ); + while(i-- > 0) { + if(list[i] === fn || list[i]._originalHandler === fn) { + list.splice(i, 1); + } + } - } + if(list.length === 0) { + delete this._listeners[eventName]; + } - }; + return this; + }; - /** - * Removes all the listeners that were active for the specified event type - * - * @method removeAllEventListeners - * @param type {string} A string representing the event type which will have all its listeners removed - */ - this.removeAllEventListeners = function( type ) { - var a = listeners[type]; - if (a) - a.length = 0; - }; + /** + * Remove all listeners or only the listeners for the specified event. + * + * @method removeAllListeners + * @param eventName {String} The event you want to remove all listeners for. + */ + obj.removeAllListeners = function removeAllListeners(eventName) { + this._listeners = this._listeners || {}; + + if(!this._listeners[eventName]) + return this; + + delete this._listeners[eventName]; + + return this; + }; + } }; - + +/** + * Creates an homogenous object for tracking events so users can know what to expect. + * + * @class Event + * @extends Object + * @constructor + * @param target {Object} The target object that the event is called on + * @param name {String} The string name of the event that was triggered + * @param data {Object} Arbitrary event data to pass along + */ +PIXI.Event = function(target, name, data) { + //for duck typing in the ".on()" function + this.__isEventObject = true; + + /** + * Tracks the state of bubbling propagation. Do not + * set this directly, instead use `event.stopPropagation()` + * + * @property stopped + * @type Boolean + * @private + * @readOnly + */ + this.stopped = false; + + /** + * Tracks the state of sibling listener propagation. Do not + * set this directly, instead use `event.stopImmediatePropagation()` + * + * @property stoppedImmediate + * @type Boolean + * @private + * @readOnly + */ + this.stoppedImmediate = false; + + /** + * The original target the event triggered on. + * + * @property target + * @type Object + * @readOnly + */ + this.target = target; + + /** + * The string name of the event that this represents. + * + * @property type + * @type String + * @readOnly + */ + this.type = name; + + /** + * The data that was passed in with this event. + * + * @property data + * @type Object + * @readOnly + */ + this.data = data; + + //backwards compat with older version of events + this.content = data; + + /** + * The timestamp when the event occurred. + * + * @property timeStamp + * @type Number + * @readOnly + */ + this.timeStamp = Date.now(); +}; + +/** + * Stops the propagation of events up the scene graph (prevents bubbling). + * + * @method stopPropagation + */ +PIXI.Event.prototype.stopPropagation = function stopPropagation() { + this.stopped = true; +}; + +/** + * Stops the propagation of events to sibling listeners (no longer calls any listeners). + * + * @method stopImmediatePropagation + */ +PIXI.Event.prototype.stopImmediatePropagation = function stopImmediatePropagation() { + this.stoppedImmediate = true; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4048,16 +5482,22 @@ PIXI.EventTarget = function () { * This helper function will automatically detect which renderer you should be using. * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by * the browser then this function will return a canvas renderer - * @class autoDetectRenderer + * + * @method autoDetectRenderer + * @for PIXI * @static * @param width=800 {Number} the width of the renderers view * @param height=600 {Number} the height of the renderers view - * @param [view] {Canvas} the canvas to use as a view, optional - * @param [transparent=false] {Boolean} the transparency of the render view, default false - * @param [antialias=false] {Boolean} sets antialias (only applicable in webGL chrome at the moment) - * + * + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 + * */ -PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) +PIXI.autoDetectRenderer = function(width, height, options) { if(!width)width = 800; if(!height)height = 600; @@ -4071,15 +5511,58 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) } } )(); - if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); + return new PIXI.WebGLRenderer(width, height, options); } - return new PIXI.CanvasRenderer(width, height, view, transparent); + return new PIXI.CanvasRenderer(width, height, options); }; - + +/** + * This helper function will automatically detect which renderer you should be using. + * This function is very similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @method autoDetectRecommendedRenderer + * @for PIXI + * @static + * @param width=800 {Number} the width of the renderers view + * @param height=600 {Number} the height of the renderers view + * + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 + * + */ +PIXI.autoDetectRecommendedRenderer = function(width, height, options) +{ + if(!width)width = 800; + if(!height)height = 600; + + // BORROWED from Mr Doob (mrdoob.com) + var webgl = ( function () { try { + var canvas = document.createElement( 'canvas' ); + return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); + } catch( e ) { + return false; + } + } )(); + + var isAndroid = /Android/i.test(navigator.userAgent); + + if( webgl && !isAndroid) + { + return new PIXI.WebGLRenderer(width, height, options); + } + + return new PIXI.CanvasRenderer(width, height, options); +}; + /* PolyK library url: http://polyk.ivank.net @@ -4110,23 +5593,21 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) This is an amazing lib! - slightly modified by Mat Groves (matgroves.com); + Slightly modified by Mat Groves (matgroves.com); */ /** * Based on the Polyk library http://polyk.ivank.net released under MIT licence. * This is an amazing lib! - * slightly modified by Mat Groves (matgroves.com); + * Slightly modified by Mat Groves (matgroves.com); * @class PolyK - * */ PIXI.PolyK = {}; /** - * Triangulates shapes for webGL graphic fills + * Triangulates shapes for webGL graphic fills. * * @method Triangulate - * */ PIXI.PolyK.Triangulate = function(p) { @@ -4191,8 +5672,8 @@ PIXI.PolyK.Triangulate = function(p) } else { - window.console.log("PIXI Warning: shape too complex to fill"); - return []; + // window.console.log("PIXI Warning: shape too complex to fill"); + return null; } } } @@ -4214,6 +5695,7 @@ PIXI.PolyK.Triangulate = function(p) * @param cx {Number} x coordinate of the c point of the triangle * @param cy {Number} y coordinate of the c point of the triangle * @private + * @return {Boolean} */ PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy) { @@ -4242,39 +5724,60 @@ PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy) * Checks whether a shape is convex * * @method _convex - * * @private + * @return {Boolean} */ PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign) { return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign; }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ -// TODO Alvin and Mat -// Should we eventually create a Utils class ? -// Or just move this file to the pixi.js file ? +/** +* @method initDefaultShaders +* @static +* @private +*/ PIXI.initDefaultShaders = function() { - - // PIXI.stripShader = new PIXI.StripShader(); -// PIXI.stripShader.init(); - }; +/** +* @method CompileVertexShader +* @static +* @param gl {WebGLContext} the current WebGL drawing context +* @param shaderSrc {Array} +* @return {Any} +*/ PIXI.CompileVertexShader = function(gl, shaderSrc) { return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); }; +/** +* @method CompileFragmentShader +* @static +* @param gl {WebGLContext} the current WebGL drawing context +* @param shaderSrc {Array} +* @return {Any} +*/ PIXI.CompileFragmentShader = function(gl, shaderSrc) { return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); }; +/** +* @method _CompileShader +* @static +* @private +* @param gl {WebGLContext} the current WebGL drawing context +* @param shaderSrc {Array} +* @param shaderType {Number} +* @return {Any} +*/ PIXI._CompileShader = function(gl, shaderSrc, shaderType) { var src = shaderSrc.join("\n"); @@ -4282,7 +5785,8 @@ PIXI._CompileShader = function(gl, shaderSrc, shaderType) gl.shaderSource(shader, src); gl.compileShader(shader); - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) + { window.console.log(gl.getShaderInfoLog(shader)); return null; } @@ -4290,6 +5794,14 @@ PIXI._CompileShader = function(gl, shaderSrc, shaderType) return shader; }; +/** +* @method compileProgram +* @static +* @param gl {WebGLContext} the current WebGL drawing context +* @param vertexSrc {Array} +* @param fragmentSrc {Array} +* @return {Any} +*/ PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) { var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); @@ -4301,13 +5813,14 @@ PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) + { window.console.log("Could not initialise shaders"); } return shaderProgram; }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 * @author Richard Davey http://www.photonstorm.com @photonstorm @@ -4316,9 +5829,17 @@ PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) /** * @class PixiShader * @constructor +* @param gl {WebGLContext} the current WebGL drawing context */ PIXI.PixiShader = function(gl) { + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + /** * @property gl * @type WebGLContext @@ -4326,13 +5847,17 @@ PIXI.PixiShader = function(gl) this.gl = gl; /** - * @property {any} program - The WebGL program. - */ + * The WebGL program. + * @property program + * @type Any + */ this.program = null; /** - * @property {array} fragmentSrc - The fragment shader. - */ + * The fragment shader. + * @property fragmentSrc + * @type Array + */ this.fragmentSrc = [ 'precision lowp float;', 'varying vec2 vTextureCoord;', @@ -4343,29 +5868,52 @@ PIXI.PixiShader = function(gl) '}' ]; - /** - * @property {number} textureCount - A local texture counter for multi-texture shaders. - */ + * A local texture counter for multi-texture shaders. + * @property textureCount + * @type Number + */ this.textureCount = 0; + /** + * A local flag + * @property firstRun + * @type Boolean + * @private + */ + this.firstRun = true; + + /** + * A dirty flag + * @property dirty + * @type Boolean + */ + this.dirty = true; + + /** + * Uniform attributes cache. + * @property attributes + * @type Array + * @private + */ this.attributes = []; this.init(); }; +PIXI.PixiShader.prototype.constructor = PIXI.PixiShader; + /** -* Initialises the shader -* @method init +* Initialises the shader. * +* @method init */ PIXI.PixiShader.prototype.init = function() { - var gl = this.gl; var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc); - + gl.useProgram(program); // get and store the uniforms for the shader @@ -4379,12 +5927,11 @@ PIXI.PixiShader.prototype.init = function() this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - // Begin worst hack eva // // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? // maybe its something to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel // If theres any webGL people that know why could happen please help :) if(this.colorAttribute === -1) { @@ -4409,6 +5956,7 @@ PIXI.PixiShader.prototype.init = function() /** * Initialises the shader uniform values. +* * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf * @@ -4495,7 +6043,7 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform) var gl = this.gl; gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTexture); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); // Extended texture data if (uniform.textureData) @@ -4569,7 +6117,6 @@ PIXI.PixiShader.prototype.syncUniforms = function() // This would probably be faster in an array and it would guarantee key order for (var key in this.uniforms) { - uniform = this.uniforms[key]; if (uniform.glValueLength === 1) @@ -4600,7 +6147,18 @@ PIXI.PixiShader.prototype.syncUniforms = function() if (uniform._init) { gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl)); + + if(uniform.value.baseTexture._dirty[gl.id]) + { + PIXI.instances[gl.id].updateTexture(uniform.value.baseTexture); + } + else + { + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + } + + // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl)); gl.uniform1i(uniform.uniformLocation, this.textureCount); this.textureCount++; } @@ -4614,9 +6172,9 @@ PIXI.PixiShader.prototype.syncUniforms = function() }; /** -* Destroys the shader -* @method destroy +* Destroys the shader. * +* @method destroy */ PIXI.PixiShader.prototype.destroy = function() { @@ -4628,6 +6186,7 @@ PIXI.PixiShader.prototype.destroy = function() }; /** +* The Default Vertex shader source. * * @property defaultVertexSrc * @type String @@ -4635,7 +6194,7 @@ PIXI.PixiShader.prototype.destroy = function() PIXI.PixiShader.defaultVertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', - 'attribute vec2 aColor;', + 'attribute vec4 aColor;', 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', @@ -4648,18 +6207,11 @@ PIXI.PixiShader.defaultVertexSrc = [ 'void main(void) {', ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', - ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = vec4(color * aColor.x, aColor.x);', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' ]; - - - - - /** * @author Mat Groves http://matgroves.com/ @Doormat23 - * @author Richard Davey http://www.photonstorm.com @photonstorm */ /** @@ -4669,7 +6221,13 @@ PIXI.PixiShader.defaultVertexSrc = [ */ PIXI.PixiFastShader = function(gl) { - + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + /** * @property gl * @type WebGLContext @@ -4677,12 +6235,16 @@ PIXI.PixiFastShader = function(gl) this.gl = gl; /** - * @property {any} program - The WebGL program. + * The WebGL program. + * @property program + * @type Any */ this.program = null; /** - * @property {array} fragmentSrc - The fragment shader. + * The fragment shader. + * @property fragmentSrc + * @type Array */ this.fragmentSrc = [ 'precision lowp float;', @@ -4695,8 +6257,10 @@ PIXI.PixiFastShader = function(gl) ]; /** - * @property {array} vertexSrc - The vertex shader - */ + * The vertex shader. + * @property vertexSrc + * @type Array + */ this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aPositionCoord;', @@ -4727,24 +6291,25 @@ PIXI.PixiFastShader = function(gl) '}' ]; - /** - * @property {number} textureCount - A local texture counter for multi-texture shaders. - */ + * A local texture counter for multi-texture shaders. + * @property textureCount + * @type Number + */ this.textureCount = 0; - this.init(); }; +PIXI.PixiFastShader.prototype.constructor = PIXI.PixiFastShader; + /** -* Initialises the shader +* Initialises the shader. +* * @method init -* */ PIXI.PixiFastShader.prototype.init = function() { - var gl = this.gl; var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); @@ -4769,8 +6334,6 @@ PIXI.PixiFastShader.prototype.init = function() this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? @@ -4786,14 +6349,13 @@ PIXI.PixiFastShader.prototype.init = function() // End worst hack eva // - this.program = program; }; /** -* Destroys the shader +* Destroys the shader. +* * @method destroy -* */ PIXI.PixiFastShader.prototype.destroy = function() { @@ -4803,67 +6365,94 @@ PIXI.PixiFastShader.prototype.destroy = function() this.attributes = null; }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - -PIXI.StripShader = function() +/** +* @class StripShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.StripShader = function(gl) { /** - * @property {any} program - The WebGL program. - */ + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * The WebGL program. + * @property program + * @type Any + */ this.program = null; /** - * @property {array} fragmentSrc - The fragment shader. + * The fragment shader. + * @property fragmentSrc + * @type Array */ this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + // 'varying float vColor;', 'uniform float alpha;', 'uniform sampler2D uSampler;', 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));', - ' gl_FragColor = gl_FragColor * alpha;', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', '}' ]; /** - * @property {array} fragmentSrc - The fragment shader. - */ - this.vertexSrc = [ + * The vertex shader. + * @property vertexSrc + * @type Array + */ + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', 'uniform mat3 translationMatrix;', 'uniform vec2 projectionVector;', - 'varying vec2 vTextureCoord;', 'uniform vec2 offsetVector;', - 'varying float vColor;', + // 'uniform float alpha;', + // 'uniform vec3 tint;', + 'varying vec2 vTextureCoord;', + // 'varying vec4 vColor;', 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / projectionVector.y + 1.0 , 0.0, 1.0);', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', - ' vColor = aColor;', + // ' vColor = aColor * vec4(tint * alpha, alpha);', '}' ]; + + this.init(); }; +PIXI.StripShader.prototype.constructor = PIXI.StripShader; + /** -* Initialises the shader +* Initialises the shader. +* * @method init -* */ PIXI.StripShader.prototype.init = function() { - - var gl = PIXI.gl; + var gl = this.gl; var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); gl.useProgram(program); @@ -4879,12 +6468,28 @@ PIXI.StripShader.prototype.init = function() this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + this.attributes = [this.aVertexPosition, this.aTextureCoord]; + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); this.alpha = gl.getUniformLocation(program, 'alpha'); this.program = program; }; - + +/** +* Destroys the shader. +* +* @method destroy +*/ +PIXI.StripShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attribute = null; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -4896,6 +6501,13 @@ PIXI.StripShader.prototype.init = function() */ PIXI.PrimitiveShader = function(gl) { + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + /** * @property gl * @type WebGLContext @@ -4903,11 +6515,14 @@ PIXI.PrimitiveShader = function(gl) this.gl = gl; /** - * @property {any} program - The WebGL program. - */ + * The WebGL program. + * @property program + * @type Any + */ this.program = null; /** + * The fragment shader. * @property fragmentSrc * @type Array */ @@ -4921,6 +6536,7 @@ PIXI.PrimitiveShader = function(gl) ]; /** + * The vertex shader. * @property vertexSrc * @type Array */ @@ -4931,13 +6547,14 @@ PIXI.PrimitiveShader = function(gl) 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', 'uniform float alpha;', + 'uniform float flipY;', 'uniform vec3 tint;', 'varying vec4 vColor;', 'void main(void) {', ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', ' vColor = aColor * vec4(tint * alpha, alpha);', '}' ]; @@ -4945,14 +6562,15 @@ PIXI.PrimitiveShader = function(gl) this.init(); }; +PIXI.PrimitiveShader.prototype.constructor = PIXI.PrimitiveShader; + /** -* Initialises the shader +* Initialises the shader. +* * @method init -* */ PIXI.PrimitiveShader.prototype.init = function() { - var gl = this.gl; var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); @@ -4962,7 +6580,7 @@ PIXI.PrimitiveShader.prototype.init = function() this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); this.tintColor = gl.getUniformLocation(program, 'tint'); - + this.flipY = gl.getUniformLocation(program, 'flipY'); // get and store the attributes this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); @@ -4977,9 +6595,9 @@ PIXI.PrimitiveShader.prototype.init = function() }; /** -* Destroys the shader +* Destroys the shader. +* * @method destroy -* */ PIXI.PrimitiveShader.prototype.destroy = function() { @@ -4987,9 +6605,132 @@ PIXI.PrimitiveShader.prototype.destroy = function() this.uniforms = null; this.gl = null; + this.attributes = null; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class ComplexPrimitiveShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.ComplexPrimitiveShader = function(gl) +{ + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * The WebGL program. + * @property program + * @type Any + */ + this.program = null; + + /** + * The fragment shader. + * @property fragmentSrc + * @type Array + */ + this.fragmentSrc = [ + + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ]; + + /** + * The vertex shader. + * @property vertexSrc + * @type Array + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + //'attribute vec4 aColor;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ]; + + this.init(); +}; + +PIXI.ComplexPrimitiveShader.prototype.constructor = PIXI.ComplexPrimitiveShader; + +/** +* Initialises the shader. +* +* @method init +*/ +PIXI.ComplexPrimitiveShader.prototype.init = function() +{ + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.tintColor = gl.getUniformLocation(program, 'tint'); + this.color = gl.getUniformLocation(program, 'color'); + this.flipY = gl.getUniformLocation(program, 'flipY'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + this.attributes = [this.aVertexPosition, this.colorAttribute]; + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** +* Destroys the shader. +* +* @method destroy +*/ +PIXI.ComplexPrimitiveShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + this.attribute = null; }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -5003,7 +6744,6 @@ PIXI.PrimitiveShader.prototype.destroy = function() */ PIXI.WebGLGraphics = function() { - }; /** @@ -5020,60 +6760,60 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projectio var gl = renderSession.gl; var projection = renderSession.projection, offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader; - - if(!graphics._webGL[gl.id])graphics._webGL[gl.id] = {points:[], indices:[], lastIndex:0, - buffer:gl.createBuffer(), - indexBuffer:gl.createBuffer()}; - - var webGL = graphics._webGL[gl.id]; + shader = renderSession.shaderManager.primitiveShader, + webGLData; if(graphics.dirty) { - graphics.dirty = false; - - if(graphics.clearDirty) - { - graphics.clearDirty = false; - - webGL.lastIndex = 0; - webGL.points = []; - webGL.indices = []; - - } - PIXI.WebGLGraphics.updateGraphics(graphics, gl); } - renderSession.shaderManager.activatePrimitiveShader(); + var webGL = graphics._webGL[gl.id]; // This could be speeded up for sure! - // set the matrix transform - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + for (var i = 0; i < webGL.data.length; i++) + { + if(webGL.data[i].mode === 1) + { + webGLData = webGL.data[i]; - gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); - gl.uniform2f(shader.projectionVector, projection.x, -projection.y); - gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + // render quad.. + gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); + + renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + } + else + { + webGLData = webGL.data[i]; + - gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderSession.shaderManager.primitiveShader; + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + + gl.uniform1f(shader.flipY, 1); + + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); - gl.uniform1f(shader.alpha, graphics.worldAlpha); - gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); - gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + gl.uniform1f(shader.alpha, graphics.worldAlpha); + - // set the index buffer! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); - gl.drawElements(gl.TRIANGLE_STRIP, webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); - renderSession.shaderManager.deactivatePrimitiveShader(); - - // return to default shader... -// PIXI.activateShader(PIXI.defaultShader); + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); + } + } }; /** @@ -5087,48 +6827,152 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projectio */ PIXI.WebGLGraphics.updateGraphics = function(graphics, gl) { + // get the contexts graphics object var webGL = graphics._webGL[gl.id]; + // if the graphics object does not exist in the webGL context time to create it! + if(!webGL)webGL = graphics._webGL[gl.id] = {lastIndex:0, data:[], gl:gl}; + + // flag the graphics as not dirty as we are about to update it... + graphics.dirty = false; + + var i; + + // if the user cleared the graphics object we will need to clear every object + if(graphics.clearDirty) + { + graphics.clearDirty = false; + + // lop through and return all the webGLDatas to the object pool so than can be reused later on + for (i = 0; i < webGL.data.length; i++) + { + var graphicsData = webGL.data[i]; + graphicsData.reset(); + PIXI.WebGLGraphics.graphicsDataPool.push( graphicsData ); + } + + // clear the array and reset the index.. + webGL.data = []; + webGL.lastIndex = 0; + } - for (var i = webGL.lastIndex; i < graphics.graphicsData.length; i++) + var webGLData; + + // loop through the graphics datas and construct each one.. + // if the object is a complex fill then the new stencil buffer technique will be used + // other wise graphics objects will be pushed into a batch.. + for (i = webGL.lastIndex; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; if(data.type === PIXI.Graphics.POLY) { + // need to add the points the the graphics object.. + data.points = data.shape.points.slice(); + if(data.shape.closed) + { + // close the poly if the value is true! + if(data.points[0] !== data.points[data.points.length-2] || data.points[1] !== data.points[data.points.length-1]) + { + data.points.push(data.points[0], data.points[1]); + } + } + + // MAKE SURE WE HAVE THE CORRECT TYPE.. if(data.fill) { - if(data.points.length>3) - PIXI.WebGLGraphics.buildPoly(data, webGL); + if(data.points.length >= 6) + { + if(data.points.length < 6 * 2) + { + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); + + var canDrawUsingSimple = PIXI.WebGLGraphics.buildPoly(data, webGLData); + // console.log(canDrawUsingSimple); + + if(!canDrawUsingSimple) + { + // console.log("<>>>") + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); + PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); + } + + } + else + { + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); + PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); + } + } } if(data.lineWidth > 0) { - PIXI.WebGLGraphics.buildLine(data, webGL); + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); + PIXI.WebGLGraphics.buildLine(data, webGLData); + } } - else if(data.type === PIXI.Graphics.RECT) + else { - PIXI.WebGLGraphics.buildRectangle(data, webGL); + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); + + if(data.type === PIXI.Graphics.RECT) + { + PIXI.WebGLGraphics.buildRectangle(data, webGLData); + } + else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) + { + PIXI.WebGLGraphics.buildCircle(data, webGLData); + } + else if(data.type === PIXI.Graphics.RREC) + { + PIXI.WebGLGraphics.buildRoundedRectangle(data, webGLData); + } } - else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) + + webGL.lastIndex++; + } + + // upload all the dirty data... + for (i = 0; i < webGL.data.length; i++) + { + webGLData = webGL.data[i]; + if(webGLData.dirty)webGLData.upload(); + } +}; + +/** + * @static + * @private + * @method switchMode + * @param webGL {WebGLContext} + * @param type {Number} + */ +PIXI.WebGLGraphics.switchMode = function(webGL, type) +{ + var webGLData; + + if(!webGL.data.length) + { + webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); + webGLData.mode = type; + webGL.data.push(webGLData); + } + else + { + webGLData = webGL.data[webGL.data.length-1]; + + if(webGLData.mode !== type || type === 1) { - PIXI.WebGLGraphics.buildCircle(data, webGL); + webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); + webGLData.mode = type; + webGL.data.push(webGLData); } } - webGL.lastIndex = graphics.graphicsData.length; + webGLData.dirty = true; - - - webGL.glPoints = new Float32Array(webGL.points); - - gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); - gl.bufferData(gl.ARRAY_BUFFER, webGL.glPoints, gl.STATIC_DRAW); - - webGL.glIndicies = new Uint16Array(webGL.indices); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, webGL.glIndicies, gl.STATIC_DRAW); + return webGLData; }; /** @@ -5145,12 +6989,11 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) // --- // // need to convert points to a nice regular data // - var rectData = graphicsData.points; - var x = rectData[0]; - var y = rectData[1]; - var width = rectData[2]; - var height = rectData[3]; - + var rectData = graphicsData.shape; + var x = rectData.x; + var y = rectData.y; + var width = rectData.width; + var height = rectData.height; if(graphicsData.fill) { @@ -5200,6 +7043,129 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) } }; +/** + * Builds a rounded rectangle to draw + * + * @static + * @private + * @method buildRoundedRectangle + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildRoundedRectangle = function(graphicsData, webGLData) +{ + var rrectData = graphicsData.shape; + var x = rrectData.x; + var y = rrectData.y; + var width = rrectData.width; + var height = rrectData.height; + + var radius = rrectData.radius; + + var recPoints = []; + recPoints.push(x, y + radius); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x, y + height - radius, x, y + height, x + radius, y + height)); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width - radius, y + height, x + width, y + height, x + width, y + height - radius)); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width, y + radius, x + width, y, x + width - radius, y)); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + radius, y, x, y, x, y + radius)); + + if (graphicsData.fill) { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + var triangles = PIXI.PolyK.Triangulate(recPoints); + + // + + var i = 0; + for (i = 0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vecPos); + indices.push(triangles[i] + vecPos); + indices.push(triangles[i+1] + vecPos); + indices.push(triangles[i+2] + vecPos); + indices.push(triangles[i+2] + vecPos); + } + + + for (i = 0; i < recPoints.length; i++) + { + verts.push(recPoints[i], recPoints[++i], r, g, b, alpha); + } + } + + if (graphicsData.lineWidth) { + var tempPoints = graphicsData.points; + + graphicsData.points = recPoints; + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Calculate the points for a quadratic bezier curve. (helper function..) + * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c + * + * @static + * @private + * @method quadraticBezierCurve + * @param fromX {Number} Origin point x + * @param fromY {Number} Origin point x + * @param cpX {Number} Control point x + * @param cpY {Number} Control point y + * @param toX {Number} Destination point x + * @param toY {Number} Destination point y + * @return {Array(Number)} + */ +PIXI.WebGLGraphics.quadraticBezierCurve = function(fromX, fromY, cpX, cpY, toX, toY) { + + var xa, + ya, + xb, + yb, + x, + y, + n = 20, + points = []; + + function getPt(n1 , n2, perc) { + var diff = n2 - n1; + + return n1 + ( diff * perc ); + } + + var j = 0; + for (var i = 0; i <= n; i++ ) + { + j = i / n; + + // The Green Line + xa = getPt( fromX , cpX , j ); + ya = getPt( fromY , cpY , j ); + xb = getPt( cpX , toX , j ); + yb = getPt( cpY , toY , j ); + + // The Black Dot + x = getPt( xa , xb , j ); + y = getPt( ya , yb , j ); + + points.push(x, y); + } + return points; +}; + /** * Builds a circle to draw * @@ -5211,13 +7177,24 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) */ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) { - // need to convert points to a nice regular data - var rectData = graphicsData.points; - var x = rectData[0]; - var y = rectData[1]; - var width = rectData[2]; - var height = rectData[3]; + var circleData = graphicsData.shape; + var x = circleData.x; + var y = circleData.y; + var width; + var height; + + // TODO - bit hacky?? + if(graphicsData.type === PIXI.Graphics.CIRC) + { + width = circleData.radius; + height = circleData.radius; + } + else + { + width = circleData.width; + height = circleData.height; + } var totalSegs = 40; var seg = (Math.PI * 2) / totalSegs ; @@ -5285,7 +7262,6 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) { // TODO OPTIMISE! var i = 0; - var points = graphicsData.points; if(points.length === 0)return; @@ -5304,6 +7280,9 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) // if the first point is the last point - gonna have issues :) if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) { + // need to clone as we are going to slightly modify the shape.. + points = points.slice(); + points.pop(); points.pop(); @@ -5482,6 +7461,68 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) indices.push(indexStart-1); }; +/** + * Builds a complex polygon to draw + * + * @static + * @private + * @method buildComplexPoly + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildComplexPoly = function(graphicsData, webGLData) +{ + //TODO - no need to copy this as it gets turned into a FLoat32Array anyways.. + var points = graphicsData.points.slice(); + if(points.length < 6)return; + + // get first and last point.. figure out the middle! + var indices = webGLData.indices; + webGLData.points = points; + webGLData.alpha = graphicsData.fillAlpha; + webGLData.color = PIXI.hex2rgb(graphicsData.fillColor); + + /* + calclate the bounds.. + */ + var minX = Infinity; + var maxX = -Infinity; + + var minY = Infinity; + var maxY = -Infinity; + + var x,y; + + // get size.. + for (var i = 0; i < points.length; i+=2) + { + x = points[i]; + y = points[i+1]; + + minX = x < minX ? x : minX; + maxX = x > maxX ? x : maxX; + + minY = y < minY ? y : minY; + maxY = y > maxY ? y : maxY; + } + + // add a quad to the end cos there is no point making another buffer! + points.push(minX, minY, + maxX, minY, + maxX, maxY, + minX, maxY); + + // push a quad onto the end.. + + //TODO - this aint needed! + var length = points.length / 2; + for (i = 0; i < length; i++) + { + indices.push( i ); + } + +}; + /** * Builds a polygon to draw * @@ -5494,8 +7535,8 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) { var points = graphicsData.points; - if(points.length < 6)return; + if(points.length < 6)return; // get first and last point.. figure out the middle! var verts = webGLData.points; var indices = webGLData.indices; @@ -5511,6 +7552,8 @@ PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) var triangles = PIXI.PolyK.Triangulate(points); + if(!triangles)return false; + var vertPos = verts.length / 6; var i = 0; @@ -5529,43 +7572,159 @@ PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) verts.push(points[i * 2], points[i * 2 + 1], r, g, b, alpha); } + + return true; }; - + +PIXI.WebGLGraphics.graphicsDataPool = []; + +/** + * @class WebGLGraphicsData + * @private + * @static + */ +PIXI.WebGLGraphicsData = function(gl) +{ + this.gl = gl; + + //TODO does this need to be split before uploding?? + this.color = [0,0,0]; // color split! + this.points = []; + this.indices = []; + this.buffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + this.mode = 1; + this.alpha = 1; + this.dirty = true; +}; + +/** + * @method reset + */ +PIXI.WebGLGraphicsData.prototype.reset = function() +{ + this.points = []; + this.indices = []; +}; + +/** + * @method upload + */ +PIXI.WebGLGraphicsData.prototype.upload = function() +{ + var gl = this.gl; + +// this.lastIndex = graphics.graphicsData.length; + this.glPoints = new PIXI.Float32Array(this.points); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); + gl.bufferData(gl.ARRAY_BUFFER, this.glPoints, gl.STATIC_DRAW); + + this.glIndicies = new PIXI.Uint16Array(this.indices); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.glIndicies, gl.STATIC_DRAW); + + this.dirty = false; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ PIXI.glContexts = []; // this is where we store the webGL contexts for easy access. +PIXI.instances = []; /** - * the WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer - * should be used for browsers that support webGL. This Render works by automatically managing webGLBatch's. - * So no need for Sprite Batch's or Sprite Cloud's - * Dont forget to add the view to your DOM or you will not see anything :) + * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. + * So no need for Sprite Batches or Sprite Clouds. + * Don't forget to add the view to your DOM or you will not see anything :) * * @class WebGLRenderer * @constructor - * @param width=0 {Number} the width of the canvas view - * @param height=0 {Number} the height of the canvas view - * @param view {HTMLCanvasElement} the canvas to use as a view, optional - * @param transparent=false {Boolean} If the render view is transparent, default false - * @param antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) - * + * @param [width=0] {Number} the width of the canvas view + * @param [height=0] {Number} the height of the canvas view + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false + * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 */ -PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) +PIXI.WebGLRenderer = function(width, height, options) { - if(!PIXI.defaultRenderer)PIXI.defaultRenderer = this; + if(options) + { + for (var i in PIXI.defaultRenderOptions) + { + if (typeof options[i] === 'undefined') options[i] = PIXI.defaultRenderOptions[i]; + } + } + else + { + options = PIXI.defaultRenderOptions; + } + if(!PIXI.defaultRenderer) + { + PIXI.sayHello('webGL'); + PIXI.defaultRenderer = this; + } + + /** + * @property type + * @type Number + */ this.type = PIXI.WEBGL_RENDERER; + /** + * The resolution of the renderer + * + * @property resolution + * @type Number + * @default 1 + */ + this.resolution = options.resolution; + // do a catch.. only 1 webGL renderer.. + /** * Whether the render view is transparent * * @property transparent * @type Boolean */ - this.transparent = !!transparent; + this.transparent = options.transparent; + + /** + * Whether the render view should be resized automatically + * + * @property autoResize + * @type Boolean + */ + this.autoResize = options.autoResize || false; + + /** + * The value of the preserveDrawingBuffer flag affects whether or not the contents of the stencil buffer is retained after rendering. + * + * @property preserveDrawingBuffer + * @type Boolean + */ + this.preserveDrawingBuffer = options.preserveDrawingBuffer; + + /** + * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: + * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. + * + * @property clearBeforeRender + * @type Boolean + * @default + */ + this.clearBeforeRender = options.clearBeforeRender; /** * The width of the canvas view @@ -5591,104 +7750,159 @@ PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) * @property view * @type HTMLCanvasElement */ - this.view = view || document.createElement( 'canvas' ); - this.view.width = this.width; - this.view.height = this.height; + this.view = options.view || document.createElement( 'canvas' ); // deal with losing context.. - this.contextLost = this.handleContextLost.bind(this); - this.contextRestoredLost = this.handleContextRestored.bind(this); - - this.view.addEventListener('webglcontextlost', this.contextLost, false); - this.view.addEventListener('webglcontextrestored', this.contextRestoredLost, false); - this.options = { + /** + * @property contextLostBound + * @type Function + */ + this.contextLostBound = this.handleContextLost.bind(this); + + /** + * @property contextRestoredBound + * @type Function + */ + this.contextRestoredBound = this.handleContextRestored.bind(this); + + this.view.addEventListener('webglcontextlost', this.contextLostBound, false); + this.view.addEventListener('webglcontextrestored', this.contextRestoredBound, false); + + /** + * @property _contextOptions + * @type Object + * @private + */ + this._contextOptions = { alpha: this.transparent, - antialias:!!antialias, // SPEED UP?? - premultipliedAlpha:!!transparent, - stencil:true + antialias: options.antialias, // SPEED UP?? + premultipliedAlpha:this.transparent && this.transparent !== 'notMultiplied', + stencil:true, + preserveDrawingBuffer: options.preserveDrawingBuffer }; - //try 'experimental-webgl' - try { - this.gl = this.view.getContext('experimental-webgl', this.options); - } catch (e) { - //try 'webgl' - try { - this.gl = this.view.getContext('webgl', this.options); - } catch (e2) { - // fail, not able to get a context - throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); - } - } - - var gl = this.gl; - this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++; - - PIXI.glContexts[this.glContextId] = gl; - - if(!PIXI.blendModesWebGL) - { - PIXI.blendModesWebGL = []; - - PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - PIXI.blendModesWebGL[PIXI.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - } - - - - + /** + * @property projection + * @type Point + */ this.projection = new PIXI.Point(); - this.projection.x = this.width/2; - this.projection.y = -this.height/2; + /** + * @property offset + * @type Point + */ this.offset = new PIXI.Point(0, 0); - this.resize(this.width, this.height); - this.contextLost = false; + // time to create the render managers! each one focuses on managing a state in webGL - // time to create the render managers! each one focuses on managine a state in webGL - this.shaderManager = new PIXI.WebGLShaderManager(gl); // deals with managing the shader programs and their attribs - this.spriteBatch = new PIXI.WebGLSpriteBatch(gl); // manages the rendering of sprites - this.maskManager = new PIXI.WebGLMaskManager(gl); // manages the masks using the stencil buffer - this.filterManager = new PIXI.WebGLFilterManager(gl, this.transparent); // manages the filters + /** + * Deals with managing the shader programs and their attribs + * @property shaderManager + * @type WebGLShaderManager + */ + this.shaderManager = new PIXI.WebGLShaderManager(); + /** + * Manages the rendering of sprites + * @property spriteBatch + * @type WebGLSpriteBatch + */ + this.spriteBatch = new PIXI.WebGLSpriteBatch(); + + /** + * Manages the masks using the stencil buffer + * @property maskManager + * @type WebGLMaskManager + */ + this.maskManager = new PIXI.WebGLMaskManager(); + + /** + * Manages the filters + * @property filterManager + * @type WebGLFilterManager + */ + this.filterManager = new PIXI.WebGLFilterManager(); + + /** + * Manages the stencil buffer + * @property stencilManager + * @type WebGLStencilManager + */ + this.stencilManager = new PIXI.WebGLStencilManager(); + + /** + * Manages the blendModes + * @property blendModeManager + * @type WebGLBlendModeManager + */ + this.blendModeManager = new PIXI.WebGLBlendModeManager(); + + /** + * TODO remove + * @property renderSession + * @type Object + */ this.renderSession = {}; this.renderSession.gl = this.gl; this.renderSession.drawCount = 0; this.renderSession.shaderManager = this.shaderManager; this.renderSession.maskManager = this.maskManager; this.renderSession.filterManager = this.filterManager; + this.renderSession.blendModeManager = this.blendModeManager; this.renderSession.spriteBatch = this.spriteBatch; + this.renderSession.stencilManager = this.stencilManager; + this.renderSession.renderer = this; + this.renderSession.resolution = this.resolution; + // time init the context.. + this.initContext(); - gl.useProgram(this.shaderManager.defaultShader.program); - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - - gl.enable(gl.BLEND); - gl.colorMask(true, true, true, this.transparent); + // map some webGL blend modes.. + this.mapBlendModes(); }; // constructor PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; +/** +* @method initContext +*/ +PIXI.WebGLRenderer.prototype.initContext = function() +{ + var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); + this.gl = gl; + + if (!gl) { + // fail, not able to get a context + throw new Error('This browser does not support webGL. Try using the canvas renderer'); + } + + this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++; + + PIXI.glContexts[this.glContextId] = gl; + + PIXI.instances[this.glContextId] = this; + + // set up the default pixi settings.. + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.enable(gl.BLEND); + + // need to set the context for all the managers... + this.shaderManager.setContext(gl); + this.spriteBatch.setContext(gl); + this.maskManager.setContext(gl); + this.filterManager.setContext(gl); + this.blendModeManager.setContext(gl); + this.stencilManager.setContext(gl); + + this.renderSession.gl = this.gl; + + // now resize and we are good to go! + this.resize(this.width, this.height); +}; + /** * Renders the stage to its webGL view * @@ -5697,9 +7911,9 @@ PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; */ PIXI.WebGLRenderer.prototype.render = function(stage) { + // no point rendering if our context has been blown up! if(this.contextLost)return; - // if rendering a new stage clear the batches.. if(this.__stage !== stage) { @@ -5710,37 +7924,13 @@ PIXI.WebGLRenderer.prototype.render = function(stage) this.__stage = stage; } - // update any textures this includes uvs and uploading them to the gpu - PIXI.WebGLRenderer.updateTextures(); - // update the scene graph stage.updateTransform(); var gl = this.gl; - // -- Does this need to be set every frame? -- // - //gl.colorMask(true, true, true, this.transparent); - gl.viewport(0, 0, this.width, this.height); - - // make sure we are bound to the main frame buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - if(this.transparent) - { - gl.clearColor(0, 0, 0, 0); - } - else - { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); - } - - - gl.clear(gl.COLOR_BUFFER_BIT); - - this.renderDisplayObject( stage, this.projection ); - // interaction - if(stage.interactive) + if(stage._interactive) { //need to add some events! if(!stage._interactiveEventsAdded) @@ -5758,43 +7948,51 @@ PIXI.WebGLRenderer.prototype.render = function(stage) } } - /* - //can simulate context loss in Chrome like so: - this.view.onmousedown = function(ev) { - console.dir(this.gl.getSupportedExtensions()); - var ext = ( - gl.getExtension("WEBGL_scompressed_texture_s3tc") - // gl.getExtension("WEBGL_compressed_texture_s3tc") || - // gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || - // gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") - ); - console.dir(ext); - var loseCtx = this.gl.getExtension("WEBGL_lose_context"); - console.log("killing context"); - loseCtx.loseContext(); - setTimeout(function() { - console.log("restoring context..."); - loseCtx.restoreContext(); - }.bind(this), 1000); - }.bind(this); - */ + // -- Does this need to be set every frame? -- // + gl.viewport(0, 0, this.width, this.height); + + // make sure we are bound to the main frame buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + if (this.clearBeforeRender) + { + if(this.transparent) + { + gl.clearColor(0, 0, 0, 0); + } + else + { + gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + } + + gl.clear (gl.COLOR_BUFFER_BIT); + } + + this.renderDisplayObject( stage, this.projection ); }; /** - * Renders a display Object + * Renders a Display Object. * - * @method renderDIsplayObject + * @method renderDisplayObject * @param displayObject {DisplayObject} The DisplayObject to render * @param projection {Point} The projection - * @param buffer {Array} a standard WebGL buffer + * @param buffer {Array} a standard WebGL buffer */ PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer) { + this.renderSession.blendModeManager.setBlendMode(PIXI.blendModes.NORMAL); + // reset the render session data.. this.renderSession.drawCount = 0; - this.renderSession.currentBlendMode = 9999; + // make sure to flip the Y if using a render texture.. + this.renderSession.flipY = buffer ? -1 : 1; + + // set the default projection this.renderSession.projection = projection; + + //set the default offset this.renderSession.offset = this.offset; // start the sprite batch @@ -5811,74 +8009,7 @@ PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, proje }; /** - * Updates the textures loaded into this webgl renderer - * - * @static - * @method updateTextures - * @private - */ -PIXI.WebGLRenderer.updateTextures = function() -{ - var i = 0; - - //TODO break this out into a texture manager... - //for (i = 0; i < PIXI.texturesToUpdate.length; i++) - // PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); - - - for (i=0; i < PIXI.Texture.frameUpdates.length; i++) - PIXI.WebGLRenderer.updateTextureFrame(PIXI.Texture.frameUpdates[i]); - - for (i = 0; i < PIXI.texturesToDestroy.length; i++) - PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); - - PIXI.texturesToUpdate.length = 0; - PIXI.texturesToDestroy.length = 0; - PIXI.Texture.frameUpdates.length = 0; -}; - -/** - * Destroys a loaded webgl texture - * - * @method destroyTexture - * @param texture {Texture} The texture to update - * @private - */ -PIXI.WebGLRenderer.destroyTexture = function(texture) -{ - //TODO break this out into a texture manager... - - for (var i = texture._glTextures.length - 1; i >= 0; i--) - { - var glTexture = texture._glTextures[i]; - var gl = PIXI.glContexts[i]; - - if(gl && glTexture) - { - gl.deleteTexture(glTexture); - } - } - - texture._glTextures.length = 0; -}; - -/** - * - * @method updateTextureFrame - * @param texture {Texture} The texture to update the frame from - * @private - */ -PIXI.WebGLRenderer.updateTextureFrame = function(texture) -{ - texture.updateFrame = false; - - // now set the uvs. Figured that the uv data sits with a texture rather than a sprite. - // so uv data is stored on the texture itself - texture._updateWebGLuvs(); -}; - -/** - * resizes the webGL view to the specified width and height + * Resizes the webGL view to the specified width and height. * * @method resize * @param width {Number} the new width of the webGL view @@ -5886,97 +8017,72 @@ PIXI.WebGLRenderer.updateTextureFrame = function(texture) */ PIXI.WebGLRenderer.prototype.resize = function(width, height) { - this.width = width; - this.height = height; + this.width = width * this.resolution; + this.height = height * this.resolution; - this.view.width = width; - this.view.height = height; + this.view.width = this.width; + this.view.height = this.height; + + if (this.autoResize) { + this.view.style.width = this.width / this.resolution + 'px'; + this.view.style.height = this.height / this.resolution + 'px'; + } this.gl.viewport(0, 0, this.width, this.height); - this.projection.x = this.width/2; - this.projection.y = -this.height/2; + this.projection.x = this.width / 2 / this.resolution; + this.projection.y = -this.height / 2 / this.resolution; }; /** - * Creates a WebGL texture + * Updates and Creates a WebGL texture for the renderers context. * - * @method createWebGLTexture - * @param texture {Texture} the texture to render - * @param gl {webglContext} the WebGL context - * @static + * @method updateTexture + * @param texture {Texture} the texture to update */ -PIXI.createWebGLTexture = function(texture, gl) +PIXI.WebGLRenderer.prototype.updateTexture = function(texture) { + if(!texture.hasLoaded )return; + var gl = this.gl; - if(texture.hasLoaded) + if(!texture._glTextures[gl.id])texture._glTextures[gl.id] = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + + + if(texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height)) { - texture._glTextures[gl.id] = gl.createTexture(); - - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); - - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - - // reguler... - - if(!texture._powerOf2) - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - } - else - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); - } - - gl.bindTexture(gl.TEXTURE_2D, null); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + gl.generateMipmap(gl.TEXTURE_2D); } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + } + + // reguler... + if(!texture._powerOf2) + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + texture._dirty[gl.id] = false; return texture._glTextures[gl.id]; }; -/** - * Updates a WebGL texture - * - * @method updateWebGLTexture - * @param texture {Texture} the texture to update - * @param gl {webglContext} the WebGL context - * @private - */ -PIXI.updateWebGLTexture = function(texture, gl) -{ - if( texture._glTextures[gl.id] ) - { - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); - - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - - // reguler... - - if(!texture._powerOf2) - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - } - else - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); - } - - gl.bindTexture(gl.TEXTURE_2D, null); - } - -}; - /** * Handles a lost webgl context * @@ -5999,55 +8105,16 @@ PIXI.WebGLRenderer.prototype.handleContextLost = function(event) */ PIXI.WebGLRenderer.prototype.handleContextRestored = function() { + this.initContext(); - //try 'experimental-webgl' - try { - this.gl = this.view.getContext('experimental-webgl', this.options); - } catch (e) { - //try 'webgl' - try { - this.gl = this.view.getContext('webgl', this.options); - } catch (e2) { - // fail, not able to get a context - throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); - } - } - - var gl = this.gl; - gl.id = PIXI.WebGLRenderer.glContextId ++; - - - - // need to set the context... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - - - this.renderSession.gl = this.gl; - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - - gl.enable(gl.BLEND); - gl.colorMask(true, true, true, this.transparent); - - this.gl.viewport(0, 0, this.width, this.height); - + // empty all the ol gl textures as they are useless now for(var key in PIXI.TextureCache) { var texture = PIXI.TextureCache[key].baseTexture; texture._glTextures = []; } - /** - * Whether the context was lost - * @property contextLost - * @type Boolean - */ this.contextLost = false; - }; /** @@ -6057,12 +8124,9 @@ PIXI.WebGLRenderer.prototype.handleContextRestored = function() */ PIXI.WebGLRenderer.prototype.destroy = function() { - - // deal with losing context.. - // remove listeners - this.view.removeEventListener('webglcontextlost', this.contextLost); - this.view.removeEventListener('webglcontextrestored', this.contextRestoredLost); + this.view.removeEventListener('webglcontextlost', this.contextLostBound); + this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); PIXI.glContexts[this.glContextId] = null; @@ -6079,36 +8143,123 @@ PIXI.WebGLRenderer.prototype.destroy = function() this.spriteBatch = null; this.maskManager = null; this.filterManager = null; - + this.gl = null; - // this.renderSession = null; }; +/** + * Maps Pixi blend modes to WebGL blend modes. + * + * @method mapBlendModes + */ +PIXI.WebGLRenderer.prototype.mapBlendModes = function() +{ + var gl = this.gl; + + if(!PIXI.blendModesWebGL) + { + PIXI.blendModesWebGL = []; + + PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + PIXI.blendModesWebGL[PIXI.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + } +}; PIXI.WebGLRenderer.glContextId = 0; - + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLBlendModeManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLBlendModeManager = function() +{ + /** + * @property currentBlendMode + * @type Number + */ + this.currentBlendMode = 99999; +}; + +PIXI.WebGLBlendModeManager.prototype.constructor = PIXI.WebGLBlendModeManager; + +/** + * Sets the WebGL Context. + * + * @method setContext + * @param gl {WebGLContext} the current WebGL drawing context + */ +PIXI.WebGLBlendModeManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +/** +* Sets-up the given blendMode from WebGL's point of view. +* +* @method setBlendMode +* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD +*/ +PIXI.WebGLBlendModeManager.prototype.setBlendMode = function(blendMode) +{ + if(this.currentBlendMode === blendMode)return false; + + this.currentBlendMode = blendMode; + + var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; + this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + + return true; +}; + +/** +* Destroys this object. +* +* @method destroy +*/ +PIXI.WebGLBlendModeManager.prototype.destroy = function() +{ + this.gl = null; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - /** * @class WebGLMaskManager * @constructor -* @param gl {WebGLContext} the current WebGL drawing context * @private */ -PIXI.WebGLMaskManager = function(gl) +PIXI.WebGLMaskManager = function() { - this.maskStack = []; - this.maskPosition = 0; - - this.setContext(gl); }; +PIXI.WebGLMaskManager.prototype.constructor = PIXI.WebGLMaskManager; + /** -* Sets the drawing context to the one given in parameter +* Sets the drawing context to the one given in parameter. +* * @method setContext * @param gl {WebGLContext} the current WebGL drawing context */ @@ -6118,73 +8269,347 @@ PIXI.WebGLMaskManager.prototype.setContext = function(gl) }; /** -* Applies the Mask and adds it to the current filter stack +* Applies the Mask and adds it to the current filter stack. +* * @method pushMask * @param maskData {Array} -* @param renderSession {RenderSession} +* @param renderSession {Object} */ PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession) { - var gl = this.gl; + var gl = renderSession.gl; - if(this.maskStack.length === 0) + if(maskData.dirty) { - gl.enable(gl.STENCIL_TEST); - gl.stencilFunc(gl.ALWAYS,1,1); + PIXI.WebGLGraphics.updateGraphics(maskData, gl); } - - // maskData.visible = false; - this.maskStack.push(maskData); - - gl.colorMask(false, false, false, true); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + if(!maskData._webGL[gl.id].data.length)return; - PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL,0, this.maskStack.length); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); }; /** -* Removes the last filter from the filter stack and doesn't return it +* Removes the last filter from the filter stack and doesn't return it. +* * @method popMask -* -* @param renderSession {RenderSession} an object containing all the useful parameters +* @param maskData {Array} +* @param renderSession {Object} an object containing all the useful parameters */ -PIXI.WebGLMaskManager.prototype.popMask = function(renderSession) +PIXI.WebGLMaskManager.prototype.popMask = function(maskData, renderSession) { var gl = this.gl; - - var maskData = this.maskStack.pop(); - - if(maskData) - { - gl.colorMask(false, false, false, false); - - //gl.stencilFunc(gl.ALWAYS,1,1); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); - - PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL,0,this.maskStack.length); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - - if(this.maskStack.length === 0)gl.disable(gl.STENCIL_TEST); + renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); }; /** -* Destroys the mask stack +* Destroys the mask stack. +* * @method destroy */ PIXI.WebGLMaskManager.prototype.destroy = function() { - this.maskStack = null; this.gl = null; -}; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLStencilManager +* @constructor +* @private +*/ +PIXI.WebGLStencilManager = function() +{ + this.stencilStack = []; + this.reverse = true; + this.count = 0; +}; + +/** +* Sets the drawing context to the one given in parameter. +* +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLStencilManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +/** +* Applies the Mask and adds it to the current filter stack. +* +* @method pushMask +* @param graphics {Graphics} +* @param webGLData {Array} +* @param renderSession {Object} +*/ +PIXI.WebGLStencilManager.prototype.pushStencil = function(graphics, webGLData, renderSession) +{ + var gl = this.gl; + this.bindGraphics(graphics, webGLData, renderSession); + + if(this.stencilStack.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + this.reverse = true; + this.count = 0; + } + + this.stencilStack.push(webGLData); + + var level = this.count; + + gl.colorMask(false, false, false, false); + + gl.stencilFunc(gl.ALWAYS,0,0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); + + // draw the triangle strip! + + if(webGLData.mode === 1) + { + gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); + + if(this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + + // draw a quad to increment.. + gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); + + if(this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + } + + this.reverse = !this.reverse; + } + else + { + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + + gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); + + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + } + } + + gl.colorMask(true, true, true, true); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + + this.count++; +}; + +/** + * TODO this does not belong here! + * + * @method bindGraphics + * @param graphics {Graphics} + * @param webGLData {Array} + * @param renderSession {Object} + */ +PIXI.WebGLStencilManager.prototype.bindGraphics = function(graphics, webGLData, renderSession) +{ + //if(this._currentGraphics === graphics)return; + this._currentGraphics = graphics; + + var gl = this.gl; + + // bind the graphics object.. + var projection = renderSession.projection, + offset = renderSession.offset, + shader;// = renderSession.shaderManager.primitiveShader; + + if(webGLData.mode === 1) + { + shader = renderSession.shaderManager.complexPrimitiveShader; + + renderSession.shaderManager.setShader( shader ); + + gl.uniform1f(shader.flipY, renderSession.flipY); + + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + gl.uniform3fv(shader.color, webGLData.color); + + gl.uniform1f(shader.alpha, graphics.worldAlpha * webGLData.alpha); + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 2, 0); + + + // now do the rest.. + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + } + else + { + //renderSession.shaderManager.activatePrimitiveShader(); + shader = renderSession.shaderManager.primitiveShader; + renderSession.shaderManager.setShader( shader ); + + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + + gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + + gl.uniform1f(shader.alpha, graphics.worldAlpha); + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + } +}; + +/** + * @method popStencil + * @param graphics {Graphics} + * @param webGLData {Array} + * @param renderSession {Object} + */ +PIXI.WebGLStencilManager.prototype.popStencil = function(graphics, webGLData, renderSession) +{ + var gl = this.gl; + this.stencilStack.pop(); + + this.count--; + + if(this.stencilStack.length === 0) + { + // the stack is empty! + gl.disable(gl.STENCIL_TEST); + + } + else + { + + var level = this.count; + + this.bindGraphics(graphics, webGLData, renderSession); + + gl.colorMask(false, false, false, false); + + if(webGLData.mode === 1) + { + this.reverse = !this.reverse; + + if(this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + + // draw a quad to increment.. + gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); + + gl.stencilFunc(gl.ALWAYS,0,0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); + + // draw the triangle strip! + gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); + + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + } + + } + else + { + // console.log("<<>>") + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + + gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); + + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + } + } + + gl.colorMask(true, true, true, true); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + + + } +}; + +/** +* Destroys the mask stack. +* +* @method destroy +*/ +PIXI.WebGLStencilManager.prototype.destroy = function() +{ + this.stencilStack = null; + this.gl = null; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6192,59 +8617,79 @@ PIXI.WebGLMaskManager.prototype.destroy = function() /** * @class WebGLShaderManager * @constructor -* @param gl {WebGLContext} the current WebGL drawing context * @private */ -PIXI.WebGLShaderManager = function(gl) +PIXI.WebGLShaderManager = function() { - + /** + * @property maxAttibs + * @type Number + */ this.maxAttibs = 10; + + /** + * @property attribState + * @type Array + */ this.attribState = []; + + /** + * @property tempAttribState + * @type Array + */ this.tempAttribState = []; - for (var i = 0; i < this.maxAttibs; i++) { + for (var i = 0; i < this.maxAttibs; i++) + { this.attribState[i] = false; } - this.setContext(gl); - // the final one is used for the rendering strips - //this.stripShader = new PIXI.StripShader(gl); + /** + * @property stack + * @type Array + */ + this.stack = []; + }; +PIXI.WebGLShaderManager.prototype.constructor = PIXI.WebGLShaderManager; /** -* Initialises the context and the properties +* Initialises the context and the properties. +* * @method setContext * @param gl {WebGLContext} the current WebGL drawing context -* @param transparent {Boolean} Whether or not the drawing context should be transparent */ PIXI.WebGLShaderManager.prototype.setContext = function(gl) { this.gl = gl; - // the next one is used for rendering primatives + // the next one is used for rendering primitives this.primitiveShader = new PIXI.PrimitiveShader(gl); + // the next one is used for rendering triangle strips + this.complexPrimitiveShader = new PIXI.ComplexPrimitiveShader(gl); + // this shader is used for the default sprite rendering this.defaultShader = new PIXI.PixiShader(gl); // this shader is used for the fast sprite rendering this.fastShader = new PIXI.PixiFastShader(gl); - - this.activateShader(this.defaultShader); + // the next one is used for rendering triangle strips + this.stripShader = new PIXI.StripShader(gl); + this.setShader(this.defaultShader); }; - /** -* Takes the attributes given in parameters +* Takes the attributes given in parameters. +* * @method setAttribs * @param attribs {Array} attribs */ PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs) { // reset temp state - var i; for (i = 0; i < this.tempAttribState.length; i++) @@ -6263,7 +8708,6 @@ PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs) for (i = 0; i < this.attribState.length; i++) { - if(this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; @@ -6281,51 +8725,28 @@ PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs) }; /** -* Sets-up the given shader -* -* @method activateShader -* @param shader {Object} the shader that is going to be activated +* Sets the current shader. +* +* @method setShader +* @param shader {Any} */ -PIXI.WebGLShaderManager.prototype.activateShader = function(shader) +PIXI.WebGLShaderManager.prototype.setShader = function(shader) { - //if(this.currentShader == shader)return; + if(this._currentId === shader._UID)return false; + + this._currentId = shader._UID; this.currentShader = shader; this.gl.useProgram(shader.program); this.setAttribs(shader.attributes); - + + return true; }; /** -* Triggers the primitive shader -* @method activatePrimitiveShader -*/ -PIXI.WebGLShaderManager.prototype.activatePrimitiveShader = function() -{ - var gl = this.gl; - - gl.useProgram(this.primitiveShader.program); - - this.setAttribs(this.primitiveShader.attributes); - -}; - -/** -* Disable the primitive shader -* @method deactivatePrimitiveShader -*/ -PIXI.WebGLShaderManager.prototype.deactivatePrimitiveShader = function() -{ - var gl = this.gl; - - gl.useProgram(this.defaultShader.program); - - this.setAttribs(this.defaultShader.attributes); -}; - -/** -* Destroys +* Destroys this object. +* * @method destroy */ PIXI.WebGLShaderManager.prototype.destroy = function() @@ -6336,20 +8757,24 @@ PIXI.WebGLShaderManager.prototype.destroy = function() this.primitiveShader.destroy(); + this.complexPrimitiveShader.destroy(); + this.defaultShader.destroy(); this.fastShader.destroy(); + this.stripShader.destroy(); + this.gl = null; }; - /** * @author Mat Groves * * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ * for creating the original pixi version! - * + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * * Heavily inspired by LibGDX's WebGLSpriteBatch: * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java */ @@ -6359,51 +8784,63 @@ PIXI.WebGLShaderManager.prototype.destroy = function() * @class WebGLSpriteBatch * @private * @constructor - * @param gl {WebGLContext} the current WebGL drawing context - * */ -PIXI.WebGLSpriteBatch = function(gl) +PIXI.WebGLSpriteBatch = function() { - /** - * - * * @property vertSize * @type Number */ - this.vertSize = 6; + this.vertSize = 5; /** * The number of images in the SpriteBatch before it flushes * @property size * @type Number */ - this.size = 10000;//Math.pow(2, 16) / this.vertSize; + this.size = 2000;//Math.pow(2, 16) / this.vertSize; - //the total number of floats in our batch - var numVerts = this.size * 4 * this.vertSize; + //the total number of bytes in our batch + var numVerts = this.size * 4 * 4 * this.vertSize; //the total number of indices in our batch var numIndices = this.size * 6; - //vertex data - /** * Holds the vertices * * @property vertices + * @type ArrayBuffer + */ + this.vertices = new PIXI.ArrayBuffer(numVerts); + + /** + * View on the vertices as a Float32Array + * + * @property positions * @type Float32Array */ - this.vertices = new Float32Array(numVerts); + this.positions = new PIXI.Float32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array + * + * @property colors + * @type Uint32Array + */ + this.colors = new PIXI.Uint32Array(this.vertices); - //index data /** * Holds the indices * * @property indices * @type Uint16Array */ - this.indices = new Uint16Array(numIndices); + this.indices = new PIXI.Uint16Array(numIndices); + /** + * @property lastIndexCount + * @type Number + */ this.lastIndexCount = 0; for (var i=0, j=0; i < numIndices; i += 6, j += 4) @@ -6416,18 +8853,71 @@ PIXI.WebGLSpriteBatch = function(gl) this.indices[i + 5] = j + 3; } - + /** + * @property drawing + * @type Boolean + */ this.drawing = false; + + /** + * @property currentBatchSize + * @type Number + */ this.currentBatchSize = 0; + + /** + * @property currentBaseTexture + * @type BaseTexture + */ this.currentBaseTexture = null; - - this.setContext(gl); + + /** + * @property dirty + * @type Boolean + */ + this.dirty = true; + + /** + * @property textures + * @type Array + */ + this.textures = []; + + /** + * @property blendModes + * @type Array + */ + this.blendModes = []; + + /** + * @property shaders + * @type Array + */ + this.shaders = []; + + /** + * @property sprites + * @type Array + */ + this.sprites = []; + + /** + * @property defaultShader + * @type AbstractFilter + */ + this.defaultShader = new PIXI.AbstractFilter([ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]); }; /** -* * @method setContext -* * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) @@ -6440,7 +8930,6 @@ PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) // 65535 is max index, so 65535 / 6 = 10922. - //upload the index data gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); @@ -6449,13 +8938,19 @@ PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); this.currentBlendMode = 99999; + + var shader = new PIXI.PixiShader(gl); + + shader.fragmentSrc = this.defaultShader.fragmentSrc; + shader.uniforms = {}; + shader.init(); + + this.defaultShader.shaders[gl.id] = shader; }; /** -* * @method begin -* -* @param renderSession {RenderSession} the RenderSession +* @param renderSession {Object} The RenderSession object */ PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession) { @@ -6466,9 +8961,7 @@ PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession) }; /** -* * @method end -* */ PIXI.WebGLSpriteBatch.prototype.end = function() { @@ -6476,146 +8969,150 @@ PIXI.WebGLSpriteBatch.prototype.end = function() }; /** -* * @method render -* * @param sprite {Sprite} the sprite to render when using this spritebatch */ PIXI.WebGLSpriteBatch.prototype.render = function(sprite) { + var texture = sprite.texture; + + //TODO set blend modes.. // check texture.. - if(sprite.texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + if(this.currentBatchSize >= this.size) { this.flush(); - this.currentBaseTexture = sprite.texture.baseTexture; - } - - - // check blend mode - if(sprite.blendMode !== this.currentBlendMode) - { - this.setBlendMode(sprite.blendMode); + this.currentBaseTexture = texture.baseTexture; } // get the uvs for the texture - var uvs = sprite._uvs || sprite.texture._uvs; + var uvs = texture._uvs; // if the uvs have not updated then no point rendering just yet! if(!uvs)return; - // get the sprites current alpha - var alpha = sprite.worldAlpha; - var tint = sprite.tint; - - var verticies = this.vertices; - - var width = sprite.texture.frame.width; - var height = sprite.texture.frame.height; - // TODO trim?? var aX = sprite.anchor.x; var aY = sprite.anchor.y; var w0, w1, h0, h1; - if (sprite.texture.trim) + if (texture.trim) { // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - var trim = sprite.texture.trim; + var trim = texture.trim; w1 = trim.x - aX * trim.width; - w0 = w1 + width; + w0 = w1 + texture.crop.width; h1 = trim.y - aY * trim.height; - h0 = h1 + height; + h0 = h1 + texture.crop.height; + } else { - w0 = (width ) * (1-aX); - w1 = (width ) * -aX; + w0 = (texture.frame.width ) * (1-aX); + w1 = (texture.frame.width ) * -aX; - h0 = height * (1-aY); - h1 = height * -aY; + h0 = texture.frame.height * (1-aY); + h1 = texture.frame.height * -aY; } var index = this.currentBatchSize * 4 * this.vertSize; - - var worldTransform = sprite.worldTransform;//.toArray(); - - var a = worldTransform.a;//[0]; - var b = worldTransform.c;//[3]; - var c = worldTransform.b;//[1]; - var d = worldTransform.d;//[4]; - var tx = worldTransform.tx;//[2]; - var ty = worldTransform.ty;///[5]; - - // xy - verticies[index++] = a * w1 + c * h1 + tx; - verticies[index++] = d * h1 + b * w1 + ty; - // uv - verticies[index++] = uvs.x0; - verticies[index++] = uvs.y0; - // color - verticies[index++] = alpha; - verticies[index++] = tint; - - // xy - verticies[index++] = a * w0 + c * h1 + tx; - verticies[index++] = d * h1 + b * w0 + ty; - // uv - verticies[index++] = uvs.x1; - verticies[index++] = uvs.y1; - // color - verticies[index++] = alpha; - verticies[index++] = tint; - - // xy - verticies[index++] = a * w0 + c * h0 + tx; - verticies[index++] = d * h0 + b * w0 + ty; - // uv - verticies[index++] = uvs.x2; - verticies[index++] = uvs.y2; - // color - verticies[index++] = alpha; - verticies[index++] = tint; - - // xy - verticies[index++] = a * w1 + c * h0 + tx; - verticies[index++] = d * h0 + b * w1 + ty; - // uv - verticies[index++] = uvs.x3; - verticies[index++] = uvs.y3; - // color - verticies[index++] = alpha; - verticies[index++] = tint; + var resolution = texture.baseTexture.resolution; + + var worldTransform = sprite.worldTransform; + + var a = worldTransform.a / resolution; + var b = worldTransform.b / resolution; + var c = worldTransform.c / resolution; + var d = worldTransform.d / resolution; + var tx = worldTransform.tx; + var ty = worldTransform.ty; + + var colors = this.colors; + var positions = this.positions; + + if(this.renderSession.roundPixels) + { + // xy + positions[index] = a * w1 + c * h1 + tx | 0; + positions[index+1] = d * h1 + b * w1 + ty | 0; + + // xy + positions[index+5] = a * w0 + c * h1 + tx | 0; + positions[index+6] = d * h1 + b * w0 + ty | 0; + + // xy + positions[index+10] = a * w0 + c * h0 + tx | 0; + positions[index+11] = d * h0 + b * w0 + ty | 0; + + // xy + positions[index+15] = a * w1 + c * h0 + tx | 0; + positions[index+16] = d * h0 + b * w1 + ty | 0; + } + else + { + // xy + positions[index] = a * w1 + c * h1 + tx; + positions[index+1] = d * h1 + b * w1 + ty; + + // xy + positions[index+5] = a * w0 + c * h1 + tx; + positions[index+6] = d * h1 + b * w0 + ty; + + // xy + positions[index+10] = a * w0 + c * h0 + tx; + positions[index+11] = d * h0 + b * w0 + ty; + + // xy + positions[index+15] = a * w1 + c * h0 + tx; + positions[index+16] = d * h0 + b * w1 + ty; + } + + // uv + positions[index+2] = uvs.x0; + positions[index+3] = uvs.y0; + + // uv + positions[index+7] = uvs.x1; + positions[index+8] = uvs.y1; + + // uv + positions[index+12] = uvs.x2; + positions[index+13] = uvs.y2; + + // uv + positions[index+17] = uvs.x3; + positions[index+18] = uvs.y3; + + // color and alpha + var tint = sprite.tint; + colors[index+4] = colors[index+9] = colors[index+14] = colors[index+19] = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + // increment the batchsize - this.currentBatchSize++; + this.sprites[this.currentBatchSize++] = sprite; }; /** -* Renders a tilingSprite using the spriteBatch -* @method renderTilingSprite +* Renders a TilingSprite using the spriteBatch. * +* @method renderTilingSprite * @param sprite {TilingSprite} the tilingSprite to render */ PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) { var texture = tilingSprite.tilingTexture; - if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + // check texture.. + if(this.currentBatchSize >= this.size) { + //return; this.flush(); this.currentBaseTexture = texture.baseTexture; } - // check blend mode - if(tilingSprite.blendMode !== this.currentBlendMode) - { - this.setBlendMode(tilingSprite.blendMode); - } - // set the textures uvs temporarily // TODO create a separate texture so that we can tile part of a texture @@ -6623,11 +9120,11 @@ PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) var uvs = tilingSprite._uvs; - tilingSprite.tilePosition.x %= texture.baseTexture.width; - tilingSprite.tilePosition.y %= texture.baseTexture.height; + tilingSprite.tilePosition.x %= texture.baseTexture.width * tilingSprite.tileScaleOffset.x; + tilingSprite.tilePosition.y %= texture.baseTexture.height * tilingSprite.tileScaleOffset.y; - var offsetX = tilingSprite.tilePosition.x/texture.baseTexture.width; - var offsetY = tilingSprite.tilePosition.y/texture.baseTexture.height; + var offsetX = tilingSprite.tilePosition.x/(texture.baseTexture.width*tilingSprite.tileScaleOffset.x); + var offsetY = tilingSprite.tilePosition.y/(texture.baseTexture.height*tilingSprite.tileScaleOffset.y); var scaleX = (tilingSprite.width / texture.baseTexture.width) / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x); var scaleY = (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y); @@ -6642,20 +9139,21 @@ PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) uvs.y2 = (1 * scaleY) - offsetY; uvs.x3 = 0 - offsetX; - uvs.y3 = (1 *scaleY) - offsetY; + uvs.y3 = (1 * scaleY) - offsetY; - // get the tilingSprites current alpha - var alpha = tilingSprite.worldAlpha; + // get the tilingSprites current alpha and tint and combining them into a single color var tint = tilingSprite.tint; + var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (tilingSprite.alpha * 255 << 24); - var verticies = this.vertices; + var positions = this.positions; + var colors = this.colors; var width = tilingSprite.width; var height = tilingSprite.height; // TODO trim?? - var aX = tilingSprite.anchor.x; // - tilingSprite.texture.trim.x - var aY = tilingSprite.anchor.y; //- tilingSprite.texture.trim.y + var aX = tilingSprite.anchor.x; + var aY = tilingSprite.anchor.y; var w0 = width * (1-aX); var w1 = width * -aX; @@ -6664,65 +9162,61 @@ PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) var index = this.currentBatchSize * 4 * this.vertSize; + var resolution = texture.baseTexture.resolution; + var worldTransform = tilingSprite.worldTransform; - var a = worldTransform.a;//[0]; - var b = worldTransform.c;//[3]; - var c = worldTransform.b;//[1]; - var d = worldTransform.d;//[4]; + var a = worldTransform.a / resolution;//[0]; + var b = worldTransform.b / resolution;//[3]; + var c = worldTransform.c / resolution;//[1]; + var d = worldTransform.d / resolution;//[4]; var tx = worldTransform.tx;//[2]; - var ty = worldTransform.ty;///[5]; + var ty = worldTransform.ty;//[5]; // xy - verticies[index++] = a * w1 + c * h1 + tx; - verticies[index++] = d * h1 + b * w1 + ty; + positions[index++] = a * w1 + c * h1 + tx; + positions[index++] = d * h1 + b * w1 + ty; // uv - verticies[index++] = uvs.x0; - verticies[index++] = uvs.y0; + positions[index++] = uvs.x0; + positions[index++] = uvs.y0; // color - verticies[index++] = alpha; - verticies[index++] = tint; + colors[index++] = color; // xy - verticies[index++] = a * w0 + c * h1 + tx; - verticies[index++] = d * h1 + b * w0 + ty; + positions[index++] = (a * w0 + c * h1 + tx); + positions[index++] = d * h1 + b * w0 + ty; // uv - verticies[index++] = uvs.x1; - verticies[index++] = uvs.y1; + positions[index++] = uvs.x1; + positions[index++] = uvs.y1; // color - verticies[index++] = alpha; - verticies[index++] = tint; + colors[index++] = color; // xy - verticies[index++] = a * w0 + c * h0 + tx; - verticies[index++] = d * h0 + b * w0 + ty; + positions[index++] = a * w0 + c * h0 + tx; + positions[index++] = d * h0 + b * w0 + ty; // uv - verticies[index++] = uvs.x2; - verticies[index++] = uvs.y2; + positions[index++] = uvs.x2; + positions[index++] = uvs.y2; // color - verticies[index++] = alpha; - verticies[index++] = tint; + colors[index++] = color; // xy - verticies[index++] = a * w1 + c * h0 + tx; - verticies[index++] = d * h0 + b * w1 + ty; + positions[index++] = a * w1 + c * h0 + tx; + positions[index++] = d * h0 + b * w1 + ty; // uv - verticies[index++] = uvs.x3; - verticies[index++] = uvs.y3; + positions[index++] = uvs.x3; + positions[index++] = uvs.y3; // color - verticies[index++] = alpha; - verticies[index++] = tint; + colors[index++] = color; - // increment the batchs - this.currentBatchSize++; + // increment the batchsize + this.sprites[this.currentBatchSize++] = tilingSprite; }; - /** -* Renders the content and empties the current batch +* Renders the content and empties the current batch. * * @method flush -* */ PIXI.WebGLSpriteBatch.prototype.flush = function() { @@ -6730,102 +9224,175 @@ PIXI.WebGLSpriteBatch.prototype.flush = function() if (this.currentBatchSize===0)return; var gl = this.gl; - - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id] || PIXI.createWebGLTexture(this.currentBaseTexture, gl)); + var shader; - // upload the verts to the buffer - + if(this.dirty) + { + this.dirty = false; + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + shader = this.defaultShader.shaders[gl.id]; + + // this is the same for each shader? + var stride = this.vertSize * 4; + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); + + // color attributes will be interpreted as unsigned bytes and normalized + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.UNSIGNED_BYTE, true, stride, 4 * 4); + } + + // upload the verts to the buffer if(this.currentBatchSize > ( this.size * 0.5 ) ) { gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); } else { - var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); - + var view = this.positions.subarray(0, this.currentBatchSize * 4 * this.vertSize); gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); } - // var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); - //gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); - - // now draw those suckas! - gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); - + var nextTexture, nextBlendMode, nextShader; + var batchSize = 0; + var start = 0; + + var currentBaseTexture = null; + var currentBlendMode = this.renderSession.blendModeManager.currentBlendMode; + var currentShader = null; + + var blendSwap = false; + var shaderSwap = false; + var sprite; + + for (var i = 0, j = this.currentBatchSize; i < j; i++) { + + sprite = this.sprites[i]; + + nextTexture = sprite.texture.baseTexture; + nextBlendMode = sprite.blendMode; + nextShader = sprite.shader || this.defaultShader; + + blendSwap = currentBlendMode !== nextBlendMode; + shaderSwap = currentShader !== nextShader; // should I use _UIDS??? + + if(currentBaseTexture !== nextTexture || blendSwap || shaderSwap) + { + this.renderBatch(currentBaseTexture, batchSize, start); + + start = i; + batchSize = 0; + currentBaseTexture = nextTexture; + + if( blendSwap ) + { + currentBlendMode = nextBlendMode; + this.renderSession.blendModeManager.setBlendMode( currentBlendMode ); + } + + if( shaderSwap ) + { + currentShader = nextShader; + + shader = currentShader.shaders[gl.id]; + + if(!shader) + { + shader = new PIXI.PixiShader(gl); + + shader.fragmentSrc =currentShader.fragmentSrc; + shader.uniforms =currentShader.uniforms; + shader.init(); + + currentShader.shaders[gl.id] = shader; + } + + // set shader function??? + this.renderSession.shaderManager.setShader(shader); + + if(shader.dirty)shader.syncUniforms(); + + // both thease only need to be set if they are changing.. + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(shader.projectionVector, projection.x, projection.y); + + // TODO - this is temprorary! + var offsetVector = this.renderSession.offset; + gl.uniform2f(shader.offsetVector, offsetVector.x, offsetVector.y); + + // set the pointers + } + } + + batchSize++; + } + + this.renderBatch(currentBaseTexture, batchSize, start); + // then reset the batch! this.currentBatchSize = 0; +}; +/** +* @method renderBatch +* @param texture {Texture} +* @param size {Number} +* @param startIndex {Number} +*/ +PIXI.WebGLSpriteBatch.prototype.renderBatch = function(texture, size, startIndex) +{ + if(size === 0)return; + + var gl = this.gl; + + // check if a texture is dirty.. + if(texture._dirty[gl.id]) + { + this.renderSession.renderer.updateTexture(texture); + } + else + { + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + } + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); + // increment the draw count this.renderSession.drawCount++; }; /** -* * @method stop -* */ PIXI.WebGLSpriteBatch.prototype.stop = function() { this.flush(); + this.dirty = true; }; /** -* * @method start -* */ PIXI.WebGLSpriteBatch.prototype.start = function() { - var gl = this.gl; - - // bind the main texture - gl.activeTexture(gl.TEXTURE0); - - // bind the buffers - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // set the projection - var projection = this.renderSession.projection; - gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); - - // set the pointers - var stride = this.vertSize * 4; - gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); - gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); - gl.vertexAttribPointer(this.shader.colorAttribute, 2, gl.FLOAT, false, stride, 4 * 4); - - // set the blend mode.. - if(this.currentBlendMode !== PIXI.blendModes.NORMAL) - { - this.setBlendMode(PIXI.blendModes.NORMAL); - } + this.dirty = true; }; /** -* Sets-up the given blendMode from WebGL's point of view -* @method setBlendMode -* -* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD -*/ -PIXI.WebGLSpriteBatch.prototype.setBlendMode = function(blendMode) -{ - this.flush(); - - this.currentBlendMode = blendMode; - - var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); -}; - -/** -* Destroys the SpriteBatch +* Destroys the SpriteBatch. +* * @method destroy */ PIXI.WebGLSpriteBatch.prototype.destroy = function() { - this.vertices = null; this.indices = null; @@ -6836,8 +9403,6 @@ PIXI.WebGLSpriteBatch.prototype.destroy = function() this.gl = null; }; - - /** * @author Mat Groves * @@ -6848,27 +9413,66 @@ PIXI.WebGLSpriteBatch.prototype.destroy = function() * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java */ +/** +* @class WebGLFastSpriteBatch +* @constructor +*/ PIXI.WebGLFastSpriteBatch = function(gl) { - - + /** + * @property vertSize + * @type Number + */ this.vertSize = 10; + + /** + * @property maxSize + * @type Number + */ this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize; + + /** + * @property size + * @type Number + */ this.size = this.maxSize; //the total number of floats in our batch var numVerts = this.size * 4 * this.vertSize; + //the total number of indices in our batch var numIndices = this.maxSize * 6; - //vertex data - this.vertices = new Float32Array(numVerts); - //index data - this.indices = new Uint16Array(numIndices); + /** + * Vertex data + * @property vertices + * @type Float32Array + */ + this.vertices = new PIXI.Float32Array(numVerts); + + /** + * Index data + * @property indices + * @type Uint16Array + */ + this.indices = new PIXI.Uint16Array(numIndices); + /** + * @property vertexBuffer + * @type Object + */ this.vertexBuffer = null; + + /** + * @property indexBuffer + * @type Object + */ this.indexBuffer = null; + /** + * @property lastIndexCount + * @type Number + */ this.lastIndexCount = 0; for (var i=0, j=0; i < numIndices; i += 6, j += 4) @@ -6881,21 +9485,59 @@ PIXI.WebGLFastSpriteBatch = function(gl) this.indices[i + 5] = j + 3; } + /** + * @property drawing + * @type Boolean + */ this.drawing = false; + + /** + * @property currentBatchSize + * @type Number + */ this.currentBatchSize = 0; + + /** + * @property currentBaseTexture + * @type BaseTexture + */ this.currentBaseTexture = null; + /** + * @property currentBlendMode + * @type Number + */ this.currentBlendMode = 0; + + /** + * @property renderSession + * @type Object + */ this.renderSession = null; - + /** + * @property shader + * @type Object + */ this.shader = null; + /** + * @property matrix + * @type Matrix + */ this.matrix = null; this.setContext(gl); }; +PIXI.WebGLFastSpriteBatch.prototype.constructor = PIXI.WebGLFastSpriteBatch; + +/** + * Sets the WebGL Context. + * + * @method setContext + * @param gl {WebGLContext} the current WebGL drawing context + */ PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl) { this.gl = gl; @@ -6906,17 +9548,19 @@ PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl) // 65535 is max index, so 65535 / 6 = 10922. - //upload the index data gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); - - this.currentBlendMode = 99999; }; +/** + * @method begin + * @param spriteBatch {WebGLSpriteBatch} + * @param renderSession {Object} + */ PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession) { this.renderSession = renderSession; @@ -6927,15 +9571,20 @@ PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession) this.start(); }; +/** + * @method end + */ PIXI.WebGLFastSpriteBatch.prototype.end = function() { this.flush(); }; - +/** + * @method render + * @param spriteBatch {WebGLSpriteBatch} + */ PIXI.WebGLFastSpriteBatch.prototype.render = function(spriteBatch) { - var children = spriteBatch.children; var sprite = children[0]; @@ -6945,10 +9594,12 @@ PIXI.WebGLFastSpriteBatch.prototype.render = function(spriteBatch) if(!sprite.texture._uvs)return; this.currentBaseTexture = sprite.texture.baseTexture; + // check blend mode - if(sprite.blendMode !== this.currentBlendMode) + if(sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { - this.setBlendMode(sprite.blendMode); + this.flush(); + this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); } for(var i=0,j= children.length; i ( this.size * 0.5 ) ) { @@ -7124,7 +9780,6 @@ PIXI.WebGLFastSpriteBatch.prototype.flush = function() gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); } - // now draw those suckas! gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); @@ -7136,11 +9791,17 @@ PIXI.WebGLFastSpriteBatch.prototype.flush = function() }; +/** + * @method stop + */ PIXI.WebGLFastSpriteBatch.prototype.stop = function() { this.flush(); }; +/** + * @method start + */ PIXI.WebGLFastSpriteBatch.prototype.start = function() { var gl = this.gl; @@ -7168,26 +9829,9 @@ PIXI.WebGLFastSpriteBatch.prototype.start = function() gl.vertexAttribPointer(this.shader.aRotation, 1, gl.FLOAT, false, stride, 6 * 4); gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4); gl.vertexAttribPointer(this.shader.colorAttribute, 1, gl.FLOAT, false, stride, 9 * 4); - - // set the blend mode.. - if(this.currentBlendMode !== PIXI.blendModes.NORMAL) - { - this.setBlendMode(PIXI.blendModes.NORMAL); - } -}; - -PIXI.WebGLFastSpriteBatch.prototype.setBlendMode = function(blendMode) -{ - this.flush(); - - this.currentBlendMode = blendMode; - var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); }; - - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7195,25 +9839,33 @@ PIXI.WebGLFastSpriteBatch.prototype.setBlendMode = function(blendMode) /** * @class WebGLFilterManager * @constructor -* @param gl {WebGLContext} the current WebGL drawing context -* @param transparent {Boolean} Whether or not the drawing context should be transparent -* @private */ -PIXI.WebGLFilterManager = function(gl, transparent) +PIXI.WebGLFilterManager = function() { - this.transparent = transparent; - + /** + * @property filterStack + * @type Array + */ this.filterStack = []; + /** + * @property offsetX + * @type Number + */ this.offsetX = 0; - this.offsetY = 0; - this.setContext(gl); + /** + * @property offsetY + * @type Number + */ + this.offsetY = 0; }; -// API +PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager; + /** -* Initialises the context and the properties +* Initialises the context and the properties. +* * @method setContext * @param gl {WebGLContext} the current WebGL drawing context */ @@ -7226,7 +9878,6 @@ PIXI.WebGLFilterManager.prototype.setContext = function(gl) }; /** -* * @method begin * @param renderSession {RenderSession} * @param buffer {ArrayBuffer} @@ -7237,14 +9888,14 @@ PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer) this.defaultShader = renderSession.shaderManager.defaultShader; var projection = this.renderSession.projection; - this.width = projection.x * 2; this.height = -projection.y * 2; this.buffer = buffer; }; /** -* Applies the filter and adds it to the current filter stack +* Applies the filter and adds it to the current filter stack. +* * @method pushFilter * @param filterBlock {Object} the filter that will be pushed to the current filter stack */ @@ -7255,6 +9906,7 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) var projection = this.renderSession.projection; var offset = this.renderSession.offset; + filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); // filter program // OPTIMISATION - the first filter is free if its a simple color change? @@ -7262,8 +9914,8 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) var filter = filterBlock.filterPasses[0]; - this.offsetX += filterBlock.target.filterArea.x; - this.offsetY += filterBlock.target.filterArea.y; + this.offsetX += filterBlock._filterArea.x; + this.offsetY += filterBlock._filterArea.y; var texture = this.texturePool.pop(); if(!texture) @@ -7277,15 +9929,13 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) gl.bindTexture(gl.TEXTURE_2D, texture.texture); - filterBlock.target.filterArea = filterBlock.target.getBounds(); + var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; - var filterArea = filterBlock.target.filterArea; - - var padidng = filter.padding; - filterArea.x -= padidng; - filterArea.y -= padidng; - filterArea.width += padidng * 2; - filterArea.height += padidng * 2; + var padding = filter.padding; + filterArea.x -= padding; + filterArea.y -= padding; + filterArea.width += padding * 2; + filterArea.height += padding * 2; // cap filter to screen size.. if(filterArea.x < 0)filterArea.x = 0; @@ -7306,8 +9956,10 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) offset.y = -filterArea.y; // update projection - gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + // now restore the regular shader.. + // this.renderSession.shaderManager.setShader(this.defaultShader); + //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); gl.colorMask(true, true, true, true); gl.clearColor(0,0,0, 0); @@ -7317,16 +9969,16 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) }; - /** -* Removes the last filter from the filter stack and doesn't return it +* Removes the last filter from the filter stack and doesn't return it. +* * @method popFilter */ PIXI.WebGLFilterManager.prototype.popFilter = function() { var gl = this.gl; var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock.target.filterArea; + var filterArea = filterBlock._filterArea; var texture = filterBlock._glFilterTexture; var projection = this.renderSession.projection; var offset = this.renderSession.offset; @@ -7363,6 +10015,7 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() var inputTexture = texture; var outputTexture = this.texturePool.pop(); if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height); + outputTexture.resize(this.width, this.height); // need to clear this FBO as it may have some left over elements from a previous filter. gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); @@ -7401,7 +10054,6 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() this.offsetX -= filterArea.x; this.offsetY -= filterArea.y; - var sizeX = this.width; var sizeY = this.height; @@ -7413,12 +10065,12 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() // time to render the filters texture to the previous scene if(this.filterStack.length === 0) { - gl.colorMask(true, true, true, this.transparent); + gl.colorMask(true, true, true, true);//this.transparent); } else { var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter.target.filterArea; + filterArea = currentFilter._filterArea; sizeX = filterArea.width; sizeY = filterArea.height; @@ -7429,16 +10081,14 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() buffer = currentFilter._glFilterTexture.frameBuffer; } - - - // TODO need toremove thease global elements.. + // TODO need to remove these global elements.. projection.x = sizeX/2; projection.y = -sizeY/2; offset.x = offsetX; offset.y = offsetY; - filterArea = filterBlock.target.filterArea; + filterArea = filterBlock._filterArea; var x = filterArea.x-offsetX; var y = filterArea.y-offsetY; @@ -7471,6 +10121,7 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); gl.viewport(0, 0, sizeX, sizeY); + // bind the buffer gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); @@ -7484,10 +10135,10 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() // apply! this.applyFilterPass(filter, filterArea, sizeX, sizeY); - // now restore the regular shader.. - gl.useProgram(this.defaultShader.program); - gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + // now restore the regular shader.. should happen automatically now.. + // this.renderSession.shaderManager.setShader(this.defaultShader); + // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); // return the texture to the pool this.texturePool.push(texture); @@ -7496,10 +10147,11 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() /** -* Applies the filter to the specified area +* Applies the filter to the specified area. +* * @method applyFilterPass * @param filter {AbstractFilter} the filter that needs to be applied -* @param filterArea {texture} TODO - might need an update +* @param filterArea {Texture} TODO - might need an update * @param width {Number} the horizontal range of the filter * @param height {Number} the vertical range of the filter */ @@ -7521,7 +10173,9 @@ PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, } // set the shader - gl.useProgram(shader.program); + this.renderSession.shaderManager.setShader(shader); + +// gl.useProgram(shader.program); gl.uniform2f(shader.projectionVector, width/2, -height/2); gl.uniform2f(shader.offsetVector, 0,0); @@ -7554,7 +10208,8 @@ PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, }; /** -* Initialises the shader buffers +* Initialises the shader buffers. +* * @method initShaderBuffers */ PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() @@ -7567,54 +10222,42 @@ PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() this.colorBuffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); - // bind and upload the vertexs.. // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, + this.vertexArray = new PIXI.Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData( - gl.ARRAY_BUFFER, - this.vertexArray, - gl.STATIC_DRAW); - + gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, + this.uvArray = new PIXI.Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]); gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData( - gl.ARRAY_BUFFER, - this.uvArray, - gl.STATIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - this.colorArray = new Float32Array([1.0, 0xFFFFFF, + this.colorArray = new PIXI.Float32Array([1.0, 0xFFFFFF, 1.0, 0xFFFFFF, 1.0, 0xFFFFFF, 1.0, 0xFFFFFF]); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData( - gl.ARRAY_BUFFER, - this.colorArray, - gl.STATIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); // bind and upload the index gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData( - gl.ELEMENT_ARRAY_BUFFER, - new Uint16Array([0, 1, 2, 1, 3, 2]), - gl.STATIC_DRAW); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); + }; /** -* Destroys the filter and removes it from the filter stack +* Destroys the filter and removes it from the filter stack. +* * @method destroy */ PIXI.WebGLFilterManager.prototype.destroy = function() @@ -7628,7 +10271,7 @@ PIXI.WebGLFilterManager.prototype.destroy = function() // destroy textures for (var i = 0; i < this.texturePool.length; i++) { - this.texturePool.destroy(); + this.texturePool[i].destroy(); } this.texturePool = null; @@ -7638,7 +10281,8 @@ PIXI.WebGLFilterManager.prototype.destroy = function() gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); -}; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -7649,9 +10293,9 @@ PIXI.WebGLFilterManager.prototype.destroy = function() * @param gl {WebGLContext} the current WebGL drawing context * @param width {Number} the horizontal range of the filter * @param height {Number} the vertical range of the filter -* @private +* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values */ -PIXI.FilterTexture = function(gl, width, height) +PIXI.FilterTexture = function(gl, width, height, scaleMode) { /** * @property gl @@ -7660,25 +10304,48 @@ PIXI.FilterTexture = function(gl, width, height) this.gl = gl; // next time to create a frame buffer and texture + + /** + * @property frameBuffer + * @type Any + */ this.frameBuffer = gl.createFramebuffer(); + + /** + * @property texture + * @type Any + */ this.texture = gl.createTexture(); + /** + * @property scaleMode + * @type Number + */ + scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; + gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer ); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); + // required for masking a mask?? + this.renderBuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer); + this.resize(width, height); }; +PIXI.FilterTexture.prototype.constructor = PIXI.FilterTexture; /** -* Clears the filter texture +* Clears the filter texture. +* * @method clear */ PIXI.FilterTexture.prototype.clear = function() @@ -7706,12 +10373,15 @@ PIXI.FilterTexture.prototype.resize = function(width, height) var gl = this.gl; gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height + gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); }; /** -* Destroys the filter texture +* Destroys the filter texture. +* * @method destroy */ PIXI.FilterTexture.prototype.destroy = function() @@ -7723,37 +10393,124 @@ PIXI.FilterTexture.prototype.destroy = function() this.frameBuffer = null; this.texture = null; }; - + /** - * @author Mat Groves - * - * + * @author Mat Groves http://matgroves.com/ @Doormat23 */ + /** - * A set of functions used to handle masking + * Creates a Canvas element of the given size. * - * @class CanvasMaskManager + * @class CanvasBuffer + * @constructor + * @param width {Number} the width for the newly created canvas + * @param height {Number} the height for the newly created canvas */ -PIXI.CanvasMaskManager = function() +PIXI.CanvasBuffer = function(width, height) { - + /** + * The width of the Canvas in pixels. + * + * @property width + * @type Number + */ + this.width = width; + + /** + * The height of the Canvas in pixels. + * + * @property height + * @type Number + */ + this.height = height; + + /** + * The Canvas object that belongs to this CanvasBuffer. + * + * @property canvas + * @type HTMLCanvasElement + */ + this.canvas = document.createElement("canvas"); + + /** + * A CanvasRenderingContext2D object representing a two-dimensional rendering context. + * + * @property context + * @type CanvasRenderingContext2D + */ + this.context = this.canvas.getContext("2d"); + + this.canvas.width = width; + this.canvas.height = height; +}; + +PIXI.CanvasBuffer.prototype.constructor = PIXI.CanvasBuffer; + +/** + * Clears the canvas that was created by the CanvasBuffer class. + * + * @method clear + * @private + */ +PIXI.CanvasBuffer.prototype.clear = function() +{ + this.context.setTransform(1, 0, 0, 1, 0, 0); + this.context.clearRect(0,0, this.width, this.height); }; /** - * This method adds it to the current stack of masks + * Resizes the canvas to the specified width and height. + * + * @method resize + * @param width {Number} the new width of the canvas + * @param height {Number} the new height of the canvas + */ +PIXI.CanvasBuffer.prototype.resize = function(width, height) +{ + this.width = this.canvas.width = width; + this.height = this.canvas.height = height; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A set of functions used to handle masking. + * + * @class CanvasMaskManager + * @constructor + */ +PIXI.CanvasMaskManager = function() +{ +}; + +PIXI.CanvasMaskManager.prototype.constructor = PIXI.CanvasMaskManager; + +/** + * This method adds it to the current stack of masks. * * @method pushMask - * @param maskData the maskData that will be pushed - * @param context {Context2D} the 2d drawing method of the canvas + * @param maskData {Object} the maskData that will be pushed + * @param renderSession {Object} The renderSession whose context will be used for this mask manager. */ -PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, context) +PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, renderSession) { + var context = renderSession.context; + context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - context.setTransform(transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty); + var resolution = renderSession.resolution; + + context.setTransform(transform.a * resolution, + transform.b * resolution, + transform.c * resolution, + transform.d * resolution, + transform.tx * resolution, + transform.ty * resolution); PIXI.CanvasGraphics.renderGraphicsMask(maskData, context); @@ -7763,46 +10520,41 @@ PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, context) }; /** - * Restores the current drawing context to the state it was before the mask was applied + * Restores the current drawing context to the state it was before the mask was applied. * * @method popMask - * @param context {Context2D} the 2d drawing method of the canvas + * @param renderSession {Object} The renderSession whose context will be used for this mask manager. */ -PIXI.CanvasMaskManager.prototype.popMask = function(context) +PIXI.CanvasMaskManager.prototype.popMask = function(renderSession) { - context.restore(); -}; + renderSession.context.restore(); +}; /** - * @author Mat Groves - * - * + * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** + * Utility methods for Sprite/Texture tinting. + * * @class CanvasTinter - * @constructor * @static */ PIXI.CanvasTinter = function() { - /// this.textureCach }; -//PIXI.CanvasTinter.cachTint = true; - - /** - * Basically this method just needs a sprite and a color and tints the sprite - * with the given color + * Basically this method just needs a sprite and a color and tints the sprite with the given color. * * @method getTintedTexture + * @static * @param sprite {Sprite} the sprite to tint * @param color {Number} the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas */ PIXI.CanvasTinter.getTintedTexture = function(sprite, color) { - var texture = sprite.texture; color = PIXI.CanvasTinter.roundColor(color); @@ -7817,8 +10569,6 @@ PIXI.CanvasTinter.getTintedTexture = function(sprite, color) var canvas = PIXI.CanvasTinter.canvas || document.createElement("canvas"); //PIXI.CanvasTinter.tintWithPerPixel(texture, stringColor, canvas); - - PIXI.CanvasTinter.tintMethod(texture, color, canvas); if(PIXI.CanvasTinter.convertTintToImage) @@ -7831,20 +10581,20 @@ PIXI.CanvasTinter.getTintedTexture = function(sprite, color) } else { - texture.tintCache[stringColor] = canvas; // if we are not converting the texture to an image then we need to lose the reference to the canvas PIXI.CanvasTinter.canvas = null; - } return canvas; }; /** - * Tint a texture using the "multiply" operation + * Tint a texture using the "multiply" operation. + * * @method tintWithMultiply - * @param texture {texture} the texture to tint + * @static + * @param texture {Texture} the texture to tint * @param color {Number} the color to use to tint the sprite with * @param canvas {HTMLCanvasElement} the current canvas */ @@ -7852,44 +10602,46 @@ PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas) { var context = canvas.getContext( "2d" ); - var frame = texture.frame; + var crop = texture.crop; - canvas.width = frame.width; - canvas.height = frame.height; + canvas.width = crop.width; + canvas.height = crop.height; context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); - context.fillRect(0, 0, frame.width, frame.height); + context.fillRect(0, 0, crop.width, crop.height); context.globalCompositeOperation = "multiply"; context.drawImage(texture.baseTexture.source, - frame.x, - frame.y, - frame.width, - frame.height, + crop.x, + crop.y, + crop.width, + crop.height, 0, 0, - frame.width, - frame.height); + crop.width, + crop.height); context.globalCompositeOperation = "destination-atop"; - + context.drawImage(texture.baseTexture.source, - frame.x, - frame.y, - frame.width, - frame.height, + crop.x, + crop.y, + crop.width, + crop.height, 0, 0, - frame.width, - frame.height); + crop.width, + crop.height); }; /** - * Tint a texture using the "overlay" operation + * Tint a texture using the "overlay" operation. + * * @method tintWithOverlay - * @param texture {texture} the texture to tint + * @static + * @param texture {Texture} the texture to tint * @param color {Number} the color to use to tint the sprite with * @param canvas {HTMLCanvasElement} the current canvas */ @@ -7897,37 +10649,35 @@ PIXI.CanvasTinter.tintWithOverlay = function(texture, color, canvas) { var context = canvas.getContext( "2d" ); - var frame = texture.frame; + var crop = texture.crop; - canvas.width = frame.width; - canvas.height = frame.height; - - + canvas.width = crop.width; + canvas.height = crop.height; context.globalCompositeOperation = "copy"; context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); - context.fillRect(0, 0, frame.width, frame.height); + context.fillRect(0, 0, crop.width, crop.height); context.globalCompositeOperation = "destination-atop"; context.drawImage(texture.baseTexture.source, - frame.x, - frame.y, - frame.width, - frame.height, + crop.x, + crop.y, + crop.width, + crop.height, 0, 0, - frame.width, - frame.height); - + crop.width, + crop.height); //context.globalCompositeOperation = "copy"; - }; /** - * Tint a texture pixel per pixel + * Tint a texture pixel per pixel. + * * @method tintPerPixel - * @param texture {texture} the texture to tint + * @static + * @param texture {Texture} the texture to tint * @param color {Number} the color to use to tint the sprite with * @param canvas {HTMLCanvasElement} the current canvas */ @@ -7935,26 +10685,26 @@ PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas) { var context = canvas.getContext( "2d" ); - var frame = texture.frame; + var crop = texture.crop; - canvas.width = frame.width; - canvas.height = frame.height; + canvas.width = crop.width; + canvas.height = crop.height; context.globalCompositeOperation = "copy"; context.drawImage(texture.baseTexture.source, - frame.x, - frame.y, - frame.width, - frame.height, + crop.x, + crop.y, + crop.width, + crop.height, 0, 0, - frame.width, - frame.height); + crop.width, + crop.height); var rgbValues = PIXI.hex2rgb(color); var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2]; - var pixelData = context.getImageData(0, 0, frame.width, frame.height); + var pixelData = context.getImageData(0, 0, crop.width, crop.height); var pixels = pixelData.data; @@ -7969,8 +10719,10 @@ PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas) }; /** - * Rounds the specified color according to the PIXI.CanvasTinter.cacheStepsPerColorChannel + * Rounds the specified color according to the PIXI.CanvasTinter.cacheStepsPerColorChannel. + * * @method roundColor + * @static * @param color {number} the color to round, should be a hex color */ PIXI.CanvasTinter.roundColor = function(color) @@ -7987,54 +10739,95 @@ PIXI.CanvasTinter.roundColor = function(color) }; /** - * - * Number of steps which will be used as a cap when rounding colors + * Number of steps which will be used as a cap when rounding colors. * - * @property cacheStepsPerColorChannel + * @property cacheStepsPerColorChannel * @type Number + * @static */ PIXI.CanvasTinter.cacheStepsPerColorChannel = 8; + /** - * - * Number of steps which will be used as a cap when rounding colors + * Tint cache boolean flag. * * @property convertTintToImage * @type Boolean + * @static */ PIXI.CanvasTinter.convertTintToImage = false; /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. * * @property canUseMultiply * @type Boolean + * @static */ PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes(); +/** + * The tinting method that will be used. + * + * @method tintMethod + * @static + */ PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel; - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. - * Dont forget to add the view to your DOM or you will not see anything :) + * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class CanvasRenderer * @constructor - * @param width=800 {Number} the width of the canvas view - * @param height=600 {Number} the height of the canvas view - * @param [view] {HTMLCanvasElement} the canvas to use as a view, optional - * @param [transparent=false] {Boolean} the transparency of the render view, default false + * @param [width=800] {Number} the width of the canvas view + * @param [height=600] {Number} the height of the canvas view + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 + * @param [options.clearBeforeRender=true] {Boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ -PIXI.CanvasRenderer = function(width, height, view, transparent) +PIXI.CanvasRenderer = function(width, height, options) { - PIXI.defaultRenderer = PIXI.defaultRenderer || this; + if(options) + { + for (var i in PIXI.defaultRenderOptions) + { + if (typeof options[i] === "undefined") options[i] = PIXI.defaultRenderOptions[i]; + } + } + else + { + options = PIXI.defaultRenderOptions; + } + if(!PIXI.defaultRenderer) + { + PIXI.sayHello("Canvas"); + PIXI.defaultRenderer = this; + } + + /** + * The renderer type. + * + * @property type + * @type Number + */ this.type = PIXI.CANVAS_RENDERER; + /** + * The resolution of the canvas. + * + * @property resolution + * @type Number + */ + this.resolution = options.resolution; + /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. @@ -8045,17 +10838,7 @@ PIXI.CanvasRenderer = function(width, height, view, transparent) * @type Boolean * @default */ - this.clearBeforeRender = true; - - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - * @property roundPixels - * @type Boolean - * @default - */ - this.roundPixels = false; + this.clearBeforeRender = options.clearBeforeRender; /** * Whether the render view is transparent @@ -8063,12 +10846,235 @@ PIXI.CanvasRenderer = function(width, height, view, transparent) * @property transparent * @type Boolean */ - this.transparent = !!transparent; + this.transparent = options.transparent; + /** + * Whether the render view should be resized automatically + * + * @property autoResize + * @type Boolean + */ + this.autoResize = options.autoResize || false; + + + /** + * The width of the canvas view + * + * @property width + * @type Number + * @default 800 + */ + this.width = width || 800; + + /** + * The height of the canvas view + * + * @property height + * @type Number + * @default 600 + */ + this.height = height || 600; + + this.width *= this.resolution; + this.height *= this.resolution; + + /** + * The canvas element that everything is drawn to. + * + * @property view + * @type HTMLCanvasElement + */ + this.view = options.view || document.createElement( "canvas" ); + + /** + * The canvas 2d context that everything is drawn with + * @property context + * @type CanvasRenderingContext2D + */ + this.context = this.view.getContext( "2d", { alpha: this.transparent } ); + + /** + * Boolean flag controlling canvas refresh. + * + * @property refresh + * @type Boolean + */ + this.refresh = true; + + this.view.width = this.width * this.resolution; + this.view.height = this.height * this.resolution; + + /** + * Internal var. + * + * @property count + * @type Number + */ + this.count = 0; + + /** + * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer + * @property CanvasMaskManager + * @type CanvasMaskManager + */ + this.maskManager = new PIXI.CanvasMaskManager(); + + /** + * The render session is just a bunch of parameter used for rendering + * @property renderSession + * @type Object + */ + this.renderSession = { + context: this.context, + maskManager: this.maskManager, + scaleMode: null, + smoothProperty: null, + /** + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + */ + roundPixels: false + }; + + this.mapBlendModes(); + + this.resize(width, height); + + if("imageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "imageSmoothingEnabled"; + else if("webkitImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "webkitImageSmoothingEnabled"; + else if("mozImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "mozImageSmoothingEnabled"; + else if("oImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "oImageSmoothingEnabled"; + else if ("msImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "msImageSmoothingEnabled"; +}; + +// constructor +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; + +/** + * Renders the Stage to this canvas view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.CanvasRenderer.prototype.render = function(stage) +{ + stage.updateTransform(); + + this.context.setTransform(1,0,0,1,0,0); + + this.context.globalAlpha = 1; + + this.renderSession.currentBlendMode = PIXI.blendModes.NORMAL; + this.context.globalCompositeOperation = PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]; + + if (navigator.isCocoonJS && this.view.screencanvas) { + this.context.fillStyle = "black"; + this.context.clear(); + } + + if (this.clearBeforeRender) + { + if (this.transparent) + { + this.context.clearRect(0, 0, this.width, this.height); + } + else + { + this.context.fillStyle = stage.backgroundColorString; + this.context.fillRect(0, 0, this.width , this.height); + } + } + + this.renderDisplayObject(stage); + + // run interaction! + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } +}; + +/** + * Removes everything from the renderer and optionally removes the Canvas DOM element. + * + * @method destroy + * @param [removeView=true] {boolean} Removes the Canvas element from the DOM. + */ +PIXI.CanvasRenderer.prototype.destroy = function(removeView) +{ + if (typeof removeView === "undefined") { removeView = true; } + + if (removeView && this.view.parent) + { + this.view.parent.removeChild(this.view); + } + + this.view = null; + this.context = null; + this.maskManager = null; + this.renderSession = null; + +}; + +/** + * Resizes the canvas view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the canvas view + * @param height {Number} the new height of the canvas view + */ +PIXI.CanvasRenderer.prototype.resize = function(width, height) +{ + this.width = width * this.resolution; + this.height = height * this.resolution; + + this.view.width = this.width; + this.view.height = this.height; + + if (this.autoResize) { + this.view.style.width = this.width / this.resolution + "px"; + this.view.style.height = this.height / this.resolution + "px"; + } +}; + +/** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render + * @param context {CanvasRenderingContext2D} the context 2d method of the canvas + * @private + */ +PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) +{ + this.renderSession.context = context || this.context; + this.renderSession.resolution = this.resolution; + displayObject._renderCanvas(this.renderSession); +}; + +/** + * Maps Pixi blend modes to canvas blend modes. + * + * @method mapBlendModes + * @private + */ +PIXI.CanvasRenderer.prototype.mapBlendModes = function() +{ if(!PIXI.blendModesCanvas) { PIXI.blendModesCanvas = []; - + if(PIXI.canUseNewCanvasBlendModes()) { PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; @@ -8111,335 +11117,49 @@ PIXI.CanvasRenderer = function(width, height, view, transparent) PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over"; } } - - /** - * The width of the canvas view - * - * @property width - * @type Number - * @default 800 - */ - this.width = width || 800; - - /** - * The height of the canvas view - * - * @property height - * @type Number - * @default 600 - */ - this.height = height || 600; - - /** - * The canvas element that everything is drawn to - * - * @property view - * @type HTMLCanvasElement - */ - this.view = view || document.createElement( "canvas" ); - - /** - * The canvas 2d context that everything is drawn with - * @property context - * @type HTMLCanvasElement 2d Context - */ - this.context = this.view.getContext( "2d", { alpha: this.transparent } ); - - this.refresh = true; - // hack to enable some hardware acceleration! - //this.view.style["transform"] = "translatez(0)"; - - this.view.width = this.width; - this.view.height = this.height; - this.count = 0; - - /** - * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer - * @property CanvasMaskManager - * @type CanvasMaskManager - */ - this.maskManager = new PIXI.CanvasMaskManager(); - - /** - * The render session is just a bunch of parameter used for rendering - * @property renderSession - * @type Object - */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null - }; - - if("imageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "imageSmoothingEnabled"; - else if("webkitImageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "webkitImageSmoothingEnabled"; - else if("mozImageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "mozImageSmoothingEnabled"; - else if("oImageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "oImageSmoothingEnabled"; }; -// constructor -PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; - -/** - * Renders the stage to its canvas view - * - * @method render - * @param stage {Stage} the Stage element to be rendered - */ -PIXI.CanvasRenderer.prototype.render = function(stage) -{ - // update textures if need be - PIXI.texturesToUpdate.length = 0; - PIXI.texturesToDestroy.length = 0; - - stage.updateTransform(); - - this.context.setTransform(1,0,0,1,0,0); - this.context.globalAlpha = 1; - - if (!this.transparent && this.clearBeforeRender) - { - this.context.fillStyle = stage.backgroundColorString; - this.context.fillRect(0, 0, this.width, this.height); - } - else if (this.transparent && this.clearBeforeRender) - { - this.context.clearRect(0, 0, this.width, this.height); - } - - this.renderDisplayObject(stage); - - // run interaction! - if(stage.interactive) - { - //need to add some events! - if(!stage._interactiveEventsAdded) - { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - - // remove frame updates.. - if(PIXI.Texture.frameUpdates.length > 0) - { - PIXI.Texture.frameUpdates.length = 0; - } -}; - -/** - * Resizes the canvas view to the specified width and height - * - * @method resize - * @param width {Number} the new width of the canvas view - * @param height {Number} the new height of the canvas view - */ -PIXI.CanvasRenderer.prototype.resize = function(width, height) -{ - this.width = width; - this.height = height; - - this.view.width = width; - this.view.height = height; -}; - -/** - * Renders a display object - * - * @method renderDisplayObject - * @param displayObject {DisplayObject} The displayObject to render - * @param context {Context2D} the context 2d method of the canvas - * @private - */ -PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) -{ - // no longer recursive! - //var transform; - //var context = this.context; - - this.renderSession.context = context || this.context; - displayObject._renderCanvas(this.renderSession); -}; - -/** - * Renders a flat strip - * - * @method renderStripFlat - * @param strip {Strip} The Strip to render - * @private - */ -PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) -{ - var context = this.context; - var verticies = strip.verticies; - - var length = verticies.length/2; - this.count++; - - context.beginPath(); - for (var i=1; i < length-2; i++) - { - // draw some triangles! - var index = i*2; - - var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; - var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = "#FF0000"; - context.fill(); - context.closePath(); -}; - -/** - * Renders a strip - * - * @method renderStrip - * @param strip {Strip} The Strip to render - * @private - */ -PIXI.CanvasRenderer.prototype.renderStrip = function(strip) -{ - var context = this.context; - - // draw triangles!! - var verticies = strip.verticies; - var uvs = strip.uvs; - - var length = verticies.length/2; - this.count++; - - for (var i = 1; i < length-2; i++) - { - // draw some triangles! - var index = i*2; - - var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; - var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; - - var u0 = uvs[index] * strip.texture.width, u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width; - var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height; - - context.save(); - context.beginPath(); - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - context.closePath(); - - context.clip(); - - // Compute matrix transform - var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2; - var deltaA = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2; - var deltaB = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2; - var deltaC = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2; - var deltaD = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2; - var deltaE = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2; - var deltaF = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2; - - context.transform(deltaA / delta, deltaD / delta, - deltaB / delta, deltaE / delta, - deltaC / delta, deltaF / delta); - - context.drawImage(strip.texture.baseTexture.source, 0, 0); - context.restore(); - } -}; - -/** - * Creates a Canvas element of the given size - * - * @method CanvasBuffer - * @param width {Number} the width for the newly created canvas - * @param height {Number} the height for the newly created canvas - * @static - * @private - */ -PIXI.CanvasBuffer = function(width, height) -{ - this.width = width; - this.height = height; - - this.canvas = document.createElement( "canvas" ); - this.context = this.canvas.getContext( "2d" ); - - this.canvas.width = width; - this.canvas.height = height; -}; - -/** - * Clears the canvas that was created by the CanvasBuffer class - * - * @method clear - * @private - */ -PIXI.CanvasBuffer.prototype.clear = function() -{ - this.context.clearRect(0,0, this.width, this.height); -}; - -/** - * Resizes the canvas that was created by the CanvasBuffer class to the specified width and height - * - * @method resize - * @param width {Number} the new width of the canvas - * @param height {Number} the new height of the canvas - * @private - */ - -PIXI.CanvasBuffer.prototype.resize = function(width, height) -{ - this.width = this.canvas.width = width; - this.height = this.canvas.height = height; -}; - - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - * A set of functions used by the canvas renderer to draw the primitive graphics data + * A set of functions used by the canvas renderer to draw the primitive graphics data. * * @class CanvasGraphics + * @static */ PIXI.CanvasGraphics = function() { - }; - /* - * Renders the graphics object + * Renders a PIXI.Graphics object to a canvas. * - * @static - * @private * @method renderGraphics + * @static * @param graphics {Graphics} the actual graphics object to render - * @param context {Context2D} the 2d drawing method of the canvas + * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas */ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - var color = ''; + + if(graphics.dirty) + { + this.updateGraphicsTint(graphics); + graphics.dirty = false; + } + for (var i = 0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; - var points = data.points; + var shape = data.shape; - context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); + var fillColor = data._fillTint; + var lineColor = data._lineTint; context.lineWidth = data.lineWidth; @@ -8447,6 +11167,8 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { context.beginPath(); + var points = shape.points; + context.moveTo(points[0], points[1]); for (var j=1; j < points.length/2; j++) @@ -8454,6 +11176,11 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.lineTo(points[j * 2], points[j * 2 + 1]); } + if(shape.closed) + { + context.lineTo(points[0], points[1]); + } + // if the first and last point are the same close the path - much neater :) if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) { @@ -8463,12 +11190,13 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fill(); } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); context.stroke(); } } @@ -8478,48 +11206,46 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); - context.fillRect(points[0], points[1], points[2], points[3]); + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fillRect(shape.x, shape.y, shape.width, shape.height); } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeRect(points[0], points[1], points[2], points[3]); + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.strokeRect(shape.x, shape.y, shape.width, shape.height); } - } else if(data.type === PIXI.Graphics.CIRC) { // TODO - need to be Undefined! context.beginPath(); - context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); context.closePath(); if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fill(); } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); context.stroke(); } } else if(data.type === PIXI.Graphics.ELIP) { - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - var ellipseData = data.points; + var w = shape.width * 2; + var h = shape.height * 2; - var w = ellipseData[2] * 2; - var h = ellipseData[3] * 2; - - var x = ellipseData[0] - w/2; - var y = ellipseData[1] - h/2; + var x = shape.x - w/2; + var y = shape.y - h/2; context.beginPath(); @@ -8542,12 +11268,50 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fill(); } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === PIXI.Graphics.RREC) + { + var rx = shape.x; + var ry = shape.y; + var width = shape.width; + var height = shape.height; + var radius = shape.radius; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + + if(data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); context.stroke(); } } @@ -8561,7 +11325,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) * @private * @method renderGraphicsMask * @param graphics {Graphics} the graphics which will be used as a mask - * @param context {Context2D} the context 2d method of the canvas + * @param context {CanvasRenderingContext2D} the context 2d method of the canvas */ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { @@ -8578,11 +11342,14 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) for (var i = 0; i < 1; i++) { var data = graphics.graphicsData[i]; - var points = data.points; + var shape = data.shape; if(data.type === PIXI.Graphics.POLY) { context.beginPath(); + + var points = shape.points; + context.moveTo(points[0], points[1]); for (var j=1; j < points.length/2; j++) @@ -8600,27 +11367,26 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) else if(data.type === PIXI.Graphics.RECT) { context.beginPath(); - context.rect(points[0], points[1], points[2], points[3]); + context.rect(shape.x, shape.y, shape.width, shape.height); context.closePath(); } else if(data.type === PIXI.Graphics.CIRC) { // TODO - need to be Undefined! context.beginPath(); - context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); context.closePath(); } else if(data.type === PIXI.Graphics.ELIP) { // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - var ellipseData = data.points; - var w = ellipseData[2] * 2; - var h = ellipseData[3] * 2; + var w = shape.width * 2; + var h = shape.height * 2; - var x = ellipseData[0] - w/2; - var y = ellipseData[1] - h/2; + var x = shape.x - w/2; + var y = shape.y - h/2; context.beginPath(); @@ -8639,19 +11405,86 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } + else if (data.type === PIXI.Graphics.RREC) + { + + var pts = shape.points; + var rx = pts[0]; + var ry = pts[1]; + var width = pts[2]; + var height = pts[3]; + var radius = pts[4]; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + } } }; - + +PIXI.CanvasGraphics.updateGraphicsTint = function(graphics) +{ + if(graphics.tint === 0xFFFFFF)return; + + var tintR = (graphics.tint >> 16 & 0xFF) / 255; + var tintG = (graphics.tint >> 8 & 0xFF) / 255; + var tintB = (graphics.tint & 0xFF)/ 255; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + var fillColor = data.fillColor | 0; + var lineColor = data.lineColor | 0; + + /* + var colorR = (fillColor >> 16 & 0xFF) / 255; + var colorG = (fillColor >> 8 & 0xFF) / 255; + var colorB = (fillColor & 0xFF) / 255; + + colorR *= tintR; + colorG *= tintG; + colorB *= tintB; + + fillColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); + + colorR = (lineColor >> 16 & 0xFF) / 255; + colorG = (lineColor >> 8 & 0xFF) / 255; + colorB = (lineColor & 0xFF) / 255; + + colorR *= tintR; + colorG *= tintG; + colorB *= tintB; + + lineColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); + */ + + // super inline cos im an optimization NAZI :) + data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); + data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); + + } +}; + + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. - * It is important to know that with the webGL renderer only simple polygons can be filled at this stage - * Complex polygons will not be filled. Heres an example of a complex polygon: http://www.goodboydigital.com/wp-content/uploads/2013/06/complexPolygon.png - * + * The Graphics class contains methods used to draw primitive shapes such as lines, circles and rectangles to the display, and color and fill them. + * * @class Graphics * @extends DisplayObjectContainer * @constructor @@ -8663,7 +11496,7 @@ PIXI.Graphics = function() this.renderable = true; /** - * The alpha of the fill of this graphics object + * The alpha value used when filling the Graphics object. * * @property fillAlpha * @type Number @@ -8671,7 +11504,7 @@ PIXI.Graphics = function() this.fillAlpha = 1; /** - * The width of any lines drawn + * The width (thickness) of any lines drawn. * * @property lineWidth * @type Number @@ -8679,12 +11512,13 @@ PIXI.Graphics = function() this.lineWidth = 0; /** - * The color of any lines drawn + * The color of any lines drawn. * * @property lineColor * @type String + * @default 0 */ - this.lineColor = "black"; + this.lineColor = 0; /** * Graphics data @@ -8695,18 +11529,17 @@ PIXI.Graphics = function() */ this.graphicsData = []; - /** - * The tint applied to the graphic shape. This is a hex value + * The tint applied to the graphic shape. This is a hex value. Apply a value of 0xFFFFFF to reset the tint. * * @property tint * @type Number * @default 0xFFFFFF */ - this.tint = 0xFFFFFF;// * Math.random(); - + this.tint = 0xFFFFFF; + /** - * The blend mode to be applied to the graphic shape + * The blend mode to be applied to the graphic shape. Apply a value of PIXI.blendModes.NORMAL to reset the blend mode. * * @property blendMode * @type Number @@ -8721,10 +11554,10 @@ PIXI.Graphics = function() * @type Object * @private */ - this.currentPath = {points:[]}; - + this.currentPath = null; + /** - * Array containing some WebGL-related properties used by the WebGL renderer + * Array containing some WebGL-related properties used by the WebGL renderer. * * @property _webGL * @type Array @@ -8733,28 +11566,50 @@ PIXI.Graphics = function() this._webGL = []; /** - * Whether this shape is being used as a mask + * Whether this shape is being used as a mask. * * @property isMask - * @type isMask + * @type Boolean */ this.isMask = false; /** - * The bounds of the graphic shape as rectangle object + * The bounds' padding used for bounds calculation. * - * @property bounds - * @type Rectangle - */ - this.bounds = null; - - /** - * the bounds' padding used for bounds calculation - * - * @property bounds + * @property boundsPadding * @type Number */ - this.boundsPadding = 10; + this.boundsPadding = 0; + + this._localBounds = new PIXI.Rectangle(0,0,1,1); + + /** + * Used to detect if the graphics object has changed. If this is set to true then the graphics object will be recalculated. + * + * @property dirty + * @type Boolean + * @private + */ + this.dirty = true; + + /** + * Used to detect if the webgl graphics object has changed. If this is set to true then the graphics object will be recalculated. + * + * @property webGLDirty + * @type Boolean + * @private + */ + this.webGLDirty = false; + + /** + * Used to detect if the cached sprite object needs to be updated. + * + * @property cachedSpriteDirty + * @type Boolean + * @private + */ + this.cachedSpriteDirty = false; + }; // constructor @@ -8762,14 +11617,14 @@ PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ) PIXI.Graphics.prototype.constructor = PIXI.Graphics; /** - * If cacheAsBitmap is true the graphics object will then be rendered as if it was a sprite. - * This is useful if your graphics element does not change often as it will speed up the rendering of the object - * It is also usful as the graphics object will always be antialiased because it will be rendered using canvas - * Not recommended if you are constanly redrawing the graphics element. + * When cacheAsBitmap is set to true the graphics object will be rendered as if it was a sprite. + * This is useful if your graphics element does not change often, as it will speed up the rendering of the object in exchange for taking up texture memory. + * It is also useful if you need the graphics object to be anti-aliased, because it will be rendered using canvas. + * This is not recommended if you are constantly redrawing the graphics element. * * @property cacheAsBitmap - * @default false * @type Boolean + * @default false * @private */ Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { @@ -8781,6 +11636,7 @@ Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { if(this._cacheAsBitmap) { + this._generateCachedSprite(); } else @@ -8792,63 +11648,326 @@ Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { } }); - /** * Specifies the line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method. * * @method lineStyle - * @param lineWidth {Number} width of the line to draw, will update the object's stored style - * @param color {Number} color of the line to draw, will update the object's stored style - * @param alpha {Number} alpha of the line to draw, will update the object's stored style + * @param lineWidth {Number} width of the line to draw, will update the objects stored style + * @param color {Number} color of the line to draw, will update the objects stored style + * @param alpha {Number} alpha of the line to draw, will update the objects stored style + * @return {Graphics} */ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { - if (!this.currentPath.points.length) this.graphicsData.pop(); - this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (arguments.length < 3) ? 1 : alpha; - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; + if(this.currentPath) + { + if(this.currentPath.shape.points.length) + { + // halfway through a line? start a new one! + this.drawShape( new PIXI.Polygon( this.currentPath.shape.points.slice(-2) )); + return this; + } - this.graphicsData.push(this.currentPath); + // otherwise its empty so lets just set the line properties + this.currentPath.lineWidth = this.lineWidth; + this.currentPath.lineColor = this.lineColor; + this.currentPath.lineAlpha = this.lineAlpha; + + } return this; }; /** - * Moves the current drawing position to (x, y). + * Moves the current drawing position to x, y. * * @method moveTo * @param x {Number} the X coordinate to move to * @param y {Number} the Y coordinate to move to - */ + * @return {Graphics} + */ PIXI.Graphics.prototype.moveTo = function(x, y) { - if (!this.currentPath.points.length) this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - - this.currentPath.points.push(x, y); - - this.graphicsData.push(this.currentPath); + this.drawShape(new PIXI.Polygon([x,y])); return this; }; /** * Draws a line using the current line style from the current drawing position to (x, y); - * the current drawing position is then set to (x, y). + * The current drawing position is then set to (x, y). * * @method lineTo * @param x {Number} the X coordinate to draw to * @param y {Number} the Y coordinate to draw to + * @return {Graphics} */ PIXI.Graphics.prototype.lineTo = function(x, y) { - this.currentPath.points.push(x, y); + this.currentPath.shape.points.push(x, y); + this.dirty = true; + + return this; +}; + +/** + * Calculate the points for a quadratic bezier curve and then draws it. + * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c + * + * @method quadraticCurveTo + * @param cpX {Number} Control point x + * @param cpY {Number} Control point y + * @param toX {Number} Destination point x + * @param toY {Number} Destination point y + * @return {Graphics} + */ +PIXI.Graphics.prototype.quadraticCurveTo = function(cpX, cpY, toX, toY) +{ + if( this.currentPath ) + { + if(this.currentPath.shape.points.length === 0)this.currentPath.shape.points = [0,0]; + } + else + { + this.moveTo(0,0); + } + + var xa, + ya, + n = 20, + points = this.currentPath.shape.points; + if(points.length === 0)this.moveTo(0, 0); + + + var fromX = points[points.length-2]; + var fromY = points[points.length-1]; + + var j = 0; + for (var i = 1; i <= n; i++ ) + { + j = i / n; + + xa = fromX + ( (cpX - fromX) * j ); + ya = fromY + ( (cpY - fromY) * j ); + + points.push( xa + ( ((cpX + ( (toX - cpX) * j )) - xa) * j ), + ya + ( ((cpY + ( (toY - cpY) * j )) - ya) * j ) ); + } + + + this.dirty = true; + + return this; +}; + +/** + * Calculate the points for a bezier curve and then draws it. + * + * @method bezierCurveTo + * @param cpX {Number} Control point x + * @param cpY {Number} Control point y + * @param cpX2 {Number} Second Control point x + * @param cpY2 {Number} Second Control point y + * @param toX {Number} Destination point x + * @param toY {Number} Destination point y + * @return {Graphics} + */ +PIXI.Graphics.prototype.bezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) +{ + if( this.currentPath ) + { + if(this.currentPath.shape.points.length === 0)this.currentPath.shape.points = [0,0]; + } + else + { + this.moveTo(0,0); + } + + var n = 20, + dt, + dt2, + dt3, + t2, + t3, + points = this.currentPath.shape.points; + + var fromX = points[points.length-2]; + var fromY = points[points.length-1]; + + var j = 0; + + for (var i=1; i<=n; i++) + { + j = i / n; + + dt = (1 - j); + dt2 = dt * dt; + dt3 = dt2 * dt; + + t2 = j * j; + t3 = t2 * j; + + points.push( dt3 * fromX + 3 * dt2 * j * cpX + 3 * dt * t2 * cpX2 + t3 * toX, + dt3 * fromY + 3 * dt2 * j * cpY + 3 * dt * t2 * cpY2 + t3 * toY); + } + + this.dirty = true; + + return this; +}; + +/* + * The arcTo() method creates an arc/curve between two tangents on the canvas. + * + * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google! + * + * @method arcTo + * @param x1 {Number} The x-coordinate of the beginning of the arc + * @param y1 {Number} The y-coordinate of the beginning of the arc + * @param x2 {Number} The x-coordinate of the end of the arc + * @param y2 {Number} The y-coordinate of the end of the arc + * @param radius {Number} The radius of the arc + * @return {Graphics} + */ +PIXI.Graphics.prototype.arcTo = function(x1, y1, x2, y2, radius) +{ + if( this.currentPath ) + { + if(this.currentPath.shape.points.length === 0) + { + this.currentPath.shape.points.push(x1, y1); + } + } + else + { + this.moveTo(x1, y1); + } + + var points = this.currentPath.shape.points; + var fromX = points[points.length-2]; + var fromY = points[points.length-1]; + var a1 = fromY - y1; + var b1 = fromX - x1; + var a2 = y2 - y1; + var b2 = x2 - x1; + var mm = Math.abs(a1 * b2 - b1 * a2); + + + if (mm < 1.0e-8 || radius === 0) + { + if( points[points.length-2] !== x1 || points[points.length-1] !== y1) + { + //console.log(">>") + points.push(x1, y1); + } + } + else + { + var dd = a1 * a1 + b1 * b1; + var cc = a2 * a2 + b2 * b2; + var tt = a1 * a2 + b1 * b2; + var k1 = radius * Math.sqrt(dd) / mm; + var k2 = radius * Math.sqrt(cc) / mm; + var j1 = k1 * tt / dd; + var j2 = k2 * tt / cc; + var cx = k1 * b2 + k2 * b1; + var cy = k1 * a2 + k2 * a1; + var px = b1 * (k2 + j1); + var py = a1 * (k2 + j1); + var qx = b2 * (k1 + j2); + var qy = a2 * (k1 + j2); + var startAngle = Math.atan2(py - cy, px - cx); + var endAngle = Math.atan2(qy - cy, qx - cx); + + this.arc(cx + x1, cy + y1, radius, startAngle, endAngle, b1 * a2 > b2 * a1); + } + + this.dirty = true; + + return this; +}; + +/** + * The arc method creates an arc/curve (used to create circles, or parts of circles). + * + * @method arc + * @param cx {Number} The x-coordinate of the center of the circle + * @param cy {Number} The y-coordinate of the center of the circle + * @param radius {Number} The radius of the circle + * @param startAngle {Number} The starting angle, in radians (0 is at the 3 o'clock position of the arc's circle) + * @param endAngle {Number} The ending angle, in radians + * @param anticlockwise {Boolean} Optional. Specifies whether the drawing should be counterclockwise or clockwise. False is default, and indicates clockwise, while true indicates counter-clockwise. + * @return {Graphics} + */ +PIXI.Graphics.prototype.arc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) +{ + var startX = cx + Math.cos(startAngle) * radius; + var startY = cy + Math.sin(startAngle) * radius; + var points; + + if( this.currentPath ) + { + points = this.currentPath.shape.points; + + if(points.length === 0) + { + points.push(startX, startY); + } + else if( points[points.length-2] !== startX || points[points.length-1] !== startY) + { + points.push(startX, startY); + } + } + else + { + this.moveTo(startX, startY); + points = this.currentPath.shape.points; + } + + if (startAngle === endAngle)return this; + + if( !anticlockwise && endAngle <= startAngle ) + { + endAngle += Math.PI * 2; + } + else if( anticlockwise && startAngle <= endAngle ) + { + startAngle += Math.PI * 2; + } + + var sweep = anticlockwise ? (startAngle - endAngle) *-1 : (endAngle - startAngle); + var segs = ( Math.abs(sweep)/ (Math.PI * 2) ) * 40; + + if( sweep === 0 ) return this; + + var theta = sweep/(segs*2); + var theta2 = theta*2; + + var cTheta = Math.cos(theta); + var sTheta = Math.sin(theta); + + var segMinus = segs - 1; + + var remainder = ( segMinus % 1 ) / segMinus; + + for(var i=0; i<=segMinus; i++) + { + var real = i + remainder * i; + + + var angle = ((theta) + startAngle + (theta2 * real)); + + var c = Math.cos(angle); + var s = -Math.sin(angle); + + points.push(( (cTheta * c) + (sTheta * s) ) * radius + cx, + ( (cTheta * -s) + (sTheta * c) ) * radius + cy); + } + this.dirty = true; return this; @@ -8861,14 +11980,23 @@ PIXI.Graphics.prototype.lineTo = function(x, y) * @method beginFill * @param color {Number} the color of the fill * @param alpha {Number} the alpha of the fill + * @return {Graphics} */ PIXI.Graphics.prototype.beginFill = function(color, alpha) { - this.filling = true; this.fillColor = color || 0; - this.fillAlpha = (arguments.length < 2) ? 1 : alpha; + this.fillAlpha = (alpha === undefined) ? 1 : alpha; + if(this.currentPath) + { + if(this.currentPath.shape.points.length <= 2) + { + this.currentPath.fill = this.filling; + this.currentPath.fillColor = this.fillColor; + this.currentPath.fillAlpha = this.fillAlpha; + } + } return this; }; @@ -8876,6 +12004,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method. * * @method endFill + * @return {Graphics} */ PIXI.Graphics.prototype.endFill = function() { @@ -8893,17 +12022,27 @@ PIXI.Graphics.prototype.endFill = function() * @param y {Number} The Y coord of the top-left of the rectangle * @param width {Number} The width of the rectangle * @param height {Number} The height of the rectangle + * @return {Graphics} */ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { - if (!this.currentPath.points.length) this.graphicsData.pop(); + this.drawShape(new PIXI.Rectangle(x,y, width, height)); - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, - points:[x, y, width, height], type:PIXI.Graphics.RECT}; + return this; +}; - this.graphicsData.push(this.currentPath); - this.dirty = true; +/** + * @method drawRoundedRect + * + * @param x {Number} The X coord of the top-left of the rectangle + * @param y {Number} The Y coord of the top-left of the rectangle + * @param width {Number} The width of the rectangle + * @param height {Number} The height of the rectangle + * @param radius {Number} Radius of the rectangle corners + */ +PIXI.Graphics.prototype.drawRoundedRect = function( x, y, width, height, radius ) +{ + this.drawShape(new PIXI.RoundedRectangle(x, y, width, height, radius)); return this; }; @@ -8915,18 +12054,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) * @param x {Number} The X coordinate of the center of the circle * @param y {Number} The Y coordinate of the center of the circle * @param radius {Number} The radius of the circle + * @return {Graphics} */ -PIXI.Graphics.prototype.drawCircle = function( x, y, radius) +PIXI.Graphics.prototype.drawCircle = function(x, y, radius) { - - if (!this.currentPath.points.length) this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, - points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - - this.graphicsData.push(this.currentPath); - this.dirty = true; + this.drawShape(new PIXI.Circle(x,y, radius)); return this; }; @@ -8935,23 +12067,30 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) * Draws an ellipse. * * @method drawEllipse - * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this ellipse - * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this ellipse - * @param width {Number} The width of the ellipse - * @param height {Number} The height of the ellipse + * @param x {Number} The X coordinate of the center of the ellipse + * @param y {Number} The Y coordinate of the center of the ellipse + * @param width {Number} The half width of the ellipse + * @param height {Number} The half height of the ellipse + * @return {Graphics} */ -PIXI.Graphics.prototype.drawEllipse = function( x, y, width, height) +PIXI.Graphics.prototype.drawEllipse = function(x, y, width, height) { + this.drawShape(new PIXI.Ellipse(x, y, width, height)); - if (!this.currentPath.points.length) this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, - points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - - this.graphicsData.push(this.currentPath); - this.dirty = true; + return this; +}; +/** + * Draws a polygon using the given path. + * + * @method drawPolygon + * @param path {Array} The path data used to construct the polygon. + * @return {Graphics} + */ +PIXI.Graphics.prototype.drawPolygon = function(path) +{ + if(!(path instanceof Array))path = Array.prototype.slice.call(arguments); + this.drawShape(new PIXI.Polygon(path)); return this; }; @@ -8959,6 +12098,7 @@ PIXI.Graphics.prototype.drawEllipse = function( x, y, width, height) * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings. * * @method clear + * @return {Graphics} */ PIXI.Graphics.prototype.clear = function() { @@ -8969,8 +12109,6 @@ PIXI.Graphics.prototype.clear = function() this.clearDirty = true; this.graphicsData = []; - this.bounds = null; //new PIXI.Rectangle(); - return this; }; @@ -8979,14 +12117,22 @@ PIXI.Graphics.prototype.clear = function() * This can be quite useful if your geometry is complicated and needs to be reused multiple times. * * @method generateTexture + * @param resolution {Number} The resolution of the texture being generated + * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts * @return {Texture} a texture of the graphics object */ -PIXI.Graphics.prototype.generateTexture = function() +PIXI.Graphics.prototype.generateTexture = function(resolution, scaleMode) { - var bounds = this.getBounds(); + resolution = resolution || 1; - var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); - var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + var bounds = this.getBounds(); + + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width * resolution, bounds.height * resolution); + + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas, scaleMode); + texture.baseTexture.resolution = resolution; + + canvasBuffer.context.scale(resolution, resolution); canvasBuffer.context.translate(-bounds.x,-bounds.y); @@ -9006,19 +12152,23 @@ PIXI.Graphics.prototype._renderWebGL = function(renderSession) { // if the sprite is not visible or the alpha is 0 then no need to render this element if(this.visible === false || this.alpha === 0 || this.isMask === true)return; - + if(this._cacheAsBitmap) { - - if(this.dirty) + + if(this.dirty || this.cachedSpriteDirty) { + this._generateCachedSprite(); + // we will also need to update the texture on the gpu too! - PIXI.updateWebGLTexture(this._cachedSprite.texture.baseTexture, renderSession.gl); - - this.dirty = false; + this.updateCachedSpriteTexture(); + + this.cachedSpriteDirty = false; + this.dirty = false; } + this._cachedSprite.worldAlpha = this.worldAlpha; PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); return; @@ -9026,8 +12176,9 @@ PIXI.Graphics.prototype._renderWebGL = function(renderSession) else { renderSession.spriteBatch.stop(); + renderSession.blendModeManager.setBlendMode(this.blendMode); - if(this._mask)renderSession.maskManager.pushMask(this.mask, renderSession); + if(this._mask)renderSession.maskManager.pushMask(this._mask, renderSession); if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock); // check blend mode @@ -9037,7 +12188,14 @@ PIXI.Graphics.prototype._renderWebGL = function(renderSession) var blendModeWebGL = PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode]; renderSession.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); } - + + // check if the webgl graphic needs to be updated + if(this.webGLDirty) + { + this.dirty = true; + this.webGLDirty = false; + } + PIXI.WebGLGraphics.renderGraphics(this, renderSession); // only render if it has children! @@ -9055,7 +12213,7 @@ PIXI.Graphics.prototype._renderWebGL = function(renderSession) } if(this._filters)renderSession.filterManager.popFilter(); - if(this._mask)renderSession.maskManager.popMask(renderSession); + if(this._mask)renderSession.maskManager.popMask(this.mask, renderSession); renderSession.drawCount++; @@ -9075,22 +12233,60 @@ PIXI.Graphics.prototype._renderCanvas = function(renderSession) // if the sprite is not visible or the alpha is 0 then no need to render this element if(this.visible === false || this.alpha === 0 || this.isMask === true)return; - var context = renderSession.context; - var transform = this.worldTransform; - - if(this.blendMode !== renderSession.currentBlendMode) + if(this._cacheAsBitmap) { - renderSession.currentBlendMode = this.blendMode; - context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; + if(this.dirty || this.cachedSpriteDirty) + { + this._generateCachedSprite(); + + // we will also need to update the texture + this.updateCachedSpriteTexture(); + + this.cachedSpriteDirty = false; + this.dirty = false; + } + + this._cachedSprite.alpha = this.alpha; + PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + + return; } - - context.setTransform(transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty); - PIXI.CanvasGraphics.renderGraphics(this, context); - - // simple render children! - for(var i=0, j=this.children.length; i maxX ? x1 : maxX; maxX = x2 > maxX ? x2 : maxX; maxX = x3 > maxX ? x3 : maxX; maxX = x4 > maxX ? x4 : maxX; - maxY = y1 > maxY ? y1 : maxY; maxY = y2 > maxY ? y2 : maxY; maxY = y3 > maxY ? y3 : maxY; maxY = y4 > maxY ? y4 : maxY; - var bounds = this._bounds; + this._bounds.x = minX; + this._bounds.width = maxX - minX; - bounds.x = minX; - bounds.width = maxX - minX; + this._bounds.y = minY; + this._bounds.height = maxY - minY; - bounds.y = minY; - bounds.height = maxY - minY; - - return bounds; + return this._bounds; }; /** * Update the bounds of the object * - * @method updateBounds + * @method updateLocalBounds */ -PIXI.Graphics.prototype.updateBounds = function() +PIXI.Graphics.prototype.updateLocalBounds = function() { - var minX = Infinity; var maxX = -Infinity; var minY = Infinity; var maxY = -Infinity; - var points, x, y, w, h; + if(this.graphicsData.length) + { + var shape, points, x, y, w, h; - for (var i = 0; i < this.graphicsData.length; i++) { - var data = this.graphicsData[i]; - var type = data.type; - var lineWidth = data.lineWidth; + for (var i = 0; i < this.graphicsData.length; i++) { + var data = this.graphicsData[i]; + var type = data.type; + var lineWidth = data.lineWidth; + shape = data.shape; + - points = data.points; - - if(type === PIXI.Graphics.RECT) - { - x = points[0] - lineWidth/2; - y = points[1] - lineWidth/2; - w = points[2] + lineWidth; - h = points[3] + lineWidth; - - minX = x < minX ? x : minX; - maxX = x + w > maxX ? x + w : maxX; - - minY = y < minY ? x : minY; - maxY = y + h > maxY ? y + h : maxY; - } - else if(type === PIXI.Graphics.CIRC || type === PIXI.Graphics.ELIP) - { - x = points[0]; - y = points[1]; - w = points[2] + lineWidth/2; - h = points[3] + lineWidth/2; - - minX = x - w < minX ? x - w : minX; - maxX = x + w > maxX ? x + w : maxX; - - minY = y - h < minY ? y - h : minY; - maxY = y + h > maxY ? y + h : maxY; - } - else - { - // POLY - for (var j = 0; j < points.length; j+=2) + if(type === PIXI.Graphics.RECT || type === PIXI.Graphics.RREC) { + x = shape.x - lineWidth/2; + y = shape.y - lineWidth/2; + w = shape.width + lineWidth; + h = shape.height + lineWidth; - x = points[j]; - y = points[j+1]; - minX = x-lineWidth < minX ? x-lineWidth : minX; - maxX = x+lineWidth > maxX ? x+lineWidth : maxX; + minX = x < minX ? x : minX; + maxX = x + w > maxX ? x + w : maxX; - minY = y-lineWidth < minY ? y-lineWidth : minY; - maxY = y+lineWidth > maxY ? y+lineWidth : maxY; + minY = y < minY ? y : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else if(type === PIXI.Graphics.CIRC) + { + x = shape.x; + y = shape.y; + w = shape.radius + lineWidth/2; + h = shape.radius + lineWidth/2; + + minX = x - w < minX ? x - w : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y - h < minY ? y - h : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else if(type === PIXI.Graphics.ELIP) + { + x = shape.x; + y = shape.y; + w = shape.width + lineWidth/2; + h = shape.height + lineWidth/2; + + minX = x - w < minX ? x - w : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y - h < minY ? y - h : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else + { + // POLY + points = shape.points; + + for (var j = 0; j < points.length; j+=2) + { + + x = points[j]; + y = points[j+1]; + minX = x-lineWidth < minX ? x-lineWidth : minX; + maxX = x+lineWidth > maxX ? x+lineWidth : maxX; + + minY = y-lineWidth < minY ? y-lineWidth : minY; + maxY = y+lineWidth > maxY ? y+lineWidth : maxY; + } } } } + else + { + minX = 0; + maxX = 0; + minY = 0; + maxY = 0; + } var padding = this.boundsPadding; - this.bounds = new PIXI.Rectangle(minX - padding, minY - padding, (maxX - minX) + padding * 2, (maxY - minY) + padding * 2); -}; + + this._localBounds.x = minX - padding; + this._localBounds.width = (maxX - minX) + padding * 2; + this._localBounds.y = minY - padding; + this._localBounds.height = (maxY - minY) + padding * 2; +}; /** * Generates the cached sprite when the sprite has cacheAsBitmap = true @@ -9271,10 +12500,43 @@ PIXI.Graphics.prototype._generateCachedSprite = function() // this._cachedSprite.buffer.context.save(); this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y); + // make sure we set the alpha of the graphics to 1 for the render.. + this.worldAlpha = 1; + + // now render the graphic.. PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context); - // this._cachedSprite.buffer.context.restore(); + this._cachedSprite.alpha = this.alpha; }; +/** + * Updates texture size based on canvas size + * + * @method updateCachedSpriteTexture + * @private + */ +PIXI.Graphics.prototype.updateCachedSpriteTexture = function() +{ + var cachedSprite = this._cachedSprite; + var texture = cachedSprite.texture; + var canvas = cachedSprite.buffer.canvas; + + texture.baseTexture.width = canvas.width; + texture.baseTexture.height = canvas.height; + texture.crop.width = texture.frame.width = canvas.width; + texture.crop.height = texture.frame.height = canvas.height; + + cachedSprite._width = canvas.width; + cachedSprite._height = canvas.height; + + // update the dirty base textures + texture.baseTexture.dirty(); +}; + +/** + * Destroys a previous cached sprite. + * + * @method destroyCachedSprite + */ PIXI.Graphics.prototype.destroyCachedSprite = function() { this._cachedSprite.texture.destroy(true); @@ -9284,102 +12546,449 @@ PIXI.Graphics.prototype.destroyCachedSprite = function() this._cachedSprite = null; }; +/** + * Draws the given shape to this Graphics object. Can be any of Circle, Rectangle, Ellipse, Line or Polygon. + * + * @method drawShape + * @param {Circle|Rectangle|Ellipse|Line|Polygon} shape The Shape object to draw. + * @return {GraphicsData} The generated GraphicsData object. + */ +PIXI.Graphics.prototype.drawShape = function(shape) +{ + if(this.currentPath) + { + // check current path! + if(this.currentPath.shape.points.length <= 2)this.graphicsData.pop(); + } + + this.currentPath = null; + + var data = new PIXI.GraphicsData(this.lineWidth, this.lineColor, this.lineAlpha, this.fillColor, this.fillAlpha, this.filling, shape); + + this.graphicsData.push(data); + + if(data.type === PIXI.Graphics.POLY) + { + data.shape.closed = this.filling; + this.currentPath = data; + } + + this.dirty = true; + + return data; +}; + +/** + * A GraphicsData object. + * + * @class GraphicsData + * @constructor + */ +PIXI.GraphicsData = function(lineWidth, lineColor, lineAlpha, fillColor, fillAlpha, fill, shape) +{ + this.lineWidth = lineWidth; + this.lineColor = lineColor; + this.lineAlpha = lineAlpha; + this._lineTint = lineColor; + + this.fillColor = fillColor; + this.fillAlpha = fillAlpha; + this._fillTint = fillColor; + this.fill = fill; + + this.shape = shape; + this.type = shape.type; +}; // SOME TYPES: PIXI.Graphics.POLY = 0; PIXI.Graphics.RECT = 1; PIXI.Graphics.CIRC = 2; PIXI.Graphics.ELIP = 3; - +PIXI.Graphics.RREC = 4; + +PIXI.Polygon.prototype.type = PIXI.Graphics.POLY; +PIXI.Rectangle.prototype.type = PIXI.Graphics.RECT; +PIXI.Circle.prototype.type = PIXI.Graphics.CIRC; +PIXI.Ellipse.prototype.type = PIXI.Graphics.ELIP; +PIXI.RoundedRectangle.prototype.type = PIXI.Graphics.RREC; + + /** * @author Mat Groves http://matgroves.com/ */ /** - * + * * @class Strip * @extends DisplayObjectContainer * @constructor * @param texture {Texture} The texture to use - * @param width {Number} the width + * @param width {Number} the width * @param height {Number} the height - * + * */ -PIXI.Strip = function(texture, width, height) +PIXI.Strip = function(texture) { PIXI.DisplayObjectContainer.call( this ); + + + /** + * The texture of the strip + * + * @property texture + * @type Texture + */ this.texture = texture; + + // set up the main bits.. + this.uvs = new PIXI.Float32Array([0, 1, + 1, 1, + 1, 0, + 0, 1]); + + this.vertices = new PIXI.Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + this.colors = new PIXI.Float32Array([1, 1, 1, 1]); + + this.indices = new PIXI.Uint16Array([0, 1, 2, 3]); + + /** + * Whether the strip is dirty or not + * + * @property dirty + * @type Boolean + */ + this.dirty = true; + + /** + * The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode. + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ this.blendMode = PIXI.blendModes.NORMAL; - try - { - this.uvs = new Float32Array([0, 1, - 1, 1, - 1, 0, 0,1]); + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. + * + * @property canvasPadding + * @type Number + */ + this.canvasPadding = 0; - this.verticies = new Float32Array([0, 0, - 0,0, - 0,0, 0, - 0, 0]); + this.drawMode = PIXI.Strip.DrawModes.TRIANGLE_STRIP; - this.colors = new Float32Array([1, 1, 1, 1]); - - this.indices = new Uint16Array([0, 1, 2, 3]); - } - catch(error) - { - this.uvs = [0, 1, - 1, 1, - 1, 0, 0,1]; - - this.verticies = [0, 0, - 0,0, - 0,0, 0, - 0, 0]; - - this.colors = [1, 1, 1, 1]; - - this.indices = [0, 1, 2, 3]; - } - - - /* - this.uvs = new Float32Array() - this.verticies = new Float32Array() - this.colors = new Float32Array() - this.indices = new Uint16Array() - */ - - this.width = width; - this.height = height; - - // load the texture! - if(texture.baseTexture.hasLoaded) - { - this.width = this.texture.frame.width; - this.height = this.texture.frame.height; - this.updateFrame = true; - } - else - { - this.onTextureUpdateBind = this.onTextureUpdate.bind(this); - this.texture.addEventListener( 'update', this.onTextureUpdateBind ); - } - - this.renderable = true; }; // constructor PIXI.Strip.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Strip.prototype.constructor = PIXI.Strip; -/* - * Sets the texture that the Strip will use +PIXI.Strip.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(!this.visible || this.alpha <= 0)return; + // render triangle strip.. + + renderSession.spriteBatch.stop(); + + // init! init! + if(!this._vertexBuffer)this._initWebGL(renderSession); + + renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader); + + this._renderStrip(renderSession); + + ///renderSession.shaderManager.activateDefaultShader(); + + renderSession.spriteBatch.start(); + + //TODO check culling +}; + +PIXI.Strip.prototype._initWebGL = function(renderSession) +{ + // build the strip! + var gl = renderSession.gl; + + this._vertexBuffer = gl.createBuffer(); + this._indexBuffer = gl.createBuffer(); + this._uvBuffer = gl.createBuffer(); + this._colorBuffer = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); +}; + +PIXI.Strip.prototype._renderStrip = function(renderSession) +{ + var gl = renderSession.gl; + var projection = renderSession.projection, + offset = renderSession.offset, + shader = renderSession.shaderManager.stripShader; + + var drawMode = this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + // gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); + + renderSession.blendModeManager.setBlendMode(this.blendMode); + + + // set uniforms + gl.uniformMatrix3fv(shader.translationMatrix, false, this.worldTransform.toArray(true)); + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + gl.uniform1f(shader.alpha, this.worldAlpha); + + if(!this.dirty) + { + + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + // update the uvs + gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + + // check if a texture is dirty.. + if(this.texture.baseTexture._dirty[gl.id]) + { + renderSession.renderer.updateTexture(this.texture.baseTexture); + } + else + { + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); + } + + // dont need to upload! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); + + + } + else + { + + this.dirty = false; + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + // update the uvs + gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + + // check if a texture is dirty.. + if(this.texture.baseTexture._dirty[gl.id]) + { + renderSession.renderer.updateTexture(this.texture.baseTexture); + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); + } + + // dont need to upload! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + } + //console.log(gl.TRIANGLE_STRIP) + // + // + gl.drawElements(drawMode, this.indices.length, gl.UNSIGNED_SHORT, 0); + + +}; + + + +PIXI.Strip.prototype._renderCanvas = function(renderSession) +{ + var context = renderSession.context; + + var transform = this.worldTransform; + + if (renderSession.roundPixels) + { + context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx | 0, transform.ty | 0); + } + else + { + context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + } + + if (this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP) + { + this._renderCanvasTriangleStrip(context); + } + else + { + this._renderCanvasTriangles(context); + } +}; + +PIXI.Strip.prototype._renderCanvasTriangleStrip = function(context) +{ + // draw triangles!! + var vertices = this.vertices; + var uvs = this.uvs; + + var length = vertices.length / 2; + this.count++; + + for (var i = 0; i < length - 2; i++) { + // draw some triangles! + var index = i * 2; + this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); + } +}; + +PIXI.Strip.prototype._renderCanvasTriangles = function(context) +{ + // draw triangles!! + var vertices = this.vertices; + var uvs = this.uvs; + var indices = this.indices; + + var length = indices.length; + this.count++; + + for (var i = 0; i < length; i += 3) { + // draw some triangles! + var index0 = indices[i] * 2, index1 = indices[i + 1] * 2, index2 = indices[i + 2] * 2; + this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); + } +}; + +PIXI.Strip.prototype._renderCanvasDrawTriangle = function(context, vertices, uvs, index0, index1, index2) +{ + var textureSource = this.texture.baseTexture.source; + var textureWidth = this.texture.width; + var textureHeight = this.texture.height; + + var x0 = vertices[index0], x1 = vertices[index1], x2 = vertices[index2]; + var y0 = vertices[index0 + 1], y1 = vertices[index1 + 1], y2 = vertices[index2 + 1]; + + var u0 = uvs[index0] * textureWidth, u1 = uvs[index1] * textureWidth, u2 = uvs[index2] * textureWidth; + var v0 = uvs[index0 + 1] * textureHeight, v1 = uvs[index1 + 1] * textureHeight, v2 = uvs[index2 + 1] * textureHeight; + + if (this.canvasPadding > 0) { + var paddingX = this.canvasPadding / this.worldTransform.a; + var paddingY = this.canvasPadding / this.worldTransform.d; + var centerX = (x0 + x1 + x2) / 3; + var centerY = (y0 + y1 + y2) / 3; + + var normX = x0 - centerX; + var normY = y0 - centerY; + + var dist = Math.sqrt(normX * normX + normY * normY); + x0 = centerX + (normX / dist) * (dist + paddingX); + y0 = centerY + (normY / dist) * (dist + paddingY); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt(normX * normX + normY * normY); + x1 = centerX + (normX / dist) * (dist + paddingX); + y1 = centerY + (normY / dist) * (dist + paddingY); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt(normX * normX + normY * normY); + x2 = centerX + (normX / dist) * (dist + paddingX); + y2 = centerY + (normY / dist) * (dist + paddingY); + } + + context.save(); + context.beginPath(); + + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + var delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + var deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + var deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + var deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + var deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + var deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + var deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform(deltaA / delta, deltaD / delta, + deltaB / delta, deltaE / delta, + deltaC / delta, deltaF / delta); + + context.drawImage(textureSource, 0, 0); + context.restore(); +}; + + + +/** + * Renders a flat strip * - * @method setTexture - * @param texture {Texture} the texture that will be used + * @method renderStripFlat + * @param strip {Strip} The Strip to render * @private */ +PIXI.Strip.prototype.renderStripFlat = function(strip) +{ + var context = this.context; + var vertices = strip.vertices; + + var length = vertices.length/2; + this.count++; + + context.beginPath(); + for (var i=1; i < length-2; i++) + { + // draw some triangles! + var index = i*2; + + var x0 = vertices[index], x1 = vertices[index+2], x2 = vertices[index+4]; + var y0 = vertices[index+1], y1 = vertices[index+3], y2 = vertices[index+5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); +}; + +/* PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES @@ -9391,6 +13000,7 @@ PIXI.Strip.prototype.setTexture = function(texture) this.height = texture.frame.height; this.updateFrame = true; }; +*/ /** * When the texture is updated, this event will fire to update the scale and frame @@ -9399,40 +13009,105 @@ PIXI.Strip.prototype.setTexture = function(texture) * @param event * @private */ + PIXI.Strip.prototype.onTextureUpdate = function() { this.updateFrame = true; -}; -/* @author Mat Groves http://matgroves.com/ @Doormat23 +}; + +/** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + * @method getBounds + * @param matrix {Matrix} the transformation matrix of the sprite + * @return {Rectangle} the framing rectangle + */ +PIXI.Strip.prototype.getBounds = function(matrix) +{ + var worldTransform = matrix || this.worldTransform; + + var a = worldTransform.a; + var b = worldTransform.b; + var c = worldTransform.c; + var d = worldTransform.d; + var tx = worldTransform.tx; + var ty = worldTransform.ty; + + var maxX = -Infinity; + var maxY = -Infinity; + + var minX = Infinity; + var minY = Infinity; + + var vertices = this.vertices; + for (var i = 0, n = vertices.length; i < n; i += 2) + { + var rawX = vertices[i], rawY = vertices[i + 1]; + var x = (a * rawX) + (c * rawY) + tx; + var y = (d * rawY) + (b * rawX) + ty; + + minX = x < minX ? x : minX; + minY = y < minY ? y : minY; + + maxX = x > maxX ? x : maxX; + maxY = y > maxY ? y : maxY; + } + + if (minX === -Infinity || maxY === Infinity) + { + return PIXI.EmptyRectangle; + } + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + +/** + * Different drawing buffer modes supported + * + * @property + * @type {{TRIANGLE_STRIP: number, TRIANGLES: number}} + * @static + */ +PIXI.Strip.DrawModes = { + TRIANGLE_STRIP: 0, + TRIANGLES: 1 +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * @copyright Mat Groves, Rovanion Luckey */ /** - * + * * @class Rope * @constructor - * @param texture {Texture} The texture to use - * @param points {Array} - * + * @extends Strip + * @param {Texture} texture - The texture to use on the rope. + * @param {Array} points - An array of {PIXI.Point}. + * */ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - try - { - this.verticies = new Float32Array(points.length * 4); - this.uvs = new Float32Array(points.length * 4); - this.colors = new Float32Array(points.length * 2); - this.indices = new Uint16Array(points.length * 2); - } - catch(error) - { - this.verticies = new Array(points.length * 4); - this.uvs = new Array(points.length * 4); - this.colors = new Array(points.length * 2); - this.indices = new Array(points.length * 2); - } + this.vertices = new PIXI.Float32Array(points.length * 4); + this.uvs = new PIXI.Float32Array(points.length * 4); + this.colors = new PIXI.Float32Array(points.length * 2); + this.indices = new PIXI.Uint16Array(points.length * 2); + this.refresh(); }; @@ -9443,7 +13118,7 @@ PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); PIXI.Rope.prototype.constructor = PIXI.Rope; /* - * Refreshes + * Refreshes * * @method refresh */ @@ -9460,9 +13135,8 @@ PIXI.Rope.prototype.refresh = function() this.count-=0.2; - uvs[0] = 0; - uvs[1] = 1; + uvs[1] = 0; uvs[2] = 0; uvs[3] = 1; @@ -9477,7 +13151,6 @@ PIXI.Rope.prototype.refresh = function() for (var i = 1; i < total; i++) { - point = points[i]; index = i * 4; // time to do some smart drawing! @@ -9490,7 +13163,6 @@ PIXI.Rope.prototype.refresh = function() uvs[index+2] = amount; uvs[index+3] = 1; - } else { @@ -9531,17 +13203,11 @@ PIXI.Rope.prototype.updateTransform = function() this.count-=0.2; - var verticies = this.verticies; - verticies[0] = lastPoint.x + perp.x; - verticies[1] = lastPoint.y + perp.y; //+ 200 - verticies[2] = lastPoint.x - perp.x; - verticies[3] = lastPoint.y - perp.y;//+200 - // time to do some smart drawing! - + var vertices = this.vertices; var total = points.length, point, index, ratio, perpLength, num; - for (var i = 1; i < total; i++) + for (var i = 0; i < total; i++) { point = points[i]; index = i * 4; @@ -9570,10 +13236,10 @@ PIXI.Rope.prototype.updateTransform = function() perp.x *= num; perp.y *= num; - verticies[index] = point.x + perp.x; - verticies[index+1] = point.y + perp.y; - verticies[index+2] = point.x - perp.x; - verticies[index+3] = point.y - perp.y; + vertices[index] = point.x + perp.x; + vertices[index+1] = point.y + perp.y; + vertices[index+2] = point.x - perp.x; + vertices[index+3] = point.y - perp.y; lastPoint = point; } @@ -9581,7 +13247,7 @@ PIXI.Rope.prototype.updateTransform = function() PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); }; /* - * Sets the texture that the Rope will use + * Sets the texture that the Rope will use * * @method setTexture * @param texture {Texture} the texture that will be used @@ -9590,9 +13256,9 @@ PIXI.Rope.prototype.setTexture = function(texture) { // stop current texture this.texture = texture; - this.updateFrame = true; + //this.updateFrame = true; }; - + /** * @author Mat Groves http://matgroves.com/ */ @@ -9601,7 +13267,7 @@ PIXI.Rope.prototype.setTexture = function(texture) * A tiling sprite is a fast way of rendering a tiling image * * @class TilingSprite - * @extends DisplayObjectContainer + * @extends Sprite * @constructor * @param texture {Texture} the texture of the tiling sprite * @param width {Number} the width of the tiling sprite @@ -9617,14 +13283,15 @@ PIXI.TilingSprite = function(texture, width, height) * @property width * @type Number */ - this.width = width || 100; + this._width = width || 100; + /** * The height of the tiling sprite * * @property height * @type Number */ - this.height = height || 100; + this._height = height || 100; /** * The scaling of the image that is being tiled @@ -9650,7 +13317,6 @@ PIXI.TilingSprite = function(texture, width, height) */ this.tilePosition = new PIXI.Point(0,0); - /** * Whether this sprite is renderable or not * @@ -9677,6 +13343,9 @@ PIXI.TilingSprite = function(texture, width, height) * @default PIXI.blendModes.NORMAL; */ this.blendMode = PIXI.blendModes.NORMAL; + + + }; // constructor @@ -9715,16 +13384,15 @@ Object.defineProperty(PIXI.TilingSprite.prototype, 'height', { } }); -/** - * When the texture is updated, this event will be fired to update the scale and frame - * - * @method onTextureUpdate - * @param event - * @private - */ -PIXI.TilingSprite.prototype.onTextureUpdate = function() +PIXI.TilingSprite.prototype.setTexture = function(texture) { - this.updateFrame = true; + if (this.texture === texture) return; + + this.texture = texture; + + this.refreshTexture = true; + + this.cachedTint = 0xFFFFFF; }; /** @@ -9736,53 +13404,52 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function() */ PIXI.TilingSprite.prototype._renderWebGL = function(renderSession) { - - if(this.visible === false || this.alpha === 0)return; - + if (this.visible === false || this.alpha === 0) return; var i,j; - if(this.mask || this.filters) + if (this._mask) { - if(this.mask) - { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); - } - - if(this.filters) - { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); - } - - if(!this.tilingTexture)this.generateTilingTexture(true); - else renderSession.spriteBatch.renderTilingSprite(this); - - // simple render children! - for(i=0,j=this.children.length; i 1) + cos = 1; + var childAngle = Math.acos(cos) * bendDirection; + var adjacent = len1 + len2 * cos, opposite = len2 * Math.sin(childAngle); + var parentAngle = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); + var rotation = (parentAngle - offset) * spine.radDeg - parentRotation; + if (rotation > 180) + rotation -= 360; + else if (rotation < -180) // + rotation += 360; + parent.rotationIK = parentRotation + rotation * alpha; + rotation = (childAngle + offset) * spine.radDeg - childRotation; + if (rotation > 180) + rotation -= 360; + else if (rotation < -180) // + rotation += 360; + child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha; +}; + spine.Skin = function (name) { this.name = name; this.attachments = {}; @@ -10153,7 +14035,7 @@ spine.Skin.prototype = { _attachAll: function (skeleton, oldSkin) { for (var key in oldSkin.attachments) { var colon = key.indexOf(":"); - var slotIndex = parseInt(key.substring(0, colon), 10); + var slotIndex = parseInt(key.substring(0, colon)); var name = key.substring(colon + 1); var slot = skeleton.slots[slotIndex]; if (slot.attachment && slot.attachment.name == name) { @@ -10170,21 +14052,26 @@ spine.Animation = function (name, timelines, duration) { this.duration = duration; }; spine.Animation.prototype = { - apply: function (skeleton, time, loop) { - if (loop && this.duration) time %= this.duration; + apply: function (skeleton, lastTime, time, loop, events) { + if (loop && this.duration != 0) { + time %= this.duration; + lastTime %= this.duration; + } var timelines = this.timelines; for (var i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, time, 1); + timelines[i].apply(skeleton, lastTime, time, events, 1); }, - mix: function (skeleton, time, loop, alpha) { - if (loop && this.duration) time %= this.duration; + mix: function (skeleton, lastTime, time, loop, events, alpha) { + if (loop && this.duration != 0) { + time %= this.duration; + lastTime %= this.duration; + } var timelines = this.timelines; for (var i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, time, alpha); + timelines[i].apply(skeleton, lastTime, time, events, alpha); } }; - -spine.binarySearch = function (values, target, step) { +spine.Animation.binarySearch = function (values, target, step) { var low = 0; var high = Math.floor(values.length / step) - 2; if (!high) return step; @@ -10198,69 +14085,56 @@ spine.binarySearch = function (values, target, step) { current = (low + high) >>> 1; } }; -spine.linearSearch = function (values, target, step) { +spine.Animation.binarySearch1 = function (values, target) { + var low = 0; + var high = values.length - 2; + if (!high) return 1; + var current = high >>> 1; + while (true) { + if (values[current + 1] <= target) + low = current + 1; + else + high = current; + if (low == high) return low + 1; + current = (low + high) >>> 1; + } +}; +spine.Animation.linearSearch = function (values, target, step) { for (var i = 0, last = values.length - step; i <= last; i += step) if (values[i] > target) return i; return -1; }; spine.Curves = function (frameCount) { - this.curves = []; // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... - this.curves.length = (frameCount - 1) * 6; + this.curves = []; // type, x, y, ... + //this.curves.length = (frameCount - 1) * 19/*BEZIER_SIZE*/; }; spine.Curves.prototype = { setLinear: function (frameIndex) { - this.curves[frameIndex * 6] = 0/*LINEAR*/; + this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 0/*LINEAR*/; }, setStepped: function (frameIndex) { - this.curves[frameIndex * 6] = -1/*STEPPED*/; + this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 1/*STEPPED*/; }, /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of * the difference between the keyframe's values. */ setCurve: function (frameIndex, cx1, cy1, cx2, cy2) { - var subdiv_step = 1 / 10/*BEZIER_SEGMENTS*/; - var subdiv_step2 = subdiv_step * subdiv_step; - var subdiv_step3 = subdiv_step2 * subdiv_step; - var pre1 = 3 * subdiv_step; - var pre2 = 3 * subdiv_step2; - var pre4 = 6 * subdiv_step2; - var pre5 = 6 * subdiv_step3; - var tmp1x = -cx1 * 2 + cx2; - var tmp1y = -cy1 * 2 + cy2; - var tmp2x = (cx1 - cx2) * 3 + 1; - var tmp2y = (cy1 - cy2) * 3 + 1; - var i = frameIndex * 6; + var subdiv1 = 1 / 10/*BEZIER_SEGMENTS*/, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1; + var pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3; + var tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1; + var dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3; + var ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5; + var dddfx = tmp2x * pre5, dddfy = tmp2y * pre5; + + var i = frameIndex * 19/*BEZIER_SIZE*/; var curves = this.curves; - curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3; - curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3; - curves[i + 2] = tmp1x * pre4 + tmp2x * pre5; - curves[i + 3] = tmp1y * pre4 + tmp2y * pre5; - curves[i + 4] = tmp2x * pre5; - curves[i + 5] = tmp2y * pre5; - }, - getCurvePercent: function (frameIndex, percent) { - percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); - var curveIndex = frameIndex * 6; - var curves = this.curves; - var dfx = curves[curveIndex]; - if (!dfx/*LINEAR*/) return percent; - if (dfx == -1/*STEPPED*/) return 0; - var dfy = curves[curveIndex + 1]; - var ddfx = curves[curveIndex + 2]; - var ddfy = curves[curveIndex + 3]; - var dddfx = curves[curveIndex + 4]; - var dddfy = curves[curveIndex + 5]; + curves[i++] = 2/*BEZIER*/; + var x = dfx, y = dfy; - var i = 10/*BEZIER_SEGMENTS*/ - 2; - while (true) { - if (x >= percent) { - var lastX = x - dfx; - var lastY = y - dfy; - return lastY + (y - lastY) * (percent - lastX) / (x - lastX); - } - if (!i) break; - i--; + for (var n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) { + curves[i] = x; + curves[i + 1] = y; dfx += ddfx; dfy += ddfy; ddfx += dddfx; @@ -10268,6 +14142,31 @@ spine.Curves.prototype = { x += dfx; y += dfy; } + }, + getCurvePercent: function (frameIndex, percent) { + percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); + var curves = this.curves; + var i = frameIndex * 19/*BEZIER_SIZE*/; + var type = curves[i]; + if (type === 0/*LINEAR*/) return percent; + if (type == 1/*STEPPED*/) return 0; + i++; + var x = 0; + for (var start = i, n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) { + x = curves[i]; + if (x >= percent) { + var prevX, prevY; + if (i == start) { + prevX = 0; + prevY = 0; + } else { + prevX = curves[i - 2]; + prevY = curves[i - 1]; + } + return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + } + } + var y = curves[i - 1]; return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. } }; @@ -10287,16 +14186,14 @@ spine.RotateTimeline.prototype = { this.frames[frameIndex] = time; this.frames[frameIndex + 1] = angle; }, - apply: function (skeleton, time, alpha) { - var frames = this.frames, - amount; - + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 2]) { // Time is after last frame. - amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; + var amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; while (amount > 180) amount -= 360; while (amount < -180) @@ -10305,19 +14202,19 @@ spine.RotateTimeline.prototype = { return; } - // Interpolate between the last frame and the current frame. - var frameIndex = spine.binarySearch(frames, time, 2); - var lastFrameValue = frames[frameIndex - 1]; + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 2); + var prevFrameValue = frames[frameIndex - 1]; var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*LAST_FRAME_TIME*/] - frameTime); + var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*PREV_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent); - amount = frames[frameIndex + 1/*FRAME_VALUE*/] - lastFrameValue; + var amount = frames[frameIndex + 1/*FRAME_VALUE*/] - prevFrameValue; while (amount > 180) amount -= 360; while (amount < -180) amount += 360; - amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation; + amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation; while (amount > 180) amount -= 360; while (amount < -180) @@ -10342,7 +14239,7 @@ spine.TranslateTimeline.prototype = { this.frames[frameIndex + 1] = x; this.frames[frameIndex + 2] = y; }, - apply: function (skeleton, time, alpha) { + apply: function (skeleton, lastTime, time, firedEvents, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -10354,16 +14251,16 @@ spine.TranslateTimeline.prototype = { return; } - // Interpolate between the last frame and the current frame. - var frameIndex = spine.binarySearch(frames, time, 3); - var lastFrameX = frames[frameIndex - 2]; - var lastFrameY = frames[frameIndex - 1]; + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameX = frames[frameIndex - 2]; + var prevFrameY = frames[frameIndex - 1]; var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); - bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; - bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; + bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent - bone.y) * alpha; } }; @@ -10383,28 +14280,28 @@ spine.ScaleTimeline.prototype = { this.frames[frameIndex + 1] = x; this.frames[frameIndex + 2] = y; }, - apply: function (skeleton, time, alpha) { + apply: function (skeleton, lastTime, time, firedEvents, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. - bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; + bone.scaleX += (bone.data.scaleX * frames[frames.length - 2] - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY * frames[frames.length - 1] - bone.scaleY) * alpha; return; } - // Interpolate between the last frame and the current frame. - var frameIndex = spine.binarySearch(frames, time, 3); - var lastFrameX = frames[frameIndex - 2]; - var lastFrameY = frames[frameIndex - 1]; + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameX = frames[frameIndex - 2]; + var prevFrameY = frames[frameIndex - 1]; var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); - bone.scaleX += (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.scaleY) * alpha; + bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent) - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent) - bone.scaleY) * alpha; } }; @@ -10416,9 +14313,9 @@ spine.ColorTimeline = function (frameCount) { spine.ColorTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length / 5; }, - setFrame: function (frameIndex, time, x, y) { + setFrame: function (frameIndex, time, r, g, b, a) { frameIndex *= 5; this.frames[frameIndex] = time; this.frames[frameIndex + 1] = r; @@ -10426,35 +14323,35 @@ spine.ColorTimeline.prototype = { this.frames[frameIndex + 3] = b; this.frames[frameIndex + 4] = a; }, - apply: function (skeleton, time, alpha) { + apply: function (skeleton, lastTime, time, firedEvents, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - var slot = skeleton.slots[this.slotIndex]; - - if (time >= frames[frames.length - 5]) { // Time is after last frame. + var r, g, b, a; + if (time >= frames[frames.length - 5]) { + // Time is after last frame. var i = frames.length - 1; - slot.r = frames[i - 3]; - slot.g = frames[i - 2]; - slot.b = frames[i - 1]; - slot.a = frames[i]; - return; + r = frames[i - 3]; + g = frames[i - 2]; + b = frames[i - 1]; + a = frames[i]; + } else { + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 5); + var prevFrameR = frames[frameIndex - 4]; + var prevFrameG = frames[frameIndex - 3]; + var prevFrameB = frames[frameIndex - 2]; + var prevFrameA = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); + + r = prevFrameR + (frames[frameIndex + 1/*FRAME_R*/] - prevFrameR) * percent; + g = prevFrameG + (frames[frameIndex + 2/*FRAME_G*/] - prevFrameG) * percent; + b = prevFrameB + (frames[frameIndex + 3/*FRAME_B*/] - prevFrameB) * percent; + a = prevFrameA + (frames[frameIndex + 4/*FRAME_A*/] - prevFrameA) * percent; } - - // Interpolate between the last frame and the current frame. - var frameIndex = spine.binarySearch(frames, time, 5); - var lastFrameR = frames[frameIndex - 4]; - var lastFrameG = frames[frameIndex - 3]; - var lastFrameB = frames[frameIndex - 2]; - var lastFrameA = frames[frameIndex - 1]; - var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*LAST_FRAME_TIME*/] - frameTime); - percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); - - var r = lastFrameR + (frames[frameIndex + 1/*FRAME_R*/] - lastFrameR) * percent; - var g = lastFrameG + (frames[frameIndex + 2/*FRAME_G*/] - lastFrameG) * percent; - var b = lastFrameB + (frames[frameIndex + 3/*FRAME_B*/] - lastFrameB) * percent; - var a = lastFrameA + (frames[frameIndex + 4/*FRAME_A*/] - lastFrameA) * percent; + var slot = skeleton.slots[this.slotIndex]; if (alpha < 1) { slot.r += (r - slot.r) * alpha; slot.g += (g - slot.g) * alpha; @@ -10473,19 +14370,95 @@ spine.AttachmentTimeline = function (frameCount) { this.curves = new spine.Curves(frameCount); this.frames = []; // time, ... this.frames.length = frameCount; - this.attachmentNames = []; // time, ... + this.attachmentNames = []; this.attachmentNames.length = frameCount; }; spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; this.attachmentNames[frameIndex] = attachmentName; }, - apply: function (skeleton, time, alpha) { + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + + var frameIndex = time >= frames[frames.length - 1] ? frames.length - 1 : spine.Animation.binarySearch1(frames, time) - 1; + if (frames[frameIndex] < lastTime) return; + + var attachmentName = this.attachmentNames[frameIndex]; + skeleton.slots[this.slotIndex].setAttachment( + !attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); + } +}; + +spine.EventTimeline = function (frameCount) { + this.frames = []; // time, ... + this.frames.length = frameCount; + this.events = []; + this.events.length = frameCount; +}; +spine.EventTimeline.prototype = { + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, event) { + this.frames[frameIndex] = time; + this.events[frameIndex] = event; + }, + /** Fires events for frames > lastTime and <= time. */ + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + if (!firedEvents) return; + + var frames = this.frames; + var frameCount = frames.length; + + if (lastTime > time) { // Fire events after last time for looped animations. + this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha); + lastTime = -1; + } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + return; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (lastTime < frames[0]) + frameIndex = 0; + else { + frameIndex = spine.Animation.binarySearch1(frames, lastTime); + var frame = frames[frameIndex]; + while (frameIndex > 0) { // Fire multiple events with the same frame. + if (frames[frameIndex - 1] != frame) break; + frameIndex--; + } + } + var events = this.events; + for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++) + firedEvents.push(events[frameIndex]); + } +}; + +spine.DrawOrderTimeline = function (frameCount) { + this.frames = []; // time, ... + this.frames.length = frameCount; + this.drawOrders = []; + this.drawOrders.length = frameCount; +}; +spine.DrawOrderTimeline.prototype = { + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, drawOrder) { + this.frames[frameIndex] = time; + this.drawOrders[frameIndex] = drawOrder; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -10493,10 +14466,182 @@ spine.AttachmentTimeline.prototype = { if (time >= frames[frames.length - 1]) // Time is after last frame. frameIndex = frames.length - 1; else - frameIndex = spine.binarySearch(frames, time, 1) - 1; + frameIndex = spine.Animation.binarySearch1(frames, time) - 1; - var attachmentName = this.attachmentNames[frameIndex]; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); + var drawOrder = skeleton.drawOrder; + var slots = skeleton.slots; + var drawOrderToSetupIndex = this.drawOrders[frameIndex]; + if (!drawOrderToSetupIndex) { + for (var i = 0, n = slots.length; i < n; i++) + drawOrder[i] = slots[i]; + } else { + for (var i = 0, n = drawOrderToSetupIndex.length; i < n; i++) + drawOrder[i] = skeleton.slots[drawOrderToSetupIndex[i]]; + } + + } +}; + +spine.FfdTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; + this.frames.length = frameCount; + this.frameVertices = []; + this.frameVertices.length = frameCount; +}; +spine.FfdTimeline.prototype = { + slotIndex: 0, + attachment: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, vertices) { + this.frames[frameIndex] = time; + this.frameVertices[frameIndex] = vertices; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var slot = skeleton.slots[this.slotIndex]; + if (slot.attachment != this.attachment) return; + + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameVertices = this.frameVertices; + var vertexCount = frameVertices[0].length; + + var vertices = slot.attachmentVertices; + if (vertices.length != vertexCount) alpha = 1; + vertices.length = vertexCount; + + if (time >= frames[frames.length - 1]) { // Time is after last frame. + var lastVertices = frameVertices[frames.length - 1]; + if (alpha < 1) { + for (var i = 0; i < vertexCount; i++) + vertices[i] += (lastVertices[i] - vertices[i]) * alpha; + } else { + for (var i = 0; i < vertexCount; i++) + vertices[i] = lastVertices[i]; + } + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch1(frames, time); + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime); + percent = this.curves.getCurvePercent(frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + var prevVertices = frameVertices[frameIndex - 1]; + var nextVertices = frameVertices[frameIndex]; + + if (alpha < 1) { + for (var i = 0; i < vertexCount; i++) { + var prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; + } + } else { + for (var i = 0; i < vertexCount; i++) { + var prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; + } + } + } +}; + +spine.IkConstraintTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, mix, bendDirection, ... + this.frames.length = frameCount * 3; +}; +spine.IkConstraintTimeline.prototype = { + ikConstraintIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, mix, bendDirection) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = mix; + this.frames[frameIndex + 2] = bendDirection; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var ikConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + ikConstraint.mix += (frames[frames.length - 2] - ikConstraint.mix) * alpha; + ikConstraint.bendDirection = frames[frames.length - 1]; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameMix = frames[frameIndex + -2/*PREV_FRAME_MIX*/]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + var mix = prevFrameMix + (frames[frameIndex + 1/*FRAME_MIX*/] - prevFrameMix) * percent; + ikConstraint.mix += (mix - ikConstraint.mix) * alpha; + ikConstraint.bendDirection = frames[frameIndex + -1/*PREV_FRAME_BEND_DIRECTION*/]; + } +}; + +spine.FlipXTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, flip, ... + this.frames.length = frameCount * 2; +}; +spine.FlipXTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, flip) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = flip ? 1 : 0; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; + if (frames[frameIndex] < lastTime) return; + skeleton.bones[boneIndex].flipX = frames[frameIndex + 1] != 0; + } +}; + +spine.FlipYTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, flip, ... + this.frames.length = frameCount * 2; +}; +spine.FlipYTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, flip) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = flip ? 1 : 0; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; + if (frames[frameIndex] < lastTime) return; + skeleton.bones[boneIndex].flipY = frames[frameIndex + 1] != 0; } }; @@ -10504,10 +14649,15 @@ spine.SkeletonData = function () { this.bones = []; this.slots = []; this.skins = []; + this.events = []; this.animations = []; + this.ikConstraints = []; }; spine.SkeletonData.prototype = { + name: null, defaultSkin: null, + width: 0, height: 0, + version: null, hash: null, /** @return May be null. */ findBone: function (boneName) { var bones = this.bones; @@ -10545,11 +14695,25 @@ spine.SkeletonData.prototype = { return null; }, /** @return May be null. */ + findEvent: function (eventName) { + var events = this.events; + for (var i = 0, n = events.length; i < n; i++) + if (events[i].name == eventName) return events[i]; + return null; + }, + /** @return May be null. */ findAnimation: function (animationName) { var animations = this.animations; for (var i = 0, n = animations.length; i < n; i++) if (animations[i].name == animationName) return animations[i]; return null; + }, + /** @return May be null. */ + findIkConstraint: function (ikConstraintName) { + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) + if (ikConstraints[i].name == ikConstraintName) return ikConstraints[i]; + return null; } }; @@ -10560,18 +14724,25 @@ spine.Skeleton = function (skeletonData) { for (var i = 0, n = skeletonData.bones.length; i < n; i++) { var boneData = skeletonData.bones[i]; var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)]; - this.bones.push(new spine.Bone(boneData, parent)); + this.bones.push(new spine.Bone(boneData, this, parent)); } this.slots = []; this.drawOrder = []; - for (i = 0, n = skeletonData.slots.length; i < n; i++) { + for (var i = 0, n = skeletonData.slots.length; i < n; i++) { var slotData = skeletonData.slots[i]; var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)]; - var slot = new spine.Slot(slotData, this, bone); + var slot = new spine.Slot(slotData, bone); this.slots.push(slot); this.drawOrder.push(slot); } + + this.ikConstraints = []; + for (var i = 0, n = skeletonData.ikConstraints.length; i < n; i++) + this.ikConstraints.push(new spine.IkConstraint(skeletonData.ikConstraints[i], this)); + + this.boneCache = []; + this.updateCache(); }; spine.Skeleton.prototype = { x: 0, y: 0, @@ -10579,13 +14750,62 @@ spine.Skeleton.prototype = { r: 1, g: 1, b: 1, a: 1, time: 0, flipX: false, flipY: false, + /** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */ + updateCache: function () { + var ikConstraints = this.ikConstraints; + var ikConstraintsCount = ikConstraints.length; + + var arrayCount = ikConstraintsCount + 1; + var boneCache = this.boneCache; + if (boneCache.length > arrayCount) boneCache.length = arrayCount; + for (var i = 0, n = boneCache.length; i < n; i++) + boneCache[i].length = 0; + while (boneCache.length < arrayCount) + boneCache[boneCache.length] = []; + + var nonIkBones = boneCache[0]; + var bones = this.bones; + + outer: + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + var current = bone; + do { + for (var ii = 0; ii < ikConstraintsCount; ii++) { + var ikConstraint = ikConstraints[ii]; + var parent = ikConstraint.bones[0]; + var child= ikConstraint.bones[ikConstraint.bones.length - 1]; + while (true) { + if (current == child) { + boneCache[ii].push(bone); + boneCache[ii + 1].push(bone); + continue outer; + } + if (child == parent) break; + child = child.parent; + } + } + current = current.parent; + } while (current); + nonIkBones[nonIkBones.length] = bone; + } + }, /** Updates the world transform for each bone. */ updateWorldTransform: function () { - var flipX = this.flipX; - var flipY = this.flipY; var bones = this.bones; - for (var i = 0, n = bones.length; i < n; i++) - bones[i].updateWorldTransform(flipX, flipY); + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + bone.rotationIK = bone.rotation; + } + var i = 0, last = this.boneCache.length - 1; + while (true) { + var cacheBones = this.boneCache[i]; + for (var ii = 0, nn = cacheBones.length; ii < nn; ii++) + cacheBones[ii].updateWorldTransform(); + if (i == last) break; + this.ikConstraints[i].apply(); + i++; + } }, /** Sets the bones and slots to their setup pose values. */ setToSetupPose: function () { @@ -10596,11 +14816,21 @@ spine.Skeleton.prototype = { var bones = this.bones; for (var i = 0, n = bones.length; i < n; i++) bones[i].setToSetupPose(); + + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) { + var ikConstraint = ikConstraints[i]; + ikConstraint.bendDirection = ikConstraint.data.bendDirection; + ikConstraint.mix = ikConstraint.data.mix; + } }, setSlotsToSetupPose: function () { var slots = this.slots; - for (var i = 0, n = slots.length; i < n; i++) + var drawOrder = this.drawOrder; + for (var i = 0, n = slots.length; i < n; i++) { + drawOrder[i] = slots[i]; slots[i].setToSetupPose(i); + } }, /** @return May return null. */ getRootBone: function () { @@ -10639,11 +14869,26 @@ spine.Skeleton.prototype = { if (!skin) throw "Skin not found: " + skinName; this.setSkin(skin); }, - /** Sets the skin used to look up attachments not found in the {@link SkeletonData#getDefaultSkin() default skin}. Attachments - * from the new skin are attached if the corresponding attachment from the old skin was attached. + /** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. + * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was + * no old skin, each slot's setup mode attachment is attached from the new skin. * @param newSkin May be null. */ setSkin: function (newSkin) { - if (this.skin && newSkin) newSkin._attachAll(this, this.skin); + if (newSkin) { + if (this.skin) + newSkin._attachAll(this, this.skin); + else { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + var slot = slots[i]; + var name = slot.data.attachmentName; + if (name) { + var attachment = newSkin.getAttachment(i, name); + if (attachment) slot.setAttachment(attachment); + } + } + } + } this.skin = newSkin; }, /** @return May be null. */ @@ -10662,13 +14907,13 @@ spine.Skeleton.prototype = { /** @param attachmentName May be null. */ setAttachment: function (slotName, attachmentName) { var slots = this.slots; - for (var i = 0, n = slots.size; i < n; i++) { + for (var i = 0, n = slots.length; i < n; i++) { var slot = slots[i]; if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); - if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; + attachment = this.getAttachmentBySlotIndex(i, attachmentName); + if (!attachment) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } slot.setAttachment(attachment); return; @@ -10676,26 +14921,58 @@ spine.Skeleton.prototype = { } throw "Slot not found: " + slotName; }, + /** @return May be null. */ + findIkConstraint: function (ikConstraintName) { + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) + if (ikConstraints[i].data.name == ikConstraintName) return ikConstraints[i]; + return null; + }, update: function (delta) { - time += delta; + this.time += delta; } }; -spine.AttachmentType = { - region: 0 +spine.EventData = function (name) { + this.name = name; +}; +spine.EventData.prototype = { + intValue: 0, + floatValue: 0, + stringValue: null }; -spine.RegionAttachment = function () { +spine.Event = function (data) { + this.data = data; +}; +spine.Event.prototype = { + intValue: 0, + floatValue: 0, + stringValue: null +}; + +spine.AttachmentType = { + region: 0, + boundingbox: 1, + mesh: 2, + skinnedmesh: 3 +}; + +spine.RegionAttachment = function (name) { + this.name = name; this.offset = []; this.offset.length = 8; this.uvs = []; this.uvs.length = 8; }; spine.RegionAttachment.prototype = { + type: spine.AttachmentType.region, x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1, width: 0, height: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, rendererObject: null, regionOffsetX: 0, regionOffsetY: 0, regionWidth: 0, regionHeight: 0, @@ -10729,7 +15006,7 @@ spine.RegionAttachment.prototype = { var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY; var localX2 = localX + this.regionWidth * regionScaleX; var localY2 = localY + this.regionHeight * regionScaleY; - var radians = this.rotation * Math.PI / 180; + var radians = this.rotation * spine.degRad; var cos = Math.cos(radians); var sin = Math.sin(radians); var localXCos = localX * cos + this.x; @@ -10753,10 +15030,7 @@ spine.RegionAttachment.prototype = { computeVertices: function (x, y, bone, vertices) { x += bone.worldX; y += bone.worldY; - var m00 = bone.m00; - var m01 = bone.m01; - var m10 = bone.m10; - var m11 = bone.m11; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; var offset = this.offset; vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x; vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y; @@ -10767,14 +15041,170 @@ spine.RegionAttachment.prototype = { vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x; vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y; } -} +}; + +spine.MeshAttachment = function (name) { + this.name = name; +}; +spine.MeshAttachment.prototype = { + type: spine.AttachmentType.mesh, + vertices: null, + uvs: null, + regionUVs: null, + triangles: null, + hullLength: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + edges: null, + width: 0, height: 0, + updateUVs: function () { + var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; + var n = this.regionUVs.length; + if (!this.uvs || this.uvs.length != n) { + this.uvs = new spine.Float32Array(n); + } + if (this.regionRotate) { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; + this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; + } + } else { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i] * width; + this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; + } + } + }, + computeWorldVertices: function (x, y, slot, worldVertices) { + var bone = slot.bone; + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var vertices = this.vertices; + var verticesCount = vertices.length; + if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices; + for (var i = 0; i < verticesCount; i += 2) { + var vx = vertices[i]; + var vy = vertices[i + 1]; + worldVertices[i] = vx * m00 + vy * m01 + x; + worldVertices[i + 1] = vx * m10 + vy * m11 + y; + } + } +}; + +spine.SkinnedMeshAttachment = function (name) { + this.name = name; +}; +spine.SkinnedMeshAttachment.prototype = { + type: spine.AttachmentType.skinnedmesh, + bones: null, + weights: null, + uvs: null, + regionUVs: null, + triangles: null, + hullLength: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + edges: null, + width: 0, height: 0, + updateUVs: function (u, v, u2, v2, rotate) { + var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; + var n = this.regionUVs.length; + if (!this.uvs || this.uvs.length != n) { + this.uvs = new spine.Float32Array(n); + } + if (this.regionRotate) { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; + this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; + } + } else { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i] * width; + this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; + } + } + }, + computeWorldVertices: function (x, y, slot, worldVertices) { + var skeletonBones = slot.bone.skeleton.bones; + var weights = this.weights; + var bones = this.bones; + + var w = 0, v = 0, b = 0, f = 0, n = bones.length, nn; + var wx, wy, bone, vx, vy, weight; + if (!slot.attachmentVertices.length) { + for (; v < n; w += 2) { + wx = 0; + wy = 0; + nn = bones[v++] + v; + for (; v < nn; v++, b += 3) { + bone = skeletonBones[bones[v]]; + vx = weights[b]; + vy = weights[b + 1]; + weight = weights[b + 2]; + wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; + wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + } + worldVertices[w] = wx + x; + worldVertices[w + 1] = wy + y; + } + } else { + var ffd = slot.attachmentVertices; + for (; v < n; w += 2) { + wx = 0; + wy = 0; + nn = bones[v++] + v; + for (; v < nn; v++, b += 3, f += 2) { + bone = skeletonBones[bones[v]]; + vx = weights[b] + ffd[f]; + vy = weights[b + 1] + ffd[f + 1]; + weight = weights[b + 2]; + wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; + wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + } + worldVertices[w] = wx + x; + worldVertices[w + 1] = wy + y; + } + } + } +}; + +spine.BoundingBoxAttachment = function (name) { + this.name = name; + this.vertices = []; +}; +spine.BoundingBoxAttachment.prototype = { + type: spine.AttachmentType.boundingbox, + computeWorldVertices: function (x, y, bone, worldVertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var vertices = this.vertices; + for (var i = 0, n = vertices.length; i < n; i += 2) { + var px = vertices[i]; + var py = vertices[i + 1]; + worldVertices[i] = px * m00 + py * m01 + x; + worldVertices[i + 1] = px * m10 + py * m11 + y; + } + } +}; spine.AnimationStateData = function (skeletonData) { this.skeletonData = skeletonData; this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { - defaultMix: 0, + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -10786,109 +15216,199 @@ spine.AnimationStateData.prototype = { this.animationToMixTime[from.name + ":" + to.name] = duration; }, getMix: function (from, to) { - var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : this.defaultMix; + var key = from.name + ":" + to.name; + return this.animationToMixTime.hasOwnProperty(key) ? this.animationToMixTime[key] : this.defaultMix; } }; +spine.TrackEntry = function () {}; +spine.TrackEntry.prototype = { + next: null, previous: null, + animation: null, + loop: false, + delay: 0, time: 0, lastTime: -1, endTime: 0, + timeScale: 1, + mixTime: 0, mixDuration: 0, mix: 1, + onStart: null, onEnd: null, onComplete: null, onEvent: null +}; + spine.AnimationState = function (stateData) { this.data = stateData; - this.queue = []; + this.tracks = []; + this.events = []; }; spine.AnimationState.prototype = { - current: null, - previous: null, - currentTime: 0, - previousTime: 0, - currentLoop: false, - previousLoop: false, - mixTime: 0, - mixDuration: 0, + onStart: null, + onEnd: null, + onComplete: null, + onEvent: null, + timeScale: 1, update: function (delta) { - this.currentTime += delta; - this.previousTime += delta; - this.mixTime += delta; + delta *= this.timeScale; + for (var i = 0; i < this.tracks.length; i++) { + var current = this.tracks[i]; + if (!current) continue; - if (this.queue.length > 0) { - var entry = this.queue[0]; - if (this.currentTime >= entry.delay) { - this._setAnimation(entry.animation, entry.loop); - this.queue.shift(); + current.time += delta * current.timeScale; + if (current.previous) { + var previousDelta = delta * current.previous.timeScale; + current.previous.time += previousDelta; + current.mixTime += previousDelta; + } + + var next = current.next; + if (next) { + next.time = current.lastTime - next.delay; + if (next.time >= 0) this.setCurrent(i, next); + } else { + // End non-looping animation when it reaches its end time and there is no next entry. + if (!current.loop && current.lastTime >= current.endTime) this.clearTrack(i); } } }, apply: function (skeleton) { - if (!this.current) return; - if (this.previous) { - this.previous.apply(skeleton, this.previousTime, this.previousLoop); - var alpha = this.mixTime / this.mixDuration; - if (alpha >= 1) { - alpha = 1; - this.previous = null; + for (var i = 0; i < this.tracks.length; i++) { + var current = this.tracks[i]; + if (!current) continue; + + this.events.length = 0; + + var time = current.time; + var lastTime = current.lastTime; + var endTime = current.endTime; + var loop = current.loop; + if (!loop && time > endTime) time = endTime; + + var previous = current.previous; + if (!previous) { + if (current.mix == 1) + current.animation.apply(skeleton, current.lastTime, time, loop, this.events); + else + current.animation.mix(skeleton, current.lastTime, time, loop, this.events, current.mix); + } else { + var previousTime = previous.time; + if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; + previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null); + + var alpha = current.mixTime / current.mixDuration * current.mix; + if (alpha >= 1) { + alpha = 1; + current.previous = null; + } + current.animation.mix(skeleton, current.lastTime, time, loop, this.events, alpha); } - this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else - this.current.apply(skeleton, this.currentTime, this.currentLoop); + + for (var ii = 0, nn = this.events.length; ii < nn; ii++) { + var event = this.events[ii]; + if (current.onEvent) current.onEvent(i, event); + if (this.onEvent) this.onEvent(i, event); + } + + // Check if completed the animation or a loop iteration. + if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) { + var count = Math.floor(time / endTime); + if (current.onComplete) current.onComplete(i, count); + if (this.onComplete) this.onComplete(i, count); + } + + current.lastTime = current.time; + } }, - clearAnimation: function () { - this.previous = null; - this.current = null; - this.queue.length = 0; + clearTracks: function () { + for (var i = 0, n = this.tracks.length; i < n; i++) + this.clearTrack(i); + this.tracks.length = 0; }, - _setAnimation: function (animation, loop) { - this.previous = null; - if (animation && this.current) { - this.mixDuration = this.data.getMix(this.current, animation); - if (this.mixDuration > 0) { - this.mixTime = 0; - this.previous = this.current; - this.previousTime = this.currentTime; - this.previousLoop = this.currentLoop; + clearTrack: function (trackIndex) { + if (trackIndex >= this.tracks.length) return; + var current = this.tracks[trackIndex]; + if (!current) return; + + if (current.onEnd) current.onEnd(trackIndex); + if (this.onEnd) this.onEnd(trackIndex); + + this.tracks[trackIndex] = null; + }, + _expandToIndex: function (index) { + if (index < this.tracks.length) return this.tracks[index]; + while (index >= this.tracks.length) + this.tracks.push(null); + return null; + }, + setCurrent: function (index, entry) { + var current = this._expandToIndex(index); + if (current) { + var previous = current.previous; + current.previous = null; + + if (current.onEnd) current.onEnd(index); + if (this.onEnd) this.onEnd(index); + + entry.mixDuration = this.data.getMix(current.animation, entry.animation); + if (entry.mixDuration > 0) { + entry.mixTime = 0; + // If a mix is in progress, mix from the closest animation. + if (previous && current.mixTime / current.mixDuration < 0.5) + entry.previous = previous; + else + entry.previous = current; } } - this.current = animation; - this.currentLoop = loop; - this.currentTime = 0; + + this.tracks[index] = entry; + + if (entry.onStart) entry.onStart(index); + if (this.onStart) this.onStart(index); }, - /** @see #setAnimation(Animation, Boolean) */ - setAnimationByName: function (animationName, loop) { + setAnimationByName: function (trackIndex, animationName, loop) { var animation = this.data.skeletonData.findAnimation(animationName); if (!animation) throw "Animation not found: " + animationName; - this.setAnimation(animation, loop); + return this.setAnimation(trackIndex, animation, loop); }, - /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0. - * @param animation May be null. */ - setAnimation: function (animation, loop) { - this.queue.length = 0; - this._setAnimation(animation, loop); + /** Set the current animation. Any queued animations are cleared. */ + setAnimation: function (trackIndex, animation, loop) { + var entry = new spine.TrackEntry(); + entry.animation = animation; + entry.loop = loop; + entry.endTime = animation.duration; + this.setCurrent(trackIndex, entry); + return entry; }, - /** @see #addAnimation(Animation, Boolean, Number) */ - addAnimationByName: function (animationName, loop, delay) { + addAnimationByName: function (trackIndex, animationName, loop, delay) { var animation = this.data.skeletonData.findAnimation(animationName); if (!animation) throw "Animation not found: " + animationName; - this.addAnimation(animation, loop, delay); + return this.addAnimation(trackIndex, animation, loop, delay); }, /** Adds an animation to be played delay seconds after the current or last queued animation. * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ - addAnimation: function (animation, loop, delay) { - var entry = {}; + addAnimation: function (trackIndex, animation, loop, delay) { + var entry = new spine.TrackEntry(); entry.animation = animation; entry.loop = loop; + entry.endTime = animation.duration; - if (!delay || delay <= 0) { - var previousAnimation = this.queue.length ? this.queue[this.queue.length - 1].animation : this.current; - if (previousAnimation != null) - delay = previousAnimation.duration - this.data.getMix(previousAnimation, animation) + (delay || 0); + var last = this._expandToIndex(trackIndex); + if (last) { + while (last.next) + last = last.next; + last.next = entry; + } else + this.tracks[trackIndex] = entry; + + if (delay <= 0) { + if (last) + delay += last.endTime - this.data.getMix(last.animation, animation); else delay = 0; } entry.delay = delay; - this.queue.push(entry); + return entry; }, - /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ - isComplete: function () { - return !this.current || this.currentTime >= this.current.duration; + /** May be null. */ + getCurrent: function (trackIndex) { + if (trackIndex >= this.tracks.length) return null; + return this.tracks[trackIndex]; } }; @@ -10897,10 +15417,18 @@ spine.SkeletonJson = function (attachmentLoader) { }; spine.SkeletonJson.prototype = { scale: 1, - readSkeletonData: function (root) { - /*jshint -W069*/ - var skeletonData = new spine.SkeletonData(), - boneData; + readSkeletonData: function (root, name) { + var skeletonData = new spine.SkeletonData(); + skeletonData.name = name; + + // Skeleton. + var skeletonMap = root["skeleton"]; + if (skeletonMap) { + skeletonData.hash = skeletonMap["hash"]; + skeletonData.version = skeletonMap["spine"]; + skeletonData.width = skeletonMap["width"] || 0; + skeletonData.height = skeletonMap["height"] || 0; + } // Bones. var bones = root["bones"]; @@ -10911,33 +15439,60 @@ spine.SkeletonJson.prototype = { parent = skeletonData.findBone(boneMap["parent"]); if (!parent) throw "Parent bone not found: " + boneMap["parent"]; } - boneData = new spine.BoneData(boneMap["name"], parent); + var boneData = new spine.BoneData(boneMap["name"], parent); boneData.length = (boneMap["length"] || 0) * this.scale; boneData.x = (boneMap["x"] || 0) * this.scale; boneData.y = (boneMap["y"] || 0) * this.scale; boneData.rotation = (boneMap["rotation"] || 0); - boneData.scaleX = boneMap["scaleX"] || 1; - boneData.scaleY = boneMap["scaleY"] || 1; + boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1; + boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1; + boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true; + boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; skeletonData.bones.push(boneData); } + // IK constraints. + var ik = root["ik"]; + if (ik) { + for (var i = 0, n = ik.length; i < n; i++) { + var ikMap = ik[i]; + var ikConstraintData = new spine.IkConstraintData(ikMap["name"]); + + var bones = ikMap["bones"]; + for (var ii = 0, nn = bones.length; ii < nn; ii++) { + var bone = skeletonData.findBone(bones[ii]); + if (!bone) throw "IK bone not found: " + bones[ii]; + ikConstraintData.bones.push(bone); + } + + ikConstraintData.target = skeletonData.findBone(ikMap["target"]); + if (!ikConstraintData.target) throw "Target bone not found: " + ikMap["target"]; + + ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1; + ikConstraintData.mix = ikMap.hasOwnProperty("mix") ? ikMap["mix"] : 1; + + skeletonData.ikConstraints.push(ikConstraintData); + } + } + // Slots. var slots = root["slots"]; - for (i = 0, n = slots.length; i < n; i++) { + for (var i = 0, n = slots.length; i < n; i++) { var slotMap = slots[i]; - boneData = skeletonData.findBone(slotMap["bone"]); + var boneData = skeletonData.findBone(slotMap["bone"]); if (!boneData) throw "Slot bone not found: " + slotMap["bone"]; var slotData = new spine.SlotData(slotMap["name"], boneData); var color = slotMap["color"]; if (color) { - slotData.r = spine.SkeletonJson.toColor(color, 0); - slotData.g = spine.SkeletonJson.toColor(color, 1); - slotData.b = spine.SkeletonJson.toColor(color, 2); - slotData.a = spine.SkeletonJson.toColor(color, 3); + slotData.r = this.toColor(color, 0); + slotData.g = this.toColor(color, 1); + slotData.b = this.toColor(color, 2); + slotData.a = this.toColor(color, 3); } slotData.attachmentName = slotMap["attachment"]; + slotData.additiveBlending = slotMap["additive"] && slotMap["additive"] == "true"; skeletonData.slots.push(slotData); } @@ -10955,13 +15510,25 @@ spine.SkeletonJson.prototype = { for (var attachmentName in slotEntry) { if (!slotEntry.hasOwnProperty(attachmentName)) continue; var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]); - if (attachment != null) skin.addAttachment(slotIndex, attachmentName, attachment); + if (attachment) skin.addAttachment(slotIndex, attachmentName, attachment); } } skeletonData.skins.push(skin); if (skin.name == "default") skeletonData.defaultSkin = skin; } + // Events. + var events = root["events"]; + for (var eventName in events) { + if (!events.hasOwnProperty(eventName)) continue; + var eventMap = events[eventName]; + var eventData = new spine.EventData(eventName); + eventData.intValue = eventMap["int"] || 0; + eventData.floatValue = eventMap["float"] || 0; + eventData.stringValue = eventMap["string"] || null; + skeletonData.events.push(eventData); + } + // Animations. var animations = root["animations"]; for (var animationName in animations) { @@ -10972,40 +15539,152 @@ spine.SkeletonJson.prototype = { return skeletonData; }, readAttachment: function (skin, name, map) { - /*jshint -W069*/ name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; + var path = map["path"] || name; + var scale = this.scale; if (type == spine.AttachmentType.region) { - var attachment = new spine.RegionAttachment(); - attachment.x = (map["x"] || 0) * this.scale; - attachment.y = (map["y"] || 0) * this.scale; - attachment.scaleX = map["scaleX"] || 1; - attachment.scaleY = map["scaleY"] || 1; - attachment.rotation = map["rotation"] || 0; - attachment.width = (map["width"] || 32) * this.scale; - attachment.height = (map["height"] || 32) * this.scale; - attachment.updateOffset(); + var region = this.attachmentLoader.newRegionAttachment(skin, name, path); + if (!region) return null; + region.path = path; + region.x = (map["x"] || 0) * scale; + region.y = (map["y"] || 0) * scale; + region.scaleX = map.hasOwnProperty("scaleX") ? map["scaleX"] : 1; + region.scaleY = map.hasOwnProperty("scaleY") ? map["scaleY"] : 1; + region.rotation = map["rotation"] || 0; + region.width = (map["width"] || 0) * scale; + region.height = (map["height"] || 0) * scale; - attachment.rendererObject = {}; - attachment.rendererObject.name = name; - attachment.rendererObject.scale = {}; - attachment.rendererObject.scale.x = attachment.scaleX; - attachment.rendererObject.scale.y = attachment.scaleY; - attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + var color = map["color"]; + if (color) { + region.r = this.toColor(color, 0); + region.g = this.toColor(color, 1); + region.b = this.toColor(color, 2); + region.a = this.toColor(color, 3); + } + + region.updateOffset(); + return region; + } else if (type == spine.AttachmentType.mesh) { + var mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + mesh.vertices = this.getFloatArray(map, "vertices", scale); + mesh.triangles = this.getIntArray(map, "triangles"); + mesh.regionUVs = this.getFloatArray(map, "uvs", 1); + mesh.updateUVs(); + + color = map["color"]; + if (color) { + mesh.r = this.toColor(color, 0); + mesh.g = this.toColor(color, 1); + mesh.b = this.toColor(color, 2); + mesh.a = this.toColor(color, 3); + } + + mesh.hullLength = (map["hull"] || 0) * 2; + if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); + mesh.width = (map["width"] || 0) * scale; + mesh.height = (map["height"] || 0) * scale; + return mesh; + } else if (type == spine.AttachmentType.skinnedmesh) { + var mesh = this.attachmentLoader.newSkinnedMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + + var uvs = this.getFloatArray(map, "uvs", 1); + var vertices = this.getFloatArray(map, "vertices", 1); + var weights = []; + var bones = []; + for (var i = 0, n = vertices.length; i < n; ) { + var boneCount = vertices[i++] | 0; + bones[bones.length] = boneCount; + for (var nn = i + boneCount * 4; i < nn; ) { + bones[bones.length] = vertices[i]; + weights[weights.length] = vertices[i + 1] * scale; + weights[weights.length] = vertices[i + 2] * scale; + weights[weights.length] = vertices[i + 3]; + i += 4; + } + } + mesh.bones = bones; + mesh.weights = weights; + mesh.triangles = this.getIntArray(map, "triangles"); + mesh.regionUVs = uvs; + mesh.updateUVs(); + + color = map["color"]; + if (color) { + mesh.r = this.toColor(color, 0); + mesh.g = this.toColor(color, 1); + mesh.b = this.toColor(color, 2); + mesh.a = this.toColor(color, 3); + } + + mesh.hullLength = (map["hull"] || 0) * 2; + if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); + mesh.width = (map["width"] || 0) * scale; + mesh.height = (map["height"] || 0) * scale; + return mesh; + } else if (type == spine.AttachmentType.boundingbox) { + var attachment = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + var vertices = map["vertices"]; + for (var i = 0, n = vertices.length; i < n; i++) + attachment.vertices.push(vertices[i] * scale); return attachment; } - - throw "Unknown attachment type: " + type; + throw "Unknown attachment type: " + type; }, - readAnimation: function (name, map, skeletonData) { - /*jshint -W069*/ var timelines = []; var duration = 0; - var frameIndex, timeline, timelineName, valueMap, values, - i, n; + + var slots = map["slots"]; + for (var slotName in slots) { + if (!slots.hasOwnProperty(slotName)) continue; + var slotMap = slots[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + + for (var timelineName in slotMap) { + if (!slotMap.hasOwnProperty(timelineName)) continue; + var values = slotMap[timelineName]; + if (timelineName == "color") { + var timeline = new spine.ColorTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var color = valueMap["color"]; + var r = this.toColor(color, 0); + var g = this.toColor(color, 1); + var b = this.toColor(color, 2); + var a = this.toColor(color, 3); + timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); + + } else if (timelineName == "attachment") { + var timeline = new spine.AttachmentTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + + } else + throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; + } + } var bones = map["bones"]; for (var boneName in bones) { @@ -11014,24 +15693,25 @@ spine.SkeletonJson.prototype = { if (boneIndex == -1) throw "Bone not found: " + boneName; var boneMap = bones[boneName]; - for (timelineName in boneMap) { + for (var timelineName in boneMap) { if (!boneMap.hasOwnProperty(timelineName)) continue; - values = boneMap[timelineName]; + var values = boneMap[timelineName]; if (timelineName == "rotate") { - timeline = new spine.RotateTimeline(values.length); + var timeline = new spine.RotateTimeline(values.length); timeline.boneIndex = boneIndex; - frameIndex = 0; - for (i = 0, n = values.length; i < n; i++) { - valueMap = values[i]; + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); - spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + this.readCurve(timeline, frameIndex, valueMap); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); } else if (timelineName == "translate" || timelineName == "scale") { + var timeline; var timelineScale = 1; if (timelineName == "scale") timeline = new spine.ScaleTimeline(values.length); @@ -11041,82 +15721,215 @@ spine.SkeletonJson.prototype = { } timeline.boneIndex = boneIndex; - frameIndex = 0; - for (i = 0, n = values.length; i < n; i++) { - valueMap = values[i]; + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; var x = (valueMap["x"] || 0) * timelineScale; var y = (valueMap["y"] || 0) * timelineScale; timeline.setFrame(frameIndex, valueMap["time"], x, y); - spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + this.readCurve(timeline, frameIndex, valueMap); frameIndex++; } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); + } else if (timelineName == "flipX" || timelineName == "flipY") { + var x = timelineName == "flipX"; + var timeline = x ? new spine.FlipXTimeline(values.length) : new spine.FlipYTimeline(values.length); + timeline.boneIndex = boneIndex; + + var field = x ? "x" : "y"; + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap[field] || false); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } } - var slots = map["slots"]; - for (var slotName in slots) { - if (!slots.hasOwnProperty(slotName)) continue; - var slotMap = slots[slotName]; - var slotIndex = skeletonData.findSlotIndex(slotName); - for (timelineName in slotMap) { - if (!slotMap.hasOwnProperty(timelineName)) continue; - values = slotMap[timelineName]; - if (timelineName == "color") { - timeline = new spine.ColorTimeline(values.length); + var ikMap = map["ik"]; + for (var ikConstraintName in ikMap) { + if (!ikMap.hasOwnProperty(ikConstraintName)) continue; + var ikConstraint = skeletonData.findIkConstraint(ikConstraintName); + var values = ikMap[ikConstraintName]; + var timeline = new spine.IkConstraintTimeline(values.length); + timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var mix = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1; + var bendDirection = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; + timeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]); + } + + var ffd = map["ffd"]; + for (var skinName in ffd) { + var skin = skeletonData.findSkin(skinName); + var slotMap = ffd[skinName]; + for (slotName in slotMap) { + var slotIndex = skeletonData.findSlotIndex(slotName); + var meshMap = slotMap[slotName]; + for (var meshName in meshMap) { + var values = meshMap[meshName]; + var timeline = new spine.FfdTimeline(values.length); + var attachment = skin.getAttachment(slotIndex, meshName); + if (!attachment) throw "FFD attachment not found: " + meshName; timeline.slotIndex = slotIndex; + timeline.attachment = attachment; - frameIndex = 0; - for (i = 0, n = values.length; i < n; i++) { - valueMap = values[i]; - var color = valueMap["color"]; - var r = spine.SkeletonJson.toColor(color, 0); - var g = spine.SkeletonJson.toColor(color, 1); - var b = spine.SkeletonJson.toColor(color, 2); - var a = spine.SkeletonJson.toColor(color, 3); - timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); - spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + var isMesh = attachment.type == spine.AttachmentType.mesh; + var vertexCount; + if (isMesh) + vertexCount = attachment.vertices.length; + else + vertexCount = attachment.weights.length / 3 * 2; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var vertices; + if (!valueMap["vertices"]) { + if (isMesh) + vertices = attachment.vertices; + else { + vertices = []; + vertices.length = vertexCount; + } + } else { + var verticesValue = valueMap["vertices"]; + var vertices = []; + vertices.length = vertexCount; + var start = valueMap["offset"] || 0; + var nn = verticesValue.length; + if (this.scale == 1) { + for (var ii = 0; ii < nn; ii++) + vertices[ii + start] = verticesValue[ii]; + } else { + for (var ii = 0; ii < nn; ii++) + vertices[ii + start] = verticesValue[ii] * this.scale; + } + if (isMesh) { + var meshVertices = attachment.vertices; + for (var ii = 0, nn = vertices.length; ii < nn; ii++) + vertices[ii] += meshVertices[ii]; + } + } + + timeline.setFrame(frameIndex, valueMap["time"], vertices); + this.readCurve(timeline, frameIndex, valueMap); frameIndex++; } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); - - } else if (timelineName == "attachment") { - timeline = new spine.AttachmentTimeline(values.length); - timeline.slotIndex = slotIndex; - - frameIndex = 0; - for (i = 0, n = values.length; i < n; i++) { - valueMap = values[i]; - timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); - - } else - throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; + timelines[timelines.length] = timeline; + duration = Math.max(duration, timeline.frames[timeline.frameCount - 1]); + } } } + + var drawOrderValues = map["drawOrder"]; + if (!drawOrderValues) drawOrderValues = map["draworder"]; + if (drawOrderValues) { + var timeline = new spine.DrawOrderTimeline(drawOrderValues.length); + var slotCount = skeletonData.slots.length; + var frameIndex = 0; + for (var i = 0, n = drawOrderValues.length; i < n; i++) { + var drawOrderMap = drawOrderValues[i]; + var drawOrder = null; + if (drawOrderMap["offsets"]) { + drawOrder = []; + drawOrder.length = slotCount; + for (var ii = slotCount - 1; ii >= 0; ii--) + drawOrder[ii] = -1; + var offsets = drawOrderMap["offsets"]; + var unchanged = []; + unchanged.length = slotCount - offsets.length; + var originalIndex = 0, unchangedIndex = 0; + for (var ii = 0, nn = offsets.length; ii < nn; ii++) { + var offsetMap = offsets[ii]; + var slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); + if (slotIndex == -1) throw "Slot not found: " + offsetMap["slot"]; + // Collect unchanged items. + while (originalIndex != slotIndex) + unchanged[unchangedIndex++] = originalIndex++; + // Set changed items. + drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; + } + // Collect remaining unchanged items. + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + // Fill in unchanged items. + for (var ii = slotCount - 1; ii >= 0; ii--) + if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + } + timeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + + var events = map["events"]; + if (events) { + var timeline = new spine.EventTimeline(events.length); + var frameIndex = 0; + for (var i = 0, n = events.length; i < n; i++) { + var eventMap = events[i]; + var eventData = skeletonData.findEvent(eventMap["name"]); + if (!eventData) throw "Event not found: " + eventMap["name"]; + var event = new spine.Event(eventData); + event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; + event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; + event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; + timeline.setFrame(frameIndex++, eventMap["time"], event); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + skeletonData.animations.push(new spine.Animation(name, timelines, duration)); + }, + readCurve: function (timeline, frameIndex, valueMap) { + var curve = valueMap["curve"]; + if (!curve) + timeline.curves.setLinear(frameIndex); + else if (curve == "stepped") + timeline.curves.setStepped(frameIndex); + else if (curve instanceof Array) + timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); + }, + toColor: function (hexString, colorIndex) { + if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString; + return parseInt(hexString.substring(colorIndex * 2, (colorIndex * 2) + 2), 16) / 255; + }, + getFloatArray: function (map, name, scale) { + var list = map[name]; + var values = new spine.Float32Array(list.length); + var i = 0, n = list.length; + if (scale == 1) { + for (; i < n; i++) + values[i] = list[i]; + } else { + for (; i < n; i++) + values[i] = list[i] * scale; + } + return values; + }, + getIntArray: function (map, name) { + var list = map[name]; + var values = new spine.Uint16Array(list.length); + for (var i = 0, n = list.length; i < n; i++) + values[i] = list[i] | 0; + return values; } }; -spine.SkeletonJson.readCurve = function (timeline, frameIndex, valueMap) { - /*jshint -W069*/ - var curve = valueMap["curve"]; - if (!curve) return; - if (curve == "stepped") - timeline.curves.setStepped(frameIndex); - else if (curve instanceof Array) - timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); -}; -spine.SkeletonJson.toColor = function (hexString, colorIndex) { - if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString; - return parseInt(hexString.substring(colorIndex * 2, 2), 16) / 255; -}; spine.Atlas = function (atlasText, textureLoader) { this.textureLoader = textureLoader; @@ -11129,7 +15942,7 @@ spine.Atlas = function (atlasText, textureLoader) { var page = null; while (true) { var line = reader.readLine(); - if (line == null) break; + if (line === null) break; line = reader.trim(line); if (!line.length) page = null; @@ -11137,7 +15950,12 @@ spine.Atlas = function (atlasText, textureLoader) { page = new spine.AtlasPage(); page.name = line; - page.format = spine.Atlas.Format[reader.readValue()]; + if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. + page.width = parseInt(tuple[0]); + page.height = parseInt(tuple[1]); + reader.readTuple(tuple); + } + page.format = spine.Atlas.Format[tuple[0]]; reader.readTuple(tuple); page.minFilter = spine.Atlas.TextureFilter[tuple[0]]; @@ -11153,7 +15971,7 @@ spine.Atlas = function (atlasText, textureLoader) { else if (direction == "xy") page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat; - textureLoader.load(page, line); + textureLoader.load(page, line, this); this.pages.push(page); @@ -11165,12 +15983,12 @@ spine.Atlas = function (atlasText, textureLoader) { region.rotate = reader.readValue() == "true"; reader.readTuple(tuple); - var x = parseInt(tuple[0], 10); - var y = parseInt(tuple[1], 10); + var x = parseInt(tuple[0]); + var y = parseInt(tuple[1]); reader.readTuple(tuple); - var width = parseInt(tuple[0], 10); - var height = parseInt(tuple[1], 10); + var width = parseInt(tuple[0]); + var height = parseInt(tuple[1]); region.u = x / page.width; region.v = y / page.height; @@ -11187,23 +16005,23 @@ spine.Atlas = function (atlasText, textureLoader) { region.height = Math.abs(height); if (reader.readTuple(tuple) == 4) { // split is optional - region.splits = [parseInt(tuple[0], 10), parseInt(tuple[1], 10), parseInt(tuple[2], 10), parseInt(tuple[3], 10)]; + region.splits = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits - region.pads = [parseInt(tuple[0], 10), parseInt(tuple[1], 10), parseInt(tuple[2], 10), parseInt(tuple[3], 10)]; + region.pads = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; reader.readTuple(tuple); } } - region.originalWidth = parseInt(tuple[0], 10); - region.originalHeight = parseInt(tuple[1], 10); + region.originalWidth = parseInt(tuple[0]); + region.originalHeight = parseInt(tuple[1]); reader.readTuple(tuple); - region.offsetX = parseInt(tuple[0], 10); - region.offsetY = parseInt(tuple[1], 10); + region.offsetX = parseInt(tuple[0]); + region.offsetY = parseInt(tuple[1]); - region.index = parseInt(reader.readValue(), 10); + region.index = parseInt(reader.readValue()); this.regions.push(region); } @@ -11290,7 +16108,7 @@ spine.AtlasRegion.prototype = { index: 0, rotate: false, splits: null, - pads: null, + pads: null }; spine.AtlasReader = function (text) { @@ -11311,53 +16129,291 @@ spine.AtlasReader.prototype = { if (colon == -1) throw "Invalid line: " + line; return this.trim(line.substring(colon + 1)); }, - /** Returns the number of tuple values read (2 or 4). */ + /** Returns the number of tuple values read (1, 2 or 4). */ readTuple: function (tuple) { var line = this.readLine(); var colon = line.indexOf(":"); if (colon == -1) throw "Invalid line: " + line; - var i = 0, lastMatch= colon + 1; + var i = 0, lastMatch = colon + 1; for (; i < 3; i++) { var comma = line.indexOf(",", lastMatch); - if (comma == -1) { - if (!i) throw "Invalid line: " + line; - break; - } + if (comma == -1) break; tuple[i] = this.trim(line.substr(lastMatch, comma - lastMatch)); lastMatch = comma + 1; } tuple[i] = this.trim(line.substring(lastMatch)); return i + 1; } -} +}; spine.AtlasAttachmentLoader = function (atlas) { this.atlas = atlas; -} +}; spine.AtlasAttachmentLoader.prototype = { - newAttachment: function (skin, type, name) { - switch (type) { - case spine.AttachmentType.region: - var region = this.atlas.findRegion(name); - if (!region) throw "Region not found in atlas: " + name + " (" + type + ")"; - var attachment = new spine.RegionAttachment(name); - attachment.rendererObject = region; - attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); - attachment.regionOffsetX = region.offsetX; - attachment.regionOffsetY = region.offsetY; - attachment.regionWidth = region.width; - attachment.regionHeight = region.height; - attachment.regionOriginalWidth = region.originalWidth; - attachment.regionOriginalHeight = region.originalHeight; - return attachment; - } - throw "Unknown attachment type: " + type; + newRegionAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw "Region not found in atlas: " + path + " (region attachment: " + name + ")"; + var attachment = new spine.RegionAttachment(name); + attachment.rendererObject = region; + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newMeshAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw "Region not found in atlas: " + path + " (mesh attachment: " + name + ")"; + var attachment = new spine.MeshAttachment(name); + attachment.rendererObject = region; + attachment.regionU = region.u; + attachment.regionV = region.v; + attachment.regionU2 = region.u2; + attachment.regionV2 = region.v2; + attachment.regionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newSkinnedMeshAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw "Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")"; + var attachment = new spine.SkinnedMeshAttachment(name); + attachment.rendererObject = region; + attachment.regionU = region.u; + attachment.regionV = region.v; + attachment.regionU2 = region.u2; + attachment.regionV2 = region.v2; + attachment.regionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newBoundingBoxAttachment: function (skin, name) { + return new spine.BoundingBoxAttachment(name); } -} +}; + +spine.SkeletonBounds = function () { + this.polygonPool = []; + this.polygons = []; + this.boundingBoxes = []; +}; +spine.SkeletonBounds.prototype = { + minX: 0, minY: 0, maxX: 0, maxY: 0, + update: function (skeleton, updateAabb) { + var slots = skeleton.slots; + var slotCount = slots.length; + var x = skeleton.x, y = skeleton.y; + var boundingBoxes = this.boundingBoxes; + var polygonPool = this.polygonPool; + var polygons = this.polygons; + + boundingBoxes.length = 0; + for (var i = 0, n = polygons.length; i < n; i++) + polygonPool.push(polygons[i]); + polygons.length = 0; + + for (var i = 0; i < slotCount; i++) { + var slot = slots[i]; + var boundingBox = slot.attachment; + if (boundingBox.type != spine.AttachmentType.boundingbox) continue; + boundingBoxes.push(boundingBox); + + var poolCount = polygonPool.length, polygon; + if (poolCount > 0) { + polygon = polygonPool[poolCount - 1]; + polygonPool.splice(poolCount - 1, 1); + } else + polygon = []; + polygons.push(polygon); + + polygon.length = boundingBox.vertices.length; + boundingBox.computeWorldVertices(x, y, slot.bone, polygon); + } + + if (updateAabb) this.aabbCompute(); + }, + aabbCompute: function () { + var polygons = this.polygons; + var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE; + for (var i = 0, n = polygons.length; i < n; i++) { + var vertices = polygons[i]; + for (var ii = 0, nn = vertices.length; ii < nn; ii += 2) { + var x = vertices[ii]; + var y = vertices[ii + 1]; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + } + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + }, + /** Returns true if the axis aligned bounding box contains the point. */ + aabbContainsPoint: function (x, y) { + return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY; + }, + /** Returns true if the axis aligned bounding box intersects the line segment. */ + aabbIntersectsSegment: function (x1, y1, x2, y2) { + var minX = this.minX, minY = this.minY, maxX = this.maxX, maxY = this.maxY; + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) + return false; + var m = (y2 - y1) / (x2 - x1); + var y = m * (minX - x1) + y1; + if (y > minY && y < maxY) return true; + y = m * (maxX - x1) + y1; + if (y > minY && y < maxY) return true; + var x = (minY - y1) / m + x1; + if (x > minX && x < maxX) return true; + x = (maxY - y1) / m + x1; + if (x > minX && x < maxX) return true; + return false; + }, + /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ + aabbIntersectsSkeleton: function (bounds) { + return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY; + }, + /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more + * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */ + containsPoint: function (x, y) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (this.polygonContainsPoint(polygons[i], x, y)) return this.boundingBoxes[i]; + return null; + }, + /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually + * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */ + intersectsSegment: function (x1, y1, x2, y2) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return this.boundingBoxes[i]; + return null; + }, + /** Returns true if the polygon contains the point. */ + polygonContainsPoint: function (polygon, x, y) { + var nn = polygon.length; + var prevIndex = nn - 2; + var inside = false; + for (var ii = 0; ii < nn; ii += 2) { + var vertexY = polygon[ii + 1]; + var prevY = polygon[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + var vertexX = polygon[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (polygon[prevIndex] - vertexX) < x) inside = !inside; + } + prevIndex = ii; + } + return inside; + }, + /** Returns true if the polygon contains the line segment. */ + polygonIntersectsSegment: function (polygon, x1, y1, x2, y2) { + var nn = polygon.length; + var width12 = x1 - x2, height12 = y1 - y2; + var det1 = x1 * y2 - y1 * x2; + var x3 = polygon[nn - 2], y3 = polygon[nn - 1]; + for (var ii = 0; ii < nn; ii += 2) { + var x4 = polygon[ii], y4 = polygon[ii + 1]; + var det2 = x3 * y4 - y3 * x4; + var width34 = x3 - x4, height34 = y3 - y4; + var det3 = width12 * height34 - height12 * width34; + var x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + var y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true; + } + x3 = x4; + y3 = y4; + } + return false; + }, + getPolygon: function (attachment) { + var index = this.boundingBoxes.indexOf(attachment); + return index == -1 ? null : this.polygons[index]; + }, + getWidth: function () { + return this.maxX - this.minX; + }, + getHeight: function () { + return this.maxY - this.minY; + } +}; + +/* Esoteric Software SPINE wrapper for pixi.js */ spine.Bone.yDown = true; PIXI.AnimCache = {}; +/** + * Supporting class to load images from spine atlases as per spine spec. + * + * @class SpineTextureLoader + * @uses EventTarget + * @constructor + * @param basePath {String} Tha base path where to look for the images to be loaded + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.SpineTextureLoader = function(basePath, crossorigin) +{ + PIXI.EventTarget.call(this); + + this.basePath = basePath; + this.crossorigin = crossorigin; + this.loadingCount = 0; +}; + +/* constructor */ +PIXI.SpineTextureLoader.prototype = PIXI.SpineTextureLoader; + +/** + * Starts loading a base texture as per spine specification + * + * @method load + * @param page {spine.AtlasPage} Atlas page to which texture belongs + * @param file {String} The file to load, this is just the file path relative to the base path configured in the constructor + */ +PIXI.SpineTextureLoader.prototype.load = function(page, file) +{ + page.rendererObject = PIXI.BaseTexture.fromImage(this.basePath + '/' + file, this.crossorigin); + if (!page.rendererObject.hasLoaded) + { + var scope = this; + ++scope.loadingCount; + page.rendererObject.addEventListener('loaded', function(){ + --scope.loadingCount; + scope.dispatchEvent({ + type: 'loadedBaseTexture', + content: scope + }); + }); + } +}; + +/** + * Unloads a previously loaded texture as per spine specification + * + * @method unload + * @param texture {BaseTexture} Texture object to destroy + */ +PIXI.SpineTextureLoader.prototype.unload = function(texture) +{ + texture.destroy(true); +}; + /** * A class that enables the you to import and run your spine animations in pixi. * Spine animation data needs to be loaded using the PIXI.AssetLoader or PIXI.SpineLoader before it can be used by this class @@ -11374,7 +16430,7 @@ PIXI.Spine = function (url) { this.spineData = PIXI.AnimCache[url]; if (!this.spineData) { - throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); + throw new Error('Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: ' + url); } this.skeleton = new spine.Skeleton(this.spineData); @@ -11391,31 +16447,67 @@ PIXI.Spine = function (url) { var slotContainer = new PIXI.DisplayObjectContainer(); this.slotContainers.push(slotContainer); this.addChild(slotContainer); - if (!(attachment instanceof spine.RegionAttachment)) { + + if (attachment instanceof spine.RegionAttachment) + { + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } + else if (attachment instanceof spine.MeshAttachment) + { + var mesh = this.createMesh(slot, attachment); + slot.currentMesh = mesh; + slot.currentMeshName = attachment.name; + slotContainer.addChild(mesh); + } + else + { continue; } - var spriteName = attachment.rendererObject.name; - var sprite = this.createSprite(slot, attachment.rendererObject); - slot.currentSprite = sprite; - slot.currentSpriteName = spriteName; - slotContainer.addChild(sprite); + } + + this.autoUpdate = true; }; PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; -/* - * Updates the object transform for rendering +/** + * If this flag is set to true, the spine animation will be autoupdated every time + * the object id drawn. The down side of this approach is that the delta time is + * automatically calculated and you could miss out on cool effects like slow motion, + * pause, skip ahead and the sorts. Most of these effects can be achieved even with + * autoupdate enabled but are harder to achieve. * - * @method updateTransform - * @private + * @property autoUpdate + * @type { Boolean } + * @default true */ -PIXI.Spine.prototype.updateTransform = function () { - this.lastTime = this.lastTime || Date.now(); - var timeDelta = (Date.now() - this.lastTime) * 0.001; - this.lastTime = Date.now(); - this.state.update(timeDelta); +Object.defineProperty(PIXI.Spine.prototype, 'autoUpdate', { + get: function() + { + return (this.updateTransform === PIXI.Spine.prototype.autoUpdateTransform); + }, + + set: function(value) + { + this.updateTransform = value ? PIXI.Spine.prototype.autoUpdateTransform : PIXI.DisplayObjectContainer.prototype.updateTransform; + } +}); + +/** + * Update the spine skeleton and its animations by delta time (dt) + * + * @method update + * @param dt {Number} Delta time. Time by which the animation should be updated + */ +PIXI.Spine.prototype.update = function(dt) +{ + this.state.update(dt); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); @@ -11424,79 +16516,181 @@ PIXI.Spine.prototype.updateTransform = function () { var slot = drawOrder[i]; var attachment = slot.attachment; var slotContainer = this.slotContainers[i]; - if (!(attachment instanceof spine.RegionAttachment)) { + + if (!attachment) + { slotContainer.visible = false; continue; } - if (attachment.rendererObject) { - if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { - var spriteName = attachment.rendererObject.name; - if (slot.currentSprite !== undefined) { - slot.currentSprite.visible = false; + var type = attachment.type; + if (type === spine.AttachmentType.region) + { + if (attachment.rendererObject) + { + if (!slot.currentSpriteName || slot.currentSpriteName !== attachment.name) + { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) + { + slot.currentSprite.visible = false; + } + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) + { + slot.sprites[spriteName].visible = true; + } + else + { + var sprite = this.createSprite(slot, attachment); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - slot.sprites = slot.sprites || {}; - if (slot.sprites[spriteName] !== undefined) { - slot.sprites[spriteName].visible = true; - } else { - var sprite = this.createSprite(slot, attachment.rendererObject); - slotContainer.addChild(sprite); - } - slot.currentSprite = slot.sprites[spriteName]; - slot.currentSpriteName = spriteName; } + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * spine.degRad); + + slot.currentSprite.tint = PIXI.rgb2hex([slot.r,slot.g,slot.b]); + } + else if (type === spine.AttachmentType.skinnedmesh) + { + if (!slot.currentMeshName || slot.currentMeshName !== attachment.name) + { + var meshName = attachment.name; + if (slot.currentMesh !== undefined) + { + slot.currentMesh.visible = false; + } + + slot.meshes = slot.meshes || {}; + + if (slot.meshes[meshName] !== undefined) + { + slot.meshes[meshName].visible = true; + } + else + { + var mesh = this.createMesh(slot, attachment); + slotContainer.addChild(mesh); + } + + slot.currentMesh = slot.meshes[meshName]; + slot.currentMeshName = meshName; + } + + attachment.computeWorldVertices(slot.bone.skeleton.x, slot.bone.skeleton.y, slot, slot.currentMesh.vertices); + + } + else + { + slotContainer.visible = false; + continue; } slotContainer.visible = true; - var bone = slot.bone; - - slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; - slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; - slotContainer.scale.x = bone.worldScaleX; - slotContainer.scale.y = bone.worldScaleY; - - slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + slotContainer.alpha = slot.a; } +}; + +/** + * When autoupdate is set to yes this function is used as pixi's updateTransform function + * + * @method autoUpdateTransform + * @private + */ +PIXI.Spine.prototype.autoUpdateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + + this.update(timeDelta); PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; +/** + * Create a new sprite to be used with spine.RegionAttachment + * + * @method createSprite + * @param slot {spine.Slot} The slot to which the attachment is parented + * @param attachment {spine.RegionAttachment} The attachment that the sprite will represent + * @private + */ +PIXI.Spine.prototype.createSprite = function (slot, attachment) { + var descriptor = attachment.rendererObject; + var baseTexture = descriptor.page.rendererObject; + var spriteRect = new PIXI.Rectangle(descriptor.x, + descriptor.y, + descriptor.rotate ? descriptor.height : descriptor.width, + descriptor.rotate ? descriptor.width : descriptor.height); + var spriteTexture = new PIXI.Texture(baseTexture, spriteRect); + var sprite = new PIXI.Sprite(spriteTexture); -PIXI.Spine.prototype.createSprite = function (slot, descriptor) { - var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); - sprite.scale = descriptor.scale; - sprite.rotation = descriptor.rotation; + var baseRotation = descriptor.rotate ? Math.PI * 0.5 : 0.0; + sprite.scale.set(descriptor.width / descriptor.originalWidth, descriptor.height / descriptor.originalHeight); + sprite.rotation = baseRotation - (attachment.rotation * spine.degRad); sprite.anchor.x = sprite.anchor.y = 0.5; slot.sprites = slot.sprites || {}; slot.sprites[descriptor.name] = sprite; return sprite; }; - + +PIXI.Spine.prototype.createMesh = function (slot, attachment) { + var descriptor = attachment.rendererObject; + var baseTexture = descriptor.page.rendererObject; + var texture = new PIXI.Texture(baseTexture); + + var strip = new PIXI.Strip(texture); + strip.drawMode = PIXI.Strip.DrawModes.TRIANGLES; + strip.canvasPadding = 1.5; + + strip.vertices = new PIXI.Float32Array(attachment.uvs.length); + strip.uvs = attachment.uvs; + strip.indices = attachment.triangles; + + slot.meshes = slot.meshes || {}; + slot.meshes[attachment.name] = strip; + + return strip; +}; + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ PIXI.BaseTextureCache = {}; -PIXI.texturesToUpdate = []; -PIXI.texturesToDestroy = []; PIXI.BaseTextureCacheIdGenerator = 0; /** - * A texture stores the information that represents an image. All textures have a base texture + * A texture stores the information that represents an image. All textures have a base texture. * * @class BaseTexture * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) - * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values */ PIXI.BaseTexture = function(source, scaleMode) { - PIXI.EventTarget.call( this ); - + /** + * The Resolution of the texture. + * + * @property resolution + * @type Number + */ + this.resolution = 1; + /** * [read-only] The width of the base texture set when the image has loaded * @@ -11517,14 +16711,15 @@ PIXI.BaseTexture = function(source, scaleMode) /** * The scale mode to apply when scaling this texture + * * @property scaleMode - * @type PIXI.scaleModes + * @type {Number} * @default PIXI.scaleModes.LINEAR */ this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; /** - * [read-only] Describes if the base texture has loaded or not + * [read-only] Set to true once the base texture has loaded * * @property hasLoaded * @type Boolean @@ -11533,52 +16728,108 @@ PIXI.BaseTexture = function(source, scaleMode) this.hasLoaded = false; /** - * The source that is loaded to create the texture + * The image source that is used to create the texture. * * @property source * @type Image */ this.source = source; + this._UID = PIXI._UID++; + + /** + * Controls if RGB channels should be pre-multiplied by Alpha (WebGL only) + * + * @property premultipliedAlpha + * @type Boolean + * @default true + */ + this.premultipliedAlpha = true; + + // used for webGL + + /** + * @property _glTextures + * @type Array + * @private + */ + this._glTextures = []; + + /** + * + * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used + * Also the texture must be a power of two size to work + * + * @property mipmap + * @type {Boolean} + */ + this.mipmap = false; + + // used for webGL texture updating... + // TODO - this needs to be addressed + + /** + * @property _dirty + * @type Array + * @private + */ + this._dirty = [ + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + ]; + if(!source)return; - if(this.source.complete || this.source.getContext) + if((this.source.complete || this.source.getContext) && this.source.width && this.source.height) { this.hasLoaded = true; - this.width = this.source.width; - this.height = this.source.height; - - PIXI.texturesToUpdate.push(this); + this.width = this.source.naturalWidth || this.source.width; + this.height = this.source.naturalHeight || this.source.height; + this.dirty(); } else { - var scope = this; + this.source.onload = function() { scope.hasLoaded = true; - scope.width = scope.source.width; - scope.height = scope.source.height; + scope.width = scope.source.naturalWidth || scope.source.width; + scope.height = scope.source.naturalHeight || scope.source.height; + + scope.dirty(); // add it to somewhere... - PIXI.texturesToUpdate.push(scope); scope.dispatchEvent( { type: 'loaded', content: scope } ); }; + + this.source.onerror = function() { + scope.dispatchEvent( { type: 'error', content: scope } ); + }; } + /** + * @property imageUrl + * @type String + */ this.imageUrl = null; + + /** + * @property _powerOf2 + * @type Boolean + * @private + */ this._powerOf2 = false; - //TODO will be used for futer pixi 1.5... - this.id = PIXI.BaseTextureCacheIdGenerator++; - - // used for webGL - this._glTextures = []; - }; PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; +PIXI.EventTarget.mixin(PIXI.BaseTexture.prototype); + /** * Destroys this base texture * @@ -11589,11 +16840,17 @@ PIXI.BaseTexture.prototype.destroy = function() if(this.imageUrl) { delete PIXI.BaseTextureCache[this.imageUrl]; + delete PIXI.TextureCache[this.imageUrl]; this.imageUrl = null; - this.source.src = null; + if (!navigator.isCocoonJS) this.source.src = ''; + } + else if (this.source && this.source._pixiId) + { + delete PIXI.BaseTextureCache[this.source._pixiId]; } this.source = null; - PIXI.texturesToDestroy.push(this); + + this.unloadFromGPU(); }; /** @@ -11610,20 +16867,62 @@ PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) }; /** - * Helper function that returns a base texture based on an image url - * If the image is not in the base texture cache it will be created and loaded + * Sets all glTextures to be dirty. + * + * @method dirty + */ +PIXI.BaseTexture.prototype.dirty = function() +{ + for (var i = 0; i < this._glTextures.length; i++) + { + this._dirty[i] = true; + } +}; + +/** + * Removes the base texture from the GPU, useful for managing resources on the GPU. + * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. + * + * @method unloadFromGPU + */ +PIXI.BaseTexture.prototype.unloadFromGPU = function() +{ + this.dirty(); + + // delete the webGL textures if any. + for (var i = this._glTextures.length - 1; i >= 0; i--) + { + var glTexture = this._glTextures[i]; + var gl = PIXI.glContexts[i]; + + if(gl && glTexture) + { + gl.deleteTexture(glTexture); + } + + } + + this._glTextures.length = 0; + + this.dirty(); +}; + +/** + * Helper function that creates a base texture from the given image url. + * If the image is not in the base texture cache it will be created and loaded. * * @static * @method fromImage * @param imageUrl {String} The image url of the texture - * @param crossorigin {Boolean} - * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts + * @param crossorigin {Boolean} + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @return BaseTexture */ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) { var baseTexture = PIXI.BaseTextureCache[imageUrl]; - crossorigin = !crossorigin; + + if(crossorigin === undefined && imageUrl.indexOf('data:') === -1) crossorigin = true; if(!baseTexture) { @@ -11634,15 +16933,31 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) { image.crossOrigin = ''; } + image.src = imageUrl; baseTexture = new PIXI.BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; PIXI.BaseTextureCache[imageUrl] = baseTexture; + + // if there is an @2x at the end of the url we are going to assume its a highres image + if( imageUrl.indexOf(PIXI.RETINA_PREFIX + '.') !== -1) + { + baseTexture.resolution = 2; + } } return baseTexture; }; +/** + * Helper function that creates a base texture from the given canvas element. + * + * @static + * @method fromCanvas + * @param canvas {Canvas} The canvas element source of the texture + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return BaseTexture + */ PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode) { if(!canvas._pixiId) @@ -11661,8 +16976,6 @@ PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode) return baseTexture; }; - - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -11674,29 +16987,39 @@ PIXI.TextureCacheIdGenerator = 0; /** * A texture stores the information that represents an image or part of an image. It cannot be added - * to the display list directly. To do this use PIXI.Sprite. If no frame is provided then the whole image is used + * to the display list directly. Instead use it as the texture for a PIXI.Sprite. If no frame is provided then the whole image is used. * * @class Texture * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from * @param frame {Rectangle} The rectangle frame of the texture to show + * @param [crop] {Rectangle} The area of original texture + * @param [trim] {Rectangle} Trimmed texture rectangle */ -PIXI.Texture = function(baseTexture, frame) +PIXI.Texture = function(baseTexture, frame, crop, trim) { - PIXI.EventTarget.call( this ); + /** + * Does this Texture have any frame data assigned to it? + * + * @property noFrame + * @type Boolean + */ + this.noFrame = false; - if(!frame) + if (!frame) { this.noFrame = true; frame = new PIXI.Rectangle(0,0,1,1); } - if(baseTexture instanceof PIXI.Texture) + if (baseTexture instanceof PIXI.Texture) + { baseTexture = baseTexture.baseTexture; + } /** - * The base texture of that this texture uses + * The base texture that this texture uses. * * @property baseTexture * @type BaseTexture @@ -11712,47 +17035,93 @@ PIXI.Texture = function(baseTexture, frame) this.frame = frame; /** - * The trim point + * The texture trim data. * * @property trim * @type Rectangle */ - this.trim = null; - - this.scope = this; + this.trim = trim; - if(baseTexture.hasLoaded) + /** + * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. + * + * @property valid + * @type Boolean + */ + this.valid = false; + + /** + * This will let a renderer know that a texture has been updated (used mainly for webGL uv updates) + * + * @property requiresUpdate + * @type Boolean + */ + this.requiresUpdate = false; + + /** + * The WebGL UV data cache. + * + * @property _uvs + * @type Object + * @private + */ + this._uvs = null; + + /** + * The width of the Texture in pixels. + * + * @property width + * @type Number + */ + this.width = 0; + + /** + * The height of the Texture in pixels. + * + * @property height + * @type Number + */ + this.height = 0; + + /** + * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, + * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) + * + * @property crop + * @type Rectangle + */ + this.crop = crop || new PIXI.Rectangle(0, 0, 1, 1); + + if (baseTexture.hasLoaded) { - if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); - + if (this.noFrame) frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height); this.setFrame(frame); } else { - var scope = this; - baseTexture.addEventListener('loaded', function(){ scope.onBaseTextureLoaded(); }); + baseTexture.addEventListener('loaded', this.onBaseTextureLoaded.bind(this)); } }; PIXI.Texture.prototype.constructor = PIXI.Texture; +PIXI.EventTarget.mixin(PIXI.Texture.prototype); /** * Called when the base texture is loaded * * @method onBaseTextureLoaded - * @param event * @private */ PIXI.Texture.prototype.onBaseTextureLoaded = function() { var baseTexture = this.baseTexture; - baseTexture.removeEventListener( 'loaded', this.onLoaded ); + baseTexture.removeEventListener('loaded', this.onLoaded); + + if (this.noFrame) this.frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height); - if(this.noFrame)this.frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); - this.setFrame(this.frame); - this.scope.dispatchEvent( { type: 'update', content: this } ); + this.dispatchEvent( { type: 'update', content: this } ); }; /** @@ -11763,42 +17132,63 @@ PIXI.Texture.prototype.onBaseTextureLoaded = function() */ PIXI.Texture.prototype.destroy = function(destroyBase) { - if(destroyBase) this.baseTexture.destroy(); + if (destroyBase) this.baseTexture.destroy(); + + this.valid = false; }; /** - * Specifies the rectangle region of the baseTexture + * Specifies the region of the baseTexture that this texture will use. * * @method setFrame * @param frame {Rectangle} The frame of the texture to set it to */ PIXI.Texture.prototype.setFrame = function(frame) { + this.noFrame = false; + this.frame = frame; this.width = frame.width; this.height = frame.height; - if(frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + this.crop.x = frame.x; + this.crop.y = frame.y; + this.crop.width = frame.width; + this.crop.height = frame.height; + + if (!this.trim && (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)) { throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); } - this.updateFrame = true; + this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - PIXI.Texture.frameUpdates.push(this); + if (this.trim) + { + this.width = this.trim.width; + this.height = this.trim.height; + this.frame.width = this.trim.width; + this.frame.height = this.trim.height; + } + + if (this.valid) this._updateUvs(); - - //this.dispatchEvent( { type: 'update', content: this } ); }; -PIXI.Texture.prototype._updateWebGLuvs = function() +/** + * Updates the internal WebGL UV cache. + * + * @method _updateUvs + * @private + */ +PIXI.Texture.prototype._updateUvs = function() { if(!this._uvs)this._uvs = new PIXI.TextureUvs(); - var frame = this.frame; + var frame = this.crop; var tw = this.baseTexture.width; var th = this.baseTexture.height; - + this._uvs.x0 = frame.x / tw; this._uvs.y0 = frame.y / th; @@ -11813,13 +17203,14 @@ PIXI.Texture.prototype._updateWebGLuvs = function() }; /** - * Helper function that returns a texture based on an image url - * If the image is not in the texture cache it will be created and loaded + * Helper function that creates a Texture object from the given image url. + * If the image is not in the texture cache it will be created and loaded. * * @static * @method fromImage * @param imageUrl {String} The image url of the texture * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @return Texture */ PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode) @@ -11836,8 +17227,8 @@ PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode) }; /** - * Helper function that returns a texture based on a frame id - * If the frame id is not in the texture cache an error will be thrown + * Helper function that returns a Texture objected based on the given frame id. + * If the frame id is not in the texture cache an error will be thrown. * * @static * @method fromFrame @@ -11852,12 +17243,12 @@ PIXI.Texture.fromFrame = function(frameId) }; /** - * Helper function that returns a texture based on a canvas element - * If the canvas is not in the texture cache it will be created and loaded + * Helper function that creates a new a Texture based on the given canvas element. * * @static * @method fromCanvas * @param canvas {Canvas} The canvas element source of the texture + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @return Texture */ PIXI.Texture.fromCanvas = function(canvas, scaleMode) @@ -11868,14 +17259,13 @@ PIXI.Texture.fromCanvas = function(canvas, scaleMode) }; - /** - * Adds a texture to the textureCache. + * Adds a texture to the global PIXI.TextureCache. This cache is shared across the whole PIXI object. * * @static * @method addTextureToCache - * @param texture {Texture} - * @param id {String} the id that the texture will be stored against. + * @param texture {Texture} The Texture to add to the cache. + * @param id {String} The id that the texture will be stored against. */ PIXI.Texture.addTextureToCache = function(texture, id) { @@ -11883,23 +17273,21 @@ PIXI.Texture.addTextureToCache = function(texture, id) }; /** - * Remove a texture from the textureCache. + * Remove a texture from the global PIXI.TextureCache. * * @static * @method removeTextureFromCache - * @param id {String} the id of the texture to be removed - * @return {Texture} the texture that was removed + * @param id {String} The id of the texture to be removed + * @return {Texture} The texture that was removed */ PIXI.Texture.removeTextureFromCache = function(id) { var texture = PIXI.TextureCache[id]; - PIXI.TextureCache[id] = null; + delete PIXI.TextureCache[id]; + delete PIXI.BaseTextureCache[id]; return texture; }; -// this is more for webGL.. it contains updated frames.. -PIXI.Texture.frameUpdates = []; - PIXI.TextureUvs = function() { this.x0 = 0; @@ -11912,48 +17300,48 @@ PIXI.TextureUvs = function() this.y2 = 0; this.x3 = 0; - this.y4 = 0; - - + this.y3 = 0; }; - +PIXI.Texture.emptyTexture = new PIXI.Texture(new PIXI.BaseTexture()); + + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** - A RenderTexture is a special texture that allows any pixi displayObject to be rendered to it. - - __Hint__: All DisplayObjects (exmpl. Sprites) that render on RenderTexture should be preloaded. - Otherwise black rectangles will be drawn instead. - - RenderTexture takes snapshot of DisplayObject passed to render method. If DisplayObject is passed to render method, position and rotation of it will be ignored. For example: - - var renderTexture = new PIXI.RenderTexture(800, 600); - var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - sprite.position.x = 800/2; - sprite.position.y = 600/2; - sprite.anchor.x = 0.5; - sprite.anchor.y = 0.5; - renderTexture.render(sprite); - - Sprite in this case will be rendered to 0,0 position. To render this sprite at center DisplayObjectContainer should be used: - - var doc = new PIXI.DisplayObjectContainer(); - doc.addChild(sprite); - renderTexture.render(doc); // Renders to center of renderTexture - + * A RenderTexture is a special texture that allows any Pixi display object to be rendered to it. + * + * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded otherwise black rectangles will be drawn instead. + * + * A RenderTexture takes a snapshot of any Display Object given to its render method. The position and rotation of the given Display Objects is ignored. For example: + * + * var renderTexture = new PIXI.RenderTexture(800, 600); + * var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); + * sprite.position.x = 800/2; + * sprite.position.y = 600/2; + * sprite.anchor.x = 0.5; + * sprite.anchor.y = 0.5; + * renderTexture.render(sprite); + * + * The Sprite in this case will be rendered to a position of 0,0. To render this sprite at its actual position a DisplayObjectContainer should be used: + * + * var doc = new PIXI.DisplayObjectContainer(); + * doc.addChild(sprite); + * renderTexture.render(doc); // Renders to center of renderTexture + * * @class RenderTexture * @extends Texture * @constructor * @param width {Number} The width of the render texture * @param height {Number} The height of the render texture + * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @param resolution {Number} The resolution of the texture being generated */ -PIXI.RenderTexture = function(width, height, renderer) +PIXI.RenderTexture = function(width, height, renderer, scaleMode, resolution) { - PIXI.EventTarget.call( this ); - /** * The with of the render texture * @@ -11961,6 +17349,7 @@ PIXI.RenderTexture = function(width, height, renderer) * @type Number */ this.width = width || 100; + /** * The height of the render texture * @@ -11969,13 +17358,30 @@ PIXI.RenderTexture = function(width, height, renderer) */ this.height = height || 100; + /** + * The Resolution of the texture. + * + * @property resolution + * @type Number + */ + this.resolution = resolution || 1; + /** * The framing rectangle of the render texture * * @property frame * @type Rectangle */ - this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); + this.frame = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); + + /** + * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, + * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) + * + * @property crop + * @type Rectangle + */ + this.crop = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); /** * The base texture object that this texture uses @@ -11984,63 +17390,107 @@ PIXI.RenderTexture = function(width, height, renderer) * @type BaseTexture */ this.baseTexture = new PIXI.BaseTexture(); - this.baseTexture.width = this.width; - this.baseTexture.height = this.height; + this.baseTexture.width = this.width * this.resolution; + this.baseTexture.height = this.height * this.resolution; this.baseTexture._glTextures = []; + this.baseTexture.resolution = this.resolution; + + this.baseTexture.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; this.baseTexture.hasLoaded = true; - // each render texture can only belong to one renderer at the moment if its webGL + PIXI.Texture.call(this, + this.baseTexture, + new PIXI.Rectangle(0, 0, this.width, this.height) + ); + + /** + * The renderer this RenderTexture uses. A RenderTexture can only belong to one renderer at the moment if its webGL. + * + * @property renderer + * @type CanvasRenderer|WebGLRenderer + */ this.renderer = renderer || PIXI.defaultRenderer; if(this.renderer.type === PIXI.WEBGL_RENDERER) { var gl = this.renderer.gl; + this.baseTexture._dirty[gl.id] = false; - this.textureBuffer = new PIXI.FilterTexture(gl, this.width, this.height); + this.textureBuffer = new PIXI.FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; this.render = this.renderWebGL; - this.projection = new PIXI.Point(this.width/2 , -this.height/2); + this.projection = new PIXI.Point(this.width*0.5, -this.height*0.5); } else { this.render = this.renderCanvas; - this.textureBuffer = new PIXI.CanvasBuffer(this.width, this.height); + this.textureBuffer = new PIXI.CanvasBuffer(this.width* this.resolution, this.height* this.resolution); this.baseTexture.source = this.textureBuffer.canvas; } - PIXI.Texture.frameUpdates.push(this); - + /** + * @property valid + * @type Boolean + */ + this.valid = true; + this._updateUvs(); }; PIXI.RenderTexture.prototype = Object.create(PIXI.Texture.prototype); PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture; -PIXI.RenderTexture.prototype.resize = function(width, height) +/** + * Resizes the RenderTexture. + * + * @method resize + * @param width {Number} The width to resize to. + * @param height {Number} The height to resize to. + * @param updateBase {Boolean} Should the baseTexture.width and height values be resized as well? + */ +PIXI.RenderTexture.prototype.resize = function(width, height, updateBase) { - this.width = width; - this.height = height; + if (width === this.width && height === this.height)return; - this.frame.width = this.width; - this.frame.height = this.height; + this.valid = (width > 0 && height > 0); - if(this.renderer.type === PIXI.WEBGL_RENDERER) + this.width = this.frame.width = this.crop.width = width; + this.height = this.frame.height = this.crop.height = height; + + if (updateBase) + { + this.baseTexture.width = this.width; + this.baseTexture.height = this.height; + } + + if (this.renderer.type === PIXI.WEBGL_RENDERER) { this.projection.x = this.width / 2; this.projection.y = -this.height / 2; - - var gl = this.renderer.gl; - gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTextures[gl.id]); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } - else + + if(!this.valid)return; + + this.textureBuffer.resize(this.width * this.resolution, this.height * this.resolution); +}; + +/** + * Clears the RenderTexture. + * + * @method clear + */ +PIXI.RenderTexture.prototype.clear = function() +{ + if(!this.valid)return; + + if (this.renderer.type === PIXI.WEBGL_RENDERER) { - this.textureBuffer.resize(this.width, this.height); + this.renderer.gl.bindFramebuffer(this.renderer.gl.FRAMEBUFFER, this.textureBuffer.frameBuffer); } - PIXI.Texture.frameUpdates.push(this); + this.textureBuffer.clear(); }; /** @@ -12048,50 +17498,47 @@ PIXI.RenderTexture.prototype.resize = function(width, height) * * @method renderWebGL * @param displayObject {DisplayObject} The display object to render this texture on - * @param clear {Boolean} If true the texture will be cleared before the displayObject is drawn + * @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering. + * @param [clear] {Boolean} If true the texture will be cleared before the displayObject is drawn * @private */ -PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear) +PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, matrix, clear) { + if(!this.valid)return; //TOOD replace position with matrix.. - var gl = this.renderer.gl; + + //Lets create a nice matrix to apply to our display object. Frame buffers come in upside down so we need to flip the matrix + var wt = displayObject.worldTransform; + wt.identity(); + wt.translate(0, this.projection.y * 2); + if(matrix)wt.append(matrix); + wt.scale(1,-1); - gl.colorMask(true, true, true, true); + // setWorld Alpha to ensure that the object is renderer at full opacity + displayObject.worldAlpha = 1; - gl.viewport(0, 0, this.width, this.height); - - gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer ); - - if(clear)this.textureBuffer.clear(); - - // THIS WILL MESS WITH HIT TESTING! + // Time to update all the children of the displayObject with the new matrix.. var children = displayObject.children; - //TODO -? create a new one??? dont think so! - var originalWorldTransform = displayObject.worldTransform; - displayObject.worldTransform = PIXI.RenderTexture.tempMatrix; - // modify to flip... - displayObject.worldTransform.d = -1; - displayObject.worldTransform.ty = this.projection.y * -2; - - if(position) - { - displayObject.worldTransform.tx = position.x; - displayObject.worldTransform.ty -= position.y; - } - for(var i=0,j=children.length; i} assetURLs an array of image/sprite sheet urls that you would like loaded + * @param assetURLs {Array(String)} An array of image/sprite sheet urls that you would like loaded * supported. Supported image formats include 'jpeg', 'jpg', 'png', 'gif'. Supported * sprite sheet data formats only include 'JSON' at this time. Supported bitmap font * data formats include 'xml' and 'fnt'. @@ -12152,13 +17834,11 @@ PIXI.RenderTexture.tempMatrix = new PIXI.Matrix(); */ PIXI.AssetLoader = function(assetURLs, crossorigin) { - PIXI.EventTarget.call(this); - /** * The array of asset URLs that are going to be loaded * * @property assetURLs - * @type Array + * @type Array(String) */ this.assetURLs = assetURLs; @@ -12181,6 +17861,7 @@ PIXI.AssetLoader = function(assetURLs, crossorigin) 'jpeg': PIXI.ImageLoader, 'png': PIXI.ImageLoader, 'gif': PIXI.ImageLoader, + 'webp': PIXI.ImageLoader, 'json': PIXI.JsonLoader, 'atlas': PIXI.AtlasLoader, 'anim': PIXI.SpineLoader, @@ -12189,6 +17870,8 @@ PIXI.AssetLoader = function(assetURLs, crossorigin) }; }; +PIXI.EventTarget.mixin(PIXI.AssetLoader.prototype); + /** * Fired when an item has loaded * @event onProgress @@ -12203,7 +17886,7 @@ PIXI.AssetLoader = function(assetURLs, crossorigin) PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; /** - * Given a filename, returns its extension, wil + * Given a filename, returns its extension. * * @method _getDataType * @param str {String} the name of the asset @@ -12245,7 +17928,7 @@ PIXI.AssetLoader.prototype.load = function() var scope = this; function onLoad(evt) { - scope.onAssetLoaded(evt.loader); + scope.onAssetLoaded(evt.data.content); } this.loadCount = this.assetURLs.length; @@ -12266,7 +17949,7 @@ PIXI.AssetLoader.prototype.load = function() var loader = new Constructor(fileName, this.crossorigin); - loader.addEventListener('loaded', onLoad); + loader.on('loaded', onLoad); loader.load(); } }; @@ -12280,16 +17963,16 @@ PIXI.AssetLoader.prototype.load = function() PIXI.AssetLoader.prototype.onAssetLoaded = function(loader) { this.loadCount--; - this.dispatchEvent({ type: 'onProgress', content: this, loader: loader }); + this.emit('onProgress', { content: this, loader: loader }); if (this.onProgress) this.onProgress(loader); if (!this.loadCount) { - this.dispatchEvent({type: 'onComplete', content: this}); + this.emit('onComplete', { content: this }); if(this.onComplete) this.onComplete(); } }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -12306,8 +17989,6 @@ PIXI.AssetLoader.prototype.onAssetLoaded = function(loader) * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.JsonLoader = function (url, crossorigin) { - PIXI.EventTarget.call(this); - /** * The url of the bitmap font data * @@ -12346,6 +18027,7 @@ PIXI.JsonLoader = function (url, crossorigin) { // constructor PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; +PIXI.EventTarget.mixin(PIXI.JsonLoader.prototype); /** * Loads the JSON data @@ -12353,15 +18035,53 @@ PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; * @method load */ PIXI.JsonLoader.prototype.load = function () { - this.ajaxRequest = new PIXI.AjaxRequest(this.crossorigin); - var scope = this; - this.ajaxRequest.onreadystatechange = function () { - scope.onJSONLoaded(); - }; - this.ajaxRequest.open('GET', this.url, true); - if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/json'); - this.ajaxRequest.send(null); + if(window.XDomainRequest && this.crossorigin) + { + this.ajaxRequest = new window.XDomainRequest(); + + // XDomainRequest has a few quirks. Occasionally it will abort requests + // A way to avoid this is to make sure ALL callbacks are set even if not used + // More info here: http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9 + this.ajaxRequest.timeout = 3000; + + this.ajaxRequest.onerror = this.onError.bind(this); + + this.ajaxRequest.ontimeout = this.onError.bind(this); + + this.ajaxRequest.onprogress = function() {}; + + this.ajaxRequest.onload = this.onJSONLoaded.bind(this); + } + else + { + if (window.XMLHttpRequest) + { + this.ajaxRequest = new window.XMLHttpRequest(); + } + else + { + this.ajaxRequest = new window.ActiveXObject('Microsoft.XMLHTTP'); + } + + this.ajaxRequest.onreadystatechange = this.onReadyStateChanged.bind(this); + } + + this.ajaxRequest.open('GET',this.url,true); + + this.ajaxRequest.send(); +}; + +/** + * Bridge function to be able to use the more reliable onreadystatechange in XMLHttpRequest. + * + * @method onReadyStateChanged + * @private + */ +PIXI.JsonLoader.prototype.onReadyStateChanged = function () { + if (this.ajaxRequest.readyState === 4 && (this.ajaxRequest.status === 200 || window.location.href.indexOf('http') === -1)) { + this.onJSONLoaded(); + } }; /** @@ -12371,67 +18091,111 @@ PIXI.JsonLoader.prototype.load = function () { * @private */ PIXI.JsonLoader.prototype.onJSONLoaded = function () { - if (this.ajaxRequest.readyState === 4) { - if (this.ajaxRequest.status === 200 || window.location.protocol.indexOf('http') === -1) { - this.json = JSON.parse(this.ajaxRequest.responseText); - if(this.json.frames) + if(!this.ajaxRequest.responseText ) + { + this.onError(); + return; + } + + this.json = JSON.parse(this.ajaxRequest.responseText); + + if(this.json.frames && this.json.meta && this.json.meta.image) + { + // sprite sheet + var textureUrl = this.baseUrl + this.json.meta.image; + var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); + var frameData = this.json.frames; + + this.texture = image.texture.baseTexture; + image.addEventListener('loaded', this.onLoaded.bind(this)); + + for (var i in frameData) + { + var rect = frameData[i].frame; + + if (rect) { - // sprite sheet - var scope = this; - var textureUrl = this.baseUrl + this.json.meta.image; - var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); - var frameData = this.json.frames; + var textureSize = new PIXI.Rectangle(rect.x, rect.y, rect.w, rect.h); + var crop = textureSize.clone(); + var trim = null; - this.texture = image.texture.baseTexture; - image.addEventListener('loaded', function() { - scope.onLoaded(); - }); - - for (var i in frameData) { - var rect = frameData[i].frame; - if (rect) { - PIXI.TextureCache[i] = new PIXI.Texture(this.texture, { - x: rect.x, - y: rect.y, - width: rect.w, - height: rect.h - }); - - // check to see ifthe sprite ha been trimmed.. - if (frameData[i].trimmed) { - - var texture = PIXI.TextureCache[i]; - - var actualSize = frameData[i].sourceSize; - var realSize = frameData[i].spriteSourceSize; - - texture.trim = new PIXI.Rectangle(realSize.x, realSize.y, actualSize.w, actualSize.h); - } - } + // Check to see if the sprite is trimmed + if (frameData[i].trimmed) + { + var actualSize = frameData[i].sourceSize; + var realSize = frameData[i].spriteSourceSize; + trim = new PIXI.Rectangle(realSize.x, realSize.y, actualSize.w, actualSize.h); } + PIXI.TextureCache[i] = new PIXI.Texture(this.texture, textureSize, crop, trim); + } + } - image.load(); + image.load(); - } - else if(this.json.bones) - { - // spine animation - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); - PIXI.AnimCache[this.url] = skeletonData; - this.onLoaded(); - } - else - { - this.onLoaded(); - } + } + else if(this.json.bones) + { + /* check if the json was loaded before */ + if (PIXI.AnimCache[this.url]) + { + this.onLoaded(); } else { - this.onError(); + /* use a bit of hackery to load the atlas file, here we assume that the .json, .atlas and .png files + * that correspond to the spine file are in the same base URL and that the .json and .atlas files + * have the same name + */ + var atlasPath = this.url.substr(0, this.url.lastIndexOf('.')) + '.atlas'; + var atlasLoader = new PIXI.JsonLoader(atlasPath, this.crossorigin); + // save a copy of the current object for future reference // + var originalLoader = this; + // before loading the file, replace the "onJSONLoaded" function for our own // + atlasLoader.onJSONLoaded = function() + { + // at this point "this" points at the atlasLoader (JsonLoader) instance // + if(!this.ajaxRequest.responseText) + { + this.onError(); // FIXME: hmm, this is funny because we are not responding to errors yet + return; + } + // create a new instance of a spine texture loader for this spine object // + var textureLoader = new PIXI.SpineTextureLoader(this.url.substring(0, this.url.lastIndexOf('/'))); + // create a spine atlas using the loaded text and a spine texture loader instance // + var spineAtlas = new spine.Atlas(this.ajaxRequest.responseText, textureLoader); + // now we use an atlas attachment loader // + var attachmentLoader = new spine.AtlasAttachmentLoader(spineAtlas); + // spine animation + var spineJsonParser = new spine.SkeletonJson(attachmentLoader); + var skeletonData = spineJsonParser.readSkeletonData(originalLoader.json); + PIXI.AnimCache[originalLoader.url] = skeletonData; + originalLoader.spine = skeletonData; + originalLoader.spineAtlas = spineAtlas; + originalLoader.spineAtlasLoader = atlasLoader; + // wait for textures to finish loading if needed + if (textureLoader.loadingCount > 0) + { + textureLoader.addEventListener('loadedBaseTexture', function(evt){ + if (evt.content.content.loadingCount <= 0) + { + originalLoader.onLoaded(); + } + }); + } + else + { + originalLoader.onLoaded(); + } + }; + // start the loading // + atlasLoader.load(); } } + else + { + this.onLoaded(); + } }; /** @@ -12455,28 +18219,32 @@ PIXI.JsonLoader.prototype.onLoaded = function () { * @private */ PIXI.JsonLoader.prototype.onError = function () { + this.dispatchEvent({ type: 'error', content: this }); -}; +}; + /** * @author Martin Kelm http://mkelm.github.com */ /** - * The atlas file loader is used to load in Atlas data and parse it - * When loaded this class will dispatch a 'loaded' event - * If loading fails this class will dispatch an 'error' event + * The atlas file loader is used to load in Texture Atlas data and parse it. When loaded this class will dispatch a 'loaded' event. If loading fails this class will dispatch an 'error' event. + * + * To generate the data you can use http://www.codeandweb.com/texturepacker and publish in the 'JSON' format. + * + * It is highly recommended to use texture atlases (also know as 'sprite sheets') as it allowed sprites to be batched and drawn together for highly increased rendering speed. + * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() + * * @class AtlasLoader - * @extends EventTarget + * @uses EventTarget * @constructor - * @param {String} url the url of the JSON file - * @param {Boolean} crossorigin + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ - PIXI.AtlasLoader = function (url, crossorigin) { - PIXI.EventTarget.call(this); this.url = url; this.baseUrl = url.replace(/[^\/]*$/, ''); this.crossorigin = crossorigin; @@ -12487,6 +18255,7 @@ PIXI.AtlasLoader = function (url, crossorigin) { // constructor PIXI.AtlasLoader.constructor = PIXI.AtlasLoader; +PIXI.EventTarget.mixin(PIXI.AtlasLoader.prototype); /** * Starts loading the JSON file @@ -12503,7 +18272,8 @@ PIXI.AtlasLoader.prototype.load = function () { }; /** - * Invoke when JSON file is loaded + * Invoked when the Atlas has fully loaded. Parses the JSON and builds the texture frames. + * * @method onAtlasLoaded * @private */ @@ -12607,7 +18377,7 @@ PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { this.currentImageId = 0; for (j = 0; j < this.images.length; j++) { - this.images[j].addEventListener('loaded', selfOnLoaded); + this.images[j].on('loaded', selfOnLoaded); } this.images[this.currentImageId].load(); @@ -12622,7 +18392,8 @@ PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { }; /** - * Invoke when json file has loaded + * Invoked when json file has loaded. + * * @method onLoaded * @private */ @@ -12632,25 +18403,20 @@ PIXI.AtlasLoader.prototype.onLoaded = function () { this.images[this.currentImageId].load(); } else { this.loaded = true; - this.dispatchEvent({ - type: 'loaded', - content: this - }); + this.emit('loaded', { content: this }); } }; /** - * Invoke when error occured + * Invoked when an error occurs. + * * @method onError * @private */ PIXI.AtlasLoader.prototype.onError = function () { - this.dispatchEvent({ - type: 'error', - content: this - }); + this.emit('error', { content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -12671,15 +18437,9 @@ PIXI.AtlasLoader.prototype.onError = function () { * @param crossorigin {Boolean} Whether requests should be treated as crossorigin */ PIXI.SpriteSheetLoader = function (url, crossorigin) { - /* - * i use texture packer to load the assets.. - * http://www.codeandweb.com/texturepacker - * make sure to set the format as 'JSON' - */ - PIXI.EventTarget.call(this); /** - * The url of the bitmap font data + * The url of the atlas data * * @property url * @type String @@ -12723,6 +18483,8 @@ PIXI.SpriteSheetLoader = function (url, crossorigin) { // constructor PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; +PIXI.EventTarget.mixin(PIXI.SpriteSheetLoader.prototype); + /** * This will begin loading the JSON file * @@ -12731,8 +18493,8 @@ PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; PIXI.SpriteSheetLoader.prototype.load = function () { var scope = this; var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.addEventListener('loaded', function (event) { - scope.json = event.content.json; + jsonLoader.on('loaded', function (event) { + scope.json = event.data.content.json; scope.onLoaded(); }); jsonLoader.load(); @@ -12745,19 +18507,18 @@ PIXI.SpriteSheetLoader.prototype.load = function () { * @private */ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { - this.dispatchEvent({ - type: 'loaded', + this.emit('loaded', { content: this }); }; - + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The image loader class is responsible for loading images file formats ('jpeg', 'jpg', 'png' and 'gif') - * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() + * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrame() and PIXI.Sprite.fromFrame() * When loaded this class will dispatch a 'loaded' event * * @class ImageLoader @@ -12768,8 +18529,6 @@ PIXI.SpriteSheetLoader.prototype.onLoaded = function () { */ PIXI.ImageLoader = function(url, crossorigin) { - PIXI.EventTarget.call(this); - /** * The texture being loaded * @@ -12782,6 +18541,9 @@ PIXI.ImageLoader = function(url, crossorigin) * if the image is loaded with loadFramedSpriteSheet * frames will contain the sprite sheet frames * + * @property frames + * @type Array + * @readOnly */ this.frames = []; }; @@ -12789,6 +18551,8 @@ PIXI.ImageLoader = function(url, crossorigin) // constructor PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; +PIXI.EventTarget.mixin(PIXI.ImageLoader.prototype); + /** * Loads image or takes it from cache * @@ -12798,11 +18562,7 @@ PIXI.ImageLoader.prototype.load = function() { if(!this.texture.baseTexture.hasLoaded) { - var scope = this; - this.texture.baseTexture.addEventListener('loaded', function() - { - scope.onLoaded(); - }); + this.texture.baseTexture.on('loaded', this.onLoaded.bind(this)); } else { @@ -12818,13 +18578,12 @@ PIXI.ImageLoader.prototype.load = function() */ PIXI.ImageLoader.prototype.onLoaded = function() { - this.dispatchEvent({type: 'loaded', content: this}); + this.emit('loaded', { content: this }); }; /** * Loads image and split it to uniform sized frames * - * * @method loadFramedSpriteSheet * @param frameWidth {Number} width of each frame * @param frameHeight {Number} height of each frame @@ -12841,7 +18600,7 @@ PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHei { for (var x=0; x/im, + bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, + hasLocation = typeof location !== 'undefined' && location.href, + defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), + defaultHostName = hasLocation && location.hostname, + defaultPort = hasLocation && (location.port || undefined), + buildMap = {}, + masterConfig = (module.config && module.config()) || {}; + + text = { + version: '2.0.14', + + strip: function (content) { + //Strips declarations so that external SVG and XML + //documents can be added to a document without worry. Also, if the string + //is an HTML document, only the part inside the body tag is returned. + if (content) { + content = content.replace(xmlRegExp, ""); + var matches = content.match(bodyRegExp); + if (matches) { + content = matches[1]; + } + } else { + content = ""; + } + return content; + }, + + jsEscape: function (content) { + return content.replace(/(['\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r") + .replace(/[\u2028]/g, "\\u2028") + .replace(/[\u2029]/g, "\\u2029"); + }, + + createXhr: masterConfig.createXhr || function () { + //Would love to dump the ActiveX crap in here. Need IE 6 to die first. + var xhr, i, progId; + if (typeof XMLHttpRequest !== "undefined") { + return new XMLHttpRequest(); + } else if (typeof ActiveXObject !== "undefined") { + for (i = 0; i < 3; i += 1) { + progId = progIds[i]; + try { + xhr = new ActiveXObject(progId); + } catch (e) {} + + if (xhr) { + progIds = [progId]; // so faster next time + break; + } + } + } + + return xhr; + }, + + /** + * Parses a resource name into its component parts. Resource names + * look like: module/name.ext!strip, where the !strip part is + * optional. + * @param {String} name the resource name + * @returns {Object} with properties "moduleName", "ext" and "strip" + * where strip is a boolean. + */ + parseName: function (name) { + var modName, ext, temp, + strip = false, + index = name.lastIndexOf("."), + isRelative = name.indexOf('./') === 0 || + name.indexOf('../') === 0; + + if (index !== -1 && (!isRelative || index > 1)) { + modName = name.substring(0, index); + ext = name.substring(index + 1); + } else { + modName = name; + } + + temp = ext || modName; + index = temp.indexOf("!"); + if (index !== -1) { + //Pull off the strip arg. + strip = temp.substring(index + 1) === "strip"; + temp = temp.substring(0, index); + if (ext) { + ext = temp; + } else { + modName = temp; + } + } + + return { + moduleName: modName, + ext: ext, + strip: strip + }; + }, + + xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, + + /** + * Is an URL on another domain. Only works for browser use, returns + * false in non-browser environments. Only used to know if an + * optimized .js version of a text resource should be loaded + * instead. + * @param {String} url + * @returns Boolean + */ + useXhr: function (url, protocol, hostname, port) { + var uProtocol, uHostName, uPort, + match = text.xdRegExp.exec(url); + if (!match) { + return true; + } + uProtocol = match[2]; + uHostName = match[3]; + + uHostName = uHostName.split(':'); + uPort = uHostName[1]; + uHostName = uHostName[0]; + + return (!uProtocol || uProtocol === protocol) && + (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && + ((!uPort && !uHostName) || uPort === port); + }, + + finishLoad: function (name, strip, content, onLoad) { + content = strip ? text.strip(content) : content; + if (masterConfig.isBuild) { + buildMap[name] = content; + } + onLoad(content); + }, + + load: function (name, req, onLoad, config) { + //Name has format: some.module.filext!strip + //The strip part is optional. + //if strip is present, then that means only get the string contents + //inside a body tag in an HTML string. For XML/SVG content it means + //removing the declarations so the content can be inserted + //into the current doc without problems. + + // Do not bother with the work if a build and text will + // not be inlined. + if (config && config.isBuild && !config.inlineText) { + onLoad(); + return; + } + + masterConfig.isBuild = config && config.isBuild; + + var parsed = text.parseName(name), + nonStripName = parsed.moduleName + + (parsed.ext ? '.' + parsed.ext : ''), + url = req.toUrl(nonStripName), + useXhr = (masterConfig.useXhr) || + text.useXhr; + + // Do not load if it is an empty: url + if (url.indexOf('empty:') === 0) { + onLoad(); + return; + } + + //Load the text. Use XHR if possible and in a browser. + if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { + text.get(url, function (content) { + text.finishLoad(name, parsed.strip, content, onLoad); + }, function (err) { + if (onLoad.error) { + onLoad.error(err); + } + }); + } else { + //Need to fetch the resource across domains. Assume + //the resource has been optimized into a JS module. Fetch + //by the module name + extension, but do not include the + //!strip part to avoid file system issues. + req([nonStripName], function (content) { + text.finishLoad(parsed.moduleName + '.' + parsed.ext, + parsed.strip, content, onLoad); + }); + } + }, + + write: function (pluginName, moduleName, write, config) { + if (buildMap.hasOwnProperty(moduleName)) { + var content = text.jsEscape(buildMap[moduleName]); + write.asModule(pluginName + "!" + moduleName, + "define(function () { return '" + + content + + "';});\n"); + } + }, + + writeFile: function (pluginName, moduleName, req, write, config) { + var parsed = text.parseName(moduleName), + extPart = parsed.ext ? '.' + parsed.ext : '', + nonStripName = parsed.moduleName + extPart, + //Use a '.js' file name so that it indicates it is a + //script that can be loaded across domains. + fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; + + //Leverage own load() method to load plugin value, but only + //write out values that do not have the strip argument, + //to avoid any potential issues with ! in file names. + text.load(nonStripName, req, function (value) { + //Use own write() method to construct full module value. + //But need to create shell that translates writeFile's + //write() to the right interface. + var textWrite = function (contents) { + return write(fileName, contents); + }; + textWrite.asModule = function (moduleName, contents) { + return write.asModule(moduleName, fileName, contents); + }; + + text.write(pluginName, nonStripName, textWrite, config); + }, config); + } + }; + + if (masterConfig.env === 'node' || (!masterConfig.env && + typeof process !== "undefined" && + process.versions && + !!process.versions.node && + !process.versions['node-webkit'] && + !process.versions['atom-shell'])) { + //Using special require.nodeRequire, something added by r.js. + fs = require.nodeRequire('fs'); + + text.get = function (url, callback, errback) { + try { + var file = fs.readFileSync(url, 'utf8'); + //Remove BOM (Byte Mark Order) from utf8 files if it is there. + if (file[0] === '\uFEFF') { + file = file.substring(1); + } + callback(file); + } catch (e) { + if (errback) { + errback(e); + } + } + }; + } else if (masterConfig.env === 'xhr' || (!masterConfig.env && + text.createXhr())) { + text.get = function (url, callback, errback, headers) { + var xhr = text.createXhr(), header; + xhr.open('GET', url, true); + + //Allow plugins direct access to xhr headers + if (headers) { + for (header in headers) { + if (headers.hasOwnProperty(header)) { + xhr.setRequestHeader(header.toLowerCase(), headers[header]); + } + } + } + + //Allow overrides specified in config + if (masterConfig.onXhr) { + masterConfig.onXhr(xhr, url); + } + + xhr.onreadystatechange = function (evt) { + var status, err; + //Do not explicitly handle errors, those should be + //visible via console output in the browser. + if (xhr.readyState === 4) { + status = xhr.status || 0; + if (status > 399 && status < 600) { + //An http 4xx or 5xx error. Signal an error. + err = new Error(url + ' HTTP status: ' + status); + err.xhr = xhr; + if (errback) { + errback(err); + } + } else { + callback(xhr.responseText); + } + + if (masterConfig.onXhrComplete) { + masterConfig.onXhrComplete(xhr, url); + } + } + }; + xhr.send(null); + }; + } else if (masterConfig.env === 'rhino' || (!masterConfig.env && + typeof Packages !== 'undefined' && typeof java !== 'undefined')) { + //Why Java, why is this so awkward? + text.get = function (url, callback) { + var stringBuffer, line, + encoding = "utf-8", + file = new java.io.File(url), + lineSeparator = java.lang.System.getProperty("line.separator"), + input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), + content = ''; + try { + stringBuffer = new java.lang.StringBuffer(); + line = input.readLine(); + + // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 + // http://www.unicode.org/faq/utf_bom.html + + // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 + if (line && line.length() && line.charAt(0) === 0xfeff) { + // Eat the BOM, since we've already found the encoding on this file, + // and we plan to concatenating this buffer with others; the BOM should + // only appear at the top of a file. + line = line.substring(1); + } + + if (line !== null) { + stringBuffer.append(line); + } + + while ((line = input.readLine()) !== null) { + stringBuffer.append(lineSeparator); + stringBuffer.append(line); + } + //Make sure we return a JavaScript string and not a Java string. + content = String(stringBuffer.toString()); //String + } finally { + input.close(); + } + callback(content); + }; + } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && + typeof Components !== 'undefined' && Components.classes && + Components.interfaces)) { + //Avert your gaze! + Cc = Components.classes; + Ci = Components.interfaces; + Components.utils['import']('resource://gre/modules/FileUtils.jsm'); + xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); + + text.get = function (url, callback) { + var inStream, convertStream, fileObj, + readData = {}; + + if (xpcIsWindows) { + url = url.replace(/\//g, '\\'); + } + + fileObj = new FileUtils.File(url); + + //XPCOM, you so crazy + try { + inStream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + inStream.init(fileObj, 1, 0, false); + + convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] + .createInstance(Ci.nsIConverterInputStream); + convertStream.init(inStream, "utf-8", inStream.available(), + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + + convertStream.readString(inStream.available(), readData); + convertStream.close(); + inStream.close(); + callback(readData.value); + } catch (e) { + throw new Error((fileObj && fileObj.path || '') + ': ' + e); + } + }; + } + return text; +}); \ No newline at end of file diff --git a/app/Lib/Vendor/RubeLoader.js b/app/Lib/Vendor/RubeLoader.js index 49674c3..b1f3b83 100644 --- a/app/Lib/Vendor/RubeLoader.js +++ b/app/Lib/Vendor/RubeLoader.js @@ -152,8 +152,7 @@ function (Box2D) { if ( bodyJson.hasOwnProperty('linearVelocity') && bodyJson.linearVelocity instanceof Object ) bd.linearVelocity.SetV( bodyJson.linearVelocity ); if ( bodyJson.hasOwnProperty('position') && bodyJson.position instanceof Object ) - bodyJson.position.y *= -1; - bd.position.SetV( bodyJson.position ); + bd.position.SetV( this.getVectorValue(bodyJson.position) ); if ( bodyJson.hasOwnProperty('awake') ) bd.awake = bodyJson.awake; else @@ -169,6 +168,7 @@ function (Box2D) { body.name = bodyJson.name; if ( bodyJson.hasOwnProperty('customProperties') ) body.customProperties = bodyJson.customProperties; + return body; } @@ -231,8 +231,7 @@ function (Box2D) { RubeLoader.prototype.getVectorValue = function (val) { if ( val instanceof Object ) { - val.y *= -1; - return val; + return { x: val.x, y: val.y * -1 }; } else { return { x:0, y:0 }; } @@ -427,7 +426,7 @@ function (Box2D) { var scene = { bodies: loadedBodies, - // joints: loadedJoints + joints: loadedJoints }; return scene; diff --git a/app/Lib/Vendor/Screenfull.js b/app/Lib/Vendor/Screenfull.js index ad4eb40..227db38 100644 --- a/app/Lib/Vendor/Screenfull.js +++ b/app/Lib/Vendor/Screenfull.js @@ -1,3 +1,3 @@ -define(["/screenfull.js"], function() { +define(["screenfull"], function() { return screenfull; }); \ No newline at end of file diff --git a/app/Lib/Vendor/SocketIO.js b/app/Lib/Vendor/SocketIO.js index 062fb3c..e7e0089 100755 --- a/app/Lib/Vendor/SocketIO.js +++ b/app/Lib/Vendor/SocketIO.js @@ -1,3 +1,3 @@ -define(["/socket.io/socket.io.js"], function() { +define(["socketio"], function(io) { return io; -}); \ No newline at end of file +}); diff --git a/app/Lib/Vendor/Stats.js b/app/Lib/Vendor/Stats.js index a66f82b..06cfb43 100755 --- a/app/Lib/Vendor/Stats.js +++ b/app/Lib/Vendor/Stats.js @@ -1,9 +1,153 @@ define(function() { - var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; - i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); - k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= - "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= - a+"px",m=b,r=0);return b},update:function(){l=this.end()}}}; + /** + * @author mrdoob / http://mrdoob.com/ + */ + + var Stats = function () { + + var startTime = Date.now(), prevTime = startTime; + var ms = 0, msMin = Infinity, msMax = 0; + var fps = 0, fpsMin = Infinity, fpsMax = 0; + var frames = 0, mode = 0; + + var container = document.createElement( 'div' ); + container.id = 'stats'; + container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); + container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; + + var fpsDiv = document.createElement( 'div' ); + fpsDiv.id = 'fps'; + fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; + container.appendChild( fpsDiv ); + + var fpsText = document.createElement( 'div' ); + fpsText.id = 'fpsText'; + fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + fpsText.innerHTML = 'FPS'; + fpsDiv.appendChild( fpsText ); + + var fpsGraph = document.createElement( 'div' ); + fpsGraph.id = 'fpsGraph'; + fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; + fpsDiv.appendChild( fpsGraph ); + + while ( fpsGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; + fpsGraph.appendChild( bar ); + + } + + var msDiv = document.createElement( 'div' ); + msDiv.id = 'ms'; + msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; + container.appendChild( msDiv ); + + var msText = document.createElement( 'div' ); + msText.id = 'msText'; + msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + msText.innerHTML = 'MS'; + msDiv.appendChild( msText ); + + var msGraph = document.createElement( 'div' ); + msGraph.id = 'msGraph'; + msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; + msDiv.appendChild( msGraph ); + + while ( msGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; + msGraph.appendChild( bar ); + + } + + var setMode = function ( value ) { + + mode = value; + + switch ( mode ) { + + case 0: + fpsDiv.style.display = 'block'; + msDiv.style.display = 'none'; + break; + case 1: + fpsDiv.style.display = 'none'; + msDiv.style.display = 'block'; + break; + } + + }; + + var updateGraph = function ( dom, value ) { + + var child = dom.appendChild( dom.firstChild ); + child.style.height = value + 'px'; + + }; + + return { + + REVISION: 12, + + domElement: container, + + setMode: setMode, + + begin: function () { + + startTime = Date.now(); + + }, + + end: function () { + + var time = Date.now(); + + ms = time - startTime; + msMin = Math.min( msMin, ms ); + msMax = Math.max( msMax, ms ); + + msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; + updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); + + frames ++; + + if ( time > prevTime + 1000 ) { + + fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); + fpsMin = Math.min( fpsMin, fps ); + fpsMax = Math.max( fpsMax, fps ); + + fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; + updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); + + prevTime = time; + frames = 0; + + } + + return time; + + }, + + update: function () { + + startTime = this.end(); + + } + + } + + }; + + if ( typeof module === 'object' ) { + + module.exports = Stats; + + } return Stats; }); diff --git a/app/Lib/Vendor/src/pixi/InteractionData.js b/app/Lib/Vendor/src/pixi/InteractionData.js new file mode 100644 index 0000000..b0d8a6a --- /dev/null +++ b/app/Lib/Vendor/src/pixi/InteractionData.js @@ -0,0 +1,66 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * Holds all information related to an Interaction event + * + * @class InteractionData + * @constructor + */ +PIXI.InteractionData = function() +{ + /** + * This point stores the global coords of where the touch/mouse event happened + * + * @property global + * @type Point + */ + this.global = new PIXI.Point(); + + /** + * The target Sprite that was interacted with + * + * @property target + * @type Sprite + */ + this.target = null; + + /** + * When passed to an event handler, this will be the original DOM Event that was captured + * + * @property originalEvent + * @type Event + */ + this.originalEvent = null; +}; + +/** + * This will return the local coordinates of the specified displayObject for this InteractionData + * + * @method getLocalPosition + * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off + * @param [point] {Point} A Point object in which to store the value, optional (otherwise will create a new point) + * @return {Point} A point containing the coordinates of the InteractionData position relative to the DisplayObject + */ +PIXI.InteractionData.prototype.getLocalPosition = function(displayObject, point) +{ + var worldTransform = displayObject.worldTransform; + var global = this.global; + + // do a cheeky transform to get the mouse coords; + var a00 = worldTransform.a, a01 = worldTransform.c, a02 = worldTransform.tx, + a10 = worldTransform.b, a11 = worldTransform.d, a12 = worldTransform.ty, + id = 1 / (a00 * a11 + a01 * -a10); + + point = point || new PIXI.Point(); + + point.x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id; + point.y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; + + // set the mouse coords... + return point; +}; + +// constructor +PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; diff --git a/app/Lib/Vendor/src/pixi/InteractionManager.js b/app/Lib/Vendor/src/pixi/InteractionManager.js new file mode 100644 index 0000000..2d08d6c --- /dev/null +++ b/app/Lib/Vendor/src/pixi/InteractionManager.js @@ -0,0 +1,870 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + /** + * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive + * if its interactive parameter is set to true + * This manager also supports multitouch. + * + * @class InteractionManager + * @constructor + * @param stage {Stage} The stage to handle interactions + */ +PIXI.InteractionManager = function(stage) +{ + /** + * A reference to the stage + * + * @property stage + * @type Stage + */ + this.stage = stage; + + /** + * The mouse data + * + * @property mouse + * @type InteractionData + */ + this.mouse = new PIXI.InteractionData(); + + /** + * An object that stores current touches (InteractionData) by id reference + * + * @property touches + * @type Object + */ + this.touches = {}; + + /** + * @property tempPoint + * @type Point + * @private + */ + this.tempPoint = new PIXI.Point(); + + /** + * @property mouseoverEnabled + * @type Boolean + * @default + */ + this.mouseoverEnabled = true; + + /** + * Tiny little interactiveData pool ! + * + * @property pool + * @type Array + */ + this.pool = []; + + /** + * An array containing all the iterative items from the our interactive tree + * @property interactiveItems + * @type Array + * @private + */ + this.interactiveItems = []; + + /** + * Our canvas + * @property interactionDOMElement + * @type HTMLCanvasElement + * @private + */ + this.interactionDOMElement = null; + + //this will make it so that you don't have to call bind all the time + + /** + * @property onMouseMove + * @type Function + */ + this.onMouseMove = this.onMouseMove.bind( this ); + + /** + * @property onMouseDown + * @type Function + */ + this.onMouseDown = this.onMouseDown.bind(this); + + /** + * @property onMouseOut + * @type Function + */ + this.onMouseOut = this.onMouseOut.bind(this); + + /** + * @property onMouseUp + * @type Function + */ + this.onMouseUp = this.onMouseUp.bind(this); + + /** + * @property onTouchStart + * @type Function + */ + this.onTouchStart = this.onTouchStart.bind(this); + + /** + * @property onTouchEnd + * @type Function + */ + this.onTouchEnd = this.onTouchEnd.bind(this); + + /** + * @property onTouchMove + * @type Function + */ + this.onTouchMove = this.onTouchMove.bind(this); + + /** + * @property last + * @type Number + */ + this.last = 0; + + /** + * The css style of the cursor that is being used + * @property currentCursorStyle + * @type String + */ + this.currentCursorStyle = 'inherit'; + + /** + * Is set to true when the mouse is moved out of the canvas + * @property mouseOut + * @type Boolean + */ + this.mouseOut = false; + + /** + * @property resolution + * @type Number + */ + this.resolution = 1; + + // used for hit testing + this._tempPoint = new PIXI.Point(); +}; + +// constructor +PIXI.InteractionManager.prototype.constructor = PIXI.InteractionManager; + +/** + * Collects an interactive sprite recursively to have their interactions managed + * + * @method collectInteractiveSprite + * @param displayObject {DisplayObject} the displayObject to collect + * @param iParent {DisplayObject} the display object's parent + * @private + */ +PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) +{ + var children = displayObject.children; + var length = children.length; + + // make an interaction tree... {item.__interactiveParent} + for (var i = length - 1; i >= 0; i--) + { + var child = children[i]; + + // push all interactive bits + if (child._interactive) + { + iParent.interactiveChildren = true; + //child.__iParent = iParent; + this.interactiveItems.push(child); + + if (child.children.length > 0) { + this.collectInteractiveSprite(child, child); + } + } + else + { + child.__iParent = null; + if (child.children.length > 0) + { + this.collectInteractiveSprite(child, iParent); + } + } + + } +}; + +/** + * Sets the target for event delegation + * + * @method setTarget + * @param target {WebGLRenderer|CanvasRenderer} the renderer to bind events to + * @private + */ +PIXI.InteractionManager.prototype.setTarget = function(target) +{ + this.target = target; + this.resolution = target.resolution; + + // Check if the dom element has been set. If it has don't do anything. + if (this.interactionDOMElement !== null) return; + + this.setTargetDomElement (target.view); +}; + +/** + * Sets the DOM element which will receive mouse/touch events. This is useful for when you have other DOM + * elements on top of the renderers Canvas element. With this you'll be able to delegate another DOM element + * to receive those events + * + * @method setTargetDomElement + * @param domElement {DOMElement} the DOM element which will receive mouse and touch events + * @private + */ +PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) +{ + this.removeEvents(); + + if (window.navigator.msPointerEnabled) + { + // time to remove some of that zoom in ja.. + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; + } + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); + + window.addEventListener('mouseup', this.onMouseUp, true); +}; + +/** + * @method removeEvents + * @private + */ +PIXI.InteractionManager.prototype.removeEvents = function() +{ + if (!this.interactionDOMElement) return; + + this.interactionDOMElement.style['-ms-content-zooming'] = ''; + this.interactionDOMElement.style['-ms-touch-action'] = ''; + + this.interactionDOMElement.removeEventListener('mousemove', this.onMouseMove, true); + this.interactionDOMElement.removeEventListener('mousedown', this.onMouseDown, true); + this.interactionDOMElement.removeEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + this.interactionDOMElement.removeEventListener('touchstart', this.onTouchStart, true); + this.interactionDOMElement.removeEventListener('touchend', this.onTouchEnd, true); + this.interactionDOMElement.removeEventListener('touchmove', this.onTouchMove, true); + + this.interactionDOMElement = null; + + window.removeEventListener('mouseup', this.onMouseUp, true); +}; + +/** + * updates the state of interactive objects + * + * @method update + * @private + */ +PIXI.InteractionManager.prototype.update = function() +{ + if (!this.target) return; + + // frequency of 30fps?? + var now = Date.now(); + var diff = now - this.last; + diff = (diff * PIXI.INTERACTION_FREQUENCY ) / 1000; + if (diff < 1) return; + this.last = now; + + var i = 0; + + // ok.. so mouse events?? + // yes for now :) + // OPTIMISE - how often to check?? + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + // loop through interactive objects! + var length = this.interactiveItems.length; + var cursor = 'inherit'; + var over = false; + + for (i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + // OPTIMISATION - only calculate every time if the mousemove function exists.. + // OK so.. does the object have any other interactive functions? + // hit-test the clip! + // if (item.mouseover || item.mouseout || item.buttonMode) + // { + // ok so there are some functions so lets hit test it.. + item.__hit = this.hitTest(item, this.mouse); + this.mouse.target = item; + // ok so deal with interactions.. + // looks like there was a hit! + if (item.__hit && !over) + { + if (item.buttonMode) cursor = item.defaultCursor; + + if (!item.interactiveChildren) + { + over = true; + } + + if (!item.__isOver) + { + if (item.mouseover) + { + item.mouseover (this.mouse); + } + item.__isOver = true; + } + } + else + { + if (item.__isOver) + { + // roll out! + if (item.mouseout) + { + item.mouseout (this.mouse); + } + item.__isOver = false; + } + } + } + + if (this.currentCursorStyle !== cursor) + { + this.currentCursorStyle = cursor; + this.interactionDOMElement.style.cursor = cursor; + } +}; + +/** + * @method rebuildInteractiveGraph + * @private + */ +PIXI.InteractionManager.prototype.rebuildInteractiveGraph = function() +{ + this.dirty = false; + + var len = this.interactiveItems.length; + + for (var i = 0; i < len; i++) { + this.interactiveItems[i].interactiveChildren = false; + } + + this.interactiveItems = []; + + if (this.stage.interactive) + { + this.interactiveItems.push(this.stage); + } + + // Go through and collect all the objects that are interactive.. + this.collectInteractiveSprite(this.stage, this.stage); +}; + +/** + * Is called when the mouse moves across the renderer element + * + * @method onMouseMove + * @param event {Event} The DOM event of the mouse moving + * @private + */ +PIXI.InteractionManager.prototype.onMouseMove = function(event) +{ + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + this.mouse.originalEvent = event; + + // TODO optimize by not check EVERY TIME! maybe half as often? // + var rect = this.interactionDOMElement.getBoundingClientRect(); + + this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width) / this.resolution; + this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height) / this.resolution; + + var length = this.interactiveItems.length; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + // Call the function! + if (item.mousemove) + { + item.mousemove(this.mouse); + } + } +}; + +/** + * Is called when the mouse button is pressed down on the renderer element + * + * @method onMouseDown + * @param event {Event} The DOM event of a mouse button being pressed down + * @private + */ +PIXI.InteractionManager.prototype.onMouseDown = function(event) +{ + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + this.mouse.originalEvent = event; + + if (PIXI.AUTO_PREVENT_DEFAULT) + { + this.mouse.originalEvent.preventDefault(); + } + + // loop through interaction tree... + // hit test each item! -> + // get interactive items under point?? + //stage.__i + var length = this.interactiveItems.length; + + var e = this.mouse.originalEvent; + var isRightButton = e.button === 2 || e.which === 3; + var downFunction = isRightButton ? 'rightdown' : 'mousedown'; + var clickFunction = isRightButton ? 'rightclick' : 'click'; + var buttonIsDown = isRightButton ? '__rightIsDown' : '__mouseIsDown'; + var isDown = isRightButton ? '__isRightDown' : '__isDown'; + + // while + // hit test + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if (item[downFunction] || item[clickFunction]) + { + item[buttonIsDown] = true; + item.__hit = this.hitTest(item, this.mouse); + + if (item.__hit) + { + //call the function! + if (item[downFunction]) + { + item[downFunction](this.mouse); + } + item[isDown] = true; + + // just the one! + if (!item.interactiveChildren) break; + } + } + } +}; + +/** + * Is called when the mouse is moved out of the renderer element + * + * @method onMouseOut + * @param event {Event} The DOM event of a mouse being moved out + * @private + */ +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + this.mouse.originalEvent = event; + + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = 'inherit'; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + if (item.__isOver) + { + this.mouse.target = item; + if (item.mouseout) + { + item.mouseout(this.mouse); + } + item.__isOver = false; + } + } + + this.mouseOut = true; + + // move the mouse to an impossible position + this.mouse.global.x = -10000; + this.mouse.global.y = -10000; +}; + +/** + * Is called when the mouse button is released on the renderer element + * + * @method onMouseUp + * @param event {Event} The DOM event of a mouse button being released + * @private + */ +PIXI.InteractionManager.prototype.onMouseUp = function(event) +{ + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + this.mouse.originalEvent = event; + + var length = this.interactiveItems.length; + var up = false; + + var e = this.mouse.originalEvent; + var isRightButton = e.button === 2 || e.which === 3; + + var upFunction = isRightButton ? 'rightup' : 'mouseup'; + var clickFunction = isRightButton ? 'rightclick' : 'click'; + var upOutsideFunction = isRightButton ? 'rightupoutside' : 'mouseupoutside'; + var isDown = isRightButton ? '__isRightDown' : '__isDown'; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if (item[clickFunction] || item[upFunction] || item[upOutsideFunction]) + { + item.__hit = this.hitTest(item, this.mouse); + + if (item.__hit && !up) + { + //call the function! + if (item[upFunction]) + { + item[upFunction](this.mouse); + } + if (item[isDown]) + { + if (item[clickFunction]) + { + item[clickFunction](this.mouse); + } + } + + if (!item.interactiveChildren) + { + up = true; + } + } + else + { + if (item[isDown]) + { + if (item[upOutsideFunction]) item[upOutsideFunction](this.mouse); + } + } + + item[isDown] = false; + } + } +}; + +/** + * Tests if the current mouse coordinates hit a sprite + * + * @method hitTest + * @param item {DisplayObject} The displayObject to test for a hit + * @param interactionData {InteractionData} The interactionData object to update in the case there is a hit + * @private + */ +PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) +{ + var global = interactionData.global; + + if (!item.worldVisible) + { + return false; + } + + // map the global point to local space. + item.worldTransform.applyInverse(global, this._tempPoint); + + var x = this._tempPoint.x, + y = this._tempPoint.y, + i; + + interactionData.target = item; + + //a sprite or display object with a hit area defined + if (item.hitArea && item.hitArea.contains) + { + return item.hitArea.contains(x, y); + } + // a sprite with no hitarea defined + else if(item instanceof PIXI.Sprite) + { + var width = item.texture.frame.width; + var height = item.texture.frame.height; + var x1 = -width * item.anchor.x; + var y1; + + if (x > x1 && x < x1 + width) + { + y1 = -height * item.anchor.y; + + if (y > y1 && y < y1 + height) + { + // set the target property if a hit is true! + return true; + } + } + } + else if(item instanceof PIXI.Graphics) + { + var graphicsData = item.graphicsData; + for (i = 0; i < graphicsData.length; i++) + { + var data = graphicsData[i]; + if(!data.fill)continue; + + // only deal with fills.. + if(data.shape) + { + if(data.shape.contains(x, y)) + { + //interactionData.target = item; + return true; + } + } + } + } + + var length = item.children.length; + + for (i = 0; i < length; i++) + { + var tempItem = item.children[i]; + var hit = this.hitTest(tempItem, interactionData); + if (hit) + { + // hmm.. TODO SET CORRECT TARGET? + interactionData.target = item; + return true; + } + } + return false; +}; + +/** + * Is called when a touch is moved across the renderer element + * + * @method onTouchMove + * @param event {Event} The DOM event of a touch moving across the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchMove = function(event) +{ + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + var touchData; + var i = 0; + + for (i = 0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + touchData = this.touches[touchEvent.identifier]; + touchData.originalEvent = event; + + // update the touch position + touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; + touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; + if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) + { + //Support for CocoonJS fullscreen scale modes + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + + for (var j = 0; j < this.interactiveItems.length; j++) + { + var item = this.interactiveItems[j]; + if (item.touchmove && item.__touchData && item.__touchData[touchEvent.identifier]) + { + item.touchmove(touchData); + } + } + } +}; + +/** + * Is called when a touch is started on the renderer element + * + * @method onTouchStart + * @param event {Event} The DOM event of a touch starting on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchStart = function(event) +{ + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + var rect = this.interactionDOMElement.getBoundingClientRect(); + + if (PIXI.AUTO_PREVENT_DEFAULT) + { + event.preventDefault(); + } + + var changedTouches = event.changedTouches; + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + + var touchData = this.pool.pop(); + if (!touchData) + { + touchData = new PIXI.InteractionData(); + } + + touchData.originalEvent = event; + + this.touches[touchEvent.identifier] = touchData; + touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; + touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; + if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) + { + //Support for CocoonJS fullscreen scale modes + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + + var length = this.interactiveItems.length; + + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + + if (item.touchstart || item.tap) + { + item.__hit = this.hitTest(item, touchData); + + if (item.__hit) + { + //call the function! + if (item.touchstart)item.touchstart(touchData); + item.__isDown = true; + item.__touchData = item.__touchData || {}; + item.__touchData[touchEvent.identifier] = touchData; + + if (!item.interactiveChildren) break; + } + } + } + } +}; + +/** + * Is called when a touch is ended on the renderer element + * + * @method onTouchEnd + * @param event {Event} The DOM event of a touch ending on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchEnd = function(event) +{ + if (this.dirty) + { + this.rebuildInteractiveGraph(); + } + + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + var touchData = this.touches[touchEvent.identifier]; + var up = false; + touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; + touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; + if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) + { + //Support for CocoonJS fullscreen scale modes + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + + var length = this.interactiveItems.length; + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + + if (item.__touchData && item.__touchData[touchEvent.identifier]) + { + + item.__hit = this.hitTest(item, item.__touchData[touchEvent.identifier]); + + // so this one WAS down... + touchData.originalEvent = event; + // hitTest?? + + if (item.touchend || item.tap) + { + if (item.__hit && !up) + { + if (item.touchend) + { + item.touchend(touchData); + } + if (item.__isDown && item.tap) + { + item.tap(touchData); + } + if (!item.interactiveChildren) + { + up = true; + } + } + else + { + if (item.__isDown && item.touchendoutside) + { + item.touchendoutside(touchData); + } + } + + item.__isDown = false; + } + + item.__touchData[touchEvent.identifier] = null; + } + } + // remove the touch.. + this.pool.push(touchData); + this.touches[touchEvent.identifier] = null; + } +}; diff --git a/app/Lib/Vendor/src/pixi/Intro.js b/app/Lib/Vendor/src/pixi/Intro.js new file mode 100644 index 0000000..07d01da --- /dev/null +++ b/app/Lib/Vendor/src/pixi/Intro.js @@ -0,0 +1,7 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +(function(){ + + var root = this; diff --git a/app/Lib/Vendor/src/pixi/Outro.js b/app/Lib/Vendor/src/pixi/Outro.js new file mode 100644 index 0000000..bf38bbc --- /dev/null +++ b/app/Lib/Vendor/src/pixi/Outro.js @@ -0,0 +1,15 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = PIXI; + } + exports.PIXI = PIXI; + } else if (typeof define !== 'undefined' && define.amd) { + define(PIXI); + } else { + root.PIXI = PIXI; + } +}).call(this); \ No newline at end of file diff --git a/app/Lib/Vendor/src/pixi/Pixi.js b/app/Lib/Vendor/src/pixi/Pixi.js new file mode 100644 index 0000000..52f001c --- /dev/null +++ b/app/Lib/Vendor/src/pixi/Pixi.js @@ -0,0 +1,213 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The [pixi.js](http://www.pixijs.com/) module/namespace. + * + * @module PIXI + */ + +/** + * Namespace-class for [pixi.js](http://www.pixijs.com/). + * + * Contains assorted static properties and enumerations. + * + * @class PIXI + * @static + */ +var PIXI = PIXI || {}; + +/** + * @property {Number} WEBGL_RENDERER + * @protected + * @static + */ +PIXI.WEBGL_RENDERER = 0; +/** + * @property {Number} CANVAS_RENDERER + * @protected + * @static + */ +PIXI.CANVAS_RENDERER = 1; + +/** + * Version of pixi that is loaded. + * @property {String} VERSION + * @static + */ +PIXI.VERSION = "v2.2.0"; + +/** + * Various blend modes supported by pixi. IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes. + * @property {Object} blendModes + * @property {Number} blendModes.NORMAL + * @property {Number} blendModes.ADD + * @property {Number} blendModes.MULTIPLY + * @property {Number} blendModes.SCREEN + * @property {Number} blendModes.OVERLAY + * @property {Number} blendModes.DARKEN + * @property {Number} blendModes.LIGHTEN + * @property {Number} blendModes.COLOR_DODGE + * @property {Number} blendModes.COLOR_BURN + * @property {Number} blendModes.HARD_LIGHT + * @property {Number} blendModes.SOFT_LIGHT + * @property {Number} blendModes.DIFFERENCE + * @property {Number} blendModes.EXCLUSION + * @property {Number} blendModes.HUE + * @property {Number} blendModes.SATURATION + * @property {Number} blendModes.COLOR + * @property {Number} blendModes.LUMINOSITY + * @static + */ +PIXI.blendModes = { + NORMAL:0, + ADD:1, + MULTIPLY:2, + SCREEN:3, + OVERLAY:4, + DARKEN:5, + LIGHTEN:6, + COLOR_DODGE:7, + COLOR_BURN:8, + HARD_LIGHT:9, + SOFT_LIGHT:10, + DIFFERENCE:11, + EXCLUSION:12, + HUE:13, + SATURATION:14, + COLOR:15, + LUMINOSITY:16 +}; + +/** + * The scale modes that are supported by pixi. + * + * The DEFAULT scale mode affects the default scaling mode of future operations. + * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability. + * + * @property {Object} scaleModes + * @property {Number} scaleModes.DEFAULT=LINEAR + * @property {Number} scaleModes.LINEAR Smooth scaling + * @property {Number} scaleModes.NEAREST Pixelating scaling + * @static + */ +PIXI.scaleModes = { + DEFAULT:0, + LINEAR:0, + NEAREST:1 +}; + +// used to create uids for various pixi objects.. +PIXI._UID = 0; + +if(typeof(Float32Array) != 'undefined') +{ + PIXI.Float32Array = Float32Array; + PIXI.Uint16Array = Uint16Array; + + // Uint32Array and ArrayBuffer only used by WebGL renderer + // We can suppose that if WebGL is supported then typed arrays are supported too + // as they predate WebGL support for all browsers: + // see typed arrays support: http://caniuse.com/#search=TypedArrays + // see WebGL support: http://caniuse.com/#search=WebGL + PIXI.Uint32Array = Uint32Array; + PIXI.ArrayBuffer = ArrayBuffer; +} +else +{ + PIXI.Float32Array = Array; + PIXI.Uint16Array = Array; +} + +// interaction frequency +PIXI.INTERACTION_FREQUENCY = 30; +PIXI.AUTO_PREVENT_DEFAULT = true; + +/** + * @property {Number} PI_2 + * @static + */ +PIXI.PI_2 = Math.PI * 2; + +/** + * @property {Number} RAD_TO_DEG + * @static + */ +PIXI.RAD_TO_DEG = 180 / Math.PI; + +/** + * @property {Number} DEG_TO_RAD + * @static + */ +PIXI.DEG_TO_RAD = Math.PI / 180; + +/** + * @property {String} RETINA_PREFIX + * @protected + * @static + */ +PIXI.RETINA_PREFIX = "@2x"; +//PIXI.SCALE_PREFIX "@x%%"; + +/** + * If true the default pixi startup (console) banner message will be suppressed. + * + * @property {Boolean} dontSayHello + * @default false + * @static + */ +PIXI.dontSayHello = false; + +/** + * The default render options if none are supplied to + * {{#crossLink "WebGLRenderer"}}{{/crossLink}} or {{#crossLink "CanvasRenderer"}}{{/crossLink}}. + * + * @property {Object} defaultRenderOptions + * @property {Object} defaultRenderOptions.view=null + * @property {Boolean} defaultRenderOptions.transparent=false + * @property {Boolean} defaultRenderOptions.antialias=false + * @property {Boolean} defaultRenderOptions.preserveDrawingBuffer=false + * @property {Number} defaultRenderOptions.resolution=1 + * @property {Boolean} defaultRenderOptions.clearBeforeRender=true + * @property {Boolean} defaultRenderOptions.autoResize=false + * @static + */ +PIXI.defaultRenderOptions = { + view:null, + transparent:false, + antialias:false, + preserveDrawingBuffer:false, + resolution:1, + clearBeforeRender:true, + autoResize:false +} + +PIXI.sayHello = function (type) +{ + if(PIXI.dontSayHello)return; + + if ( navigator.userAgent.toLowerCase().indexOf('chrome') > -1 ) + { + var args = [ + '%c %c %c Pixi.js ' + PIXI.VERSION + ' - ' + type + ' %c ' + ' %c ' + ' http://www.pixijs.com/ %c %c ♥%c♥%c♥ ', + 'background: #ff66a5', + 'background: #ff66a5', + 'color: #ff66a5; background: #030307;', + 'background: #ff66a5', + 'background: #ffc3dc', + 'background: #ff66a5', + 'color: #ff2424; background: #fff', + 'color: #ff2424; background: #fff', + 'color: #ff2424; background: #fff' + ]; + + console.log.apply(console, args); + } + else if (window['console']) + { + console.log('Pixi.js ' + PIXI.VERSION + ' - http://www.pixijs.com/'); + } + + PIXI.dontSayHello = true; +}; diff --git a/app/Lib/Vendor/src/pixi/display/DisplayObject.js b/app/Lib/Vendor/src/pixi/display/DisplayObject.js new file mode 100755 index 0000000..58b375c --- /dev/null +++ b/app/Lib/Vendor/src/pixi/display/DisplayObject.js @@ -0,0 +1,765 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The base class for all objects that are rendered on the screen. + * This is an abstract class and should not be used on its own rather it should be extended. + * + * @class DisplayObject + * @constructor + */ +PIXI.DisplayObject = function() +{ + /** + * The coordinate of the object relative to the local coordinates of the parent. + * + * @property position + * @type Point + */ + this.position = new PIXI.Point(); + + /** + * The scale factor of the object. + * + * @property scale + * @type Point + */ + this.scale = new PIXI.Point(1,1);//{x:1, y:1}; + + /** + * The pivot point of the displayObject that it rotates around + * + * @property pivot + * @type Point + */ + this.pivot = new PIXI.Point(0,0); + + /** + * The rotation of the object in radians. + * + * @property rotation + * @type Number + */ + this.rotation = 0; + + /** + * The opacity of the object. + * + * @property alpha + * @type Number + */ + this.alpha = 1; + + /** + * The visibility of the object. + * + * @property visible + * @type Boolean + */ + this.visible = true; + + /** + * This is the defined area that will pick up mouse / touch events. It is null by default. + * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children) + * + * @property hitArea + * @type Rectangle|Circle|Ellipse|Polygon + */ + this.hitArea = null; + + /** + * This is used to indicate if the displayObject should display a mouse hand cursor on rollover + * + * @property buttonMode + * @type Boolean + */ + this.buttonMode = false; + + /** + * Can this object be rendered + * + * @property renderable + * @type Boolean + */ + this.renderable = false; + + /** + * [read-only] The display object container that contains this display object. + * + * @property parent + * @type DisplayObjectContainer + * @readOnly + */ + this.parent = null; + + /** + * [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage. + * + * @property stage + * @type Stage + * @readOnly + */ + this.stage = null; + + /** + * [read-only] The multiplied alpha of the displayObject + * + * @property worldAlpha + * @type Number + * @readOnly + */ + this.worldAlpha = 1; + + /** + * [read-only] Whether or not the object is interactive, do not toggle directly! use the `interactive` property + * + * @property _interactive + * @type Boolean + * @readOnly + * @private + */ + this._interactive = false; + + /** + * This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true + * + * @property defaultCursor + * @type String + * + */ + this.defaultCursor = 'pointer'; + + /** + * [read-only] Current transform of the object based on world (parent) factors + * + * @property worldTransform + * @type Matrix + * @readOnly + * @private + */ + this.worldTransform = new PIXI.Matrix(); + + /** + * cached sin rotation and cos rotation + * + * @property _sr + * @type Number + * @private + */ + this._sr = 0; + + /** + * cached sin rotation and cos rotation + * + * @property _cr + * @type Number + * @private + */ + this._cr = 1; + + /** + * The area the filter is applied to like the hitArea this is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @property filterArea + * @type Rectangle + */ + this.filterArea = null;//new PIXI.Rectangle(0,0,1,1); + + /** + * The original, cached bounds of the object + * + * @property _bounds + * @type Rectangle + * @private + */ + this._bounds = new PIXI.Rectangle(0, 0, 1, 1); + + /** + * The most up-to-date bounds of the object + * + * @property _currentBounds + * @type Rectangle + * @private + */ + this._currentBounds = null; + + /** + * The original, cached mask of the object + * + * @property _currentBounds + * @type Rectangle + * @private + */ + this._mask = null; + + /** + * Cached internal flag. + * + * @property _cacheAsBitmap + * @type Boolean + * @private + */ + this._cacheAsBitmap = false; + + /** + * Cached internal flag. + * + * @property _cacheIsDirty + * @type Boolean + * @private + */ + this._cacheIsDirty = false; + + + /* + * MOUSE Callbacks + */ + + /** + * A callback that is used when the users mouse rolls over the displayObject + * @method mouseover + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the users mouse leaves the displayObject + * @method mouseout + * @param interactionData {InteractionData} + */ + + //Left button + /** + * A callback that is used when the users clicks on the displayObject with their mouse's left button + * @method click + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user clicks the mouse's left button down over the sprite + * @method mousedown + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's left button that was over the displayObject + * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject + * @method mouseup + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's left button that was over the displayObject but is no longer over the displayObject + * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject + * @method mouseupoutside + * @param interactionData {InteractionData} + */ + + //Right button + /** + * A callback that is used when the users clicks on the displayObject with their mouse's right button + * @method rightclick + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user clicks the mouse's right button down over the sprite + * @method rightdown + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's right button that was over the displayObject + * for this callback to be fired the mouse's right button must have been pressed down over the displayObject + * @method rightup + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse's right button that was over the displayObject but is no longer over the displayObject + * for this callback to be fired, the mouse's right button must have been pressed down over the displayObject + * @method rightupoutside + * @param interactionData {InteractionData} + */ + + /* + * TOUCH Callbacks + */ + + /** + * A callback that is used when the users taps on the sprite with their finger + * basically a touch version of click + * @method tap + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user touches over the displayObject + * @method touchstart + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases a touch over the displayObject + * @method touchend + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the touch that was over the displayObject + * for this callback to be fired, The touch must have started over the sprite + * @method touchendoutside + * @param interactionData {InteractionData} + */ +}; + +// constructor +PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; + +/** + * Indicates if the sprite will have touch and mouse interactivity. It is false by default + * + * @property interactive + * @type Boolean + * @default false + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { + get: function() { + return this._interactive; + }, + set: function(value) { + this._interactive = value; + + // TODO more to be done here.. + // need to sort out a re-crawl! + if(this.stage)this.stage.dirty = true; + } +}); + +/** + * [read-only] Indicates if the sprite is globally visible. + * + * @property worldVisible + * @type Boolean + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', { + get: function() { + var item = this; + + do + { + if(!item.visible)return false; + item = item.parent; + } + while(item); + + return true; + } +}); + +/** + * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it. + * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping. + * To remove a mask, set this property to null. + * + * @property mask + * @type Graphics + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { + get: function() { + return this._mask; + }, + set: function(value) { + + if(this._mask)this._mask.isMask = false; + this._mask = value; + if(this._mask)this._mask.isMask = true; + } +}); + +/** + * Sets the filters for the displayObject. + * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer. + * To remove filters simply set this property to 'null' + * @property filters + * @type Array(Filter) + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + + get: function() { + return this._filters; + }, + + set: function(value) { + + if(value) + { + // now put all the passes in one place.. + var passes = []; + for (var i = 0; i < value.length; i++) + { + var filterPasses = value[i].passes; + for (var j = 0; j < filterPasses.length; j++) + { + passes.push(filterPasses[j]); + } + } + + // TODO change this as it is legacy + this._filterBlock = {target:this, filterPasses:passes}; + } + + this._filters = value; + } +}); + +/** + * Set if this display object is cached as a bitmap. + * This basically takes a snap shot of the display object as it is at that moment. It can provide a performance benefit for complex static displayObjects. + * To remove simply set this property to 'null' + * @property cacheAsBitmap + * @type Boolean + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'cacheAsBitmap', { + + get: function() { + return this._cacheAsBitmap; + }, + + set: function(value) { + + if(this._cacheAsBitmap === value)return; + + if(value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } +}); + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.DisplayObject.prototype.updateTransform = function() +{ + // create some matrix refs for easy access + var pt = this.parent.worldTransform; + var wt = this.worldTransform; + + // temporary matrix variables + var a, b, c, d, tx, ty; + + // so if rotation is between 0 then we can simplify the multiplication process.. + if(this.rotation % PIXI.PI_2) + { + // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes + if(this.rotation !== this.rotationCache) + { + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); + } + + // get the matrix values of the displayobject based on its transform properties.. + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; + + // check for pivot.. not often used so geared towards that fact! + if(this.pivot.x || this.pivot.y) + { + tx -= this.pivot.x * a + this.pivot.y * c; + ty -= this.pivot.x * b + this.pivot.y * d; + } + + // concat the parent matrix with the objects transform. + wt.a = a * pt.a + b * pt.c; + wt.b = a * pt.b + b * pt.d; + wt.c = c * pt.a + d * pt.c; + wt.d = c * pt.b + d * pt.d; + wt.tx = tx * pt.a + ty * pt.c + pt.tx; + wt.ty = tx * pt.b + ty * pt.d + pt.ty; + + + } + else + { + // lets do the fast version as we know there is no rotation.. + a = this.scale.x; + d = this.scale.y; + + tx = this.position.x - this.pivot.x * a; + ty = this.position.y - this.pivot.y * d; + + wt.a = a * pt.a; + wt.b = a * pt.b; + wt.c = d * pt.c; + wt.d = d * pt.d; + wt.tx = tx * pt.a + ty * pt.c + pt.tx; + wt.ty = tx * pt.b + ty * pt.d + pt.ty; + } + + // multiply the alphas.. + this.worldAlpha = this.alpha * this.parent.worldAlpha; +}; + +// performance increase to avoid using call.. (10x faster) +PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform; + +/** + * Retrieves the bounds of the displayObject as a rectangle object + * + * @method getBounds + * @param matrix {Matrix} + * @return {Rectangle} the rectangular bounding area + */ +PIXI.DisplayObject.prototype.getBounds = function(matrix) +{ + matrix = matrix;//just to get passed js hinting (and preserve inheritance) + return PIXI.EmptyRectangle; +}; + +/** + * Retrieves the local bounds of the displayObject as a rectangle object + * + * @method getLocalBounds + * @return {Rectangle} the rectangular bounding area + */ +PIXI.DisplayObject.prototype.getLocalBounds = function() +{ + return this.getBounds(PIXI.identityMatrix);///PIXI.EmptyRectangle(); +}; + +/** + * Sets the object's stage reference, the stage this object is connected to + * + * @method setStageReference + * @param stage {Stage} the stage that the object will have as its current stage reference + */ +PIXI.DisplayObject.prototype.setStageReference = function(stage) +{ + this.stage = stage; + if(this._interactive)this.stage.dirty = true; +}; + +/** + * Useful function that returns a texture of the displayObject object that can then be used to create sprites + * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. + * + * @method generateTexture + * @param resolution {Number} The resolution of the texture being generated + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used to generate the texture. + * @return {Texture} a texture of the graphics object + */ +PIXI.DisplayObject.prototype.generateTexture = function(resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + PIXI.DisplayObject._tempMatrix.tx = -bounds.x; + PIXI.DisplayObject._tempMatrix.ty = -bounds.y; + + renderTexture.render(this, PIXI.DisplayObject._tempMatrix); + + return renderTexture; +}; + +/** + * Generates and updates the cached sprite for this object. + * + * @method updateCache + */ +PIXI.DisplayObject.prototype.updateCache = function() +{ + this._generateCachedSprite(); +}; + +/** + * Calculates the global position of the display object + * + * @method toGlobal + * @param position {Point} The world origin to calculate from + * @return {Point} A point object representing the position of this object + */ +PIXI.DisplayObject.prototype.toGlobal = function(position) +{ + // don't need to u[date the lot + this.displayObjectUpdateTransform(); + return this.worldTransform.apply(position); +}; + +/** + * Calculates the local position of the display object relative to another point + * + * @method toLocal + * @param position {Point} The world origin to calculate from + * @param [from] {DisplayObject} The DisplayObject to calculate the global position from + * @return {Point} A point object representing the position of this object + */ +PIXI.DisplayObject.prototype.toLocal = function(position, from) +{ + // + if (from) + { + position = from.toGlobal(position); + } + + // don't need to u[date the lot + this.displayObjectUpdateTransform(); + return this.worldTransform.applyInverse(position); +}; + +/** + * Internal method. + * + * @method _renderCachedSprite + * @param renderSession {Object} The render session + * @private + */ +PIXI.DisplayObject.prototype._renderCachedSprite = function(renderSession) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if(renderSession.gl) + { + PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + } + else + { + PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + } +}; + +/** + * Internal method. + * + * @method _generateCachedSprite + * @private + */ +PIXI.DisplayObject.prototype._generateCachedSprite = function() +{ + this._cacheAsBitmap = false; + var bounds = this.getLocalBounds(); + + if(!this._cachedSprite) + { + var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0);//, renderSession.renderer); + + this._cachedSprite = new PIXI.Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + //REMOVE filter! + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + PIXI.DisplayObject._tempMatrix.tx = -bounds.x; + PIXI.DisplayObject._tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, PIXI.DisplayObject._tempMatrix, true); + + this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); + this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); + + this._filters = tempFilters; + + this._cacheAsBitmap = true; +}; + +/** +* Destroys the cached sprite. +* +* @method _destroyCachedSprite +* @private +*/ +PIXI.DisplayObject.prototype._destroyCachedSprite = function() +{ + if(!this._cachedSprite)return; + + this._cachedSprite.texture.destroy(true); + + // TODO could be object pooled! + this._cachedSprite = null; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + +/** +* Renders the object using the Canvas renderer +* +* @method _renderCanvas +* @param renderSession {RenderSession} +* @private +*/ +PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + + +PIXI.DisplayObject._tempMatrix = new PIXI.Matrix(); + +/** + * The position of the displayObject on the x axis relative to the local coordinates of the parent. + * + * @property x + * @type Number + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'x', { + get: function() { + return this.position.x; + }, + set: function(value) { + this.position.x = value; + } +}); + +/** + * The position of the displayObject on the y axis relative to the local coordinates of the parent. + * + * @property y + * @type Number + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'y', { + get: function() { + return this.position.y; + }, + set: function(value) { + this.position.y = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/display/DisplayObjectContainer.js b/app/Lib/Vendor/src/pixi/display/DisplayObjectContainer.js new file mode 100644 index 0000000..e3ccc20 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/display/DisplayObjectContainer.js @@ -0,0 +1,515 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A DisplayObjectContainer represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class DisplayObjectContainer + * @extends DisplayObject + * @constructor + */ +PIXI.DisplayObjectContainer = function() +{ + PIXI.DisplayObject.call( this ); + + /** + * [read-only] The array of children of this container. + * + * @property children + * @type Array(DisplayObject) + * @readOnly + */ + this.children = []; + + // fast access to update transform.. + +}; + +// constructor +PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); +PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; + + +/** + * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { + get: function() { + return this.scale.x * this.getLocalBounds().width; + }, + set: function(value) { + + var width = this.getLocalBounds().width; + + if(width !== 0) + { + this.scale.x = value / width; + } + else + { + this.scale.x = 1; + } + + + this._width = value; + } +}); + +/** + * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { + get: function() { + return this.scale.y * this.getLocalBounds().height; + }, + set: function(value) { + + var height = this.getLocalBounds().height; + + if(height !== 0) + { + this.scale.y = value / height ; + } + else + { + this.scale.y = 1; + } + + this._height = value; + } +}); + +/** + * Adds a child to the container. + * + * @method addChild + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +PIXI.DisplayObjectContainer.prototype.addChild = function(child) +{ + return this.addChildAt(child, this.children.length); +}; + +/** + * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown + * + * @method addChildAt + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) +{ + if(index >= 0 && index <= this.children.length) + { + if(child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + if(this.stage)child.setStageReference(this.stage); + + return child; + } + else + { + throw new Error(child + 'addChildAt: The index '+ index +' supplied is out of bounds ' + this.children.length); + } +}; + +/** + * Swaps the position of 2 Display Objects within this container. + * + * @method swapChildren + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) +{ + if(child === child2) { + return; + } + + var index1 = this.getChildIndex(child); + var index2 = this.getChildIndex(child2); + + if(index1 < 0 || index2 < 0) { + throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); + } + + this.children[index1] = child2; + this.children[index2] = child; + +}; + +/** + * Returns the index position of a child DisplayObject instance + * + * @method getChildIndex + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +PIXI.DisplayObjectContainer.prototype.getChildIndex = function(child) +{ + var index = this.children.indexOf(child); + if (index === -1) + { + throw new Error('The supplied DisplayObject must be a child of the caller'); + } + return index; +}; + +/** + * Changes the position of an existing child in the display object container + * + * @method setChildIndex + * @param child {DisplayObject} The child DisplayObject instance for which you want to change the index number + * @param index {Number} The resulting index number for the child display object + */ +PIXI.DisplayObjectContainer.prototype.setChildIndex = function(child, index) +{ + if (index < 0 || index >= this.children.length) + { + throw new Error('The supplied index is out of bounds'); + } + var currentIndex = this.getChildIndex(child); + this.children.splice(currentIndex, 1); //remove from old position + this.children.splice(index, 0, child); //add at new position +}; + +/** + * Returns the child at the specified index + * + * @method getChildAt + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) +{ + if (index < 0 || index >= this.children.length) + { + throw new Error('getChildAt: Supplied index '+ index +' does not exist in the child list, or the supplied DisplayObject must be a child of the caller'); + } + return this.children[index]; + +}; + +/** + * Removes a child from the container. + * + * @method removeChild + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +PIXI.DisplayObjectContainer.prototype.removeChild = function(child) +{ + var index = this.children.indexOf( child ); + if(index === -1)return; + + return this.removeChildAt( index ); +}; + +/** + * Removes a child from the specified index position. + * + * @method removeChildAt + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +PIXI.DisplayObjectContainer.prototype.removeChildAt = function(index) +{ + var child = this.getChildAt( index ); + if(this.stage) + child.removeStageReference(); + + child.parent = undefined; + this.children.splice( index, 1 ); + return child; +}; + +/** +* Removes all children from this container that are within the begin and end indexes. +* +* @method removeChildren +* @param beginIndex {Number} The beginning position. Default value is 0. +* @param endIndex {Number} The ending position. Default value is size of the container. +*/ +PIXI.DisplayObjectContainer.prototype.removeChildren = function(beginIndex, endIndex) +{ + var begin = beginIndex || 0; + var end = typeof endIndex === 'number' ? endIndex : this.children.length; + var range = end - begin; + + if (range > 0 && range <= end) + { + var removed = this.children.splice(begin, range); + for (var i = 0; i < removed.length; i++) { + var child = removed[i]; + if(this.stage) + child.removeStageReference(); + child.parent = undefined; + } + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new Error( 'removeChildren: Range Error, numeric values are outside the acceptable range' ); + } +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @method updateTransform + * @private + */ +PIXI.DisplayObjectContainer.prototype.updateTransform = function() +{ + if(!this.visible)return; + + this.displayObjectUpdateTransform(); + + //PIXI.DisplayObject.prototype.updateTransform.call( this ); + + if(this._cacheAsBitmap)return; + + for(var i=0,j=this.children.length; i childMaxX ? maxX : childMaxX; + maxY = maxY > childMaxY ? maxY : childMaxY; + } + + if(!childVisible) + return PIXI.EmptyRectangle; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.y = minY; + bounds.width = maxX - minX; + bounds.height = maxY - minY; + + // TODO: store a reference so that if this function gets called again in the render cycle we do not have to recalculate + //this._currentBounds = bounds; + + return bounds; +}; + +/** + * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. The calculation takes all visible children into consideration. + * + * @method getLocalBounds + * @return {Rectangle} The rectangular bounding area + */ +PIXI.DisplayObjectContainer.prototype.getLocalBounds = function() +{ + var matrixCache = this.worldTransform; + + this.worldTransform = PIXI.identityMatrix; + + for(var i=0,j=this.children.length; i= this.textures.length) + { + this.gotoAndStop(this.textures.length - 1); + if(this.onComplete) + { + this.onComplete(); + } + } +}; + +/** + * A short hand way of creating a movieclip from an array of frame ids + * + * @static + * @method fromFrames + * @param frames {Array} the array of frames ids the movieclip will use as its texture frames + */ +PIXI.MovieClip.fromFrames = function(frames) +{ + var textures = []; + + for (var i = 0; i < frames.length; i++) + { + textures.push(new PIXI.Texture.fromFrame(frames[i])); + } + + return new PIXI.MovieClip(textures); +}; + +/** + * A short hand way of creating a movieclip from an array of image ids + * + * @static + * @method fromImages + * @param frames {Array} the array of image ids the movieclip will use as its texture frames + */ +PIXI.MovieClip.fromImages = function(images) +{ + var textures = []; + + for (var i = 0; i < images.length; i++) + { + textures.push(new PIXI.Texture.fromImage(images[i])); + } + + return new PIXI.MovieClip(textures); +}; diff --git a/app/Lib/Vendor/src/pixi/display/Sprite.js b/app/Lib/Vendor/src/pixi/display/Sprite.js new file mode 100644 index 0000000..aab559a --- /dev/null +++ b/app/Lib/Vendor/src/pixi/display/Sprite.js @@ -0,0 +1,471 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The Sprite object is the base for all textured objects that are rendered to the screen + * + * @class Sprite + * @extends DisplayObjectContainer + * @constructor + * @param texture {Texture} The texture for this sprite + * + * A sprite can be created directly from an image like this : + * var sprite = new PIXI.Sprite.fromImage('assets/image.png'); + * yourStage.addChild(sprite); + * then obviously don't forget to add it to the stage you have already created + */ +PIXI.Sprite = function(texture) +{ + PIXI.DisplayObjectContainer.call( this ); + + /** + * The anchor sets the origin point of the texture. + * The default is 0,0 this means the texture's origin is the top left + * Setting than anchor to 0.5,0.5 means the textures origin is centered + * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right corner + * + * @property anchor + * @type Point + */ + this.anchor = new PIXI.Point(); + + /** + * The texture that the sprite is using + * + * @property texture + * @type Texture + */ + this.texture = texture || PIXI.Texture.emptyTexture; + + /** + * The width of the sprite (this is initially set by the texture) + * + * @property _width + * @type Number + * @private + */ + this._width = 0; + + /** + * The height of the sprite (this is initially set by the texture) + * + * @property _height + * @type Number + * @private + */ + this._height = 0; + + /** + * The tint applied to the sprite. This is a hex value. A value of 0xFFFFFF will remove any tint effect. + * + * @property tint + * @type Number + * @default 0xFFFFFF + */ + this.tint = 0xFFFFFF; + + /** + * The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode. + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ + this.blendMode = PIXI.blendModes.NORMAL; + + /** + * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * + * @property shader + * @type AbstractFilter + * @default null + */ + this.shader = null; + + if(this.texture.baseTexture.hasLoaded) + { + this.onTextureUpdate(); + } + else + { + this.texture.on( 'update', this.onTextureUpdate.bind(this) ); + } + + this.renderable = true; + +}; + +// constructor +PIXI.Sprite.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Sprite.prototype.constructor = PIXI.Sprite; + +/** + * The width of the sprite, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.Sprite.prototype, 'width', { + get: function() { + return this.scale.x * this.texture.frame.width; + }, + set: function(value) { + this.scale.x = value / this.texture.frame.width; + this._width = value; + } +}); + +/** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.Sprite.prototype, 'height', { + get: function() { + return this.scale.y * this.texture.frame.height; + }, + set: function(value) { + this.scale.y = value / this.texture.frame.height; + this._height = value; + } +}); + +/** + * Sets the texture of the sprite + * + * @method setTexture + * @param texture {Texture} The PIXI texture that is displayed by the sprite + */ +PIXI.Sprite.prototype.setTexture = function(texture) +{ + this.texture = texture; + this.cachedTint = 0xFFFFFF; +}; + +/** + * When the texture is updated, this event will fire to update the scale and frame + * + * @method onTextureUpdate + * @param event + * @private + */ +PIXI.Sprite.prototype.onTextureUpdate = function() +{ + // so if _width is 0 then width was not set.. + if(this._width)this.scale.x = this._width / this.texture.frame.width; + if(this._height)this.scale.y = this._height / this.texture.frame.height; + + //this.updateFrame = true; +}; + +/** +* Returns the bounds of the Sprite as a rectangle. The bounds calculation takes the worldTransform into account. +* +* @method getBounds +* @param matrix {Matrix} the transformation matrix of the sprite +* @return {Rectangle} the framing rectangle +*/ +PIXI.Sprite.prototype.getBounds = function(matrix) +{ + var width = this.texture.frame.width; + var height = this.texture.frame.height; + + var w0 = width * (1-this.anchor.x); + var w1 = width * -this.anchor.x; + + var h0 = height * (1-this.anchor.y); + var h1 = height * -this.anchor.y; + + var worldTransform = matrix || this.worldTransform ; + + var a = worldTransform.a; + var b = worldTransform.b; + var c = worldTransform.c; + var d = worldTransform.d; + var tx = worldTransform.tx; + var ty = worldTransform.ty; + + var maxX = -Infinity; + var maxY = -Infinity; + + var minX = Infinity; + var minY = Infinity; + + if(b === 0 && c === 0) + { + // scale may be negative! + if(a < 0)a *= -1; + if(d < 0)d *= -1; + + // this means there is no rotation going on right? RIGHT? + // if thats the case then we can avoid checking the bound values! yay + minX = a * w1 + tx; + maxX = a * w0 + tx; + minY = d * h1 + ty; + maxY = d * h0 + ty; + } + else + { + var x1 = a * w1 + c * h1 + tx; + var y1 = d * h1 + b * w1 + ty; + + var x2 = a * w0 + c * h1 + tx; + var y2 = d * h1 + b * w0 + ty; + + var x3 = a * w0 + c * h0 + tx; + var y3 = d * h0 + b * w0 + ty; + + var x4 = a * w1 + c * h0 + tx; + var y4 = d * h0 + b * w1 + ty; + + minX = x1 < minX ? x1 : minX; + minX = x2 < minX ? x2 : minX; + minX = x3 < minX ? x3 : minX; + minX = x4 < minX ? x4 : minX; + + minY = y1 < minY ? y1 : minY; + minY = y2 < minY ? y2 : minY; + minY = y3 < minY ? y3 : minY; + minY = y4 < minY ? y4 : minY; + + maxX = x1 > maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + } + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Sprite.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(!this.visible || this.alpha <= 0)return; + + var i,j; + + // do a quick check to see if this element has a mask or a filter. + if(this._mask || this._filters) + { + var spriteBatch = renderSession.spriteBatch; + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if(this._filters) + { + spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + + if(this._mask) + { + spriteBatch.stop(); + renderSession.maskManager.pushMask(this.mask, renderSession); + spriteBatch.start(); + } + + // add this sprite to the batch + spriteBatch.render(this); + + // now loop through the children and make sure they get rendered + for(i=0,j=this.children.length; i 1) ratio = 1; + + perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); + num = this.texture.height / 2; //(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + perp.x /= perpLength; + perp.y /= perpLength; + + perp.x *= num; + perp.y *= num; + + vertices[index] = point.x + perp.x; + vertices[index+1] = point.y + perp.y; + vertices[index+2] = point.x - perp.x; + vertices[index+3] = point.y - perp.y; + + lastPoint = point; + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); +}; +/* + * Sets the texture that the Rope will use + * + * @method setTexture + * @param texture {Texture} the texture that will be used + */ +PIXI.Rope.prototype.setTexture = function(texture) +{ + // stop current texture + this.texture = texture; + //this.updateFrame = true; +}; diff --git a/app/Lib/Vendor/src/pixi/extras/SPINE-LICENSE b/app/Lib/Vendor/src/pixi/extras/SPINE-LICENSE new file mode 100755 index 0000000..7bb7566 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/extras/SPINE-LICENSE @@ -0,0 +1,27 @@ +Spine Runtimes Software License +Version 2.1 + +Copyright (c) 2013, Esoteric Software +All rights reserved. + +You are granted a perpetual, non-exclusive, non-sublicensable and +non-transferable license to install, execute and perform the Spine Runtimes +Software (the "Software") solely for internal use. Without the written +permission of Esoteric Software (typically granted by licensing Spine), you +may not (a) modify, translate, adapt or otherwise create derivative works, +improvements of the Software or develop new applications using the Software +or (b) remove, delete, alter or obscure any trademarks or any copyright, +trademark, patent or other intellectual property or proprietary rights notices +on or in the Software, including any copy thereof. Redistributions in binary +or source form must include this license and terms. + +THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/app/Lib/Vendor/src/pixi/extras/Spine.js b/app/Lib/Vendor/src/pixi/extras/Spine.js new file mode 100644 index 0000000..06ce610 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/extras/Spine.js @@ -0,0 +1,2626 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +var spine = { + radDeg: 180 / Math.PI, + degRad: Math.PI / 180, + temp: [], + Float32Array: (typeof(Float32Array) === 'undefined') ? Array : Float32Array, + Uint16Array: (typeof(Uint16Array) === 'undefined') ? Array : Uint16Array +}; + +spine.BoneData = function (name, parent) { + this.name = name; + this.parent = parent; +}; +spine.BoneData.prototype = { + length: 0, + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + inheritScale: true, + inheritRotation: true, + flipX: false, flipY: false +}; + +spine.SlotData = function (name, boneData) { + this.name = name; + this.boneData = boneData; +}; +spine.SlotData.prototype = { + r: 1, g: 1, b: 1, a: 1, + attachmentName: null, + additiveBlending: false +}; + +spine.IkConstraintData = function (name) { + this.name = name; + this.bones = []; +}; +spine.IkConstraintData.prototype = { + target: null, + bendDirection: 1, + mix: 1 +}; + +spine.Bone = function (boneData, skeleton, parent) { + this.data = boneData; + this.skeleton = skeleton; + this.parent = parent; + this.setToSetupPose(); +}; +spine.Bone.yDown = false; +spine.Bone.prototype = { + x: 0, y: 0, + rotation: 0, rotationIK: 0, + scaleX: 1, scaleY: 1, + flipX: false, flipY: false, + m00: 0, m01: 0, worldX: 0, // a b x + m10: 0, m11: 0, worldY: 0, // c d y + worldRotation: 0, + worldScaleX: 1, worldScaleY: 1, + worldFlipX: false, worldFlipY: false, + updateWorldTransform: function () { + var parent = this.parent; + if (parent) { + this.worldX = this.x * parent.m00 + this.y * parent.m01 + parent.worldX; + this.worldY = this.x * parent.m10 + this.y * parent.m11 + parent.worldY; + if (this.data.inheritScale) { + this.worldScaleX = parent.worldScaleX * this.scaleX; + this.worldScaleY = parent.worldScaleY * this.scaleY; + } else { + this.worldScaleX = this.scaleX; + this.worldScaleY = this.scaleY; + } + this.worldRotation = this.data.inheritRotation ? (parent.worldRotation + this.rotationIK) : this.rotationIK; + this.worldFlipX = parent.worldFlipX != this.flipX; + this.worldFlipY = parent.worldFlipY != this.flipY; + } else { + var skeletonFlipX = this.skeleton.flipX, skeletonFlipY = this.skeleton.flipY; + this.worldX = skeletonFlipX ? -this.x : this.x; + this.worldY = (skeletonFlipY != spine.Bone.yDown) ? -this.y : this.y; + this.worldScaleX = this.scaleX; + this.worldScaleY = this.scaleY; + this.worldRotation = this.rotationIK; + this.worldFlipX = skeletonFlipX != this.flipX; + this.worldFlipY = skeletonFlipY != this.flipY; + } + var radians = this.worldRotation * spine.degRad; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + if (this.worldFlipX) { + this.m00 = -cos * this.worldScaleX; + this.m01 = sin * this.worldScaleY; + } else { + this.m00 = cos * this.worldScaleX; + this.m01 = -sin * this.worldScaleY; + } + if (this.worldFlipY != spine.Bone.yDown) { + this.m10 = -sin * this.worldScaleX; + this.m11 = -cos * this.worldScaleY; + } else { + this.m10 = sin * this.worldScaleX; + this.m11 = cos * this.worldScaleY; + } + }, + setToSetupPose: function () { + var data = this.data; + this.x = data.x; + this.y = data.y; + this.rotation = data.rotation; + this.rotationIK = this.rotation; + this.scaleX = data.scaleX; + this.scaleY = data.scaleY; + this.flipX = data.flipX; + this.flipY = data.flipY; + }, + worldToLocal: function (world) { + var dx = world[0] - this.worldX, dy = world[1] - this.worldY; + var m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; + if (this.worldFlipX != (this.worldFlipY != spine.Bone.yDown)) { + m00 = -m00; + m11 = -m11; + } + var invDet = 1 / (m00 * m11 - m01 * m10); + world[0] = dx * m00 * invDet - dy * m01 * invDet; + world[1] = dy * m11 * invDet - dx * m10 * invDet; + }, + localToWorld: function (local) { + var localX = local[0], localY = local[1]; + local[0] = localX * this.m00 + localY * this.m01 + this.worldX; + local[1] = localX * this.m10 + localY * this.m11 + this.worldY; + } +}; + +spine.Slot = function (slotData, bone) { + this.data = slotData; + this.bone = bone; + this.setToSetupPose(); +}; +spine.Slot.prototype = { + r: 1, g: 1, b: 1, a: 1, + _attachmentTime: 0, + attachment: null, + attachmentVertices: [], + setAttachment: function (attachment) { + this.attachment = attachment; + this._attachmentTime = this.bone.skeleton.time; + this.attachmentVertices.length = 0; + }, + setAttachmentTime: function (time) { + this._attachmentTime = this.bone.skeleton.time - time; + }, + getAttachmentTime: function () { + return this.bone.skeleton.time - this._attachmentTime; + }, + setToSetupPose: function () { + var data = this.data; + this.r = data.r; + this.g = data.g; + this.b = data.b; + this.a = data.a; + + var slotDatas = this.bone.skeleton.data.slots; + for (var i = 0, n = slotDatas.length; i < n; i++) { + if (slotDatas[i] == data) { + this.setAttachment(!data.attachmentName ? null : this.bone.skeleton.getAttachmentBySlotIndex(i, data.attachmentName)); + break; + } + } + } +}; + +spine.IkConstraint = function (data, skeleton) { + this.data = data; + this.mix = data.mix; + this.bendDirection = data.bendDirection; + + this.bones = []; + for (var i = 0, n = data.bones.length; i < n; i++) + this.bones.push(skeleton.findBone(data.bones[i].name)); + this.target = skeleton.findBone(data.target.name); +}; +spine.IkConstraint.prototype = { + apply: function () { + var target = this.target; + var bones = this.bones; + switch (bones.length) { + case 1: + spine.IkConstraint.apply1(bones[0], target.worldX, target.worldY, this.mix); + break; + case 2: + spine.IkConstraint.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.mix); + break; + } + } +}; +/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world + * coordinate system. */ +spine.IkConstraint.apply1 = function (bone, targetX, targetY, alpha) { + var parentRotation = (!bone.data.inheritRotation || !bone.parent) ? 0 : bone.parent.worldRotation; + var rotation = bone.rotation; + var rotationIK = Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * spine.radDeg - parentRotation; + bone.rotationIK = rotation + (rotationIK - rotation) * alpha; +}; +/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The + * target is specified in the world coordinate system. + * @param child Any descendant bone of the parent. */ +spine.IkConstraint.apply2 = function (parent, child, targetX, targetY, bendDirection, alpha) { + var childRotation = child.rotation, parentRotation = parent.rotation; + if (!alpha) { + child.rotationIK = childRotation; + parent.rotationIK = parentRotation; + return; + } + var positionX, positionY, tempPosition = spine.temp; + var parentParent = parent.parent; + if (parentParent) { + tempPosition[0] = targetX; + tempPosition[1] = targetY; + parentParent.worldToLocal(tempPosition); + targetX = (tempPosition[0] - parent.x) * parentParent.worldScaleX; + targetY = (tempPosition[1] - parent.y) * parentParent.worldScaleY; + } else { + targetX -= parent.x; + targetY -= parent.y; + } + if (child.parent == parent) { + positionX = child.x; + positionY = child.y; + } else { + tempPosition[0] = child.x; + tempPosition[1] = child.y; + child.parent.localToWorld(tempPosition); + parent.worldToLocal(tempPosition); + positionX = tempPosition[0]; + positionY = tempPosition[1]; + } + var childX = positionX * parent.worldScaleX, childY = positionY * parent.worldScaleY; + var offset = Math.atan2(childY, childX); + var len1 = Math.sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX; + // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/ + var cosDenom = 2 * len1 * len2; + if (cosDenom < 0.0001) { + child.rotationIK = childRotation + (Math.atan2(targetY, targetX) * spine.radDeg - parentRotation - childRotation) * alpha; + return; + } + var cos = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom; + if (cos < -1) + cos = -1; + else if (cos > 1) + cos = 1; + var childAngle = Math.acos(cos) * bendDirection; + var adjacent = len1 + len2 * cos, opposite = len2 * Math.sin(childAngle); + var parentAngle = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); + var rotation = (parentAngle - offset) * spine.radDeg - parentRotation; + if (rotation > 180) + rotation -= 360; + else if (rotation < -180) // + rotation += 360; + parent.rotationIK = parentRotation + rotation * alpha; + rotation = (childAngle + offset) * spine.radDeg - childRotation; + if (rotation > 180) + rotation -= 360; + else if (rotation < -180) // + rotation += 360; + child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha; +}; + +spine.Skin = function (name) { + this.name = name; + this.attachments = {}; +}; +spine.Skin.prototype = { + addAttachment: function (slotIndex, name, attachment) { + this.attachments[slotIndex + ":" + name] = attachment; + }, + getAttachment: function (slotIndex, name) { + return this.attachments[slotIndex + ":" + name]; + }, + _attachAll: function (skeleton, oldSkin) { + for (var key in oldSkin.attachments) { + var colon = key.indexOf(":"); + var slotIndex = parseInt(key.substring(0, colon)); + var name = key.substring(colon + 1); + var slot = skeleton.slots[slotIndex]; + if (slot.attachment && slot.attachment.name == name) { + var attachment = this.getAttachment(slotIndex, name); + if (attachment) slot.setAttachment(attachment); + } + } + } +}; + +spine.Animation = function (name, timelines, duration) { + this.name = name; + this.timelines = timelines; + this.duration = duration; +}; +spine.Animation.prototype = { + apply: function (skeleton, lastTime, time, loop, events) { + if (loop && this.duration != 0) { + time %= this.duration; + lastTime %= this.duration; + } + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, lastTime, time, events, 1); + }, + mix: function (skeleton, lastTime, time, loop, events, alpha) { + if (loop && this.duration != 0) { + time %= this.duration; + lastTime %= this.duration; + } + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, lastTime, time, events, alpha); + } +}; +spine.Animation.binarySearch = function (values, target, step) { + var low = 0; + var high = Math.floor(values.length / step) - 2; + if (!high) return step; + var current = high >>> 1; + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1) * step; + current = (low + high) >>> 1; + } +}; +spine.Animation.binarySearch1 = function (values, target) { + var low = 0; + var high = values.length - 2; + if (!high) return 1; + var current = high >>> 1; + while (true) { + if (values[current + 1] <= target) + low = current + 1; + else + high = current; + if (low == high) return low + 1; + current = (low + high) >>> 1; + } +}; +spine.Animation.linearSearch = function (values, target, step) { + for (var i = 0, last = values.length - step; i <= last; i += step) + if (values[i] > target) return i; + return -1; +}; + +spine.Curves = function (frameCount) { + this.curves = []; // type, x, y, ... + //this.curves.length = (frameCount - 1) * 19/*BEZIER_SIZE*/; +}; +spine.Curves.prototype = { + setLinear: function (frameIndex) { + this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 0/*LINEAR*/; + }, + setStepped: function (frameIndex) { + this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 1/*STEPPED*/; + }, + /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ + setCurve: function (frameIndex, cx1, cy1, cx2, cy2) { + var subdiv1 = 1 / 10/*BEZIER_SEGMENTS*/, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1; + var pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3; + var tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1; + var dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3; + var ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5; + var dddfx = tmp2x * pre5, dddfy = tmp2y * pre5; + + var i = frameIndex * 19/*BEZIER_SIZE*/; + var curves = this.curves; + curves[i++] = 2/*BEZIER*/; + + var x = dfx, y = dfy; + for (var n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) { + curves[i] = x; + curves[i + 1] = y; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + }, + getCurvePercent: function (frameIndex, percent) { + percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); + var curves = this.curves; + var i = frameIndex * 19/*BEZIER_SIZE*/; + var type = curves[i]; + if (type === 0/*LINEAR*/) return percent; + if (type == 1/*STEPPED*/) return 0; + i++; + var x = 0; + for (var start = i, n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) { + x = curves[i]; + if (x >= percent) { + var prevX, prevY; + if (i == start) { + prevX = 0; + prevY = 0; + } else { + prevX = curves[i - 2]; + prevY = curves[i - 1]; + } + return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + } + } + var y = curves[i - 1]; + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } +}; + +spine.RotateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, angle, ... + this.frames.length = frameCount * 2; +}; +spine.RotateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, angle) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = angle; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 2]) { // Time is after last frame. + var amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 2); + var prevFrameValue = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent); + + var amount = frames[frameIndex + 1/*FRAME_VALUE*/] - prevFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + } +}; + +spine.TranslateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.TranslateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha; + bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameX = frames[frameIndex - 2]; + var prevFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent - bone.y) * alpha; + } +}; + +spine.ScaleTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.ScaleTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.scaleX += (bone.data.scaleX * frames[frames.length - 2] - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY * frames[frames.length - 1] - bone.scaleY) * alpha; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameX = frames[frameIndex - 2]; + var prevFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent) - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent) - bone.scaleY) * alpha; + } +}; + +spine.ColorTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, r, g, b, a, ... + this.frames.length = frameCount * 5; +}; +spine.ColorTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length / 5; + }, + setFrame: function (frameIndex, time, r, g, b, a) { + frameIndex *= 5; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = r; + this.frames[frameIndex + 2] = g; + this.frames[frameIndex + 3] = b; + this.frames[frameIndex + 4] = a; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var r, g, b, a; + if (time >= frames[frames.length - 5]) { + // Time is after last frame. + var i = frames.length - 1; + r = frames[i - 3]; + g = frames[i - 2]; + b = frames[i - 1]; + a = frames[i]; + } else { + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 5); + var prevFrameR = frames[frameIndex - 4]; + var prevFrameG = frames[frameIndex - 3]; + var prevFrameB = frames[frameIndex - 2]; + var prevFrameA = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); + + r = prevFrameR + (frames[frameIndex + 1/*FRAME_R*/] - prevFrameR) * percent; + g = prevFrameG + (frames[frameIndex + 2/*FRAME_G*/] - prevFrameG) * percent; + b = prevFrameB + (frames[frameIndex + 3/*FRAME_B*/] - prevFrameB) * percent; + a = prevFrameA + (frames[frameIndex + 4/*FRAME_A*/] - prevFrameA) * percent; + } + var slot = skeleton.slots[this.slotIndex]; + if (alpha < 1) { + slot.r += (r - slot.r) * alpha; + slot.g += (g - slot.g) * alpha; + slot.b += (b - slot.b) * alpha; + slot.a += (a - slot.a) * alpha; + } else { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + } + } +}; + +spine.AttachmentTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, ... + this.frames.length = frameCount; + this.attachmentNames = []; + this.attachmentNames.length = frameCount; +}; +spine.AttachmentTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, attachmentName) { + this.frames[frameIndex] = time; + this.attachmentNames[frameIndex] = attachmentName; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + + var frameIndex = time >= frames[frames.length - 1] ? frames.length - 1 : spine.Animation.binarySearch1(frames, time) - 1; + if (frames[frameIndex] < lastTime) return; + + var attachmentName = this.attachmentNames[frameIndex]; + skeleton.slots[this.slotIndex].setAttachment( + !attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); + } +}; + +spine.EventTimeline = function (frameCount) { + this.frames = []; // time, ... + this.frames.length = frameCount; + this.events = []; + this.events.length = frameCount; +}; +spine.EventTimeline.prototype = { + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, event) { + this.frames[frameIndex] = time; + this.events[frameIndex] = event; + }, + /** Fires events for frames > lastTime and <= time. */ + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + if (!firedEvents) return; + + var frames = this.frames; + var frameCount = frames.length; + + if (lastTime > time) { // Fire events after last time for looped animations. + this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha); + lastTime = -1; + } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + return; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (lastTime < frames[0]) + frameIndex = 0; + else { + frameIndex = spine.Animation.binarySearch1(frames, lastTime); + var frame = frames[frameIndex]; + while (frameIndex > 0) { // Fire multiple events with the same frame. + if (frames[frameIndex - 1] != frame) break; + frameIndex--; + } + } + var events = this.events; + for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++) + firedEvents.push(events[frameIndex]); + } +}; + +spine.DrawOrderTimeline = function (frameCount) { + this.frames = []; // time, ... + this.frames.length = frameCount; + this.drawOrders = []; + this.drawOrders.length = frameCount; +}; +spine.DrawOrderTimeline.prototype = { + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, drawOrder) { + this.frames[frameIndex] = time; + this.drawOrders[frameIndex] = drawOrder; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = spine.Animation.binarySearch1(frames, time) - 1; + + var drawOrder = skeleton.drawOrder; + var slots = skeleton.slots; + var drawOrderToSetupIndex = this.drawOrders[frameIndex]; + if (!drawOrderToSetupIndex) { + for (var i = 0, n = slots.length; i < n; i++) + drawOrder[i] = slots[i]; + } else { + for (var i = 0, n = drawOrderToSetupIndex.length; i < n; i++) + drawOrder[i] = skeleton.slots[drawOrderToSetupIndex[i]]; + } + + } +}; + +spine.FfdTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; + this.frames.length = frameCount; + this.frameVertices = []; + this.frameVertices.length = frameCount; +}; +spine.FfdTimeline.prototype = { + slotIndex: 0, + attachment: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, vertices) { + this.frames[frameIndex] = time; + this.frameVertices[frameIndex] = vertices; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var slot = skeleton.slots[this.slotIndex]; + if (slot.attachment != this.attachment) return; + + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameVertices = this.frameVertices; + var vertexCount = frameVertices[0].length; + + var vertices = slot.attachmentVertices; + if (vertices.length != vertexCount) alpha = 1; + vertices.length = vertexCount; + + if (time >= frames[frames.length - 1]) { // Time is after last frame. + var lastVertices = frameVertices[frames.length - 1]; + if (alpha < 1) { + for (var i = 0; i < vertexCount; i++) + vertices[i] += (lastVertices[i] - vertices[i]) * alpha; + } else { + for (var i = 0; i < vertexCount; i++) + vertices[i] = lastVertices[i]; + } + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch1(frames, time); + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime); + percent = this.curves.getCurvePercent(frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + var prevVertices = frameVertices[frameIndex - 1]; + var nextVertices = frameVertices[frameIndex]; + + if (alpha < 1) { + for (var i = 0; i < vertexCount; i++) { + var prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; + } + } else { + for (var i = 0; i < vertexCount; i++) { + var prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; + } + } + } +}; + +spine.IkConstraintTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, mix, bendDirection, ... + this.frames.length = frameCount * 3; +}; +spine.IkConstraintTimeline.prototype = { + ikConstraintIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, mix, bendDirection) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = mix; + this.frames[frameIndex + 2] = bendDirection; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var ikConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + ikConstraint.mix += (frames[frames.length - 2] - ikConstraint.mix) * alpha; + ikConstraint.bendDirection = frames[frames.length - 1]; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameMix = frames[frameIndex + -2/*PREV_FRAME_MIX*/]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + var mix = prevFrameMix + (frames[frameIndex + 1/*FRAME_MIX*/] - prevFrameMix) * percent; + ikConstraint.mix += (mix - ikConstraint.mix) * alpha; + ikConstraint.bendDirection = frames[frameIndex + -1/*PREV_FRAME_BEND_DIRECTION*/]; + } +}; + +spine.FlipXTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, flip, ... + this.frames.length = frameCount * 2; +}; +spine.FlipXTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, flip) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = flip ? 1 : 0; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; + if (frames[frameIndex] < lastTime) return; + skeleton.bones[boneIndex].flipX = frames[frameIndex + 1] != 0; + } +}; + +spine.FlipYTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, flip, ... + this.frames.length = frameCount * 2; +}; +spine.FlipYTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, flip) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = flip ? 1 : 0; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; + if (frames[frameIndex] < lastTime) return; + skeleton.bones[boneIndex].flipY = frames[frameIndex + 1] != 0; + } +}; + +spine.SkeletonData = function () { + this.bones = []; + this.slots = []; + this.skins = []; + this.events = []; + this.animations = []; + this.ikConstraints = []; +}; +spine.SkeletonData.prototype = { + name: null, + defaultSkin: null, + width: 0, height: 0, + version: null, hash: null, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + if (slots[i].name == slotName) return slot[i]; + } + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].name == slotName) return i; + return -1; + }, + /** @return May be null. */ + findSkin: function (skinName) { + var skins = this.skins; + for (var i = 0, n = skins.length; i < n; i++) + if (skins[i].name == skinName) return skins[i]; + return null; + }, + /** @return May be null. */ + findEvent: function (eventName) { + var events = this.events; + for (var i = 0, n = events.length; i < n; i++) + if (events[i].name == eventName) return events[i]; + return null; + }, + /** @return May be null. */ + findAnimation: function (animationName) { + var animations = this.animations; + for (var i = 0, n = animations.length; i < n; i++) + if (animations[i].name == animationName) return animations[i]; + return null; + }, + /** @return May be null. */ + findIkConstraint: function (ikConstraintName) { + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) + if (ikConstraints[i].name == ikConstraintName) return ikConstraints[i]; + return null; + } +}; + +spine.Skeleton = function (skeletonData) { + this.data = skeletonData; + + this.bones = []; + for (var i = 0, n = skeletonData.bones.length; i < n; i++) { + var boneData = skeletonData.bones[i]; + var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)]; + this.bones.push(new spine.Bone(boneData, this, parent)); + } + + this.slots = []; + this.drawOrder = []; + for (var i = 0, n = skeletonData.slots.length; i < n; i++) { + var slotData = skeletonData.slots[i]; + var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)]; + var slot = new spine.Slot(slotData, bone); + this.slots.push(slot); + this.drawOrder.push(slot); + } + + this.ikConstraints = []; + for (var i = 0, n = skeletonData.ikConstraints.length; i < n; i++) + this.ikConstraints.push(new spine.IkConstraint(skeletonData.ikConstraints[i], this)); + + this.boneCache = []; + this.updateCache(); +}; +spine.Skeleton.prototype = { + x: 0, y: 0, + skin: null, + r: 1, g: 1, b: 1, a: 1, + time: 0, + flipX: false, flipY: false, + /** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */ + updateCache: function () { + var ikConstraints = this.ikConstraints; + var ikConstraintsCount = ikConstraints.length; + + var arrayCount = ikConstraintsCount + 1; + var boneCache = this.boneCache; + if (boneCache.length > arrayCount) boneCache.length = arrayCount; + for (var i = 0, n = boneCache.length; i < n; i++) + boneCache[i].length = 0; + while (boneCache.length < arrayCount) + boneCache[boneCache.length] = []; + + var nonIkBones = boneCache[0]; + var bones = this.bones; + + outer: + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + var current = bone; + do { + for (var ii = 0; ii < ikConstraintsCount; ii++) { + var ikConstraint = ikConstraints[ii]; + var parent = ikConstraint.bones[0]; + var child= ikConstraint.bones[ikConstraint.bones.length - 1]; + while (true) { + if (current == child) { + boneCache[ii].push(bone); + boneCache[ii + 1].push(bone); + continue outer; + } + if (child == parent) break; + child = child.parent; + } + } + current = current.parent; + } while (current); + nonIkBones[nonIkBones.length] = bone; + } + }, + /** Updates the world transform for each bone. */ + updateWorldTransform: function () { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + bone.rotationIK = bone.rotation; + } + var i = 0, last = this.boneCache.length - 1; + while (true) { + var cacheBones = this.boneCache[i]; + for (var ii = 0, nn = cacheBones.length; ii < nn; ii++) + cacheBones[ii].updateWorldTransform(); + if (i == last) break; + this.ikConstraints[i].apply(); + i++; + } + }, + /** Sets the bones and slots to their setup pose values. */ + setToSetupPose: function () { + this.setBonesToSetupPose(); + this.setSlotsToSetupPose(); + }, + setBonesToSetupPose: function () { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].setToSetupPose(); + + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) { + var ikConstraint = ikConstraints[i]; + ikConstraint.bendDirection = ikConstraint.data.bendDirection; + ikConstraint.mix = ikConstraint.data.mix; + } + }, + setSlotsToSetupPose: function () { + var slots = this.slots; + var drawOrder = this.drawOrder; + for (var i = 0, n = slots.length; i < n; i++) { + drawOrder[i] = slots[i]; + slots[i].setToSetupPose(i); + } + }, + /** @return May return null. */ + getRootBone: function () { + return this.bones.length ? this.bones[0] : null; + }, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return slots[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return i; + return -1; + }, + setSkinByName: function (skinName) { + var skin = this.data.findSkin(skinName); + if (!skin) throw "Skin not found: " + skinName; + this.setSkin(skin); + }, + /** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. + * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was + * no old skin, each slot's setup mode attachment is attached from the new skin. + * @param newSkin May be null. */ + setSkin: function (newSkin) { + if (newSkin) { + if (this.skin) + newSkin._attachAll(this, this.skin); + else { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + var slot = slots[i]; + var name = slot.data.attachmentName; + if (name) { + var attachment = newSkin.getAttachment(i, name); + if (attachment) slot.setAttachment(attachment); + } + } + } + } + this.skin = newSkin; + }, + /** @return May be null. */ + getAttachmentBySlotName: function (slotName, attachmentName) { + return this.getAttachmentBySlotIndex(this.data.findSlotIndex(slotName), attachmentName); + }, + /** @return May be null. */ + getAttachmentBySlotIndex: function (slotIndex, attachmentName) { + if (this.skin) { + var attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment) return attachment; + } + if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; + }, + /** @param attachmentName May be null. */ + setAttachment: function (slotName, attachmentName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + var slot = slots[i]; + if (slot.data.name == slotName) { + var attachment = null; + if (attachmentName) { + attachment = this.getAttachmentBySlotIndex(i, attachmentName); + if (!attachment) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; + } + slot.setAttachment(attachment); + return; + } + } + throw "Slot not found: " + slotName; + }, + /** @return May be null. */ + findIkConstraint: function (ikConstraintName) { + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) + if (ikConstraints[i].data.name == ikConstraintName) return ikConstraints[i]; + return null; + }, + update: function (delta) { + this.time += delta; + } +}; + +spine.EventData = function (name) { + this.name = name; +}; +spine.EventData.prototype = { + intValue: 0, + floatValue: 0, + stringValue: null +}; + +spine.Event = function (data) { + this.data = data; +}; +spine.Event.prototype = { + intValue: 0, + floatValue: 0, + stringValue: null +}; + +spine.AttachmentType = { + region: 0, + boundingbox: 1, + mesh: 2, + skinnedmesh: 3 +}; + +spine.RegionAttachment = function (name) { + this.name = name; + this.offset = []; + this.offset.length = 8; + this.uvs = []; + this.uvs.length = 8; +}; +spine.RegionAttachment.prototype = { + type: spine.AttachmentType.region, + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + width: 0, height: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + setUVs: function (u, v, u2, v2, rotate) { + var uvs = this.uvs; + if (rotate) { + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v2; + uvs[4/*X3*/] = u; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v; + uvs[0/*X1*/] = u2; + uvs[1/*Y1*/] = v2; + } else { + uvs[0/*X1*/] = u; + uvs[1/*Y1*/] = v2; + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v; + uvs[4/*X3*/] = u2; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v2; + } + }, + updateOffset: function () { + var regionScaleX = this.width / this.regionOriginalWidth * this.scaleX; + var regionScaleY = this.height / this.regionOriginalHeight * this.scaleY; + var localX = -this.width / 2 * this.scaleX + this.regionOffsetX * regionScaleX; + var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY; + var localX2 = localX + this.regionWidth * regionScaleX; + var localY2 = localY + this.regionHeight * regionScaleY; + var radians = this.rotation * spine.degRad; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + var localXCos = localX * cos + this.x; + var localXSin = localX * sin; + var localYCos = localY * cos + this.y; + var localYSin = localY * sin; + var localX2Cos = localX2 * cos + this.x; + var localX2Sin = localX2 * sin; + var localY2Cos = localY2 * cos + this.y; + var localY2Sin = localY2 * sin; + var offset = this.offset; + offset[0/*X1*/] = localXCos - localYSin; + offset[1/*Y1*/] = localYCos + localXSin; + offset[2/*X2*/] = localXCos - localY2Sin; + offset[3/*Y2*/] = localY2Cos + localXSin; + offset[4/*X3*/] = localX2Cos - localY2Sin; + offset[5/*Y3*/] = localY2Cos + localX2Sin; + offset[6/*X4*/] = localX2Cos - localYSin; + offset[7/*Y4*/] = localYCos + localX2Sin; + }, + computeVertices: function (x, y, bone, vertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var offset = this.offset; + vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x; + vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y; + vertices[2/*X2*/] = offset[2/*X2*/] * m00 + offset[3/*Y2*/] * m01 + x; + vertices[3/*Y2*/] = offset[2/*X2*/] * m10 + offset[3/*Y2*/] * m11 + y; + vertices[4/*X3*/] = offset[4/*X3*/] * m00 + offset[5/*X3*/] * m01 + x; + vertices[5/*X3*/] = offset[4/*X3*/] * m10 + offset[5/*X3*/] * m11 + y; + vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x; + vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y; + } +}; + +spine.MeshAttachment = function (name) { + this.name = name; +}; +spine.MeshAttachment.prototype = { + type: spine.AttachmentType.mesh, + vertices: null, + uvs: null, + regionUVs: null, + triangles: null, + hullLength: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + edges: null, + width: 0, height: 0, + updateUVs: function () { + var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; + var n = this.regionUVs.length; + if (!this.uvs || this.uvs.length != n) { + this.uvs = new spine.Float32Array(n); + } + if (this.regionRotate) { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; + this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; + } + } else { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i] * width; + this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; + } + } + }, + computeWorldVertices: function (x, y, slot, worldVertices) { + var bone = slot.bone; + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var vertices = this.vertices; + var verticesCount = vertices.length; + if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices; + for (var i = 0; i < verticesCount; i += 2) { + var vx = vertices[i]; + var vy = vertices[i + 1]; + worldVertices[i] = vx * m00 + vy * m01 + x; + worldVertices[i + 1] = vx * m10 + vy * m11 + y; + } + } +}; + +spine.SkinnedMeshAttachment = function (name) { + this.name = name; +}; +spine.SkinnedMeshAttachment.prototype = { + type: spine.AttachmentType.skinnedmesh, + bones: null, + weights: null, + uvs: null, + regionUVs: null, + triangles: null, + hullLength: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + edges: null, + width: 0, height: 0, + updateUVs: function (u, v, u2, v2, rotate) { + var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; + var n = this.regionUVs.length; + if (!this.uvs || this.uvs.length != n) { + this.uvs = new spine.Float32Array(n); + } + if (this.regionRotate) { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; + this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; + } + } else { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i] * width; + this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; + } + } + }, + computeWorldVertices: function (x, y, slot, worldVertices) { + var skeletonBones = slot.bone.skeleton.bones; + var weights = this.weights; + var bones = this.bones; + + var w = 0, v = 0, b = 0, f = 0, n = bones.length, nn; + var wx, wy, bone, vx, vy, weight; + if (!slot.attachmentVertices.length) { + for (; v < n; w += 2) { + wx = 0; + wy = 0; + nn = bones[v++] + v; + for (; v < nn; v++, b += 3) { + bone = skeletonBones[bones[v]]; + vx = weights[b]; + vy = weights[b + 1]; + weight = weights[b + 2]; + wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; + wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + } + worldVertices[w] = wx + x; + worldVertices[w + 1] = wy + y; + } + } else { + var ffd = slot.attachmentVertices; + for (; v < n; w += 2) { + wx = 0; + wy = 0; + nn = bones[v++] + v; + for (; v < nn; v++, b += 3, f += 2) { + bone = skeletonBones[bones[v]]; + vx = weights[b] + ffd[f]; + vy = weights[b + 1] + ffd[f + 1]; + weight = weights[b + 2]; + wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; + wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + } + worldVertices[w] = wx + x; + worldVertices[w + 1] = wy + y; + } + } + } +}; + +spine.BoundingBoxAttachment = function (name) { + this.name = name; + this.vertices = []; +}; +spine.BoundingBoxAttachment.prototype = { + type: spine.AttachmentType.boundingbox, + computeWorldVertices: function (x, y, bone, worldVertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var vertices = this.vertices; + for (var i = 0, n = vertices.length; i < n; i += 2) { + var px = vertices[i]; + var py = vertices[i + 1]; + worldVertices[i] = px * m00 + py * m01 + x; + worldVertices[i + 1] = px * m10 + py * m11 + y; + } + } +}; + +spine.AnimationStateData = function (skeletonData) { + this.skeletonData = skeletonData; + this.animationToMixTime = {}; +}; +spine.AnimationStateData.prototype = { + defaultMix: 0, + setMixByName: function (fromName, toName, duration) { + var from = this.skeletonData.findAnimation(fromName); + if (!from) throw "Animation not found: " + fromName; + var to = this.skeletonData.findAnimation(toName); + if (!to) throw "Animation not found: " + toName; + this.setMix(from, to, duration); + }, + setMix: function (from, to, duration) { + this.animationToMixTime[from.name + ":" + to.name] = duration; + }, + getMix: function (from, to) { + var key = from.name + ":" + to.name; + return this.animationToMixTime.hasOwnProperty(key) ? this.animationToMixTime[key] : this.defaultMix; + } +}; + +spine.TrackEntry = function () {}; +spine.TrackEntry.prototype = { + next: null, previous: null, + animation: null, + loop: false, + delay: 0, time: 0, lastTime: -1, endTime: 0, + timeScale: 1, + mixTime: 0, mixDuration: 0, mix: 1, + onStart: null, onEnd: null, onComplete: null, onEvent: null +}; + +spine.AnimationState = function (stateData) { + this.data = stateData; + this.tracks = []; + this.events = []; +}; +spine.AnimationState.prototype = { + onStart: null, + onEnd: null, + onComplete: null, + onEvent: null, + timeScale: 1, + update: function (delta) { + delta *= this.timeScale; + for (var i = 0; i < this.tracks.length; i++) { + var current = this.tracks[i]; + if (!current) continue; + + current.time += delta * current.timeScale; + if (current.previous) { + var previousDelta = delta * current.previous.timeScale; + current.previous.time += previousDelta; + current.mixTime += previousDelta; + } + + var next = current.next; + if (next) { + next.time = current.lastTime - next.delay; + if (next.time >= 0) this.setCurrent(i, next); + } else { + // End non-looping animation when it reaches its end time and there is no next entry. + if (!current.loop && current.lastTime >= current.endTime) this.clearTrack(i); + } + } + }, + apply: function (skeleton) { + for (var i = 0; i < this.tracks.length; i++) { + var current = this.tracks[i]; + if (!current) continue; + + this.events.length = 0; + + var time = current.time; + var lastTime = current.lastTime; + var endTime = current.endTime; + var loop = current.loop; + if (!loop && time > endTime) time = endTime; + + var previous = current.previous; + if (!previous) { + if (current.mix == 1) + current.animation.apply(skeleton, current.lastTime, time, loop, this.events); + else + current.animation.mix(skeleton, current.lastTime, time, loop, this.events, current.mix); + } else { + var previousTime = previous.time; + if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; + previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null); + + var alpha = current.mixTime / current.mixDuration * current.mix; + if (alpha >= 1) { + alpha = 1; + current.previous = null; + } + current.animation.mix(skeleton, current.lastTime, time, loop, this.events, alpha); + } + + for (var ii = 0, nn = this.events.length; ii < nn; ii++) { + var event = this.events[ii]; + if (current.onEvent) current.onEvent(i, event); + if (this.onEvent) this.onEvent(i, event); + } + + // Check if completed the animation or a loop iteration. + if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) { + var count = Math.floor(time / endTime); + if (current.onComplete) current.onComplete(i, count); + if (this.onComplete) this.onComplete(i, count); + } + + current.lastTime = current.time; + } + }, + clearTracks: function () { + for (var i = 0, n = this.tracks.length; i < n; i++) + this.clearTrack(i); + this.tracks.length = 0; + }, + clearTrack: function (trackIndex) { + if (trackIndex >= this.tracks.length) return; + var current = this.tracks[trackIndex]; + if (!current) return; + + if (current.onEnd) current.onEnd(trackIndex); + if (this.onEnd) this.onEnd(trackIndex); + + this.tracks[trackIndex] = null; + }, + _expandToIndex: function (index) { + if (index < this.tracks.length) return this.tracks[index]; + while (index >= this.tracks.length) + this.tracks.push(null); + return null; + }, + setCurrent: function (index, entry) { + var current = this._expandToIndex(index); + if (current) { + var previous = current.previous; + current.previous = null; + + if (current.onEnd) current.onEnd(index); + if (this.onEnd) this.onEnd(index); + + entry.mixDuration = this.data.getMix(current.animation, entry.animation); + if (entry.mixDuration > 0) { + entry.mixTime = 0; + // If a mix is in progress, mix from the closest animation. + if (previous && current.mixTime / current.mixDuration < 0.5) + entry.previous = previous; + else + entry.previous = current; + } + } + + this.tracks[index] = entry; + + if (entry.onStart) entry.onStart(index); + if (this.onStart) this.onStart(index); + }, + setAnimationByName: function (trackIndex, animationName, loop) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + return this.setAnimation(trackIndex, animation, loop); + }, + /** Set the current animation. Any queued animations are cleared. */ + setAnimation: function (trackIndex, animation, loop) { + var entry = new spine.TrackEntry(); + entry.animation = animation; + entry.loop = loop; + entry.endTime = animation.duration; + this.setCurrent(trackIndex, entry); + return entry; + }, + addAnimationByName: function (trackIndex, animationName, loop, delay) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + return this.addAnimation(trackIndex, animation, loop, delay); + }, + /** Adds an animation to be played delay seconds after the current or last queued animation. + * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ + addAnimation: function (trackIndex, animation, loop, delay) { + var entry = new spine.TrackEntry(); + entry.animation = animation; + entry.loop = loop; + entry.endTime = animation.duration; + + var last = this._expandToIndex(trackIndex); + if (last) { + while (last.next) + last = last.next; + last.next = entry; + } else + this.tracks[trackIndex] = entry; + + if (delay <= 0) { + if (last) + delay += last.endTime - this.data.getMix(last.animation, animation); + else + delay = 0; + } + entry.delay = delay; + + return entry; + }, + /** May be null. */ + getCurrent: function (trackIndex) { + if (trackIndex >= this.tracks.length) return null; + return this.tracks[trackIndex]; + } +}; + +spine.SkeletonJson = function (attachmentLoader) { + this.attachmentLoader = attachmentLoader; +}; +spine.SkeletonJson.prototype = { + scale: 1, + readSkeletonData: function (root, name) { + var skeletonData = new spine.SkeletonData(); + skeletonData.name = name; + + // Skeleton. + var skeletonMap = root["skeleton"]; + if (skeletonMap) { + skeletonData.hash = skeletonMap["hash"]; + skeletonData.version = skeletonMap["spine"]; + skeletonData.width = skeletonMap["width"] || 0; + skeletonData.height = skeletonMap["height"] || 0; + } + + // Bones. + var bones = root["bones"]; + for (var i = 0, n = bones.length; i < n; i++) { + var boneMap = bones[i]; + var parent = null; + if (boneMap["parent"]) { + parent = skeletonData.findBone(boneMap["parent"]); + if (!parent) throw "Parent bone not found: " + boneMap["parent"]; + } + var boneData = new spine.BoneData(boneMap["name"], parent); + boneData.length = (boneMap["length"] || 0) * this.scale; + boneData.x = (boneMap["x"] || 0) * this.scale; + boneData.y = (boneMap["y"] || 0) * this.scale; + boneData.rotation = (boneMap["rotation"] || 0); + boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1; + boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1; + boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true; + boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; + skeletonData.bones.push(boneData); + } + + // IK constraints. + var ik = root["ik"]; + if (ik) { + for (var i = 0, n = ik.length; i < n; i++) { + var ikMap = ik[i]; + var ikConstraintData = new spine.IkConstraintData(ikMap["name"]); + + var bones = ikMap["bones"]; + for (var ii = 0, nn = bones.length; ii < nn; ii++) { + var bone = skeletonData.findBone(bones[ii]); + if (!bone) throw "IK bone not found: " + bones[ii]; + ikConstraintData.bones.push(bone); + } + + ikConstraintData.target = skeletonData.findBone(ikMap["target"]); + if (!ikConstraintData.target) throw "Target bone not found: " + ikMap["target"]; + + ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1; + ikConstraintData.mix = ikMap.hasOwnProperty("mix") ? ikMap["mix"] : 1; + + skeletonData.ikConstraints.push(ikConstraintData); + } + } + + // Slots. + var slots = root["slots"]; + for (var i = 0, n = slots.length; i < n; i++) { + var slotMap = slots[i]; + var boneData = skeletonData.findBone(slotMap["bone"]); + if (!boneData) throw "Slot bone not found: " + slotMap["bone"]; + var slotData = new spine.SlotData(slotMap["name"], boneData); + + var color = slotMap["color"]; + if (color) { + slotData.r = this.toColor(color, 0); + slotData.g = this.toColor(color, 1); + slotData.b = this.toColor(color, 2); + slotData.a = this.toColor(color, 3); + } + + slotData.attachmentName = slotMap["attachment"]; + slotData.additiveBlending = slotMap["additive"] && slotMap["additive"] == "true"; + + skeletonData.slots.push(slotData); + } + + // Skins. + var skins = root["skins"]; + for (var skinName in skins) { + if (!skins.hasOwnProperty(skinName)) continue; + var skinMap = skins[skinName]; + var skin = new spine.Skin(skinName); + for (var slotName in skinMap) { + if (!skinMap.hasOwnProperty(slotName)) continue; + var slotIndex = skeletonData.findSlotIndex(slotName); + var slotEntry = skinMap[slotName]; + for (var attachmentName in slotEntry) { + if (!slotEntry.hasOwnProperty(attachmentName)) continue; + var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]); + if (attachment) skin.addAttachment(slotIndex, attachmentName, attachment); + } + } + skeletonData.skins.push(skin); + if (skin.name == "default") skeletonData.defaultSkin = skin; + } + + // Events. + var events = root["events"]; + for (var eventName in events) { + if (!events.hasOwnProperty(eventName)) continue; + var eventMap = events[eventName]; + var eventData = new spine.EventData(eventName); + eventData.intValue = eventMap["int"] || 0; + eventData.floatValue = eventMap["float"] || 0; + eventData.stringValue = eventMap["string"] || null; + skeletonData.events.push(eventData); + } + + // Animations. + var animations = root["animations"]; + for (var animationName in animations) { + if (!animations.hasOwnProperty(animationName)) continue; + this.readAnimation(animationName, animations[animationName], skeletonData); + } + + return skeletonData; + }, + readAttachment: function (skin, name, map) { + name = map["name"] || name; + + var type = spine.AttachmentType[map["type"] || "region"]; + var path = map["path"] || name; + + var scale = this.scale; + if (type == spine.AttachmentType.region) { + var region = this.attachmentLoader.newRegionAttachment(skin, name, path); + if (!region) return null; + region.path = path; + region.x = (map["x"] || 0) * scale; + region.y = (map["y"] || 0) * scale; + region.scaleX = map.hasOwnProperty("scaleX") ? map["scaleX"] : 1; + region.scaleY = map.hasOwnProperty("scaleY") ? map["scaleY"] : 1; + region.rotation = map["rotation"] || 0; + region.width = (map["width"] || 0) * scale; + region.height = (map["height"] || 0) * scale; + + var color = map["color"]; + if (color) { + region.r = this.toColor(color, 0); + region.g = this.toColor(color, 1); + region.b = this.toColor(color, 2); + region.a = this.toColor(color, 3); + } + + region.updateOffset(); + return region; + } else if (type == spine.AttachmentType.mesh) { + var mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + mesh.vertices = this.getFloatArray(map, "vertices", scale); + mesh.triangles = this.getIntArray(map, "triangles"); + mesh.regionUVs = this.getFloatArray(map, "uvs", 1); + mesh.updateUVs(); + + color = map["color"]; + if (color) { + mesh.r = this.toColor(color, 0); + mesh.g = this.toColor(color, 1); + mesh.b = this.toColor(color, 2); + mesh.a = this.toColor(color, 3); + } + + mesh.hullLength = (map["hull"] || 0) * 2; + if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); + mesh.width = (map["width"] || 0) * scale; + mesh.height = (map["height"] || 0) * scale; + return mesh; + } else if (type == spine.AttachmentType.skinnedmesh) { + var mesh = this.attachmentLoader.newSkinnedMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + + var uvs = this.getFloatArray(map, "uvs", 1); + var vertices = this.getFloatArray(map, "vertices", 1); + var weights = []; + var bones = []; + for (var i = 0, n = vertices.length; i < n; ) { + var boneCount = vertices[i++] | 0; + bones[bones.length] = boneCount; + for (var nn = i + boneCount * 4; i < nn; ) { + bones[bones.length] = vertices[i]; + weights[weights.length] = vertices[i + 1] * scale; + weights[weights.length] = vertices[i + 2] * scale; + weights[weights.length] = vertices[i + 3]; + i += 4; + } + } + mesh.bones = bones; + mesh.weights = weights; + mesh.triangles = this.getIntArray(map, "triangles"); + mesh.regionUVs = uvs; + mesh.updateUVs(); + + color = map["color"]; + if (color) { + mesh.r = this.toColor(color, 0); + mesh.g = this.toColor(color, 1); + mesh.b = this.toColor(color, 2); + mesh.a = this.toColor(color, 3); + } + + mesh.hullLength = (map["hull"] || 0) * 2; + if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); + mesh.width = (map["width"] || 0) * scale; + mesh.height = (map["height"] || 0) * scale; + return mesh; + } else if (type == spine.AttachmentType.boundingbox) { + var attachment = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + var vertices = map["vertices"]; + for (var i = 0, n = vertices.length; i < n; i++) + attachment.vertices.push(vertices[i] * scale); + return attachment; + } + throw "Unknown attachment type: " + type; + }, + readAnimation: function (name, map, skeletonData) { + var timelines = []; + var duration = 0; + + var slots = map["slots"]; + for (var slotName in slots) { + if (!slots.hasOwnProperty(slotName)) continue; + var slotMap = slots[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + + for (var timelineName in slotMap) { + if (!slotMap.hasOwnProperty(timelineName)) continue; + var values = slotMap[timelineName]; + if (timelineName == "color") { + var timeline = new spine.ColorTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var color = valueMap["color"]; + var r = this.toColor(color, 0); + var g = this.toColor(color, 1); + var b = this.toColor(color, 2); + var a = this.toColor(color, 3); + timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); + + } else if (timelineName == "attachment") { + var timeline = new spine.AttachmentTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + + } else + throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; + } + } + + var bones = map["bones"]; + for (var boneName in bones) { + if (!bones.hasOwnProperty(boneName)) continue; + var boneIndex = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) throw "Bone not found: " + boneName; + var boneMap = bones[boneName]; + + for (var timelineName in boneMap) { + if (!boneMap.hasOwnProperty(timelineName)) continue; + var values = boneMap[timelineName]; + if (timelineName == "rotate") { + var timeline = new spine.RotateTimeline(values.length); + timeline.boneIndex = boneIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); + + } else if (timelineName == "translate" || timelineName == "scale") { + var timeline; + var timelineScale = 1; + if (timelineName == "scale") + timeline = new spine.ScaleTimeline(values.length); + else { + timeline = new spine.TranslateTimeline(values.length); + timelineScale = this.scale; + } + timeline.boneIndex = boneIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var x = (valueMap["x"] || 0) * timelineScale; + var y = (valueMap["y"] || 0) * timelineScale; + timeline.setFrame(frameIndex, valueMap["time"], x, y); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); + + } else if (timelineName == "flipX" || timelineName == "flipY") { + var x = timelineName == "flipX"; + var timeline = x ? new spine.FlipXTimeline(values.length) : new spine.FlipYTimeline(values.length); + timeline.boneIndex = boneIndex; + + var field = x ? "x" : "y"; + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap[field] || false); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); + } else + throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; + } + } + + var ikMap = map["ik"]; + for (var ikConstraintName in ikMap) { + if (!ikMap.hasOwnProperty(ikConstraintName)) continue; + var ikConstraint = skeletonData.findIkConstraint(ikConstraintName); + var values = ikMap[ikConstraintName]; + var timeline = new spine.IkConstraintTimeline(values.length); + timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var mix = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1; + var bendDirection = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; + timeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]); + } + + var ffd = map["ffd"]; + for (var skinName in ffd) { + var skin = skeletonData.findSkin(skinName); + var slotMap = ffd[skinName]; + for (slotName in slotMap) { + var slotIndex = skeletonData.findSlotIndex(slotName); + var meshMap = slotMap[slotName]; + for (var meshName in meshMap) { + var values = meshMap[meshName]; + var timeline = new spine.FfdTimeline(values.length); + var attachment = skin.getAttachment(slotIndex, meshName); + if (!attachment) throw "FFD attachment not found: " + meshName; + timeline.slotIndex = slotIndex; + timeline.attachment = attachment; + + var isMesh = attachment.type == spine.AttachmentType.mesh; + var vertexCount; + if (isMesh) + vertexCount = attachment.vertices.length; + else + vertexCount = attachment.weights.length / 3 * 2; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var vertices; + if (!valueMap["vertices"]) { + if (isMesh) + vertices = attachment.vertices; + else { + vertices = []; + vertices.length = vertexCount; + } + } else { + var verticesValue = valueMap["vertices"]; + var vertices = []; + vertices.length = vertexCount; + var start = valueMap["offset"] || 0; + var nn = verticesValue.length; + if (this.scale == 1) { + for (var ii = 0; ii < nn; ii++) + vertices[ii + start] = verticesValue[ii]; + } else { + for (var ii = 0; ii < nn; ii++) + vertices[ii + start] = verticesValue[ii] * this.scale; + } + if (isMesh) { + var meshVertices = attachment.vertices; + for (var ii = 0, nn = vertices.length; ii < nn; ii++) + vertices[ii] += meshVertices[ii]; + } + } + + timeline.setFrame(frameIndex, valueMap["time"], vertices); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines[timelines.length] = timeline; + duration = Math.max(duration, timeline.frames[timeline.frameCount - 1]); + } + } + } + + var drawOrderValues = map["drawOrder"]; + if (!drawOrderValues) drawOrderValues = map["draworder"]; + if (drawOrderValues) { + var timeline = new spine.DrawOrderTimeline(drawOrderValues.length); + var slotCount = skeletonData.slots.length; + var frameIndex = 0; + for (var i = 0, n = drawOrderValues.length; i < n; i++) { + var drawOrderMap = drawOrderValues[i]; + var drawOrder = null; + if (drawOrderMap["offsets"]) { + drawOrder = []; + drawOrder.length = slotCount; + for (var ii = slotCount - 1; ii >= 0; ii--) + drawOrder[ii] = -1; + var offsets = drawOrderMap["offsets"]; + var unchanged = []; + unchanged.length = slotCount - offsets.length; + var originalIndex = 0, unchangedIndex = 0; + for (var ii = 0, nn = offsets.length; ii < nn; ii++) { + var offsetMap = offsets[ii]; + var slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); + if (slotIndex == -1) throw "Slot not found: " + offsetMap["slot"]; + // Collect unchanged items. + while (originalIndex != slotIndex) + unchanged[unchangedIndex++] = originalIndex++; + // Set changed items. + drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; + } + // Collect remaining unchanged items. + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + // Fill in unchanged items. + for (var ii = slotCount - 1; ii >= 0; ii--) + if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + } + timeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + + var events = map["events"]; + if (events) { + var timeline = new spine.EventTimeline(events.length); + var frameIndex = 0; + for (var i = 0, n = events.length; i < n; i++) { + var eventMap = events[i]; + var eventData = skeletonData.findEvent(eventMap["name"]); + if (!eventData) throw "Event not found: " + eventMap["name"]; + var event = new spine.Event(eventData); + event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; + event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; + event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; + timeline.setFrame(frameIndex++, eventMap["time"], event); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + + skeletonData.animations.push(new spine.Animation(name, timelines, duration)); + }, + readCurve: function (timeline, frameIndex, valueMap) { + var curve = valueMap["curve"]; + if (!curve) + timeline.curves.setLinear(frameIndex); + else if (curve == "stepped") + timeline.curves.setStepped(frameIndex); + else if (curve instanceof Array) + timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); + }, + toColor: function (hexString, colorIndex) { + if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString; + return parseInt(hexString.substring(colorIndex * 2, (colorIndex * 2) + 2), 16) / 255; + }, + getFloatArray: function (map, name, scale) { + var list = map[name]; + var values = new spine.Float32Array(list.length); + var i = 0, n = list.length; + if (scale == 1) { + for (; i < n; i++) + values[i] = list[i]; + } else { + for (; i < n; i++) + values[i] = list[i] * scale; + } + return values; + }, + getIntArray: function (map, name) { + var list = map[name]; + var values = new spine.Uint16Array(list.length); + for (var i = 0, n = list.length; i < n; i++) + values[i] = list[i] | 0; + return values; + } +}; + +spine.Atlas = function (atlasText, textureLoader) { + this.textureLoader = textureLoader; + this.pages = []; + this.regions = []; + + var reader = new spine.AtlasReader(atlasText); + var tuple = []; + tuple.length = 4; + var page = null; + while (true) { + var line = reader.readLine(); + if (line === null) break; + line = reader.trim(line); + if (!line.length) + page = null; + else if (!page) { + page = new spine.AtlasPage(); + page.name = line; + + if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. + page.width = parseInt(tuple[0]); + page.height = parseInt(tuple[1]); + reader.readTuple(tuple); + } + page.format = spine.Atlas.Format[tuple[0]]; + + reader.readTuple(tuple); + page.minFilter = spine.Atlas.TextureFilter[tuple[0]]; + page.magFilter = spine.Atlas.TextureFilter[tuple[1]]; + + var direction = reader.readValue(); + page.uWrap = spine.Atlas.TextureWrap.clampToEdge; + page.vWrap = spine.Atlas.TextureWrap.clampToEdge; + if (direction == "x") + page.uWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "y") + page.vWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat; + + textureLoader.load(page, line, this); + + this.pages.push(page); + + } else { + var region = new spine.AtlasRegion(); + region.name = line; + region.page = page; + + region.rotate = reader.readValue() == "true"; + + reader.readTuple(tuple); + var x = parseInt(tuple[0]); + var y = parseInt(tuple[1]); + + reader.readTuple(tuple); + var width = parseInt(tuple[0]); + var height = parseInt(tuple[1]); + + region.u = x / page.width; + region.v = y / page.height; + if (region.rotate) { + region.u2 = (x + height) / page.width; + region.v2 = (y + width) / page.height; + } else { + region.u2 = (x + width) / page.width; + region.v2 = (y + height) / page.height; + } + region.x = x; + region.y = y; + region.width = Math.abs(width); + region.height = Math.abs(height); + + if (reader.readTuple(tuple) == 4) { // split is optional + region.splits = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; + + if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits + region.pads = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; + + reader.readTuple(tuple); + } + } + + region.originalWidth = parseInt(tuple[0]); + region.originalHeight = parseInt(tuple[1]); + + reader.readTuple(tuple); + region.offsetX = parseInt(tuple[0]); + region.offsetY = parseInt(tuple[1]); + + region.index = parseInt(reader.readValue()); + + this.regions.push(region); + } + } +}; +spine.Atlas.prototype = { + findRegion: function (name) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) + if (regions[i].name == name) return regions[i]; + return null; + }, + dispose: function () { + var pages = this.pages; + for (var i = 0, n = pages.length; i < n; i++) + this.textureLoader.unload(pages[i].rendererObject); + }, + updateUVs: function (page) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) { + var region = regions[i]; + if (region.page != page) continue; + region.u = region.x / page.width; + region.v = region.y / page.height; + if (region.rotate) { + region.u2 = (region.x + region.height) / page.width; + region.v2 = (region.y + region.width) / page.height; + } else { + region.u2 = (region.x + region.width) / page.width; + region.v2 = (region.y + region.height) / page.height; + } + } + } +}; + +spine.Atlas.Format = { + alpha: 0, + intensity: 1, + luminanceAlpha: 2, + rgb565: 3, + rgba4444: 4, + rgb888: 5, + rgba8888: 6 +}; + +spine.Atlas.TextureFilter = { + nearest: 0, + linear: 1, + mipMap: 2, + mipMapNearestNearest: 3, + mipMapLinearNearest: 4, + mipMapNearestLinear: 5, + mipMapLinearLinear: 6 +}; + +spine.Atlas.TextureWrap = { + mirroredRepeat: 0, + clampToEdge: 1, + repeat: 2 +}; + +spine.AtlasPage = function () {}; +spine.AtlasPage.prototype = { + name: null, + format: null, + minFilter: null, + magFilter: null, + uWrap: null, + vWrap: null, + rendererObject: null, + width: 0, + height: 0 +}; + +spine.AtlasRegion = function () {}; +spine.AtlasRegion.prototype = { + page: null, + name: null, + x: 0, y: 0, + width: 0, height: 0, + u: 0, v: 0, u2: 0, v2: 0, + offsetX: 0, offsetY: 0, + originalWidth: 0, originalHeight: 0, + index: 0, + rotate: false, + splits: null, + pads: null +}; + +spine.AtlasReader = function (text) { + this.lines = text.split(/\r\n|\r|\n/); +}; +spine.AtlasReader.prototype = { + index: 0, + trim: function (value) { + return value.replace(/^\s+|\s+$/g, ""); + }, + readLine: function () { + if (this.index >= this.lines.length) return null; + return this.lines[this.index++]; + }, + readValue: function () { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + return this.trim(line.substring(colon + 1)); + }, + /** Returns the number of tuple values read (1, 2 or 4). */ + readTuple: function (tuple) { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + var i = 0, lastMatch = colon + 1; + for (; i < 3; i++) { + var comma = line.indexOf(",", lastMatch); + if (comma == -1) break; + tuple[i] = this.trim(line.substr(lastMatch, comma - lastMatch)); + lastMatch = comma + 1; + } + tuple[i] = this.trim(line.substring(lastMatch)); + return i + 1; + } +}; + +spine.AtlasAttachmentLoader = function (atlas) { + this.atlas = atlas; +}; +spine.AtlasAttachmentLoader.prototype = { + newRegionAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw "Region not found in atlas: " + path + " (region attachment: " + name + ")"; + var attachment = new spine.RegionAttachment(name); + attachment.rendererObject = region; + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newMeshAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw "Region not found in atlas: " + path + " (mesh attachment: " + name + ")"; + var attachment = new spine.MeshAttachment(name); + attachment.rendererObject = region; + attachment.regionU = region.u; + attachment.regionV = region.v; + attachment.regionU2 = region.u2; + attachment.regionV2 = region.v2; + attachment.regionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newSkinnedMeshAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw "Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")"; + var attachment = new spine.SkinnedMeshAttachment(name); + attachment.rendererObject = region; + attachment.regionU = region.u; + attachment.regionV = region.v; + attachment.regionU2 = region.u2; + attachment.regionV2 = region.v2; + attachment.regionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newBoundingBoxAttachment: function (skin, name) { + return new spine.BoundingBoxAttachment(name); + } +}; + +spine.SkeletonBounds = function () { + this.polygonPool = []; + this.polygons = []; + this.boundingBoxes = []; +}; +spine.SkeletonBounds.prototype = { + minX: 0, minY: 0, maxX: 0, maxY: 0, + update: function (skeleton, updateAabb) { + var slots = skeleton.slots; + var slotCount = slots.length; + var x = skeleton.x, y = skeleton.y; + var boundingBoxes = this.boundingBoxes; + var polygonPool = this.polygonPool; + var polygons = this.polygons; + + boundingBoxes.length = 0; + for (var i = 0, n = polygons.length; i < n; i++) + polygonPool.push(polygons[i]); + polygons.length = 0; + + for (var i = 0; i < slotCount; i++) { + var slot = slots[i]; + var boundingBox = slot.attachment; + if (boundingBox.type != spine.AttachmentType.boundingbox) continue; + boundingBoxes.push(boundingBox); + + var poolCount = polygonPool.length, polygon; + if (poolCount > 0) { + polygon = polygonPool[poolCount - 1]; + polygonPool.splice(poolCount - 1, 1); + } else + polygon = []; + polygons.push(polygon); + + polygon.length = boundingBox.vertices.length; + boundingBox.computeWorldVertices(x, y, slot.bone, polygon); + } + + if (updateAabb) this.aabbCompute(); + }, + aabbCompute: function () { + var polygons = this.polygons; + var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE; + for (var i = 0, n = polygons.length; i < n; i++) { + var vertices = polygons[i]; + for (var ii = 0, nn = vertices.length; ii < nn; ii += 2) { + var x = vertices[ii]; + var y = vertices[ii + 1]; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + } + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + }, + /** Returns true if the axis aligned bounding box contains the point. */ + aabbContainsPoint: function (x, y) { + return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY; + }, + /** Returns true if the axis aligned bounding box intersects the line segment. */ + aabbIntersectsSegment: function (x1, y1, x2, y2) { + var minX = this.minX, minY = this.minY, maxX = this.maxX, maxY = this.maxY; + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) + return false; + var m = (y2 - y1) / (x2 - x1); + var y = m * (minX - x1) + y1; + if (y > minY && y < maxY) return true; + y = m * (maxX - x1) + y1; + if (y > minY && y < maxY) return true; + var x = (minY - y1) / m + x1; + if (x > minX && x < maxX) return true; + x = (maxY - y1) / m + x1; + if (x > minX && x < maxX) return true; + return false; + }, + /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ + aabbIntersectsSkeleton: function (bounds) { + return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY; + }, + /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more + * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */ + containsPoint: function (x, y) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (this.polygonContainsPoint(polygons[i], x, y)) return this.boundingBoxes[i]; + return null; + }, + /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually + * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */ + intersectsSegment: function (x1, y1, x2, y2) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return this.boundingBoxes[i]; + return null; + }, + /** Returns true if the polygon contains the point. */ + polygonContainsPoint: function (polygon, x, y) { + var nn = polygon.length; + var prevIndex = nn - 2; + var inside = false; + for (var ii = 0; ii < nn; ii += 2) { + var vertexY = polygon[ii + 1]; + var prevY = polygon[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + var vertexX = polygon[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (polygon[prevIndex] - vertexX) < x) inside = !inside; + } + prevIndex = ii; + } + return inside; + }, + /** Returns true if the polygon contains the line segment. */ + polygonIntersectsSegment: function (polygon, x1, y1, x2, y2) { + var nn = polygon.length; + var width12 = x1 - x2, height12 = y1 - y2; + var det1 = x1 * y2 - y1 * x2; + var x3 = polygon[nn - 2], y3 = polygon[nn - 1]; + for (var ii = 0; ii < nn; ii += 2) { + var x4 = polygon[ii], y4 = polygon[ii + 1]; + var det2 = x3 * y4 - y3 * x4; + var width34 = x3 - x4, height34 = y3 - y4; + var det3 = width12 * height34 - height12 * width34; + var x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + var y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true; + } + x3 = x4; + y3 = y4; + } + return false; + }, + getPolygon: function (attachment) { + var index = this.boundingBoxes.indexOf(attachment); + return index == -1 ? null : this.polygons[index]; + }, + getWidth: function () { + return this.maxX - this.minX; + }, + getHeight: function () { + return this.maxY - this.minY; + } +}; diff --git a/app/Lib/Vendor/src/pixi/extras/Strip.js b/app/Lib/Vendor/src/pixi/extras/Strip.js new file mode 100644 index 0000000..6cff5df --- /dev/null +++ b/app/Lib/Vendor/src/pixi/extras/Strip.js @@ -0,0 +1,469 @@ +/** + * @author Mat Groves http://matgroves.com/ + */ + + /** + * + * @class Strip + * @extends DisplayObjectContainer + * @constructor + * @param texture {Texture} The texture to use + * @param width {Number} the width + * @param height {Number} the height + * + */ +PIXI.Strip = function(texture) +{ + PIXI.DisplayObjectContainer.call( this ); + + + /** + * The texture of the strip + * + * @property texture + * @type Texture + */ + this.texture = texture; + + // set up the main bits.. + this.uvs = new PIXI.Float32Array([0, 1, + 1, 1, + 1, 0, + 0, 1]); + + this.vertices = new PIXI.Float32Array([0, 0, + 100, 0, + 100, 100, + 0, 100]); + + this.colors = new PIXI.Float32Array([1, 1, 1, 1]); + + this.indices = new PIXI.Uint16Array([0, 1, 2, 3]); + + /** + * Whether the strip is dirty or not + * + * @property dirty + * @type Boolean + */ + this.dirty = true; + + /** + * The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode. + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ + this.blendMode = PIXI.blendModes.NORMAL; + + /** + * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. + * + * @property canvasPadding + * @type Number + */ + this.canvasPadding = 0; + + this.drawMode = PIXI.Strip.DrawModes.TRIANGLE_STRIP; + +}; + +// constructor +PIXI.Strip.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.Strip.prototype.constructor = PIXI.Strip; + +PIXI.Strip.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(!this.visible || this.alpha <= 0)return; + // render triangle strip.. + + renderSession.spriteBatch.stop(); + + // init! init! + if(!this._vertexBuffer)this._initWebGL(renderSession); + + renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader); + + this._renderStrip(renderSession); + + ///renderSession.shaderManager.activateDefaultShader(); + + renderSession.spriteBatch.start(); + + //TODO check culling +}; + +PIXI.Strip.prototype._initWebGL = function(renderSession) +{ + // build the strip! + var gl = renderSession.gl; + + this._vertexBuffer = gl.createBuffer(); + this._indexBuffer = gl.createBuffer(); + this._uvBuffer = gl.createBuffer(); + this._colorBuffer = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); +}; + +PIXI.Strip.prototype._renderStrip = function(renderSession) +{ + var gl = renderSession.gl; + var projection = renderSession.projection, + offset = renderSession.offset, + shader = renderSession.shaderManager.stripShader; + + var drawMode = this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP ? gl.TRIANGLE_STRIP : gl.TRIANGLES; + + // gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); + + renderSession.blendModeManager.setBlendMode(this.blendMode); + + + // set uniforms + gl.uniformMatrix3fv(shader.translationMatrix, false, this.worldTransform.toArray(true)); + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + gl.uniform1f(shader.alpha, this.worldAlpha); + + if(!this.dirty) + { + + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + // update the uvs + gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + + // check if a texture is dirty.. + if(this.texture.baseTexture._dirty[gl.id]) + { + renderSession.renderer.updateTexture(this.texture.baseTexture); + } + else + { + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); + } + + // dont need to upload! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); + + + } + else + { + + this.dirty = false; + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + // update the uvs + gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.activeTexture(gl.TEXTURE0); + + // check if a texture is dirty.. + if(this.texture.baseTexture._dirty[gl.id]) + { + renderSession.renderer.updateTexture(this.texture.baseTexture); + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); + } + + // dont need to upload! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + } + //console.log(gl.TRIANGLE_STRIP) + // + // + gl.drawElements(drawMode, this.indices.length, gl.UNSIGNED_SHORT, 0); + + +}; + + + +PIXI.Strip.prototype._renderCanvas = function(renderSession) +{ + var context = renderSession.context; + + var transform = this.worldTransform; + + if (renderSession.roundPixels) + { + context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx | 0, transform.ty | 0); + } + else + { + context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + } + + if (this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP) + { + this._renderCanvasTriangleStrip(context); + } + else + { + this._renderCanvasTriangles(context); + } +}; + +PIXI.Strip.prototype._renderCanvasTriangleStrip = function(context) +{ + // draw triangles!! + var vertices = this.vertices; + var uvs = this.uvs; + + var length = vertices.length / 2; + this.count++; + + for (var i = 0; i < length - 2; i++) { + // draw some triangles! + var index = i * 2; + this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); + } +}; + +PIXI.Strip.prototype._renderCanvasTriangles = function(context) +{ + // draw triangles!! + var vertices = this.vertices; + var uvs = this.uvs; + var indices = this.indices; + + var length = indices.length; + this.count++; + + for (var i = 0; i < length; i += 3) { + // draw some triangles! + var index0 = indices[i] * 2, index1 = indices[i + 1] * 2, index2 = indices[i + 2] * 2; + this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); + } +}; + +PIXI.Strip.prototype._renderCanvasDrawTriangle = function(context, vertices, uvs, index0, index1, index2) +{ + var textureSource = this.texture.baseTexture.source; + var textureWidth = this.texture.width; + var textureHeight = this.texture.height; + + var x0 = vertices[index0], x1 = vertices[index1], x2 = vertices[index2]; + var y0 = vertices[index0 + 1], y1 = vertices[index1 + 1], y2 = vertices[index2 + 1]; + + var u0 = uvs[index0] * textureWidth, u1 = uvs[index1] * textureWidth, u2 = uvs[index2] * textureWidth; + var v0 = uvs[index0 + 1] * textureHeight, v1 = uvs[index1 + 1] * textureHeight, v2 = uvs[index2 + 1] * textureHeight; + + if (this.canvasPadding > 0) { + var paddingX = this.canvasPadding / this.worldTransform.a; + var paddingY = this.canvasPadding / this.worldTransform.d; + var centerX = (x0 + x1 + x2) / 3; + var centerY = (y0 + y1 + y2) / 3; + + var normX = x0 - centerX; + var normY = y0 - centerY; + + var dist = Math.sqrt(normX * normX + normY * normY); + x0 = centerX + (normX / dist) * (dist + paddingX); + y0 = centerY + (normY / dist) * (dist + paddingY); + + // + + normX = x1 - centerX; + normY = y1 - centerY; + + dist = Math.sqrt(normX * normX + normY * normY); + x1 = centerX + (normX / dist) * (dist + paddingX); + y1 = centerY + (normY / dist) * (dist + paddingY); + + normX = x2 - centerX; + normY = y2 - centerY; + + dist = Math.sqrt(normX * normX + normY * normY); + x2 = centerX + (normX / dist) * (dist + paddingX); + y2 = centerY + (normY / dist) * (dist + paddingY); + } + + context.save(); + context.beginPath(); + + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + context.closePath(); + + context.clip(); + + // Compute matrix transform + var delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); + var deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); + var deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); + var deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); + var deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); + var deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); + var deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); + + context.transform(deltaA / delta, deltaD / delta, + deltaB / delta, deltaE / delta, + deltaC / delta, deltaF / delta); + + context.drawImage(textureSource, 0, 0); + context.restore(); +}; + + + +/** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render + * @private + */ +PIXI.Strip.prototype.renderStripFlat = function(strip) +{ + var context = this.context; + var vertices = strip.vertices; + + var length = vertices.length/2; + this.count++; + + context.beginPath(); + for (var i=1; i < length-2; i++) + { + // draw some triangles! + var index = i*2; + + var x0 = vertices[index], x1 = vertices[index+2], x2 = vertices[index+4]; + var y0 = vertices[index+1], y1 = vertices[index+3], y2 = vertices[index+5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = '#FF0000'; + context.fill(); + context.closePath(); +}; + +/* +PIXI.Strip.prototype.setTexture = function(texture) +{ + //TODO SET THE TEXTURES + //TODO VISIBILITY + + // stop current texture + this.texture = texture; + this.width = texture.frame.width; + this.height = texture.frame.height; + this.updateFrame = true; +}; +*/ + +/** + * When the texture is updated, this event will fire to update the scale and frame + * + * @method onTextureUpdate + * @param event + * @private + */ + +PIXI.Strip.prototype.onTextureUpdate = function() +{ + this.updateFrame = true; +}; + +/** + * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. + * + * @method getBounds + * @param matrix {Matrix} the transformation matrix of the sprite + * @return {Rectangle} the framing rectangle + */ +PIXI.Strip.prototype.getBounds = function(matrix) +{ + var worldTransform = matrix || this.worldTransform; + + var a = worldTransform.a; + var b = worldTransform.b; + var c = worldTransform.c; + var d = worldTransform.d; + var tx = worldTransform.tx; + var ty = worldTransform.ty; + + var maxX = -Infinity; + var maxY = -Infinity; + + var minX = Infinity; + var minY = Infinity; + + var vertices = this.vertices; + for (var i = 0, n = vertices.length; i < n; i += 2) + { + var rawX = vertices[i], rawY = vertices[i + 1]; + var x = (a * rawX) + (c * rawY) + tx; + var y = (d * rawY) + (b * rawX) + ty; + + minX = x < minX ? x : minX; + minY = y < minY ? y : minY; + + maxX = x > maxX ? x : maxX; + maxY = y > maxY ? y : maxY; + } + + if (minX === -Infinity || maxY === Infinity) + { + return PIXI.EmptyRectangle; + } + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + +/** + * Different drawing buffer modes supported + * + * @property + * @type {{TRIANGLE_STRIP: number, TRIANGLES: number}} + * @static + */ +PIXI.Strip.DrawModes = { + TRIANGLE_STRIP: 0, + TRIANGLES: 1 +}; diff --git a/app/Lib/Vendor/src/pixi/extras/TilingSprite.js b/app/Lib/Vendor/src/pixi/extras/TilingSprite.js new file mode 100644 index 0000000..2b758e2 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/extras/TilingSprite.js @@ -0,0 +1,466 @@ +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * A tiling sprite is a fast way of rendering a tiling image + * + * @class TilingSprite + * @extends Sprite + * @constructor + * @param texture {Texture} the texture of the tiling sprite + * @param width {Number} the width of the tiling sprite + * @param height {Number} the height of the tiling sprite + */ +PIXI.TilingSprite = function(texture, width, height) +{ + PIXI.Sprite.call( this, texture); + + /** + * The with of the tiling sprite + * + * @property width + * @type Number + */ + this._width = width || 100; + + /** + * The height of the tiling sprite + * + * @property height + * @type Number + */ + this._height = height || 100; + + /** + * The scaling of the image that is being tiled + * + * @property tileScale + * @type Point + */ + this.tileScale = new PIXI.Point(1,1); + + /** + * A point that represents the scale of the texture object + * + * @property tileScaleOffset + * @type Point + */ + this.tileScaleOffset = new PIXI.Point(1,1); + + /** + * The offset position of the image that is being tiled + * + * @property tilePosition + * @type Point + */ + this.tilePosition = new PIXI.Point(0,0); + + /** + * Whether this sprite is renderable or not + * + * @property renderable + * @type Boolean + * @default true + */ + this.renderable = true; + + /** + * The tint applied to the sprite. This is a hex value + * + * @property tint + * @type Number + * @default 0xFFFFFF + */ + this.tint = 0xFFFFFF; + + /** + * The blend mode to be applied to the sprite + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ + this.blendMode = PIXI.blendModes.NORMAL; + + + +}; + +// constructor +PIXI.TilingSprite.prototype = Object.create(PIXI.Sprite.prototype); +PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite; + + +/** + * The width of the sprite, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.TilingSprite.prototype, 'width', { + get: function() { + return this._width; + }, + set: function(value) { + + this._width = value; + } +}); + +/** + * The height of the TilingSprite, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.TilingSprite.prototype, 'height', { + get: function() { + return this._height; + }, + set: function(value) { + this._height = value; + } +}); + +PIXI.TilingSprite.prototype.setTexture = function(texture) +{ + if (this.texture === texture) return; + + this.texture = texture; + + this.refreshTexture = true; + + this.cachedTint = 0xFFFFFF; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.TilingSprite.prototype._renderWebGL = function(renderSession) +{ + if (this.visible === false || this.alpha === 0) return; + var i,j; + + if (this._mask) + { + renderSession.spriteBatch.stop(); + renderSession.maskManager.pushMask(this.mask, renderSession); + renderSession.spriteBatch.start(); + } + + if (this._filters) + { + renderSession.spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + + + + if (!this.tilingTexture || this.refreshTexture) + { + this.generateTilingTexture(true); + + if (this.tilingTexture && this.tilingTexture.needsUpdate) + { + //TODO - tweaking + PIXI.updateWebGLTexture(this.tilingTexture.baseTexture, renderSession.gl); + this.tilingTexture.needsUpdate = false; + // this.tilingTexture._uvs = null; + } + } + else + { + renderSession.spriteBatch.renderTilingSprite(this); + } + // simple render children! + for (i=0,j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + + + +/** + * When the texture is updated, this event will fire to update the scale and frame + * + * @method onTextureUpdate + * @param event + * @private + */ +PIXI.TilingSprite.prototype.onTextureUpdate = function() +{ + // overriding the sprite version of this! +}; + + +/** +* +* @method generateTilingTexture +* +* @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two +*/ +PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo) +{ + if (!this.texture.baseTexture.hasLoaded) return; + + var texture = this.originalTexture || this.texture; + var frame = texture.frame; + var targetWidth, targetHeight; + + // Check that the frame is the same size as the base texture. + var isFrame = frame.width !== texture.baseTexture.width || frame.height !== texture.baseTexture.height; + + var newTextureRequired = false; + + if (!forcePowerOfTwo) + { + if (isFrame) + { + targetWidth = frame.width; + targetHeight = frame.height; + + newTextureRequired = true; + } + } + else + { + targetWidth = PIXI.getNextPowerOfTwo(frame.width); + targetHeight = PIXI.getNextPowerOfTwo(frame.height); + + // If the BaseTexture dimensions don't match the texture frame then we need a new texture anyway because it's part of a texture atlas + if (frame.width !== targetWidth || frame.height !== targetHeight || texture.baseTexture.width !== targetWidth || texture.baseTexture.height || targetHeight) newTextureRequired = true; + } + + if (newTextureRequired) + { + var canvasBuffer; + + if (this.tilingTexture && this.tilingTexture.isTiling) + { + canvasBuffer = this.tilingTexture.canvasBuffer; + canvasBuffer.resize(targetWidth, targetHeight); + this.tilingTexture.baseTexture.width = targetWidth; + this.tilingTexture.baseTexture.height = targetHeight; + this.tilingTexture.needsUpdate = true; + } + else + { + canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight); + + this.tilingTexture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + this.tilingTexture.canvasBuffer = canvasBuffer; + this.tilingTexture.isTiling = true; + } + + canvasBuffer.context.drawImage(texture.baseTexture.source, + texture.crop.x, + texture.crop.y, + texture.crop.width, + texture.crop.height, + 0, + 0, + targetWidth, + targetHeight); + + this.tileScaleOffset.x = frame.width / targetWidth; + this.tileScaleOffset.y = frame.height / targetHeight; + } + else + { + // TODO - switching? + if (this.tilingTexture && this.tilingTexture.isTiling) + { + // destroy the tiling texture! + // TODO could store this somewhere? + this.tilingTexture.destroy(true); + } + + this.tileScaleOffset.x = 1; + this.tileScaleOffset.y = 1; + this.tilingTexture = texture; + } + + this.refreshTexture = false; + + this.originalTexture = this.texture; + this.texture = this.tilingTexture; + + this.tilingTexture.baseTexture._powerOf2 = true; +}; diff --git a/app/Lib/Vendor/src/pixi/filters/AbstractFilter.js b/app/Lib/Vendor/src/pixi/filters/AbstractFilter.js new file mode 100644 index 0000000..6727dc5 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/AbstractFilter.js @@ -0,0 +1,78 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This is the base class for creating a PIXI filter. Currently only webGL supports filters. + * If you want to make a custom filter this should be your base class. + * @class AbstractFilter + * @constructor + * @param fragmentSrc {Array} The fragment source in an array of strings. + * @param uniforms {Object} An object containing the uniforms for this filter. + */ +PIXI.AbstractFilter = function(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * @property passes + * @type Array(Filter) + * @private + */ + this.passes = [this]; + + /** + * @property shaders + * @type Array(Shader) + * @private + */ + this.shaders = []; + + /** + * @property dirty + * @type Boolean + */ + this.dirty = true; + + /** + * @property padding + * @type Number + */ + this.padding = 0; + + /** + * @property uniforms + * @type object + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @property fragmentSrc + * @type Array + * @private + */ + this.fragmentSrc = fragmentSrc || []; +}; + +PIXI.AbstractFilter.prototype.constructor = PIXI.AbstractFilter; + +/** + * Syncs the uniforms between the class object and the shaders. + * + * @method syncUniforms + */ +PIXI.AbstractFilter.prototype.syncUniforms = function() +{ + for(var i=0,j=this.shaders.length; i 0.2) n = 65600.0; // :', + ' if (gray > 0.3) n = 332772.0; // *', + ' if (gray > 0.4) n = 15255086.0; // o', + ' if (gray > 0.5) n = 23385164.0; // &', + ' if (gray > 0.6) n = 15252014.0; // 8', + ' if (gray > 0.7) n = 13199452.0; // @', + ' if (gray > 0.8) n = 11512810.0; // #', + + ' vec2 p = mod( uv / ( pixelSize * 0.5 ), 2.0) - vec2(1.0);', + ' col = col * character(n, p);', + + ' gl_FragColor = vec4(col, 1.0);', + '}' + ]; +}; + +PIXI.AsciiFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.AsciiFilter.prototype.constructor = PIXI.AsciiFilter; + +/** + * The pixel size used by the filter. + * + * @property size + * @type Number + */ +Object.defineProperty(PIXI.AsciiFilter.prototype, 'size', { + get: function() { + return this.uniforms.pixelSize.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.pixelSize.value = value; + } +}); \ No newline at end of file diff --git a/app/Lib/Vendor/src/pixi/filters/BlurFilter.js b/app/Lib/Vendor/src/pixi/filters/BlurFilter.js new file mode 100644 index 0000000..d93d147 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/BlurFilter.js @@ -0,0 +1,70 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The BlurFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately (always relative to the stage). + * + * @class BlurFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.BlurFilter = function() +{ + this.blurXFilter = new PIXI.BlurXFilter(); + this.blurYFilter = new PIXI.BlurYFilter(); + + this.passes =[this.blurXFilter, this.blurYFilter]; +}; + +PIXI.BlurFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.BlurFilter.prototype.constructor = PIXI.BlurFilter; + +/** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @property blur + * @type Number + * @default 2 + */ +Object.defineProperty(PIXI.BlurFilter.prototype, 'blur', { + get: function() { + return this.blurXFilter.blur; + }, + set: function(value) { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } +}); + +/** + * Sets the strength of the blurX property + * + * @property blurX + * @type Number + * @default 2 + */ +Object.defineProperty(PIXI.BlurFilter.prototype, 'blurX', { + get: function() { + return this.blurXFilter.blur; + }, + set: function(value) { + this.blurXFilter.blur = value; + } +}); + +/** + * Sets the strength of the blurY property + * + * @property blurY + * @type Number + * @default 2 + */ +Object.defineProperty(PIXI.BlurFilter.prototype, 'blurY', { + get: function() { + return this.blurYFilter.blur; + }, + set: function(value) { + this.blurYFilter.blur = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/BlurXFilter.js b/app/Lib/Vendor/src/pixi/filters/BlurXFilter.js new file mode 100644 index 0000000..13f67c3 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/BlurXFilter.js @@ -0,0 +1,67 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The BlurXFilter applies a horizontal Gaussian blur to an object. + * + * @class BlurXFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.BlurXFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + blur: {type: '1f', value: 1/512} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform float blur;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' vec4 sum = vec4(0.0);', + + ' sum += texture2D(uSampler, vec2(vTextureCoord.x - 4.0*blur, vTextureCoord.y)) * 0.05;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x - 3.0*blur, vTextureCoord.y)) * 0.09;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x - 2.0*blur, vTextureCoord.y)) * 0.12;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x - blur, vTextureCoord.y)) * 0.15;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * 0.16;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x + blur, vTextureCoord.y)) * 0.15;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x + 2.0*blur, vTextureCoord.y)) * 0.12;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x + 3.0*blur, vTextureCoord.y)) * 0.09;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x + 4.0*blur, vTextureCoord.y)) * 0.05;', + + ' gl_FragColor = sum;', + '}' + ]; +}; + +PIXI.BlurXFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.BlurXFilter.prototype.constructor = PIXI.BlurXFilter; + +/** + * Sets the strength of both the blur. + * + * @property blur + * @type Number + * @default 2 + */ +Object.defineProperty(PIXI.BlurXFilter.prototype, 'blur', { + get: function() { + return this.uniforms.blur.value / (1/7000); + }, + set: function(value) { + + this.dirty = true; + this.uniforms.blur.value = (1/7000) * value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/BlurYFilter.js b/app/Lib/Vendor/src/pixi/filters/BlurYFilter.js new file mode 100644 index 0000000..5aecfef --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/BlurYFilter.js @@ -0,0 +1,66 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The BlurYFilter applies a vertical Gaussian blur to an object. + * + * @class BlurYFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.BlurYFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + blur: {type: '1f', value: 1/512} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform float blur;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' vec4 sum = vec4(0.0);', + + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - 4.0*blur)) * 0.05;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - 3.0*blur)) * 0.09;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - 2.0*blur)) * 0.12;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y - blur)) * 0.15;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * 0.16;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + blur)) * 0.15;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + 2.0*blur)) * 0.12;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + 3.0*blur)) * 0.09;', + ' sum += texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y + 4.0*blur)) * 0.05;', + + ' gl_FragColor = sum;', + '}' + ]; +}; + +PIXI.BlurYFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.BlurYFilter.prototype.constructor = PIXI.BlurYFilter; + +/** + * Sets the strength of both the blur. + * + * @property blur + * @type Number + * @default 2 + */ +Object.defineProperty(PIXI.BlurYFilter.prototype, 'blur', { + get: function() { + return this.uniforms.blur.value / (1/7000); + }, + set: function(value) { + //this.padding = value; + this.uniforms.blur.value = (1/7000) * value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/ColorMatrixFilter.js b/app/Lib/Vendor/src/pixi/filters/ColorMatrixFilter.js new file mode 100644 index 0000000..8f1dcbe --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/ColorMatrixFilter.js @@ -0,0 +1,60 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The ColorMatrixFilter class lets you apply a 4x4 matrix transformation on the RGBA + * color and alpha values of every pixel on your displayObject to produce a result + * with a new set of RGBA color and alpha values. It's pretty powerful! + * + * @class ColorMatrixFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.ColorMatrixFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform float invert;', + 'uniform mat4 matrix;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;', + // ' gl_FragColor = gl_FragColor;', + '}' + ]; +}; + +PIXI.ColorMatrixFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.ColorMatrixFilter.prototype.constructor = PIXI.ColorMatrixFilter; + +/** + * Sets the matrix of the color matrix filter + * + * @property matrix + * @type Array(Number) + * @default [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1] + */ +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); \ No newline at end of file diff --git a/app/Lib/Vendor/src/pixi/filters/ColorStepFilter.js b/app/Lib/Vendor/src/pixi/filters/ColorStepFilter.js new file mode 100644 index 0000000..9481acd --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/ColorStepFilter.js @@ -0,0 +1,54 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This lowers the color depth of your image by the given amount, producing an image with a smaller palette. + * + * @class ColorStepFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.ColorStepFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + step: {type: '1f', value: 5} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'uniform float step;', + + 'void main(void) {', + ' vec4 color = texture2D(uSampler, vTextureCoord);', + ' color = floor(color * step) / step;', + ' gl_FragColor = color;', + '}' + ]; +}; + +PIXI.ColorStepFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.ColorStepFilter.prototype.constructor = PIXI.ColorStepFilter; + +/** + * The number of steps to reduce the palette by. + * + * @property step + * @type Number + */ +Object.defineProperty(PIXI.ColorStepFilter.prototype, 'step', { + get: function() { + return this.uniforms.step.value; + }, + set: function(value) { + this.uniforms.step.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/ConvolutionFilter.js b/app/Lib/Vendor/src/pixi/filters/ConvolutionFilter.js new file mode 100644 index 0000000..1d180d7 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/ConvolutionFilter.js @@ -0,0 +1,106 @@ +/** + * The ConvolutionFilter class applies a matrix convolution filter effect. + * A convolution combines pixels in the input image with neighboring pixels to produce a new image. + * A wide variety of image effects can be achieved through convolutions, including blurring, edge detection, sharpening, embossing, and beveling. + * The matrix should be specified as a 9 point Array. See http://docs.gimp.org/en/plug-in-convmatrix.html for more info. + * + * @class ConvolutionFilter + * @extends AbstractFilter + * @constructor + * @param matrix {Array} An array of values used for matrix transformation. Specified as a 9 point Array. + * @param width {Number} Width of the object you are transforming + * @param height {Number} Height of the object you are transforming + */ +PIXI.ConvolutionFilter = function(matrix, width, height) +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + m : {type: '1fv', value: new PIXI.Float32Array(matrix)}, + texelSizeX: {type: '1f', value: 1 / width}, + texelSizeY: {type: '1f', value: 1 / height} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying mediump vec2 vTextureCoord;', + 'uniform sampler2D texture;', + 'uniform float texelSizeX;', + 'uniform float texelSizeY;', + 'uniform float m[9];', + + 'vec2 px = vec2(texelSizeX, texelSizeY);', + + 'void main(void) {', + 'vec4 c11 = texture2D(texture, vTextureCoord - px);', // top left + 'vec4 c12 = texture2D(texture, vec2(vTextureCoord.x, vTextureCoord.y - px.y));', // top center + 'vec4 c13 = texture2D(texture, vec2(vTextureCoord.x + px.x, vTextureCoord.y - px.y));', // top right + + 'vec4 c21 = texture2D(texture, vec2(vTextureCoord.x - px.x, vTextureCoord.y) );', // mid left + 'vec4 c22 = texture2D(texture, vTextureCoord);', // mid center + 'vec4 c23 = texture2D(texture, vec2(vTextureCoord.x + px.x, vTextureCoord.y) );', // mid right + + 'vec4 c31 = texture2D(texture, vec2(vTextureCoord.x - px.x, vTextureCoord.y + px.y) );', // bottom left + 'vec4 c32 = texture2D(texture, vec2(vTextureCoord.x, vTextureCoord.y + px.y) );', // bottom center + 'vec4 c33 = texture2D(texture, vTextureCoord + px );', // bottom right + + 'gl_FragColor = ', + 'c11 * m[0] + c12 * m[1] + c22 * m[2] +', + 'c21 * m[3] + c22 * m[4] + c23 * m[5] +', + 'c31 * m[6] + c32 * m[7] + c33 * m[8];', + 'gl_FragColor.a = c22.a;', + '}' + ]; + +}; + +PIXI.ConvolutionFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.ConvolutionFilter.prototype.constructor = PIXI.ConvolutionFilter; + +/** + * An array of values used for matrix transformation. Specified as a 9 point Array. + * + * @property matrix + * @type Array + */ +Object.defineProperty(PIXI.ConvolutionFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.m.value; + }, + set: function(value) { + this.uniforms.m.value = new PIXI.Float32Array(value); + } +}); + +/** + * Width of the object you are transforming + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.ConvolutionFilter.prototype, 'width', { + get: function() { + return this.uniforms.texelSizeX.value; + }, + set: function(value) { + this.uniforms.texelSizeX.value = 1/value; + } +}); + +/** + * Height of the object you are transforming + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.ConvolutionFilter.prototype, 'height', { + get: function() { + return this.uniforms.texelSizeY.value; + }, + set: function(value) { + this.uniforms.texelSizeY.value = 1/value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/CrossHatchFilter.js b/app/Lib/Vendor/src/pixi/filters/CrossHatchFilter.js new file mode 100644 index 0000000..4b9f381 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/CrossHatchFilter.js @@ -0,0 +1,80 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Cross Hatch effect filter. + * + * @class CrossHatchFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.CrossHatchFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + blur: {type: '1f', value: 1 / 512} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform float blur;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' float lum = length(texture2D(uSampler, vTextureCoord.xy).rgb);', + + ' gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);', + + ' if (lum < 1.00) {', + ' if (mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) {', + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);', + ' }', + ' }', + + ' if (lum < 0.75) {', + ' if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) {', + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);', + ' }', + ' }', + + ' if (lum < 0.50) {', + ' if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) {', + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);', + ' }', + ' }', + + ' if (lum < 0.3) {', + ' if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) {', + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);', + ' }', + ' }', + '}' + ]; +}; + +PIXI.CrossHatchFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.CrossHatchFilter.prototype.constructor = PIXI.CrossHatchFilter; + +/** + * Sets the strength of both the blur. + * + * @property blur + * @type Number + * @default 2 + */ +Object.defineProperty(PIXI.CrossHatchFilter.prototype, 'blur', { + get: function() { + return this.uniforms.blur.value / (1/7000); + }, + set: function(value) { + //this.padding = value; + this.uniforms.blur.value = (1/7000) * value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/DisplacementFilter.js b/app/Lib/Vendor/src/pixi/filters/DisplacementFilter.js new file mode 100644 index 0000000..803b7b8 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/DisplacementFilter.js @@ -0,0 +1,134 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The DisplacementFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used offset the x and the g property of the texture is used to offset the y. + * + * @class DisplacementFilter + * @extends AbstractFilter + * @constructor + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +PIXI.DisplacementFilter = function(texture) +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + displacementMap: {type: 'sampler2D', value:texture}, + scale: {type: '2f', value:{x:30, y:30}}, + offset: {type: '2f', value:{x:0, y:0}}, + mapDimensions: {type: '2f', value:{x:1, y:5112}}, + dimensions: {type: '4fv', value:[0,0,0,0]} + }; + + if(texture.baseTexture.hasLoaded) + { + this.uniforms.mapDimensions.value.x = texture.width; + this.uniforms.mapDimensions.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D displacementMap;', + 'uniform sampler2D uSampler;', + 'uniform vec2 scale;', + 'uniform vec2 offset;', + 'uniform vec4 dimensions;', + 'uniform vec2 mapDimensions;',// = vec2(256.0, 256.0);', + // 'const vec2 textureDimensions = vec2(750.0, 750.0);', + + 'void main(void) {', + ' vec2 mapCords = vTextureCoord.xy;', + //' mapCords -= ;', + ' mapCords += (dimensions.zw + offset)/ dimensions.xy ;', + ' mapCords.y *= -1.0;', + ' mapCords.y += 1.0;', + ' vec2 matSample = texture2D(displacementMap, mapCords).xy;', + ' matSample -= 0.5;', + ' matSample *= scale;', + ' matSample /= mapDimensions;', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x + matSample.x, vTextureCoord.y + matSample.y));', + ' gl_FragColor.rgb = mix( gl_FragColor.rgb, gl_FragColor.rgb, 1.0);', + ' vec2 cord = vTextureCoord;', + + //' gl_FragColor = texture2D(displacementMap, cord);', + // ' gl_FragColor = gl_FragColor;', + '}' + ]; +}; + +PIXI.DisplacementFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.DisplacementFilter.prototype.constructor = PIXI.DisplacementFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + * @method onTextureLoaded + */ +PIXI.DisplacementFilter.prototype.onTextureLoaded = function() +{ + this.uniforms.mapDimensions.value.x = this.uniforms.displacementMap.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.displacementMap.value.height; + + this.uniforms.displacementMap.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +/** + * The texture used for the displacement map. Must be power of 2 texture. + * + * @property map + * @type Texture + */ +Object.defineProperty(PIXI.DisplacementFilter.prototype, 'map', { + get: function() { + return this.uniforms.displacementMap.value; + }, + set: function(value) { + this.uniforms.displacementMap.value = value; + } +}); + +/** + * The multiplier used to scale the displacement result from the map calculation. + * + * @property scale + * @type Point + */ +Object.defineProperty(PIXI.DisplacementFilter.prototype, 'scale', { + get: function() { + return this.uniforms.scale.value; + }, + set: function(value) { + this.uniforms.scale.value = value; + } +}); + +/** + * The offset used to move the displacement map. + * + * @property offset + * @type Point + */ +Object.defineProperty(PIXI.DisplacementFilter.prototype, 'offset', { + get: function() { + return this.uniforms.offset.value; + }, + set: function(value) { + this.uniforms.offset.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/DotScreenFilter.js b/app/Lib/Vendor/src/pixi/filters/DotScreenFilter.js new file mode 100644 index 0000000..5a7462f --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/DotScreenFilter.js @@ -0,0 +1,85 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * original filter: https://github.com/evanw/glfx.js/blob/master/src/filters/fun/dotscreen.js + */ + +/** + * This filter applies a dotscreen effect making display objects appear to be made out of black and white halftone dots like an old printer. + * + * @class DotScreenFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.DotScreenFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + scale: {type: '1f', value:1}, + angle: {type: '1f', value:5}, + dimensions: {type: '4fv', value:[0,0,0,0]} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform vec4 dimensions;', + 'uniform sampler2D uSampler;', + + 'uniform float angle;', + 'uniform float scale;', + + 'float pattern() {', + ' float s = sin(angle), c = cos(angle);', + ' vec2 tex = vTextureCoord * dimensions.xy;', + ' vec2 point = vec2(', + ' c * tex.x - s * tex.y,', + ' s * tex.x + c * tex.y', + ' ) * scale;', + ' return (sin(point.x) * sin(point.y)) * 4.0;', + '}', + + 'void main() {', + ' vec4 color = texture2D(uSampler, vTextureCoord);', + ' float average = (color.r + color.g + color.b) / 3.0;', + ' gl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);', + '}' + ]; +}; + +PIXI.DotScreenFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.DotScreenFilter.prototype.constructor = PIXI.DotScreenFilter; + +/** + * The scale of the effect. + * @property scale + * @type Number + */ +Object.defineProperty(PIXI.DotScreenFilter.prototype, 'scale', { + get: function() { + return this.uniforms.scale.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.scale.value = value; + } +}); + +/** + * The radius of the effect. + * @property angle + * @type Number + */ +Object.defineProperty(PIXI.DotScreenFilter.prototype, 'angle', { + get: function() { + return this.uniforms.angle.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.angle.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/FilterBlock.js b/app/Lib/Vendor/src/pixi/filters/FilterBlock.js new file mode 100644 index 0000000..fbdacc2 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/FilterBlock.js @@ -0,0 +1,30 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A target and pass info object for filters. + * + * @class FilterBlock + * @constructor + */ +PIXI.FilterBlock = function() +{ + /** + * The visible state of this FilterBlock. + * + * @property visible + * @type Boolean + */ + this.visible = true; + + /** + * The renderable state of this FilterBlock. + * + * @property renderable + * @type Boolean + */ + this.renderable = true; +}; + +PIXI.FilterBlock.prototype.constructor = PIXI.FilterBlock; diff --git a/app/Lib/Vendor/src/pixi/filters/GrayFilter.js b/app/Lib/Vendor/src/pixi/filters/GrayFilter.js new file mode 100644 index 0000000..201d026 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/GrayFilter.js @@ -0,0 +1,53 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This greyscales the palette of your Display Objects. + * + * @class GrayFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.GrayFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + gray: {type: '1f', value: 1} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'uniform float gray;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord);', + ' gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126*gl_FragColor.r + 0.7152*gl_FragColor.g + 0.0722*gl_FragColor.b), gray);', + // ' gl_FragColor = gl_FragColor;', + '}' + ]; +}; + +PIXI.GrayFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.GrayFilter.prototype.constructor = PIXI.GrayFilter; + +/** + * The strength of the gray. 1 will make the object black and white, 0 will make the object its normal color. + * @property gray + * @type Number + */ +Object.defineProperty(PIXI.GrayFilter.prototype, 'gray', { + get: function() { + return this.uniforms.gray.value; + }, + set: function(value) { + this.uniforms.gray.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/InvertFilter.js b/app/Lib/Vendor/src/pixi/filters/InvertFilter.js new file mode 100644 index 0000000..7f5e84c --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/InvertFilter.js @@ -0,0 +1,54 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This inverts your Display Objects colors. + * + * @class InvertFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.InvertFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + invert: {type: '1f', value: 1} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform float invert;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord);', + ' gl_FragColor.rgb = mix( (vec3(1)-gl_FragColor.rgb) * gl_FragColor.a, gl_FragColor.rgb, 1.0 - invert);', + //' gl_FragColor.rgb = gl_FragColor.rgb * gl_FragColor.a;', + // ' gl_FragColor = gl_FragColor * vColor;', + '}' + ]; +}; + +PIXI.InvertFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.InvertFilter.prototype.constructor = PIXI.InvertFilter; + +/** + * The strength of the invert. 1 will fully invert the colors, 0 will make the object its normal color + * @property invert + * @type Number +*/ +Object.defineProperty(PIXI.InvertFilter.prototype, 'invert', { + get: function() { + return this.uniforms.invert.value; + }, + set: function(value) { + this.uniforms.invert.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/NoiseFilter.js b/app/Lib/Vendor/src/pixi/filters/NoiseFilter.js new file mode 100644 index 0000000..ff248e4 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/NoiseFilter.js @@ -0,0 +1,62 @@ +/** + * @author Vico @vicocotea + * original filter: https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/noise.js + */ + +/** + * A Noise effect filter. + * + * @class NoiseFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.NoiseFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + noise: {type: '1f', value: 0.5} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'uniform sampler2D uSampler;', + 'uniform float noise;', + 'varying vec2 vTextureCoord;', + + 'float rand(vec2 co) {', + ' return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);', + '}', + 'void main() {', + ' vec4 color = texture2D(uSampler, vTextureCoord);', + + ' float diff = (rand(vTextureCoord) - 0.5) * noise;', + ' color.r += diff;', + ' color.g += diff;', + ' color.b += diff;', + + ' gl_FragColor = color;', + '}' + ]; +}; + +PIXI.NoiseFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.NoiseFilter.prototype.constructor = PIXI.NoiseFilter; + +/** + * The amount of noise to apply. + * @property noise + * @type Number +*/ +Object.defineProperty(PIXI.NoiseFilter.prototype, 'noise', { + get: function() { + return this.uniforms.noise.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.noise.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/NormalMapFilter.js b/app/Lib/Vendor/src/pixi/filters/NormalMapFilter.js new file mode 100644 index 0000000..e686905 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/NormalMapFilter.js @@ -0,0 +1,196 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * The NormalMapFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used offset the x and the g property of the texture is used to offset the y. + * + * @class NormalMapFilter + * @extends AbstractFilter + * @constructor + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +PIXI.NormalMapFilter = function(texture) +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + displacementMap: {type: 'sampler2D', value:texture}, + scale: {type: '2f', value:{x:15, y:15}}, + offset: {type: '2f', value:{x:0, y:0}}, + mapDimensions: {type: '2f', value:{x:1, y:1}}, + dimensions: {type: '4f', value:[0,0,0,0]}, + // LightDir: {type: 'f3', value:[0, 1, 0]}, + LightPos: {type: '3f', value:[0, 1, 0]} + }; + + + if(texture.baseTexture.hasLoaded) + { + this.uniforms.mapDimensions.value.x = texture.width; + this.uniforms.mapDimensions.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on("loaded", this.boundLoadedFunction); + } + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D displacementMap;", + "uniform sampler2D uSampler;", + + "uniform vec4 dimensions;", + + "const vec2 Resolution = vec2(1.0,1.0);", //resolution of screen + "uniform vec3 LightPos;", //light position, normalized + "const vec4 LightColor = vec4(1.0, 1.0, 1.0, 1.0);", //light RGBA -- alpha is intensity + "const vec4 AmbientColor = vec4(1.0, 1.0, 1.0, 0.5);", //ambient RGBA -- alpha is intensity + "const vec3 Falloff = vec3(0.0, 1.0, 0.2);", //attenuation coefficients + + "uniform vec3 LightDir;",//" = vec3(1.0, 0.0, 1.0);", + + + "uniform vec2 mapDimensions;",// = vec2(256.0, 256.0);", + + + "void main(void) {", + "vec2 mapCords = vTextureCoord.xy;", + + "vec4 color = texture2D(uSampler, vTextureCoord.st);", + "vec3 nColor = texture2D(displacementMap, vTextureCoord.st).rgb;", + + + "mapCords *= vec2(dimensions.x/512.0, dimensions.y/512.0);", + + "mapCords.y *= -1.0;", + "mapCords.y += 1.0;", + + //RGBA of our diffuse color + "vec4 DiffuseColor = texture2D(uSampler, vTextureCoord);", + + //RGB of our normal map + "vec3 NormalMap = texture2D(displacementMap, mapCords).rgb;", + + //The delta position of light + //"vec3 LightDir = vec3(LightPos.xy - (gl_FragCoord.xy / Resolution.xy), LightPos.z);", + "vec3 LightDir = vec3(LightPos.xy - (mapCords.xy), LightPos.z);", + //Correct for aspect ratio + //"LightDir.x *= Resolution.x / Resolution.y;", + + //Determine distance (used for attenuation) BEFORE we normalize our LightDir + "float D = length(LightDir);", + + //normalize our vectors + "vec3 N = normalize(NormalMap * 2.0 - 1.0);", + "vec3 L = normalize(LightDir);", + + //Pre-multiply light color with intensity + //Then perform "N dot L" to determine our diffuse term + "vec3 Diffuse = (LightColor.rgb * LightColor.a) * max(dot(N, L), 0.0);", + + //pre-multiply ambient color with intensity + "vec3 Ambient = AmbientColor.rgb * AmbientColor.a;", + + //calculate attenuation + "float Attenuation = 1.0 / ( Falloff.x + (Falloff.y*D) + (Falloff.z*D*D) );", + + //the calculation which brings it all together + "vec3 Intensity = Ambient + Diffuse * Attenuation;", + "vec3 FinalColor = DiffuseColor.rgb * Intensity;", + "gl_FragColor = vColor * vec4(FinalColor, DiffuseColor.a);", + //"gl_FragColor = vec4(1.0, 0.0, 0.0, Attenuation);",//vColor * vec4(FinalColor, DiffuseColor.a);", + /* + // normalise color + "vec3 normal = normalize(nColor * 2.0 - 1.0);", + + "vec3 deltaPos = vec3( (light.xy - gl_FragCoord.xy) / resolution.xy, light.z );", + + "float lambert = clamp(dot(normal, lightDir), 0.0, 1.0);", + + "float d = sqrt(dot(deltaPos, deltaPos));", + "float att = 1.0 / ( attenuation.x + (attenuation.y*d) + (attenuation.z*d*d) );", + + "vec3 result = (ambientColor * ambientIntensity) + (lightColor.rgb * lambert) * att;", + "result *= color.rgb;", + + "gl_FragColor = vec4(result, 1.0);",*/ + + + + "}" + ]; + +} + +PIXI.NormalMapFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.NormalMapFilter.prototype.constructor = PIXI.NormalMapFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + * @method onTextureLoaded + */ +PIXI.NormalMapFilter.prototype.onTextureLoaded = function() +{ + this.uniforms.mapDimensions.value.x = this.uniforms.displacementMap.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.displacementMap.value.height; + + this.uniforms.displacementMap.value.baseTexture.off("loaded", this.boundLoadedFunction) +}; + +/** + * The texture used for the displacement map. Must be power of 2 texture. + * + * @property map + * @type Texture + */ +Object.defineProperty(PIXI.NormalMapFilter.prototype, 'map', { + get: function() { + return this.uniforms.displacementMap.value; + }, + set: function(value) { + this.uniforms.displacementMap.value = value; + } +}); + +/** + * The multiplier used to scale the displacement result from the map calculation. + * + * @property scale + * @type Point + */ +Object.defineProperty(PIXI.NormalMapFilter.prototype, 'scale', { + get: function() { + return this.uniforms.scale.value; + }, + set: function(value) { + this.uniforms.scale.value = value; + } +}); + +/** + * The offset used to move the displacement map. + * + * @property offset + * @type Point + */ +Object.defineProperty(PIXI.NormalMapFilter.prototype, 'offset', { + get: function() { + return this.uniforms.offset.value; + }, + set: function(value) { + this.uniforms.offset.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/PixelateFilter.js b/app/Lib/Vendor/src/pixi/filters/PixelateFilter.js new file mode 100644 index 0000000..2a3127d --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/PixelateFilter.js @@ -0,0 +1,62 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This filter applies a pixelate effect making display objects appear 'blocky'. + * + * @class PixelateFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.PixelateFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + invert: {type: '1f', value: 0}, + dimensions: {type: '4fv', value:new PIXI.Float32Array([10000, 100, 10, 10])}, + pixelSize: {type: '2f', value:{x:10, y:10}} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform vec2 testDim;', + 'uniform vec4 dimensions;', + 'uniform vec2 pixelSize;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' vec2 coord = vTextureCoord;', + + ' vec2 size = dimensions.xy/pixelSize;', + + ' vec2 color = floor( ( vTextureCoord * size ) ) / size + pixelSize/dimensions.xy * 0.5;', + ' gl_FragColor = texture2D(uSampler, color);', + '}' + ]; +}; + +PIXI.PixelateFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.PixelateFilter.prototype.constructor = PIXI.PixelateFilter; + +/** + * This a point that describes the size of the blocks. x is the width of the block and y is the height. + * + * @property size + * @type Point + */ +Object.defineProperty(PIXI.PixelateFilter.prototype, 'size', { + get: function() { + return this.uniforms.pixelSize.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.pixelSize.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/RGBSplitFilter.js b/app/Lib/Vendor/src/pixi/filters/RGBSplitFilter.js new file mode 100644 index 0000000..7169637 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/RGBSplitFilter.js @@ -0,0 +1,91 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * An RGB Split Filter. + * + * @class RGBSplitFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.RGBSplitFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + red: {type: '2f', value: {x:20, y:20}}, + green: {type: '2f', value: {x:-20, y:20}}, + blue: {type: '2f', value: {x:20, y:-20}}, + dimensions: {type: '4fv', value:[0,0,0,0]} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform vec2 red;', + 'uniform vec2 green;', + 'uniform vec2 blue;', + 'uniform vec4 dimensions;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor.r = texture2D(uSampler, vTextureCoord + red/dimensions.xy).r;', + ' gl_FragColor.g = texture2D(uSampler, vTextureCoord + green/dimensions.xy).g;', + ' gl_FragColor.b = texture2D(uSampler, vTextureCoord + blue/dimensions.xy).b;', + ' gl_FragColor.a = texture2D(uSampler, vTextureCoord).a;', + '}' + ]; +}; + +PIXI.RGBSplitFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.RGBSplitFilter.prototype.constructor = PIXI.RGBSplitFilter; + +/** + * Red channel offset. + * + * @property red + * @type Point + */ +Object.defineProperty(PIXI.RGBSplitFilter.prototype, 'red', { + get: function() { + return this.uniforms.red.value; + }, + set: function(value) { + this.uniforms.red.value = value; + } +}); + +/** + * Green channel offset. + * + * @property green + * @type Point + */ +Object.defineProperty(PIXI.RGBSplitFilter.prototype, 'green', { + get: function() { + return this.uniforms.green.value; + }, + set: function(value) { + this.uniforms.green.value = value; + } +}); + +/** + * Blue offset. + * + * @property blue + * @type Point + */ +Object.defineProperty(PIXI.RGBSplitFilter.prototype, 'blue', { + get: function() { + return this.uniforms.blue.value; + }, + set: function(value) { + this.uniforms.blue.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/SepiaFilter.js b/app/Lib/Vendor/src/pixi/filters/SepiaFilter.js new file mode 100644 index 0000000..5596e53 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/SepiaFilter.js @@ -0,0 +1,55 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This applies a sepia effect to your Display Objects. + * + * @class SepiaFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.SepiaFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + sepia: {type: '1f', value: 1} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform float sepia;', + 'uniform sampler2D uSampler;', + + 'const mat3 sepiaMatrix = mat3(0.3588, 0.7044, 0.1368, 0.2990, 0.5870, 0.1140, 0.2392, 0.4696, 0.0912);', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord);', + ' gl_FragColor.rgb = mix( gl_FragColor.rgb, gl_FragColor.rgb * sepiaMatrix, sepia);', + // ' gl_FragColor = gl_FragColor * vColor;', + '}' + ]; +}; + +PIXI.SepiaFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.SepiaFilter.prototype.constructor = PIXI.SepiaFilter; + +/** + * The strength of the sepia. 1 will apply the full sepia effect, 0 will make the object its normal color. + * @property sepia + * @type Number +*/ +Object.defineProperty(PIXI.SepiaFilter.prototype, 'sepia', { + get: function() { + return this.uniforms.sepia.value; + }, + set: function(value) { + this.uniforms.sepia.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/SmartBlurFilter.js b/app/Lib/Vendor/src/pixi/filters/SmartBlurFilter.js new file mode 100644 index 0000000..44a47f4 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/SmartBlurFilter.js @@ -0,0 +1,75 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Smart Blur Filter. + * + * @class SmartBlurFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.SmartBlurFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + blur: {type: '1f', value: 1/512} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'uniform sampler2D uSampler;', + //'uniform vec2 delta;', + 'const vec2 delta = vec2(1.0/10.0, 0.0);', + //'uniform float darkness;', + + 'float random(vec3 scale, float seed) {', + ' return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);', + '}', + + + 'void main(void) {', + ' vec4 color = vec4(0.0);', + ' float total = 0.0;', + + ' float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);', + + ' for (float t = -30.0; t <= 30.0; t++) {', + ' float percent = (t + offset - 0.5) / 30.0;', + ' float weight = 1.0 - abs(percent);', + ' vec4 sample = texture2D(uSampler, vTextureCoord + delta * percent);', + ' sample.rgb *= sample.a;', + ' color += sample * weight;', + ' total += weight;', + ' }', + + ' gl_FragColor = color / total;', + ' gl_FragColor.rgb /= gl_FragColor.a + 0.00001;', + //' gl_FragColor.rgb *= darkness;', + '}' + ]; +}; + +PIXI.SmartBlurFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.SmartBlurFilter.prototype.constructor = PIXI.SmartBlurFilter; + +/** + * The strength of the blur. + * + * @property blur + * @type Number + * @default 2 + */ +Object.defineProperty(PIXI.SmartBlurFilter.prototype, 'blur', { + get: function() { + return this.uniforms.blur.value; + }, + set: function(value) { + this.uniforms.blur.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/TiltShiftFilter.js b/app/Lib/Vendor/src/pixi/filters/TiltShiftFilter.js new file mode 100644 index 0000000..2eb3776 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/TiltShiftFilter.js @@ -0,0 +1,82 @@ +/** + * @author Vico @vicocotea + * original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js by Evan Wallace : http://madebyevan.com/ + */ + +/** + * A TiltShift Filter. Manages the pass of both a TiltShiftXFilter and TiltShiftYFilter. + * + * @class TiltShiftFilter + * @constructor + */ +PIXI.TiltShiftFilter = function() +{ + this.tiltShiftXFilter = new PIXI.TiltShiftXFilter(); + this.tiltShiftYFilter = new PIXI.TiltShiftYFilter(); + this.tiltShiftXFilter.updateDelta(); + this.tiltShiftXFilter.updateDelta(); + + this.passes = [this.tiltShiftXFilter, this.tiltShiftYFilter]; +}; + +PIXI.TiltShiftFilter.prototype.constructor = PIXI.TiltShiftFilter; + +/** + * The strength of the blur. + * + * @property blur + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftFilter.prototype, 'blur', { + get: function() { + return this.tiltShiftXFilter.blur; + }, + set: function(value) { + this.tiltShiftXFilter.blur = this.tiltShiftYFilter.blur = value; + } +}); + +/** + * The strength of the gradient blur. + * + * @property gradientBlur + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftFilter.prototype, 'gradientBlur', { + get: function() { + return this.tiltShiftXFilter.gradientBlur; + }, + set: function(value) { + this.tiltShiftXFilter.gradientBlur = this.tiltShiftYFilter.gradientBlur = value; + } +}); + +/** + * The Y value to start the effect at. + * + * @property start + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftFilter.prototype, 'start', { + get: function() { + return this.tiltShiftXFilter.start; + }, + set: function(value) { + this.tiltShiftXFilter.start = this.tiltShiftYFilter.start = value; + } +}); + +/** + * The Y value to end the effect at. + * + * @property end + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftFilter.prototype, 'end', { + get: function() { + return this.tiltShiftXFilter.end; + }, + set: function(value) { + this.tiltShiftXFilter.end = this.tiltShiftYFilter.end = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/filters/TiltShiftXFilter.js b/app/Lib/Vendor/src/pixi/filters/TiltShiftXFilter.js new file mode 100644 index 0000000..29d8f38 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/TiltShiftXFilter.js @@ -0,0 +1,149 @@ +/** + * @author Vico @vicocotea + * original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js by Evan Wallace : http://madebyevan.com/ + */ + +/** + * A TiltShiftXFilter. + * + * @class TiltShiftXFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.TiltShiftXFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + blur: {type: '1f', value: 100.0}, + gradientBlur: {type: '1f', value: 600.0}, + start: {type: '2f', value:{x:0, y:window.screenHeight / 2}}, + end: {type: '2f', value:{x:600, y:window.screenHeight / 2}}, + delta: {type: '2f', value:{x:30, y:30}}, + texSize: {type: '2f', value:{x:window.screenWidth, y:window.screenHeight}} + }; + + this.updateDelta(); + + this.fragmentSrc = [ + 'precision mediump float;', + 'uniform sampler2D uSampler;', + 'uniform float blur;', + 'uniform float gradientBlur;', + 'uniform vec2 start;', + 'uniform vec2 end;', + 'uniform vec2 delta;', + 'uniform vec2 texSize;', + 'varying vec2 vTextureCoord;', + + 'float random(vec3 scale, float seed) {', + ' return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);', + '}', + + 'void main(void) {', + ' vec4 color = vec4(0.0);', + ' float total = 0.0;', + + ' float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);', + ' vec2 normal = normalize(vec2(start.y - end.y, end.x - start.x));', + ' float radius = smoothstep(0.0, 1.0, abs(dot(vTextureCoord * texSize - start, normal)) / gradientBlur) * blur;', + + ' for (float t = -30.0; t <= 30.0; t++) {', + ' float percent = (t + offset - 0.5) / 30.0;', + ' float weight = 1.0 - abs(percent);', + ' vec4 sample = texture2D(uSampler, vTextureCoord + delta / texSize * percent * radius);', + ' sample.rgb *= sample.a;', + ' color += sample * weight;', + ' total += weight;', + ' }', + + ' gl_FragColor = color / total;', + ' gl_FragColor.rgb /= gl_FragColor.a + 0.00001;', + '}' + ]; +}; + +PIXI.TiltShiftXFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.TiltShiftXFilter.prototype.constructor = PIXI.TiltShiftXFilter; + +/** + * The strength of the blur. + * + * @property blur + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftXFilter.prototype, 'blur', { + get: function() { + return this.uniforms.blur.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.blur.value = value; + } +}); + +/** + * The strength of the gradient blur. + * + * @property gradientBlur + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftXFilter.prototype, 'gradientBlur', { + get: function() { + return this.uniforms.gradientBlur.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.gradientBlur.value = value; + } +}); + +/** + * The X value to start the effect at. + * + * @property start + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftXFilter.prototype, 'start', { + get: function() { + return this.uniforms.start.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.start.value = value; + this.updateDelta(); + } +}); + +/** + * The X value to end the effect at. + * + * @property end + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftXFilter.prototype, 'end', { + get: function() { + return this.uniforms.end.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.end.value = value; + this.updateDelta(); + } +}); + +/** + * Updates the filter delta values. + * + * @method updateDelta + */ +PIXI.TiltShiftXFilter.prototype.updateDelta = function(){ + var dx = this.uniforms.end.value.x - this.uniforms.start.value.x; + var dy = this.uniforms.end.value.y - this.uniforms.start.value.y; + var d = Math.sqrt(dx * dx + dy * dy); + this.uniforms.delta.value.x = dx / d; + this.uniforms.delta.value.y = dy / d; +}; diff --git a/app/Lib/Vendor/src/pixi/filters/TiltShiftYFilter.js b/app/Lib/Vendor/src/pixi/filters/TiltShiftYFilter.js new file mode 100644 index 0000000..3a92851 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/TiltShiftYFilter.js @@ -0,0 +1,149 @@ +/** + * @author Vico @vicocotea + * original filter https://github.com/evanw/glfx.js/blob/master/src/filters/blur/tiltshift.js by Evan Wallace : http://madebyevan.com/ + */ + +/** + * A TiltShiftYFilter. + * + * @class TiltShiftYFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.TiltShiftYFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + blur: {type: '1f', value: 100.0}, + gradientBlur: {type: '1f', value: 600.0}, + start: {type: '2f', value:{x:0, y:window.screenHeight / 2}}, + end: {type: '2f', value:{x:600, y:window.screenHeight / 2}}, + delta: {type: '2f', value:{x:30, y:30}}, + texSize: {type: '2f', value:{x:window.screenWidth, y:window.screenHeight}} + }; + + this.updateDelta(); + + this.fragmentSrc = [ + 'precision mediump float;', + 'uniform sampler2D uSampler;', + 'uniform float blur;', + 'uniform float gradientBlur;', + 'uniform vec2 start;', + 'uniform vec2 end;', + 'uniform vec2 delta;', + 'uniform vec2 texSize;', + 'varying vec2 vTextureCoord;', + + 'float random(vec3 scale, float seed) {', + ' return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);', + '}', + + 'void main(void) {', + ' vec4 color = vec4(0.0);', + ' float total = 0.0;', + + ' float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0);', + ' vec2 normal = normalize(vec2(start.y - end.y, end.x - start.x));', + ' float radius = smoothstep(0.0, 1.0, abs(dot(vTextureCoord * texSize - start, normal)) / gradientBlur) * blur;', + + ' for (float t = -30.0; t <= 30.0; t++) {', + ' float percent = (t + offset - 0.5) / 30.0;', + ' float weight = 1.0 - abs(percent);', + ' vec4 sample = texture2D(uSampler, vTextureCoord + delta / texSize * percent * radius);', + ' sample.rgb *= sample.a;', + ' color += sample * weight;', + ' total += weight;', + ' }', + + ' gl_FragColor = color / total;', + ' gl_FragColor.rgb /= gl_FragColor.a + 0.00001;', + '}' + ]; +}; + +PIXI.TiltShiftYFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.TiltShiftYFilter.prototype.constructor = PIXI.TiltShiftYFilter; + +/** + * The strength of the blur. + * + * @property blur + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftYFilter.prototype, 'blur', { + get: function() { + return this.uniforms.blur.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.blur.value = value; + } +}); + +/** + * The strength of the gradient blur. + * + * @property gradientBlur + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftYFilter.prototype, 'gradientBlur', { + get: function() { + return this.uniforms.gradientBlur.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.gradientBlur.value = value; + } +}); + +/** + * The Y value to start the effect at. + * + * @property start + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftYFilter.prototype, 'start', { + get: function() { + return this.uniforms.start.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.start.value = value; + this.updateDelta(); + } +}); + +/** + * The Y value to end the effect at. + * + * @property end + * @type Number + */ +Object.defineProperty(PIXI.TiltShiftYFilter.prototype, 'end', { + get: function() { + return this.uniforms.end.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.end.value = value; + this.updateDelta(); + } +}); + +/** + * Updates the filter delta values. + * + * @method updateDelta + */ +PIXI.TiltShiftYFilter.prototype.updateDelta = function(){ + var dx = this.uniforms.end.value.x - this.uniforms.start.value.x; + var dy = this.uniforms.end.value.y - this.uniforms.start.value.y; + var d = Math.sqrt(dx * dx + dy * dy); + this.uniforms.delta.value.x = -dy / d; + this.uniforms.delta.value.y = dx / d; +}; diff --git a/app/Lib/Vendor/src/pixi/filters/TwistFilter.js b/app/Lib/Vendor/src/pixi/filters/TwistFilter.js new file mode 100644 index 0000000..08a3122 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/filters/TwistFilter.js @@ -0,0 +1,102 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This filter applies a twist effect making display objects appear twisted in the given direction. + * + * @class TwistFilter + * @extends AbstractFilter + * @constructor + */ +PIXI.TwistFilter = function() +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + + // set the uniforms + this.uniforms = { + radius: {type: '1f', value:0.5}, + angle: {type: '1f', value:5}, + offset: {type: '2f', value:{x:0.5, y:0.5}} + }; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform vec4 dimensions;', + 'uniform sampler2D uSampler;', + + 'uniform float radius;', + 'uniform float angle;', + 'uniform vec2 offset;', + + 'void main(void) {', + ' vec2 coord = vTextureCoord - offset;', + ' float distance = length(coord);', + + ' if (distance < radius) {', + ' float ratio = (radius - distance) / radius;', + ' float angleMod = ratio * ratio * angle;', + ' float s = sin(angleMod);', + ' float c = cos(angleMod);', + ' coord = vec2(coord.x * c - coord.y * s, coord.x * s + coord.y * c);', + ' }', + + ' gl_FragColor = texture2D(uSampler, coord+offset);', + '}' + ]; +}; + +PIXI.TwistFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.TwistFilter.prototype.constructor = PIXI.TwistFilter; + +/** + * This point describes the the offset of the twist. + * + * @property offset + * @type Point + */ +Object.defineProperty(PIXI.TwistFilter.prototype, 'offset', { + get: function() { + return this.uniforms.offset.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.offset.value = value; + } +}); + +/** + * This radius of the twist. + * + * @property radius + * @type Number + */ +Object.defineProperty(PIXI.TwistFilter.prototype, 'radius', { + get: function() { + return this.uniforms.radius.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.radius.value = value; + } +}); + +/** + * This angle of the twist. + * + * @property angle + * @type Number + */ +Object.defineProperty(PIXI.TwistFilter.prototype, 'angle', { + get: function() { + return this.uniforms.angle.value; + }, + set: function(value) { + this.dirty = true; + this.uniforms.angle.value = value; + } +}); diff --git a/app/Lib/Vendor/src/pixi/geom/Circle.js b/app/Lib/Vendor/src/pixi/geom/Circle.js new file mode 100644 index 0000000..541e0fe --- /dev/null +++ b/app/Lib/Vendor/src/pixi/geom/Circle.js @@ -0,0 +1,84 @@ +/** + * @author Chad Engler + */ + +/** + * The Circle object can be used to specify a hit area for displayObjects + * + * @class Circle + * @constructor + * @param x {Number} The X coordinate of the center of this circle + * @param y {Number} The Y coordinate of the center of this circle + * @param radius {Number} The radius of the circle + */ +PIXI.Circle = function(x, y, radius) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +}; + +/** + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the Circle + */ +PIXI.Circle.prototype.clone = function() +{ + return new PIXI.Circle(this.x, this.y, this.radius); +}; + +/** + * Checks whether the x and y coordinates given are contained within this circle + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this Circle + */ +PIXI.Circle.prototype.contains = function(x, y) +{ + if(this.radius <= 0) + return false; + + var dx = (this.x - x), + dy = (this.y - y), + r2 = this.radius * this.radius; + + dx *= dx; + dy *= dy; + + return (dx + dy <= r2); +}; + +/** +* Returns the framing rectangle of the circle as a PIXI.Rectangle object +* +* @method getBounds +* @return {Rectangle} the framing rectangle +*/ +PIXI.Circle.prototype.getBounds = function() +{ + return new PIXI.Rectangle(this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2); +}; + +// constructor +PIXI.Circle.prototype.constructor = PIXI.Circle; diff --git a/app/Lib/Vendor/src/pixi/geom/Ellipse.js b/app/Lib/Vendor/src/pixi/geom/Ellipse.js new file mode 100644 index 0000000..0dd71f3 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/geom/Ellipse.js @@ -0,0 +1,92 @@ +/** + * @author Chad Engler + */ + +/** + * The Ellipse object can be used to specify a hit area for displayObjects + * + * @class Ellipse + * @constructor + * @param x {Number} The X coordinate of the center of the ellipse + * @param y {Number} The Y coordinate of the center of the ellipse + * @param width {Number} The half width of this ellipse + * @param height {Number} The half height of this ellipse + */ +PIXI.Ellipse = function(x, y, width, height) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; +}; + +/** + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse + */ +PIXI.Ellipse.prototype.clone = function() +{ + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); +}; + +/** + * Checks whether the x and y coordinates given are contained within this ellipse + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coords are within this ellipse + */ +PIXI.Ellipse.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + //normalize the coords to an ellipse with center 0,0 + var normx = ((x - this.x) / this.width), + normy = ((y - this.y) / this.height); + + normx *= normx; + normy *= normy; + + return (normx + normy <= 1); +}; + +/** +* Returns the framing rectangle of the ellipse as a PIXI.Rectangle object +* +* @method getBounds +* @return {Rectangle} the framing rectangle +*/ +PIXI.Ellipse.prototype.getBounds = function() +{ + return new PIXI.Rectangle(this.x - this.width, this.y - this.height, this.width, this.height); +}; + +// constructor +PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; diff --git a/app/Lib/Vendor/src/pixi/geom/Matrix.js b/app/Lib/Vendor/src/pixi/geom/Matrix.js new file mode 100644 index 0000000..b0f043f --- /dev/null +++ b/app/Lib/Vendor/src/pixi/geom/Matrix.js @@ -0,0 +1,268 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The Matrix class is now an object, which makes it a lot faster, + * here is a representation of it : + * | a | b | tx| + * | c | d | ty| + * | 0 | 0 | 1 | + * + * @class Matrix + * @constructor + */ +PIXI.Matrix = function() +{ + /** + * @property a + * @type Number + * @default 1 + */ + this.a = 1; + + /** + * @property b + * @type Number + * @default 0 + */ + this.b = 0; + + /** + * @property c + * @type Number + * @default 0 + */ + this.c = 0; + + /** + * @property d + * @type Number + * @default 1 + */ + this.d = 1; + + /** + * @property tx + * @type Number + * @default 0 + */ + this.tx = 0; + + /** + * @property ty + * @type Number + * @default 0 + */ + this.ty = 0; +}; + +/** + * Creates a Matrix object based on the given array. The Element to Matrix mapping order is as follows: + * + * a = array[0] + * b = array[1] + * c = array[3] + * d = array[4] + * tx = array[2] + * ty = array[5] + * + * @method fromArray + * @param array {Array} The array that the matrix will be populated from. + */ +PIXI.Matrix.prototype.fromArray = function(array) +{ + this.a = array[0]; + this.b = array[1]; + this.c = array[3]; + this.d = array[4]; + this.tx = array[2]; + this.ty = array[5]; +}; + +/** + * Creates an array from the current Matrix object. + * + * @method toArray + * @param transpose {Boolean} Whether we need to transpose the matrix or not + * @return {Array} the newly created array which contains the matrix + */ +PIXI.Matrix.prototype.toArray = function(transpose) +{ + if(!this.array) this.array = new PIXI.Float32Array(9); + var array = this.array; + + if(transpose) + { + array[0] = this.a; + array[1] = this.b; + array[2] = 0; + array[3] = this.c; + array[4] = this.d; + array[5] = 0; + array[6] = this.tx; + array[7] = this.ty; + array[8] = 1; + } + else + { + array[0] = this.a; + array[1] = this.c; + array[2] = this.tx; + array[3] = this.b; + array[4] = this.d; + array[5] = this.ty; + array[6] = 0; + array[7] = 0; + array[8] = 1; + } + + return array; +}; + +/** + * Get a new position with the current transformation applied. + * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering) + * + * @method apply + * @param pos {Point} The origin + * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) + * @return {Point} The new point, transformed through this matrix + */ +PIXI.Matrix.prototype.apply = function(pos, newPos) +{ + newPos = newPos || new PIXI.Point(); + + newPos.x = this.a * pos.x + this.c * pos.y + this.tx; + newPos.y = this.b * pos.x + this.d * pos.y + this.ty; + + return newPos; +}; + +/** + * Get a new position with the inverse of the current transformation applied. + * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input) + * + * @method applyInverse + * @param pos {Point} The origin + * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) + * @return {Point} The new point, inverse-transformed through this matrix + */ +PIXI.Matrix.prototype.applyInverse = function(pos, newPos) +{ + newPos = newPos || new PIXI.Point(); + + var id = 1 / (this.a * this.d + this.c * -this.b); + + newPos.x = this.d * id * pos.x + -this.c * id * pos.y + (this.ty * this.c - this.tx * this.d) * id; + newPos.y = this.a * id * pos.y + -this.b * id * pos.x + (-this.ty * this.a + this.tx * this.b) * id; + + return newPos; +}; + +/** + * Translates the matrix on the x and y. + * + * @method translate + * @param {Number} x + * @param {Number} y + * @return {Matrix} This matrix. Good for chaining method calls. + **/ +PIXI.Matrix.prototype.translate = function(x, y) +{ + this.tx += x; + this.ty += y; + + return this; +}; + +/** + * Applies a scale transformation to the matrix. + * + * @method scale + * @param {Number} x The amount to scale horizontally + * @param {Number} y The amount to scale vertically + * @return {Matrix} This matrix. Good for chaining method calls. + **/ +PIXI.Matrix.prototype.scale = function(x, y) +{ + this.a *= x; + this.d *= y; + this.c *= x; + this.b *= y; + this.tx *= x; + this.ty *= y; + + return this; +}; + + +/** + * Applies a rotation transformation to the matrix. + * @method rotate + * @param {Number} angle The angle in radians. + * @return {Matrix} This matrix. Good for chaining method calls. + **/ +PIXI.Matrix.prototype.rotate = function(angle) +{ + var cos = Math.cos( angle ); + var sin = Math.sin( angle ); + + var a1 = this.a; + var c1 = this.c; + var tx1 = this.tx; + + this.a = a1 * cos-this.b * sin; + this.b = a1 * sin+this.b * cos; + this.c = c1 * cos-this.d * sin; + this.d = c1 * sin+this.d * cos; + this.tx = tx1 * cos - this.ty * sin; + this.ty = tx1 * sin + this.ty * cos; + + return this; +}; + +/** + * Appends the given Matrix to this Matrix. + * + * @method append + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +PIXI.Matrix.prototype.append = function(matrix) +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + + this.a = matrix.a * a1 + matrix.b * c1; + this.b = matrix.a * b1 + matrix.b * d1; + this.c = matrix.c * a1 + matrix.d * c1; + this.d = matrix.c * b1 + matrix.d * d1; + + this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx; + this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty; + + return this; +}; + +/** + * Resets this Matix to an identity (default) matrix. + * + * @method identity + * @return {Matrix} This matrix. Good for chaining method calls. + */ +PIXI.Matrix.prototype.identity = function() +{ + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.tx = 0; + this.ty = 0; + + return this; +}; + +PIXI.identityMatrix = new PIXI.Matrix(); diff --git a/app/Lib/Vendor/src/pixi/geom/Point.js b/app/Lib/Vendor/src/pixi/geom/Point.js new file mode 100644 index 0000000..2adcb13 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/geom/Point.js @@ -0,0 +1,56 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. + * + * @class Point + * @constructor + * @param x {Number} position of the point on the x axis + * @param y {Number} position of the point on the y axis + */ +PIXI.Point = function(x, y) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; +}; + +/** + * Creates a clone of this point + * + * @method clone + * @return {Point} a copy of the point + */ +PIXI.Point.prototype.clone = function() +{ + return new PIXI.Point(this.x, this.y); +}; + +/** + * Sets the point to a new x and y position. + * If y is omitted, both x and y will be set to x. + * + * @method set + * @param [x=0] {Number} position of the point on the x axis + * @param [y=0] {Number} position of the point on the y axis + */ +PIXI.Point.prototype.set = function(x, y) +{ + this.x = x || 0; + this.y = y || ( (y !== 0) ? this.x : 0 ) ; +}; + +// constructor +PIXI.Point.prototype.constructor = PIXI.Point; \ No newline at end of file diff --git a/app/Lib/Vendor/src/pixi/geom/Polygon.js b/app/Lib/Vendor/src/pixi/geom/Polygon.js new file mode 100644 index 0000000..d615a89 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/geom/Polygon.js @@ -0,0 +1,76 @@ +/** + * @author Adrien Brault + */ + +/** + * @class Polygon + * @constructor + * @param points* {Array(Point)|Array(Number)|Point...|Number...} This can be an array of Points that form the polygon, + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be + * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the + * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are + * Numbers. + */ +PIXI.Polygon = function(points) +{ + //if points isn't an array, use arguments as the array + if(!(points instanceof Array))points = Array.prototype.slice.call(arguments); + + //if this is a flat array of numbers, convert it to points + if(points[0] instanceof PIXI.Point) + { + var p = []; + for(var i = 0, il = points.length; i < il; i++) + { + p.push(points[i].x, points[i].y); + } + + points = p; + } + + this.closed = true; + this.points = points; +}; + +/** + * Creates a clone of this polygon + * + * @method clone + * @return {Polygon} a copy of the polygon + */ +PIXI.Polygon.prototype.clone = function() +{ + var points = this.points.slice(); + return new PIXI.Polygon(points); +}; + +/** + * Checks whether the x and y coordinates passed to this function are contained within this polygon + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this polygon + */ +PIXI.Polygon.prototype.contains = function(x, y) +{ + var inside = false; + + // use some raycasting to test hits + // https://github.com/substack/point-in-polygon/blob/master/index.js + var length = this.points.length / 2; + + for(var i = 0, j = length - 1; i < length; j = i++) + { + var xi = this.points[i * 2], yi = this.points[i * 2 + 1], + xj = this.points[j * 2], yj = this.points[j * 2 + 1], + intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + + if(intersect) inside = !inside; + } + + return inside; +}; + +// constructor +PIXI.Polygon.prototype.constructor = PIXI.Polygon; diff --git a/app/Lib/Vendor/src/pixi/geom/Rectangle.js b/app/Lib/Vendor/src/pixi/geom/Rectangle.js new file mode 100644 index 0000000..2ccd1d1 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/geom/Rectangle.js @@ -0,0 +1,87 @@ +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height. + * + * @class Rectangle + * @constructor + * @param x {Number} The X coordinate of the upper-left corner of the rectangle + * @param y {Number} The Y coordinate of the upper-left corner of the rectangle + * @param width {Number} The overall width of this rectangle + * @param height {Number} The overall height of this rectangle + */ +PIXI.Rectangle = function(x, y, width, height) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; +}; + +/** + * Creates a clone of this Rectangle + * + * @method clone + * @return {Rectangle} a copy of the rectangle + */ +PIXI.Rectangle.prototype.clone = function() +{ + return new PIXI.Rectangle(this.x, this.y, this.width, this.height); +}; + +/** + * Checks whether the x and y coordinates given are contained within this Rectangle + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this Rectangle + */ +PIXI.Rectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + var x1 = this.x; + if(x >= x1 && x <= x1 + this.width) + { + var y1 = this.y; + + if(y >= y1 && y <= y1 + this.height) + { + return true; + } + } + + return false; +}; + +// constructor +PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; + +PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); \ No newline at end of file diff --git a/app/Lib/Vendor/src/pixi/geom/RoundedRectangle.js b/app/Lib/Vendor/src/pixi/geom/RoundedRectangle.js new file mode 100644 index 0000000..dae668d --- /dev/null +++ b/app/Lib/Vendor/src/pixi/geom/RoundedRectangle.js @@ -0,0 +1,94 @@ +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * The Rounded Rectangle object is an area defined by its position and has nice rounded corners, as indicated by its top-left corner point (x, y) and by its width and its height. + * + * @class RoundedRectangle + * @constructor + * @param x {Number} The X coordinate of the upper-left corner of the rounded rectangle + * @param y {Number} The Y coordinate of the upper-left corner of the rounded rectangle + * @param width {Number} The overall width of this rounded rectangle + * @param height {Number} The overall height of this rounded rectangle + * @param radius {Number} The overall radius of this corners of this rounded rectangle + */ +PIXI.RoundedRectangle = function(x, y, width, height, radius) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; + + /** + * @property radius + * @type Number + * @default 20 + */ + this.radius = radius || 20; +}; + +/** + * Creates a clone of this Rounded Rectangle + * + * @method clone + * @return {RoundedRectangle} a copy of the rounded rectangle + */ +PIXI.RoundedRectangle.prototype.clone = function() +{ + return new PIXI.RoundedRectangle(this.x, this.y, this.width, this.height, this.radius); +}; + +/** + * Checks whether the x and y coordinates given are contained within this Rounded Rectangle + * + * @method contains + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this Rounded Rectangle + */ +PIXI.RoundedRectangle.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + var x1 = this.x; + if(x >= x1 && x <= x1 + this.width) + { + var y1 = this.y; + + if(y >= y1 && y <= y1 + this.height) + { + return true; + } + } + + return false; +}; + +// constructor +PIXI.RoundedRectangle.prototype.constructor = PIXI.RoundedRectangle; + diff --git a/app/Lib/Vendor/src/pixi/loaders/AssetLoader.js b/app/Lib/Vendor/src/pixi/loaders/AssetLoader.js new file mode 100644 index 0000000..25adce2 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/loaders/AssetLoader.js @@ -0,0 +1,160 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Class that loads a bunch of images / sprite sheet / bitmap font files. Once the + * assets have been loaded they are added to the PIXI Texture cache and can be accessed + * easily through PIXI.Texture.fromImage() and PIXI.Sprite.fromImage() + * When all items have been loaded this class will dispatch a 'onLoaded' event + * As each individual item is loaded this class will dispatch a 'onProgress' event + * + * @class AssetLoader + * @constructor + * @uses EventTarget + * @param assetURLs {Array(String)} An array of image/sprite sheet urls that you would like loaded + * supported. Supported image formats include 'jpeg', 'jpg', 'png', 'gif'. Supported + * sprite sheet data formats only include 'JSON' at this time. Supported bitmap font + * data formats include 'xml' and 'fnt'. + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.AssetLoader = function(assetURLs, crossorigin) +{ + /** + * The array of asset URLs that are going to be loaded + * + * @property assetURLs + * @type Array(String) + */ + this.assetURLs = assetURLs; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * Maps file extension to loader types + * + * @property loadersByType + * @type Object + */ + this.loadersByType = { + 'jpg': PIXI.ImageLoader, + 'jpeg': PIXI.ImageLoader, + 'png': PIXI.ImageLoader, + 'gif': PIXI.ImageLoader, + 'webp': PIXI.ImageLoader, + 'json': PIXI.JsonLoader, + 'atlas': PIXI.AtlasLoader, + 'anim': PIXI.SpineLoader, + 'xml': PIXI.BitmapFontLoader, + 'fnt': PIXI.BitmapFontLoader + }; +}; + +PIXI.EventTarget.mixin(PIXI.AssetLoader.prototype); + +/** + * Fired when an item has loaded + * @event onProgress + */ + +/** + * Fired when all the assets have loaded + * @event onComplete + */ + +// constructor +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; + +/** + * Given a filename, returns its extension. + * + * @method _getDataType + * @param str {String} the name of the asset + */ +PIXI.AssetLoader.prototype._getDataType = function(str) +{ + var test = 'data:'; + //starts with 'data:' + var start = str.slice(0, test.length).toLowerCase(); + if (start === test) { + var data = str.slice(test.length); + + var sepIdx = data.indexOf(','); + if (sepIdx === -1) //malformed data URI scheme + return null; + + //e.g. 'image/gif;base64' => 'image/gif' + var info = data.slice(0, sepIdx).split(';')[0]; + + //We might need to handle some special cases here... + //standardize text/plain to 'txt' file extension + if (!info || info.toLowerCase() === 'text/plain') + return 'txt'; + + //User specified mime type, try splitting it by '/' + return info.split('/').pop().toLowerCase(); + } + + return null; +}; + +/** + * Starts loading the assets sequentially + * + * @method load + */ +PIXI.AssetLoader.prototype.load = function() +{ + var scope = this; + + function onLoad(evt) { + scope.onAssetLoaded(evt.data.content); + } + + this.loadCount = this.assetURLs.length; + + for (var i=0; i < this.assetURLs.length; i++) + { + var fileName = this.assetURLs[i]; + //first see if we have a data URI scheme.. + var fileType = this._getDataType(fileName); + + //if not, assume it's a file URI + if (!fileType) + fileType = fileName.split('?').shift().split('.').pop().toLowerCase(); + + var Constructor = this.loadersByType[fileType]; + if(!Constructor) + throw new Error(fileType + ' is an unsupported file type'); + + var loader = new Constructor(fileName, this.crossorigin); + + loader.on('loaded', onLoad); + loader.load(); + } +}; + +/** + * Invoked after each file is loaded + * + * @method onAssetLoaded + * @private + */ +PIXI.AssetLoader.prototype.onAssetLoaded = function(loader) +{ + this.loadCount--; + this.emit('onProgress', { content: this, loader: loader }); + if (this.onProgress) this.onProgress(loader); + + if (!this.loadCount) + { + this.emit('onComplete', { content: this }); + if(this.onComplete) this.onComplete(); + } +}; diff --git a/app/Lib/Vendor/src/pixi/loaders/AtlasLoader.js b/app/Lib/Vendor/src/pixi/loaders/AtlasLoader.js new file mode 100644 index 0000000..5368b6b --- /dev/null +++ b/app/Lib/Vendor/src/pixi/loaders/AtlasLoader.js @@ -0,0 +1,190 @@ +/** + * @author Martin Kelm http://mkelm.github.com + */ + +/** + * The atlas file loader is used to load in Texture Atlas data and parse it. When loaded this class will dispatch a 'loaded' event. If loading fails this class will dispatch an 'error' event. + * + * To generate the data you can use http://www.codeandweb.com/texturepacker and publish in the 'JSON' format. + * + * It is highly recommended to use texture atlases (also know as 'sprite sheets') as it allowed sprites to be batched and drawn together for highly increased rendering speed. + * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() + * + * @class AtlasLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.AtlasLoader = function (url, crossorigin) { + this.url = url; + this.baseUrl = url.replace(/[^\/]*$/, ''); + this.crossorigin = crossorigin; + this.loaded = false; + +}; + +// constructor +PIXI.AtlasLoader.constructor = PIXI.AtlasLoader; + +PIXI.EventTarget.mixin(PIXI.AtlasLoader.prototype); + + /** + * Starts loading the JSON file + * + * @method load + */ +PIXI.AtlasLoader.prototype.load = function () { + this.ajaxRequest = new PIXI.AjaxRequest(); + this.ajaxRequest.onreadystatechange = this.onAtlasLoaded.bind(this); + + this.ajaxRequest.open('GET', this.url, true); + if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/json'); + this.ajaxRequest.send(null); +}; + +/** + * Invoked when the Atlas has fully loaded. Parses the JSON and builds the texture frames. + * + * @method onAtlasLoaded + * @private + */ +PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { + if (this.ajaxRequest.readyState === 4) { + if (this.ajaxRequest.status === 200 || window.location.href.indexOf('http') === -1) { + this.atlas = { + meta : { + image : [] + }, + frames : [] + }; + var result = this.ajaxRequest.responseText.split(/\r?\n/); + var lineCount = -3; + + var currentImageId = 0; + var currentFrame = null; + var nameInNextLine = false; + + var i = 0, + j = 0, + selfOnLoaded = this.onLoaded.bind(this); + + // parser without rotation support yet! + for (i = 0; i < result.length; i++) { + result[i] = result[i].replace(/^\s+|\s+$/g, ''); + if (result[i] === '') { + nameInNextLine = i+1; + } + if (result[i].length > 0) { + if (nameInNextLine === i) { + this.atlas.meta.image.push(result[i]); + currentImageId = this.atlas.meta.image.length - 1; + this.atlas.frames.push({}); + lineCount = -3; + } else if (lineCount > 0) { + if (lineCount % 7 === 1) { // frame name + if (currentFrame != null) { //jshint ignore:line + this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; + } + currentFrame = { name: result[i], frame : {} }; + } else { + var text = result[i].split(' '); + if (lineCount % 7 === 3) { // position + currentFrame.frame.x = Number(text[1].replace(',', '')); + currentFrame.frame.y = Number(text[2]); + } else if (lineCount % 7 === 4) { // size + currentFrame.frame.w = Number(text[1].replace(',', '')); + currentFrame.frame.h = Number(text[2]); + } else if (lineCount % 7 === 5) { // real size + var realSize = { + x : 0, + y : 0, + w : Number(text[1].replace(',', '')), + h : Number(text[2]) + }; + + if (realSize.w > currentFrame.frame.w || realSize.h > currentFrame.frame.h) { + currentFrame.trimmed = true; + currentFrame.realSize = realSize; + } else { + currentFrame.trimmed = false; + } + } + } + } + lineCount++; + } + } + + if (currentFrame != null) { //jshint ignore:line + this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; + } + + if (this.atlas.meta.image.length > 0) { + this.images = []; + for (j = 0; j < this.atlas.meta.image.length; j++) { + // sprite sheet + var textureUrl = this.baseUrl + this.atlas.meta.image[j]; + var frameData = this.atlas.frames[j]; + this.images.push(new PIXI.ImageLoader(textureUrl, this.crossorigin)); + + for (i in frameData) { + var rect = frameData[i].frame; + if (rect) { + PIXI.TextureCache[i] = new PIXI.Texture(this.images[j].texture.baseTexture, { + x: rect.x, + y: rect.y, + width: rect.w, + height: rect.h + }); + if (frameData[i].trimmed) { + PIXI.TextureCache[i].realSize = frameData[i].realSize; + // trim in pixi not supported yet, todo update trim properties if it is done ... + PIXI.TextureCache[i].trim.x = 0; + PIXI.TextureCache[i].trim.y = 0; + } + } + } + } + + this.currentImageId = 0; + for (j = 0; j < this.images.length; j++) { + this.images[j].on('loaded', selfOnLoaded); + } + this.images[this.currentImageId].load(); + + } else { + this.onLoaded(); + } + + } else { + this.onError(); + } + } +}; + +/** + * Invoked when json file has loaded. + * + * @method onLoaded + * @private + */ +PIXI.AtlasLoader.prototype.onLoaded = function () { + if (this.images.length - 1 > this.currentImageId) { + this.currentImageId++; + this.images[this.currentImageId].load(); + } else { + this.loaded = true; + this.emit('loaded', { content: this }); + } +}; + +/** + * Invoked when an error occurs. + * + * @method onError + * @private + */ +PIXI.AtlasLoader.prototype.onError = function () { + this.emit('error', { content: this }); +}; diff --git a/app/Lib/Vendor/src/pixi/loaders/BitmapFontLoader.js b/app/Lib/Vendor/src/pixi/loaders/BitmapFontLoader.js new file mode 100644 index 0000000..3eb54c4 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/loaders/BitmapFontLoader.js @@ -0,0 +1,161 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The xml loader is used to load in XML bitmap font data ('xml' or 'fnt') + * To generate the data you can use http://www.angelcode.com/products/bmfont/ + * This loader will also load the image file as the data. + * When loaded this class will dispatch a 'loaded' event + * + * @class BitmapFontLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.BitmapFontLoader = function(url, crossorigin) +{ + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ''); + + /** + * [read-only] The texture of the bitmap font + * + * @property texture + * @type Texture + */ + this.texture = null; +}; + +// constructor +PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; +PIXI.EventTarget.mixin(PIXI.BitmapFontLoader.prototype); + +/** + * Loads the XML font data + * + * @method load + */ +PIXI.BitmapFontLoader.prototype.load = function() +{ + this.ajaxRequest = new PIXI.AjaxRequest(); + this.ajaxRequest.onreadystatechange = this.onXMLLoaded.bind(this); + + this.ajaxRequest.open('GET', this.url, true); + if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/xml'); + this.ajaxRequest.send(null); +}; + +/** + * Invoked when the XML file is loaded, parses the data. + * + * @method onXMLLoaded + * @private + */ +PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() +{ + if (this.ajaxRequest.readyState === 4) + { + if (this.ajaxRequest.status === 200 || window.location.protocol.indexOf('http') === -1) + { + var responseXML = this.ajaxRequest.responseXML; + if(!responseXML || /MSIE 9/i.test(navigator.userAgent) || navigator.isCocoonJS) { + if(typeof(window.DOMParser) === 'function') { + var domparser = new DOMParser(); + responseXML = domparser.parseFromString(this.ajaxRequest.responseText, 'text/xml'); + } else { + var div = document.createElement('div'); + div.innerHTML = this.ajaxRequest.responseText; + responseXML = div; + } + } + + var textureUrl = this.baseUrl + responseXML.getElementsByTagName('page')[0].getAttribute('file'); + var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); + this.texture = image.texture.baseTexture; + + var data = {}; + var info = responseXML.getElementsByTagName('info')[0]; + var common = responseXML.getElementsByTagName('common')[0]; + data.font = info.getAttribute('face'); + data.size = parseInt(info.getAttribute('size'), 10); + data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10); + data.chars = {}; + + //parse letters + var letters = responseXML.getElementsByTagName('char'); + + for (var i = 0; i < letters.length; i++) + { + var charCode = parseInt(letters[i].getAttribute('id'), 10); + + var textureRect = new PIXI.Rectangle( + parseInt(letters[i].getAttribute('x'), 10), + parseInt(letters[i].getAttribute('y'), 10), + parseInt(letters[i].getAttribute('width'), 10), + parseInt(letters[i].getAttribute('height'), 10) + ); + + data.chars[charCode] = { + xOffset: parseInt(letters[i].getAttribute('xoffset'), 10), + yOffset: parseInt(letters[i].getAttribute('yoffset'), 10), + xAdvance: parseInt(letters[i].getAttribute('xadvance'), 10), + kerning: {}, + texture: PIXI.TextureCache[charCode] = new PIXI.Texture(this.texture, textureRect) + + }; + } + + //parse kernings + var kernings = responseXML.getElementsByTagName('kerning'); + for (i = 0; i < kernings.length; i++) + { + var first = parseInt(kernings[i].getAttribute('first'), 10); + var second = parseInt(kernings[i].getAttribute('second'), 10); + var amount = parseInt(kernings[i].getAttribute('amount'), 10); + + data.chars[second].kerning[first] = amount; + + } + + PIXI.BitmapText.fonts[data.font] = data; + + image.addEventListener('loaded', this.onLoaded.bind(this)); + image.load(); + } + } +}; + +/** + * Invoked when all files are loaded (xml/fnt and texture) + * + * @method onLoaded + * @private + */ +PIXI.BitmapFontLoader.prototype.onLoaded = function() +{ + this.emit('loaded', { content: this }); +}; diff --git a/app/Lib/Vendor/src/pixi/loaders/ImageLoader.js b/app/Lib/Vendor/src/pixi/loaders/ImageLoader.js new file mode 100644 index 0000000..1567f29 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/loaders/ImageLoader.js @@ -0,0 +1,102 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The image loader class is responsible for loading images file formats ('jpeg', 'jpg', 'png' and 'gif') + * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrame() and PIXI.Sprite.fromFrame() + * When loaded this class will dispatch a 'loaded' event + * + * @class ImageLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the image + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.ImageLoader = function(url, crossorigin) +{ + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = PIXI.Texture.fromImage(url, crossorigin); + + /** + * if the image is loaded with loadFramedSpriteSheet + * frames will contain the sprite sheet frames + * + * @property frames + * @type Array + * @readOnly + */ + this.frames = []; +}; + +// constructor +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; + +PIXI.EventTarget.mixin(PIXI.ImageLoader.prototype); + +/** + * Loads image or takes it from cache + * + * @method load + */ +PIXI.ImageLoader.prototype.load = function() +{ + if(!this.texture.baseTexture.hasLoaded) + { + this.texture.baseTexture.on('loaded', this.onLoaded.bind(this)); + } + else + { + this.onLoaded(); + } +}; + +/** + * Invoked when image file is loaded or it is already cached and ready to use + * + * @method onLoaded + * @private + */ +PIXI.ImageLoader.prototype.onLoaded = function() +{ + this.emit('loaded', { content: this }); +}; + +/** + * Loads image and split it to uniform sized frames + * + * @method loadFramedSpriteSheet + * @param frameWidth {Number} width of each frame + * @param frameHeight {Number} height of each frame + * @param textureName {String} if given, the frames will be cached in - format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y 0) + { + textureLoader.addEventListener('loadedBaseTexture', function(evt){ + if (evt.content.content.loadingCount <= 0) + { + originalLoader.onLoaded(); + } + }); + } + else + { + originalLoader.onLoaded(); + } + }; + // start the loading // + atlasLoader.load(); + } + } + else + { + this.onLoaded(); + } +}; + +/** + * Invoke when json file loaded + * + * @method onLoaded + * @private + */ +PIXI.JsonLoader.prototype.onLoaded = function () { + this.loaded = true; + this.dispatchEvent({ + type: 'loaded', + content: this + }); +}; + +/** + * Invoke when error occured + * + * @method onError + * @private + */ +PIXI.JsonLoader.prototype.onError = function () { + + this.dispatchEvent({ + type: 'error', + content: this + }); +}; diff --git a/app/Lib/Vendor/src/pixi/loaders/SpineLoader.js b/app/Lib/Vendor/src/pixi/loaders/SpineLoader.js new file mode 100644 index 0000000..c736c50 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/loaders/SpineLoader.js @@ -0,0 +1,81 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi + * + * Awesome JS run time provided by EsotericSoftware + * https://github.com/EsotericSoftware/spine-runtimes + * + */ + +/** + * The Spine loader is used to load in JSON spine data + * To generate the data you need to use http://esotericsoftware.com/ and export in the "JSON" format + * Due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load + * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * You will need to generate a sprite sheet to accompany the spine data + * When loaded this class will dispatch a "loaded" event + * + * @class SpineLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.SpineLoader = function(url, crossorigin) +{ + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ + this.loaded = false; +}; + +PIXI.SpineLoader.prototype.constructor = PIXI.SpineLoader; + +PIXI.EventTarget.mixin(PIXI.SpineLoader.prototype); + +/** + * Loads the JSON data + * + * @method load + */ +PIXI.SpineLoader.prototype.load = function () { + + var scope = this; + var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); + jsonLoader.on('loaded', function (event) { + scope.json = event.data.content.json; + scope.onLoaded(); + }); + jsonLoader.load(); +}; + +/** + * Invoked when JSON file is loaded. + * + * @method onLoaded + * @private + */ +PIXI.SpineLoader.prototype.onLoaded = function () { + this.loaded = true; + this.emit('loaded', { content: this }); +}; diff --git a/app/Lib/Vendor/src/pixi/loaders/SpriteSheetLoader.js b/app/Lib/Vendor/src/pixi/loaders/SpriteSheetLoader.js new file mode 100644 index 0000000..4d50430 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/loaders/SpriteSheetLoader.js @@ -0,0 +1,94 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The sprite sheet loader is used to load in JSON sprite sheet data + * To generate the data you can use http://www.codeandweb.com/texturepacker and publish in the 'JSON' format + * There is a free version so thats nice, although the paid version is great value for money. + * It is highly recommended to use Sprite sheets (also know as a 'texture atlas') as it means sprites can be batched and drawn together for highly increased rendering speed. + * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() + * This loader will load the image file that the Spritesheet points to as well as the data. + * When loaded this class will dispatch a 'loaded' event + * + * @class SpriteSheetLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.SpriteSheetLoader = function (url, crossorigin) { + + /** + * The url of the atlas data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ''); + + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = null; + + /** + * The frames of the sprite sheet + * + * @property frames + * @type Object + */ + this.frames = {}; +}; + +// constructor +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; + +PIXI.EventTarget.mixin(PIXI.SpriteSheetLoader.prototype); + +/** + * This will begin loading the JSON file + * + * @method load + */ +PIXI.SpriteSheetLoader.prototype.load = function () { + var scope = this; + var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); + jsonLoader.on('loaded', function (event) { + scope.json = event.data.content.json; + scope.onLoaded(); + }); + jsonLoader.load(); +}; + +/** + * Invoke when all files are loaded (json and texture) + * + * @method onLoaded + * @private + */ +PIXI.SpriteSheetLoader.prototype.onLoaded = function () { + this.emit('loaded', { + content: this + }); +}; diff --git a/app/Lib/Vendor/src/pixi/primitives/Graphics.js b/app/Lib/Vendor/src/pixi/primitives/Graphics.js new file mode 100644 index 0000000..91a44ea --- /dev/null +++ b/app/Lib/Vendor/src/pixi/primitives/Graphics.js @@ -0,0 +1,1135 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The Graphics class contains methods used to draw primitive shapes such as lines, circles and rectangles to the display, and color and fill them. + * + * @class Graphics + * @extends DisplayObjectContainer + * @constructor + */ +PIXI.Graphics = function() +{ + PIXI.DisplayObjectContainer.call( this ); + + this.renderable = true; + + /** + * The alpha value used when filling the Graphics object. + * + * @property fillAlpha + * @type Number + */ + this.fillAlpha = 1; + + /** + * The width (thickness) of any lines drawn. + * + * @property lineWidth + * @type Number + */ + this.lineWidth = 0; + + /** + * The color of any lines drawn. + * + * @property lineColor + * @type String + * @default 0 + */ + this.lineColor = 0; + + /** + * Graphics data + * + * @property graphicsData + * @type Array + * @private + */ + this.graphicsData = []; + + /** + * The tint applied to the graphic shape. This is a hex value. Apply a value of 0xFFFFFF to reset the tint. + * + * @property tint + * @type Number + * @default 0xFFFFFF + */ + this.tint = 0xFFFFFF; + + /** + * The blend mode to be applied to the graphic shape. Apply a value of PIXI.blendModes.NORMAL to reset the blend mode. + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ + this.blendMode = PIXI.blendModes.NORMAL; + + /** + * Current path + * + * @property currentPath + * @type Object + * @private + */ + this.currentPath = null; + + /** + * Array containing some WebGL-related properties used by the WebGL renderer. + * + * @property _webGL + * @type Array + * @private + */ + this._webGL = []; + + /** + * Whether this shape is being used as a mask. + * + * @property isMask + * @type Boolean + */ + this.isMask = false; + + /** + * The bounds' padding used for bounds calculation. + * + * @property boundsPadding + * @type Number + */ + this.boundsPadding = 0; + + this._localBounds = new PIXI.Rectangle(0,0,1,1); + + /** + * Used to detect if the graphics object has changed. If this is set to true then the graphics object will be recalculated. + * + * @property dirty + * @type Boolean + * @private + */ + this.dirty = true; + + /** + * Used to detect if the webgl graphics object has changed. If this is set to true then the graphics object will be recalculated. + * + * @property webGLDirty + * @type Boolean + * @private + */ + this.webGLDirty = false; + + /** + * Used to detect if the cached sprite object needs to be updated. + * + * @property cachedSpriteDirty + * @type Boolean + * @private + */ + this.cachedSpriteDirty = false; + +}; + +// constructor +PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Graphics.prototype.constructor = PIXI.Graphics; + +/** + * When cacheAsBitmap is set to true the graphics object will be rendered as if it was a sprite. + * This is useful if your graphics element does not change often, as it will speed up the rendering of the object in exchange for taking up texture memory. + * It is also useful if you need the graphics object to be anti-aliased, because it will be rendered using canvas. + * This is not recommended if you are constantly redrawing the graphics element. + * + * @property cacheAsBitmap + * @type Boolean + * @default false + * @private + */ +Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { + get: function() { + return this._cacheAsBitmap; + }, + set: function(value) { + this._cacheAsBitmap = value; + + if(this._cacheAsBitmap) + { + + this._generateCachedSprite(); + } + else + { + this.destroyCachedSprite(); + this.dirty = true; + } + + } +}); + +/** + * Specifies the line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method. + * + * @method lineStyle + * @param lineWidth {Number} width of the line to draw, will update the objects stored style + * @param color {Number} color of the line to draw, will update the objects stored style + * @param alpha {Number} alpha of the line to draw, will update the objects stored style + * @return {Graphics} + */ +PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) +{ + this.lineWidth = lineWidth || 0; + this.lineColor = color || 0; + this.lineAlpha = (arguments.length < 3) ? 1 : alpha; + + if(this.currentPath) + { + if(this.currentPath.shape.points.length) + { + // halfway through a line? start a new one! + this.drawShape( new PIXI.Polygon( this.currentPath.shape.points.slice(-2) )); + return this; + } + + // otherwise its empty so lets just set the line properties + this.currentPath.lineWidth = this.lineWidth; + this.currentPath.lineColor = this.lineColor; + this.currentPath.lineAlpha = this.lineAlpha; + + } + + return this; +}; + +/** + * Moves the current drawing position to x, y. + * + * @method moveTo + * @param x {Number} the X coordinate to move to + * @param y {Number} the Y coordinate to move to + * @return {Graphics} + */ +PIXI.Graphics.prototype.moveTo = function(x, y) +{ + this.drawShape(new PIXI.Polygon([x,y])); + + return this; +}; + +/** + * Draws a line using the current line style from the current drawing position to (x, y); + * The current drawing position is then set to (x, y). + * + * @method lineTo + * @param x {Number} the X coordinate to draw to + * @param y {Number} the Y coordinate to draw to + * @return {Graphics} + */ +PIXI.Graphics.prototype.lineTo = function(x, y) +{ + this.currentPath.shape.points.push(x, y); + this.dirty = true; + + return this; +}; + +/** + * Calculate the points for a quadratic bezier curve and then draws it. + * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c + * + * @method quadraticCurveTo + * @param cpX {Number} Control point x + * @param cpY {Number} Control point y + * @param toX {Number} Destination point x + * @param toY {Number} Destination point y + * @return {Graphics} + */ +PIXI.Graphics.prototype.quadraticCurveTo = function(cpX, cpY, toX, toY) +{ + if( this.currentPath ) + { + if(this.currentPath.shape.points.length === 0)this.currentPath.shape.points = [0,0]; + } + else + { + this.moveTo(0,0); + } + + var xa, + ya, + n = 20, + points = this.currentPath.shape.points; + if(points.length === 0)this.moveTo(0, 0); + + + var fromX = points[points.length-2]; + var fromY = points[points.length-1]; + + var j = 0; + for (var i = 1; i <= n; i++ ) + { + j = i / n; + + xa = fromX + ( (cpX - fromX) * j ); + ya = fromY + ( (cpY - fromY) * j ); + + points.push( xa + ( ((cpX + ( (toX - cpX) * j )) - xa) * j ), + ya + ( ((cpY + ( (toY - cpY) * j )) - ya) * j ) ); + } + + + this.dirty = true; + + return this; +}; + +/** + * Calculate the points for a bezier curve and then draws it. + * + * @method bezierCurveTo + * @param cpX {Number} Control point x + * @param cpY {Number} Control point y + * @param cpX2 {Number} Second Control point x + * @param cpY2 {Number} Second Control point y + * @param toX {Number} Destination point x + * @param toY {Number} Destination point y + * @return {Graphics} + */ +PIXI.Graphics.prototype.bezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) +{ + if( this.currentPath ) + { + if(this.currentPath.shape.points.length === 0)this.currentPath.shape.points = [0,0]; + } + else + { + this.moveTo(0,0); + } + + var n = 20, + dt, + dt2, + dt3, + t2, + t3, + points = this.currentPath.shape.points; + + var fromX = points[points.length-2]; + var fromY = points[points.length-1]; + + var j = 0; + + for (var i=1; i<=n; i++) + { + j = i / n; + + dt = (1 - j); + dt2 = dt * dt; + dt3 = dt2 * dt; + + t2 = j * j; + t3 = t2 * j; + + points.push( dt3 * fromX + 3 * dt2 * j * cpX + 3 * dt * t2 * cpX2 + t3 * toX, + dt3 * fromY + 3 * dt2 * j * cpY + 3 * dt * t2 * cpY2 + t3 * toY); + } + + this.dirty = true; + + return this; +}; + +/* + * The arcTo() method creates an arc/curve between two tangents on the canvas. + * + * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google! + * + * @method arcTo + * @param x1 {Number} The x-coordinate of the beginning of the arc + * @param y1 {Number} The y-coordinate of the beginning of the arc + * @param x2 {Number} The x-coordinate of the end of the arc + * @param y2 {Number} The y-coordinate of the end of the arc + * @param radius {Number} The radius of the arc + * @return {Graphics} + */ +PIXI.Graphics.prototype.arcTo = function(x1, y1, x2, y2, radius) +{ + if( this.currentPath ) + { + if(this.currentPath.shape.points.length === 0) + { + this.currentPath.shape.points.push(x1, y1); + } + } + else + { + this.moveTo(x1, y1); + } + + var points = this.currentPath.shape.points; + var fromX = points[points.length-2]; + var fromY = points[points.length-1]; + var a1 = fromY - y1; + var b1 = fromX - x1; + var a2 = y2 - y1; + var b2 = x2 - x1; + var mm = Math.abs(a1 * b2 - b1 * a2); + + + if (mm < 1.0e-8 || radius === 0) + { + if( points[points.length-2] !== x1 || points[points.length-1] !== y1) + { + //console.log(">>") + points.push(x1, y1); + } + } + else + { + var dd = a1 * a1 + b1 * b1; + var cc = a2 * a2 + b2 * b2; + var tt = a1 * a2 + b1 * b2; + var k1 = radius * Math.sqrt(dd) / mm; + var k2 = radius * Math.sqrt(cc) / mm; + var j1 = k1 * tt / dd; + var j2 = k2 * tt / cc; + var cx = k1 * b2 + k2 * b1; + var cy = k1 * a2 + k2 * a1; + var px = b1 * (k2 + j1); + var py = a1 * (k2 + j1); + var qx = b2 * (k1 + j2); + var qy = a2 * (k1 + j2); + var startAngle = Math.atan2(py - cy, px - cx); + var endAngle = Math.atan2(qy - cy, qx - cx); + + this.arc(cx + x1, cy + y1, radius, startAngle, endAngle, b1 * a2 > b2 * a1); + } + + this.dirty = true; + + return this; +}; + +/** + * The arc method creates an arc/curve (used to create circles, or parts of circles). + * + * @method arc + * @param cx {Number} The x-coordinate of the center of the circle + * @param cy {Number} The y-coordinate of the center of the circle + * @param radius {Number} The radius of the circle + * @param startAngle {Number} The starting angle, in radians (0 is at the 3 o'clock position of the arc's circle) + * @param endAngle {Number} The ending angle, in radians + * @param anticlockwise {Boolean} Optional. Specifies whether the drawing should be counterclockwise or clockwise. False is default, and indicates clockwise, while true indicates counter-clockwise. + * @return {Graphics} + */ +PIXI.Graphics.prototype.arc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) +{ + var startX = cx + Math.cos(startAngle) * radius; + var startY = cy + Math.sin(startAngle) * radius; + var points; + + if( this.currentPath ) + { + points = this.currentPath.shape.points; + + if(points.length === 0) + { + points.push(startX, startY); + } + else if( points[points.length-2] !== startX || points[points.length-1] !== startY) + { + points.push(startX, startY); + } + } + else + { + this.moveTo(startX, startY); + points = this.currentPath.shape.points; + } + + if (startAngle === endAngle)return this; + + if( !anticlockwise && endAngle <= startAngle ) + { + endAngle += Math.PI * 2; + } + else if( anticlockwise && startAngle <= endAngle ) + { + startAngle += Math.PI * 2; + } + + var sweep = anticlockwise ? (startAngle - endAngle) *-1 : (endAngle - startAngle); + var segs = ( Math.abs(sweep)/ (Math.PI * 2) ) * 40; + + if( sweep === 0 ) return this; + + var theta = sweep/(segs*2); + var theta2 = theta*2; + + var cTheta = Math.cos(theta); + var sTheta = Math.sin(theta); + + var segMinus = segs - 1; + + var remainder = ( segMinus % 1 ) / segMinus; + + for(var i=0; i<=segMinus; i++) + { + var real = i + remainder * i; + + + var angle = ((theta) + startAngle + (theta2 * real)); + + var c = Math.cos(angle); + var s = -Math.sin(angle); + + points.push(( (cTheta * c) + (sTheta * s) ) * radius + cx, + ( (cTheta * -s) + (sTheta * c) ) * radius + cy); + } + + this.dirty = true; + + return this; +}; + +/** + * Specifies a simple one-color fill that subsequent calls to other Graphics methods + * (such as lineTo() or drawCircle()) use when drawing. + * + * @method beginFill + * @param color {Number} the color of the fill + * @param alpha {Number} the alpha of the fill + * @return {Graphics} + */ +PIXI.Graphics.prototype.beginFill = function(color, alpha) +{ + this.filling = true; + this.fillColor = color || 0; + this.fillAlpha = (alpha === undefined) ? 1 : alpha; + + if(this.currentPath) + { + if(this.currentPath.shape.points.length <= 2) + { + this.currentPath.fill = this.filling; + this.currentPath.fillColor = this.fillColor; + this.currentPath.fillAlpha = this.fillAlpha; + } + } + return this; +}; + +/** + * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method. + * + * @method endFill + * @return {Graphics} + */ +PIXI.Graphics.prototype.endFill = function() +{ + this.filling = false; + this.fillColor = null; + this.fillAlpha = 1; + + return this; +}; + +/** + * @method drawRect + * + * @param x {Number} The X coord of the top-left of the rectangle + * @param y {Number} The Y coord of the top-left of the rectangle + * @param width {Number} The width of the rectangle + * @param height {Number} The height of the rectangle + * @return {Graphics} + */ +PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) +{ + this.drawShape(new PIXI.Rectangle(x,y, width, height)); + + return this; +}; + +/** + * @method drawRoundedRect + * + * @param x {Number} The X coord of the top-left of the rectangle + * @param y {Number} The Y coord of the top-left of the rectangle + * @param width {Number} The width of the rectangle + * @param height {Number} The height of the rectangle + * @param radius {Number} Radius of the rectangle corners + */ +PIXI.Graphics.prototype.drawRoundedRect = function( x, y, width, height, radius ) +{ + this.drawShape(new PIXI.RoundedRectangle(x, y, width, height, radius)); + + return this; +}; + +/** + * Draws a circle. + * + * @method drawCircle + * @param x {Number} The X coordinate of the center of the circle + * @param y {Number} The Y coordinate of the center of the circle + * @param radius {Number} The radius of the circle + * @return {Graphics} + */ +PIXI.Graphics.prototype.drawCircle = function(x, y, radius) +{ + this.drawShape(new PIXI.Circle(x,y, radius)); + + return this; +}; + +/** + * Draws an ellipse. + * + * @method drawEllipse + * @param x {Number} The X coordinate of the center of the ellipse + * @param y {Number} The Y coordinate of the center of the ellipse + * @param width {Number} The half width of the ellipse + * @param height {Number} The half height of the ellipse + * @return {Graphics} + */ +PIXI.Graphics.prototype.drawEllipse = function(x, y, width, height) +{ + this.drawShape(new PIXI.Ellipse(x, y, width, height)); + + return this; +}; + +/** + * Draws a polygon using the given path. + * + * @method drawPolygon + * @param path {Array} The path data used to construct the polygon. + * @return {Graphics} + */ +PIXI.Graphics.prototype.drawPolygon = function(path) +{ + if(!(path instanceof Array))path = Array.prototype.slice.call(arguments); + this.drawShape(new PIXI.Polygon(path)); + return this; +}; + +/** + * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings. + * + * @method clear + * @return {Graphics} + */ +PIXI.Graphics.prototype.clear = function() +{ + this.lineWidth = 0; + this.filling = false; + + this.dirty = true; + this.clearDirty = true; + this.graphicsData = []; + + return this; +}; + +/** + * Useful function that returns a texture of the graphics object that can then be used to create sprites + * This can be quite useful if your geometry is complicated and needs to be reused multiple times. + * + * @method generateTexture + * @param resolution {Number} The resolution of the texture being generated + * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts + * @return {Texture} a texture of the graphics object + */ +PIXI.Graphics.prototype.generateTexture = function(resolution, scaleMode) +{ + resolution = resolution || 1; + + var bounds = this.getBounds(); + + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width * resolution, bounds.height * resolution); + + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas, scaleMode); + texture.baseTexture.resolution = resolution; + + canvasBuffer.context.scale(resolution, resolution); + + canvasBuffer.context.translate(-bounds.x,-bounds.y); + + PIXI.CanvasGraphics.renderGraphics(this, canvasBuffer.context); + + return texture; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Graphics.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(this.visible === false || this.alpha === 0 || this.isMask === true)return; + + if(this._cacheAsBitmap) + { + + if(this.dirty || this.cachedSpriteDirty) + { + + this._generateCachedSprite(); + + // we will also need to update the texture on the gpu too! + this.updateCachedSpriteTexture(); + + this.cachedSpriteDirty = false; + this.dirty = false; + } + + this._cachedSprite.worldAlpha = this.worldAlpha; + PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + + return; + } + else + { + renderSession.spriteBatch.stop(); + renderSession.blendModeManager.setBlendMode(this.blendMode); + + if(this._mask)renderSession.maskManager.pushMask(this._mask, renderSession); + if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock); + + // check blend mode + if(this.blendMode !== renderSession.spriteBatch.currentBlendMode) + { + renderSession.spriteBatch.currentBlendMode = this.blendMode; + var blendModeWebGL = PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode]; + renderSession.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + } + + // check if the webgl graphic needs to be updated + if(this.webGLDirty) + { + this.dirty = true; + this.webGLDirty = false; + } + + PIXI.WebGLGraphics.renderGraphics(this, renderSession); + + // only render if it has children! + if(this.children.length) + { + renderSession.spriteBatch.start(); + + // simple render children! + for(var i=0, j=this.children.length; i maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + this._bounds.x = minX; + this._bounds.width = maxX - minX; + + this._bounds.y = minY; + this._bounds.height = maxY - minY; + + return this._bounds; +}; + +/** + * Update the bounds of the object + * + * @method updateLocalBounds + */ +PIXI.Graphics.prototype.updateLocalBounds = function() +{ + var minX = Infinity; + var maxX = -Infinity; + + var minY = Infinity; + var maxY = -Infinity; + + if(this.graphicsData.length) + { + var shape, points, x, y, w, h; + + for (var i = 0; i < this.graphicsData.length; i++) { + var data = this.graphicsData[i]; + var type = data.type; + var lineWidth = data.lineWidth; + shape = data.shape; + + + if(type === PIXI.Graphics.RECT || type === PIXI.Graphics.RREC) + { + x = shape.x - lineWidth/2; + y = shape.y - lineWidth/2; + w = shape.width + lineWidth; + h = shape.height + lineWidth; + + minX = x < minX ? x : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y < minY ? y : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else if(type === PIXI.Graphics.CIRC) + { + x = shape.x; + y = shape.y; + w = shape.radius + lineWidth/2; + h = shape.radius + lineWidth/2; + + minX = x - w < minX ? x - w : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y - h < minY ? y - h : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else if(type === PIXI.Graphics.ELIP) + { + x = shape.x; + y = shape.y; + w = shape.width + lineWidth/2; + h = shape.height + lineWidth/2; + + minX = x - w < minX ? x - w : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y - h < minY ? y - h : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else + { + // POLY + points = shape.points; + + for (var j = 0; j < points.length; j+=2) + { + + x = points[j]; + y = points[j+1]; + minX = x-lineWidth < minX ? x-lineWidth : minX; + maxX = x+lineWidth > maxX ? x+lineWidth : maxX; + + minY = y-lineWidth < minY ? y-lineWidth : minY; + maxY = y+lineWidth > maxY ? y+lineWidth : maxY; + } + } + } + } + else + { + minX = 0; + maxX = 0; + minY = 0; + maxY = 0; + } + + var padding = this.boundsPadding; + + this._localBounds.x = minX - padding; + this._localBounds.width = (maxX - minX) + padding * 2; + + this._localBounds.y = minY - padding; + this._localBounds.height = (maxY - minY) + padding * 2; +}; + +/** + * Generates the cached sprite when the sprite has cacheAsBitmap = true + * + * @method _generateCachedSprite + * @private + */ +PIXI.Graphics.prototype._generateCachedSprite = function() +{ + var bounds = this.getLocalBounds(); + + if(!this._cachedSprite) + { + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + this._cachedSprite = new PIXI.Sprite(texture); + this._cachedSprite.buffer = canvasBuffer; + + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.buffer.resize(bounds.width, bounds.height); + } + + // leverage the anchor to account for the offset of the element + this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); + this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); + + // this._cachedSprite.buffer.context.save(); + this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y); + + // make sure we set the alpha of the graphics to 1 for the render.. + this.worldAlpha = 1; + + // now render the graphic.. + PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context); + this._cachedSprite.alpha = this.alpha; +}; + +/** + * Updates texture size based on canvas size + * + * @method updateCachedSpriteTexture + * @private + */ +PIXI.Graphics.prototype.updateCachedSpriteTexture = function() +{ + var cachedSprite = this._cachedSprite; + var texture = cachedSprite.texture; + var canvas = cachedSprite.buffer.canvas; + + texture.baseTexture.width = canvas.width; + texture.baseTexture.height = canvas.height; + texture.crop.width = texture.frame.width = canvas.width; + texture.crop.height = texture.frame.height = canvas.height; + + cachedSprite._width = canvas.width; + cachedSprite._height = canvas.height; + + // update the dirty base textures + texture.baseTexture.dirty(); +}; + +/** + * Destroys a previous cached sprite. + * + * @method destroyCachedSprite + */ +PIXI.Graphics.prototype.destroyCachedSprite = function() +{ + this._cachedSprite.texture.destroy(true); + + // let the gc collect the unused sprite + // TODO could be object pooled! + this._cachedSprite = null; +}; + +/** + * Draws the given shape to this Graphics object. Can be any of Circle, Rectangle, Ellipse, Line or Polygon. + * + * @method drawShape + * @param {Circle|Rectangle|Ellipse|Line|Polygon} shape The Shape object to draw. + * @return {GraphicsData} The generated GraphicsData object. + */ +PIXI.Graphics.prototype.drawShape = function(shape) +{ + if(this.currentPath) + { + // check current path! + if(this.currentPath.shape.points.length <= 2)this.graphicsData.pop(); + } + + this.currentPath = null; + + var data = new PIXI.GraphicsData(this.lineWidth, this.lineColor, this.lineAlpha, this.fillColor, this.fillAlpha, this.filling, shape); + + this.graphicsData.push(data); + + if(data.type === PIXI.Graphics.POLY) + { + data.shape.closed = this.filling; + this.currentPath = data; + } + + this.dirty = true; + + return data; +}; + +/** + * A GraphicsData object. + * + * @class GraphicsData + * @constructor + */ +PIXI.GraphicsData = function(lineWidth, lineColor, lineAlpha, fillColor, fillAlpha, fill, shape) +{ + this.lineWidth = lineWidth; + this.lineColor = lineColor; + this.lineAlpha = lineAlpha; + this._lineTint = lineColor; + + this.fillColor = fillColor; + this.fillAlpha = fillAlpha; + this._fillTint = fillColor; + this.fill = fill; + + this.shape = shape; + this.type = shape.type; +}; + +// SOME TYPES: +PIXI.Graphics.POLY = 0; +PIXI.Graphics.RECT = 1; +PIXI.Graphics.CIRC = 2; +PIXI.Graphics.ELIP = 3; +PIXI.Graphics.RREC = 4; + +PIXI.Polygon.prototype.type = PIXI.Graphics.POLY; +PIXI.Rectangle.prototype.type = PIXI.Graphics.RECT; +PIXI.Circle.prototype.type = PIXI.Graphics.CIRC; +PIXI.Ellipse.prototype.type = PIXI.Graphics.ELIP; +PIXI.RoundedRectangle.prototype.type = PIXI.Graphics.RREC; + diff --git a/app/Lib/Vendor/src/pixi/renderers/canvas/CanvasGraphics.js b/app/Lib/Vendor/src/pixi/renderers/canvas/CanvasGraphics.js new file mode 100644 index 0000000..1fb5372 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/canvas/CanvasGraphics.js @@ -0,0 +1,358 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * A set of functions used by the canvas renderer to draw the primitive graphics data. + * + * @class CanvasGraphics + * @static + */ +PIXI.CanvasGraphics = function() +{ +}; + +/* + * Renders a PIXI.Graphics object to a canvas. + * + * @method renderGraphics + * @static + * @param graphics {Graphics} the actual graphics object to render + * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas + */ +PIXI.CanvasGraphics.renderGraphics = function(graphics, context) +{ + var worldAlpha = graphics.worldAlpha; + + if(graphics.dirty) + { + this.updateGraphicsTint(graphics); + graphics.dirty = false; + } + + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var shape = data.shape; + + var fillColor = data._fillTint; + var lineColor = data._lineTint; + + context.lineWidth = data.lineWidth; + + if(data.type === PIXI.Graphics.POLY) + { + context.beginPath(); + + var points = shape.points; + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + if(shape.closed) + { + context.lineTo(points[0], points[1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if(data.type === PIXI.Graphics.RECT) + { + + if(data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fillRect(shape.x, shape.y, shape.width, shape.height); + + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.strokeRect(shape.x, shape.y, shape.width, shape.height); + } + } + else if(data.type === PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if(data.type === PIXI.Graphics.ELIP) + { + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var w = shape.width * 2; + var h = shape.height * 2; + + var x = shape.x - w/2; + var y = shape.y - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === PIXI.Graphics.RREC) + { + var rx = shape.x; + var ry = shape.y; + var width = shape.width; + var height = shape.height; + var radius = shape.radius; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + + if(data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + } +}; + +/* + * Renders a graphics mask + * + * @static + * @private + * @method renderGraphicsMask + * @param graphics {Graphics} the graphics which will be used as a mask + * @param context {CanvasRenderingContext2D} the context 2d method of the canvas + */ +PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) +{ + var len = graphics.graphicsData.length; + + if(len === 0) return; + + if(len > 1) + { + len = 1; + window.console.log('Pixi.js warning: masks in canvas can only mask using the first path in the graphics object'); + } + + for (var i = 0; i < 1; i++) + { + var data = graphics.graphicsData[i]; + var shape = data.shape; + + if(data.type === PIXI.Graphics.POLY) + { + context.beginPath(); + + var points = shape.points; + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + } + else if(data.type === PIXI.Graphics.RECT) + { + context.beginPath(); + context.rect(shape.x, shape.y, shape.width, shape.height); + context.closePath(); + } + else if(data.type === PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); + context.closePath(); + } + else if(data.type === PIXI.Graphics.ELIP) + { + + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var w = shape.width * 2; + var h = shape.height * 2; + + var x = shape.x - w/2; + var y = shape.y - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + context.closePath(); + } + else if (data.type === PIXI.Graphics.RREC) + { + + var pts = shape.points; + var rx = pts[0]; + var ry = pts[1]; + var width = pts[2]; + var height = pts[3]; + var radius = pts[4]; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + } + } +}; + +PIXI.CanvasGraphics.updateGraphicsTint = function(graphics) +{ + if(graphics.tint === 0xFFFFFF)return; + + var tintR = (graphics.tint >> 16 & 0xFF) / 255; + var tintG = (graphics.tint >> 8 & 0xFF) / 255; + var tintB = (graphics.tint & 0xFF)/ 255; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + var fillColor = data.fillColor | 0; + var lineColor = data.lineColor | 0; + + /* + var colorR = (fillColor >> 16 & 0xFF) / 255; + var colorG = (fillColor >> 8 & 0xFF) / 255; + var colorB = (fillColor & 0xFF) / 255; + + colorR *= tintR; + colorG *= tintG; + colorB *= tintB; + + fillColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); + + colorR = (lineColor >> 16 & 0xFF) / 255; + colorG = (lineColor >> 8 & 0xFF) / 255; + colorB = (lineColor & 0xFF) / 255; + + colorR *= tintR; + colorG *= tintG; + colorB *= tintB; + + lineColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); + */ + + // super inline cos im an optimization NAZI :) + data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); + data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); + + } +}; + diff --git a/app/Lib/Vendor/src/pixi/renderers/canvas/CanvasRenderer.js b/app/Lib/Vendor/src/pixi/renderers/canvas/CanvasRenderer.js new file mode 100644 index 0000000..02f1170 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/canvas/CanvasRenderer.js @@ -0,0 +1,345 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) + * + * @class CanvasRenderer + * @constructor + * @param [width=800] {Number} the width of the canvas view + * @param [height=600] {Number} the height of the canvas view + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 + * @param [options.clearBeforeRender=true] {Boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. + */ +PIXI.CanvasRenderer = function(width, height, options) +{ + if(options) + { + for (var i in PIXI.defaultRenderOptions) + { + if (typeof options[i] === "undefined") options[i] = PIXI.defaultRenderOptions[i]; + } + } + else + { + options = PIXI.defaultRenderOptions; + } + + if(!PIXI.defaultRenderer) + { + PIXI.sayHello("Canvas"); + PIXI.defaultRenderer = this; + } + + /** + * The renderer type. + * + * @property type + * @type Number + */ + this.type = PIXI.CANVAS_RENDERER; + + /** + * The resolution of the canvas. + * + * @property resolution + * @type Number + */ + this.resolution = options.resolution; + + /** + * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. + * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. + * + * @property clearBeforeRender + * @type Boolean + * @default + */ + this.clearBeforeRender = options.clearBeforeRender; + + /** + * Whether the render view is transparent + * + * @property transparent + * @type Boolean + */ + this.transparent = options.transparent; + + /** + * Whether the render view should be resized automatically + * + * @property autoResize + * @type Boolean + */ + this.autoResize = options.autoResize || false; + + + /** + * The width of the canvas view + * + * @property width + * @type Number + * @default 800 + */ + this.width = width || 800; + + /** + * The height of the canvas view + * + * @property height + * @type Number + * @default 600 + */ + this.height = height || 600; + + this.width *= this.resolution; + this.height *= this.resolution; + + /** + * The canvas element that everything is drawn to. + * + * @property view + * @type HTMLCanvasElement + */ + this.view = options.view || document.createElement( "canvas" ); + + /** + * The canvas 2d context that everything is drawn with + * @property context + * @type CanvasRenderingContext2D + */ + this.context = this.view.getContext( "2d", { alpha: this.transparent } ); + + /** + * Boolean flag controlling canvas refresh. + * + * @property refresh + * @type Boolean + */ + this.refresh = true; + + this.view.width = this.width * this.resolution; + this.view.height = this.height * this.resolution; + + /** + * Internal var. + * + * @property count + * @type Number + */ + this.count = 0; + + /** + * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer + * @property CanvasMaskManager + * @type CanvasMaskManager + */ + this.maskManager = new PIXI.CanvasMaskManager(); + + /** + * The render session is just a bunch of parameter used for rendering + * @property renderSession + * @type Object + */ + this.renderSession = { + context: this.context, + maskManager: this.maskManager, + scaleMode: null, + smoothProperty: null, + /** + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + */ + roundPixels: false + }; + + this.mapBlendModes(); + + this.resize(width, height); + + if("imageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "imageSmoothingEnabled"; + else if("webkitImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "webkitImageSmoothingEnabled"; + else if("mozImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "mozImageSmoothingEnabled"; + else if("oImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "oImageSmoothingEnabled"; + else if ("msImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "msImageSmoothingEnabled"; +}; + +// constructor +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; + +/** + * Renders the Stage to this canvas view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.CanvasRenderer.prototype.render = function(stage) +{ + stage.updateTransform(); + + this.context.setTransform(1,0,0,1,0,0); + + this.context.globalAlpha = 1; + + this.renderSession.currentBlendMode = PIXI.blendModes.NORMAL; + this.context.globalCompositeOperation = PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]; + + if (navigator.isCocoonJS && this.view.screencanvas) { + this.context.fillStyle = "black"; + this.context.clear(); + } + + if (this.clearBeforeRender) + { + if (this.transparent) + { + this.context.clearRect(0, 0, this.width, this.height); + } + else + { + this.context.fillStyle = stage.backgroundColorString; + this.context.fillRect(0, 0, this.width , this.height); + } + } + + this.renderDisplayObject(stage); + + // run interaction! + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } +}; + +/** + * Removes everything from the renderer and optionally removes the Canvas DOM element. + * + * @method destroy + * @param [removeView=true] {boolean} Removes the Canvas element from the DOM. + */ +PIXI.CanvasRenderer.prototype.destroy = function(removeView) +{ + if (typeof removeView === "undefined") { removeView = true; } + + if (removeView && this.view.parent) + { + this.view.parent.removeChild(this.view); + } + + this.view = null; + this.context = null; + this.maskManager = null; + this.renderSession = null; + +}; + +/** + * Resizes the canvas view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the canvas view + * @param height {Number} the new height of the canvas view + */ +PIXI.CanvasRenderer.prototype.resize = function(width, height) +{ + this.width = width * this.resolution; + this.height = height * this.resolution; + + this.view.width = this.width; + this.view.height = this.height; + + if (this.autoResize) { + this.view.style.width = this.width / this.resolution + "px"; + this.view.style.height = this.height / this.resolution + "px"; + } +}; + +/** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render + * @param context {CanvasRenderingContext2D} the context 2d method of the canvas + * @private + */ +PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) +{ + this.renderSession.context = context || this.context; + this.renderSession.resolution = this.resolution; + displayObject._renderCanvas(this.renderSession); +}; + +/** + * Maps Pixi blend modes to canvas blend modes. + * + * @method mapBlendModes + * @private + */ +PIXI.CanvasRenderer.prototype.mapBlendModes = function() +{ + if(!PIXI.blendModesCanvas) + { + PIXI.blendModesCanvas = []; + + if(PIXI.canUseNewCanvasBlendModes()) + { + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "screen"; + PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "overlay"; + PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "darken"; + PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "lighten"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn"; + PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light"; + PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light"; + PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference"; + PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion"; + PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "hue"; + PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "color"; + PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity"; + } + else + { + // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough" + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over"; + } + } +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasBuffer.js b/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasBuffer.js new file mode 100644 index 0000000..b53d851 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasBuffer.js @@ -0,0 +1,76 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * Creates a Canvas element of the given size. + * + * @class CanvasBuffer + * @constructor + * @param width {Number} the width for the newly created canvas + * @param height {Number} the height for the newly created canvas + */ +PIXI.CanvasBuffer = function(width, height) +{ + /** + * The width of the Canvas in pixels. + * + * @property width + * @type Number + */ + this.width = width; + + /** + * The height of the Canvas in pixels. + * + * @property height + * @type Number + */ + this.height = height; + + /** + * The Canvas object that belongs to this CanvasBuffer. + * + * @property canvas + * @type HTMLCanvasElement + */ + this.canvas = document.createElement("canvas"); + + /** + * A CanvasRenderingContext2D object representing a two-dimensional rendering context. + * + * @property context + * @type CanvasRenderingContext2D + */ + this.context = this.canvas.getContext("2d"); + + this.canvas.width = width; + this.canvas.height = height; +}; + +PIXI.CanvasBuffer.prototype.constructor = PIXI.CanvasBuffer; + +/** + * Clears the canvas that was created by the CanvasBuffer class. + * + * @method clear + * @private + */ +PIXI.CanvasBuffer.prototype.clear = function() +{ + this.context.setTransform(1, 0, 0, 1, 0, 0); + this.context.clearRect(0,0, this.width, this.height); +}; + +/** + * Resizes the canvas to the specified width and height. + * + * @method resize + * @param width {Number} the new width of the canvas + * @param height {Number} the new height of the canvas + */ +PIXI.CanvasBuffer.prototype.resize = function(width, height) +{ + this.width = this.canvas.width = width; + this.height = this.canvas.height = height; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasMaskManager.js b/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasMaskManager.js new file mode 100644 index 0000000..748adda --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasMaskManager.js @@ -0,0 +1,58 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A set of functions used to handle masking. + * + * @class CanvasMaskManager + * @constructor + */ +PIXI.CanvasMaskManager = function() +{ +}; + +PIXI.CanvasMaskManager.prototype.constructor = PIXI.CanvasMaskManager; + +/** + * This method adds it to the current stack of masks. + * + * @method pushMask + * @param maskData {Object} the maskData that will be pushed + * @param renderSession {Object} The renderSession whose context will be used for this mask manager. + */ +PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, renderSession) +{ + var context = renderSession.context; + + context.save(); + + var cacheAlpha = maskData.alpha; + var transform = maskData.worldTransform; + + var resolution = renderSession.resolution; + + context.setTransform(transform.a * resolution, + transform.b * resolution, + transform.c * resolution, + transform.d * resolution, + transform.tx * resolution, + transform.ty * resolution); + + PIXI.CanvasGraphics.renderGraphicsMask(maskData, context); + + context.clip(); + + maskData.worldAlpha = cacheAlpha; +}; + +/** + * Restores the current drawing context to the state it was before the mask was applied. + * + * @method popMask + * @param renderSession {Object} The renderSession whose context will be used for this mask manager. + */ +PIXI.CanvasMaskManager.prototype.popMask = function(renderSession) +{ + renderSession.context.restore(); +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasTinter.js b/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasTinter.js new file mode 100644 index 0000000..dd50b06 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/canvas/utils/CanvasTinter.js @@ -0,0 +1,242 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * Utility methods for Sprite/Texture tinting. + * + * @class CanvasTinter + * @static + */ +PIXI.CanvasTinter = function() +{ +}; + +/** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @method getTintedTexture + * @static + * @param sprite {Sprite} the sprite to tint + * @param color {Number} the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ +PIXI.CanvasTinter.getTintedTexture = function(sprite, color) +{ + var texture = sprite.texture; + + color = PIXI.CanvasTinter.roundColor(color); + + var stringColor = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + texture.tintCache = texture.tintCache || {}; + + if(texture.tintCache[stringColor]) return texture.tintCache[stringColor]; + + // clone texture.. + var canvas = PIXI.CanvasTinter.canvas || document.createElement("canvas"); + + //PIXI.CanvasTinter.tintWithPerPixel(texture, stringColor, canvas); + PIXI.CanvasTinter.tintMethod(texture, color, canvas); + + if(PIXI.CanvasTinter.convertTintToImage) + { + // is this better? + var tintImage = new Image(); + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + PIXI.CanvasTinter.canvas = null; + } + + return canvas; +}; + +/** + * Tint a texture using the "multiply" operation. + * + * @method tintWithMultiply + * @static + * @param texture {Texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var crop = texture.crop; + + canvas.width = crop.width; + canvas.height = crop.height; + + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = "multiply"; + + context.drawImage(texture.baseTexture.source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height); + + context.globalCompositeOperation = "destination-atop"; + + context.drawImage(texture.baseTexture.source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height); +}; + +/** + * Tint a texture using the "overlay" operation. + * + * @method tintWithOverlay + * @static + * @param texture {Texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithOverlay = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var crop = texture.crop; + + canvas.width = crop.width; + canvas.height = crop.height; + + context.globalCompositeOperation = "copy"; + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = "destination-atop"; + context.drawImage(texture.baseTexture.source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height); + + //context.globalCompositeOperation = "copy"; +}; + +/** + * Tint a texture pixel per pixel. + * + * @method tintPerPixel + * @static + * @param texture {Texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var crop = texture.crop; + + canvas.width = crop.width; + canvas.height = crop.height; + + context.globalCompositeOperation = "copy"; + context.drawImage(texture.baseTexture.source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height); + + var rgbValues = PIXI.hex2rgb(color); + var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2]; + + var pixelData = context.getImageData(0, 0, crop.width, crop.height); + + var pixels = pixelData.data; + + for (var i = 0; i < pixels.length; i += 4) + { + pixels[i+0] *= r; + pixels[i+1] *= g; + pixels[i+2] *= b; + } + + context.putImageData(pixelData, 0, 0); +}; + +/** + * Rounds the specified color according to the PIXI.CanvasTinter.cacheStepsPerColorChannel. + * + * @method roundColor + * @static + * @param color {number} the color to round, should be a hex color + */ +PIXI.CanvasTinter.roundColor = function(color) +{ + var step = PIXI.CanvasTinter.cacheStepsPerColorChannel; + + var rgbValues = PIXI.hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return PIXI.rgb2hex(rgbValues); +}; + +/** + * Number of steps which will be used as a cap when rounding colors. + * + * @property cacheStepsPerColorChannel + * @type Number + * @static + */ +PIXI.CanvasTinter.cacheStepsPerColorChannel = 8; + +/** + * Tint cache boolean flag. + * + * @property convertTintToImage + * @type Boolean + * @static + */ +PIXI.CanvasTinter.convertTintToImage = false; + +/** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @property canUseMultiply + * @type Boolean + * @static + */ +PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes(); + +/** + * The tinting method that will be used. + * + * @method tintMethod + * @static + */ +PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/WebGLRenderer.js b/app/Lib/Vendor/src/pixi/renderers/webgl/WebGLRenderer.js new file mode 100644 index 0000000..02b30e4 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/WebGLRenderer.js @@ -0,0 +1,554 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.glContexts = []; // this is where we store the webGL contexts for easy access. +PIXI.instances = []; + +/** + * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. + * So no need for Sprite Batches or Sprite Clouds. + * Don't forget to add the view to your DOM or you will not see anything :) + * + * @class WebGLRenderer + * @constructor + * @param [width=0] {Number} the width of the canvas view + * @param [height=0] {Number} the height of the canvas view + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false + * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 + */ +PIXI.WebGLRenderer = function(width, height, options) +{ + if(options) + { + for (var i in PIXI.defaultRenderOptions) + { + if (typeof options[i] === 'undefined') options[i] = PIXI.defaultRenderOptions[i]; + } + } + else + { + options = PIXI.defaultRenderOptions; + } + + if(!PIXI.defaultRenderer) + { + PIXI.sayHello('webGL'); + PIXI.defaultRenderer = this; + } + + /** + * @property type + * @type Number + */ + this.type = PIXI.WEBGL_RENDERER; + + /** + * The resolution of the renderer + * + * @property resolution + * @type Number + * @default 1 + */ + this.resolution = options.resolution; + + // do a catch.. only 1 webGL renderer.. + + /** + * Whether the render view is transparent + * + * @property transparent + * @type Boolean + */ + this.transparent = options.transparent; + + /** + * Whether the render view should be resized automatically + * + * @property autoResize + * @type Boolean + */ + this.autoResize = options.autoResize || false; + + /** + * The value of the preserveDrawingBuffer flag affects whether or not the contents of the stencil buffer is retained after rendering. + * + * @property preserveDrawingBuffer + * @type Boolean + */ + this.preserveDrawingBuffer = options.preserveDrawingBuffer; + + /** + * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: + * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. + * + * @property clearBeforeRender + * @type Boolean + * @default + */ + this.clearBeforeRender = options.clearBeforeRender; + + /** + * The width of the canvas view + * + * @property width + * @type Number + * @default 800 + */ + this.width = width || 800; + + /** + * The height of the canvas view + * + * @property height + * @type Number + * @default 600 + */ + this.height = height || 600; + + /** + * The canvas element that everything is drawn to + * + * @property view + * @type HTMLCanvasElement + */ + this.view = options.view || document.createElement( 'canvas' ); + + // deal with losing context.. + + /** + * @property contextLostBound + * @type Function + */ + this.contextLostBound = this.handleContextLost.bind(this); + + /** + * @property contextRestoredBound + * @type Function + */ + this.contextRestoredBound = this.handleContextRestored.bind(this); + + this.view.addEventListener('webglcontextlost', this.contextLostBound, false); + this.view.addEventListener('webglcontextrestored', this.contextRestoredBound, false); + + /** + * @property _contextOptions + * @type Object + * @private + */ + this._contextOptions = { + alpha: this.transparent, + antialias: options.antialias, // SPEED UP?? + premultipliedAlpha:this.transparent && this.transparent !== 'notMultiplied', + stencil:true, + preserveDrawingBuffer: options.preserveDrawingBuffer + }; + + /** + * @property projection + * @type Point + */ + this.projection = new PIXI.Point(); + + /** + * @property offset + * @type Point + */ + this.offset = new PIXI.Point(0, 0); + + // time to create the render managers! each one focuses on managing a state in webGL + + /** + * Deals with managing the shader programs and their attribs + * @property shaderManager + * @type WebGLShaderManager + */ + this.shaderManager = new PIXI.WebGLShaderManager(); + + /** + * Manages the rendering of sprites + * @property spriteBatch + * @type WebGLSpriteBatch + */ + this.spriteBatch = new PIXI.WebGLSpriteBatch(); + + /** + * Manages the masks using the stencil buffer + * @property maskManager + * @type WebGLMaskManager + */ + this.maskManager = new PIXI.WebGLMaskManager(); + + /** + * Manages the filters + * @property filterManager + * @type WebGLFilterManager + */ + this.filterManager = new PIXI.WebGLFilterManager(); + + /** + * Manages the stencil buffer + * @property stencilManager + * @type WebGLStencilManager + */ + this.stencilManager = new PIXI.WebGLStencilManager(); + + /** + * Manages the blendModes + * @property blendModeManager + * @type WebGLBlendModeManager + */ + this.blendModeManager = new PIXI.WebGLBlendModeManager(); + + /** + * TODO remove + * @property renderSession + * @type Object + */ + this.renderSession = {}; + this.renderSession.gl = this.gl; + this.renderSession.drawCount = 0; + this.renderSession.shaderManager = this.shaderManager; + this.renderSession.maskManager = this.maskManager; + this.renderSession.filterManager = this.filterManager; + this.renderSession.blendModeManager = this.blendModeManager; + this.renderSession.spriteBatch = this.spriteBatch; + this.renderSession.stencilManager = this.stencilManager; + this.renderSession.renderer = this; + this.renderSession.resolution = this.resolution; + + // time init the context.. + this.initContext(); + + // map some webGL blend modes.. + this.mapBlendModes(); +}; + +// constructor +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; + +/** +* @method initContext +*/ +PIXI.WebGLRenderer.prototype.initContext = function() +{ + var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); + this.gl = gl; + + if (!gl) { + // fail, not able to get a context + throw new Error('This browser does not support webGL. Try using the canvas renderer'); + } + + this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++; + + PIXI.glContexts[this.glContextId] = gl; + + PIXI.instances[this.glContextId] = this; + + // set up the default pixi settings.. + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.enable(gl.BLEND); + + // need to set the context for all the managers... + this.shaderManager.setContext(gl); + this.spriteBatch.setContext(gl); + this.maskManager.setContext(gl); + this.filterManager.setContext(gl); + this.blendModeManager.setContext(gl); + this.stencilManager.setContext(gl); + + this.renderSession.gl = this.gl; + + // now resize and we are good to go! + this.resize(this.width, this.height); +}; + +/** + * Renders the stage to its webGL view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.WebGLRenderer.prototype.render = function(stage) +{ + // no point rendering if our context has been blown up! + if(this.contextLost)return; + + // if rendering a new stage clear the batches.. + if(this.__stage !== stage) + { + if(stage.interactive)stage.interactionManager.removeEvents(); + + // TODO make this work + // dont think this is needed any more? + this.__stage = stage; + } + + // update the scene graph + stage.updateTransform(); + + var gl = this.gl; + + // interaction + if(stage._interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } + else + { + if(stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = false; + stage.interactionManager.setTarget(this); + } + } + + // -- Does this need to be set every frame? -- // + gl.viewport(0, 0, this.width, this.height); + + // make sure we are bound to the main frame buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + if (this.clearBeforeRender) + { + if(this.transparent) + { + gl.clearColor(0, 0, 0, 0); + } + else + { + gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + } + + gl.clear (gl.COLOR_BUFFER_BIT); + } + + this.renderDisplayObject( stage, this.projection ); +}; + +/** + * Renders a Display Object. + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The DisplayObject to render + * @param projection {Point} The projection + * @param buffer {Array} a standard WebGL buffer + */ +PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer) +{ + this.renderSession.blendModeManager.setBlendMode(PIXI.blendModes.NORMAL); + + // reset the render session data.. + this.renderSession.drawCount = 0; + + // make sure to flip the Y if using a render texture.. + this.renderSession.flipY = buffer ? -1 : 1; + + // set the default projection + this.renderSession.projection = projection; + + //set the default offset + this.renderSession.offset = this.offset; + + // start the sprite batch + this.spriteBatch.begin(this.renderSession); + + // start the filter manager + this.filterManager.begin(this.renderSession, buffer); + + // render the scene! + displayObject._renderWebGL(this.renderSession); + + // finish the sprite batch + this.spriteBatch.end(); +}; + +/** + * Resizes the webGL view to the specified width and height. + * + * @method resize + * @param width {Number} the new width of the webGL view + * @param height {Number} the new height of the webGL view + */ +PIXI.WebGLRenderer.prototype.resize = function(width, height) +{ + this.width = width * this.resolution; + this.height = height * this.resolution; + + this.view.width = this.width; + this.view.height = this.height; + + if (this.autoResize) { + this.view.style.width = this.width / this.resolution + 'px'; + this.view.style.height = this.height / this.resolution + 'px'; + } + + this.gl.viewport(0, 0, this.width, this.height); + + this.projection.x = this.width / 2 / this.resolution; + this.projection.y = -this.height / 2 / this.resolution; +}; + +/** + * Updates and Creates a WebGL texture for the renderers context. + * + * @method updateTexture + * @param texture {Texture} the texture to update + */ +PIXI.WebGLRenderer.prototype.updateTexture = function(texture) +{ + if(!texture.hasLoaded )return; + + var gl = this.gl; + + if(!texture._glTextures[gl.id])texture._glTextures[gl.id] = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + + + if(texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height)) + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + gl.generateMipmap(gl.TEXTURE_2D); + } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + } + + // reguler... + if(!texture._powerOf2) + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + texture._dirty[gl.id] = false; + + return texture._glTextures[gl.id]; +}; + +/** + * Handles a lost webgl context + * + * @method handleContextLost + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextLost = function(event) +{ + event.preventDefault(); + this.contextLost = true; +}; + +/** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextRestored = function() +{ + this.initContext(); + + // empty all the ol gl textures as they are useless now + for(var key in PIXI.TextureCache) + { + var texture = PIXI.TextureCache[key].baseTexture; + texture._glTextures = []; + } + + this.contextLost = false; +}; + +/** + * Removes everything from the renderer (event listeners, spritebatch, etc...) + * + * @method destroy + */ +PIXI.WebGLRenderer.prototype.destroy = function() +{ + // remove listeners + this.view.removeEventListener('webglcontextlost', this.contextLostBound); + this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); + + PIXI.glContexts[this.glContextId] = null; + + this.projection = null; + this.offset = null; + + // time to create the render managers! each one focuses on managine a state in webGL + this.shaderManager.destroy(); + this.spriteBatch.destroy(); + this.maskManager.destroy(); + this.filterManager.destroy(); + + this.shaderManager = null; + this.spriteBatch = null; + this.maskManager = null; + this.filterManager = null; + + this.gl = null; + this.renderSession = null; +}; + +/** + * Maps Pixi blend modes to WebGL blend modes. + * + * @method mapBlendModes + */ +PIXI.WebGLRenderer.prototype.mapBlendModes = function() +{ + var gl = this.gl; + + if(!PIXI.blendModesWebGL) + { + PIXI.blendModesWebGL = []; + + PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + PIXI.blendModesWebGL[PIXI.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + } +}; + +PIXI.WebGLRenderer.glContextId = 0; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/ComplexPrimitiveShader.js b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..bbd3259 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,122 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class ComplexPrimitiveShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.ComplexPrimitiveShader = function(gl) +{ + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * The WebGL program. + * @property program + * @type Any + */ + this.program = null; + + /** + * The fragment shader. + * @property fragmentSrc + * @type Array + */ + this.fragmentSrc = [ + + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ]; + + /** + * The vertex shader. + * @property vertexSrc + * @type Array + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + //'attribute vec4 aColor;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ]; + + this.init(); +}; + +PIXI.ComplexPrimitiveShader.prototype.constructor = PIXI.ComplexPrimitiveShader; + +/** +* Initialises the shader. +* +* @method init +*/ +PIXI.ComplexPrimitiveShader.prototype.init = function() +{ + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.tintColor = gl.getUniformLocation(program, 'tint'); + this.color = gl.getUniformLocation(program, 'color'); + this.flipY = gl.getUniformLocation(program, 'flipY'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + this.attributes = [this.aVertexPosition, this.colorAttribute]; + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** +* Destroys the shader. +* +* @method destroy +*/ +PIXI.ComplexPrimitiveShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attribute = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PixiFastShader.js b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PixiFastShader.js new file mode 100644 index 0000000..8e685e7 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PixiFastShader.js @@ -0,0 +1,155 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class PixiFastShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.PixiFastShader = function(gl) +{ + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * The WebGL program. + * @property program + * @type Any + */ + this.program = null; + + /** + * The fragment shader. + * @property fragmentSrc + * @type Array + */ + this.fragmentSrc = [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying float vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]; + + /** + * The vertex shader. + * @property vertexSrc + * @type Array + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying float vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ]; + + /** + * A local texture counter for multi-texture shaders. + * @property textureCount + * @type Number + */ + this.textureCount = 0; + + this.init(); +}; + +PIXI.PixiFastShader.prototype.constructor = PIXI.PixiFastShader; + +/** +* Initialises the shader. +* +* @method init +*/ +PIXI.PixiFastShader.prototype.init = function() +{ + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.dimensions = gl.getUniformLocation(program, 'dimensions'); + this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); + + this.aScale = gl.getAttribLocation(program, 'aScale'); + this.aRotation = gl.getAttribLocation(program, 'aRotation'); + + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its somthing to do with the current state of the gl context. + // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if(this.colorAttribute === -1) + { + this.colorAttribute = 2; + } + + this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; + + // End worst hack eva // + + this.program = program; +}; + +/** +* Destroys the shader. +* +* @method destroy +*/ +PIXI.PixiFastShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attributes = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PixiShader.js b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PixiShader.js new file mode 100644 index 0000000..e70be7f --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PixiShader.js @@ -0,0 +1,389 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Richard Davey http://www.photonstorm.com @photonstorm + */ + +/** +* @class PixiShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.PixiShader = function(gl) +{ + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * The WebGL program. + * @property program + * @type Any + */ + this.program = null; + + /** + * The fragment shader. + * @property fragmentSrc + * @type Array + */ + this.fragmentSrc = [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]; + + /** + * A local texture counter for multi-texture shaders. + * @property textureCount + * @type Number + */ + this.textureCount = 0; + + /** + * A local flag + * @property firstRun + * @type Boolean + * @private + */ + this.firstRun = true; + + /** + * A dirty flag + * @property dirty + * @type Boolean + */ + this.dirty = true; + + /** + * Uniform attributes cache. + * @property attributes + * @type Array + * @private + */ + this.attributes = []; + + this.init(); +}; + +PIXI.PixiShader.prototype.constructor = PIXI.PixiShader; + +/** +* Initialises the shader. +* +* @method init +*/ +PIXI.PixiShader.prototype.init = function() +{ + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc); + + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.dimensions = gl.getUniformLocation(program, 'dimensions'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if(this.colorAttribute === -1) + { + this.colorAttribute = 2; + } + + this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; + + // End worst hack eva // + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); + } + + this.initUniforms(); + + this.program = program; +}; + +/** +* Initialises the shader uniform values. +* +* Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ +* http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf +* +* @method initUniforms +*/ +PIXI.PixiShader.prototype.initUniforms = function() +{ + this.textureCount = 1; + var gl = this.gl; + var uniform; + + for (var key in this.uniforms) + { + uniform = this.uniforms[key]; + + var type = uniform.type; + + if (type === 'sampler2D') + { + uniform._init = false; + + if (uniform.value !== null) + { + this.initSampler2D(uniform); + } + } + else if (type === 'mat2' || type === 'mat3' || type === 'mat4') + { + // These require special handling + uniform.glMatrix = true; + uniform.glValueLength = 1; + + if (type === 'mat2') + { + uniform.glFunc = gl.uniformMatrix2fv; + } + else if (type === 'mat3') + { + uniform.glFunc = gl.uniformMatrix3fv; + } + else if (type === 'mat4') + { + uniform.glFunc = gl.uniformMatrix4fv; + } + } + else + { + // GL function reference + uniform.glFunc = gl['uniform' + type]; + + if (type === '2f' || type === '2i') + { + uniform.glValueLength = 2; + } + else if (type === '3f' || type === '3i') + { + uniform.glValueLength = 3; + } + else if (type === '4f' || type === '4i') + { + uniform.glValueLength = 4; + } + else + { + uniform.glValueLength = 1; + } + } + } + +}; + +/** +* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) +* +* @method initSampler2D +*/ +PIXI.PixiShader.prototype.initSampler2D = function(uniform) +{ + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + return; + } + + var gl = this.gl; + + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // Extended texture data + if (uniform.textureData) + { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) + { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) + { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else + { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } + + gl.uniform1i(uniform.uniformLocation, this.textureCount); + + uniform._init = true; + + this.textureCount++; + +}; + +/** +* Updates the shader uniform values. +* +* @method syncUniforms +*/ +PIXI.PixiShader.prototype.syncUniforms = function() +{ + this.textureCount = 1; + var uniform; + var gl = this.gl; + + // This would probably be faster in an array and it would guarantee key order + for (var key in this.uniforms) + { + uniform = this.uniforms[key]; + + if (uniform.glValueLength === 1) + { + if (uniform.glMatrix === true) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); + } + else + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); + } + } + else if (uniform.glValueLength === 2) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); + } + else if (uniform.glValueLength === 3) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); + } + else if (uniform.glValueLength === 4) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); + } + else if (uniform.type === 'sampler2D') + { + if (uniform._init) + { + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + if(uniform.value.baseTexture._dirty[gl.id]) + { + PIXI.instances[gl.id].updateTexture(uniform.value.baseTexture); + } + else + { + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + } + + // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl)); + gl.uniform1i(uniform.uniformLocation, this.textureCount); + this.textureCount++; + } + else + { + this.initSampler2D(uniform); + } + } + } + +}; + +/** +* Destroys the shader. +* +* @method destroy +*/ +PIXI.PixiShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attributes = null; +}; + +/** +* The Default Vertex shader source. +* +* @property defaultVertexSrc +* @type String +*/ +PIXI.PixiShader.defaultVertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' +]; \ No newline at end of file diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PrimitiveShader.js b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..bdaab89 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,117 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class PrimitiveShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.PrimitiveShader = function(gl) +{ + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * The WebGL program. + * @property program + * @type Any + */ + this.program = null; + + /** + * The fragment shader. + * @property fragmentSrc + * @type Array + */ + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ]; + + /** + * The vertex shader. + * @property vertexSrc + * @type Array + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ]; + + this.init(); +}; + +PIXI.PrimitiveShader.prototype.constructor = PIXI.PrimitiveShader; + +/** +* Initialises the shader. +* +* @method init +*/ +PIXI.PrimitiveShader.prototype.init = function() +{ + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.tintColor = gl.getUniformLocation(program, 'tint'); + this.flipY = gl.getUniformLocation(program, 'flipY'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + this.attributes = [this.aVertexPosition, this.colorAttribute]; + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** +* Destroys the shader. +* +* @method destroy +*/ +PIXI.PrimitiveShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attributes = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/StripShader.js b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/StripShader.js new file mode 100644 index 0000000..6b47244 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/shaders/StripShader.js @@ -0,0 +1,123 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class StripShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.StripShader = function(gl) +{ + /** + * @property _UID + * @type Number + * @private + */ + this._UID = PIXI._UID++; + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * The WebGL program. + * @property program + * @type Any + */ + this.program = null; + + /** + * The fragment shader. + * @property fragmentSrc + * @type Array + */ + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + // 'varying float vColor;', + 'uniform float alpha;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', + '}' + ]; + + /** + * The vertex shader. + * @property vertexSrc + * @type Array + */ + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + // 'uniform float alpha;', + // 'uniform vec3 tint;', + 'varying vec2 vTextureCoord;', + // 'varying vec4 vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ]; + + this.init(); +}; + +PIXI.StripShader.prototype.constructor = PIXI.StripShader; + +/** +* Initialises the shader. +* +* @method init +*/ +PIXI.StripShader.prototype.init = function() +{ + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + + this.attributes = [this.aVertexPosition, this.aTextureCoord]; + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** +* Destroys the shader. +* +* @method destroy +*/ +PIXI.StripShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attribute = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/FilterTexture.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/FilterTexture.js new file mode 100644 index 0000000..0340289 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/FilterTexture.js @@ -0,0 +1,110 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class FilterTexture +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @param width {Number} the horizontal range of the filter +* @param height {Number} the vertical range of the filter +* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values +*/ +PIXI.FilterTexture = function(gl, width, height, scaleMode) +{ + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + // next time to create a frame buffer and texture + + /** + * @property frameBuffer + * @type Any + */ + this.frameBuffer = gl.createFramebuffer(); + + /** + * @property texture + * @type Any + */ + this.texture = gl.createTexture(); + + /** + * @property scaleMode + * @type Number + */ + scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); + + // required for masking a mask?? + this.renderBuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer); + + this.resize(width, height); +}; + +PIXI.FilterTexture.prototype.constructor = PIXI.FilterTexture; + +/** +* Clears the filter texture. +* +* @method clear +*/ +PIXI.FilterTexture.prototype.clear = function() +{ + var gl = this.gl; + + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); +}; + +/** + * Resizes the texture to the specified width and height + * + * @method resize + * @param width {Number} the new width of the texture + * @param height {Number} the new height of the texture + */ +PIXI.FilterTexture.prototype.resize = function(width, height) +{ + if(this.width === width && this.height === height) return; + + this.width = width; + this.height = height; + + var gl = this.gl; + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height + gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); +}; + +/** +* Destroys the filter texture. +* +* @method destroy +*/ +PIXI.FilterTexture.prototype.destroy = function() +{ + var gl = this.gl; + gl.deleteFramebuffer( this.frameBuffer ); + gl.deleteTexture( this.texture ); + + this.frameBuffer = null; + this.texture = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLBlendModeManager.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLBlendModeManager.js new file mode 100644 index 0000000..3de0ec9 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLBlendModeManager.js @@ -0,0 +1,58 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLBlendModeManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLBlendModeManager = function() +{ + /** + * @property currentBlendMode + * @type Number + */ + this.currentBlendMode = 99999; +}; + +PIXI.WebGLBlendModeManager.prototype.constructor = PIXI.WebGLBlendModeManager; + +/** + * Sets the WebGL Context. + * + * @method setContext + * @param gl {WebGLContext} the current WebGL drawing context + */ +PIXI.WebGLBlendModeManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +/** +* Sets-up the given blendMode from WebGL's point of view. +* +* @method setBlendMode +* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD +*/ +PIXI.WebGLBlendModeManager.prototype.setBlendMode = function(blendMode) +{ + if(this.currentBlendMode === blendMode)return false; + + this.currentBlendMode = blendMode; + + var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; + this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + + return true; +}; + +/** +* Destroys this object. +* +* @method destroy +*/ +PIXI.WebGLBlendModeManager.prototype.destroy = function() +{ + this.gl = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js new file mode 100644 index 0000000..15ed7ae --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -0,0 +1,428 @@ +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * + * Heavily inspired by LibGDX's WebGLSpriteBatch: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java + */ + +/** +* @class WebGLFastSpriteBatch +* @constructor +*/ +PIXI.WebGLFastSpriteBatch = function(gl) +{ + /** + * @property vertSize + * @type Number + */ + this.vertSize = 10; + + /** + * @property maxSize + * @type Number + */ + this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize; + + /** + * @property size + * @type Number + */ + this.size = this.maxSize; + + //the total number of floats in our batch + var numVerts = this.size * 4 * this.vertSize; + + //the total number of indices in our batch + var numIndices = this.maxSize * 6; + + /** + * Vertex data + * @property vertices + * @type Float32Array + */ + this.vertices = new PIXI.Float32Array(numVerts); + + /** + * Index data + * @property indices + * @type Uint16Array + */ + this.indices = new PIXI.Uint16Array(numIndices); + + /** + * @property vertexBuffer + * @type Object + */ + this.vertexBuffer = null; + + /** + * @property indexBuffer + * @type Object + */ + this.indexBuffer = null; + + /** + * @property lastIndexCount + * @type Number + */ + this.lastIndexCount = 0; + + for (var i=0, j=0; i < numIndices; i += 6, j += 4) + { + this.indices[i + 0] = j + 0; + this.indices[i + 1] = j + 1; + this.indices[i + 2] = j + 2; + this.indices[i + 3] = j + 0; + this.indices[i + 4] = j + 2; + this.indices[i + 5] = j + 3; + } + + /** + * @property drawing + * @type Boolean + */ + this.drawing = false; + + /** + * @property currentBatchSize + * @type Number + */ + this.currentBatchSize = 0; + + /** + * @property currentBaseTexture + * @type BaseTexture + */ + this.currentBaseTexture = null; + + /** + * @property currentBlendMode + * @type Number + */ + this.currentBlendMode = 0; + + /** + * @property renderSession + * @type Object + */ + this.renderSession = null; + + /** + * @property shader + * @type Object + */ + this.shader = null; + + /** + * @property matrix + * @type Matrix + */ + this.matrix = null; + + this.setContext(gl); +}; + +PIXI.WebGLFastSpriteBatch.prototype.constructor = PIXI.WebGLFastSpriteBatch; + +/** + * Sets the WebGL Context. + * + * @method setContext + * @param gl {WebGLContext} the current WebGL drawing context + */ +PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl) +{ + this.gl = gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // 65535 is max index, so 65535 / 6 = 10922. + + //upload the index data + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); +}; + +/** + * @method begin + * @param spriteBatch {WebGLSpriteBatch} + * @param renderSession {Object} + */ +PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession) +{ + this.renderSession = renderSession; + this.shader = this.renderSession.shaderManager.fastShader; + + this.matrix = spriteBatch.worldTransform.toArray(true); + + this.start(); +}; + +/** + * @method end + */ +PIXI.WebGLFastSpriteBatch.prototype.end = function() +{ + this.flush(); +}; + +/** + * @method render + * @param spriteBatch {WebGLSpriteBatch} + */ +PIXI.WebGLFastSpriteBatch.prototype.render = function(spriteBatch) +{ + var children = spriteBatch.children; + var sprite = children[0]; + + // if the uvs have not updated then no point rendering just yet! + + // check texture. + if(!sprite.texture._uvs)return; + + this.currentBaseTexture = sprite.texture.baseTexture; + + // check blend mode + if(sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) + { + this.flush(); + this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + } + + for(var i=0,j= children.length; i= this.size) + { + this.flush(); + } +}; + +/** + * @method flush + */ +PIXI.WebGLFastSpriteBatch.prototype.flush = function() +{ + // If the batch is length 0 then return as there is nothing to draw + if (this.currentBatchSize===0)return; + + var gl = this.gl; + + // bind the current texture + + if(!this.currentBaseTexture._glTextures[gl.id])this.renderSession.renderer.updateTexture(this.currentBaseTexture, gl); + + gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id]); + + // upload the verts to the buffer + + if(this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + } + else + { + var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); + + // then reset the batch! + this.currentBatchSize = 0; + + // increment the draw count + this.renderSession.drawCount++; +}; + + +/** + * @method stop + */ +PIXI.WebGLFastSpriteBatch.prototype.stop = function() +{ + this.flush(); +}; + +/** + * @method start + */ +PIXI.WebGLFastSpriteBatch.prototype.start = function() +{ + var gl = this.gl; + + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); + + // set the matrix + gl.uniformMatrix3fv(this.shader.uMatrix, false, this.matrix); + + // set the pointers + var stride = this.vertSize * 4; + + gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(this.shader.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4); + gl.vertexAttribPointer(this.shader.aScale, 2, gl.FLOAT, false, stride, 4 * 4); + gl.vertexAttribPointer(this.shader.aRotation, 1, gl.FLOAT, false, stride, 6 * 4); + gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4); + gl.vertexAttribPointer(this.shader.colorAttribute, 1, gl.FLOAT, false, stride, 9 * 4); + +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLFilterManager.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLFilterManager.js new file mode 100644 index 0000000..ddc4691 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLFilterManager.js @@ -0,0 +1,450 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLFilterManager +* @constructor +*/ +PIXI.WebGLFilterManager = function() +{ + /** + * @property filterStack + * @type Array + */ + this.filterStack = []; + + /** + * @property offsetX + * @type Number + */ + this.offsetX = 0; + + /** + * @property offsetY + * @type Number + */ + this.offsetY = 0; +}; + +PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager; + +/** +* Initialises the context and the properties. +* +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLFilterManager.prototype.setContext = function(gl) +{ + this.gl = gl; + this.texturePool = []; + + this.initShaderBuffers(); +}; + +/** +* @method begin +* @param renderSession {RenderSession} +* @param buffer {ArrayBuffer} +*/ +PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer) +{ + this.renderSession = renderSession; + this.defaultShader = renderSession.shaderManager.defaultShader; + + var projection = this.renderSession.projection; + this.width = projection.x * 2; + this.height = -projection.y * 2; + this.buffer = buffer; +}; + +/** +* Applies the filter and adds it to the current filter stack. +* +* @method pushFilter +* @param filterBlock {Object} the filter that will be pushed to the current filter stack +*/ +PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) +{ + var gl = this.gl; + + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; + + filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); + + // filter program + // OPTIMISATION - the first filter is free if its a simple color change? + this.filterStack.push(filterBlock); + + var filter = filterBlock.filterPasses[0]; + + this.offsetX += filterBlock._filterArea.x; + this.offsetY += filterBlock._filterArea.y; + + var texture = this.texturePool.pop(); + if(!texture) + { + texture = new PIXI.FilterTexture(this.gl, this.width, this.height); + } + else + { + texture.resize(this.width, this.height); + } + + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + + var padding = filter.padding; + filterArea.x -= padding; + filterArea.y -= padding; + filterArea.width += padding * 2; + filterArea.height += padding * 2; + + // cap filter to screen size.. + if(filterArea.x < 0)filterArea.x = 0; + if(filterArea.width > this.width)filterArea.width = this.width; + if(filterArea.y < 0)filterArea.y = 0; + if(filterArea.height > this.height)filterArea.height = this.height; + + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + + // set view port + gl.viewport(0, 0, filterArea.width, filterArea.height); + + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; + + offset.x = -filterArea.x; + offset.y = -filterArea.y; + + // update projection + // now restore the regular shader.. + // this.renderSession.shaderManager.setShader(this.defaultShader); + //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + + gl.colorMask(true, true, true, true); + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + filterBlock._glFilterTexture = texture; + +}; + +/** +* Removes the last filter from the filter stack and doesn't return it. +* +* @method popFilter +*/ +PIXI.WebGLFilterManager.prototype.popFilter = function() +{ + var gl = this.gl; + var filterBlock = this.filterStack.pop(); + var filterArea = filterBlock._filterArea; + var texture = filterBlock._glFilterTexture; + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; + + if(filterBlock.filterPasses.length > 1) + { + gl.viewport(0, 0, filterArea.width, filterArea.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = 0; + this.vertexArray[1] = filterArea.height; + + this.vertexArray[2] = filterArea.width; + this.vertexArray[3] = filterArea.height; + + this.vertexArray[4] = 0; + this.vertexArray[5] = 0; + + this.vertexArray[6] = filterArea.width; + this.vertexArray[7] = 0; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + // now set the uvs.. + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + var inputTexture = texture; + var outputTexture = this.texturePool.pop(); + if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height); + outputTexture.resize(this.width, this.height); + + // need to clear this FBO as it may have some left over elements from a previous filter. + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + for (var i = 0; i < filterBlock.filterPasses.length-1; i++) + { + var filterPass = filterBlock.filterPasses[i]; + + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); + + // draw texture.. + //filterPass.applyFilterPass(filterArea.width, filterArea.height); + this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); + + // swap the textures.. + var temp = inputTexture; + inputTexture = outputTexture; + outputTexture = temp; + } + + gl.enable(gl.BLEND); + + texture = inputTexture; + this.texturePool.push(outputTexture); + } + + var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; + + this.offsetX -= filterArea.x; + this.offsetY -= filterArea.y; + + var sizeX = this.width; + var sizeY = this.height; + + var offsetX = 0; + var offsetY = 0; + + var buffer = this.buffer; + + // time to render the filters texture to the previous scene + if(this.filterStack.length === 0) + { + gl.colorMask(true, true, true, true);//this.transparent); + } + else + { + var currentFilter = this.filterStack[this.filterStack.length-1]; + filterArea = currentFilter._filterArea; + + sizeX = filterArea.width; + sizeY = filterArea.height; + + offsetX = filterArea.x; + offsetY = filterArea.y; + + buffer = currentFilter._glFilterTexture.frameBuffer; + } + + // TODO need to remove these global elements.. + projection.x = sizeX/2; + projection.y = -sizeY/2; + + offset.x = offsetX; + offset.y = offsetY; + + filterArea = filterBlock._filterArea; + + var x = filterArea.x-offsetX; + var y = filterArea.y-offsetY; + + // update the buffers.. + // make sure to flip the y! + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = x; + this.vertexArray[1] = y + filterArea.height; + + this.vertexArray[2] = x + filterArea.width; + this.vertexArray[3] = y + filterArea.height; + + this.vertexArray[4] = x; + this.vertexArray[5] = y; + + this.vertexArray[6] = x + filterArea.width; + this.vertexArray[7] = y; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + gl.viewport(0, 0, sizeX, sizeY); + + // bind the buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + // apply! + this.applyFilterPass(filter, filterArea, sizeX, sizeY); + + // now restore the regular shader.. should happen automatically now.. + // this.renderSession.shaderManager.setShader(this.defaultShader); + // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + + // return the texture to the pool + this.texturePool.push(texture); + filterBlock._glFilterTexture = null; +}; + + +/** +* Applies the filter to the specified area. +* +* @method applyFilterPass +* @param filter {AbstractFilter} the filter that needs to be applied +* @param filterArea {Texture} TODO - might need an update +* @param width {Number} the horizontal range of the filter +* @param height {Number} the vertical range of the filter +*/ +PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height) +{ + // use program + var gl = this.gl; + var shader = filter.shaders[gl.id]; + + if(!shader) + { + shader = new PIXI.PixiShader(gl); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shaders[gl.id] = shader; + } + + // set the shader + this.renderSession.shaderManager.setShader(shader); + +// gl.useProgram(shader.program); + + gl.uniform2f(shader.projectionVector, width/2, -height/2); + gl.uniform2f(shader.offsetVector, 0,0); + + if(filter.uniforms.dimensions) + { + filter.uniforms.dimensions.value[0] = this.width;//width; + filter.uniforms.dimensions.value[1] = this.height;//height; + filter.uniforms.dimensions.value[2] = this.vertexArray[0]; + filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + } + + shader.syncUniforms(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderSession.drawCount++; +}; + +/** +* Initialises the shader buffers. +* +* @method initShaderBuffers +*/ +PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() +{ + var gl = this.gl; + + // create some buffers + this.vertexBuffer = gl.createBuffer(); + this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // bind and upload the vertexs.. + // keep a reference to the vertexFloatData.. + this.vertexArray = new PIXI.Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); + + // bind and upload the uv buffer + this.uvArray = new PIXI.Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); + + this.colorArray = new PIXI.Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); + + // bind and upload the index + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); + +}; + +/** +* Destroys the filter and removes it from the filter stack. +* +* @method destroy +*/ +PIXI.WebGLFilterManager.prototype.destroy = function() +{ + var gl = this.gl; + + this.filterStack = null; + + this.offsetX = 0; + this.offsetY = 0; + + // destroy textures + for (var i = 0; i < this.texturePool.length; i++) { + this.texturePool[i].destroy(); + } + + this.texturePool = null; + + //destroy buffers.. + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.uvBuffer); + gl.deleteBuffer(this.colorBuffer); + gl.deleteBuffer(this.indexBuffer); +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLGraphics.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLGraphics.js new file mode 100644 index 0000000..710383c --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLGraphics.js @@ -0,0 +1,896 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A set of functions used by the webGL renderer to draw the primitive graphics data + * + * @class WebGLGraphics + * @private + * @static + */ +PIXI.WebGLGraphics = function() +{ +}; + +/** + * Renders the graphics object + * + * @static + * @private + * @method renderGraphics + * @param graphics {Graphics} + * @param renderSession {Object} + */ +PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projection, offset) +{ + var gl = renderSession.gl; + var projection = renderSession.projection, + offset = renderSession.offset, + shader = renderSession.shaderManager.primitiveShader, + webGLData; + + if(graphics.dirty) + { + PIXI.WebGLGraphics.updateGraphics(graphics, gl); + } + + var webGL = graphics._webGL[gl.id]; + + // This could be speeded up for sure! + + for (var i = 0; i < webGL.data.length; i++) + { + if(webGL.data[i].mode === 1) + { + webGLData = webGL.data[i]; + + renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + + // render quad.. + gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); + + renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + } + else + { + webGLData = webGL.data[i]; + + + renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderSession.shaderManager.primitiveShader; + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + + gl.uniform1f(shader.flipY, 1); + + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + + gl.uniform1f(shader.alpha, graphics.worldAlpha); + + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); + } + } +}; + +/** + * Updates the graphics object + * + * @static + * @private + * @method updateGraphics + * @param graphicsData {Graphics} The graphics object to update + * @param gl {WebGLContext} the current WebGL drawing context + */ +PIXI.WebGLGraphics.updateGraphics = function(graphics, gl) +{ + // get the contexts graphics object + var webGL = graphics._webGL[gl.id]; + // if the graphics object does not exist in the webGL context time to create it! + if(!webGL)webGL = graphics._webGL[gl.id] = {lastIndex:0, data:[], gl:gl}; + + // flag the graphics as not dirty as we are about to update it... + graphics.dirty = false; + + var i; + + // if the user cleared the graphics object we will need to clear every object + if(graphics.clearDirty) + { + graphics.clearDirty = false; + + // lop through and return all the webGLDatas to the object pool so than can be reused later on + for (i = 0; i < webGL.data.length; i++) + { + var graphicsData = webGL.data[i]; + graphicsData.reset(); + PIXI.WebGLGraphics.graphicsDataPool.push( graphicsData ); + } + + // clear the array and reset the index.. + webGL.data = []; + webGL.lastIndex = 0; + } + + var webGLData; + + // loop through the graphics datas and construct each one.. + // if the object is a complex fill then the new stencil buffer technique will be used + // other wise graphics objects will be pushed into a batch.. + for (i = webGL.lastIndex; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + if(data.type === PIXI.Graphics.POLY) + { + // need to add the points the the graphics object.. + data.points = data.shape.points.slice(); + if(data.shape.closed) + { + // close the poly if the value is true! + if(data.points[0] !== data.points[data.points.length-2] || data.points[1] !== data.points[data.points.length-1]) + { + data.points.push(data.points[0], data.points[1]); + } + } + + // MAKE SURE WE HAVE THE CORRECT TYPE.. + if(data.fill) + { + if(data.points.length >= 6) + { + if(data.points.length < 6 * 2) + { + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); + + var canDrawUsingSimple = PIXI.WebGLGraphics.buildPoly(data, webGLData); + // console.log(canDrawUsingSimple); + + if(!canDrawUsingSimple) + { + // console.log("<>>>") + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); + PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); + } + + } + else + { + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); + PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); + } + } + } + + if(data.lineWidth > 0) + { + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); + PIXI.WebGLGraphics.buildLine(data, webGLData); + + } + } + else + { + webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); + + if(data.type === PIXI.Graphics.RECT) + { + PIXI.WebGLGraphics.buildRectangle(data, webGLData); + } + else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) + { + PIXI.WebGLGraphics.buildCircle(data, webGLData); + } + else if(data.type === PIXI.Graphics.RREC) + { + PIXI.WebGLGraphics.buildRoundedRectangle(data, webGLData); + } + } + + webGL.lastIndex++; + } + + // upload all the dirty data... + for (i = 0; i < webGL.data.length; i++) + { + webGLData = webGL.data[i]; + if(webGLData.dirty)webGLData.upload(); + } +}; + +/** + * @static + * @private + * @method switchMode + * @param webGL {WebGLContext} + * @param type {Number} + */ +PIXI.WebGLGraphics.switchMode = function(webGL, type) +{ + var webGLData; + + if(!webGL.data.length) + { + webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); + webGLData.mode = type; + webGL.data.push(webGLData); + } + else + { + webGLData = webGL.data[webGL.data.length-1]; + + if(webGLData.mode !== type || type === 1) + { + webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); + webGLData.mode = type; + webGL.data.push(webGLData); + } + } + + webGLData.dirty = true; + + return webGLData; +}; + +/** + * Builds a rectangle to draw + * + * @static + * @private + * @method buildRectangle + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) +{ + // --- // + // need to convert points to a nice regular data + // + var rectData = graphicsData.shape; + var x = rectData.x; + var y = rectData.y; + var width = rectData.width; + var height = rectData.height; + + if(graphicsData.fill) + { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vertPos = verts.length/6; + + // start + verts.push(x, y); + verts.push(r, g, b, alpha); + + verts.push(x + width, y); + verts.push(r, g, b, alpha); + + verts.push(x , y + height); + verts.push(r, g, b, alpha); + + verts.push(x + width, y + height); + verts.push(r, g, b, alpha); + + // insert 2 dead triangles.. + indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3); + } + + if(graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = [x, y, + x + width, y, + x + width, y + height, + x, y + height, + x, y]; + + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a rounded rectangle to draw + * + * @static + * @private + * @method buildRoundedRectangle + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildRoundedRectangle = function(graphicsData, webGLData) +{ + var rrectData = graphicsData.shape; + var x = rrectData.x; + var y = rrectData.y; + var width = rrectData.width; + var height = rrectData.height; + + var radius = rrectData.radius; + + var recPoints = []; + recPoints.push(x, y + radius); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x, y + height - radius, x, y + height, x + radius, y + height)); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width - radius, y + height, x + width, y + height, x + width, y + height - radius)); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width, y + radius, x + width, y, x + width - radius, y)); + recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + radius, y, x, y, x, y + radius)); + + if (graphicsData.fill) { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + var triangles = PIXI.PolyK.Triangulate(recPoints); + + // + + var i = 0; + for (i = 0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vecPos); + indices.push(triangles[i] + vecPos); + indices.push(triangles[i+1] + vecPos); + indices.push(triangles[i+2] + vecPos); + indices.push(triangles[i+2] + vecPos); + } + + + for (i = 0; i < recPoints.length; i++) + { + verts.push(recPoints[i], recPoints[++i], r, g, b, alpha); + } + } + + if (graphicsData.lineWidth) { + var tempPoints = graphicsData.points; + + graphicsData.points = recPoints; + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Calculate the points for a quadratic bezier curve. (helper function..) + * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c + * + * @static + * @private + * @method quadraticBezierCurve + * @param fromX {Number} Origin point x + * @param fromY {Number} Origin point x + * @param cpX {Number} Control point x + * @param cpY {Number} Control point y + * @param toX {Number} Destination point x + * @param toY {Number} Destination point y + * @return {Array(Number)} + */ +PIXI.WebGLGraphics.quadraticBezierCurve = function(fromX, fromY, cpX, cpY, toX, toY) { + + var xa, + ya, + xb, + yb, + x, + y, + n = 20, + points = []; + + function getPt(n1 , n2, perc) { + var diff = n2 - n1; + + return n1 + ( diff * perc ); + } + + var j = 0; + for (var i = 0; i <= n; i++ ) + { + j = i / n; + + // The Green Line + xa = getPt( fromX , cpX , j ); + ya = getPt( fromY , cpY , j ); + xb = getPt( cpX , toX , j ); + yb = getPt( cpY , toY , j ); + + // The Black Dot + x = getPt( xa , xb , j ); + y = getPt( ya , yb , j ); + + points.push(x, y); + } + return points; +}; + +/** + * Builds a circle to draw + * + * @static + * @private + * @method buildCircle + * @param graphicsData {Graphics} The graphics object to draw + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) +{ + // need to convert points to a nice regular data + var circleData = graphicsData.shape; + var x = circleData.x; + var y = circleData.y; + var width; + var height; + + // TODO - bit hacky?? + if(graphicsData.type === PIXI.Graphics.CIRC) + { + width = circleData.radius; + height = circleData.radius; + } + else + { + width = circleData.width; + height = circleData.height; + } + + var totalSegs = 40; + var seg = (Math.PI * 2) / totalSegs ; + + var i = 0; + + if(graphicsData.fill) + { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + indices.push(vecPos); + + for (i = 0; i < totalSegs + 1 ; i++) + { + verts.push(x,y, r, g, b, alpha); + + verts.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height, + r, g, b, alpha); + + indices.push(vecPos++, vecPos++); + } + + indices.push(vecPos-1); + } + + if(graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = []; + + for (i = 0; i < totalSegs + 1; i++) + { + graphicsData.points.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height); + } + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a line to draw + * + * @static + * @private + * @method buildLine + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) +{ + // TODO OPTIMISE! + var i = 0; + var points = graphicsData.points; + if(points.length === 0)return; + + // if the line width is an odd number add 0.5 to align to a whole pixel + if(graphicsData.lineWidth%2) + { + for (i = 0; i < points.length; i++) { + points[i] += 0.5; + } + } + + // get first and last point.. figure out the middle! + var firstPoint = new PIXI.Point( points[0], points[1] ); + var lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); + + // if the first point is the last point - gonna have issues :) + if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) + { + // need to clone as we are going to slightly modify the shape.. + points = points.slice(); + + points.pop(); + points.pop(); + + lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); + + var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; + var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; + + points.unshift(midPointX, midPointY); + points.push(midPointX, midPointY); + } + + var verts = webGLData.points; + var indices = webGLData.indices; + var length = points.length / 2; + var indexCount = points.length; + var indexStart = verts.length/6; + + // DRAW the Line + var width = graphicsData.lineWidth / 2; + + // sort color + var color = PIXI.hex2rgb(graphicsData.lineColor); + var alpha = graphicsData.lineAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var px, py, p1x, p1y, p2x, p2y, p3x, p3y; + var perpx, perpy, perp2x, perp2y, perp3x, perp3y; + var a1, b1, c1, a2, b2, c2; + var denom, pdist, dist; + + p1x = points[0]; + p1y = points[1]; + + p2x = points[2]; + p2y = points[3]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + // start + verts.push(p1x - perpx , p1y - perpy, + r, g, b, alpha); + + verts.push(p1x + perpx , p1y + perpy, + r, g, b, alpha); + + for (i = 1; i < length-1; i++) + { + p1x = points[(i-1)*2]; + p1y = points[(i-1)*2 + 1]; + + p2x = points[(i)*2]; + p2y = points[(i)*2 + 1]; + + p3x = points[(i+1)*2]; + p3y = points[(i+1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + perp2x = -(p2y - p3y); + perp2y = p2x - p3x; + + dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); + perp2x /= dist; + perp2y /= dist; + perp2x *= width; + perp2y *= width; + + a1 = (-perpy + p1y) - (-perpy + p2y); + b1 = (-perpx + p2x) - (-perpx + p1x); + c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); + a2 = (-perp2y + p3y) - (-perp2y + p2y); + b2 = (-perp2x + p2x) - (-perp2x + p3x); + c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); + + denom = a1*b2 - a2*b1; + + if(Math.abs(denom) < 0.1 ) + { + + denom+=10.1; + verts.push(p2x - perpx , p2y - perpy, + r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy, + r, g, b, alpha); + + continue; + } + + px = (b1*c2 - b2*c1)/denom; + py = (a2*c1 - a1*c2)/denom; + + + pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); + + + if(pdist > 140 * 140) + { + perp3x = perpx - perp2x; + perp3y = perpy - perp2y; + + dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); + perp3x /= dist; + perp3y /= dist; + perp3x *= width; + perp3y *= width; + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x + perp3x, p2y +perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + indexCount++; + } + else + { + + verts.push(px , py); + verts.push(r, g, b, alpha); + + verts.push(p2x - (px-p2x), p2y - (py - p2y)); + verts.push(r, g, b, alpha); + } + } + + p1x = points[(length-2)*2]; + p1y = points[(length-2)*2 + 1]; + + p2x = points[(length-1)*2]; + p2y = points[(length-1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + verts.push(p2x - perpx , p2y - perpy); + verts.push(r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy); + verts.push(r, g, b, alpha); + + indices.push(indexStart); + + for (i = 0; i < indexCount; i++) + { + indices.push(indexStart++); + } + + indices.push(indexStart-1); +}; + +/** + * Builds a complex polygon to draw + * + * @static + * @private + * @method buildComplexPoly + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildComplexPoly = function(graphicsData, webGLData) +{ + //TODO - no need to copy this as it gets turned into a FLoat32Array anyways.. + var points = graphicsData.points.slice(); + if(points.length < 6)return; + + // get first and last point.. figure out the middle! + var indices = webGLData.indices; + webGLData.points = points; + webGLData.alpha = graphicsData.fillAlpha; + webGLData.color = PIXI.hex2rgb(graphicsData.fillColor); + + /* + calclate the bounds.. + */ + var minX = Infinity; + var maxX = -Infinity; + + var minY = Infinity; + var maxY = -Infinity; + + var x,y; + + // get size.. + for (var i = 0; i < points.length; i+=2) + { + x = points[i]; + y = points[i+1]; + + minX = x < minX ? x : minX; + maxX = x > maxX ? x : maxX; + + minY = y < minY ? y : minY; + maxY = y > maxY ? y : maxY; + } + + // add a quad to the end cos there is no point making another buffer! + points.push(minX, minY, + maxX, minY, + maxX, maxY, + minX, maxY); + + // push a quad onto the end.. + + //TODO - this aint needed! + var length = points.length / 2; + for (i = 0; i < length; i++) + { + indices.push( i ); + } + +}; + +/** + * Builds a polygon to draw + * + * @static + * @private + * @method buildPoly + * @param graphicsData {Graphics} The graphics object containing all the necessary properties + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) +{ + var points = graphicsData.points; + + if(points.length < 6)return; + // get first and last point.. figure out the middle! + var verts = webGLData.points; + var indices = webGLData.indices; + + var length = points.length / 2; + + // sort color + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var triangles = PIXI.PolyK.Triangulate(points); + + if(!triangles)return false; + + var vertPos = verts.length / 6; + + var i = 0; + + for (i = 0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vertPos); + indices.push(triangles[i] + vertPos); + indices.push(triangles[i+1] + vertPos); + indices.push(triangles[i+2] +vertPos); + indices.push(triangles[i+2] + vertPos); + } + + for (i = 0; i < length; i++) + { + verts.push(points[i * 2], points[i * 2 + 1], + r, g, b, alpha); + } + + return true; +}; + +PIXI.WebGLGraphics.graphicsDataPool = []; + +/** + * @class WebGLGraphicsData + * @private + * @static + */ +PIXI.WebGLGraphicsData = function(gl) +{ + this.gl = gl; + + //TODO does this need to be split before uploding?? + this.color = [0,0,0]; // color split! + this.points = []; + this.indices = []; + this.buffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + this.mode = 1; + this.alpha = 1; + this.dirty = true; +}; + +/** + * @method reset + */ +PIXI.WebGLGraphicsData.prototype.reset = function() +{ + this.points = []; + this.indices = []; +}; + +/** + * @method upload + */ +PIXI.WebGLGraphicsData.prototype.upload = function() +{ + var gl = this.gl; + +// this.lastIndex = graphics.graphicsData.length; + this.glPoints = new PIXI.Float32Array(this.points); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); + gl.bufferData(gl.ARRAY_BUFFER, this.glPoints, gl.STATIC_DRAW); + + this.glIndicies = new PIXI.Uint16Array(this.indices); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.glIndicies, gl.STATIC_DRAW); + + this.dirty = false; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLMaskManager.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLMaskManager.js new file mode 100644 index 0000000..607d5dd --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLMaskManager.js @@ -0,0 +1,69 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLMaskManager +* @constructor +* @private +*/ +PIXI.WebGLMaskManager = function() +{ +}; + +PIXI.WebGLMaskManager.prototype.constructor = PIXI.WebGLMaskManager; + +/** +* Sets the drawing context to the one given in parameter. +* +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLMaskManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +/** +* Applies the Mask and adds it to the current filter stack. +* +* @method pushMask +* @param maskData {Array} +* @param renderSession {Object} +*/ +PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession) +{ + var gl = renderSession.gl; + + if(maskData.dirty) + { + PIXI.WebGLGraphics.updateGraphics(maskData, gl); + } + + if(!maskData._webGL[gl.id].data.length)return; + + renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); +}; + +/** +* Removes the last filter from the filter stack and doesn't return it. +* +* @method popMask +* @param maskData {Array} +* @param renderSession {Object} an object containing all the useful parameters +*/ +PIXI.WebGLMaskManager.prototype.popMask = function(maskData, renderSession) +{ + var gl = this.gl; + renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); +}; + +/** +* Destroys the mask stack. +* +* @method destroy +*/ +PIXI.WebGLMaskManager.prototype.destroy = function() +{ + this.gl = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLShaderManager.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLShaderManager.js new file mode 100644 index 0000000..a15974e --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLShaderManager.js @@ -0,0 +1,157 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLShaderManager +* @constructor +* @private +*/ +PIXI.WebGLShaderManager = function() +{ + /** + * @property maxAttibs + * @type Number + */ + this.maxAttibs = 10; + + /** + * @property attribState + * @type Array + */ + this.attribState = []; + + /** + * @property tempAttribState + * @type Array + */ + this.tempAttribState = []; + + for (var i = 0; i < this.maxAttibs; i++) + { + this.attribState[i] = false; + } + + /** + * @property stack + * @type Array + */ + this.stack = []; + +}; + +PIXI.WebGLShaderManager.prototype.constructor = PIXI.WebGLShaderManager; + +/** +* Initialises the context and the properties. +* +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLShaderManager.prototype.setContext = function(gl) +{ + this.gl = gl; + + // the next one is used for rendering primitives + this.primitiveShader = new PIXI.PrimitiveShader(gl); + + // the next one is used for rendering triangle strips + this.complexPrimitiveShader = new PIXI.ComplexPrimitiveShader(gl); + + // this shader is used for the default sprite rendering + this.defaultShader = new PIXI.PixiShader(gl); + + // this shader is used for the fast sprite rendering + this.fastShader = new PIXI.PixiFastShader(gl); + + // the next one is used for rendering triangle strips + this.stripShader = new PIXI.StripShader(gl); + this.setShader(this.defaultShader); +}; + +/** +* Takes the attributes given in parameters. +* +* @method setAttribs +* @param attribs {Array} attribs +*/ +PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs) +{ + // reset temp state + var i; + + for (i = 0; i < this.tempAttribState.length; i++) + { + this.tempAttribState[i] = false; + } + + // set the new attribs + for (i = 0; i < attribs.length; i++) + { + var attribId = attribs[i]; + this.tempAttribState[attribId] = true; + } + + var gl = this.gl; + + for (i = 0; i < this.attribState.length; i++) + { + if(this.attribState[i] !== this.tempAttribState[i]) + { + this.attribState[i] = this.tempAttribState[i]; + + if(this.tempAttribState[i]) + { + gl.enableVertexAttribArray(i); + } + else + { + gl.disableVertexAttribArray(i); + } + } + } +}; + +/** +* Sets the current shader. +* +* @method setShader +* @param shader {Any} +*/ +PIXI.WebGLShaderManager.prototype.setShader = function(shader) +{ + if(this._currentId === shader._UID)return false; + + this._currentId = shader._UID; + + this.currentShader = shader; + + this.gl.useProgram(shader.program); + this.setAttribs(shader.attributes); + + return true; +}; + +/** +* Destroys this object. +* +* @method destroy +*/ +PIXI.WebGLShaderManager.prototype.destroy = function() +{ + this.attribState = null; + + this.tempAttribState = null; + + this.primitiveShader.destroy(); + + this.complexPrimitiveShader.destroy(); + + this.defaultShader.destroy(); + + this.fastShader.destroy(); + + this.stripShader.destroy(); + + this.gl = null; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLShaderUtils.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLShaderUtils.js new file mode 100644 index 0000000..a7d7826 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLShaderUtils.js @@ -0,0 +1,88 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @method initDefaultShaders +* @static +* @private +*/ +PIXI.initDefaultShaders = function() +{ +}; + +/** +* @method CompileVertexShader +* @static +* @param gl {WebGLContext} the current WebGL drawing context +* @param shaderSrc {Array} +* @return {Any} +*/ +PIXI.CompileVertexShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); +}; + +/** +* @method CompileFragmentShader +* @static +* @param gl {WebGLContext} the current WebGL drawing context +* @param shaderSrc {Array} +* @return {Any} +*/ +PIXI.CompileFragmentShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); +}; + +/** +* @method _CompileShader +* @static +* @private +* @param gl {WebGLContext} the current WebGL drawing context +* @param shaderSrc {Array} +* @param shaderType {Number} +* @return {Any} +*/ +PIXI._CompileShader = function(gl, shaderSrc, shaderType) +{ + var src = shaderSrc.join("\n"); + var shader = gl.createShader(shaderType); + gl.shaderSource(shader, src); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) + { + window.console.log(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; + +/** +* @method compileProgram +* @static +* @param gl {WebGLContext} the current WebGL drawing context +* @param vertexSrc {Array} +* @param fragmentSrc {Array} +* @return {Any} +*/ +PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) +{ + var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); + var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc); + + var shaderProgram = gl.createProgram(); + + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) + { + window.console.log("Could not initialise shaders"); + } + + return shaderProgram; +}; diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js new file mode 100755 index 0000000..711b4da --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js @@ -0,0 +1,635 @@ +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's WebGLSpriteBatch: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java + */ + + /** + * + * @class WebGLSpriteBatch + * @private + * @constructor + */ +PIXI.WebGLSpriteBatch = function() +{ + /** + * @property vertSize + * @type Number + */ + this.vertSize = 5; + + /** + * The number of images in the SpriteBatch before it flushes + * @property size + * @type Number + */ + this.size = 2000;//Math.pow(2, 16) / this.vertSize; + + //the total number of bytes in our batch + var numVerts = this.size * 4 * 4 * this.vertSize; + //the total number of indices in our batch + var numIndices = this.size * 6; + + /** + * Holds the vertices + * + * @property vertices + * @type ArrayBuffer + */ + this.vertices = new PIXI.ArrayBuffer(numVerts); + + /** + * View on the vertices as a Float32Array + * + * @property positions + * @type Float32Array + */ + this.positions = new PIXI.Float32Array(this.vertices); + + /** + * View on the vertices as a Uint32Array + * + * @property colors + * @type Uint32Array + */ + this.colors = new PIXI.Uint32Array(this.vertices); + + /** + * Holds the indices + * + * @property indices + * @type Uint16Array + */ + this.indices = new PIXI.Uint16Array(numIndices); + + /** + * @property lastIndexCount + * @type Number + */ + this.lastIndexCount = 0; + + for (var i=0, j=0; i < numIndices; i += 6, j += 4) + { + this.indices[i + 0] = j + 0; + this.indices[i + 1] = j + 1; + this.indices[i + 2] = j + 2; + this.indices[i + 3] = j + 0; + this.indices[i + 4] = j + 2; + this.indices[i + 5] = j + 3; + } + + /** + * @property drawing + * @type Boolean + */ + this.drawing = false; + + /** + * @property currentBatchSize + * @type Number + */ + this.currentBatchSize = 0; + + /** + * @property currentBaseTexture + * @type BaseTexture + */ + this.currentBaseTexture = null; + + /** + * @property dirty + * @type Boolean + */ + this.dirty = true; + + /** + * @property textures + * @type Array + */ + this.textures = []; + + /** + * @property blendModes + * @type Array + */ + this.blendModes = []; + + /** + * @property shaders + * @type Array + */ + this.shaders = []; + + /** + * @property sprites + * @type Array + */ + this.sprites = []; + + /** + * @property defaultShader + * @type AbstractFilter + */ + this.defaultShader = new PIXI.AbstractFilter([ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]); +}; + +/** +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) +{ + this.gl = gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // 65535 is max index, so 65535 / 6 = 10922. + + //upload the index data + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + this.currentBlendMode = 99999; + + var shader = new PIXI.PixiShader(gl); + + shader.fragmentSrc = this.defaultShader.fragmentSrc; + shader.uniforms = {}; + shader.init(); + + this.defaultShader.shaders[gl.id] = shader; +}; + +/** +* @method begin +* @param renderSession {Object} The RenderSession object +*/ +PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession) +{ + this.renderSession = renderSession; + this.shader = this.renderSession.shaderManager.defaultShader; + + this.start(); +}; + +/** +* @method end +*/ +PIXI.WebGLSpriteBatch.prototype.end = function() +{ + this.flush(); +}; + +/** +* @method render +* @param sprite {Sprite} the sprite to render when using this spritebatch +*/ +PIXI.WebGLSpriteBatch.prototype.render = function(sprite) +{ + var texture = sprite.texture; + + //TODO set blend modes.. + // check texture.. + if(this.currentBatchSize >= this.size) + { + this.flush(); + this.currentBaseTexture = texture.baseTexture; + } + + // get the uvs for the texture + var uvs = texture._uvs; + // if the uvs have not updated then no point rendering just yet! + if(!uvs)return; + + // TODO trim?? + var aX = sprite.anchor.x; + var aY = sprite.anchor.y; + + var w0, w1, h0, h1; + + if (texture.trim) + { + // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. + var trim = texture.trim; + + w1 = trim.x - aX * trim.width; + w0 = w1 + texture.crop.width; + + h1 = trim.y - aY * trim.height; + h0 = h1 + texture.crop.height; + + } + else + { + w0 = (texture.frame.width ) * (1-aX); + w1 = (texture.frame.width ) * -aX; + + h0 = texture.frame.height * (1-aY); + h1 = texture.frame.height * -aY; + } + + var index = this.currentBatchSize * 4 * this.vertSize; + + var resolution = texture.baseTexture.resolution; + + var worldTransform = sprite.worldTransform; + + var a = worldTransform.a / resolution; + var b = worldTransform.b / resolution; + var c = worldTransform.c / resolution; + var d = worldTransform.d / resolution; + var tx = worldTransform.tx; + var ty = worldTransform.ty; + + var colors = this.colors; + var positions = this.positions; + + if(this.renderSession.roundPixels) + { + // xy + positions[index] = a * w1 + c * h1 + tx | 0; + positions[index+1] = d * h1 + b * w1 + ty | 0; + + // xy + positions[index+5] = a * w0 + c * h1 + tx | 0; + positions[index+6] = d * h1 + b * w0 + ty | 0; + + // xy + positions[index+10] = a * w0 + c * h0 + tx | 0; + positions[index+11] = d * h0 + b * w0 + ty | 0; + + // xy + positions[index+15] = a * w1 + c * h0 + tx | 0; + positions[index+16] = d * h0 + b * w1 + ty | 0; + } + else + { + // xy + positions[index] = a * w1 + c * h1 + tx; + positions[index+1] = d * h1 + b * w1 + ty; + + // xy + positions[index+5] = a * w0 + c * h1 + tx; + positions[index+6] = d * h1 + b * w0 + ty; + + // xy + positions[index+10] = a * w0 + c * h0 + tx; + positions[index+11] = d * h0 + b * w0 + ty; + + // xy + positions[index+15] = a * w1 + c * h0 + tx; + positions[index+16] = d * h0 + b * w1 + ty; + } + + // uv + positions[index+2] = uvs.x0; + positions[index+3] = uvs.y0; + + // uv + positions[index+7] = uvs.x1; + positions[index+8] = uvs.y1; + + // uv + positions[index+12] = uvs.x2; + positions[index+13] = uvs.y2; + + // uv + positions[index+17] = uvs.x3; + positions[index+18] = uvs.y3; + + // color and alpha + var tint = sprite.tint; + colors[index+4] = colors[index+9] = colors[index+14] = colors[index+19] = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); + + // increment the batchsize + this.sprites[this.currentBatchSize++] = sprite; + + +}; + +/** +* Renders a TilingSprite using the spriteBatch. +* +* @method renderTilingSprite +* @param sprite {TilingSprite} the tilingSprite to render +*/ +PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) +{ + var texture = tilingSprite.tilingTexture; + + // check texture.. + if(this.currentBatchSize >= this.size) + { + //return; + this.flush(); + this.currentBaseTexture = texture.baseTexture; + } + + // set the textures uvs temporarily + // TODO create a separate texture so that we can tile part of a texture + + if(!tilingSprite._uvs)tilingSprite._uvs = new PIXI.TextureUvs(); + + var uvs = tilingSprite._uvs; + + tilingSprite.tilePosition.x %= texture.baseTexture.width * tilingSprite.tileScaleOffset.x; + tilingSprite.tilePosition.y %= texture.baseTexture.height * tilingSprite.tileScaleOffset.y; + + var offsetX = tilingSprite.tilePosition.x/(texture.baseTexture.width*tilingSprite.tileScaleOffset.x); + var offsetY = tilingSprite.tilePosition.y/(texture.baseTexture.height*tilingSprite.tileScaleOffset.y); + + var scaleX = (tilingSprite.width / texture.baseTexture.width) / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x); + var scaleY = (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y); + + uvs.x0 = 0 - offsetX; + uvs.y0 = 0 - offsetY; + + uvs.x1 = (1 * scaleX) - offsetX; + uvs.y1 = 0 - offsetY; + + uvs.x2 = (1 * scaleX) - offsetX; + uvs.y2 = (1 * scaleY) - offsetY; + + uvs.x3 = 0 - offsetX; + uvs.y3 = (1 * scaleY) - offsetY; + + // get the tilingSprites current alpha and tint and combining them into a single color + var tint = tilingSprite.tint; + var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (tilingSprite.alpha * 255 << 24); + + var positions = this.positions; + var colors = this.colors; + + var width = tilingSprite.width; + var height = tilingSprite.height; + + // TODO trim?? + var aX = tilingSprite.anchor.x; + var aY = tilingSprite.anchor.y; + var w0 = width * (1-aX); + var w1 = width * -aX; + + var h0 = height * (1-aY); + var h1 = height * -aY; + + var index = this.currentBatchSize * 4 * this.vertSize; + + var resolution = texture.baseTexture.resolution; + + var worldTransform = tilingSprite.worldTransform; + + var a = worldTransform.a / resolution;//[0]; + var b = worldTransform.b / resolution;//[3]; + var c = worldTransform.c / resolution;//[1]; + var d = worldTransform.d / resolution;//[4]; + var tx = worldTransform.tx;//[2]; + var ty = worldTransform.ty;//[5]; + + // xy + positions[index++] = a * w1 + c * h1 + tx; + positions[index++] = d * h1 + b * w1 + ty; + // uv + positions[index++] = uvs.x0; + positions[index++] = uvs.y0; + // color + colors[index++] = color; + + // xy + positions[index++] = (a * w0 + c * h1 + tx); + positions[index++] = d * h1 + b * w0 + ty; + // uv + positions[index++] = uvs.x1; + positions[index++] = uvs.y1; + // color + colors[index++] = color; + + // xy + positions[index++] = a * w0 + c * h0 + tx; + positions[index++] = d * h0 + b * w0 + ty; + // uv + positions[index++] = uvs.x2; + positions[index++] = uvs.y2; + // color + colors[index++] = color; + + // xy + positions[index++] = a * w1 + c * h0 + tx; + positions[index++] = d * h0 + b * w1 + ty; + // uv + positions[index++] = uvs.x3; + positions[index++] = uvs.y3; + // color + colors[index++] = color; + + // increment the batchsize + this.sprites[this.currentBatchSize++] = tilingSprite; +}; + +/** +* Renders the content and empties the current batch. +* +* @method flush +*/ +PIXI.WebGLSpriteBatch.prototype.flush = function() +{ + // If the batch is length 0 then return as there is nothing to draw + if (this.currentBatchSize===0)return; + + var gl = this.gl; + var shader; + + if(this.dirty) + { + this.dirty = false; + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + shader = this.defaultShader.shaders[gl.id]; + + // this is the same for each shader? + var stride = this.vertSize * 4; + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); + + // color attributes will be interpreted as unsigned bytes and normalized + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.UNSIGNED_BYTE, true, stride, 4 * 4); + } + + // upload the verts to the buffer + if(this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + } + else + { + var view = this.positions.subarray(0, this.currentBatchSize * 4 * this.vertSize); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } + + var nextTexture, nextBlendMode, nextShader; + var batchSize = 0; + var start = 0; + + var currentBaseTexture = null; + var currentBlendMode = this.renderSession.blendModeManager.currentBlendMode; + var currentShader = null; + + var blendSwap = false; + var shaderSwap = false; + var sprite; + + for (var i = 0, j = this.currentBatchSize; i < j; i++) { + + sprite = this.sprites[i]; + + nextTexture = sprite.texture.baseTexture; + nextBlendMode = sprite.blendMode; + nextShader = sprite.shader || this.defaultShader; + + blendSwap = currentBlendMode !== nextBlendMode; + shaderSwap = currentShader !== nextShader; // should I use _UIDS??? + + if(currentBaseTexture !== nextTexture || blendSwap || shaderSwap) + { + this.renderBatch(currentBaseTexture, batchSize, start); + + start = i; + batchSize = 0; + currentBaseTexture = nextTexture; + + if( blendSwap ) + { + currentBlendMode = nextBlendMode; + this.renderSession.blendModeManager.setBlendMode( currentBlendMode ); + } + + if( shaderSwap ) + { + currentShader = nextShader; + + shader = currentShader.shaders[gl.id]; + + if(!shader) + { + shader = new PIXI.PixiShader(gl); + + shader.fragmentSrc =currentShader.fragmentSrc; + shader.uniforms =currentShader.uniforms; + shader.init(); + + currentShader.shaders[gl.id] = shader; + } + + // set shader function??? + this.renderSession.shaderManager.setShader(shader); + + if(shader.dirty)shader.syncUniforms(); + + // both thease only need to be set if they are changing.. + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(shader.projectionVector, projection.x, projection.y); + + // TODO - this is temprorary! + var offsetVector = this.renderSession.offset; + gl.uniform2f(shader.offsetVector, offsetVector.x, offsetVector.y); + + // set the pointers + } + } + + batchSize++; + } + + this.renderBatch(currentBaseTexture, batchSize, start); + + // then reset the batch! + this.currentBatchSize = 0; +}; + +/** +* @method renderBatch +* @param texture {Texture} +* @param size {Number} +* @param startIndex {Number} +*/ +PIXI.WebGLSpriteBatch.prototype.renderBatch = function(texture, size, startIndex) +{ + if(size === 0)return; + + var gl = this.gl; + + // check if a texture is dirty.. + if(texture._dirty[gl.id]) + { + this.renderSession.renderer.updateTexture(texture); + } + else + { + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + } + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); + + // increment the draw count + this.renderSession.drawCount++; +}; + +/** +* @method stop +*/ +PIXI.WebGLSpriteBatch.prototype.stop = function() +{ + this.flush(); + this.dirty = true; +}; + +/** +* @method start +*/ +PIXI.WebGLSpriteBatch.prototype.start = function() +{ + this.dirty = true; +}; + +/** +* Destroys the SpriteBatch. +* +* @method destroy +*/ +PIXI.WebGLSpriteBatch.prototype.destroy = function() +{ + this.vertices = null; + this.indices = null; + + this.gl.deleteBuffer( this.vertexBuffer ); + this.gl.deleteBuffer( this.indexBuffer ); + + this.currentBaseTexture = null; + + this.gl = null; +}; \ No newline at end of file diff --git a/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLStencilManager.js b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLStencilManager.js new file mode 100644 index 0000000..989d8cf --- /dev/null +++ b/app/Lib/Vendor/src/pixi/renderers/webgl/utils/WebGLStencilManager.js @@ -0,0 +1,297 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLStencilManager +* @constructor +* @private +*/ +PIXI.WebGLStencilManager = function() +{ + this.stencilStack = []; + this.reverse = true; + this.count = 0; +}; + +/** +* Sets the drawing context to the one given in parameter. +* +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLStencilManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +/** +* Applies the Mask and adds it to the current filter stack. +* +* @method pushMask +* @param graphics {Graphics} +* @param webGLData {Array} +* @param renderSession {Object} +*/ +PIXI.WebGLStencilManager.prototype.pushStencil = function(graphics, webGLData, renderSession) +{ + var gl = this.gl; + this.bindGraphics(graphics, webGLData, renderSession); + + if(this.stencilStack.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + this.reverse = true; + this.count = 0; + } + + this.stencilStack.push(webGLData); + + var level = this.count; + + gl.colorMask(false, false, false, false); + + gl.stencilFunc(gl.ALWAYS,0,0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); + + // draw the triangle strip! + + if(webGLData.mode === 1) + { + gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); + + if(this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + + // draw a quad to increment.. + gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); + + if(this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + } + + this.reverse = !this.reverse; + } + else + { + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + + gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); + + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + } + } + + gl.colorMask(true, true, true, true); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + + this.count++; +}; + +/** + * TODO this does not belong here! + * + * @method bindGraphics + * @param graphics {Graphics} + * @param webGLData {Array} + * @param renderSession {Object} + */ +PIXI.WebGLStencilManager.prototype.bindGraphics = function(graphics, webGLData, renderSession) +{ + //if(this._currentGraphics === graphics)return; + this._currentGraphics = graphics; + + var gl = this.gl; + + // bind the graphics object.. + var projection = renderSession.projection, + offset = renderSession.offset, + shader;// = renderSession.shaderManager.primitiveShader; + + if(webGLData.mode === 1) + { + shader = renderSession.shaderManager.complexPrimitiveShader; + + renderSession.shaderManager.setShader( shader ); + + gl.uniform1f(shader.flipY, renderSession.flipY); + + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + gl.uniform3fv(shader.color, webGLData.color); + + gl.uniform1f(shader.alpha, graphics.worldAlpha * webGLData.alpha); + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 2, 0); + + + // now do the rest.. + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + } + else + { + //renderSession.shaderManager.activatePrimitiveShader(); + shader = renderSession.shaderManager.primitiveShader; + renderSession.shaderManager.setShader( shader ); + + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); + + gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + + gl.uniform1f(shader.alpha, graphics.worldAlpha); + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + } +}; + +/** + * @method popStencil + * @param graphics {Graphics} + * @param webGLData {Array} + * @param renderSession {Object} + */ +PIXI.WebGLStencilManager.prototype.popStencil = function(graphics, webGLData, renderSession) +{ + var gl = this.gl; + this.stencilStack.pop(); + + this.count--; + + if(this.stencilStack.length === 0) + { + // the stack is empty! + gl.disable(gl.STENCIL_TEST); + + } + else + { + + var level = this.count; + + this.bindGraphics(graphics, webGLData, renderSession); + + gl.colorMask(false, false, false, false); + + if(webGLData.mode === 1) + { + this.reverse = !this.reverse; + + if(this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + + // draw a quad to increment.. + gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); + + gl.stencilFunc(gl.ALWAYS,0,0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); + + // draw the triangle strip! + gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); + + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + } + + } + else + { + // console.log("<<>>") + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + } + else + { + gl.stencilFunc(gl.EQUAL,level+1, 0xFF); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + } + + gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); + + if(!this.reverse) + { + gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); + } + else + { + gl.stencilFunc(gl.EQUAL,level, 0xFF); + } + } + + gl.colorMask(true, true, true, true); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + + + } +}; + +/** +* Destroys the mask stack. +* +* @method destroy +*/ +PIXI.WebGLStencilManager.prototype.destroy = function() +{ + this.stencilStack = null; + this.gl = null; +}; diff --git a/app/Lib/Vendor/src/pixi/text/BitmapText.js b/app/Lib/Vendor/src/pixi/text/BitmapText.js new file mode 100644 index 0000000..a1d20af --- /dev/null +++ b/app/Lib/Vendor/src/pixi/text/BitmapText.js @@ -0,0 +1,215 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A BitmapText object will create a line or multiple lines of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n' in your string. + * You can generate the fnt files using + * http://www.angelcode.com/products/bmfont/ for windows or + * http://www.bmglyph.com/ for mac. + * + * @class BitmapText + * @extends DisplayObjectContainer + * @constructor + * @param text {String} The copy that you would like the text to display + * @param style {Object} The style parameters + * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + */ +PIXI.BitmapText = function(text, style) +{ + PIXI.DisplayObjectContainer.call(this); + + /** + * [read-only] The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @property textWidth + * @type Number + * @readOnly + */ + this.textWidth = 0; + + /** + * [read-only] The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @property textHeight + * @type Number + * @readOnly + */ + this.textHeight = 0; + + /** + * @property _pool + * @type Array + * @private + */ + this._pool = []; + + this.setText(text); + this.setStyle(style); + this.updateText(); + + /** + * The dirty state of this object. + * @property dirty + * @type Boolean + */ + this.dirty = false; +}; + +// constructor +PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.BitmapText.prototype.constructor = PIXI.BitmapText; + +/** + * Set the text string to be rendered. + * + * @method setText + * @param text {String} The text that you would like displayed + */ +PIXI.BitmapText.prototype.setText = function(text) +{ + this.text = text || ' '; + this.dirty = true; +}; + +/** + * Set the style of the text + * style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) + * [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single lines of text + * + * @method setStyle + * @param style {Object} The style parameters, contained as properties of an object + */ +PIXI.BitmapText.prototype.setStyle = function(style) +{ + style = style || {}; + style.align = style.align || 'left'; + this.style = style; + + var font = style.font.split(' '); + this.fontName = font[font.length - 1]; + this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size; + + this.dirty = true; + this.tint = style.tint; +}; + +/** + * Renders text and updates it when needed + * + * @method updateText + * @private + */ +PIXI.BitmapText.prototype.updateText = function() +{ + var data = PIXI.BitmapText.fonts[this.fontName]; + var pos = new PIXI.Point(); + var prevCharCode = null; + var chars = []; + var maxLineWidth = 0; + var lineWidths = []; + var line = 0; + var scale = this.fontSize / data.size; + + for(var i = 0; i < this.text.length; i++) + { + var charCode = this.text.charCodeAt(i); + + if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i))) + { + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + line++; + + pos.x = 0; + pos.y += data.lineHeight; + prevCharCode = null; + continue; + } + + var charData = data.chars[charCode]; + + if(!charData) continue; + + if(prevCharCode && charData.kerning[prevCharCode]) + { + pos.x += charData.kerning[prevCharCode]; + } + + chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)}); + pos.x += charData.xAdvance; + + prevCharCode = charCode; + } + + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + + var lineAlignOffsets = []; + + for(i = 0; i <= line; i++) + { + var alignOffset = 0; + if(this.style.align === 'right') + { + alignOffset = maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + alignOffset = (maxLineWidth - lineWidths[i]) / 2; + } + lineAlignOffsets.push(alignOffset); + } + + var lenChildren = this.children.length; + var lenChars = chars.length; + var tint = this.tint || 0xFFFFFF; + + for(i = 0; i < lenChars; i++) + { + var c = i < lenChildren ? this.children[i] : this._pool.pop(); // get old child if have. if not - take from pool. + + if (c) c.setTexture(chars[i].texture); // check if got one before. + else c = new PIXI.Sprite(chars[i].texture); // if no create new one. + + c.position.x = (chars[i].position.x + lineAlignOffsets[chars[i].line]) * scale; + c.position.y = chars[i].position.y * scale; + c.scale.x = c.scale.y = scale; + c.tint = tint; + if (!c.parent) this.addChild(c); + } + + // remove unnecessary children. + // and put their into the pool. + while(this.children.length > lenChars) + { + var child = this.getChildAt(this.children.length - 1); + this._pool.push(child); + this.removeChild(child); + } + + this.textWidth = maxLineWidth * scale; + this.textHeight = (pos.y + data.lineHeight) * scale; +}; + +/** + * Updates the transform of this object + * + * @method updateTransform + * @private + */ +PIXI.BitmapText.prototype.updateTransform = function() +{ + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); +}; + +PIXI.BitmapText.fonts = {}; diff --git a/app/Lib/Vendor/src/pixi/text/Text.js b/app/Lib/Vendor/src/pixi/text/Text.js new file mode 100644 index 0000000..abf0242 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/text/Text.js @@ -0,0 +1,533 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * Modified by Tom Slezakowski http://www.tomslezakowski.com @TomSlezakowski (24/03/2014) - Added dropShadowColor. + */ + +/** + * A Text Object will create a line or multiple lines of text. To split a line you can use '\n' in your text string, + * or add a wordWrap property set to true and and wordWrapWidth property with a value in the style object. + * + * @class Text + * @extends Sprite + * @constructor + * @param text {String} The copy that you would like the text to display + * @param [style] {Object} The style parameters + * @param [style.font] {String} default 'bold 20px Arial' The style and size of the font + * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * @param [style.stroke] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.dropShadow=false] {Boolean} Set a drop shadow for the text + * @param [style.dropShadowColor='#000000'] {String} A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param [style.dropShadowAngle=Math.PI/4] {Number} Set a angle of the drop shadow + * @param [style.dropShadowDistance=5] {Number} Set a distance of the drop shadow + */ +PIXI.Text = function(text, style) +{ + /** + * The canvas element that everything is drawn to + * + * @property canvas + * @type HTMLCanvasElement + */ + this.canvas = document.createElement('canvas'); + + /** + * The canvas 2d context that everything is drawn with + * @property context + * @type HTMLCanvasElement + */ + this.context = this.canvas.getContext('2d'); + + /** + * The resolution of the canvas. + * @property resolution + * @type Number + */ + this.resolution = 1; + + PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas)); + + this.setText(text); + this.setStyle(style); + +}; + +// constructor +PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); +PIXI.Text.prototype.constructor = PIXI.Text; + +/** + * The width of the Text, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.Text.prototype, 'width', { + get: function() { + + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + + return this.scale.x * this.texture.frame.width; + }, + set: function(value) { + this.scale.x = value / this.texture.frame.width; + this._width = value; + } +}); + +/** + * The height of the Text, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.Text.prototype, 'height', { + get: function() { + + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + + return this.scale.y * this.texture.frame.height; + }, + set: function(value) { + this.scale.y = value / this.texture.frame.height; + this._height = value; + } +}); + +/** + * Set the style of the text + * + * @method setStyle + * @param [style] {Object} The style parameters + * @param [style.font='bold 20pt Arial'] {String} The style and size of the font + * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * @param [style.stroke='black'] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap + * @param [style.dropShadow=false] {Boolean} Set a drop shadow for the text + * @param [style.dropShadowColor='#000000'] {String} A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param [style.dropShadowAngle=Math.PI/4] {Number} Set a angle of the drop shadow + * @param [style.dropShadowDistance=5] {Number} Set a distance of the drop shadow + */ +PIXI.Text.prototype.setStyle = function(style) +{ + style = style || {}; + style.font = style.font || 'bold 20pt Arial'; + style.fill = style.fill || 'black'; + style.align = style.align || 'left'; + style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136 + style.strokeThickness = style.strokeThickness || 0; + style.wordWrap = style.wordWrap || false; + style.wordWrapWidth = style.wordWrapWidth || 100; + + style.dropShadow = style.dropShadow || false; + style.dropShadowAngle = style.dropShadowAngle || Math.PI / 6; + style.dropShadowDistance = style.dropShadowDistance || 4; + style.dropShadowColor = style.dropShadowColor || 'black'; + + this.style = style; + this.dirty = true; +}; + +/** + * Set the copy for the text object. To split a line you can use '\n'. + * + * @method setText + * @param text {String} The copy that you would like the text to display + */ +PIXI.Text.prototype.setText = function(text) +{ + this.text = text.toString() || ' '; + this.dirty = true; +}; + +/** + * Renders text and updates it when needed + * + * @method updateText + * @private + */ +PIXI.Text.prototype.updateText = function() +{ + this.texture.baseTexture.resolution = this.resolution; + + this.context.font = this.style.font; + + var outputText = this.text; + + // word wrap + // preserve original text + if(this.style.wordWrap)outputText = this.wordWrap(this.text); + + //split text into lines + var lines = outputText.split(/(?:\r\n|\r|\n)/); + + //calculate text width + var lineWidths = []; + var maxLineWidth = 0; + var fontProperties = this.determineFontProperties(this.style.font); + for (var i = 0; i < lines.length; i++) + { + var lineWidth = this.context.measureText(lines[i]).width; + lineWidths[i] = lineWidth; + maxLineWidth = Math.max(maxLineWidth, lineWidth); + } + + var width = maxLineWidth + this.style.strokeThickness; + if(this.style.dropShadow)width += this.style.dropShadowDistance; + + this.canvas.width = ( width + this.context.lineWidth ) * this.resolution; + + //calculate text height + var lineHeight = fontProperties.fontSize + this.style.strokeThickness; + + var height = lineHeight * lines.length; + if(this.style.dropShadow)height += this.style.dropShadowDistance; + + this.canvas.height = height * this.resolution; + + this.context.scale( this.resolution, this.resolution); + + if(navigator.isCocoonJS) this.context.clearRect(0,0,this.canvas.width,this.canvas.height); + + // used for debugging.. + //this.context.fillStyle ="#FF0000" + //this.context.fillRect(0, 0, this.canvas.width,this.canvas.height); + + this.context.font = this.style.font; + this.context.strokeStyle = this.style.stroke; + this.context.lineWidth = this.style.strokeThickness; + this.context.textBaseline = 'alphabetic'; + //this.context.lineJoin = 'round'; + + var linePositionX; + var linePositionY; + + if(this.style.dropShadow) + { + this.context.fillStyle = this.style.dropShadowColor; + + var xShadowOffset = Math.sin(this.style.dropShadowAngle) * this.style.dropShadowDistance; + var yShadowOffset = Math.cos(this.style.dropShadowAngle) * this.style.dropShadowDistance; + + for (i = 0; i < lines.length; i++) + { + linePositionX = this.style.strokeThickness / 2; + linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; + + if(this.style.align === 'right') + { + linePositionX += maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + linePositionX += (maxLineWidth - lineWidths[i]) / 2; + } + + if(this.style.fill) + { + this.context.fillText(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset); + } + + // if(dropShadow) + } + } + + //set canvas text styles + this.context.fillStyle = this.style.fill; + + //draw lines line by line + for (i = 0; i < lines.length; i++) + { + linePositionX = this.style.strokeThickness / 2; + linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; + + if(this.style.align === 'right') + { + linePositionX += maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + linePositionX += (maxLineWidth - lineWidths[i]) / 2; + } + + if(this.style.stroke && this.style.strokeThickness) + { + this.context.strokeText(lines[i], linePositionX, linePositionY); + } + + if(this.style.fill) + { + this.context.fillText(lines[i], linePositionX, linePositionY); + } + + // if(dropShadow) + } + + this.updateTexture(); +}; + +/** + * Updates texture size based on canvas size + * + * @method updateTexture + * @private + */ +PIXI.Text.prototype.updateTexture = function() +{ + this.texture.baseTexture.width = this.canvas.width; + this.texture.baseTexture.height = this.canvas.height; + this.texture.crop.width = this.texture.frame.width = this.canvas.width; + this.texture.crop.height = this.texture.frame.height = this.canvas.height; + + this._width = this.canvas.width; + this._height = this.canvas.height; + + // update the dirty base textures + this.texture.baseTexture.dirty(); +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Text.prototype._renderWebGL = function(renderSession) +{ + if(this.dirty) + { + this.resolution = renderSession.resolution; + + this.updateText(); + this.dirty = false; + } + + PIXI.Sprite.prototype._renderWebGL.call(this, renderSession); +}; + +/** +* Renders the object using the Canvas renderer +* +* @method _renderCanvas +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Text.prototype._renderCanvas = function(renderSession) +{ + if(this.dirty) + { + this.resolution = renderSession.resolution; + + this.updateText(); + this.dirty = false; + } + + PIXI.Sprite.prototype._renderCanvas.call(this, renderSession); +}; + +/** +* Calculates the ascent, descent and fontSize of a given fontStyle +* +* @method determineFontProperties +* @param fontStyle {Object} +* @private +*/ +PIXI.Text.prototype.determineFontProperties = function(fontStyle) +{ + var properties = PIXI.Text.fontPropertiesCache[fontStyle]; + + if(!properties) + { + properties = {}; + + var canvas = PIXI.Text.fontPropertiesCanvas; + var context = PIXI.Text.fontPropertiesContext; + + context.font = fontStyle; + + var width = Math.ceil(context.measureText('|Mq').width); + var baseline = Math.ceil(context.measureText('M').width); + var height = 2 * baseline; + + baseline = baseline * 1.4 | 0; + + canvas.width = width; + canvas.height = height; + + context.fillStyle = '#f00'; + context.fillRect(0, 0, width, height); + + context.font = fontStyle; + + context.textBaseline = 'alphabetic'; + context.fillStyle = '#000'; + context.fillText('|MÉq', 0, baseline); + + var imagedata = context.getImageData(0, 0, width, height).data; + var pixels = imagedata.length; + var line = width * 4; + + var i, j; + + var idx = 0; + var stop = false; + + // ascent. scan from top to bottom until we find a non red pixel + for(i = 0; i < baseline; i++) + { + for(j = 0; j < line; j += 4) + { + if(imagedata[idx + j] !== 255) + { + stop = true; + break; + } + } + if(!stop) + { + idx += line; + } + else + { + break; + } + } + + properties.ascent = baseline - i; + + idx = pixels - line; + stop = false; + + // descent. scan from bottom to top until we find a non red pixel + for(i = height; i > baseline; i--) + { + for(j = 0; j < line; j += 4) + { + if(imagedata[idx + j] !== 255) + { + stop = true; + break; + } + } + if(!stop) + { + idx -= line; + } + else + { + break; + } + } + + properties.descent = i - baseline; + //TODO might need a tweak. kind of a temp fix! + properties.descent += 6; + properties.fontSize = properties.ascent + properties.descent; + + PIXI.Text.fontPropertiesCache[fontStyle] = properties; + } + + return properties; +}; + +/** + * Applies newlines to a string to have it optimally fit into the horizontal + * bounds set by the Text object's wordWrapWidth property. + * + * @method wordWrap + * @param text {String} + * @private + */ +PIXI.Text.prototype.wordWrap = function(text) +{ + // Greedy wrapping algorithm that will wrap words as the line grows longer + // than its horizontal bounds. + var result = ''; + var lines = text.split('\n'); + for (var i = 0; i < lines.length; i++) + { + var spaceLeft = this.style.wordWrapWidth; + var words = lines[i].split(' '); + for (var j = 0; j < words.length; j++) + { + var wordWidth = this.context.measureText(words[j]).width; + var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; + if(j === 0 || wordWidthWithSpace > spaceLeft) + { + // Skip printing the newline if it's the first word of the line that is + // greater than the word wrap width. + if(j > 0) + { + result += '\n'; + } + result += words[j]; + spaceLeft = this.style.wordWrapWidth - wordWidth; + } + else + { + spaceLeft -= wordWidthWithSpace; + result += ' ' + words[j]; + } + } + + if (i < lines.length-1) + { + result += '\n'; + } + } + return result; +}; + +/** +* Returns the bounds of the Text as a rectangle. The bounds calculation takes the worldTransform into account. +* +* @method getBounds +* @param matrix {Matrix} the transformation matrix of the Text +* @return {Rectangle} the framing rectangle +*/ +PIXI.Text.prototype.getBounds = function(matrix) +{ + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + return PIXI.Sprite.prototype.getBounds.call(this, matrix); +}; + +/** + * Destroys this text object. + * + * @method destroy + * @param destroyBaseTexture {Boolean} whether to destroy the base texture as well + */ +PIXI.Text.prototype.destroy = function(destroyBaseTexture) +{ + // make sure to reset the the context and canvas.. dont want this hanging around in memory! + this.context = null; + this.canvas = null; + + this.texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); +}; + +PIXI.Text.fontPropertiesCache = {}; +PIXI.Text.fontPropertiesCanvas = document.createElement('canvas'); +PIXI.Text.fontPropertiesContext = PIXI.Text.fontPropertiesCanvas.getContext('2d'); diff --git a/app/Lib/Vendor/src/pixi/textures/BaseTexture.js b/app/Lib/Vendor/src/pixi/textures/BaseTexture.js new file mode 100644 index 0000000..0145849 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/textures/BaseTexture.js @@ -0,0 +1,305 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.BaseTextureCache = {}; + +PIXI.BaseTextureCacheIdGenerator = 0; + +/** + * A texture stores the information that represents an image. All textures have a base texture. + * + * @class BaseTexture + * @uses EventTarget + * @constructor + * @param source {String} the source object (image or canvas) + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + */ +PIXI.BaseTexture = function(source, scaleMode) +{ + /** + * The Resolution of the texture. + * + * @property resolution + * @type Number + */ + this.resolution = 1; + + /** + * [read-only] The width of the base texture set when the image has loaded + * + * @property width + * @type Number + * @readOnly + */ + this.width = 100; + + /** + * [read-only] The height of the base texture set when the image has loaded + * + * @property height + * @type Number + * @readOnly + */ + this.height = 100; + + /** + * The scale mode to apply when scaling this texture + * + * @property scaleMode + * @type {Number} + * @default PIXI.scaleModes.LINEAR + */ + this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; + + /** + * [read-only] Set to true once the base texture has loaded + * + * @property hasLoaded + * @type Boolean + * @readOnly + */ + this.hasLoaded = false; + + /** + * The image source that is used to create the texture. + * + * @property source + * @type Image + */ + this.source = source; + + this._UID = PIXI._UID++; + + /** + * Controls if RGB channels should be pre-multiplied by Alpha (WebGL only) + * + * @property premultipliedAlpha + * @type Boolean + * @default true + */ + this.premultipliedAlpha = true; + + // used for webGL + + /** + * @property _glTextures + * @type Array + * @private + */ + this._glTextures = []; + + /** + * + * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used + * Also the texture must be a power of two size to work + * + * @property mipmap + * @type {Boolean} + */ + this.mipmap = false; + + // used for webGL texture updating... + // TODO - this needs to be addressed + + /** + * @property _dirty + * @type Array + * @private + */ + this._dirty = [true, true, true, true]; + + if(!source)return; + + if((this.source.complete || this.source.getContext) && this.source.width && this.source.height) + { + this.hasLoaded = true; + this.width = this.source.naturalWidth || this.source.width; + this.height = this.source.naturalHeight || this.source.height; + this.dirty(); + } + else + { + var scope = this; + + this.source.onload = function() { + + scope.hasLoaded = true; + scope.width = scope.source.naturalWidth || scope.source.width; + scope.height = scope.source.naturalHeight || scope.source.height; + + scope.dirty(); + + // add it to somewhere... + scope.dispatchEvent( { type: 'loaded', content: scope } ); + }; + + this.source.onerror = function() { + scope.dispatchEvent( { type: 'error', content: scope } ); + }; + } + + /** + * @property imageUrl + * @type String + */ + this.imageUrl = null; + + /** + * @property _powerOf2 + * @type Boolean + * @private + */ + this._powerOf2 = false; + +}; + +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; + +PIXI.EventTarget.mixin(PIXI.BaseTexture.prototype); + +/** + * Destroys this base texture + * + * @method destroy + */ +PIXI.BaseTexture.prototype.destroy = function() +{ + if(this.imageUrl) + { + delete PIXI.BaseTextureCache[this.imageUrl]; + delete PIXI.TextureCache[this.imageUrl]; + this.imageUrl = null; + if (!navigator.isCocoonJS) this.source.src = ''; + } + else if (this.source && this.source._pixiId) + { + delete PIXI.BaseTextureCache[this.source._pixiId]; + } + this.source = null; + + this.unloadFromGPU(); +}; + +/** + * Changes the source image of the texture + * + * @method updateSourceImage + * @param newSrc {String} the path of the image + */ +PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) +{ + this.hasLoaded = false; + this.source.src = null; + this.source.src = newSrc; +}; + +/** + * Sets all glTextures to be dirty. + * + * @method dirty + */ +PIXI.BaseTexture.prototype.dirty = function() +{ + for (var i = 0; i < this._glTextures.length; i++) + { + this._dirty[i] = true; + } +}; + +/** + * Removes the base texture from the GPU, useful for managing resources on the GPU. + * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. + * + * @method unloadFromGPU + */ +PIXI.BaseTexture.prototype.unloadFromGPU = function() +{ + this.dirty(); + + // delete the webGL textures if any. + for (var i = this._glTextures.length - 1; i >= 0; i--) + { + var glTexture = this._glTextures[i]; + var gl = PIXI.glContexts[i]; + + if(gl && glTexture) + { + gl.deleteTexture(glTexture); + } + + } + + this._glTextures.length = 0; + + this.dirty(); +}; + +/** + * Helper function that creates a base texture from the given image url. + * If the image is not in the base texture cache it will be created and loaded. + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @param crossorigin {Boolean} + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return BaseTexture + */ +PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) +{ + var baseTexture = PIXI.BaseTextureCache[imageUrl]; + + if(crossorigin === undefined && imageUrl.indexOf('data:') === -1) crossorigin = true; + + if(!baseTexture) + { + // new Image() breaks tex loading in some versions of Chrome. + // See https://code.google.com/p/chromium/issues/detail?id=238071 + var image = new Image();//document.createElement('img'); + if (crossorigin) + { + image.crossOrigin = ''; + } + + image.src = imageUrl; + baseTexture = new PIXI.BaseTexture(image, scaleMode); + baseTexture.imageUrl = imageUrl; + PIXI.BaseTextureCache[imageUrl] = baseTexture; + + // if there is an @2x at the end of the url we are going to assume its a highres image + if( imageUrl.indexOf(PIXI.RETINA_PREFIX + '.') !== -1) + { + baseTexture.resolution = 2; + } + } + + return baseTexture; +}; + +/** + * Helper function that creates a base texture from the given canvas element. + * + * @static + * @method fromCanvas + * @param canvas {Canvas} The canvas element source of the texture + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return BaseTexture + */ +PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode) +{ + if(!canvas._pixiId) + { + canvas._pixiId = 'canvas_' + PIXI.TextureCacheIdGenerator++; + } + + var baseTexture = PIXI.BaseTextureCache[canvas._pixiId]; + + if(!baseTexture) + { + baseTexture = new PIXI.BaseTexture(canvas, scaleMode); + PIXI.BaseTextureCache[canvas._pixiId] = baseTexture; + } + + return baseTexture; +}; diff --git a/app/Lib/Vendor/src/pixi/textures/RenderTexture.js b/app/Lib/Vendor/src/pixi/textures/RenderTexture.js new file mode 100644 index 0000000..37dd7c8 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/textures/RenderTexture.js @@ -0,0 +1,336 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A RenderTexture is a special texture that allows any Pixi display object to be rendered to it. + * + * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded otherwise black rectangles will be drawn instead. + * + * A RenderTexture takes a snapshot of any Display Object given to its render method. The position and rotation of the given Display Objects is ignored. For example: + * + * var renderTexture = new PIXI.RenderTexture(800, 600); + * var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); + * sprite.position.x = 800/2; + * sprite.position.y = 600/2; + * sprite.anchor.x = 0.5; + * sprite.anchor.y = 0.5; + * renderTexture.render(sprite); + * + * The Sprite in this case will be rendered to a position of 0,0. To render this sprite at its actual position a DisplayObjectContainer should be used: + * + * var doc = new PIXI.DisplayObjectContainer(); + * doc.addChild(sprite); + * renderTexture.render(doc); // Renders to center of renderTexture + * + * @class RenderTexture + * @extends Texture + * @constructor + * @param width {Number} The width of the render texture + * @param height {Number} The height of the render texture + * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @param resolution {Number} The resolution of the texture being generated + */ +PIXI.RenderTexture = function(width, height, renderer, scaleMode, resolution) +{ + /** + * The with of the render texture + * + * @property width + * @type Number + */ + this.width = width || 100; + + /** + * The height of the render texture + * + * @property height + * @type Number + */ + this.height = height || 100; + + /** + * The Resolution of the texture. + * + * @property resolution + * @type Number + */ + this.resolution = resolution || 1; + + /** + * The framing rectangle of the render texture + * + * @property frame + * @type Rectangle + */ + this.frame = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); + + /** + * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, + * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) + * + * @property crop + * @type Rectangle + */ + this.crop = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); + + /** + * The base texture object that this texture uses + * + * @property baseTexture + * @type BaseTexture + */ + this.baseTexture = new PIXI.BaseTexture(); + this.baseTexture.width = this.width * this.resolution; + this.baseTexture.height = this.height * this.resolution; + this.baseTexture._glTextures = []; + this.baseTexture.resolution = this.resolution; + + this.baseTexture.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; + + this.baseTexture.hasLoaded = true; + + PIXI.Texture.call(this, + this.baseTexture, + new PIXI.Rectangle(0, 0, this.width, this.height) + ); + + /** + * The renderer this RenderTexture uses. A RenderTexture can only belong to one renderer at the moment if its webGL. + * + * @property renderer + * @type CanvasRenderer|WebGLRenderer + */ + this.renderer = renderer || PIXI.defaultRenderer; + + if(this.renderer.type === PIXI.WEBGL_RENDERER) + { + var gl = this.renderer.gl; + this.baseTexture._dirty[gl.id] = false; + + this.textureBuffer = new PIXI.FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); + this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; + + this.render = this.renderWebGL; + this.projection = new PIXI.Point(this.width*0.5, -this.height*0.5); + } + else + { + this.render = this.renderCanvas; + this.textureBuffer = new PIXI.CanvasBuffer(this.width* this.resolution, this.height* this.resolution); + this.baseTexture.source = this.textureBuffer.canvas; + } + + /** + * @property valid + * @type Boolean + */ + this.valid = true; + + this._updateUvs(); +}; + +PIXI.RenderTexture.prototype = Object.create(PIXI.Texture.prototype); +PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture; + +/** + * Resizes the RenderTexture. + * + * @method resize + * @param width {Number} The width to resize to. + * @param height {Number} The height to resize to. + * @param updateBase {Boolean} Should the baseTexture.width and height values be resized as well? + */ +PIXI.RenderTexture.prototype.resize = function(width, height, updateBase) +{ + if (width === this.width && height === this.height)return; + + this.valid = (width > 0 && height > 0); + + this.width = this.frame.width = this.crop.width = width; + this.height = this.frame.height = this.crop.height = height; + + if (updateBase) + { + this.baseTexture.width = this.width; + this.baseTexture.height = this.height; + } + + if (this.renderer.type === PIXI.WEBGL_RENDERER) + { + this.projection.x = this.width / 2; + this.projection.y = -this.height / 2; + } + + if(!this.valid)return; + + this.textureBuffer.resize(this.width * this.resolution, this.height * this.resolution); +}; + +/** + * Clears the RenderTexture. + * + * @method clear + */ +PIXI.RenderTexture.prototype.clear = function() +{ + if(!this.valid)return; + + if (this.renderer.type === PIXI.WEBGL_RENDERER) + { + this.renderer.gl.bindFramebuffer(this.renderer.gl.FRAMEBUFFER, this.textureBuffer.frameBuffer); + } + + this.textureBuffer.clear(); +}; + +/** + * This function will draw the display object to the texture. + * + * @method renderWebGL + * @param displayObject {DisplayObject} The display object to render this texture on + * @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering. + * @param [clear] {Boolean} If true the texture will be cleared before the displayObject is drawn + * @private + */ +PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, matrix, clear) +{ + if(!this.valid)return; + //TOOD replace position with matrix.. + + //Lets create a nice matrix to apply to our display object. Frame buffers come in upside down so we need to flip the matrix + var wt = displayObject.worldTransform; + wt.identity(); + wt.translate(0, this.projection.y * 2); + if(matrix)wt.append(matrix); + wt.scale(1,-1); + + // setWorld Alpha to ensure that the object is renderer at full opacity + displayObject.worldAlpha = 1; + + // Time to update all the children of the displayObject with the new matrix.. + var children = displayObject.children; + + for(var i=0,j=children.length; i this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)) + { + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); + } + + this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; + + if (this.trim) + { + this.width = this.trim.width; + this.height = this.trim.height; + this.frame.width = this.trim.width; + this.frame.height = this.trim.height; + } + + if (this.valid) this._updateUvs(); + +}; + +/** + * Updates the internal WebGL UV cache. + * + * @method _updateUvs + * @private + */ +PIXI.Texture.prototype._updateUvs = function() +{ + if(!this._uvs)this._uvs = new PIXI.TextureUvs(); + + var frame = this.crop; + var tw = this.baseTexture.width; + var th = this.baseTexture.height; + + this._uvs.x0 = frame.x / tw; + this._uvs.y0 = frame.y / th; + + this._uvs.x1 = (frame.x + frame.width) / tw; + this._uvs.y1 = frame.y / th; + + this._uvs.x2 = (frame.x + frame.width) / tw; + this._uvs.y2 = (frame.y + frame.height) / th; + + this._uvs.x3 = frame.x / tw; + this._uvs.y3 = (frame.y + frame.height) / th; +}; + +/** + * Helper function that creates a Texture object from the given image url. + * If the image is not in the texture cache it will be created and loaded. + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return Texture + */ +PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode) +{ + var texture = PIXI.TextureCache[imageUrl]; + + if(!texture) + { + texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin, scaleMode)); + PIXI.TextureCache[imageUrl] = texture; + } + + return texture; +}; + +/** + * Helper function that returns a Texture objected based on the given frame id. + * If the frame id is not in the texture cache an error will be thrown. + * + * @static + * @method fromFrame + * @param frameId {String} The frame id of the texture + * @return Texture + */ +PIXI.Texture.fromFrame = function(frameId) +{ + var texture = PIXI.TextureCache[frameId]; + if(!texture) throw new Error('The frameId "' + frameId + '" does not exist in the texture cache '); + return texture; +}; + +/** + * Helper function that creates a new a Texture based on the given canvas element. + * + * @static + * @method fromCanvas + * @param canvas {Canvas} The canvas element source of the texture + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return Texture + */ +PIXI.Texture.fromCanvas = function(canvas, scaleMode) +{ + var baseTexture = PIXI.BaseTexture.fromCanvas(canvas, scaleMode); + + return new PIXI.Texture( baseTexture ); + +}; + +/** + * Adds a texture to the global PIXI.TextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @method addTextureToCache + * @param texture {Texture} The Texture to add to the cache. + * @param id {String} The id that the texture will be stored against. + */ +PIXI.Texture.addTextureToCache = function(texture, id) +{ + PIXI.TextureCache[id] = texture; +}; + +/** + * Remove a texture from the global PIXI.TextureCache. + * + * @static + * @method removeTextureFromCache + * @param id {String} The id of the texture to be removed + * @return {Texture} The texture that was removed + */ +PIXI.Texture.removeTextureFromCache = function(id) +{ + var texture = PIXI.TextureCache[id]; + delete PIXI.TextureCache[id]; + delete PIXI.BaseTextureCache[id]; + return texture; +}; + +PIXI.TextureUvs = function() +{ + this.x0 = 0; + this.y0 = 0; + + this.x1 = 0; + this.y1 = 0; + + this.x2 = 0; + this.y2 = 0; + + this.x3 = 0; + this.y3 = 0; +}; + +PIXI.Texture.emptyTexture = new PIXI.Texture(new PIXI.BaseTexture()); + diff --git a/app/Lib/Vendor/src/pixi/textures/VideoTexture.js b/app/Lib/Vendor/src/pixi/textures/VideoTexture.js new file mode 100644 index 0000000..ad58ec5 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/textures/VideoTexture.js @@ -0,0 +1,168 @@ +/** + * A texture of a [playing] Video. + * + * See the ["deus" demo](http://www.goodboydigital.com/pixijs/examples/deus/). + * + * @class VideoTexture + * @extends BaseTexture + * @constructor + * @param source {HTMLVideoElement} + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + */ +PIXI.VideoTexture = function( source, scaleMode ) +{ + if( !source ){ + throw new Error( 'No video source element specified.' ); + } + + // hook in here to check if video is already available. + // PIXI.BaseTexture looks for a source.complete boolean, plus width & height. + + if( (source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA ) && source.width && source.height ) + { + source.complete = true; + } + + PIXI.BaseTexture.call( this, source, scaleMode ); + + this.autoUpdate = false; + this.updateBound = this._onUpdate.bind(this); + + if( !source.complete ) + { + this._onCanPlay = this.onCanPlay.bind(this); + + source.addEventListener( 'canplay', this._onCanPlay ); + source.addEventListener( 'canplaythrough', this._onCanPlay ); + + // started playing.. + source.addEventListener( 'play', this.onPlayStart.bind(this) ); + source.addEventListener( 'pause', this.onPlayStop.bind(this) ); + } + +}; + +PIXI.VideoTexture.prototype = Object.create( PIXI.BaseTexture.prototype ); + +PIXI.VideoTexture.constructor = PIXI.VideoTexture; + +PIXI.VideoTexture.prototype._onUpdate = function() +{ + if(this.autoUpdate) + { + window.requestAnimationFrame(this.updateBound); + this.dirty(); + } +}; + +PIXI.VideoTexture.prototype.onPlayStart = function() +{ + if(!this.autoUpdate) + { + window.requestAnimationFrame(this.updateBound); + this.autoUpdate = true; + } +}; + +PIXI.VideoTexture.prototype.onPlayStop = function() +{ + this.autoUpdate = false; +}; + +PIXI.VideoTexture.prototype.onCanPlay = function() +{ + if( event.type === 'canplaythrough' ) + { + this.hasLoaded = true; + + + if( this.source ) + { + this.source.removeEventListener( 'canplay', this._onCanPlay ); + this.source.removeEventListener( 'canplaythrough', this._onCanPlay ); + + this.width = this.source.videoWidth; + this.height = this.source.videoHeight; + + // prevent multiple loaded dispatches.. + if( !this.__loaded ){ + this.__loaded = true; + this.dispatchEvent( { type: 'loaded', content: this } ); + } + } + } +}; + +PIXI.VideoTexture.prototype.destroy = function() +{ + if( this.source && this.source._pixiId ) + { + PIXI.BaseTextureCache[ this.source._pixiId ] = null; + delete PIXI.BaseTextureCache[ this.source._pixiId ]; + + this.source._pixiId = null; + delete this.source._pixiId; + } + + PIXI.BaseTexture.prototype.destroy.call( this ); +}; + +/** + * Mimic Pixi BaseTexture.from.... method. + * + * @static + * @method baseTextureFromVideo + * @param video {HTMLVideoElement} + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return {VideoTexture} + */ +PIXI.VideoTexture.baseTextureFromVideo = function( video, scaleMode ) +{ + if( !video._pixiId ) + { + video._pixiId = 'video_' + PIXI.TextureCacheIdGenerator++; + } + + var baseTexture = PIXI.BaseTextureCache[ video._pixiId ]; + + if( !baseTexture ) + { + baseTexture = new PIXI.VideoTexture( video, scaleMode ); + PIXI.BaseTextureCache[ video._pixiId ] = baseTexture; + } + + return baseTexture; +}; + +/** + * Mimic Pixi BaseTexture.from.... method. + * + * @static + * @method textureFromVideo + * @param video {HTMLVideoElement} + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return {Texture} A Texture, but not a VideoTexture. + */ +PIXI.VideoTexture.textureFromVideo = function( video, scaleMode ) +{ + var baseTexture = PIXI.VideoTexture.baseTextureFromVideo( video, scaleMode ); + return new PIXI.Texture( baseTexture ); +}; + +/** + * Mimic Pixi BaseTexture.from.... method. + * + * @static + * @method fromUrl + * @param videoSrc {String} The URL for the video. + * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values + * @return {VideoTexture} + */ +PIXI.VideoTexture.fromUrl = function( videoSrc, scaleMode ) +{ + var video = document.createElement('video'); + video.src = videoSrc; + video.autoPlay = true; + video.play(); + return PIXI.VideoTexture.textureFromVideo( video, scaleMode); +}; diff --git a/app/Lib/Vendor/src/pixi/utils/Detector.js b/app/Lib/Vendor/src/pixi/utils/Detector.js new file mode 100644 index 0000000..fe38f04 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/utils/Detector.js @@ -0,0 +1,88 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @method autoDetectRenderer + * @for PIXI + * @static + * @param width=800 {Number} the width of the renderers view + * @param height=600 {Number} the height of the renderers view + * + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 + * + */ +PIXI.autoDetectRenderer = function(width, height, options) +{ + if(!width)width = 800; + if(!height)height = 600; + + // BORROWED from Mr Doob (mrdoob.com) + var webgl = ( function () { try { + var canvas = document.createElement( 'canvas' ); + return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); + } catch( e ) { + return false; + } + } )(); + + if( webgl ) + { + return new PIXI.WebGLRenderer(width, height, options); + } + + return new PIXI.CanvasRenderer(width, height, options); +}; + +/** + * This helper function will automatically detect which renderer you should be using. + * This function is very similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @method autoDetectRecommendedRenderer + * @for PIXI + * @static + * @param width=800 {Number} the width of the renderers view + * @param height=600 {Number} the height of the renderers view + * + * @param [options] {Object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {Boolean} If the render view is transparent, default false + * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context + * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 + * + */ +PIXI.autoDetectRecommendedRenderer = function(width, height, options) +{ + if(!width)width = 800; + if(!height)height = 600; + + // BORROWED from Mr Doob (mrdoob.com) + var webgl = ( function () { try { + var canvas = document.createElement( 'canvas' ); + return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); + } catch( e ) { + return false; + } + } )(); + + var isAndroid = /Android/i.test(navigator.userAgent); + + if( webgl && !isAndroid) + { + return new PIXI.WebGLRenderer(width, height, options); + } + + return new PIXI.CanvasRenderer(width, height, options); +}; diff --git a/app/Lib/Vendor/src/pixi/utils/EventTarget.js b/app/Lib/Vendor/src/pixi/utils/EventTarget.js new file mode 100644 index 0000000..35aa31b --- /dev/null +++ b/app/Lib/Vendor/src/pixi/utils/EventTarget.js @@ -0,0 +1,284 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Chad Engler https://github.com/englercj @Rolnaaba + */ + +/** + * Originally based on https://github.com/mrdoob/eventtarget.js/ from mr Doob. + * Currently takes inspiration from the nodejs EventEmitter, EventEmitter3, and smokesignals + */ + +/** + * Mixins event emitter functionality to a class + * + * @class EventTarget + * @example + * function MyEmitter() {} + * + * PIXI.EventTarget.mixin(MyEmitter.prototype); + * + * var em = new MyEmitter(); + * em.emit('eventName', 'some data', 'some more data', {}, null, ...); + */ +PIXI.EventTarget = { + /** + * Backward compat from when this used to be a function + */ + call: function callCompat(obj) { + if(obj) { + obj = obj.prototype || obj; + PIXI.EventTarget.mixin(obj); + } + }, + + /** + * Mixes in the properties of the EventTarget prototype onto another object + * + * @method mixin + * @param object {Object} The obj to mix into + */ + mixin: function mixin(obj) { + /** + * Return a list of assigned event listeners. + * + * @method listeners + * @param eventName {String} The events that should be listed. + * @return {Array} An array of listener functions + */ + obj.listeners = function listeners(eventName) { + this._listeners = this._listeners || {}; + + return this._listeners[eventName] ? this._listeners[eventName].slice() : []; + }; + + /** + * Emit an event to all registered event listeners. + * + * @method emit + * @alias dispatchEvent + * @param eventName {String} The name of the event. + * @return {Boolean} Indication if we've emitted an event. + */ + obj.emit = obj.dispatchEvent = function emit(eventName, data) { + this._listeners = this._listeners || {}; + + //backwards compat with old method ".emit({ type: 'something' })" + if(typeof eventName === 'object') { + data = eventName; + eventName = eventName.type; + } + + //ensure we are using a real pixi event + if(!data || data.__isEventObject !== true) { + data = new PIXI.Event(this, eventName, data); + } + + //iterate the listeners + if(this._listeners && this._listeners[eventName]) { + var listeners = this._listeners[eventName].slice(0), + length = listeners.length, + fn = listeners[0], + i; + + for(i = 0; i < length; fn = listeners[++i]) { + //call the event listener + fn.call(this, data); + + //if "stopImmediatePropagation" is called, stop calling sibling events + if(data.stoppedImmediate) { + return this; + } + } + + //if "stopPropagation" is called then don't bubble the event + if(data.stopped) { + return this; + } + } + + //bubble this event up the scene graph + if(this.parent && this.parent.emit) { + this.parent.emit.call(this.parent, eventName, data); + } + + return this; + }; + + /** + * Register a new EventListener for the given event. + * + * @method on + * @alias addEventListener + * @param eventName {String} Name of the event. + * @param callback {Functon} fn Callback function. + */ + obj.on = obj.addEventListener = function on(eventName, fn) { + this._listeners = this._listeners || {}; + + (this._listeners[eventName] = this._listeners[eventName] || []) + .push(fn); + + return this; + }; + + /** + * Add an EventListener that's only called once. + * + * @method once + * @param eventName {String} Name of the event. + * @param callback {Function} Callback function. + */ + obj.once = function once(eventName, fn) { + this._listeners = this._listeners || {}; + + var self = this; + function onceHandlerWrapper() { + fn.apply(self.off(eventName, onceHandlerWrapper), arguments); + } + onceHandlerWrapper._originalHandler = fn; + + return this.on(eventName, onceHandlerWrapper); + }; + + /** + * Remove event listeners. + * + * @method off + * @alias removeEventListener + * @param eventName {String} The event we want to remove. + * @param callback {Function} The listener that we need to find. + */ + obj.off = obj.removeEventListener = function off(eventName, fn) { + this._listeners = this._listeners || {}; + + if(!this._listeners[eventName]) + return this; + + var list = this._listeners[eventName], + i = fn ? list.length : 0; + + while(i-- > 0) { + if(list[i] === fn || list[i]._originalHandler === fn) { + list.splice(i, 1); + } + } + + if(list.length === 0) { + delete this._listeners[eventName]; + } + + return this; + }; + + /** + * Remove all listeners or only the listeners for the specified event. + * + * @method removeAllListeners + * @param eventName {String} The event you want to remove all listeners for. + */ + obj.removeAllListeners = function removeAllListeners(eventName) { + this._listeners = this._listeners || {}; + + if(!this._listeners[eventName]) + return this; + + delete this._listeners[eventName]; + + return this; + }; + } +}; + +/** + * Creates an homogenous object for tracking events so users can know what to expect. + * + * @class Event + * @extends Object + * @constructor + * @param target {Object} The target object that the event is called on + * @param name {String} The string name of the event that was triggered + * @param data {Object} Arbitrary event data to pass along + */ +PIXI.Event = function(target, name, data) { + //for duck typing in the ".on()" function + this.__isEventObject = true; + + /** + * Tracks the state of bubbling propagation. Do not + * set this directly, instead use `event.stopPropagation()` + * + * @property stopped + * @type Boolean + * @private + * @readOnly + */ + this.stopped = false; + + /** + * Tracks the state of sibling listener propagation. Do not + * set this directly, instead use `event.stopImmediatePropagation()` + * + * @property stoppedImmediate + * @type Boolean + * @private + * @readOnly + */ + this.stoppedImmediate = false; + + /** + * The original target the event triggered on. + * + * @property target + * @type Object + * @readOnly + */ + this.target = target; + + /** + * The string name of the event that this represents. + * + * @property type + * @type String + * @readOnly + */ + this.type = name; + + /** + * The data that was passed in with this event. + * + * @property data + * @type Object + * @readOnly + */ + this.data = data; + + //backwards compat with older version of events + this.content = data; + + /** + * The timestamp when the event occurred. + * + * @property timeStamp + * @type Number + * @readOnly + */ + this.timeStamp = Date.now(); +}; + +/** + * Stops the propagation of events up the scene graph (prevents bubbling). + * + * @method stopPropagation + */ +PIXI.Event.prototype.stopPropagation = function stopPropagation() { + this.stopped = true; +}; + +/** + * Stops the propagation of events to sibling listeners (no longer calls any listeners). + * + * @method stopImmediatePropagation + */ +PIXI.Event.prototype.stopImmediatePropagation = function stopImmediatePropagation() { + this.stoppedImmediate = true; +}; diff --git a/app/Lib/Vendor/src/pixi/utils/Polyk.js b/app/Lib/Vendor/src/pixi/utils/Polyk.js new file mode 100644 index 0000000..838e6a2 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/utils/Polyk.js @@ -0,0 +1,168 @@ +/* + PolyK library + url: http://polyk.ivank.net + Released under MIT licence. + + Copyright (c) 2012 Ivan Kuckir + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + This is an amazing lib! + + Slightly modified by Mat Groves (matgroves.com); +*/ + +/** + * Based on the Polyk library http://polyk.ivank.net released under MIT licence. + * This is an amazing lib! + * Slightly modified by Mat Groves (matgroves.com); + * @class PolyK + */ +PIXI.PolyK = {}; + +/** + * Triangulates shapes for webGL graphic fills. + * + * @method Triangulate + */ +PIXI.PolyK.Triangulate = function(p) +{ + var sign = true; + + var n = p.length >> 1; + if(n < 3) return []; + + var tgs = []; + var avl = []; + for(var i = 0; i < n; i++) avl.push(i); + + i = 0; + var al = n; + while(al > 3) + { + var i0 = avl[(i+0)%al]; + var i1 = avl[(i+1)%al]; + var i2 = avl[(i+2)%al]; + + var ax = p[2*i0], ay = p[2*i0+1]; + var bx = p[2*i1], by = p[2*i1+1]; + var cx = p[2*i2], cy = p[2*i2+1]; + + var earFound = false; + if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) + { + earFound = true; + for(var j = 0; j < al; j++) + { + var vi = avl[j]; + if(vi === i0 || vi === i1 || vi === i2) continue; + + if(PIXI.PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) { + earFound = false; + break; + } + } + } + + if(earFound) + { + tgs.push(i0, i1, i2); + avl.splice((i+1)%al, 1); + al--; + i = 0; + } + else if(i++ > 3*al) + { + // need to flip flip reverse it! + // reset! + if(sign) + { + tgs = []; + avl = []; + for(i = 0; i < n; i++) avl.push(i); + + i = 0; + al = n; + + sign = false; + } + else + { + // window.console.log("PIXI Warning: shape too complex to fill"); + return null; + } + } + } + + tgs.push(avl[0], avl[1], avl[2]); + return tgs; +}; + +/** + * Checks whether a point is within a triangle + * + * @method _PointInTriangle + * @param px {Number} x coordinate of the point to test + * @param py {Number} y coordinate of the point to test + * @param ax {Number} x coordinate of the a point of the triangle + * @param ay {Number} y coordinate of the a point of the triangle + * @param bx {Number} x coordinate of the b point of the triangle + * @param by {Number} y coordinate of the b point of the triangle + * @param cx {Number} x coordinate of the c point of the triangle + * @param cy {Number} y coordinate of the c point of the triangle + * @private + * @return {Boolean} + */ +PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy) +{ + var v0x = cx-ax; + var v0y = cy-ay; + var v1x = bx-ax; + var v1y = by-ay; + var v2x = px-ax; + var v2y = py-ay; + + var dot00 = v0x*v0x+v0y*v0y; + var dot01 = v0x*v1x+v0y*v1y; + var dot02 = v0x*v2x+v0y*v2y; + var dot11 = v1x*v1x+v1y*v1y; + var dot12 = v1x*v2x+v1y*v2y; + + var invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + var u = (dot11 * dot02 - dot01 * dot12) * invDenom; + var v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + // Check if point is in triangle + return (u >= 0) && (v >= 0) && (u + v < 1); +}; + +/** + * Checks whether a shape is convex + * + * @method _convex + * @private + * @return {Boolean} + */ +PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign) +{ + return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign; +}; diff --git a/app/Lib/Vendor/src/pixi/utils/Utils.js b/app/Lib/Vendor/src/pixi/utils/Utils.js new file mode 100644 index 0000000..3cfcff8 --- /dev/null +++ b/app/Lib/Vendor/src/pixi/utils/Utils.js @@ -0,0 +1,213 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating + +// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel + +// MIT license + +/** + * A polyfill for requestAnimationFrame + * You can actually use both requestAnimationFrame and requestAnimFrame, + * you will still benefit from the polyfill + * + * @method requestAnimationFrame + */ + +/** + * A polyfill for cancelAnimationFrame + * + * @method cancelAnimationFrame + */ +(function(window) { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || + window[vendors[x] + 'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + + window.requestAnimFrame = window.requestAnimationFrame; +})(this); + +/** + * Converts a hex color number to an [R, G, B] array + * + * @method hex2rgb + * @param hex {Number} + */ +PIXI.hex2rgb = function(hex) { + return [(hex >> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; +}; + +/** + * Converts a color as an [R, G, B] array to a hex number + * + * @method rgb2hex + * @param rgb {Array} + */ +PIXI.rgb2hex = function(rgb) { + return ((rgb[0]*255 << 16) + (rgb[1]*255 << 8) + rgb[2]*255); +}; + +/** + * A polyfill for Function.prototype.bind + * + * @method bind + */ +if (typeof Function.prototype.bind !== 'function') { + Function.prototype.bind = (function () { + return function (thisArg) { + var target = this, i = arguments.length - 1, boundArgs = []; + if (i > 0) + { + boundArgs.length = i; + while (i--) boundArgs[i] = arguments[i + 1]; + } + + if (typeof target !== 'function') throw new TypeError(); + + function bound() { + var i = arguments.length, args = new Array(i); + while (i--) args[i] = arguments[i]; + args = boundArgs.concat(args); + return target.apply(this instanceof bound ? this : thisArg, args); + } + + bound.prototype = (function F(proto) { + if (proto) F.prototype = proto; + if (!(this instanceof F)) return new F(); + })(target.prototype); + + return bound; + }; + })(); +} + +/** + * A wrapper for ajax requests to be handled cross browser + * + * @class AjaxRequest + * @constructor + */ +PIXI.AjaxRequest = function() +{ + var activexmodes = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.3.0', 'Microsoft.XMLHTTP']; //activeX versions to check for in IE + + if (window.ActiveXObject) + { //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken) + for (var i=0; i 0 && (number & (number - 1)) === 0) // see: http://goo.gl/D9kPj + return number; + else + { + var result = 1; + while (result < number) result <<= 1; + return result; + } +}; + +PIXI.isPowerOfTwo = function(width, height) +{ + return (width > 0 && (width & (width - 1)) === 0 && height > 0 && (height & (height - 1)) === 0); + +}; diff --git a/app/Menu/Menu.js b/app/Menu/Menu.js new file mode 100644 index 0000000..62ae3cc --- /dev/null +++ b/app/Menu/Menu.js @@ -0,0 +1,435 @@ +define([ + "Game/Config/Settings", + "Lib/Utilities/ColorConverter", + "Lib/Utilities/Exception", + "Game/Client/PointerLockManager", + "Lib/Utilities/QuerySelector" +], + +function (Settings, ColorConverter, Exception, pointerLockManager, qs) { + + "use strict"; + + var instance = null + var quickstartChannelName = "Quickstart"; + + function Menu() { + + } + + Menu.prototype.init = function() { + instance = this; // Dum und Dümmer + + if(localStorage["player"]) { + var player = JSON.parse(localStorage["player"]); + if(player.nickname) { + qs.$("#nick").value = player.nickname; + } + } + + if(localStorage["customname"]) { + qs.$("#customname").value = localStorage["customname"]; + } + qs.$("#scoreLimit").value = Settings.CHANNEL_DEFAULT_SCORE_LIMIT; + qs.$("#userLimit").value = Settings.CHANNEL_DEFAULT_MAX_USERS; + + + qs.$("#refresh").onclick = refresh; + refresh(); + populateMaps(); + this.channelDestructionTimeout = null; + this.refreshInterval = setInterval(refresh, 5000); + + qs.$("#createbutton").onclick = function() { + show('#createform'); + return false; + }; + qs.$("#quickstartbutton").onclick = quickstart; + + var cancelButtons = qs.$$(".cancel"); + for (var i = 0; i < cancelButtons.length; i++) { + cancelButtons[i].onclick = function() { + show('#listform'); + return false; + }; + }; + + this.colorConverter = new ColorConverter(); + var c = qs.$("#nick"); + c.onchange = c.onkeyup = c.onblur = c.onclick = this.updatePrimaryColor.bind(this); + this.updatePrimaryColor({target:c}); + }; + + Menu.prototype.updatePrimaryColor = function(e) { + qs.$("#primarycolor").style.backgroundColor = "#" + this.colorConverter.getColorByName(e.target.value).toString(16); + }; + + Menu.prototype.onRun = function(channelName, nickname) { + throw new Exception("Menu onRun has to be overwritten"); + } + + window.onhashchange = function() { + if(window.location.hash) { + if(qs.$("#game").style.display == "block") { + window.location.reload(); + } + refresh(function(list) { + var channelName = unescape(window.location.hash.substr(1)); + + if (channelName == quickstartChannelName) { + quickstart(); + return; + } + + if(channelExists(list, channelName)) { + showCustomJoinForm() + } else { + alert("Channel \"" + channelName + "\" does not exist (anymore).") + window.location.href = "/"; + } + }); + } + } + + window.onload = window.onhashchange; + + var lastRefreshResponse; + function refresh(callback) { + + ajax("getChannels", {}, function(response) { + if(response != lastRefreshResponse) { + lastRefreshResponse = response; + populate(JSON.parse(response).success); + } + document.body.className = ""; + + if(typeof callback == 'function') { + callback(JSON.parse(response).success) + } + + }, function(status, responseText) { + console.error("getChannels error: ", responseText) + }); + + return false; + } + + function ajax(command, options, callback, errorCallback) { + try { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if(xhr.readyState == 4) { + if(xhr.status == 200) { + if(typeof callback == 'function') { + callback(xhr.responseText) + } + } else { + if(typeof errorCallback == 'function' && xhr.status == "400") { + errorCallback(xhr.status, xhr.responseText); + } else { + console.error("Ajax error: " + xhr.status + " " + xhr.responseText) + qs.$("#list").innerHTML = ""; + document.body.className = "offline"; + } + } + } + } + xhr.open("POST", "/api", true); + xhr.send(JSON.stringify({command:command, options:options})); + } catch(e) { + console.error(e) + } + } + + function populate(list) { + + + var html = ""; + if(list.length > 0) { + for (var i = 0; i < list.length; i++) { + + var channel = list[i]; + var fullState = channel.playerCount >= channel.maxUsers; + var fullString = fullState ? " Full" : ""; + var fullStyle = fullState ? 'class="full"' : ""; + var players = channel.playerCount + ? "Player:
- " + channel.players.join("
- ") + "
" + : ""; + + html += ""; + html += "" + channel.channelName + ""; + html += "death match"; + html += "" + channel.playerCount + fullString + players + ""; + html += ""; + }; + } else { + html += "No channels found."; + } + + qs.$("#list").innerHTML = html; + } + + function populateMaps() { + ajax("getMaps", {}, function(responseText) { + var maps = JSON.parse(responseText).success; + var html = ""; + for (var i = 0; i < maps.length; i++) { + var map = maps[i]; + html += "
  • "; + }; + + qs.$("#maps").innerHTML = html; + }, function(status, responseText) { + console.error("getMaps error:", status, responseText); + }); + } + + qs.$("form#listform").onsubmit = function(e) { + try { + var nickname = qs.$("#nick").value; + var channelName = getSelectedChannel(); + join(nickname, channelName); + } catch(e) { + console.error(e) + } + + return false; + } + + qs.$("form#createform").onsubmit = function(e) { + try { + + var options = { + channelName: qs.$("#customname").value, + levelUids: getSelectedMaps(), + maxUsers: parseInt(qs.$("#userLimit").value, 10), + scoreLimit: parseInt(qs.$("#scoreLimit").value, 10) + }; + + create(options, onCreateSuccess); + } catch(e) { + console.error(e) + } + + return false; + } + + qs.$("form#customjoinform").onsubmit = function(e) { + try { + var nickname = qs.$("#nick").value; + var channelName = qs.$("#customname").value; + join(nickname, channelName); + } catch(e) { + console.error(e); + } + + return false; + } + + function onCreateSuccess(options) { + window.location.hash = options.channelName; + startTimer(options.timeout); + } + + function showCustomJoinForm() { + qs.$("#customname").value = unescape(window.location.hash.substr(1)); + qs.$("#link").value = window.location.href; + show("#customjoinform"); + } + + function show(id) { + qs.$("#createform").style.display = "none"; + qs.$("#listform").style.display = "none"; + qs.$("#customjoinform").style.display = "none"; + qs.$("#game").style.display = "none"; + + if(id != "#customjoinform") { + history.pushState("", document.title, window.location.pathname); + } + + qs.$(id).style.display = "block"; + } + + function quickstart() { + refresh(function(list){ + var defaultChannelName = quickstartChannelName; + history.pushState("", document.title, window.location.pathname + "#" + defaultChannelName); + var nickname = qs.$("#nick").value; + + if(!nickname) { + nickname = "Guest" + (Math.floor(Math.random() * 899) + 100) + } + + if(!channelExists(list, defaultChannelName)) { + + var options = { + channelName: defaultChannelName, + levelUids: getSelectedMaps(), + maxUsers: parseInt(qs.$("#userLimit").value, 10), + scoreLimit: parseInt(qs.$("#scoreLimit").value, 10) + }; + + create(options, function() { + join(nickname, defaultChannelName); // only called on success + }); + } else { + join(nickname, defaultChannelName); + } + }); + return false; + } + + function startTimer(seconds) { + var now = new Date(); + var end = new Date(now.getTime() + seconds * 1000); + instance.channelDestructionTimeout = setInterval(function() { + now = new Date(); + var diff = new Date(end.getTime() - now.getTime()); + if(diff.getTime() < 0) { + alert("Your channel has timed out."); + window.location.href = "/"; + } else { + qs.$("#timeout").innerHTML = " within " + formatDate(diff) + " minutes"; + } + }, 1000); + } + + function formatDate(date) { + var minutes = date.getMinutes(); + var seconds = date.getSeconds(); + if(minutes < 10) minutes = "0" + minutes; + if(seconds < 10) seconds = "0" + seconds; + + return minutes + ":" + seconds; + } + + function channelExists(list, channelName) { + for (var i = 0; i < list.length; i++) { + var channel = list[i]; + if(channel.channelName == channelName) { + return true; + } + } + return false; + } + + function validateForJoin(nickname, channelName) { + if(!nickname || nickname.length < 3) { + alert("Nickname too short") + return false; + } + if(!channelName) { + alert('No channel name provided'); + return false; + } + return true; + } + + function validateForCreate(options) { + + return true; + // great validation on server side does it all. + + /* + if(options.levelUids.length < 1) { + alert("Please choose at least one map.") + return false; + } + + if(!options.channelName || options.channelName.length < 3) { + alert("Please provide a channel name of at least 3 characters.") + return false; + } + + if(!parseInt(options.maxUsers) > 1 || !parseInt(options.maxUsers) < 20) { + alert("Number of users must be larger than 1 and smaller than 20."); + return false; + } + + if(!parseInt(options.scoreLimit) > 1 || !parseInt(options.scoreLimit) < 99) { + alert("Score limit must be larger than 1 and smaller than 99."); + return false; + } + return true; + */ + } + + function getSelectedMaps() { + var maps = []; + var checkboxes = document.querySelectorAll("form#createform input[name=maps]"); + for (var i = 0; i < checkboxes.length; i++) { + var checkbox = checkboxes[i]; + if(checkbox.checked) { + maps.push(checkbox.value); + } + }; + return maps; + } + + function getSelectedChannel() { + var name = null; + var radios = document.querySelectorAll("form#listform input[name=channel]"); + for (var i = 0; i < radios.length; i++) { + var radio = radios[i]; + if(radio.checked) { + name = radio.value; + break; + } + }; + return name + } + + function join(nickname, channelName) { + if(validateForJoin(nickname, channelName)) { + localStorage["player"] = JSON.stringify({ + nickname: nickname + }); + localStorage["channel"] = JSON.stringify({ + name: channelName + }); + + //window.location.href = "/game.html"; + qs.$("#menu").style.display = "none"; + qs.$("#game").style.display = "block"; + instance.onRun(channelName, nickname); // Dumm und dümmer + + if(instance.refreshInterval) { + clearInterval(instance.refreshInterval); + } + + if(instance.channelDestructionTimeout) { + clearInterval(instance.channelDestructionTimeout); + } + + pointerLockManager.request(); + } + } + + function create(options, callback) { + + if(validateForCreate(options)) { + + options["minUsers"] = 1; + localStorage["customname"] = options.channelName; + + ajax("createChannel", options, function(responseText) { + if(typeof callback == 'function') { + callback(JSON.parse(responseText).success); + } + }, function(status, responseText) { + console.log(responseText) + alert(JSON.parse(responseText).error) + }); + } + } + + qs.$("#canvas").onclick = function(){ + pointerLockManager.request(); + }; + + return Menu; + +}); \ No newline at end of file diff --git a/app/Server/Api.js b/app/Server/Api.js index 23bb3cf..f237c26 100644 --- a/app/Server/Api.js +++ b/app/Server/Api.js @@ -2,12 +2,15 @@ define([ "Lib/Utilities/NotificationCenter", "Lib/Utilities/Protocol/Helper", "Lib/Utilities/Validate", - "Lib/Utilities/Options", - "Game/Config/Settings" + "Lib/Utilities/OptionsHelper", + "Game/Config/Settings", + "fs" ], -function (Nc, ProtocolHelper, validate, Options, Settings) { - +function (nc, ProtocolHelper, validate, optionsHelper, Settings, FileSystem) { + + "use strict"; + function Api(coordinator) { this.coordinator = coordinator; this.isError = false; @@ -31,12 +34,15 @@ function (Nc, ProtocolHelper, validate, Options, Settings) { switch(command) { case "getChannels": - output = this.coordinator.getChannels(); + output = this.getChannels(); break; case "createChannel": // FIXME: sanitize input output = this.createChannel(message.options); break; + case "getMaps": + output = this.getMaps(); + break; default: this.isError = true; output = "Command not found"; @@ -46,6 +52,10 @@ function (Nc, ProtocolHelper, validate, Options, Settings) { this.output = output; } + Api.prototype.getChannels = function() { + return this.coordinator.getChannels(); + }; + Api.prototype.createChannel = function(options) { var allowedOptionKeys = [ @@ -73,7 +83,7 @@ function (Nc, ProtocolHelper, validate, Options, Settings) { } for(var i = 0; i < options.levelUids.length; i++) { - if(!validate(options.levelUids[i], {type: 'string', in: ['stones2', 'debug']})) { + if(!validate(options.levelUids[i], {type: 'string', in: this.getMaps()})) { this.isError = true; return "Could not create channel, invalid map (" + options.levelUids[i] + ")."; } @@ -86,34 +96,32 @@ function (Nc, ProtocolHelper, validate, Options, Settings) { newOptions.maxUsers = options.maxUsers; } else { this.isError = true; - return "Could not create channel, Max users invalid. (Limited to " + Settings.CHANNEL_MAX_USERS + " users)"; + return "Could not create channel, user limit invalid. Limited to " + Settings.CHANNEL_MAX_USERS + " users"; } if(validate(options.minUsers, {optional: true, type: 'number', min: 0, max: Settings.CHANNEL_MAX_USERS})) { newOptions.minUsers = options.minUsers; } else { this.isError = true; - return "Could not create channel, Max users too high. Limited to: " + Settings.CHANNEL_MAX_USERS; + return "Could not create channel, minimal users limit too high. Limited to: " + Settings.CHANNEL_MAX_USERS; } // Limits - if(validate(options.scoreLimit, {type: 'number', min: 1, max: 999})) { + var scoreLimitPreferences = {type: 'number', min: 1, max: 999}; + if(validate(options.scoreLimit, scoreLimitPreferences)) { newOptions.scoreLimit = options.scoreLimit; } else { this.isError = true; - return "Could not create channel, score limit (" + options.scoreLimit + ")."; + return "Could not create channel, score limit (" + options.scoreLimit + ") must be between " + scoreLimitPreferences.min + " and " + scoreLimitPreferences.max; } - - var defaultOptions = { maxUsers: Settings.CHANNEL_DEFAULT_MAX_USERS, minUsers: 0, scoreLimit: Settings.CHANNEL_DEFAULT_SCORE_LIMIT }; - options = Options.merge(options, defaultOptions); - + options = optionsHelper.merge(options, defaultOptions); var result = this.coordinator.createChannel(options); if(result !== false) { @@ -134,14 +142,25 @@ function (Nc, ProtocolHelper, validate, Options, Settings) { } return JSON.stringify(output); - }; Api.prototype.getContentType = function() { return "application/json"; }; - + Api.prototype.getMaps = function(callback) { + + var list = FileSystem.readdirSync(Settings.MAPS_PATH); + var maps = []; + for (var i = 0; i < list.length; i++) { + var fileinfo = list[i].split("."); + if(fileinfo[1] == "json") { + maps.push(fileinfo[0]); + } + }; + //return ["stones"]; + return maps.sort(); + }; return Api; diff --git a/app/Server/Coordinator.js b/app/Server/Coordinator.js index 407b539..20d8c93 100644 --- a/app/Server/Coordinator.js +++ b/app/Server/Coordinator.js @@ -1,17 +1,18 @@ define([ "Server/User", - "Game/Channel/Channel", "Server/PipeToChannel", "Lib/Utilities/NotificationCenter", "Game/Config/Settings" ], -function (User, Channel, PipeToChannel, Nc, Settings) { +function (User, PipeToChannel, nc, Settings) { + + "use strict"; function Coordinator() { this.channelPipes = {}; - Nc.on(Nc.ns.server.events.controlCommand.coordinator, this.onMessage, this); + nc.on(nc.ns.server.events.controlCommand.coordinator, this.onMessage, this); console.checkpoint('create Coordinator'); } @@ -20,9 +21,9 @@ function (User, Channel, PipeToChannel, Nc, Settings) { new User(socketLink, this); } - Coordinator.prototype.assignUserToChannel = function (user, channelName) { - var channelPipe = this.channelPipes[channelName]; - user.setChannelPipe(channelPipe); + // was assignUserToChannel... + Coordinator.prototype.getChannelPipeByName = function (channelName) { + return this.channelPipes[channelName]; } Coordinator.prototype.onDestroyPipe = function(channelName) { @@ -32,9 +33,18 @@ function (User, Channel, PipeToChannel, Nc, Settings) { Coordinator.prototype.getChannels = function(options) { var list = []; for (var channelName in this.channelPipes) { - list.push({ - name: channelName - }); + + var options = this.channelPipes[channelName].options; + + var playerNames = []; + var users = this.channelPipes[channelName].getUsers(); + for (var i = 0; i < users.length; i++) { + playerNames[i] = users[i].options.nickname; + }; + options.players = playerNames; + options.playerCount = options.players.length; + + list.push(options); } return list; } diff --git a/app/Server/PipeToChannel.js b/app/Server/PipeToChannel.js index 94bc11b..19bee94 100755 --- a/app/Server/PipeToChannel.js +++ b/app/Server/PipeToChannel.js @@ -3,16 +3,22 @@ define([ "child_process" ], -function (Nc, childProcess) { +function (nc, childProcess) { - var fork = childProcess.fork; + "use strict"; function PipeToChannel (options) { this.fork = null; + this.options = options; + this.users = []; try { - this.fork = fork('channel.js'); + this.fork = childProcess.fork('channel.js' + /*, { + execArgv: ['--debug=5859'] + }*/ + ); } catch (err) { throw 'Failed to fork channel! (' + err + ')'; } @@ -22,8 +28,6 @@ function (Nc, childProcess) { this.send('channel/' + options.channelName, { CREATE: true, options: options }); this.fork.on('message', this.onMessage.bind(this)); - - var self = this; } // While creating user @@ -46,18 +50,42 @@ function (Nc, childProcess) { this.fork.send(message); } + PipeToChannel.prototype.isFull = function() { + return this.users.length >= this.options.maxUsers; + }; + PipeToChannel.prototype.onMessage = function (message) { switch(message.recipient) { case 'coordinator': - Nc.trigger(Nc.ns.server.events.controlCommand.coordinator, message.data); + nc.trigger(nc.ns.server.events.controlCommand.coordinator, message.data); break; default: - Nc.trigger(Nc.ns.server.events.controlCommand.user + message.recipient, message.data); + nc.trigger(nc.ns.server.events.controlCommand.user + message.recipient, message.data); break; } } + PipeToChannel.prototype.addUser = function(user) { + this.users.push(user); + this.send('channel', { addUser: user.options }); + }; + + PipeToChannel.prototype.removeUser = function(user) { + for(var i = 0; i < this.users.length; i++) { + if(this.users[i] === user) { + this.users.splice(i, 1); + break; + } + } + + this.send('channel', { releaseUser: user.id }); + }; + + PipeToChannel.prototype.getUsers = function() { + return this.users; + }; + return PipeToChannel; }); \ No newline at end of file diff --git a/app/Server/User.js b/app/Server/User.js index 3f3b47f..1ba04bc 100644 --- a/app/Server/User.js +++ b/app/Server/User.js @@ -4,7 +4,9 @@ define([ "Lib/Utilities/NotificationCenter" ], -function (Parent, ProtocolHelper, Nc) { +function (Parent, ProtocolHelper, nc) { + + "use strict"; function User (socketLink, coordinator) { Parent.call(this, socketLink.id, {}); @@ -12,24 +14,33 @@ function (Parent, ProtocolHelper, Nc) { this.coordinator = coordinator; this.socketLink = socketLink; this.channelPipe = null; + this.options = null; socketLink.on('message', this.onMessage.bind(this)); socketLink.on('disconnect', this.onDisconnect.bind(this)); - Nc.on(Nc.ns.server.events.controlCommand.user + this.id, this.socketLink.send, this.socketLink); + nc.on(nc.ns.server.events.controlCommand.user + this.id, this.socketLink.send, this.socketLink); } User.prototype = Object.create(Parent.prototype); +/* User.prototype.setChannelPipe = function(channelPipe) { if(channelPipe) { - this.channelPipe = channelPipe; + if (channelPipe.isWithinUserLimit()) { + this.channelPipe = channelPipe; + this.channelPipe.addUser(this); + } else { + var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel is full"}); + this.socketLink.send(message); + } + } else { var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel not found"}); this.socketLink.send(message); } }; - + */ // Socket callbacks @@ -38,12 +49,13 @@ function (Parent, ProtocolHelper, Nc) { } User.prototype.onDisconnect = function () { + if(!this.channelPipe) { - console.warn("Disconnecting user without a channel."); + console.warn("Disconnecting user without a channel. (Maybe channel was full)"); return; } - this.channelPipe.send('channel', { releaseUser: this.id }); + this.channelPipe.removeUser(this); } @@ -51,18 +63,29 @@ function (Parent, ProtocolHelper, Nc) { // Remember: control commands are coordinator relevant commands User.prototype.onJoin = function(options) { - this.coordinator.assignUserToChannel(this, options.channelName); - if(!this.channelPipe) { - console.warn("Can not join user because channel (" + options.channelName + ") does not exist.") + var channelPipe = this.coordinator.getChannelPipeByName(options.channelName); + + if(!channelPipe) { + var message = ProtocolHelper.encodeCommand("joinError", {message:"Channel " + options.channelName + " not found."}); + this.socketLink.send(message); return; } + if (channelPipe.isFull()) { + var message = ProtocolHelper.encodeCommand("joinError", {message:"Sorry! Channel " + options.channelName + " is full."}); + this.socketLink.send(message); + return; + } + + this.channelPipe = channelPipe; + var userOptions = { id: this.id, nickname: options.nickname } - this.channelPipe.send('channel', { addUser: userOptions }); + this.options = userOptions; + this.channelPipe.addUser(this); }; /* FIXME: watch out and check in wich direction game and control commands flow */ @@ -74,7 +97,7 @@ function (Parent, ProtocolHelper, Nc) { User.prototype.onPing = function(timestamp) { var message = ProtocolHelper.encodeCommand("pong", timestamp); - Nc.trigger(Nc.ns.server.events.controlCommand.user + this.id, message); + nc.trigger(nc.ns.server.events.controlCommand.user + this.id, message); }; return User; diff --git a/channel.js b/channel.js index 3294623..a94689f 100755 --- a/channel.js +++ b/channel.js @@ -4,7 +4,11 @@ var requirejs = require('requirejs'); requirejs.config({ nodeRequire: require, baseUrl: 'app', - deps: ['Lib/Utilities/Extensions'] + deps: ['Lib/Utilities/Channel/Extensions'], + paths: { + text: 'Lib/Vendor/RequireJs/Plugin/Text', + json: 'Lib/Vendor/RequireJs/Plugin/Json', + }, }); var inspector = {}; diff --git a/client.js b/client.js index 6bc6999..f96233a 100755 --- a/client.js +++ b/client.js @@ -1,35 +1,45 @@ -GLOBALS = { context: "Client" }; +"use strict"; + +var GLOBALS = { context: "Client" }; requirejs.config({ baseUrl: 'app', - deps: ['Lib/Utilities/Extensions'], - waitSeconds: 0 + deps: ['Lib/Utilities/Client/Extensions'], + waitSeconds: 0, + paths: { + text: 'Lib/Vendor/RequireJs/Plugin/Text', + json: 'Lib/Vendor/RequireJs/Plugin/Json', + screenfull: "/screenfull", + chart: "/chart", + socketio: "/socket.io/socket.io" + }, }); -var inspector = {}; +if(!Chuck) var Chuck = {}; +Chuck.inspector = {}; requirejs([ "Game/Client/Networker", "Lib/Vendor/SocketIO", "Game/Config/Settings", "Lib/Utilities/Exception", - "Lib/Vendor/Pixi" + "Lib/Utilities/NotificationCenter", + "Menu/Menu" ], -function (Networker, SocketIO, Settings, Exception, PIXI) { +function (Networker, io, Settings, Exception, nc, Menu) { - var options = { - "reconnect": false, - "reconnection delay": 500, - "max reconnection attempts": 10, - "transports": [ - "websocket", - "flashsocket" - ] - }; - var socket = SocketIO.connect("/", options); - var networker = new Networker(socket); - inspector.networker = networker; - inspector.settings = Settings; - inspector.resetLevel = function() { networker.sendGameCommand("resetLevel"); } -}); \ No newline at end of file + var menu = new Menu(); + menu.onRun = function(channelName, nickname) { + var options = { + transports: ["websocket"] // v4: only use websocket, flashsocket is gone + }; + var socket = io("/", options); + var networker = new Networker(socket, channelName, nickname); + Chuck.inspector.networker = networker; + Chuck.inspector.settings = Settings; + Chuck.inspector.nc = nc; + Chuck.inspector.resetLevel = function() { networker.sendGameCommand("resetLevel"); } + } + menu.init(); +}); diff --git a/config/build-profile.js b/config/build-profile.js new file mode 100644 index 0000000..e650ccc --- /dev/null +++ b/config/build-profile.js @@ -0,0 +1,16 @@ +({ + baseUrl: "../app", + paths: { + "text": 'Lib/Vendor/RequireJs/Plugin/Text', + "json": 'Lib/Vendor/RequireJs/Plugin/Json', + "screenfull": "../node_modules/screenfull/dist/screenfull", + "socketio": "../node_modules/socket.io/node_modules/socket.io-client/dist/socket.io", + "chart": "../node_modules/chart.js/Chart" + }, + name: "../client", + out: "../build/client.min.js", + onBuildRead: function (moduleName, path, contents) { + var contents = contents.replace(/\" \+ GLOBALS.context \+ \"/g, "Client"); + return contents; + } +}) diff --git a/config/ecosystem-dev.json5 b/config/ecosystem-dev.json5 new file mode 100644 index 0000000..d09f2ec --- /dev/null +++ b/config/ecosystem-dev.json5 @@ -0,0 +1,42 @@ +{ + /** + * This is a sample configuration file for PM2 + */ + + /** + * Here we declare the apps that must be managed by PM2 + * All options are listed here: + * https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#json-app-declaration + * + */ + apps : [ + + // Development + { + name : "chuck-dev", + script : "server.js", + env : { + NODE_ENV: "dev", + PORT: "1234" + } + } + + ], + + + /** + * PM2 help you to deploy apps over your servers + * For more help go to : + * https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#deployment-pm2--090 + */ + deploy : { + dev : { + user : "chuck", + host : "fuuuuu.de", + ref : "origin/master", + repo : "https://github.com/logsol/chuck.js.git", + path : "/home/chuck/development", + "post-deploy" : "npm install && pm2 startOrRestart config/ecosystem-dev.json5 --env dev", + } + } +} diff --git a/config/ecosystem.json5 b/config/ecosystem.json5 new file mode 100644 index 0000000..0f48026 --- /dev/null +++ b/config/ecosystem.json5 @@ -0,0 +1,50 @@ +{ + /** + * This is a sample configuration file for PM2 + */ + + /** + * Here we declare the apps that must be managed by PM2 + * All options are listed here: + * https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#json-app-declaration + * + */ + apps : [ + + // Production + { + name : "chuck", + script : "server.js", + env_production : { + NODE_ENV: "production", + PORT: "1235" + } + } + + ], + + + /** + * PM2 help you to deploy apps over your servers + * For more help go to : + * https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#deployment-pm2--090 + */ + deploy : { + production : { + user : "chuck", + host : "fuuuuu.de", + ref : "origin/master", + repo : "https://github.com/logsol/chuck.js.git", + path : "/home/chuck/production", + "post-deploy" : "npm install && NODE_ENV=production sh scripts/build.sh && pm2 startOrRestart config/ecosystem.json5 --env production" + }, + production2 : { + user : "jeena", + host : "chuck2.fuuuuu.de", + ref : "origin/master", + repo : "https://github.com/logsol/chuck.js.git", + path : "/home/jeena/chuck/production", + "post-deploy" : "npm install && NODE_ENV=production sh scripts/build.sh && pm2 startOrRestart config/ecosystem.json5 --env production" + } + } +} diff --git a/deploy.sh b/deploy.sh deleted file mode 100755 index 8d99702..0000000 --- a/deploy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# cd /home/logsol/projects/js/chuck.js/ && forever stop server.js -# cd /home/logsol/projects/js/chuck.js/ && forever start server.js && echo "forever - just restarted server" - -cd /home/logsol/projects/js/chuck.js/ && forever restart server.js - diff --git a/lab/Worker.js b/lab/Worker.js index 62483bb..04585b1 100755 --- a/lab/Worker.js +++ b/lab/Worker.js @@ -6,7 +6,7 @@ define([ "Lib/Utilities/NotificationCenter" ], -function (Parent, ProtocolHelper, GameController, User, Nc) { +function (Parent, ProtocolHelper, GameController, User) { function Worker () { //this.socketLink = socketLink; @@ -40,7 +40,7 @@ function (Parent, ProtocolHelper, GameController, User, Nc) { } }, this); - Nc.on(Nc.ns.client.to.server.gameCommand.send, this.sendGameCommand, this); + nc.on(nc.ns.client.to.server.gameCommand.send, this.sendGameCommand, this); } Worker.prototype.sendCommand = function (command, options) { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3f2a506 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2352 @@ +{ + "name": "chuck.js", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "chuck.js", + "version": "0.1.0", + "dependencies": { + "chart.js": "^4.4.0", + "express": "^4.18.2", + "requirejs": "^2.3.6", + "screenfull": "^6.0.2", + "socket.io": "^4.7.4" + }, + "devDependencies": { + "nodemon": "^3.0.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "24.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", + "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chart.js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/requirejs": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz", + "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==", + "bin": { + "r_js": "bin/r.js", + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/screenfull": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz", + "integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, + "@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "24.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", + "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "requires": { + "undici-types": "~7.8.0" + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true + }, + "body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, + "chart.js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "requires": { + "@kurkle/color": "^0.3.0" + } + }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, + "engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "requires": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "dependencies": { + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + }, + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==" + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "requires": { + "side-channel": "^1.0.6" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "requirejs": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz", + "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "screenfull": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-6.0.2.tgz", + "integrity": "sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==" + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + }, + "send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "requires": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + } + }, + "simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "requires": { + "debug": "~4.3.4", + "ws": "~8.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "requires": {} + } + } +} diff --git a/package.json b/package.json index 21de95a..6fbcbb3 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { "name": "chuck.js", "author": "logsol ", - "contributors": ["Jeena Paradies (https://jeena.net)"], + "contributors": [ + "Jeena Paradies (https://jeena.net)" + ], "description": "Multiplayer browser jump and run game", "version": "0.1.0", "homepage": "http://chuck-game.tumblr.com/", @@ -13,17 +15,28 @@ "url": "https://github.com/logsol/chuck.js/issues", "email": "fartman@gmx.de" }, - "main": "", + "main": "server.js", "dependencies": { - "socket.io": ">= 0.9.6", - "node-static": ">= 0.6.0", - "requirejs": "= 2.0.4", - "node-fork": ">= 0.4.2", - "screenfull": ">= 1.0.4" + "socket.io": "^4.7.4", + "express": "^4.18.2", + "requirejs": "^2.3.6", + "screenfull": "^6.0.2", + "chart.js": "^4.4.0" + }, + "devDependencies": { + "nodemon": "^3.0.2" }, - "devDependencies": {}, "optionalDependencies": {}, - "engine": "node >= 0.8.4", - "config": { "port": "1234" }, - "private": true + "engines": { + "node": ">=16.0.0" + }, + "config": { + "port": "1234" + }, + "private": true, + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js", + "prestart": "scripts/build.sh" + } } diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..4b58bcb --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash +if env | grep -q "^NODE_ENV=production$" +then + echo "[ PRODUCTION ]" + mkdir -p build + echo "- compiling client scripts.." + rm -f "build/client.min.js.gz" + node_modules/requirejs/bin/r.js -o config/build-profile.js \ + && gzip -c build/client.min.js > build/client.min.js.gz \ + && echo "- done." +else + echo "[ DEVELOPMENT ]" +fi diff --git a/scripts/core_analyzer.js b/scripts/core_analyzer.js new file mode 100644 index 0000000..3885d12 --- /dev/null +++ b/scripts/core_analyzer.js @@ -0,0 +1,247 @@ +/* + * This will generate a core/channel/client overview of a class + * + * usage: + * node scripts/core_analyzer.js [relative/path/class.js] -> relative path from core/client/channel split + * + * example: + * node scripts/core_analyzer.js GameObjects/Item.js + * + * example result: + * + * | CHANNEL | CORE | CLIENT + * |---------------------------------------|---------------------------------------|-------------------------------- + * | | getBodyDef | + * | | getFixtureDef | + * | | createFixture | + * | | flip | flip + * | beingGrabbed | beingGrabbed | + * | beingReleased | beingReleased | + * | onCollisionChange | onCollisionChange | + * | | reposition | + * | | getGrabPoint | + * | | throw | + * | | accelerateBody | + * | | destroy | destroy + * | | | createMesh + * | | | render + * | getLastMovedBy | | + * | setLastMovedBy | | + * | isGrabbingAllowed | | + * | isReleasingAllowed | | + * + */ + +var fs = require('fs'); +var util = require('util'); +var esprima = require('esprima'); +var escodegen = require('escodegen'); + +var info = []; +var overview = {}; + +var generatorOption = { + format: { + indent: { + style: ' ', + base: 0, + adjustMultilineComment: true//false + }, + newline: '\n', + space: ' ', + json: false, + renumber: false, + hexadecimal: false, + quotes: 'single', + escapeless: false, + compact: false, + parentheses: true, + semicolons: true, + safeConcatenation: true + }, + moz: { + starlessGenerator: false, + parenthesizedComprehensionBlock: false, + comprehensionExpressionStartsWithAssignment: false + }, + parse: null, + comment: true, + sourceMap: undefined, + sourceMapRoot: null, + sourceMapWithCode: false, + file: undefined, + directive: false, + verbatim: undefined +}; + +function readFile (path, category, fileName) { + //console.log("+++++ " + moduleName + " ++++++"); + var contents = fs.readFileSync(path + "/" + category + "/" + fileName); + contents = contents.toString(); + + getMethods(path, fileName, contents, category); +} + +function getMethods (path, fileName, contents, category) { + + category = category.toLowerCase(); + + overview[category] = []; + + var fullPath = path + "/" + fileName; + var moduleName = fileName.split(".js").join(""); + var tree = esprima.parse(contents); + + // find moduleId from return statement on module level + var module = tree.body[0].expression.arguments[1].body.body; + var moduleId = findModuleId(module); + + if (!moduleId) { + info.push("could not find moduleId in: " + fullPath); + return; + } + + if (moduleId == "Parent") { + info.push("not optimizing empty module (returning Parent) in: " + fullPath); + return; + } + + // find constructor + var constructorPosition = findConstructorPosition(module, moduleId); + if (constructorPosition === false) { + info.push("could not find constructor in: " + fileName) + return; + } + var constructor = module[constructorPosition]; + + + + for (var j = 0; j < module.length; j++) { + var expression = module[j]; + + if (expression.type == "ExpressionStatement") { + if(expression.expression && expression.expression.right) { + if(expression.expression.right.type == "FunctionExpression") { + overview[category].push(expression.expression.left.property.name); + } + } + } + } + + //console.log(escodegen.generate(tree, generatorOption)); +} + +function findModuleId (module) { + var moduleId = false; + for (var j = 0; j < module.length; j++) { + var expression = module[j]; + + //console.log(util.inspect(expression, { showHidden: true, depth: 4 })); + + if (expression.type == "ReturnStatement") { + + if(expression.argument.type == "Identifier") { + + // for return Module; + moduleId = expression.argument.name; + break; + + } else if (expression.argument.type == "NewExpression") { + + // for return new Module; + moduleId = expression.argument.callee.name; + break; + + } else { + info.push("Unexpected return type at module level. " + fullPath) + } + } + } + return moduleId; +}; + +function findConstructorPosition (module, moduleId) { + + for (var j = 0; j < module.length; j++) { + var expression = module[j]; + + if (expression.type == "FunctionDeclaration" && expression.id.name == moduleId) { + return j; + } + } + return false; +} + + +var superOverview = {}; +function display (category) { + + var all = ["core", "client", "channel"]; + var removeIndex = all.indexOf(category); + all.splice(removeIndex, 1); + + for (var k in overview[category]) { + + var name = overview[category][k]; + + if(superOverview.hasOwnProperty(name)) { + continue; + } + + var first = all[0]; + var second = all[1]; + + fv = 0 + (overview[first].indexOf(name) !== -1); + sv = 0 + (overview[second].indexOf(name) !== -1); + + superOverview[name] = {}; + + superOverview[name][category] = 1; + superOverview[name][first] = fv; + superOverview[name][second] = sv; + superOverview[name]['count'] = 1 + fv + sv; + } +} + +function show(){ + var alle = ['channel', 'core', 'client']; + console.log("| CHANNEL | CORE | CLIENT"); + console.log("|---------------------------------------|---------------------------------------|--------------------------------"); + for (var method in superOverview) { + var line = ""; + + for (var i = 0; i < alle.length; i++) { + if (superOverview[method] && superOverview[method].hasOwnProperty(alle[i])) { + + line += "| " ; + + if(superOverview[method][alle[i]] === 1) { + line += method; + line += Array(39 - method.length).join(" "); + } else { + line += Array(39).join(" "); + } + + + } else { + + } + + } + console.log(line) + } +} + + + +readFile("app/Game", "Core", process.argv[2]); +readFile("app/Game", "Channel", process.argv[2]); +readFile("app/Game", "Client", process.argv[2]); + +display("core") +display("client") +display("channel") + +console.log("\n") +show() +console.log("\n") \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..60b20ff --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Get dir of this script +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.. + +if [ -z "$1" ] +then + pm2 deploy $DIR/config/ecosystem-dev.json5 dev + exit +fi + +if [ "$1" == "production" ] +then + pm2 deploy $DIR/config/ecosystem.json5 production + exit +fi + +if [ "$1" == "production2" ] +then + pm2 deploy $DIR/config/ecosystem.json5 production2 + exit +fi + +echo "Case ($1) not defined. doing nothing." \ No newline at end of file diff --git a/scripts/optimize.js b/scripts/optimize.js new file mode 100644 index 0000000..63d0a76 --- /dev/null +++ b/scripts/optimize.js @@ -0,0 +1,218 @@ +/* + * This is supposed to be a code optimizer. + * + * usage: + * node scripts/optimize.js + * + * based on https://developers.google.com/speed/articles/optimizing-javascript + * I wanted to automatically replace properties as discribed under "Initializing instance variables" + * + * So far it is the only thing this script does, but the potential for more is there. + * Also it does not (yet) write to any files, only outputs the results in the console. + * + * The script fully disasembles the entire codebase (with esprima) and reconstructs (via escodegen) it + * with a few optimisations unfortunately it loses ambiguous new lines and such, so that it might + * not be entirely usable as a code replacing script - it could perhaps be used as a compile script in + * production + * + * But it has also nice things as at will add semicolons everywhere automatically and will + * definately keep a solid indentation style for everything. Since it lacks a lot of extra + * \n newlines it looks quite cluttered though. + * + */ + +var fs = require('fs'); +var util = require('util'); +var esprima = require('esprima'); +var escodegen = require('escodegen'); + +var info = []; + +var generatorOption = { + format: { + indent: { + style: ' ', + base: 0, + adjustMultilineComment: true//false + }, + newline: '\n', + space: ' ', + json: false, + renumber: false, + hexadecimal: false, + quotes: 'single', + escapeless: false, + compact: false, + parentheses: true, + semicolons: true, + safeConcatenation: true + }, + moz: { + starlessGenerator: false, + parenthesizedComprehensionBlock: false, + comprehensionExpressionStartsWithAssignment: false + }, + parse: null, + comment: true, + sourceMap: undefined, + sourceMapRoot: null, + sourceMapWithCode: false, + file: undefined, + directive: false, + verbatim: undefined +}; + +function readdir (path) { + var filesNames = fs.readdirSync(path) + + for (var i = 0; i < filesNames.length; i++) { + + var fileName = filesNames[i]; + var fullPath = path + "/" + fileName; + + if(path == "app/Lib/Vendor"){ + continue; + } + + if(fileName.indexOf(".js") == -1) { + var stats = fs.lstatSync(fullPath); + if (stats.isDirectory()) { + readdir(fullPath); + } + continue; + } + + readFile(path, fileName); + }; +} + +function readFile (path, fileName) { + //console.log("+++++ " + moduleName + " ++++++"); + var contents = fs.readFileSync(path + "/" + fileName); + contents = contents.toString(); + + optimize(path, fileName, contents); +} + +function optimize (path, fileName, contents) { + var fullPath = path + "/" + fileName; + var moduleName = fileName.split(".js").join(""); + var tree = esprima.parse(contents); + + // find moduleId from return statement on module level + var module = tree.body[0].expression.arguments[1].body.body; + var moduleId = findModuleId(module); + + if (!moduleId) { + info.push("could not find moduleId in: " + fullPath); + return; + } + + if (moduleId == "Parent") { + info.push("not optimizing empty module (returning Parent) in: " + fullPath); + return; + } + + // find constructor + var constructorPosition = findConstructorPosition(module, moduleId); + if (constructorPosition === false) { + info.push("could not find constructor in: " + fileName) + return; + } + var constructor = module[constructorPosition]; + transformProps(tree, moduleId, constructor, constructorPosition); + + console.log(escodegen.generate(tree, generatorOption)); +} + +function findModuleId (module) { + var moduleId = false; + for (var j = 0; j < module.length; j++) { + var expression = module[j]; + + //console.log(util.inspect(expression, { showHidden: true, depth: 4 })); + + if (expression.type == "ReturnStatement") { + + if(expression.argument.type == "Identifier") { + + // for return Module; + moduleId = expression.argument.name; + break; + + } else if (expression.argument.type == "NewExpression") { + + // for return new Module; + moduleId = expression.argument.callee.name; + break; + + } else { + info.push("Unexpected return type at module level. " + fullPath) + } + } + } + return moduleId; +}; + +function findConstructorPosition (module, moduleId) { + + for (var j = 0; j < module.length; j++) { + var expression = module[j]; + + if (expression.type == "FunctionDeclaration" && expression.id.name == moduleId) { + return j; + } + } + return false; +} + +function transformProps (tree, moduleId, constructor, constructorPosition) { + var props = []; + for (var k = constructor.body.body.length - 1; k >= 0; k--) { + var line = constructor.body.body[k]; + + if(line.expression + && line.expression.type == "AssignmentExpression" + && line.expression.operator == "=" + && line.expression.left.type == "MemberExpression" + && line.expression.left.object.type == "ThisExpression" + && line.expression.right.type == "Literal") { + + // remove "this" properties with with value type from constructor + constructor.body.body.splice(k, 1); + //console.log(util.inspect(line, { showHidden: true, depth: 4 })); + + props.push({ + name: line.expression.left.property.name, + value: line.expression.right.value, + raw: line.expression.right.raw + }); + } + }; + + // generate prototype properties + for (var l = 0; l < props.length; l++) { + var attributes = props[l]; + + var prop = { type: 'ExpressionStatement', + expression: + { type: 'AssignmentExpression', + operator: '=', + left: + { type: 'MemberExpression', + computed: false, + object: + { type: 'MemberExpression', + computed: false, + object: { type: 'Identifier', name: moduleId }, + property: { type: 'Identifier', name: 'prototype' } }, + property: { type: 'Identifier', name: attributes.name } }, + right: { type: 'Literal', value: attributes.value, raw: attributes.raw } } } + + // place property after constructor + tree.body[0].expression.arguments[1].body.body.splice(constructorPosition+1, 0, prop); + }; +} + +readdir("app"); +console.log(info) diff --git a/scripts/optimize_.js b/scripts/optimize_.js new file mode 100644 index 0000000..4e5883a --- /dev/null +++ b/scripts/optimize_.js @@ -0,0 +1,125 @@ +/* + * First version of the optimizer, relying completely on regular expressions + * + * it grabs the code as text and just executes the outermost function (module level) + * therefore it would not execute any real code, only setup the prototype functinos + * and the constructor + * + * since some modules use "return new Module();" to create a singleton, I added + * a mechanism to remove the "new" keyword and get the same kind of non-executing behaviour + * + * the trial failed because i could not replace the old constructor with the new one + * with simple replace algorithms because of whitespace differences + * + * Spits out some small statistics at the end, which is quite nice + */ + +var fs = require('fs'); + +var successful = []; +var canceled = []; +var noconstruct = []; +var repaired = []; + +function readdir (path) { + var filesNames = fs.readdirSync(path) + + for (var i = 0; i < filesNames.length; i++) { + + var fileName = filesNames[i]; + var newPath = path + "/" + fileName; + + if(path == "app/Lib/Vendor"){ + continue; + } + + if(fileName.indexOf(".js") == -1) { + var stats = fs.lstatSync(newPath); + if (stats.isDirectory()) { + readdir(newPath); + } + continue; + } + + var moduleName = fileName.split(".js").join(""); + + var contents = fs.readFileSync(newPath); + contents = contents.toString(); + + // remove define construct around module, just get the module function + contents = contents.replace(/define\(\[[^\]]*\],\s*([\s\S]*)\);/, '$1'); + + // remove new from last return / disable singletons (eg. return new NotificationCenter;) + var before = contents; + contents = contents.replace(/([\s\S]*)return new ([a-zA-Z0-9]*)\s*\(?.*\);/, '$1return $2;'); + if (contents != before){ + repaired.push(moduleName); + } + + + eval("var module = " + contents); + + try { + // test run, to get possible exceptions + var Parent = function(){}; + var constructor = module(Parent); + + if (typeof constructor == 'object') { + noconstruct.push(newPath); + continue; + } + + //var optimizedModule = optimize(moduleName, module); + //console.log(optimizedModule); + + successful.push(newPath); + + } catch (e) { + //console.log(e) // see whats making it cancel + canceled.push(newPath); + } + }; +} + +function optimize(moduleName, module) { +/* + var better = module.toString(); + + var Parent = function(){}; + var constructor = module(Parent).toString(); + + var regex = /^\s*this\.(.*) = (.*)\n?/mig; + var props = []; + + do { + match = regex.exec(constructor); + if(match) { + props.push(moduleName + ".prototype." + match[1] + " = " + match[2]); + } + + } while (match != null); + + // remove original this.prop + constructor = constructor.replace(regex, ''); + + // add prototype variables at bottom of constructor + if (props.length > 0) { + constructor = constructor + "\n\n" + props.join("\n"); + } + //constructor = constructor.replace("function " + moduleName + "(", "function " + moduleName + " ("); + */ + + return ""; +} + +readdir("app"); + + +console.log("- Successful:", successful.length) +console.log("- Canceled:", canceled.length) +console.log("- No Constructor:", noconstruct.length) +console.log("- Repaired Singletons:", repaired.length) + +console.log("canceled:", canceled) +console.log("no constructor:", noconstruct) +//console.log("successful:", successful) \ No newline at end of file diff --git a/server.js b/server.js index 9d829aa..bb9586f 100755 --- a/server.js +++ b/server.js @@ -1,12 +1,15 @@ -GLOBALS = { context: "Channel" }; +"use strict" + +var GLOBALS = { context: "Channel" }; var requirejs = require('requirejs'); +var fs = require('fs'); var inspector; requirejs.config({ nodeRequire: require, baseUrl: 'app', - deps: ['Lib/Utilities/Extensions'] + deps: ['Lib/Utilities/Channel/Extensions'] }); var port = process.argv[2] @@ -23,10 +26,17 @@ var options = { requirejs([ "Bootstrap/HttpServer", "Bootstrap/Socket", - "Server/Coordinator" + "Server/Coordinator", + "Game/Config/Settings" ], -function (HttpServer, Socket, Coordinator) { +function (HttpServer, Socket, Coordinator, Settings) { + + var records = fs.readdirSync(Settings.CHANNEL_RECORDING_PATH); + if (records.length > 200) { + console.warn('Too many recordings!'); + } + var coordinator = new Coordinator(); var httpServer = new HttpServer(options, coordinator); var socket = new Socket(httpServer.getServer(), options, coordinator); diff --git a/snippets/cheats/leftRightBot.js b/snippets/cheats/leftRightBot.js new file mode 100644 index 0000000..45e6b92 --- /dev/null +++ b/snippets/cheats/leftRightBot.js @@ -0,0 +1,31 @@ +var runFor = 5000; +var jumpEvery = 1300; + +var runBot = setInterval(function(){ + + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'stop'); + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'lookAt', {x:-0.5, y:0}); + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'moveLeft'); + + setTimeout(function(){ + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'stop'); + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'lookAt', {x:0.5, y:0}); + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'moveRight'); + }, runFor); + +}, runFor * 2); + +var jumpBot = setInterval(function(){ + + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'jump'); + + setTimeout(function(){ + Chuck.inspector.nc.trigger(Chuck.inspector.nc.ns.client.to.server.gameCommand.send, 'jumpStop'); + }, (jumpEvery - 100)); + +}, jumpEvery); + +function stop () { + clearInterval(runBot); + clearInterval(jumpBot); +} \ No newline at end of file diff --git a/snippets/sublime/js-define-child.sublime-snippet b/snippets/sublime/js-define-child.sublime-snippet index 9d74c6e..27857d4 100644 --- a/snippets/sublime/js-define-child.sublime-snippet +++ b/snippets/sublime/js-define-child.sublime-snippet @@ -6,6 +6,8 @@ define([ function (Parent) { + "use strict"; + return Parent; }); diff --git a/snippets/sublime/js-define-extend.sublime-snippet b/snippets/sublime/js-define-extend.sublime-snippet index 711e7ce..1e9447d 100755 --- a/snippets/sublime/js-define-extend.sublime-snippet +++ b/snippets/sublime/js-define-extend.sublime-snippet @@ -5,6 +5,8 @@ define([ ], function (Parent) { + + "use strict"; function ${1:Module}() { Parent.call(this); @@ -14,7 +16,7 @@ function (Parent) { ${1:Module}.prototype.${3:name} = function(${4:arguments}) { return null; - } + }; return ${1:Module}; diff --git a/snippets/sublime/js-define.sublime-snippet b/snippets/sublime/js-define.sublime-snippet index 390cb91..31eac40 100755 --- a/snippets/sublime/js-define.sublime-snippet +++ b/snippets/sublime/js-define.sublime-snippet @@ -4,13 +4,16 @@ define([ ], function () { + + "use strict"; function ${1:Module}() { + } ${1:Module}.prototype.${2:name} = function(${3:arguments}) { return null; - } + }; return ${1:Module}; diff --git a/static/css/screen.css b/static/css/screen.css new file mode 100644 index 0000000..6cc74a3 --- /dev/null +++ b/static/css/screen.css @@ -0,0 +1,211 @@ +@font-face { + font-family: 'Joystix'; + src: url('/static/fonts/2D6D36_0_0.eot'); + src: url('/static/fonts/2D6D36_0_0.eot?#iefix') format('embedded-opentype'), + url('/static/fonts/2D6D36_0_0.woff2') format('woff2'), + url('/static/fonts/2D6D36_0_0.woff') format('woff'), + url('/static/fonts/2D6D36_0_0.ttf') format('truetype'); +} + +h1, h2, h3, h4, th { + font-weight: normal; +} + +html, body { + width: 100%; + height: 100%; +} + +body { + background: #222; + color: #ccc; + font-family: 'Joystix', "Lucida Grande", sans-serif; + /*text-transform: uppercase;*/ + margin: 0; + padding: 0; + display: table; + -moz-osx-font-smoothing: grayscale; +} + +#menuBar { + + list-style-type: none; + background: rgba(0, 0, 0, 0.5); + position: absolute; + z-index: 1; + width: 100%; + margin: 0; + padding: 5px 0; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#menuBar button { + outline : 0; + -moz-outline : 0; +} + +#menuBar li { + float: left; + margin-left: 40px; +} + +#menuBar li label { + + padding: 5px 0; + display: block; +} + +#back-to-menu { + margin-left: 5px !important; +} + +#graph-fps { + background: rgba(0,0,0,0.3); + border: 1px solid rgba(0,0,0,0.5); + padding: 2px; + display: inline; +} + + +#fullscreen { + float: right !important; + margin-right: 5px; +} + +::selection { + background: #720000; /* Safari */ + } +::-moz-selection { + background: #720000; /* Firefox */ +} + +input, button { + background: black; + color: #ccc; + border: 0; + padding: 0.3em 0.6em 0.6em; + font-size: 1em; + font-family: inherit; + text-transform: inherit; +} + +#createform, #customjoinform, #game { + display: none; +} + +#createform #maps { + text-transform: capitalize; +} + +#primarycolor { + padding: 0.3em; + width: 1em; + background-color: #777; +} + +article#menu { + margin: 4em auto; + background: #1a1a1a; + padding: 2em; + max-width: 40em; +} + +table, th, td { + border: 1px solid #777; + border-collapse: collapse; +} + +th, td { + padding: 0.5em 1em; +} + +ul { + list-style-type: none; + padding-left: 0; +} + +#link { + width: 100%; +} + +.offline { + background: #ccc; +} + +tr:hover td { + background: #222; +} + +.full { + color: #777; +} + +.full a { + color: inherit; + cursor:not-allowed;; +} + +#players { + position: absolute; + + border: 1px solid #777; + padding: 20px; + margin-left: 35px; + margin-top: -10px; + background: rgba(20, 20, 20, 0.95); + box-shadow: 5px 5px 5px #000; + display: none; +} + +.playersCell:hover #players { + display: block; +} + +a { + color: #ccc; +} + + +#canvasContainer { + /* + text-align: center; + display: table-cell; + vertical-align: middle; + */ + height: 100%; +} + +#canvasContainer canvas { + position: absolute;/* + top: 50%; + left: 50%; + margin-top: -200px; + margin-left: -300px;*/ +} + + +:-webkit-full-screen { + top: 0; + left: 0; + margin: 0; + padding: 0; +} + +#devtools { + position: absolute; + right: 0; + top: 0; + padding: 10px; + background: #333; +} + +#devtools p { + padding: 5px 0 0 0; + margin: 0; +} diff --git a/static/fonts/2D6D36_0_0.eot b/static/fonts/2D6D36_0_0.eot new file mode 100644 index 0000000..6d3dfd6 Binary files /dev/null and b/static/fonts/2D6D36_0_0.eot differ diff --git a/static/fonts/2D6D36_0_0.ttf b/static/fonts/2D6D36_0_0.ttf new file mode 100644 index 0000000..f2f5c2a Binary files /dev/null and b/static/fonts/2D6D36_0_0.ttf differ diff --git a/static/fonts/2D6D36_0_0.woff b/static/fonts/2D6D36_0_0.woff new file mode 100644 index 0000000..dfd6506 Binary files /dev/null and b/static/fonts/2D6D36_0_0.woff differ diff --git a/static/fonts/2D6D36_0_0.woff2 b/static/fonts/2D6D36_0_0.woff2 new file mode 100644 index 0000000..5a3a3c7 Binary files /dev/null and b/static/fonts/2D6D36_0_0.woff2 differ diff --git a/static/html/game.html b/static/html/game.html deleted file mode 100755 index 29ac5eb..0000000 --- a/static/html/game.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - Chuck - - - -
    - -
    - - - diff --git a/static/html/index.html b/static/html/index.html index 6b7399a..24f0143 100644 --- a/static/html/index.html +++ b/static/html/index.html @@ -1,26 +1,19 @@ - Chuck Lobby - + + Chuck + + -
    + - +
    + +
    + +
    +
    + - \ No newline at end of file + diff --git a/static/html/not_available.html b/static/html/not_available.html new file mode 100644 index 0000000..77a5ff8 --- /dev/null +++ b/static/html/not_available.html @@ -0,0 +1,131 @@ + + + + Chuck + + + + + + + + +
    +

    + Hi There! +

    +

    + The game is currently not available. +

    + +

    We will announce the date of the next test session, once we finish the current alpha milestone.

    +

    Here you can see the current status:

    + + + + + + + + + + + + + + + + + + +
    Current milestone 
    Tasks still open 
    Tasks finished 
    Progress 
    + +

    + To get notified and be able to join the next test session, join the alpha team by requesting an invitation. +

    +

    + You can do that on our blog: + http://chuck-game.tumblr.com +

    + +

    + Thanks for testing & see you soon!
    + The developers +

    +
    + + diff --git a/static/img/10.gif b/static/img/10.gif deleted file mode 100755 index 99e5663..0000000 Binary files a/static/img/10.gif and /dev/null differ diff --git a/static/img/100.gif b/static/img/100.gif deleted file mode 100755 index c65bc2f..0000000 Binary files a/static/img/100.gif and /dev/null differ diff --git a/static/img/100.png b/static/img/100.png deleted file mode 100755 index d94254f..0000000 Binary files a/static/img/100.png and /dev/null differ diff --git a/static/img/Backgrounds/clouds.png b/static/img/Backgrounds/clouds.png new file mode 100644 index 0000000..2f4e128 Binary files /dev/null and b/static/img/Backgrounds/clouds.png differ diff --git a/static/img/Backgrounds/londonatnight-transparent.png b/static/img/Backgrounds/londonatnight-transparent.png new file mode 100644 index 0000000..14920ee Binary files /dev/null and b/static/img/Backgrounds/londonatnight-transparent.png differ diff --git a/static/img/Backgrounds/starnight-small.png b/static/img/Backgrounds/starnight-small.png new file mode 100644 index 0000000..4486e9e Binary files /dev/null and b/static/img/Backgrounds/starnight-small.png differ diff --git a/static/img/Backgrounds/starnight.png b/static/img/Backgrounds/starnight.png new file mode 100644 index 0000000..fb76caf Binary files /dev/null and b/static/img/Backgrounds/starnight.png differ diff --git a/static/img/Characters/Chuck/Animation/TexturePacker/chuck.tps b/static/img/Characters/Chuck/Animation/TexturePacker/chuck.tps new file mode 100644 index 0000000..0584d17 --- /dev/null +++ b/static/img/Characters/Chuck/Animation/TexturePacker/chuck.tps @@ -0,0 +1,204 @@ + + + + fileFormatVersion + 3 + texturePackerVersion + 3.6.0 + fileName + /Users/logsol/Work/projects/js/chuck.js/static/img/Characters/Chuck/Animation/tp/tp.tps + autoSDSettings + + + scale + 1 + extension + + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + allowRotation + + premultiplyAlpha + + shapeDebug + + dpi + 72 + dataFormat + json + textureFileName + wa.png + flipPVR + + pvrCompressionQuality + PVR_QUALITY_NORMAL + mipMapMinSize + 32768 + etc1CompressionQuality + ETC1_QUALITY_LOW_PERCEPTUAL + dxtCompressionMode + DXT_PERCEPTUAL + jxrColorFormat + JXR_YUV444 + jxrTrimFlexBits + 0 + jxrCompressionLevel + 0 + ditherType + NearestNeighbour + backgroundColor + 0 + libGdx + + filtering + + x + Linear + y + Linear + + + shapePadding + 2 + jpgQuality + 80 + pngOptimizationLevel + 1 + webpQualityLevel + 101 + textureSubPath + + textureFormat + png + borderPadding + 2 + maxTextureSize + + width + 2048 + height + 2048 + + fixedTextureSize + + width + -1 + height + -1 + + reduceBorderArtifacts + + algorithmSettings + + algorithm + MaxRects + freeSizeMode + Best + sizeConstraints + AnySize + forceSquared + + forceWordAligned + + maxRects + + heuristic + Best + + basic + + sortBy + Best + order + Ascending + + + andEngine + + minFilter + Linear + packageName + Texture + wrap + + s + Clamp + t + Clamp + + magFilter + MagLinear + + dataFileNames + + data + + name + witharms.json + + + multiPack + + forceIdenticalLayout + + outputFormat + RGBA8888 + contentProtection + + key + + + autoAliasEnabled + + trimSpriteNames + + prependSmartFolderName + + cleanTransparentPixels + + globalSpriteSettings + + scale + 1 + scaleMode + Smooth + innerPadding + 0 + extrude + 0 + trimThreshold + 1 + trimMode + Trim + heuristicMask + + pivotPoint + Center + + fileList + + ../WithArms + + ignoreFileList + + replaceList + + ignoredWarnings + + commonDivisorX + 1 + commonDivisorY + 1 + + diff --git a/static/img/Characters/Chuck/Animation/TexturePacker/chuck_sheet.json b/static/img/Characters/Chuck/Animation/TexturePacker/chuck_sheet.json new file mode 100644 index 0000000..fbf6e7d --- /dev/null +++ b/static/img/Characters/Chuck/Animation/TexturePacker/chuck_sheet.json @@ -0,0 +1,2280 @@ +{"frames": { + +"ChuckWithArms0001.png": +{ + "frame": {"x":411,"y":920,"w":31,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":51,"y":60,"w":31,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0002.png": +{ + "frame": {"x":116,"y":308,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0003.png": +{ + "frame": {"x":127,"y":206,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0004.png": +{ + "frame": {"x":164,"y":410,"w":46,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":46,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0005.png": +{ + "frame": {"x":214,"y":308,"w":45,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":45,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0006.png": +{ + "frame": {"x":489,"y":815,"w":41,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":41,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0007.png": +{ + "frame": {"x":257,"y":716,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0008.png": +{ + "frame": {"x":333,"y":818,"w":32,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":50,"y":60,"w":32,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0009.png": +{ + "frame": {"x":408,"y":512,"w":29,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":29,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0010.png": +{ + "frame": {"x":343,"y":920,"w":32,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":32,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0011.png": +{ + "frame": {"x":272,"y":206,"w":39,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":39,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0012.png": +{ + "frame": {"x":163,"y":614,"w":43,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":43,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0013.png": +{ + "frame": {"x":61,"y":614,"w":49,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":49,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0014.png": +{ + "frame": {"x":475,"y":206,"w":53,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":60,"w":53,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0015.png": +{ + "frame": {"x":2,"y":308,"w":57,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":57,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0016.png": +{ + "frame": {"x":59,"y":920,"w":54,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":54,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0017.png": +{ + "frame": {"x":2,"y":818,"w":55,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":55,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0018.png": +{ + "frame": {"x":464,"y":613,"w":50,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":60,"w":50,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0019.png": +{ + "frame": {"x":532,"y":103,"w":44,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":44,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0020.png": +{ + "frame": {"x":296,"y":614,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0021.png": +{ + "frame": {"x":308,"y":920,"w":33,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":60,"w":33,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0022.png": +{ + "frame": {"x":380,"y":2,"w":34,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":50,"y":60,"w":34,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0023.png": +{ + "frame": {"x":301,"y":512,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0024.png": +{ + "frame": {"x":172,"y":818,"w":42,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":42,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0025.png": +{ + "frame": {"x":182,"y":104,"w":45,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":45,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0026.png": +{ + "frame": {"x":166,"y":308,"w":46,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":46,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0027.png": +{ + "frame": {"x":527,"y":307,"w":48,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0028.png": +{ + "frame": {"x":132,"y":104,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0029.png": +{ + "frame": {"x":132,"y":104,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0030.png": +{ + "frame": {"x":530,"y":204,"w":48,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0031.png": +{ + "frame": {"x":212,"y":410,"w":46,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":46,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0032.png": +{ + "frame": {"x":225,"y":206,"w":45,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":45,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0033.png": +{ + "frame": {"x":213,"y":716,"w":42,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":42,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0034.png": +{ + "frame": {"x":257,"y":818,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0035.png": +{ + "frame": {"x":297,"y":716,"w":34,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":50,"y":60,"w":34,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0036.png": +{ + "frame": {"x":333,"y":716,"w":33,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":60,"w":33,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0037.png": +{ + "frame": {"x":268,"y":920,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0038.png": +{ + "frame": {"x":554,"y":2,"w":44,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":44,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0039.png": +{ + "frame": {"x":107,"y":2,"w":50,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":60,"w":50,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0040.png": +{ + "frame": {"x":2,"y":920,"w":55,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":55,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0041.png": +{ + "frame": {"x":115,"y":920,"w":54,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":54,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0042.png": +{ + "frame": {"x":2,"y":410,"w":57,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":57,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0043.png": +{ + "frame": {"x":61,"y":308,"w":53,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":60,"w":53,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0044.png": +{ + "frame": {"x":112,"y":614,"w":49,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":49,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0045.png": +{ + "frame": {"x":168,"y":716,"w":43,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":43,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0046.png": +{ + "frame": {"x":275,"y":104,"w":39,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":39,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0047.png": +{ + "frame": {"x":336,"y":614,"w":33,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":33,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0048.png": +{ + "frame": {"x":380,"y":308,"w":29,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":29,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0049.png": +{ + "frame": {"x":367,"y":818,"w":32,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":50,"y":60,"w":32,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0050.png": +{ + "frame": {"x":302,"y":2,"w":39,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":39,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0051.png": +{ + "frame": {"x":600,"y":2,"w":42,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":42,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0052.png": +{ + "frame": {"x":209,"y":2,"w":45,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":45,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0053.png": +{ + "frame": {"x":177,"y":206,"w":46,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":46,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0054.png": +{ + "frame": {"x":159,"y":2,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0055.png": +{ + "frame": {"x":116,"y":308,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0056.png": +{ + "frame": {"x":411,"y":920,"w":31,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":51,"y":60,"w":31,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0057.png": +{ + "frame": {"x":665,"y":687,"w":36,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":68,"w":36,"h":91}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0058.png": +{ + "frame": {"x":862,"y":841,"w":39,"h":84}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":76,"w":39,"h":84}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0059.png": +{ + "frame": {"x":903,"y":832,"w":41,"h":78}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":82,"w":41,"h":78}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0060.png": +{ + "frame": {"x":968,"y":749,"w":43,"h":74}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":86,"w":43,"h":74}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0061.png": +{ + "frame": {"x":1040,"y":560,"w":46,"h":70}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":90,"w":46,"h":70}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0062.png": +{ + "frame": {"x":991,"y":681,"w":44,"h":66}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":93,"w":44,"h":66}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0063.png": +{ + "frame": {"x":1040,"y":632,"w":45,"h":64}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":96,"w":45,"h":64}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0064.png": +{ + "frame": {"x":831,"y":957,"w":47,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":97,"w":47,"h":63}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0065.png": +{ + "frame": {"x":992,"y":616,"w":46,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":97,"w":46,"h":63}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0066.png": +{ + "frame": {"x":1049,"y":351,"w":48,"h":71}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":38,"y":89,"w":48,"h":71}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0067.png": +{ + "frame": {"x":1049,"y":270,"w":65,"h":79}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":31,"y":81,"w":65,"h":79}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0068.png": +{ + "frame": {"x":924,"y":2,"w":85,"h":89}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":24,"y":70,"w":85,"h":89}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0069.png": +{ + "frame": {"x":2,"y":2,"w":103,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":18,"y":60,"w":103,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0070.png": +{ + "frame": {"x":553,"y":612,"w":103,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":18,"y":60,"w":103,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0071.png": +{ + "frame": {"x":636,"y":403,"w":102,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":18,"y":60,"w":102,"h":94}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0072.png": +{ + "frame": {"x":637,"y":499,"w":100,"h":92}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":19,"y":60,"w":100,"h":92}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0073.png": +{ + "frame": {"x":770,"y":389,"w":96,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":22,"y":60,"w":96,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0074.png": +{ + "frame": {"x":899,"y":94,"w":91,"h":89}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":24,"y":60,"w":91,"h":89}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0075.png": +{ + "frame": {"x":1011,"y":2,"w":86,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":27,"y":60,"w":86,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0076.png": +{ + "frame": {"x":1011,"y":92,"w":78,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":32,"y":60,"w":78,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0077.png": +{ + "frame": {"x":992,"y":181,"w":72,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":72,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0078.png": +{ + "frame": {"x":804,"y":481,"w":70,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":70,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0079.png": +{ + "frame": {"x":921,"y":469,"w":69,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":69,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0080.png": +{ + "frame": {"x":882,"y":282,"w":67,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":67,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0081.png": +{ + "frame": {"x":909,"y":185,"w":67,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":67,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0082.png": +{ + "frame": {"x":772,"y":293,"w":65,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":65,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0083.png": +{ + "frame": {"x":795,"y":867,"w":65,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":38,"y":60,"w":65,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0084.png": +{ + "frame": {"x":833,"y":98,"w":64,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":60,"w":64,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0085.png": +{ + "frame": {"x":727,"y":870,"w":66,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":38,"y":60,"w":66,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0086.png": +{ + "frame": {"x":801,"y":195,"w":65,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":60,"w":65,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0087.png": +{ + "frame": {"x":777,"y":777,"w":66,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":60,"w":66,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0088.png": +{ + "frame": {"x":707,"y":779,"w":68,"h":89}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":68,"h":89}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0089.png": +{ + "frame": {"x":940,"y":372,"w":68,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":68,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0090.png": +{ + "frame": {"x":868,"y":379,"w":70,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":70,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0091.png": +{ + "frame": {"x":992,"y":181,"w":72,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":72,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0092.png": +{ + "frame": {"x":411,"y":920,"w":31,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":51,"y":60,"w":31,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0093.png": +{ + "frame": {"x":700,"y":593,"w":33,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":69,"w":33,"h":91}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0094.png": +{ + "frame": {"x":952,"y":558,"w":38,"h":82}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":78,"w":38,"h":82}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0095.png": +{ + "frame": {"x":1013,"y":766,"w":43,"h":74}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":86,"w":43,"h":74}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0096.png": +{ + "frame": {"x":1049,"y":424,"w":47,"h":66}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":94,"w":47,"h":66}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0097.png": +{ + "frame": {"x":727,"y":960,"w":51,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":43,"y":103,"w":51,"h":57}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0098.png": +{ + "frame": {"x":727,"y":960,"w":51,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":43,"y":103,"w":51,"h":57}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0099.png": +{ + "frame": {"x":1049,"y":492,"w":47,"h":66}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":94,"w":47,"h":66}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0100.png": +{ + "frame": {"x":1058,"y":771,"w":42,"h":74}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":86,"w":42,"h":74}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0101.png": +{ + "frame": {"x":1010,"y":359,"w":37,"h":82}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":78,"w":37,"h":82}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0102.png": +{ + "frame": {"x":703,"y":686,"w":32,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":69,"w":32,"h":91}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0103.png": +{ + "frame": {"x":411,"y":920,"w":31,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":51,"y":60,"w":31,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0104.png": +{ + "frame": {"x":651,"y":298,"w":80,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":32,"y":60,"w":80,"h":94}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0105.png": +{ + "frame": {"x":648,"y":201,"w":78,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":32,"y":60,"w":78,"h":95}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0106.png": +{ + "frame": {"x":577,"y":305,"w":72,"h":96}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":34,"y":60,"w":72,"h":96}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0107.png": +{ + "frame": {"x":439,"y":512,"w":61,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":60,"w":61,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0108.png": +{ + "frame": {"x":61,"y":410,"w":50,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":43,"y":60,"w":50,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0109.png": +{ + "frame": {"x":343,"y":2,"w":35,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":35,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0110.png": +{ + "frame": {"x":303,"y":410,"w":35,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":35,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0111.png": +{ + "frame": {"x":229,"y":104,"w":44,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":44,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0112.png": +{ + "frame": {"x":79,"y":104,"w":51,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":60,"w":51,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0113.png": +{ + "frame": {"x":532,"y":815,"w":59,"h":98}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":59,"h":98}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0114.png": +{ + "frame": {"x":593,"y":813,"w":62,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":34,"y":60,"w":62,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0115.png": +{ + "frame": {"x":728,"y":197,"w":71,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":31,"y":60,"w":71,"h":94}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0116.png": +{ + "frame": {"x":692,"y":100,"w":66,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":33,"y":60,"w":66,"h":95}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0117.png": +{ + "frame": {"x":580,"y":204,"w":66,"h":96}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":30,"y":60,"w":66,"h":96}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0118.png": +{ + "frame": {"x":465,"y":410,"w":58,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":35,"y":60,"w":58,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0119.png": +{ + "frame": {"x":113,"y":512,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0120.png": +{ + "frame": {"x":303,"y":308,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0121.png": +{ + "frame": {"x":2,"y":716,"w":56,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":43,"y":60,"w":56,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0122.png": +{ + "frame": {"x":2,"y":206,"w":68,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":38,"y":60,"w":68,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0123.png": +{ + "frame": {"x":2,"y":104,"w":75,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":35,"y":60,"w":75,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0124.png": +{ + "frame": {"x":474,"y":2,"w":78,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":33,"y":60,"w":78,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0125.png": +{ + "frame": {"x":554,"y":915,"w":80,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":32,"y":60,"w":80,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithArms0126.png": +{ + "frame": {"x":554,"y":510,"w":81,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":32,"y":60,"w":81,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0001.png": +{ + "frame": {"x":409,"y":410,"w":28,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":28,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0002.png": +{ + "frame": {"x":253,"y":614,"w":41,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":41,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0003.png": +{ + "frame": {"x":258,"y":512,"w":41,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":41,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0004.png": +{ + "frame": {"x":340,"y":410,"w":35,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":60,"w":35,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0005.png": +{ + "frame": {"x":371,"y":614,"w":31,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":31,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0006.png": +{ + "frame": {"x":498,"y":916,"w":26,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":26,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0007.png": +{ + "frame": {"x":393,"y":104,"w":27,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":27,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0008.png": +{ + "frame": {"x":420,"y":206,"w":27,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":27,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0009.png": +{ + "frame": {"x":390,"y":206,"w":28,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":28,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0010.png": +{ + "frame": {"x":377,"y":920,"w":32,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":32,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0011.png": +{ + "frame": {"x":216,"y":818,"w":39,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":39,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0012.png": +{ + "frame": {"x":208,"y":614,"w":43,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":43,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0013.png": +{ + "frame": {"x":117,"y":716,"w":49,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":49,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0014.png": +{ + "frame": {"x":477,"y":103,"w":53,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":60,"w":53,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0015.png": +{ + "frame": {"x":2,"y":512,"w":57,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":57,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0016.png": +{ + "frame": {"x":116,"y":818,"w":54,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":54,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0017.png": +{ + "frame": {"x":59,"y":818,"w":55,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":55,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0018.png": +{ + "frame": {"x":502,"y":511,"w":50,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":60,"w":50,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0019.png": +{ + "frame": {"x":578,"y":103,"w":44,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":44,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0020.png": +{ + "frame": {"x":313,"y":206,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0021.png": +{ + "frame": {"x":368,"y":716,"w":32,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":60,"w":32,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0022.png": +{ + "frame": {"x":416,"y":2,"w":27,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":27,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0023.png": +{ + "frame": {"x":439,"y":410,"w":24,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":24,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0024.png": +{ + "frame": {"x":441,"y":308,"w":24,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":24,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0025.png": +{ + "frame": {"x":435,"y":818,"w":25,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":25,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0026.png": +{ + "frame": {"x":376,"y":512,"w":30,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":30,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0027.png": +{ + "frame": {"x":508,"y":714,"w":35,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":50,"y":60,"w":35,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0028.png": +{ + "frame": {"x":227,"y":920,"w":39,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":39,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0029.png": +{ + "frame": {"x":227,"y":920,"w":39,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":39,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0030.png": +{ + "frame": {"x":516,"y":612,"w":35,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":50,"y":60,"w":35,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0031.png": +{ + "frame": {"x":404,"y":614,"w":30,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":30,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0032.png": +{ + "frame": {"x":444,"y":920,"w":25,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":25,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0033.png": +{ + "frame": {"x":449,"y":206,"w":24,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":24,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0034.png": +{ + "frame": {"x":451,"y":104,"w":24,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":24,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0035.png": +{ + "frame": {"x":422,"y":104,"w":27,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":27,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0036.png": +{ + "frame": {"x":401,"y":818,"w":32,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":60,"w":32,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0037.png": +{ + "frame": {"x":316,"y":104,"w":38,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":60,"w":38,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0038.png": +{ + "frame": {"x":578,"y":103,"w":44,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":44,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0039.png": +{ + "frame": {"x":61,"y":512,"w":50,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":41,"y":60,"w":50,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0040.png": +{ + "frame": {"x":60,"y":716,"w":55,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":37,"y":60,"w":55,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0041.png": +{ + "frame": {"x":171,"y":920,"w":54,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":54,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0042.png": +{ + "frame": {"x":2,"y":614,"w":57,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":57,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0043.png": +{ + "frame": {"x":72,"y":206,"w":53,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":60,"w":53,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0044.png": +{ + "frame": {"x":113,"y":410,"w":49,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":44,"y":60,"w":49,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0045.png": +{ + "frame": {"x":213,"y":512,"w":43,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":43,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0046.png": +{ + "frame": {"x":216,"y":818,"w":39,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":39,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0047.png": +{ + "frame": {"x":341,"y":512,"w":33,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":33,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0048.png": +{ + "frame": {"x":411,"y":308,"w":28,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":28,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0049.png": +{ + "frame": {"x":445,"y":2,"w":27,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":27,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0050.png": +{ + "frame": {"x":435,"y":716,"w":27,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":27,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0051.png": +{ + "frame": {"x":526,"y":916,"w":26,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":26,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0052.png": +{ + "frame": {"x":402,"y":716,"w":31,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":31,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0053.png": +{ + "frame": {"x":343,"y":308,"w":35,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":60,"w":35,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0054.png": +{ + "frame": {"x":260,"y":410,"w":41,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":41,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0055.png": +{ + "frame": {"x":253,"y":614,"w":41,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":41,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0056.png": +{ + "frame": {"x":409,"y":410,"w":28,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":28,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0057.png": +{ + "frame": {"x":740,"y":389,"w":28,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":68,"w":28,"h":91}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0058.png": +{ + "frame": {"x":880,"y":927,"w":29,"h":84}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":76,"w":29,"h":84}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0059.png": +{ + "frame": {"x":911,"y":912,"w":30,"h":78}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":82,"w":30,"h":78}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0060.png": +{ + "frame": {"x":981,"y":918,"w":30,"h":74}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":86,"w":30,"h":74}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0061.png": +{ + "frame": {"x":1013,"y":918,"w":31,"h":70}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":90,"w":31,"h":70}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0062.png": +{ + "frame": {"x":1032,"y":847,"w":31,"h":66}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":93,"w":31,"h":66}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0063.png": +{ + "frame": {"x":1065,"y":847,"w":32,"h":64}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":96,"w":32,"h":64}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0064.png": +{ + "frame": {"x":946,"y":901,"w":33,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":97,"w":33,"h":63}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0065.png": +{ + "frame": {"x":1065,"y":913,"w":32,"h":63}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":97,"w":32,"h":63}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0066.png": +{ + "frame": {"x":1083,"y":698,"w":31,"h":71}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":89,"w":31,"h":71}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0067.png": +{ + "frame": {"x":890,"y":751,"w":30,"h":79}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":81,"w":30,"h":79}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0068.png": +{ + "frame": {"x":773,"y":481,"w":29,"h":89}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":70,"w":29,"h":89}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0069.png": +{ + "frame": {"x":409,"y":410,"w":28,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":28,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0070.png": +{ + "frame": {"x":692,"y":909,"w":33,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":33,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0071.png": +{ + "frame": {"x":733,"y":293,"w":37,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":37,"h":94}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0072.png": +{ + "frame": {"x":658,"y":593,"w":40,"h":92}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":40,"h":92}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0073.png": +{ + "frame": {"x":836,"y":2,"w":43,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":43,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0074.png": +{ + "frame": {"x":737,"y":686,"w":44,"h":89}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":44,"h":89}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0075.png": +{ + "frame": {"x":783,"y":683,"w":46,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":46,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0076.png": +{ + "frame": {"x":1066,"y":181,"w":48,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":48,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0077.png": +{ + "frame": {"x":951,"y":275,"w":48,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":48,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0078.png": +{ + "frame": {"x":815,"y":571,"w":46,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":46,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0079.png": +{ + "frame": {"x":1001,"y":270,"w":46,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":46,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0080.png": +{ + "frame": {"x":863,"y":571,"w":44,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":44,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0081.png": +{ + "frame": {"x":876,"y":469,"w":43,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":43,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0082.png": +{ + "frame": {"x":881,"y":2,"w":41,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":41,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0083.png": +{ + "frame": {"x":879,"y":661,"w":41,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":41,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0084.png": +{ + "frame": {"x":868,"y":190,"w":39,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":39,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0085.png": +{ + "frame": {"x":909,"y":559,"w":41,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":41,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0086.png": +{ + "frame": {"x":839,"y":287,"w":41,"h":90}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":41,"h":90}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0087.png": +{ + "frame": {"x":845,"y":751,"w":43,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":43,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0088.png": +{ + "frame": {"x":769,"y":592,"w":44,"h":89}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":44,"h":89}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0089.png": +{ + "frame": {"x":992,"y":527,"w":46,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":46,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0090.png": +{ + "frame": {"x":831,"y":661,"w":46,"h":88}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":46,"h":88}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0091.png": +{ + "frame": {"x":951,"y":275,"w":48,"h":87}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":48,"h":87}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0092.png": +{ + "frame": {"x":409,"y":410,"w":28,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":28,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0093.png": +{ + "frame": {"x":735,"y":593,"w":32,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":69,"w":32,"h":91}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0094.png": +{ + "frame": {"x":1010,"y":443,"w":37,"h":82}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":78,"w":37,"h":82}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0095.png": +{ + "frame": {"x":946,"y":825,"w":41,"h":74}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":86,"w":41,"h":74}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0096.png": +{ + "frame": {"x":922,"y":726,"w":44,"h":66}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":94,"w":44,"h":66}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0097.png": +{ + "frame": {"x":780,"y":960,"w":49,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":43,"y":103,"w":49,"h":57}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0098.png": +{ + "frame": {"x":780,"y":960,"w":49,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":43,"y":103,"w":49,"h":57}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0099.png": +{ + "frame": {"x":1037,"y":698,"w":44,"h":66}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":94,"w":44,"h":66}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0100.png": +{ + "frame": {"x":989,"y":842,"w":41,"h":74}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":47,"y":86,"w":41,"h":74}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0101.png": +{ + "frame": {"x":952,"y":642,"w":37,"h":82}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":49,"y":78,"w":37,"h":82}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0102.png": +{ + "frame": {"x":739,"y":499,"w":32,"h":91}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":69,"w":32,"h":91}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0103.png": +{ + "frame": {"x":409,"y":410,"w":28,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":54,"y":60,"w":28,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0104.png": +{ + "frame": {"x":772,"y":2,"w":62,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":62,"h":94}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0105.png": +{ + "frame": {"x":577,"y":403,"w":57,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":38,"y":60,"w":57,"h":95}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0106.png": +{ + "frame": {"x":644,"y":2,"w":58,"h":96}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":34,"y":60,"w":58,"h":96}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0107.png": +{ + "frame": {"x":525,"y":409,"w":50,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":60,"w":50,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0108.png": +{ + "frame": {"x":261,"y":308,"w":40,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":43,"y":60,"w":40,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0109.png": +{ + "frame": {"x":377,"y":410,"w":30,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":30,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0110.png": +{ + "frame": {"x":462,"y":818,"w":25,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":25,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0111.png": +{ + "frame": {"x":353,"y":206,"w":35,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":48,"y":60,"w":35,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0112.png": +{ + "frame": {"x":256,"y":2,"w":44,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":60,"w":44,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0113.png": +{ + "frame": {"x":545,"y":713,"w":54,"h":98}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":36,"y":60,"w":54,"h":98}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0114.png": +{ + "frame": {"x":601,"y":711,"w":62,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":34,"y":60,"w":62,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0115.png": +{ + "frame": {"x":760,"y":99,"w":71,"h":94}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":31,"y":60,"w":71,"h":94}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0116.png": +{ + "frame": {"x":704,"y":2,"w":66,"h":95}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":33,"y":60,"w":66,"h":95}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0117.png": +{ + "frame": {"x":624,"y":103,"w":66,"h":96}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":30,"y":60,"w":66,"h":96}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0118.png": +{ + "frame": {"x":467,"y":308,"w":58,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":35,"y":60,"w":58,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0119.png": +{ + "frame": {"x":163,"y":512,"w":48,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":39,"y":60,"w":48,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0120.png": +{ + "frame": {"x":356,"y":104,"w":35,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":45,"y":60,"w":35,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0121.png": +{ + "frame": {"x":471,"y":920,"w":25,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":53,"y":60,"w":25,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0122.png": +{ + "frame": {"x":436,"y":614,"w":26,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":52,"y":60,"w":26,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0123.png": +{ + "frame": {"x":297,"y":818,"w":34,"h":100}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":46,"y":60,"w":34,"h":100}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0124.png": +{ + "frame": {"x":464,"y":714,"w":42,"h":99}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":42,"y":60,"w":42,"h":99}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0125.png": +{ + "frame": {"x":657,"y":810,"w":48,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":40,"y":60,"w":48,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}, +"ChuckWithoutArms0126.png": +{ + "frame": {"x":636,"y":912,"w":54,"h":97}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":38,"y":60,"w":54,"h":97}, + "sourceSize": {"w":140,"h":160}, + "pivot": {"x":0.5,"y":0.5} +}}, +"meta": { + "app": "http://www.codeandweb.com/texturepacker", + "version": "1.0", + "image": "chuck_sheet.png", + "format": "RGBA8888", + "size": {"w":1116,"h":1022}, + "scale": "1", + "smartupdate": "$TexturePacker:SmartUpdate:f601e9fc8b302ef469ac6bb647498e07:b5e01d57e2ce81c9b81519ee38e748b4:f0fdc5009020226362ff14efeb082c18$" +} +} diff --git a/static/img/Characters/Chuck/Animation/TexturePacker/chuck_sheet.png b/static/img/Characters/Chuck/Animation/TexturePacker/chuck_sheet.png new file mode 100644 index 0000000..d2a7b00 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/TexturePacker/chuck_sheet.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0001.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0001.png deleted file mode 100644 index 93e4272..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0001.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0002.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0002.png deleted file mode 100644 index 57e8b36..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0002.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0003.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0003.png deleted file mode 100644 index b16776a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0003.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0004.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0004.png deleted file mode 100644 index a01494f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0004.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0005.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0005.png deleted file mode 100644 index 2ec2eae..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0005.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0006.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0006.png deleted file mode 100644 index c026496..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0006.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0007.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0007.png deleted file mode 100644 index ae1e2cd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0007.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0008.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0008.png deleted file mode 100644 index 976e931..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0008.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0009.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0009.png deleted file mode 100644 index a8d1aa9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0009.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0010.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0010.png deleted file mode 100644 index 47bf811..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0010.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0011.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0011.png deleted file mode 100644 index 4f5cf0b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0011.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0012.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0012.png deleted file mode 100644 index 589788d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0012.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0013.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0013.png deleted file mode 100644 index e9ccc94..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0013.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0014.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0014.png deleted file mode 100644 index 2bcc099..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0014.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0015.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0015.png deleted file mode 100644 index 772cd97..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0015.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0016.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0016.png deleted file mode 100644 index 62b1039..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0016.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0017.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0017.png deleted file mode 100644 index ce556cc..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0017.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0018.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0018.png deleted file mode 100644 index da7568d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0018.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0019.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0019.png deleted file mode 100644 index cd2cb2b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0019.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0020.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0020.png deleted file mode 100644 index 43e7825..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0020.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0021.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0021.png deleted file mode 100644 index 05295db..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0021.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0022.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0022.png deleted file mode 100644 index 635b4cb..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0022.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0023.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0023.png deleted file mode 100644 index 4a10257..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0023.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0024.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0024.png deleted file mode 100644 index 33da2c9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0024.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0025.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0025.png deleted file mode 100644 index 9ea2265..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0025.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0026.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0026.png deleted file mode 100644 index b4e4c08..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0026.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0027.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0027.png deleted file mode 100644 index 14f683d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0027.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0028.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0028.png deleted file mode 100644 index 54c81de..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0028.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0029.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0029.png deleted file mode 100644 index 54c81de..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0029.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0030.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0030.png deleted file mode 100644 index aaeaa99..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0030.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0031.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0031.png deleted file mode 100644 index a87ad62..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0031.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0032.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0032.png deleted file mode 100644 index b3bf860..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0032.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0033.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0033.png deleted file mode 100644 index 352c6f4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0033.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0034.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0034.png deleted file mode 100644 index cbd0051..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0034.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0035.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0035.png deleted file mode 100644 index b506d15..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0035.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0036.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0036.png deleted file mode 100644 index 0615a12..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0036.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0037.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0037.png deleted file mode 100644 index 7ca4b61..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0037.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0038.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0038.png deleted file mode 100644 index f11e9ee..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0038.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0039.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0039.png deleted file mode 100644 index 1993253..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0039.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0040.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0040.png deleted file mode 100644 index 4777b4e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0040.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0041.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0041.png deleted file mode 100644 index 95aabad..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0041.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0042.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0042.png deleted file mode 100644 index 90373b1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0042.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0043.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0043.png deleted file mode 100644 index 7558783..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0043.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0044.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0044.png deleted file mode 100644 index e7a6e38..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0044.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0045.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0045.png deleted file mode 100644 index 41cb53b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0045.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0046.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0046.png deleted file mode 100644 index bd43160..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0046.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0047.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0047.png deleted file mode 100644 index ac31546..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0047.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0048.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0048.png deleted file mode 100644 index 06f788f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0048.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0049.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0049.png deleted file mode 100644 index 1eb0205..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0049.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0050.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0050.png deleted file mode 100644 index 5472367..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0050.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0051.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0051.png deleted file mode 100644 index 9d787c2..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0051.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0052.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0052.png deleted file mode 100644 index 45860b8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0052.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0053.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0053.png deleted file mode 100644 index e4c434b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0053.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0054.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0054.png deleted file mode 100644 index 534c03c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0054.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0055.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0055.png deleted file mode 100644 index 57e8b36..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0055.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0056.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0056.png deleted file mode 100644 index 93e4272..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0056.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0057.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0057.png deleted file mode 100644 index 3881500..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0057.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0058.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0058.png deleted file mode 100644 index 9dcc498..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0058.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0059.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0059.png deleted file mode 100644 index 387b532..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0059.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0060.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0060.png deleted file mode 100644 index f06cb38..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0060.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0061.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0061.png deleted file mode 100644 index 6914a7e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0061.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0062.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0062.png deleted file mode 100644 index b922243..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0062.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0063.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0063.png deleted file mode 100644 index 621230e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0063.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0064.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0064.png deleted file mode 100644 index 269c084..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0064.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0065.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0065.png deleted file mode 100644 index ed93ebf..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0065.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0066.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0066.png deleted file mode 100644 index 6cd8f57..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0066.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0067.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0067.png deleted file mode 100644 index 41ed3e6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0067.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0068.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0068.png deleted file mode 100644 index cfe6174..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0068.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0069.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0069.png deleted file mode 100644 index 60be902..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0069.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0070.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0070.png deleted file mode 100644 index 48036fd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0070.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0071.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0071.png deleted file mode 100644 index 6fdd3fa..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0071.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0072.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0072.png deleted file mode 100644 index e28bc83..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0072.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0073.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0073.png deleted file mode 100644 index 4997891..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0073.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0074.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0074.png deleted file mode 100644 index 8367605..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0074.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0075.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0075.png deleted file mode 100644 index c5445a6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0075.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0076.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0076.png deleted file mode 100644 index 15c2b84..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0076.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0077.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0077.png deleted file mode 100644 index 55c1d59..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0077.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0078.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0078.png deleted file mode 100644 index 4a6743b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0078.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0079.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0079.png deleted file mode 100644 index 14d8bd5..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0079.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0080.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0080.png deleted file mode 100644 index 08575f7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0080.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0081.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0081.png deleted file mode 100644 index 7cd811b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0081.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0082.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0082.png deleted file mode 100644 index f677be6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0082.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0083.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0083.png deleted file mode 100644 index 9920857..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0083.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0084.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0084.png deleted file mode 100644 index 3b77c99..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0084.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0085.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0085.png deleted file mode 100644 index 70fd18f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0085.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0086.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0086.png deleted file mode 100644 index b813c36..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0086.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0087.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0087.png deleted file mode 100644 index 8aeb840..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0087.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0088.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0088.png deleted file mode 100644 index d937a0a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0088.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0089.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0089.png deleted file mode 100644 index c8398b7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0089.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0090.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0090.png deleted file mode 100644 index 288efac..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0090.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0091.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0091.png deleted file mode 100644 index 55c1d59..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0091.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0092.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0092.png deleted file mode 100644 index 93e4272..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0092.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0093.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0093.png deleted file mode 100644 index ef3458b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0093.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0094.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0094.png deleted file mode 100644 index db772be..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0094.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0095.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0095.png deleted file mode 100644 index c6d817f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0095.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0096.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0096.png deleted file mode 100644 index b43dde3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0096.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0097.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0097.png deleted file mode 100644 index 9e91811..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0097.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0098.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0098.png deleted file mode 100644 index 9e91811..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0098.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0099.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0099.png deleted file mode 100644 index 1797b33..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0099.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0100.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0100.png deleted file mode 100644 index c678a40..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0100.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0101.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0101.png deleted file mode 100644 index c679b55..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0101.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0102.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0102.png deleted file mode 100644 index 8927026..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0102.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0103.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0103.png deleted file mode 100644 index 93e4272..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0103.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0104.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0104.png deleted file mode 100644 index 7f2b733..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0104.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0105.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0105.png deleted file mode 100644 index 2e6f683..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0105.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0106.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0106.png deleted file mode 100644 index 68808ba..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0106.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0107.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0107.png deleted file mode 100644 index 686176c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0107.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0108.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0108.png deleted file mode 100644 index 510007f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0108.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0109.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0109.png deleted file mode 100644 index d56abda..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0109.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0110.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0110.png deleted file mode 100644 index 6b3ab53..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0110.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0111.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0111.png deleted file mode 100644 index bc80b13..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0111.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0112.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0112.png deleted file mode 100644 index 8d41183..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0112.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0113.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0113.png deleted file mode 100644 index d624129..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0113.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0114.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0114.png deleted file mode 100644 index f4a5356..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0114.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0115.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0115.png deleted file mode 100644 index c6c431a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0115.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0116.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0116.png deleted file mode 100644 index d9508c6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0116.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0117.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0117.png deleted file mode 100644 index 88d672a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0117.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0118.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0118.png deleted file mode 100644 index 389fc76..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0118.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0119.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0119.png deleted file mode 100644 index 39d4232..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0119.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0120.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0120.png deleted file mode 100644 index e828058..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0120.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0121.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0121.png deleted file mode 100644 index 11174fa..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0121.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0122.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0122.png deleted file mode 100644 index 5f827fe..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0122.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0123.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0123.png deleted file mode 100644 index 5d0297f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0123.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0124.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0124.png deleted file mode 100644 index f5a4090..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0124.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0125.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0125.png deleted file mode 100644 index f78da15..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0125.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0126.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0126.png deleted file mode 100644 index e20ff86..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArms/ChuckAnimations0126.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0001.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0001.png new file mode 100644 index 0000000..03341d7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0001.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0002.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0002.png new file mode 100644 index 0000000..8ee7fac Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0002.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0003.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0003.png new file mode 100644 index 0000000..232765c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0003.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0004.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0004.png new file mode 100644 index 0000000..34dc861 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0004.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0005.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0005.png new file mode 100644 index 0000000..6b3aa85 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0005.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0006.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0006.png new file mode 100644 index 0000000..5110a52 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0006.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0007.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0007.png new file mode 100644 index 0000000..1e2d1fe Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0007.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0008.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0008.png new file mode 100644 index 0000000..e624a32 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0008.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0009.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0009.png new file mode 100644 index 0000000..9ae9c27 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0009.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0010.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0010.png new file mode 100644 index 0000000..63639b7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0010.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0011.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0011.png new file mode 100644 index 0000000..7b17053 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0011.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0012.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0012.png new file mode 100644 index 0000000..d5dd27b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0012.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0013.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0013.png new file mode 100644 index 0000000..a1bbf88 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0013.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0014.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0014.png new file mode 100644 index 0000000..3d1da89 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0014.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0015.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0015.png new file mode 100644 index 0000000..ff8aa03 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0015.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0016.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0016.png new file mode 100644 index 0000000..405cbe2 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0016.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0017.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0017.png new file mode 100644 index 0000000..8611359 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0017.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0018.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0018.png new file mode 100644 index 0000000..e5cd2d7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0018.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0019.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0019.png new file mode 100644 index 0000000..4c2beb4 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0019.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0020.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0020.png new file mode 100644 index 0000000..58b52f0 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0020.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0021.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0021.png new file mode 100644 index 0000000..6e35b1e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0021.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0022.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0022.png new file mode 100644 index 0000000..46ce40a Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0022.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0023.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0023.png new file mode 100644 index 0000000..8910e52 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0023.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0024.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0024.png new file mode 100644 index 0000000..21610fe Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0024.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0025.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0025.png new file mode 100644 index 0000000..e9ce6e1 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0025.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0026.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0026.png new file mode 100644 index 0000000..bf853c7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0026.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0027.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0027.png new file mode 100644 index 0000000..819f288 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0027.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0028.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0028.png new file mode 100644 index 0000000..0ac1954 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0028.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0029.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0029.png new file mode 100644 index 0000000..0ac1954 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0029.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0030.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0030.png new file mode 100644 index 0000000..73c9316 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0030.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0031.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0031.png new file mode 100644 index 0000000..7891332 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0031.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0032.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0032.png new file mode 100644 index 0000000..caac194 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0032.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0033.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0033.png new file mode 100644 index 0000000..cd4f1b5 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0033.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0034.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0034.png new file mode 100644 index 0000000..16f4087 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0034.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0035.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0035.png new file mode 100644 index 0000000..8bb928b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0035.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0036.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0036.png new file mode 100644 index 0000000..fb6c137 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0036.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0037.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0037.png new file mode 100644 index 0000000..20a32d0 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0037.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0038.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0038.png new file mode 100644 index 0000000..e804b5c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0038.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0039.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0039.png new file mode 100644 index 0000000..87fcde3 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0039.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0040.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0040.png new file mode 100644 index 0000000..556d8bf Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0040.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0041.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0041.png new file mode 100644 index 0000000..639bb22 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0041.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0042.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0042.png new file mode 100644 index 0000000..6e3faff Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0042.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0043.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0043.png new file mode 100644 index 0000000..221fa82 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0043.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0044.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0044.png new file mode 100644 index 0000000..42f894f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0044.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0045.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0045.png new file mode 100644 index 0000000..ab7ef00 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0045.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0046.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0046.png new file mode 100644 index 0000000..93f29d3 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0046.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0047.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0047.png new file mode 100644 index 0000000..58b8597 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0047.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0048.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0048.png new file mode 100644 index 0000000..5aae908 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0048.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0049.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0049.png new file mode 100644 index 0000000..16b3896 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0049.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0050.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0050.png new file mode 100644 index 0000000..6eac307 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0050.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0051.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0051.png new file mode 100644 index 0000000..1773ed8 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0051.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0052.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0052.png new file mode 100644 index 0000000..def8206 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0052.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0053.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0053.png new file mode 100644 index 0000000..2f906c7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0053.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0054.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0054.png new file mode 100644 index 0000000..8eb39d8 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0054.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0055.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0055.png new file mode 100644 index 0000000..8ee7fac Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0055.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0056.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0056.png new file mode 100644 index 0000000..03341d7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0056.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0057.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0057.png new file mode 100644 index 0000000..886bd9a Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0057.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0058.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0058.png new file mode 100644 index 0000000..4f2c38c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0058.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0059.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0059.png new file mode 100644 index 0000000..8a28299 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0059.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0060.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0060.png new file mode 100644 index 0000000..5c5a629 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0060.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0061.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0061.png new file mode 100644 index 0000000..4d6ba77 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0061.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0062.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0062.png new file mode 100644 index 0000000..89d3f4e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0062.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0063.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0063.png new file mode 100644 index 0000000..dc0cbca Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0063.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0064.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0064.png new file mode 100644 index 0000000..a30694c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0064.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0065.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0065.png new file mode 100644 index 0000000..334bec3 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0065.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0066.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0066.png new file mode 100644 index 0000000..fb36de9 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0066.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0067.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0067.png new file mode 100644 index 0000000..83d81cd Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0067.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0068.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0068.png new file mode 100644 index 0000000..d601f03 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0068.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0069.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0069.png new file mode 100644 index 0000000..3461739 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0069.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0070.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0070.png new file mode 100644 index 0000000..9dfec60 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0070.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0071.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0071.png new file mode 100644 index 0000000..adcc466 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0071.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0072.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0072.png new file mode 100644 index 0000000..7e0357c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0072.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0073.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0073.png new file mode 100644 index 0000000..d806ed7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0073.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0074.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0074.png new file mode 100644 index 0000000..b31e2d0 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0074.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0075.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0075.png new file mode 100644 index 0000000..77348dc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0075.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0076.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0076.png new file mode 100644 index 0000000..c036471 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0076.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0077.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0077.png new file mode 100644 index 0000000..4206c00 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0077.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0078.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0078.png new file mode 100644 index 0000000..7f24a17 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0078.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0079.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0079.png new file mode 100644 index 0000000..3119c41 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0079.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0080.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0080.png new file mode 100644 index 0000000..b784ceb Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0080.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0081.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0081.png new file mode 100644 index 0000000..9be3842 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0081.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0082.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0082.png new file mode 100644 index 0000000..43c410a Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0082.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0083.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0083.png new file mode 100644 index 0000000..34da3bc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0083.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0084.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0084.png new file mode 100644 index 0000000..08ae152 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0084.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0085.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0085.png new file mode 100644 index 0000000..23d40c1 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0085.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0086.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0086.png new file mode 100644 index 0000000..5e4b92b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0086.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0087.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0087.png new file mode 100644 index 0000000..9fc6d78 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0087.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0088.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0088.png new file mode 100644 index 0000000..df3eaaf Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0088.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0089.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0089.png new file mode 100644 index 0000000..c2d4268 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0089.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0090.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0090.png new file mode 100644 index 0000000..6e8323d Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0090.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0091.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0091.png new file mode 100644 index 0000000..4206c00 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0091.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0092.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0092.png new file mode 100644 index 0000000..03341d7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0092.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0093.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0093.png new file mode 100644 index 0000000..4840546 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0093.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0094.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0094.png new file mode 100644 index 0000000..bb5d4db Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0094.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0095.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0095.png new file mode 100644 index 0000000..6d039db Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0095.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0096.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0096.png new file mode 100644 index 0000000..b93b1d9 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0096.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0097.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0097.png new file mode 100644 index 0000000..ce1accc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0097.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0098.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0098.png new file mode 100644 index 0000000..ce1accc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0098.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0099.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0099.png new file mode 100644 index 0000000..5ff2400 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0099.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0100.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0100.png new file mode 100644 index 0000000..f2e2e9e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0100.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0101.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0101.png new file mode 100644 index 0000000..b35c0c1 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0101.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0102.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0102.png new file mode 100644 index 0000000..26a3229 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0102.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0103.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0103.png new file mode 100644 index 0000000..03341d7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0103.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0104.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0104.png new file mode 100644 index 0000000..914bc0f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0104.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0105.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0105.png new file mode 100644 index 0000000..eeb744e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0105.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0106.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0106.png new file mode 100644 index 0000000..6e11adc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0106.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0107.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0107.png new file mode 100644 index 0000000..e96cc43 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0107.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0108.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0108.png new file mode 100644 index 0000000..9435ed4 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0108.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0109.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0109.png new file mode 100644 index 0000000..c534432 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0109.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0110.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0110.png new file mode 100644 index 0000000..5b9871f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0110.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0111.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0111.png new file mode 100644 index 0000000..076531e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0111.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0112.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0112.png new file mode 100644 index 0000000..68b5117 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0112.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0113.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0113.png new file mode 100644 index 0000000..00bcd87 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0113.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0114.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0114.png new file mode 100644 index 0000000..d0c5d97 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0114.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0115.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0115.png new file mode 100644 index 0000000..6634e54 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0115.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0116.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0116.png new file mode 100644 index 0000000..d62b7c6 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0116.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0117.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0117.png new file mode 100644 index 0000000..4bb3448 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0117.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0118.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0118.png new file mode 100644 index 0000000..30c7542 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0118.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0119.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0119.png new file mode 100644 index 0000000..48ba953 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0119.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0120.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0120.png new file mode 100644 index 0000000..bed1544 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0120.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0121.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0121.png new file mode 100644 index 0000000..1dde7cb Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0121.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0122.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0122.png new file mode 100644 index 0000000..17153a4 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0122.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0123.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0123.png new file mode 100644 index 0000000..c7d003b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0123.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0124.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0124.png new file mode 100644 index 0000000..9efa415 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0124.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0125.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0125.png new file mode 100644 index 0000000..0502aa5 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0125.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0126.png b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0126.png new file mode 100644 index 0000000..cc6d01d Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithArms/ChuckWithArms0126.png differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0001.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0001.png deleted file mode 100644 index 0aeceaf..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0001.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0002.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0002.png deleted file mode 100644 index 49b09d7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0002.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0003.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0003.png deleted file mode 100644 index 3a91772..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0003.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0004.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0004.png deleted file mode 100644 index d34f542..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0004.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0005.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0005.png deleted file mode 100644 index 81fc9d4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0005.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0006.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0006.png deleted file mode 100644 index 2e49559..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0006.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0007.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0007.png deleted file mode 100644 index a4c016d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0007.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0008.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0008.png deleted file mode 100644 index fb42595..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0008.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0009.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0009.png deleted file mode 100644 index 6fba3ed..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0009.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0010.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0010.png deleted file mode 100644 index 7bd153c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0010.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0011.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0011.png deleted file mode 100644 index d3dac01..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0011.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0012.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0012.png deleted file mode 100644 index 7564241..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0012.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0013.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0013.png deleted file mode 100644 index 2b4e18f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0013.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0014.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0014.png deleted file mode 100644 index 2e444c3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0014.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0015.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0015.png deleted file mode 100644 index 7018376..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0015.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0016.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0016.png deleted file mode 100644 index dc6c1d1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0016.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0017.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0017.png deleted file mode 100644 index 8d9dbb3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0017.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0018.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0018.png deleted file mode 100644 index d53dc71..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0018.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0019.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0019.png deleted file mode 100644 index f72b3af..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0019.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0020.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0020.png deleted file mode 100644 index f9d8cf8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0020.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0021.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0021.png deleted file mode 100644 index 77401be..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0021.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0022.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0022.png deleted file mode 100644 index 9c92bd9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0022.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0023.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0023.png deleted file mode 100644 index c3d4d6c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0023.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0024.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0024.png deleted file mode 100644 index f83507e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0024.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0025.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0025.png deleted file mode 100644 index e1f2010..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0025.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0026.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0026.png deleted file mode 100644 index 2a7139f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0026.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0027.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0027.png deleted file mode 100644 index c855c47..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0027.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0028.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0028.png deleted file mode 100644 index 5439f07..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0028.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0029.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0029.png deleted file mode 100644 index 5439f07..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0029.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0030.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0030.png deleted file mode 100644 index c855c47..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0030.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0031.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0031.png deleted file mode 100644 index bba8b89..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0031.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0032.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0032.png deleted file mode 100644 index e1f2010..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0032.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0033.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0033.png deleted file mode 100644 index 1417c12..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0033.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0034.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0034.png deleted file mode 100644 index 5a71e6c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0034.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0035.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0035.png deleted file mode 100644 index 9c92bd9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0035.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0036.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0036.png deleted file mode 100644 index d1bd3bb..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0036.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0037.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0037.png deleted file mode 100644 index 7afa488..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0037.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0038.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0038.png deleted file mode 100644 index f72b3af..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0038.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0039.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0039.png deleted file mode 100644 index aac15df..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0039.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0040.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0040.png deleted file mode 100644 index 8d9dbb3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0040.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0041.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0041.png deleted file mode 100644 index 5df46ac..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0041.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0042.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0042.png deleted file mode 100644 index 7018376..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0042.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0043.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0043.png deleted file mode 100644 index 2e444c3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0043.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0044.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0044.png deleted file mode 100644 index 2b4e18f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0044.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0045.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0045.png deleted file mode 100644 index 4d9a185..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0045.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0046.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0046.png deleted file mode 100644 index 10cd24e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0046.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0047.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0047.png deleted file mode 100644 index 7bd153c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0047.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0048.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0048.png deleted file mode 100644 index 02e75fe..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0048.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0049.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0049.png deleted file mode 100644 index fb42595..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0049.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0050.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0050.png deleted file mode 100644 index c2e9c51..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0050.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0051.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0051.png deleted file mode 100644 index 2e49559..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0051.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0052.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0052.png deleted file mode 100644 index 23ef8ef..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0052.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0053.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0053.png deleted file mode 100644 index 175a4eb..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0053.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0054.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0054.png deleted file mode 100644 index 3a91772..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0054.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0055.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0055.png deleted file mode 100644 index 49b09d7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0055.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0056.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0056.png deleted file mode 100644 index 0aeceaf..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0056.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0057.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0057.png deleted file mode 100644 index 8fbc78b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0057.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0058.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0058.png deleted file mode 100644 index 974d75e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0058.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0059.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0059.png deleted file mode 100644 index b653839..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0059.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0060.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0060.png deleted file mode 100644 index fa49974..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0060.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0061.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0061.png deleted file mode 100644 index 0a9e2d2..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0061.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0062.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0062.png deleted file mode 100644 index 1dab56f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0062.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0063.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0063.png deleted file mode 100644 index 3209c8e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0063.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0064.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0064.png deleted file mode 100644 index c1bcf44..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0064.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0065.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0065.png deleted file mode 100644 index f093092..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0065.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0066.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0066.png deleted file mode 100644 index 4155763..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0066.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0067.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0067.png deleted file mode 100644 index c78f3c0..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0067.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0068.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0068.png deleted file mode 100644 index abb8185..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0068.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0069.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0069.png deleted file mode 100644 index fccd7e9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0069.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0070.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0070.png deleted file mode 100644 index 1f39f78..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0070.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0071.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0071.png deleted file mode 100644 index 9f1e963..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0071.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0072.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0072.png deleted file mode 100644 index 3ae22b8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0072.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0073.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0073.png deleted file mode 100644 index 9427613..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0073.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0074.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0074.png deleted file mode 100644 index 6d8d0c0..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0074.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0075.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0075.png deleted file mode 100644 index 117404c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0075.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0076.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0076.png deleted file mode 100644 index 1f1d591..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0076.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0077.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0077.png deleted file mode 100644 index bc03465..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0077.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0078.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0078.png deleted file mode 100644 index 774e39d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0078.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0079.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0079.png deleted file mode 100644 index 9f9aa8b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0079.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0080.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0080.png deleted file mode 100644 index 67744d5..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0080.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0081.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0081.png deleted file mode 100644 index 890b9eb..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0081.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0082.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0082.png deleted file mode 100644 index 49d1d8f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0082.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0083.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0083.png deleted file mode 100644 index 6b89754..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0083.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0084.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0084.png deleted file mode 100644 index d572f63..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0084.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0085.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0085.png deleted file mode 100644 index 109bf21..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0085.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0086.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0086.png deleted file mode 100644 index 70f24cd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0086.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0087.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0087.png deleted file mode 100644 index 981160f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0087.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0088.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0088.png deleted file mode 100644 index 489614d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0088.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0089.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0089.png deleted file mode 100644 index 2ef5cd4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0089.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0090.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0090.png deleted file mode 100644 index bc134bf..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0090.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0091.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0091.png deleted file mode 100644 index bc03465..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0091.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0092.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0092.png deleted file mode 100644 index 0aeceaf..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0092.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0093.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0093.png deleted file mode 100644 index 402b2e3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0093.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0094.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0094.png deleted file mode 100644 index cef6ef9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0094.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0095.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0095.png deleted file mode 100644 index 05e2cfd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0095.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0096.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0096.png deleted file mode 100644 index 5de0393..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0096.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0097.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0097.png deleted file mode 100644 index 667df10..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0097.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0098.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0098.png deleted file mode 100644 index 667df10..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0098.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0099.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0099.png deleted file mode 100644 index 5de0393..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0099.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0100.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0100.png deleted file mode 100644 index 2b35552..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0100.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0101.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0101.png deleted file mode 100644 index 17e120b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0101.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0102.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0102.png deleted file mode 100644 index 406c971..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0102.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0103.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0103.png deleted file mode 100644 index 0aeceaf..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0103.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0104.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0104.png deleted file mode 100644 index c49ad55..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0104.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0105.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0105.png deleted file mode 100644 index b96ce95..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0105.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0106.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0106.png deleted file mode 100644 index 9782fba..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0106.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0107.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0107.png deleted file mode 100644 index 9212e91..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0107.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0108.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0108.png deleted file mode 100644 index b0d1036..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0108.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0109.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0109.png deleted file mode 100644 index 0941c51..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0109.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0110.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0110.png deleted file mode 100644 index 695a7cd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0110.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0111.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0111.png deleted file mode 100644 index 748b92f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0111.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0112.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0112.png deleted file mode 100644 index cccacb4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0112.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0113.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0113.png deleted file mode 100644 index 7aa03d6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0113.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0114.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0114.png deleted file mode 100644 index a03b3e3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0114.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0115.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0115.png deleted file mode 100644 index 4e4d6f4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0115.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0116.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0116.png deleted file mode 100644 index db45728..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0116.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0117.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0117.png deleted file mode 100644 index e9c2c5c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0117.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0118.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0118.png deleted file mode 100644 index c1a1490..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0118.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0119.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0119.png deleted file mode 100644 index 47cd98e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0119.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0120.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0120.png deleted file mode 100644 index b710652..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0120.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0121.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0121.png deleted file mode 100644 index 5a9a258..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0121.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0122.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0122.png deleted file mode 100644 index 4daadc7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0122.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0123.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0123.png deleted file mode 100644 index 0e69218..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0123.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0124.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0124.png deleted file mode 100644 index 0b33f2a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0124.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0125.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0125.png deleted file mode 100644 index fddd4f6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0125.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0126.png b/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0126.png deleted file mode 100644 index 83dfec8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsPixel/ChuckAnimations0126.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0001.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0001.png deleted file mode 100644 index 6229837..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0001.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0002.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0002.png deleted file mode 100644 index a5be68a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0002.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0003.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0003.png deleted file mode 100644 index 81041d1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0003.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0004.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0004.png deleted file mode 100644 index 66675b1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0004.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0005.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0005.png deleted file mode 100644 index 27aa0a7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0005.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0006.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0006.png deleted file mode 100644 index c0e86f1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0006.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0007.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0007.png deleted file mode 100644 index 3f2172a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0007.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0008.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0008.png deleted file mode 100644 index ff3eb40..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0008.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0009.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0009.png deleted file mode 100644 index f4dd59e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0009.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0010.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0010.png deleted file mode 100644 index 4608cce..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0010.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0011.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0011.png deleted file mode 100644 index ba556a0..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0011.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0012.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0012.png deleted file mode 100644 index 70fa589..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0012.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0013.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0013.png deleted file mode 100644 index d6bab1e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0013.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0014.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0014.png deleted file mode 100644 index ae75f72..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0014.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0015.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0015.png deleted file mode 100644 index 9877aad..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0015.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0016.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0016.png deleted file mode 100644 index 7a38c87..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0016.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0017.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0017.png deleted file mode 100644 index cc4810c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0017.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0018.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0018.png deleted file mode 100644 index 4fca975..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0018.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0019.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0019.png deleted file mode 100644 index 8b55fd1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0019.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0020.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0020.png deleted file mode 100644 index 7f56567..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0020.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0021.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0021.png deleted file mode 100644 index e4098c4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0021.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0022.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0022.png deleted file mode 100644 index c5cfd76..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0022.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0023.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0023.png deleted file mode 100644 index 352c1be..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0023.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0024.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0024.png deleted file mode 100644 index 3433f1b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0024.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0025.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0025.png deleted file mode 100644 index 65021d7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0025.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0026.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0026.png deleted file mode 100644 index ff771bb..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0026.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0027.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0027.png deleted file mode 100644 index 14c6088..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0027.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0028.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0028.png deleted file mode 100644 index d107337..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0028.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0029.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0029.png deleted file mode 100644 index d107337..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0029.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0030.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0030.png deleted file mode 100644 index 14c6088..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0030.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0031.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0031.png deleted file mode 100644 index 33726d2..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0031.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0032.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0032.png deleted file mode 100644 index 17c5023..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0032.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0033.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0033.png deleted file mode 100644 index d1eb0f2..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0033.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0034.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0034.png deleted file mode 100644 index 47c91b7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0034.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0035.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0035.png deleted file mode 100644 index 505da7e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0035.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0036.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0036.png deleted file mode 100644 index b1d626b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0036.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0037.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0037.png deleted file mode 100644 index 8e65336..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0037.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0038.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0038.png deleted file mode 100644 index 21f369a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0038.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0039.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0039.png deleted file mode 100644 index 9439526..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0039.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0040.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0040.png deleted file mode 100644 index ee13c24..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0040.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0041.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0041.png deleted file mode 100644 index c7f0dca..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0041.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0042.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0042.png deleted file mode 100644 index b86f56f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0042.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0043.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0043.png deleted file mode 100644 index 82d06b1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0043.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0044.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0044.png deleted file mode 100644 index 82a3f54..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0044.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0045.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0045.png deleted file mode 100644 index 9221722..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0045.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0046.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0046.png deleted file mode 100644 index c42a8a6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0046.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0047.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0047.png deleted file mode 100644 index f6a4689..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0047.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0048.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0048.png deleted file mode 100644 index cb51e1c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0048.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0049.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0049.png deleted file mode 100644 index bbdb726..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0049.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0050.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0050.png deleted file mode 100644 index 4a771e3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0050.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0051.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0051.png deleted file mode 100644 index d435fa8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0051.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0052.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0052.png deleted file mode 100644 index b3f3c28..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0052.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0053.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0053.png deleted file mode 100644 index b466484..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0053.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0054.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0054.png deleted file mode 100644 index f8a0cfc..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0054.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0055.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0055.png deleted file mode 100644 index a5be68a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0055.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0056.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0056.png deleted file mode 100644 index 6229837..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0056.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0057.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0057.png deleted file mode 100644 index 0790b40..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0057.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0058.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0058.png deleted file mode 100644 index 507a41f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0058.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0059.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0059.png deleted file mode 100644 index 7e3c763..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0059.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0060.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0060.png deleted file mode 100644 index a5ca97e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0060.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0061.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0061.png deleted file mode 100644 index f5066a4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0061.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0062.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0062.png deleted file mode 100644 index 6ee4843..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0062.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0063.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0063.png deleted file mode 100644 index d73f360..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0063.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0064.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0064.png deleted file mode 100644 index 156af10..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0064.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0065.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0065.png deleted file mode 100644 index 79330f4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0065.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0066.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0066.png deleted file mode 100644 index f47767e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0066.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0067.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0067.png deleted file mode 100644 index 778fa53..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0067.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0068.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0068.png deleted file mode 100644 index d84eea6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0068.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0069.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0069.png deleted file mode 100644 index fd6ddd5..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0069.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0070.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0070.png deleted file mode 100644 index 5a8e898..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0070.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0071.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0071.png deleted file mode 100644 index 189f5c8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0071.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0072.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0072.png deleted file mode 100644 index 9a414c6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0072.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0073.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0073.png deleted file mode 100644 index 96e7ee0..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0073.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0074.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0074.png deleted file mode 100644 index c1fe811..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0074.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0075.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0075.png deleted file mode 100644 index e5561a5..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0075.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0076.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0076.png deleted file mode 100644 index 0961d4e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0076.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0077.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0077.png deleted file mode 100644 index 5414b3e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0077.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0078.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0078.png deleted file mode 100644 index 1da7567..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0078.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0079.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0079.png deleted file mode 100644 index 0f09a02..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0079.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0080.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0080.png deleted file mode 100644 index 2b098a0..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0080.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0081.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0081.png deleted file mode 100644 index 5482d9c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0081.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0082.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0082.png deleted file mode 100644 index 004f022..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0082.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0083.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0083.png deleted file mode 100644 index 0b7f0df..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0083.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0084.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0084.png deleted file mode 100644 index 715279a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0084.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0085.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0085.png deleted file mode 100644 index bacba50..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0085.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0086.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0086.png deleted file mode 100644 index 49f60c9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0086.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0087.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0087.png deleted file mode 100644 index 2b99933..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0087.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0088.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0088.png deleted file mode 100644 index 50ba2d7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0088.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0089.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0089.png deleted file mode 100644 index 7c3f08a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0089.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0090.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0090.png deleted file mode 100644 index 991c4fc..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0090.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0091.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0091.png deleted file mode 100644 index 5414b3e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0091.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0092.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0092.png deleted file mode 100644 index 6229837..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0092.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0093.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0093.png deleted file mode 100644 index 6ffe33b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0093.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0094.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0094.png deleted file mode 100644 index 7f72355..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0094.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0095.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0095.png deleted file mode 100644 index ec44fb5..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0095.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0096.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0096.png deleted file mode 100644 index 0b8dd1a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0096.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0097.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0097.png deleted file mode 100644 index 0899a89..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0097.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0098.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0098.png deleted file mode 100644 index 0899a89..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0098.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0099.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0099.png deleted file mode 100644 index 055fadc..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0099.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0100.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0100.png deleted file mode 100644 index 85b38c9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0100.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0101.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0101.png deleted file mode 100644 index 32fb876..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0101.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0102.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0102.png deleted file mode 100644 index 16ec6ce..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0102.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0103.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0103.png deleted file mode 100644 index 6229837..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0103.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0104.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0104.png deleted file mode 100644 index 2b67a7f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0104.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0105.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0105.png deleted file mode 100644 index 47a7b93..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0105.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0106.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0106.png deleted file mode 100644 index d3de5d9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0106.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0107.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0107.png deleted file mode 100644 index e501af4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0107.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0108.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0108.png deleted file mode 100644 index 8493c48..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0108.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0109.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0109.png deleted file mode 100644 index 228acfe..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0109.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0110.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0110.png deleted file mode 100644 index e339aad..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0110.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0111.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0111.png deleted file mode 100644 index a4d7191..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0111.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0112.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0112.png deleted file mode 100644 index 2b5c57d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0112.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0113.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0113.png deleted file mode 100644 index 8336201..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0113.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0114.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0114.png deleted file mode 100644 index e26a11b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0114.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0115.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0115.png deleted file mode 100644 index 7f86d2d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0115.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0116.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0116.png deleted file mode 100644 index f9abe05..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0116.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0117.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0117.png deleted file mode 100644 index cb5c5a9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0117.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0118.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0118.png deleted file mode 100644 index a15ac81..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0118.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0119.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0119.png deleted file mode 100644 index ac5680a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0119.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0120.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0120.png deleted file mode 100644 index 94f7cf3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0120.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0121.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0121.png deleted file mode 100644 index 155a466..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0121.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0122.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0122.png deleted file mode 100644 index 9d3e8f7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0122.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0123.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0123.png deleted file mode 100644 index 5123dbb..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0123.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0124.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0124.png deleted file mode 100644 index 8dc4002..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0124.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0125.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0125.png deleted file mode 100644 index 0eeef26..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0125.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0126.png b/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0126.png deleted file mode 100644 index 8512cad..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithArmsSmall/ChuckAnimations0126.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0001.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0001.png deleted file mode 100755 index a43b94a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0001.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0002.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0002.png deleted file mode 100755 index 889c57b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0002.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0003.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0003.png deleted file mode 100755 index 569f986..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0003.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0004.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0004.png deleted file mode 100755 index f95c011..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0004.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0005.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0005.png deleted file mode 100755 index 567987f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0005.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0006.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0006.png deleted file mode 100755 index f18650e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0006.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0007.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0007.png deleted file mode 100755 index 6ad399f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0007.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0008.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0008.png deleted file mode 100755 index de48fc0..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0008.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0009.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0009.png deleted file mode 100755 index d250a62..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0009.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0010.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0010.png deleted file mode 100755 index f544677..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0010.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0011.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0011.png deleted file mode 100755 index d014210..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0011.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0012.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0012.png deleted file mode 100755 index 8c6c6a9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0012.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0013.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0013.png deleted file mode 100755 index 98e469b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0013.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0014.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0014.png deleted file mode 100755 index 0dc72d2..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0014.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0015.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0015.png deleted file mode 100755 index 1d024c9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0015.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0016.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0016.png deleted file mode 100755 index 7520a10..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0016.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0017.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0017.png deleted file mode 100755 index eca3d5a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0017.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0018.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0018.png deleted file mode 100755 index e74e65f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0018.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0019.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0019.png deleted file mode 100755 index 627e581..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0019.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0020.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0020.png deleted file mode 100755 index 17107d1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0020.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0021.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0021.png deleted file mode 100755 index f25d271..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0021.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0022.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0022.png deleted file mode 100755 index bce41a9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0022.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0023.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0023.png deleted file mode 100755 index 9613820..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0023.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0024.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0024.png deleted file mode 100755 index b344aa4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0024.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0025.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0025.png deleted file mode 100755 index ee53a0c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0025.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0026.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0026.png deleted file mode 100755 index 8ae0514..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0026.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0027.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0027.png deleted file mode 100755 index 2636127..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0027.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0028.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0028.png deleted file mode 100755 index eb12c00..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0028.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0029.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0029.png deleted file mode 100755 index eb12c00..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0029.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0030.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0030.png deleted file mode 100755 index ef9f93d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0030.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0031.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0031.png deleted file mode 100755 index e5a7d85..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0031.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0032.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0032.png deleted file mode 100755 index 3672aa3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0032.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0033.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0033.png deleted file mode 100755 index ba46823..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0033.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0034.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0034.png deleted file mode 100755 index 962ad48..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0034.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0035.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0035.png deleted file mode 100755 index 45535d5..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0035.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0036.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0036.png deleted file mode 100755 index 8f73556..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0036.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0037.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0037.png deleted file mode 100755 index 0c1f93e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0037.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0038.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0038.png deleted file mode 100755 index 9481d99..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0038.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0039.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0039.png deleted file mode 100755 index cc4f91a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0039.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0040.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0040.png deleted file mode 100755 index d5d59c2..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0040.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0041.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0041.png deleted file mode 100755 index c2f76f9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0041.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0042.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0042.png deleted file mode 100755 index 160a516..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0042.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0043.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0043.png deleted file mode 100755 index 71d5b94..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0043.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0044.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0044.png deleted file mode 100755 index b8403a3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0044.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0045.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0045.png deleted file mode 100755 index 081df13..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0045.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0046.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0046.png deleted file mode 100755 index 3f46d76..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0046.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0047.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0047.png deleted file mode 100755 index 4c7d383..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0047.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0048.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0048.png deleted file mode 100755 index e1154d3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0048.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0049.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0049.png deleted file mode 100755 index f3b2be6..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0049.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0050.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0050.png deleted file mode 100755 index 98e55dd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0050.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0051.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0051.png deleted file mode 100755 index 8d31529..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0051.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0052.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0052.png deleted file mode 100755 index fbd829c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0052.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0053.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0053.png deleted file mode 100755 index f2fcfb7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0053.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0054.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0054.png deleted file mode 100755 index cf3352e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0054.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0055.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0055.png deleted file mode 100755 index 889c57b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0055.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0056.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0056.png deleted file mode 100755 index a43b94a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0056.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0057.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0057.png deleted file mode 100755 index 9983066..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0057.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0058.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0058.png deleted file mode 100755 index 66f2309..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0058.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0059.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0059.png deleted file mode 100755 index 2b40463..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0059.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0060.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0060.png deleted file mode 100755 index a4c5f68..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0060.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0061.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0061.png deleted file mode 100755 index 8c416fa..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0061.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0062.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0062.png deleted file mode 100755 index 8e21f82..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0062.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0063.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0063.png deleted file mode 100755 index 00e2970..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0063.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0064.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0064.png deleted file mode 100755 index d779c27..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0064.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0065.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0065.png deleted file mode 100755 index 0d5c1e1..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0065.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0066.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0066.png deleted file mode 100755 index ef1fd8a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0066.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0067.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0067.png deleted file mode 100755 index 8edce0d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0067.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0068.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0068.png deleted file mode 100755 index 7911b96..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0068.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0069.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0069.png deleted file mode 100755 index a43b94a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0069.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0070.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0070.png deleted file mode 100755 index 326296b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0070.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0071.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0071.png deleted file mode 100755 index 5d095a7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0071.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0072.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0072.png deleted file mode 100755 index 9ee1793..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0072.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0073.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0073.png deleted file mode 100755 index 684d138..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0073.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0074.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0074.png deleted file mode 100755 index 6cc8d9c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0074.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0075.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0075.png deleted file mode 100755 index 8266d97..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0075.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0076.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0076.png deleted file mode 100755 index 6450ba8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0076.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0077.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0077.png deleted file mode 100755 index a7bbb6b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0077.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0078.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0078.png deleted file mode 100755 index d6450c3..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0078.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0079.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0079.png deleted file mode 100755 index 47fc743..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0079.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0080.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0080.png deleted file mode 100755 index 6265aed..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0080.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0081.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0081.png deleted file mode 100755 index 61174a5..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0081.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0082.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0082.png deleted file mode 100755 index 6ed7da9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0082.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0083.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0083.png deleted file mode 100755 index f8e0c19..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0083.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0084.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0084.png deleted file mode 100755 index 8a2ec54..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0084.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0085.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0085.png deleted file mode 100755 index a0ddcb0..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0085.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0086.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0086.png deleted file mode 100755 index d3a1da9..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0086.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0087.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0087.png deleted file mode 100755 index e6ba43f..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0087.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0088.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0088.png deleted file mode 100755 index fff250a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0088.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0089.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0089.png deleted file mode 100755 index 6c042a4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0089.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0090.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0090.png deleted file mode 100755 index e359754..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0090.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0091.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0091.png deleted file mode 100755 index a7bbb6b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0091.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0092.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0092.png deleted file mode 100755 index a43b94a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0092.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0093.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0093.png deleted file mode 100755 index 10921b7..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0093.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0094.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0094.png deleted file mode 100755 index 82fa11e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0094.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0095.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0095.png deleted file mode 100755 index 0ed0300..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0095.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0096.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0096.png deleted file mode 100755 index 78537ba..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0096.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0097.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0097.png deleted file mode 100755 index c9f1e01..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0097.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0098.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0098.png deleted file mode 100755 index c9f1e01..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0098.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0099.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0099.png deleted file mode 100755 index b343980..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0099.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0100.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0100.png deleted file mode 100755 index 15571bd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0100.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0101.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0101.png deleted file mode 100755 index c777e29..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0101.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0102.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0102.png deleted file mode 100755 index 7cede48..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0102.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0103.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0103.png deleted file mode 100755 index a43b94a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0103.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0104.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0104.png deleted file mode 100755 index 17a1258..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0104.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0105.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0105.png deleted file mode 100755 index 1c5f7cf..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0105.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0106.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0106.png deleted file mode 100755 index bf9e631..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0106.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0107.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0107.png deleted file mode 100755 index 9e6d1c8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0107.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0108.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0108.png deleted file mode 100755 index dd5ad48..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0108.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0109.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0109.png deleted file mode 100755 index 18c13a8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0109.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0110.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0110.png deleted file mode 100755 index fbd55ad..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0110.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0111.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0111.png deleted file mode 100755 index 8233331..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0111.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0112.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0112.png deleted file mode 100755 index ba0c5dd..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0112.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0113.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0113.png deleted file mode 100755 index 7687ba4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0113.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0114.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0114.png deleted file mode 100755 index dac3f9d..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0114.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0115.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0115.png deleted file mode 100755 index 52a0d19..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0115.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0116.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0116.png deleted file mode 100755 index afb08cb..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0116.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0117.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0117.png deleted file mode 100755 index f4677a8..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0117.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0118.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0118.png deleted file mode 100755 index 3fe7480..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0118.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0119.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0119.png deleted file mode 100755 index 2216d74..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0119.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0120.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0120.png deleted file mode 100755 index 256a82c..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0120.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0121.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0121.png deleted file mode 100755 index 3e23700..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0121.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0122.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0122.png deleted file mode 100755 index e3ad9a4..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0122.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0123.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0123.png deleted file mode 100755 index 63e772e..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0123.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0124.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0124.png deleted file mode 100755 index c822a3b..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0124.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0125.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0125.png deleted file mode 100755 index b721d8a..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0125.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0126.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0126.png deleted file mode 100755 index 1d545fe..0000000 Binary files a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckAnimationsWithoutArms0126.png and /dev/null differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0001.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0001.png new file mode 100644 index 0000000..7cefd60 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0001.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0002.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0002.png new file mode 100644 index 0000000..7b2763b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0002.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0003.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0003.png new file mode 100644 index 0000000..e32e7f2 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0003.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0004.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0004.png new file mode 100644 index 0000000..19b21be Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0004.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0005.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0005.png new file mode 100644 index 0000000..d40b968 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0005.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0006.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0006.png new file mode 100644 index 0000000..a848919 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0006.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0007.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0007.png new file mode 100644 index 0000000..a1f7655 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0007.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0008.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0008.png new file mode 100644 index 0000000..c3ee9b6 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0008.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0009.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0009.png new file mode 100644 index 0000000..d2a4daa Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0009.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0010.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0010.png new file mode 100644 index 0000000..36de13d Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0010.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0011.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0011.png new file mode 100644 index 0000000..f41b7cb Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0011.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0012.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0012.png new file mode 100644 index 0000000..2ae40d4 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0012.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0013.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0013.png new file mode 100644 index 0000000..fc90276 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0013.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0014.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0014.png new file mode 100644 index 0000000..816cafe Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0014.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0015.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0015.png new file mode 100644 index 0000000..175c488 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0015.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0016.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0016.png new file mode 100644 index 0000000..e48973e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0016.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0017.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0017.png new file mode 100644 index 0000000..cc3a9c8 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0017.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0018.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0018.png new file mode 100644 index 0000000..91a78f2 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0018.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0019.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0019.png new file mode 100644 index 0000000..04ce31f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0019.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0020.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0020.png new file mode 100644 index 0000000..8fca7dc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0020.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0021.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0021.png new file mode 100644 index 0000000..d6ccadf Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0021.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0022.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0022.png new file mode 100644 index 0000000..c9202bb Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0022.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0023.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0023.png new file mode 100644 index 0000000..b88c6a5 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0023.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0024.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0024.png new file mode 100644 index 0000000..cf95a93 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0024.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0025.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0025.png new file mode 100644 index 0000000..4caa673 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0025.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0026.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0026.png new file mode 100644 index 0000000..556bff0 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0026.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0027.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0027.png new file mode 100644 index 0000000..900b71a Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0027.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0028.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0028.png new file mode 100644 index 0000000..50380c0 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0028.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0029.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0029.png new file mode 100644 index 0000000..50380c0 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0029.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0030.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0030.png new file mode 100644 index 0000000..2380967 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0030.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0031.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0031.png new file mode 100644 index 0000000..dc769b9 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0031.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0032.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0032.png new file mode 100644 index 0000000..50c3ec7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0032.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0033.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0033.png new file mode 100644 index 0000000..b36ba47 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0033.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0034.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0034.png new file mode 100644 index 0000000..60fcf85 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0034.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0035.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0035.png new file mode 100644 index 0000000..bdd023c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0035.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0036.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0036.png new file mode 100644 index 0000000..85a58a1 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0036.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0037.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0037.png new file mode 100644 index 0000000..7f65428 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0037.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0038.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0038.png new file mode 100644 index 0000000..04ce31f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0038.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0039.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0039.png new file mode 100644 index 0000000..cfaf429 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0039.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0040.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0040.png new file mode 100644 index 0000000..288b475 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0040.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0041.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0041.png new file mode 100644 index 0000000..f469692 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0041.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0042.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0042.png new file mode 100644 index 0000000..db018b9 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0042.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0043.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0043.png new file mode 100644 index 0000000..de8cd95 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0043.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0044.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0044.png new file mode 100644 index 0000000..b2dd24c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0044.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0045.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0045.png new file mode 100644 index 0000000..1613823 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0045.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0046.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0046.png new file mode 100644 index 0000000..f41b7cb Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0046.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0047.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0047.png new file mode 100644 index 0000000..a5e962a Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0047.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0048.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0048.png new file mode 100644 index 0000000..24751c2 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0048.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0049.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0049.png new file mode 100644 index 0000000..875dd07 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0049.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0050.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0050.png new file mode 100644 index 0000000..d2b5b6a Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0050.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0051.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0051.png new file mode 100644 index 0000000..f378d1d Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0051.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0052.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0052.png new file mode 100644 index 0000000..910b070 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0052.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0053.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0053.png new file mode 100644 index 0000000..a26d51d Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0053.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0054.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0054.png new file mode 100644 index 0000000..46f36a5 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0054.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0055.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0055.png new file mode 100644 index 0000000..7b2763b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0055.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0056.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0056.png new file mode 100644 index 0000000..7cefd60 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0056.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0057.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0057.png new file mode 100644 index 0000000..43c282d Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0057.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0058.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0058.png new file mode 100644 index 0000000..9700613 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0058.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0059.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0059.png new file mode 100644 index 0000000..479a07d Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0059.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0060.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0060.png new file mode 100644 index 0000000..272ed28 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0060.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0061.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0061.png new file mode 100644 index 0000000..dad9143 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0061.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0062.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0062.png new file mode 100644 index 0000000..1291664 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0062.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0063.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0063.png new file mode 100644 index 0000000..334eee6 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0063.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0064.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0064.png new file mode 100644 index 0000000..f259166 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0064.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0065.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0065.png new file mode 100644 index 0000000..1aedb70 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0065.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0066.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0066.png new file mode 100644 index 0000000..1c9a105 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0066.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0067.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0067.png new file mode 100644 index 0000000..2757f1e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0067.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0068.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0068.png new file mode 100644 index 0000000..01ec35e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0068.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0069.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0069.png new file mode 100644 index 0000000..7cefd60 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0069.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0070.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0070.png new file mode 100644 index 0000000..cd5ebef Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0070.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0071.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0071.png new file mode 100644 index 0000000..1eae9ce Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0071.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0072.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0072.png new file mode 100644 index 0000000..5a054b2 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0072.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0073.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0073.png new file mode 100644 index 0000000..fcebc18 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0073.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0074.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0074.png new file mode 100644 index 0000000..2214f74 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0074.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0075.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0075.png new file mode 100644 index 0000000..fa4a4dd Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0075.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0076.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0076.png new file mode 100644 index 0000000..b584ba5 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0076.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0077.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0077.png new file mode 100644 index 0000000..1355468 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0077.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0078.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0078.png new file mode 100644 index 0000000..cefb324 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0078.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0079.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0079.png new file mode 100644 index 0000000..b4b13ac Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0079.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0080.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0080.png new file mode 100644 index 0000000..6a51f0f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0080.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0081.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0081.png new file mode 100644 index 0000000..ad1bd83 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0081.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0082.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0082.png new file mode 100644 index 0000000..d48919b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0082.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0083.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0083.png new file mode 100644 index 0000000..99d3715 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0083.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0084.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0084.png new file mode 100644 index 0000000..71a0929 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0084.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0085.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0085.png new file mode 100644 index 0000000..7e75fa3 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0085.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0086.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0086.png new file mode 100644 index 0000000..cda38bc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0086.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0087.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0087.png new file mode 100644 index 0000000..f30f88e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0087.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0088.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0088.png new file mode 100644 index 0000000..ce4db0e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0088.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0089.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0089.png new file mode 100644 index 0000000..b9005df Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0089.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0090.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0090.png new file mode 100644 index 0000000..121d13b Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0090.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0091.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0091.png new file mode 100644 index 0000000..1355468 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0091.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0092.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0092.png new file mode 100644 index 0000000..7cefd60 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0092.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0093.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0093.png new file mode 100644 index 0000000..d23cfd5 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0093.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0094.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0094.png new file mode 100644 index 0000000..f3bab80 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0094.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0095.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0095.png new file mode 100644 index 0000000..9a3a076 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0095.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0096.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0096.png new file mode 100644 index 0000000..6824b1a Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0096.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0097.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0097.png new file mode 100644 index 0000000..77139fd Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0097.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0098.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0098.png new file mode 100644 index 0000000..77139fd Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0098.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0099.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0099.png new file mode 100644 index 0000000..8ba9366 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0099.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0100.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0100.png new file mode 100644 index 0000000..2eed240 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0100.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0101.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0101.png new file mode 100644 index 0000000..d5d9c1c Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0101.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0102.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0102.png new file mode 100644 index 0000000..7a3bf22 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0102.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0103.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0103.png new file mode 100644 index 0000000..7cefd60 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0103.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0104.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0104.png new file mode 100644 index 0000000..ef8966f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0104.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0105.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0105.png new file mode 100644 index 0000000..88f42c3 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0105.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0106.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0106.png new file mode 100644 index 0000000..92d20ee Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0106.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0107.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0107.png new file mode 100644 index 0000000..ba0af92 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0107.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0108.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0108.png new file mode 100644 index 0000000..f9d05af Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0108.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0109.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0109.png new file mode 100644 index 0000000..8499e5e Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0109.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0110.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0110.png new file mode 100644 index 0000000..20be9b9 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0110.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0111.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0111.png new file mode 100644 index 0000000..00b9a86 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0111.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0112.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0112.png new file mode 100644 index 0000000..a3a46ce Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0112.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0113.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0113.png new file mode 100644 index 0000000..ea3103f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0113.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0114.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0114.png new file mode 100644 index 0000000..4e7e455 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0114.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0115.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0115.png new file mode 100644 index 0000000..ac34fb7 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0115.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0116.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0116.png new file mode 100644 index 0000000..02aa2c2 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0116.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0117.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0117.png new file mode 100644 index 0000000..f5aef6f Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0117.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0118.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0118.png new file mode 100644 index 0000000..5808c38 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0118.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0119.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0119.png new file mode 100644 index 0000000..68e7dbd Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0119.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0120.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0120.png new file mode 100644 index 0000000..0d660cc Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0120.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0121.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0121.png new file mode 100644 index 0000000..19c9656 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0121.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0122.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0122.png new file mode 100644 index 0000000..8c80f28 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0122.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0123.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0123.png new file mode 100644 index 0000000..39eca53 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0123.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0124.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0124.png new file mode 100644 index 0000000..2aac490 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0124.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0125.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0125.png new file mode 100644 index 0000000..ee61c80 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0125.png differ diff --git a/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0126.png b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0126.png new file mode 100644 index 0000000..49f0b09 Binary files /dev/null and b/static/img/Characters/Chuck/Animation/WithoutArms/ChuckWithoutArms0126.png differ diff --git a/static/img/Characters/Chuck/holdingArm.png b/static/img/Characters/Chuck/holdingArm.png new file mode 100644 index 0000000..752ac9c Binary files /dev/null and b/static/img/Characters/Chuck/holdingArm.png differ diff --git a/static/img/Characters/Chuck/lowerLeftArm.png b/static/img/Characters/Chuck/lowerLeftArm.png index fb43dd5..85d7ac4 100755 Binary files a/static/img/Characters/Chuck/lowerLeftArm.png and b/static/img/Characters/Chuck/lowerLeftArm.png differ diff --git a/static/img/Characters/Chuck/lowerLeftLeg.png b/static/img/Characters/Chuck/lowerLeftLeg.png index 2314dec..c7b0803 100755 Binary files a/static/img/Characters/Chuck/lowerLeftLeg.png and b/static/img/Characters/Chuck/lowerLeftLeg.png differ diff --git a/static/img/Characters/Chuck/lowerRightArm.png b/static/img/Characters/Chuck/lowerRightArm.png index 2d64327..0a43089 100755 Binary files a/static/img/Characters/Chuck/lowerRightArm.png and b/static/img/Characters/Chuck/lowerRightArm.png differ diff --git a/static/img/Characters/Chuck/lowerRightLeg.png b/static/img/Characters/Chuck/lowerRightLeg.png index 2314dec..1d7c7b8 100755 Binary files a/static/img/Characters/Chuck/lowerRightLeg.png and b/static/img/Characters/Chuck/lowerRightLeg.png differ diff --git a/static/img/Characters/Chuck/upperLeftArm.png b/static/img/Characters/Chuck/upperLeftArm.png index 5a15fbe..61258a8 100755 Binary files a/static/img/Characters/Chuck/upperLeftArm.png and b/static/img/Characters/Chuck/upperLeftArm.png differ diff --git a/static/img/Characters/Chuck/upperRightArm.png b/static/img/Characters/Chuck/upperRightArm.png index 44bf6cf..cd31fce 100755 Binary files a/static/img/Characters/Chuck/upperRightArm.png and b/static/img/Characters/Chuck/upperRightArm.png differ diff --git a/static/img/Characters/Chuck/upperRightLeg.png b/static/img/Characters/Chuck/upperRightLeg.png index 11b5f29..c8ab6d6 100644 Binary files a/static/img/Characters/Chuck/upperRightLeg.png and b/static/img/Characters/Chuck/upperRightLeg.png differ diff --git a/static/img/tile_shape_rotation_map.jpg b/static/img/Extra/tile_shape_rotation_map.jpg similarity index 100% rename from static/img/tile_shape_rotation_map.jpg rename to static/img/Extra/tile_shape_rotation_map.jpg diff --git a/static/img/Items/outdoor/fence.gif b/static/img/Items/outdoor/fence.gif deleted file mode 100755 index 75642fc..0000000 Binary files a/static/img/Items/outdoor/fence.gif and /dev/null differ diff --git a/static/img/Items/outdoor/fence_door.gif b/static/img/Items/outdoor/fence_door.gif deleted file mode 100755 index 561d35a..0000000 Binary files a/static/img/Items/outdoor/fence_door.gif and /dev/null differ diff --git a/static/img/Items/outdoor/fencedoor.png b/static/img/Items/outdoor/fencedoor.png new file mode 100644 index 0000000..a2c07f0 Binary files /dev/null and b/static/img/Items/outdoor/fencedoor.png differ diff --git a/static/img/Items/outdoor/fencepiece.png b/static/img/Items/outdoor/fencepiece.png new file mode 100644 index 0000000..1bc055b Binary files /dev/null and b/static/img/Items/outdoor/fencepiece.png differ diff --git a/static/img/Tiles/Background/darkkitchentile.png b/static/img/Tiles/Background/darkkitchentile.png new file mode 100644 index 0000000..8000d07 Binary files /dev/null and b/static/img/Tiles/Background/darkkitchentile.png differ diff --git a/static/img/Tiles/Background/darksoil.png b/static/img/Tiles/Background/darksoil.png new file mode 100644 index 0000000..c3c9b63 Binary files /dev/null and b/static/img/Tiles/Background/darksoil.png differ diff --git a/static/img/Tiles/Background/window.png b/static/img/Tiles/Background/window.png new file mode 100644 index 0000000..4166cbb Binary files /dev/null and b/static/img/Tiles/Background/window.png differ diff --git a/static/img/Tiles/Background/woodpanels.png b/static/img/Tiles/Background/woodpanels.png new file mode 100644 index 0000000..e23ece4 Binary files /dev/null and b/static/img/Tiles/Background/woodpanels.png differ diff --git a/static/items/rube/ragdoll-full.json b/static/items/rube/ragdoll-full.json new file mode 100644 index 0000000..fc81cad --- /dev/null +++ b/static/items/rube/ragdoll-full.json @@ -0,0 +1,1346 @@ + +{ + "allowSleep" : true, + "autoClearForces" : true, + "body" : + [ + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.05748672783374786, + 0.05748672783374786, + -0.05748683214187622, + -0.05748683214187622 + ], + "y" : + [ + -0.2322469353675842, + 0.2322462797164917, + 0.2322462797164917, + -0.2322469353675842 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.001019014045596123, + "massData-center" : + { + "x" : -5.215406062575312e-08, + "y" : -3.278255462646484e-07 + }, + "massData-mass" : 0.05340443924069405, + "name" : "upperArmLeft", + "position" : + { + "x" : -0.1699507087469101, + "y" : 1.113796472549438 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture0", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.1718577891588211, + 0.1684816330671310, + 0.001688212156295776, + -0.1718577295541763, + -0.1718577295541763, + 0.001460619270801544 + ], + "y" : + [ + -0.3928470611572266, + 0.4921868443489075, + 0.4921868443489075, + 0.3841522336006165, + -0.4204435348510742, + -0.4519201517105103 + ] + } + } + }, + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture2", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.1679489463567734, + 0.1679489463567734, + -0.004204027354717255, + -0.004204027354717255 + ], + "y" : + [ + 0.4449140429496765, + 0.6170670390129089, + 0.6170670390129089, + 0.4449140429496765 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.03228222951292992, + "massData-center" : + { + "x" : 0.008858840912580490, + "y" : 0.06282533705234528 + }, + "massData-mass" : 0.3355117142200470, + "name" : "chest", + "position" : + { + "x" : -0.05338868126273155, + "y" : 0.9620395302772522 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "circle" : + { + "center" : + { + "x" : -0.007499951869249344, + "y" : 0.003749847412109375 + }, + "radius" : 0.2746430933475494 + }, + "density" : 0.2204959988594055, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture1" + }, + + { + "circle" : + { + "center" : + { + "x" : -0.03327952325344086, + "y" : -0.1384725570678711 + }, + "radius" : 0.2485582530498505 + }, + "density" : 0.2204959988594055, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture1" + } + ], + "linearVelocity" : 0, + "massData-I" : 0.004164268728345633, + "massData-center" : + { + "x" : -0.01910765282809734, + "y" : -0.06028826907277107 + }, + "massData-mass" : 0.09504657238721848, + "name" : "head", + "position" : + { + "x" : 0.04257059469819069, + "y" : 1.812389135360718 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.05748683214187622, + 0.05748683214187622, + -0.05748690664768219, + -0.05748690664768219 + ], + "y" : + [ + -0.1419981122016907, + 0.1419981718063354, + 0.1419981718063354, + -0.1419981122016907 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0002554289239924401, + "massData-center" : + { + "x" : -3.725290298461914e-08, + "y" : 2.980232238769531e-08 + }, + "massData-mass" : 0.03265211358666420, + "name" : "lowerArmRight", + "position" : + { + "x" : 0.1177217364311218, + "y" : 0.8479318022727966 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.1415265351533890, + 0.1415265351533890, + -0.08457186818122864, + -0.08457186818122864 + ], + "y" : + [ + -0.1143886670470238, + -0.05680520832538605, + -0.05680520832538605, + -0.1143886670470238 + ] + } + } + }, + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.08623030036687851, + 0.08623030036687851, + -0.08623020350933075, + -0.08623020350933075 + ], + "y" : + [ + -0.1138511821627617, + 0.1565139442682266, + 0.1565139442682266, + -0.1138511821627617 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0005858240183442831, + "massData-center" : + { + "x" : 0.006215983536094427, + "y" : -0.002008607611060143 + }, + "massData-mass" : 0.05964682996273041, + "name" : "lowerLegLeft", + "position" : + { + "x" : -0.08319067955017090, + "y" : 0.1298431605100632 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.05748684704303741, + 0.05748684704303741, + -0.05748672783374786, + -0.05748672783374786 + ], + "y" : + [ + -0.1419981122016907, + 0.1419981718063354, + 0.1419981718063354, + -0.1419981122016907 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0002554284874349833, + "massData-center" : + { + "x" : 5.960464477539062e-08, + "y" : 2.980232238769531e-08 + }, + "massData-mass" : 0.03265206888318062, + "name" : "lowerArmLeft", + "position" : + { + "x" : -0.1699528992176056, + "y" : 0.8479318022727966 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.05748683214187622, + 0.05748683214187622, + -0.05748690664768219, + -0.05748690664768219 + ], + "y" : + [ + -0.2322469353675842, + 0.2322462797164917, + 0.2322462797164917, + -0.2322469353675842 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.001019015791825950, + "massData-center" : + { + "x" : -3.725290298461914e-08, + "y" : -3.278255462646484e-07 + }, + "massData-mass" : 0.05340452119708061, + "name" : "upperArmRight", + "position" : + { + "x" : 0.1177217364311218, + "y" : 1.113796472549438 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.08623021841049194, + 0.08623021841049194, + -0.08623008430004120, + -0.08623008430004120 + ], + "y" : + [ + -0.2315792292356491, + 0.2315795421600342, + 0.2315795421600342, + -0.2315792292356491 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.001625877106562257, + "massData-center" : + { + "x" : 6.705522537231445e-08, + "y" : 1.564621925354004e-07 + }, + "massData-mass" : 0.07987650483846664, + "name" : "upperLegRight", + "position" : + { + "x" : 0.03142313286662102, + "y" : 0.4171121716499329 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.08623032271862030, + 0.08623032271862030, + -0.08623017370700836, + -0.08623017370700836 + ], + "y" : + [ + -0.2315792292356491, + 0.2315795421600342, + 0.2315795421600342, + -0.2315792292356491 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.001625879434868693, + "massData-center" : + { + "x" : 7.450580596923828e-08, + "y" : 1.564621925354004e-07 + }, + "massData-mass" : 0.07987659424543381, + "name" : "upperLegLeft", + "position" : + { + "x" : -0.08319067955017090, + "y" : 0.4171121716499329 + }, + "type" : 2 + }, + + { + "angle" : 0, + "angularVelocity" : 0, + "awake" : true, + "fixture" : + [ + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.08623021841049194, + 0.08623021841049194, + -0.08623008430004120, + -0.08623008430004120 + ], + "y" : + [ + -0.1138515025377274, + 0.1563164740800858, + 0.1563164740800858, + -0.1138515025377274 + ] + } + } + }, + + { + "density" : 1, + "filter-groupIndex" : -55, + "friction" : 0.2, + "name" : "fixture3", + "polygon" : + { + "vertices" : + { + "x" : + [ + 0.1415264606475830, + 0.1415264606475830, + -0.08457189798355103, + -0.08457189798355103 + ], + "y" : + [ + -0.1143886670470238, + -0.05680520832538605, + -0.05680520832538605, + -0.1143886670470238 + ] + } + } + } + ], + "linearVelocity" : 0, + "massData-I" : 0.0005849063745699823, + "massData-center" : + { + "x" : 0.006219535600394011, + "y" : -0.002099231118336320 + }, + "massData-mass" : 0.05961277708411217, + "name" : "lowerLegRight", + "position" : + { + "x" : 0.03142313286662102, + "y" : 0.1298431605100632 + }, + "type" : 2 + } + ], + "collisionbitplanes" : + { + "names" : + [ + "bitplane1", + "bitplane2", + "bitplane3", + "bitplane4", + "bitplane5", + "bitplane6", + "bitplane7", + "bitplane8", + "bitplane9", + "bitplane10", + "bitplane11", + "bitplane12", + "bitplane13", + "bitplane14", + "bitplane15", + "bitplane16", + "bitplane17", + "bitplane18", + "bitplane19", + "bitplane20", + "bitplane21", + "bitplane22", + "bitplane23", + "bitplane24", + "bitplane25", + "bitplane26", + "bitplane27", + "bitplane28", + "bitplane29", + "bitplane30", + "bitplane31", + "bitplane32" + ] + }, + "continuousPhysics" : true, + "gravity" : + { + "x" : 0, + "y" : -10 + }, + "image" : + [ + + { + "aspectScale" : 1, + "body" : 9, + "center" : + { + "x" : 0.02911517955362797, + "y" : -0.0009155124425888062 + }, + "corners" : + { + "x" : + [ + -0.08536797016859055, + 0.1435983330011368, + 0.1435983330011368, + -0.08536797016859055 + ], + "y" : + [ + -0.1153986603021622, + -0.1153986603021622, + 0.1135676354169846, + 0.1135676354169846 + ] + }, + "file" : "../../img/Characters/Chuck/lowerLeftLeg.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.08536797016859055, + -0.1153986603021622, + 0.1435983330011368, + -0.1153986603021622, + 0.1435983330011368, + 0.1135676354169846, + -0.08536797016859055, + 0.1135676354169846 + ], + "name" : "image5", + "opacity" : 1, + "scale" : 0.2289662957191467 + }, + + { + "aspectScale" : 1, + "body" : 7, + "center" : + { + "x" : -0.02732392773032188, + "y" : 0.02671334147453308 + }, + "corners" : + { + "x" : + [ + -0.1425068378448486, + 0.08785898983478546, + 0.08785898983478546, + -0.1425068378448486 + ], + "y" : + [ + -0.2324482202529907, + -0.2324482202529907, + 0.2858749032020569, + 0.2858749032020569 + ] + }, + "file" : "../../img/Characters/Chuck/upperRightLeg.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.1425068378448486, + -0.2324482202529907, + 0.08785898983478546, + -0.2324482202529907, + 0.08785898983478546, + 0.2858749032020569, + -0.1425068378448486, + 0.2858749032020569 + ], + "name" : "image6", + "opacity" : 1, + "scale" : 0.5183231234550476 + }, + + { + "aspectScale" : 1, + "body" : 6, + "center" : + { + "x" : 0.0003027096390724182, + "y" : 0.0006600618362426758 + }, + "corners" : + { + "x" : + [ + -0.05836960300803185, + 0.05897502228617668, + 0.05897502228617668, + -0.05836960300803185 + ], + "y" : + [ + -0.2340291887521744, + -0.2340291887521744, + 0.2353493124246597, + 0.2353493124246597 + ] + }, + "file" : "../../img/Characters/Chuck/upperLeftArm.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.05836960300803185, + -0.2340291887521744, + 0.05897502228617668, + -0.2340291887521744, + 0.05897502228617668, + 0.2353493124246597, + -0.05836960300803185, + 0.2353493124246597 + ], + "name" : "image4", + "opacity" : 1, + "renderOrder" : 1, + "scale" : 0.4693785011768341 + }, + + { + "aspectScale" : 1, + "body" : 3, + "center" : + { + "x" : 0.0007003694772720337, + "y" : 0.001779437065124512 + }, + "corners" : + { + "x" : + [ + -0.05596264451742172, + 0.05736338347196579, + 0.05736338347196579, + -0.05596264451742172 + ], + "y" : + [ + -0.1398780941963196, + -0.1398780941963196, + 0.1434369683265686, + 0.1434369683265686 + ] + }, + "file" : "../../img/Characters/Chuck/lowerLeftArm.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.05596264451742172, + -0.1398780941963196, + 0.05736338347196579, + -0.1398780941963196, + 0.05736338347196579, + 0.1434369683265686, + -0.05596264451742172, + 0.1434369683265686 + ], + "name" : "image3", + "opacity" : 1, + "renderOrder" : 1, + "scale" : 0.2833150625228882 + }, + + { + "aspectScale" : 1, + "body" : 1, + "center" : + { + "x" : -0.0008481591939926147, + "y" : -0.001265347003936768 + }, + "corners" : + { + "x" : + [ + -0.1698881536722183, + 0.1681918352842331, + 0.1681918352842331, + -0.1698881536722183 + ], + "y" : + [ + -0.480212002992630, + -0.480212002992630, + 0.4776813089847565, + 0.4776813089847565 + ] + }, + "file" : "../../img/Characters/Chuck/chest.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.1698881536722183, + -0.480212002992630, + 0.1681918352842331, + -0.480212002992630, + 0.1681918352842331, + 0.4776813089847565, + -0.1698881536722183, + 0.4776813089847565 + ], + "name" : "image2", + "opacity" : 1, + "renderOrder" : 5, + "scale" : 0.9578933119773865 + }, + + { + "aspectScale" : 1, + "body" : 8, + "center" : + { + "x" : 0.003173574805259705, + "y" : -0.001172244548797607 + }, + "corners" : + { + "x" : + [ + -0.1414211541414261, + 0.1477683037519455, + 0.1477683037519455, + -0.1414211541414261 + ], + "y" : + [ + -0.2325238138437271, + -0.2325238138437271, + 0.2301793247461319, + 0.2301793247461319 + ] + }, + "file" : "../../img/Characters/Chuck/upperLeftLeg.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.1414211541414261, + -0.2325238138437271, + 0.1477683037519455, + -0.2325238138437271, + 0.1477683037519455, + 0.2301793247461319, + -0.1414211541414261, + 0.2301793247461319 + ], + "name" : "image6", + "opacity" : 1, + "renderOrder" : 6, + "scale" : 0.4627031385898590 + }, + + { + "aspectScale" : 1, + "body" : 4, + "center" : + { + "x" : 0.02851789817214012, + "y" : -0.0009155124425888062 + }, + "corners" : + { + "x" : + [ + -0.08596524596214294, + 0.1430010497570038, + 0.1430010497570038, + -0.08596524596214294 + ], + "y" : + [ + -0.1153986603021622, + -0.1153986603021622, + 0.1135676354169846, + 0.1135676354169846 + ] + }, + "file" : "../../img/Characters/Chuck/lowerLeftLeg.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.08596524596214294, + -0.1153986603021622, + 0.1430010497570038, + -0.1153986603021622, + 0.1430010497570038, + 0.1135676354169846, + -0.08596524596214294, + 0.1135676354169846 + ], + "name" : "image5", + "opacity" : 1, + "renderOrder" : 6, + "scale" : 0.2289662957191467 + }, + + { + "aspectScale" : 1, + "body" : 2, + "center" : + { + "x" : 0.01975236460566521, + "y" : -0.07194232940673828 + }, + "corners" : + { + "x" : + [ + -0.2679373621940613, + 0.3074420690536499, + 0.3074420690536499, + -0.2679373621940613 + ], + "y" : + [ + -0.4171699881553650, + -0.4171699881553650, + 0.2732853293418884, + 0.2732853293418884 + ] + }, + "file" : "../../img/Characters/Chuck/head.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.2679373621940613, + -0.4171699881553650, + 0.3074420690536499, + -0.4171699881553650, + 0.3074420690536499, + 0.2732853293418884, + -0.2679373621940613, + 0.2732853293418884 + ], + "name" : "image1", + "opacity" : 1, + "renderOrder" : 6, + "scale" : 0.6904553174972534 + }, + + { + "aspectScale" : 1, + "body" : 0, + "center" : + { + "x" : 0.002138927578926086, + "y" : 0.0006600618362426758 + }, + "corners" : + { + "x" : + [ + -0.05653338506817818, + 0.06081124022603035, + 0.06081124022603035, + -0.05653338506817818 + ], + "y" : + [ + -0.2340291887521744, + -0.2340291887521744, + 0.2353493124246597, + 0.2353493124246597 + ] + }, + "file" : "../../img/Characters/Chuck/upperLeftArm.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.05653338506817818, + -0.2340291887521744, + 0.06081124022603035, + -0.2340291887521744, + 0.06081124022603035, + 0.2353493124246597, + -0.05653338506817818, + 0.2353493124246597 + ], + "name" : "image4", + "opacity" : 1, + "renderOrder" : 8, + "scale" : 0.4693785011768341 + }, + + { + "aspectScale" : 1, + "body" : 5, + "center" : + { + "x" : 0.002538725733757019, + "y" : 0.001779437065124512 + }, + "corners" : + { + "x" : + [ + -0.05412428826093674, + 0.05920173972845078, + 0.05920173972845078, + -0.05412428826093674 + ], + "y" : + [ + -0.1398780941963196, + -0.1398780941963196, + 0.1434369683265686, + 0.1434369683265686 + ] + }, + "file" : "../../img/Characters/Chuck/lowerLeftArm.png", + "filter" : 0, + "glDrawElements" : [ 0, 1, 2, 2, 3, 0 ], + "glTexCoordPointer" : [ 0.0, 0.0, 1, 0.0, 1, 1, 0.0, 1 ], + "glVertexPointer" : + [ + -0.05412428826093674, + -0.1398780941963196, + 0.05920173972845078, + -0.1398780941963196, + 0.05920173972845078, + 0.1434369683265686, + -0.05412428826093674, + 0.1434369683265686 + ], + "name" : "image3", + "opacity" : 1, + "renderOrder" : 8, + "scale" : 0.2833150625228882 + } + ], + "joint" : + [ + + { + "anchorA" : + { + "x" : 0.001047849655151367, + "y" : -0.1790249347686768 + }, + "anchorB" : + { + "x" : 0.001048207283020020, + "y" : 0.08683943748474121 + }, + "bodyA" : 0, + "bodyB" : 5, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : 0, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint4", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.919862151145935 + }, + + { + "anchorA" : + { + "x" : -0.1165831685066223, + "y" : 0.3330366015434265 + }, + "anchorB" : + { + "x" : -2.135336399078369e-05, + "y" : 0.1812803745269775 + }, + "bodyA" : 1, + "bodyB" : 0, + "enableLimit" : false, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -2.268928050994873, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint3", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 3.141592741012573 + }, + + { + "anchorA" : + { + "x" : 0.07454992830753326, + "y" : 0.5068108439445496 + }, + "anchorB" : + { + "x" : -0.02141102217137814, + "y" : -0.3435407876968384 + }, + "bodyA" : 1, + "bodyB" : 2, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -1.221730470657349, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint0", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 0.6981316804885864 + }, + + { + "anchorA" : + { + "x" : 0.1367489844560623, + "y" : -0.3606387376785278 + }, + "anchorB" : + { + "x" : 0.05056380107998848, + "y" : 0.1842886805534363 + }, + "bodyA" : 1, + "bodyB" : 7, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -0.6981316804885864, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint5", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.919862151145935 + }, + + { + "anchorA" : + { + "x" : -0.08329562842845917, + "y" : -0.3541148304939270 + }, + "anchorB" : + { + "x" : -0.05503869056701660, + "y" : 0.1909851431846619 + }, + "bodyA" : 1, + "bodyB" : 8, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -0.6981316804885864, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint6", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.919862151145935 + }, + + { + "anchorA" : + { + "x" : 0.1710196435451508, + "y" : 0.3308989405632019 + }, + "anchorB" : + { + "x" : -9.131431579589844e-05, + "y" : 0.1791421175003052 + }, + "bodyA" : 1, + "bodyB" : 6, + "enableLimit" : false, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -2.268928050994873, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint2", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 3.141592741012573 + }, + + { + "anchorA" : + { + "x" : 0.0004334598779678345, + "y" : 0.08706557750701904 + }, + "anchorB" : + { + "x" : 0.0004332214593887329, + "y" : -0.1787990331649780 + }, + "bodyA" : 3, + "bodyB" : 6, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : 0, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint1", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.919862151145935 + }, + + { + "anchorA" : + { + "x" : 0.002425249665975571, + "y" : -0.1845821887254715 + }, + "anchorB" : + { + "x" : 0.002425376325845718, + "y" : 0.1026860624551773 + }, + "bodyA" : 7, + "bodyB" : 9, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -2.268928050994873, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint8", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 0 + }, + + { + "anchorA" : + { + "x" : -0.0009558200836181641, + "y" : -0.1818936169147491 + }, + "anchorB" : + { + "x" : -0.0009555891156196594, + "y" : 0.1055182516574860 + }, + "bodyA" : 8, + "bodyB" : 4, + "enableLimit" : true, + "enableMotor" : false, + "jointSpeed" : 0, + "lowerLimit" : -2.268928050994873, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint7", + "refAngle" : 0, + "type" : "revolute", + "upperLimit" : 0 + } + ], + "positionIterations" : 3, + "stepsPerSecond" : 60.0, + "subStepping" : false, + "velocityIterations" : 8, + "warmStarting" : true +} diff --git a/static/items/rube/ragdoll.rube b/static/items/rube/ragdoll.rube index 53f1406..bbe7d2d 100644 --- a/static/items/rube/ragdoll.rube +++ b/static/items/rube/ragdoll.rube @@ -71,7 +71,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 17, + "id" : 2, "name" : "fixture3", "shapes" : [ @@ -84,67 +84,35 @@ { "x" : [ - -0.08623008430004120, - 0.08623021841049194, - 0.08623021841049194, - -0.08623008430004120 + 0.07039500027894974, + 0.07039500027894974, + -0.07039490342140198, + -0.07039490342140198 ], "y" : [ - -0.1138515025377274, - -0.1138515025377274, - 0.1563164740800858, - 0.1563164740800858 - ] - } - }, - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "id" : 18, - "name" : "fixture3", - "shapes" : - [ - - { - "type" : "polygon" - } - ], - "vertices" : - { - "x" : - [ - -0.08457189798355103, - 0.1415264606475830, - 0.1415264606475830, - -0.08457189798355103 - ], - "y" : - [ - -0.1143886670470238, - -0.1143886670470238, - -0.05680520832538605, - -0.05680520832538605 + -0.09294389933347702, + 0.1276109963655472, + 0.1276109963655472, + -0.09294389933347702 ] } } ], - "id" : 13, + "id" : 1, "linearVelocity" : 0, - "massData-I" : 0.0005849063745699823, + "massData-I" : 0.0001864968799054623, "massData-center" : { - "x" : 0.006219535600394011, - "y" : -0.002099231118336320 + "x" : 4.842877032729120e-08, + "y" : 0.01733354665338993 }, - "massData-mass" : 0.05961277708411217, - "name" : "lowerLegRight", + "massData-mass" : 0.03105190023779869, + "name" : "lowerRightLeg", "position" : { - "x" : 0.03142313286662102, - "y" : 0.1298431605100632 + "x" : 0.03859551250934601, + "y" : 0.09799569845199585 }, "type" : "dynamic" }, @@ -160,7 +128,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 19, + "id" : 3, "name" : "fixture3", "shapes" : [ @@ -173,35 +141,35 @@ { "x" : [ - -0.08623017370700836, - 0.08623032271862030, - 0.08623032271862030, - -0.08623017370700836 + 0.07039505988359451, + 0.07039505988359451, + -0.07039495557546616, + -0.07039495557546616 ], "y" : [ - -0.2315792292356491, - -0.2315792292356491, - 0.2315795421600342, - 0.2315795421600342 + -0.1890522241592407, + 0.1890524625778198, + 0.1890524625778198, + -0.1890522241592407 ] } } ], - "id" : 14, + "id" : 2, "linearVelocity" : 0, - "massData-I" : 0.001625879434868693, + "massData-I" : 0.000722132739610970, "massData-center" : { - "x" : 7.450580596923828e-08, - "y" : 1.564621925354004e-07 + "x" : 5.215406417846680e-08, + "y" : 1.192092824453539e-07 }, - "massData-mass" : 0.07987659424543381, - "name" : "upperLegLeft", + "massData-mass" : 0.05323336645960808, + "name" : "upperLeftLeg", "position" : { - "x" : -0.08319067955017090, - "y" : 0.4171121716499329 + "x" : -0.03829947486519814, + "y" : 0.3325110077857971 }, "type" : "dynamic" }, @@ -217,7 +185,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 20, + "id" : 4, "name" : "fixture3", "shapes" : [ @@ -230,35 +198,35 @@ { "x" : [ - -0.08623008430004120, - 0.08623021841049194, - 0.08623021841049194, - -0.08623008430004120 + 0.07039496302604675, + 0.07039496302604675, + -0.07039486616849899, + -0.07039486616849899 ], "y" : [ - -0.2315792292356491, - -0.2315792292356491, - 0.2315795421600342, - 0.2315795421600342 + -0.1890522241592407, + 0.1890524625778198, + 0.1890524625778198, + -0.1890522241592407 ] } } ], - "id" : 15, + "id" : 3, "linearVelocity" : 0, - "massData-I" : 0.001625877106562257, + "massData-I" : 0.0007221315754577518, "massData-center" : { - "x" : 6.705522537231445e-08, - "y" : 1.564621925354004e-07 + "x" : 4.842877388000488e-08, + "y" : 1.192092895507812e-07 }, - "massData-mass" : 0.07987650483846664, - "name" : "upperLegRight", + "massData-mass" : 0.05323329567909241, + "name" : "upperRightLeg", "position" : { - "x" : 0.03142313286662102, - "y" : 0.4171121716499329 + "x" : 0.03859551250934601, + "y" : 0.3325110077857971 }, "type" : "dynamic" }, @@ -274,7 +242,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 21, + "id" : 5, "name" : "fixture3", "shapes" : [ @@ -287,35 +255,35 @@ { "x" : [ - -0.05748690664768219, - 0.05748683214187622, - 0.05748683214187622, - -0.05748690664768219 + 0.04693000018596649, + 0.04693000018596649, + -0.04693010076880455, + -0.04693010076880455 ], "y" : [ - -0.2322469353675842, - -0.2322469353675842, - 0.2322462797164917, - 0.2322462797164917 + -0.1895969957113266, + 0.1895969957113266, + 0.1895969957113266, + -0.1895969957113266 ] } } ], - "id" : 16, + "id" : 4, "linearVelocity" : 0, - "massData-I" : 0.001019015791825950, + "massData-I" : 0.0004525947733782232, "massData-center" : { - "x" : -3.725290298461914e-08, - "y" : -3.278255462646484e-07 + "x" : -5.029141902923584e-08, + "y" : 0 }, - "massData-mass" : 0.05340452119708061, - "name" : "upperArmRight", + "massData-mass" : 0.03559118881821632, + "name" : "upperRightArm", "position" : { - "x" : 0.1177217364311218, - "y" : 1.113796472549438 + "x" : 0.1183080002665520, + "y" : 0.9012569785118103 }, "type" : "dynamic" }, @@ -331,7 +299,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 22, + "id" : 6, "name" : "fixture3", "shapes" : [ @@ -344,35 +312,35 @@ { "x" : [ - -0.05748672783374786, - 0.05748684704303741, - 0.05748684704303741, - -0.05748672783374786 + 0.04693000763654709, + 0.04693000763654709, + -0.04692991077899933, + -0.04692991077899933 ], "y" : [ - -0.1419981122016907, - -0.1419981122016907, - 0.1419981718063354, - 0.1419981718063354 + -0.1159216761589050, + 0.1159217953681946, + 0.1159217953681946, + -0.1159216761589050 ] } } ], - "id" : 17, + "id" : 5, "linearVelocity" : 0, - "massData-I" : 0.0002554284874349833, + "massData-I" : 0.0001134483172791079, "massData-center" : { - "x" : 5.960464477539062e-08, - "y" : 2.980232238769531e-08 + "x" : 4.842877032729120e-08, + "y" : 5.960464477539062e-08 }, - "massData-mass" : 0.03265206888318062, - "name" : "lowerArmLeft", + "massData-mass" : 0.02176081016659737, + "name" : "lowerLeftArm", "position" : { - "x" : -0.1699528992176056, - "y" : 0.8479318022727966 + "x" : -0.1165381968021393, + "y" : 0.6842151284217834 }, "type" : "dynamic" }, @@ -388,7 +356,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 23, + "id" : 7, "name" : "fixture3", "shapes" : [ @@ -401,67 +369,35 @@ { "x" : [ - -0.08623020350933075, - 0.08623030036687851, - 0.08623030036687851, - -0.08623020350933075 + 0.07039500027894974, + 0.07039500027894974, + -0.07039500027894974, + -0.07039500027894974 ], "y" : [ - -0.1138511821627617, - -0.1138511821627617, - 0.1565139442682266, - 0.1565139442682266 - ] - } - }, - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "id" : 24, - "name" : "fixture3", - "shapes" : - [ - - { - "type" : "polygon" - } - ], - "vertices" : - { - "x" : - [ - -0.08457186818122864, - 0.1415265351533890, - 0.1415265351533890, - -0.08457186818122864 - ], - "y" : - [ - -0.1143886670470238, - -0.1143886670470238, - -0.05680520832538605, - -0.05680520832538605 + -0.09294360131025314, + 0.1277720034122467, + 0.1277720034122467, + -0.09294360131025314 ] } } ], - "id" : 18, + "id" : 6, "linearVelocity" : 0, - "massData-I" : 0.0005858240183442831, + "massData-I" : 0.0001869037223514169, "massData-center" : { - "x" : 0.006215983536094427, - "y" : -0.002008607611060143 + "x" : 0, + "y" : 0.01741420105099678 }, - "massData-mass" : 0.05964682996273041, - "name" : "lowerLegLeft", + "massData-mass" : 0.03107454814016819, + "name" : "lowerLeftLeg", "position" : { - "x" : -0.08319067955017090, - "y" : 0.1298431605100632 + "x" : -0.03829947486519814, + "y" : 0.09799569845199585 }, "type" : "dynamic" }, @@ -477,7 +413,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 25, + "id" : 9, "name" : "fixture3", "shapes" : [ @@ -490,35 +426,35 @@ { "x" : [ - -0.05748690664768219, - 0.05748683214187622, - 0.05748683214187622, - -0.05748690664768219 + 0.04693000018596649, + 0.04693000018596649, + -0.04693005979061127, + -0.04693005979061127 ], "y" : [ - -0.1419981122016907, - -0.1419981122016907, - 0.1419981718063354, - 0.1419981718063354 + -0.1159216761589050, + 0.1159217953681946, + 0.1159217953681946, + -0.1159216761589050 ] } } ], - "id" : 19, + "id" : 7, "linearVelocity" : 0, - "massData-I" : 0.0002554289239924401, + "massData-I" : 0.0001134485355578363, "massData-center" : { - "x" : -3.725290298461914e-08, - "y" : 2.980232238769531e-08 + "x" : -2.980232238769531e-08, + "y" : 5.960464477539062e-08 }, - "massData-mass" : 0.03265211358666420, - "name" : "lowerArmRight", + "massData-mass" : 0.02176084183156490, + "name" : "lowerRightArm", "position" : { - "x" : 0.1177217364311218, - "y" : 0.8479318022727966 + "x" : 0.1183081120252609, + "y" : 0.6842151284217834 }, "type" : "dynamic" }, @@ -534,58 +470,37 @@ "density" : 0.2204959988594055, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 26, + "id" : 10, "name" : "fixture1", "shapes" : [ { - "radius" : 0.2746430933475494, + "radius" : 0.2268356680870056, "type" : "circle" } ], "vertices" : { - "x" : [ -0.007499951869249344 ], - "y" : [ 0.003749847412109375 ] - } - }, - - { - "density" : 0.2204959988594055, - "filter-groupIndex" : -55, - "friction" : 0.2, - "id" : 27, - "name" : "fixture1", - "shapes" : - [ - - { - "radius" : 0.2485582530498505, - "type" : "circle" - } - ], - "vertices" : - { - "x" : [ -0.03327952325344086 ], - "y" : [ -0.1384725570678711 ] + "x" : [ -0.01561669446527958 ], + "y" : [ 0.004700659774243832 ] } } ], - "id" : 20, + "id" : 8, "linearVelocity" : 0, - "massData-I" : 0.004164268728345633, + "massData-I" : 0.0009264730615541339, "massData-center" : { - "x" : -0.01910765282809734, - "y" : -0.06028826907277107 + "x" : -0.01561669446527958, + "y" : 0.004700659774243832 }, - "massData-mass" : 0.09504657238721848, + "massData-mass" : 0.03564291819930077, "name" : "head", "position" : { - "x" : 0.04257059469819069, - "y" : 1.812389135360718 + "x" : 0.02309736609458923, + "y" : 1.497289657592773 }, "type" : "dynamic" }, @@ -601,43 +516,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 28, - "name" : "fixture0", - "shapes" : - [ - - { - "type" : "polygon" - } - ], - "vertices" : - { - "x" : - [ - -0.1718577295541763, - 0.001460619270801544, - 0.1718577891588211, - 0.1684816330671310, - 0.001688212156295776, - -0.1718577295541763 - ], - "y" : - [ - -0.4204435348510742, - -0.4519201517105103, - -0.3928470611572266, - 0.4921868443489075, - 0.4921868443489075, - 0.3841522336006165 - ] - } - }, - - { - "density" : 1, - "filter-groupIndex" : -55, - "friction" : 0.2, - "id" : 29, + "id" : 11, "name" : "fixture2", "shapes" : [ @@ -650,35 +529,35 @@ { "x" : [ - -0.004204027354717255, - 0.1679489463567734, - 0.1679489463567734, - -0.004204027354717255 + -0.1360991001129150, + 0.1366278976202011, + 0.1366278976202011, + -0.1360991001129150 ], "y" : [ - 0.4449140429496765, - 0.4449140429496765, - 0.6170670390129089, - 0.6170670390129089 + -0.3788780868053436, + -0.3788780868053436, + 0.3830279111862183, + 0.3830279111862183 ] } } ], - "id" : 21, + "id" : 9, "linearVelocity" : 0, - "massData-I" : 0.03228222951292992, + "massData-I" : 0.01134084537625313, "massData-center" : { - "x" : 0.008858840912580490, - "y" : 0.06282533705234528 + "x" : 0.0002643987536430359, + "y" : 0.002074912190437317 }, - "massData-mass" : 0.3355117142200470, + "massData-mass" : 0.2077923566102982, "name" : "chest", "position" : { - "x" : -0.05338868126273155, - "y" : 0.9620395302772522 + "x" : 0.0007875636219978333, + "y" : 0.7995355725288391 }, "type" : "dynamic" }, @@ -694,7 +573,7 @@ "density" : 1, "filter-groupIndex" : -55, "friction" : 0.2, - "id" : 30, + "id" : 14, "name" : "fixture3", "shapes" : [ @@ -707,318 +586,137 @@ { "x" : [ - -0.05748683214187622, - 0.05748672783374786, - 0.05748672783374786, - -0.05748683214187622 + 0.04692989960312843, + 0.04692989960312843, + -0.04693000018596649, + -0.04693000018596649 ], "y" : [ - -0.2322469353675842, - -0.2322469353675842, - 0.2322462797164917, - 0.2322462797164917 + -0.1895969957113266, + 0.1895969957113266, + 0.1895969957113266, + -0.1895969957113266 ] } } ], - "id" : 22, + "id" : 10, "linearVelocity" : 0, - "massData-I" : 0.001019014045596123, + "massData-I" : 0.0004525936674326658, "massData-center" : { - "x" : -5.215406062575312e-08, - "y" : -3.278255462646484e-07 + "x" : -5.029141902923584e-08, + "y" : 0 }, - "massData-mass" : 0.05340443924069405, - "name" : "upperArmLeft", + "massData-mass" : 0.03559110686182976, + "name" : "upperLeftArm", "position" : { - "x" : -0.1699507087469101, - "y" : 1.113796472549438 + "x" : -0.1165359988808632, + "y" : 0.9012569785118103 }, "type" : "dynamic" } ], - "metaimage" : - [ - - { - "aspectScale" : 1, - "body" : 21, - "center" : - { - "x" : -0.0008481591939926147, - "y" : -0.001265347003936768 - }, - "file" : "../../img/Characters/Chuck/chest.png", - "filter" : 0, - "flip" : false, - "id" : 15, - "name" : "image2", - "opacity" : 1, - "renderOrder" : 5, - "scale" : 0.9578933119773865 - }, - - { - "aspectScale" : 1, - "body" : 18, - "center" : - { - "x" : 0.02851789817214012, - "y" : -0.0009155124425888062 - }, - "file" : "../../img/Characters/Chuck/lowerLeftLeg.png", - "filter" : 0, - "flip" : false, - "id" : 16, - "name" : "image5", - "opacity" : 1, - "renderOrder" : 6, - "scale" : 0.2289662957191467 - }, - - { - "aspectScale" : 1, - "body" : 20, - "center" : - { - "x" : 0.01975236460566521, - "y" : -0.07194232940673828 - }, - "file" : "../../img/Characters/Chuck/head.png", - "filter" : 0, - "flip" : false, - "id" : 17, - "name" : "image1", - "opacity" : 1, - "renderOrder" : 6, - "scale" : 0.6904553174972534 - }, - - { - "aspectScale" : 1, - "body" : 22, - "center" : - { - "x" : 0.002138927578926086, - "y" : 0.0006600618362426758 - }, - "file" : "../../img/Characters/Chuck/upperLeftArm.png", - "filter" : 0, - "flip" : false, - "id" : 18, - "name" : "image4", - "opacity" : 1, - "renderOrder" : 8, - "scale" : 0.4693785011768341 - }, - - { - "aspectScale" : 1, - "body" : 16, - "center" : - { - "x" : 0.0003027096390724182, - "y" : 0.0006600618362426758 - }, - "file" : "../../img/Characters/Chuck/upperLeftArm.png", - "filter" : 0, - "flip" : false, - "id" : 19, - "name" : "image4", - "opacity" : 1, - "renderOrder" : 1, - "scale" : 0.4693785011768341 - }, - - { - "aspectScale" : 1, - "body" : 17, - "center" : - { - "x" : 0.002538725733757019, - "y" : 0.001779437065124512 - }, - "file" : "../../img/Characters/Chuck/lowerLeftArm.png", - "filter" : 0, - "flip" : false, - "id" : 20, - "name" : "image3", - "opacity" : 1, - "renderOrder" : 8, - "scale" : 0.2833150625228882 - }, - - { - "aspectScale" : 1, - "body" : 19, - "center" : - { - "x" : 0.0007003694772720337, - "y" : 0.001779437065124512 - }, - "file" : "../../img/Characters/Chuck/lowerLeftArm.png", - "filter" : 0, - "flip" : false, - "id" : 21, - "name" : "image3", - "opacity" : 1, - "renderOrder" : 1, - "scale" : 0.2833150625228882 - }, - - { - "aspectScale" : 1, - "body" : 14, - "center" : - { - "x" : 0.003173574805259705, - "y" : -0.001172244548797607 - }, - "file" : "../../img/Characters/Chuck/upperLeftLeg.png", - "filter" : 0, - "flip" : false, - "id" : 22, - "name" : "image6", - "opacity" : 1, - "renderOrder" : 6, - "scale" : 0.4627031385898590 - }, - - { - "aspectScale" : 1, - "body" : 15, - "center" : - { - "x" : -0.02732392773032188, - "y" : 0.02671334147453308 - }, - "file" : "../../img/Characters/Chuck/upperRightLeg.png", - "filter" : 0, - "flip" : false, - "id" : 23, - "name" : "image6", - "opacity" : 1, - "scale" : 0.5183231234550476 - }, - - { - "aspectScale" : 1, - "body" : 13, - "center" : - { - "x" : 0.02911517955362797, - "y" : -0.0009155124425888062 - }, - "file" : "../../img/Characters/Chuck/lowerLeftLeg.png", - "filter" : 0, - "flip" : false, - "id" : 24, - "name" : "image5", - "opacity" : 1, - "scale" : 0.2289662957191467 - } - ], "metajoint" : [ { "anchorA" : { - "x" : 0.07454992830753326, - "y" : 0.5068107843399048 + "x" : -0.0007802959880791605, + "y" : -0.1484909951686859 }, "anchorB" : { - "x" : -0.02141102217137814, - "y" : -0.3435407876968384 + "x" : -0.0007801019819453359, + "y" : 0.08614099770784378 }, - "bodyA" : 21, - "bodyB" : 20, + "bodyA" : 2, + "bodyB" : 6, "collideConnected" : false, "enableLimit" : true, "enableMotor" : false, - "id" : 10, - "lowerLimit" : -1.221730470657349, + "id" : 1, + "lowerLimit" : 0.01745329238474369, "maxMotorTorque" : 1, "motorSpeed" : 0, - "name" : "joint0", + "name" : "joint7", "referenceAngle" : 0, "type" : "revolute", - "upperLimit" : 0.6981316804885864 + "upperLimit" : 2.443460941314697 }, { "anchorA" : { - "x" : 0.002425247803330421, - "y" : -0.1845821887254715 + "x" : -0.004979589954018593, + "y" : -0.1506859958171844 }, "anchorB" : { - "x" : 0.002425374463200569, - "y" : 0.1026860624551773 + "x" : -0.005973690189421177, + "y" : 0.08482310175895691 }, - "bodyA" : 15, - "bodyB" : 13, + "bodyA" : 3, + "bodyB" : 1, "collideConnected" : false, "enableLimit" : true, "enableMotor" : false, - "id" : 11, - "lowerLimit" : -2.268928050994873, + "id" : 2, + "lowerLimit" : 0.01745329238474369, "maxMotorTorque" : 1, "motorSpeed" : 0, "name" : "joint8", "referenceAngle" : 0, "type" : "revolute", - "upperLimit" : 0 + "upperLimit" : 2.443460941314697 }, { "anchorA" : { - "x" : -0.1165831685066223, - "y" : 0.3330365419387817 + "x" : 0.0003538504242897034, + "y" : 0.07107692956924438 }, "anchorB" : { - "x" : -2.135336399078369e-05, - "y" : 0.1812803745269775 + "x" : 0.0003536640142556280, + "y" : -0.1459649950265884 }, - "bodyA" : 21, - "bodyB" : 22, + "bodyA" : 7, + "bodyB" : 4, "collideConnected" : false, - "enableLimit" : false, + "enableLimit" : true, "enableMotor" : false, - "id" : 12, - "lowerLimit" : -2.268928050994873, + "id" : 3, + "lowerLimit" : 0.01745329238474369, "maxMotorTorque" : 1, "motorSpeed" : 0, - "name" : "joint3", + "name" : "joint1", "referenceAngle" : 0, "type" : "revolute", - "upperLimit" : 3.141592741012573 + "upperLimit" : 1.919862151145935 }, { "anchorA" : { - "x" : 0.1710196435451508, - "y" : 0.3308988809585571 + "x" : 0.1179294362664223, + "y" : 0.2464744448661804 }, "anchorB" : { - "x" : -9.131431579589844e-05, - "y" : 0.1791421175003052 + "x" : 0.0004089996218681335, + "y" : 0.1447530388832092 }, - "bodyA" : 21, - "bodyB" : 16, + "bodyA" : 9, + "bodyB" : 4, "collideConnected" : false, "enableLimit" : false, "enableMotor" : false, - "id" : 13, + "id" : 4, "lowerLimit" : -2.268928050994873, "maxMotorTorque" : 1, "motorSpeed" : 0, @@ -1031,131 +729,131 @@ { "anchorA" : { - "x" : 0.0004334598779678345, - "y" : 0.08706557750701904 + "x" : -0.06799955666065216, + "y" : -0.3021813035011292 }, "anchorB" : { - "x" : 0.0004332214593887329, - "y" : -0.1787990331649780 + "x" : -0.02891255542635918, + "y" : 0.1648437976837158 }, - "bodyA" : 19, - "bodyB" : 16, + "bodyA" : 9, + "bodyB" : 2, "collideConnected" : false, "enableLimit" : true, "enableMotor" : false, - "id" : 14, - "lowerLimit" : 0, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint1", - "referenceAngle" : 0, - "type" : "revolute", - "upperLimit" : 1.919862151145935 - }, - - { - "anchorA" : - { - "x" : 0.1367489844560623, - "y" : -0.3606387376785278 - }, - "anchorB" : - { - "x" : 0.05056379735469818, - "y" : 0.1842886805534363 - }, - "bodyA" : 21, - "bodyB" : 15, - "collideConnected" : false, - "enableLimit" : true, - "enableMotor" : false, - "id" : 15, - "lowerLimit" : -0.6981316804885864, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint5", - "referenceAngle" : 0, - "type" : "revolute", - "upperLimit" : 1.919862151145935 - }, - - { - "anchorA" : - { - "x" : 0.001047849655151367, - "y" : -0.1790249347686768 - }, - "anchorB" : - { - "x" : 0.001048207283020020, - "y" : 0.08683943748474121 - }, - "bodyA" : 22, - "bodyB" : 17, - "collideConnected" : false, - "enableLimit" : true, - "enableMotor" : false, - "id" : 16, - "lowerLimit" : 0, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint4", - "referenceAngle" : 0, - "type" : "revolute", - "upperLimit" : 1.919862151145935 - }, - - { - "anchorA" : - { - "x" : -0.0009558200836181641, - "y" : -0.1818936169147491 - }, - "anchorB" : - { - "x" : -0.0009555891156196594, - "y" : 0.1055182516574860 - }, - "bodyA" : 14, - "bodyB" : 18, - "collideConnected" : false, - "enableLimit" : true, - "enableMotor" : false, - "id" : 17, - "lowerLimit" : -2.268928050994873, - "maxMotorTorque" : 1, - "motorSpeed" : 0, - "name" : "joint7", - "referenceAngle" : 0, - "type" : "revolute", - "upperLimit" : 0 - }, - - { - "anchorA" : - { - "x" : -0.08329562842845917, - "y" : -0.3541148304939270 - }, - "anchorB" : - { - "x" : -0.0550386831164360, - "y" : 0.1909851431846619 - }, - "bodyA" : 21, - "bodyB" : 14, - "collideConnected" : false, - "enableLimit" : true, - "enableMotor" : false, - "id" : 18, - "lowerLimit" : -0.6981316804885864, + "id" : 5, + "lowerLimit" : -0.7853981852531433, "maxMotorTorque" : 1, "motorSpeed" : 0, "name" : "joint6", "referenceAngle" : 0, "type" : "revolute", - "upperLimit" : 1.919862151145935 + "upperLimit" : 1.570796370506287 + }, + + { + "anchorA" : + { + "x" : 0.06260262429714203, + "y" : -0.3029872477054596 + }, + "anchorB" : + { + "x" : 0.02294230461120605, + "y" : 0.1640380322933197 + }, + "bodyA" : 9, + "bodyB" : 3, + "collideConnected" : false, + "enableLimit" : true, + "enableMotor" : false, + "id" : 6, + "lowerLimit" : -0.7853981852531433, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint5", + "referenceAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.570796370506287 + }, + + { + "anchorA" : + { + "x" : -0.1188285648822784, + "y" : 0.2521644234657288 + }, + "anchorB" : + { + "x" : -0.001505002379417419, + "y" : 0.1504430174827576 + }, + "bodyA" : 9, + "bodyB" : 10, + "collideConnected" : false, + "enableLimit" : false, + "enableMotor" : false, + "id" : 8, + "lowerLimit" : -2.268928050994873, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint3", + "referenceAngle" : 0, + "type" : "revolute", + "upperLimit" : 3.141592741012573 + }, + + { + "anchorA" : + { + "x" : 0.0008554459782317281, + "y" : -0.1461489945650101 + }, + "anchorB" : + { + "x" : 0.0008557140827178955, + "y" : 0.07089227437973022 + }, + "bodyA" : 10, + "bodyB" : 5, + "collideConnected" : false, + "enableLimit" : true, + "enableMotor" : false, + "id" : 9, + "lowerLimit" : -1.919862151145935, + "maxMotorTorque" : 1, + "motorSpeed" : 0, + "name" : "joint4", + "referenceAngle" : 0, + "type" : "revolute", + "upperLimit" : 0.01745329238474369 + }, + + { + "anchorA" : + { + "x" : 0.02210754156112671, + "y" : 0.4607425630092621 + }, + "anchorB" : + { + "x" : -0.02544101513922215, + "y" : -0.2591779530048370 + }, + "bodyA" : 9, + "bodyB" : 8, + "collideConnected" : false, + "enableLimit" : true, + "enableMotor" : false, + "id" : 29, + "lowerLimit" : -0.6981316804885864, + "maxMotorTorque" : 0, + "motorSpeed" : 0, + "name" : "joint9", + "referenceAngle" : 0, + "type" : "revolute", + "upperLimit" : 1.221730470657349 } ], "positionIterations" : 3, diff --git a/static/js/menu.js b/static/js/menu.js deleted file mode 100644 index 600a41b..0000000 --- a/static/js/menu.js +++ /dev/null @@ -1,276 +0,0 @@ -function $(selector) { - return document.querySelector(selector); -} - -function $$(selector) { - return document.querySelectorAll(selector); -} - -if(localStorage["player"]) { - var player = JSON.parse(localStorage["player"]); - if(player.nickname) { - $("#nick").value = player.nickname; - } -} - -if(localStorage["customname"]) { - $("#customname").value = localStorage["customname"]; -} - -var lastRefreshResponse; -function refresh(callback) { - try { - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if(xhr.readyState == 4) { - if(xhr.status == 200) { - - var response = xhr.responseText; - if(response != lastRefreshResponse) { - lastRefreshResponse = response; - populate(JSON.parse(response).success); - } - document.body.className = ""; - - if(typeof callback == 'function') { - callback(JSON.parse(response).success) - } - } else { - console.error("Ajax error: " + xhr.status + " " + xhr.responseText) - $("#list").innerHTML = ""; - document.body.className = "offline"; - } - } - } - xhr.open("POST", "/api", true); - xhr.send(JSON.stringify({command:"getChannels"})); - } catch(e) { - console.error(e) - } - return false; -} - -function populate(list) { - var html = ""; - if(list.length > 0) { - for (var i = 0; i < list.length; i++) { - var channel = list[i]; - html += "