├── .gitignore ├── AStar └── AStar.js ├── CSS3Filters └── CSS3Filters.js ├── ColorHarmony └── ColorHarmony.js ├── Juicy └── Juicy.js ├── KineticScrolling └── KineticScrolling.js ├── ProTracker └── ProTracker.js ├── README.md ├── SamplePlugin └── SamplePlugin.js ├── SaveCPU └── SaveCPU.js ├── ScreenShake └── ScreenShake.js ├── TilemapWalker └── TilemapWalker.js ├── VirtualJoystick └── VirtualJoystick.js ├── Webcam └── Webcam.js └── YM └── YM.js /.gitignore: -------------------------------------------------------------------------------- 1 | # System and IDE files 2 | Thumbs.db 3 | .DS_Store 4 | .idea 5 | *.suo 6 | *.sublime-project 7 | *.sublime-workspace 8 | 9 | # Vendors 10 | node_modules/ 11 | 12 | # Build 13 | dist/ 14 | -------------------------------------------------------------------------------- /AStar/AStar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | 4 | * Copyright (c) 2014 Raphaël Roux 5 | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | * 25 | * 26 | */ 27 | 28 | /** 29 | * @author Raphaël Roux 30 | * @copyright 2014 Raphaël Roux 31 | * @license {@link http://opensource.org/licenses/MIT} 32 | */ 33 | 34 | /** 35 | * AStar is a phaser pathfinding plugin based on an A* kind of algorythm 36 | * It works with the Phaser.Tilemap 37 | * 38 | * @class Phaser.Plugin.AStar 39 | * @constructor 40 | * @param {Any} parent - The object that owns this plugin, usually Phaser.PluginManager. 41 | */ 42 | Phaser.Plugin.AStar = function (parent) 43 | { 44 | 45 | /** 46 | * @property {Any} parent - The parent of this plugin. If added to the PluginManager the parent will be set to that, otherwise it will be null. 47 | */ 48 | this.parent = parent; 49 | 50 | /** 51 | * @property {Phaser.Tilemap} _tilemap - A reference to the tilemap used to store astar nodes according to the Phaser.Tilemap structure. 52 | */ 53 | this._tilemap; 54 | 55 | /** 56 | * @property {number} _layerIndex - The layer index of the tilemap that is used to store astar nodes. 57 | */ 58 | this._layerIndex; 59 | 60 | /** 61 | * @property {number} _tilesetIndex - The tileset index of the tileset that handle tiles properties. 62 | */ 63 | this._tilesetIndex; 64 | 65 | /** 66 | * @property {array} _open - An array that references nodes to be considered by the search path algorythm. 67 | */ 68 | this._open; 69 | 70 | /** 71 | * @property {array} _closed - An array that references nodes not to consider anymore. 72 | */ 73 | this._closed; 74 | 75 | /** 76 | * @property {array} _visited - Internal array of visited tiles, use for debug pupose. 77 | */ 78 | this._visited; 79 | 80 | /** 81 | * @property {boolean} _useDiagonal - Does the astar algorythm can use tile diagonal? 82 | * @default true 83 | */ 84 | this._useDiagonal = true; 85 | 86 | /** 87 | * @property {boolean} _findClosest - Does the findPath algorythm must calculate the closest result if destination is unreachable. If not findPath will return an empty array 88 | * @default true 89 | */ 90 | this._findClosest = true; 91 | 92 | /** 93 | * @property {string} _walkablePropName - Wich name have the walkable propertiy in your tileset. 94 | * @default 'walkable' 95 | */ 96 | this._walkablePropName = 'walkable'; 97 | 98 | /** 99 | * @property {function} _distanceFunction - The function used to calculate distance. 100 | */ 101 | this._distanceFunction = Phaser.Plugin.AStar.DISTANCE_EUCLIDIAN; 102 | 103 | /** 104 | * @property {Phaser.Plugin.AStar.AStarPath} _lastPath - The last path calculated by astar. 105 | */ 106 | this._lastPath = null; 107 | 108 | /** 109 | * @property {boolean} _debug - Boolean to debug mode, stores visited nodes, and have a cost. Disable in production. 110 | * @default false 111 | */ 112 | this._debug = true; 113 | }; 114 | 115 | Phaser.Plugin.AStar.prototype = Object.create(Phaser.Plugin.prototype); 116 | Phaser.Plugin.AStar.prototype.constructor = Phaser.Plugin.AStar; 117 | 118 | 119 | Phaser.Plugin.AStar.VERSION = '0.0.101'; 120 | Phaser.Plugin.AStar.COST_ORTHOGONAL = 1; 121 | Phaser.Plugin.AStar.COST_DIAGONAL = Phaser.Plugin.AStar.COST_ORTHOGONAL*Math.sqrt(2); 122 | Phaser.Plugin.AStar.DISTANCE_MANHATTAN = 'distManhattan'; 123 | Phaser.Plugin.AStar.DISTANCE_EUCLIDIAN = 'distEuclidian'; 124 | 125 | /** 126 | * Sets the Phaser.Tilemap used to searchPath into. 127 | * @method Phaser.Plugin.AStar#setAStarMap 128 | * @public 129 | * @param {Phaser.Tilemap} map - the Phaser.Tilemap used to searchPath into. It must have a tileset with tile porperties to know if tiles are walkable or not. 130 | * @param {string} layerName - The name of the layer that handle tiles. 131 | * @param {string} tilesetName - The name of the tileset that have walkable properties. 132 | * @return {Phaser.Plugin.AStar} The Phaser.Plugin.AStar itself. 133 | */ 134 | Phaser.Plugin.AStar.prototype.setAStarMap = function(map, layerName, tilesetName) 135 | { 136 | this._tilemap = map; 137 | this._layerIndex = this._tilemap.getLayerIndex(layerName);; 138 | this._tilesetIndex = this._tilemap.getTilesetIndex(tilesetName); 139 | 140 | this.updateMap(); 141 | 142 | return this; 143 | }; 144 | 145 | 146 | /** 147 | * Sets the Phaser.Tilemap used to searchPath into. 148 | * @method Phaser.Plugin.AStar-setAStarMap 149 | * @private 150 | * @return {void} The Phaser.Plugin.AStar itself. 151 | */ 152 | Phaser.Plugin.AStar.prototype.updateMap = function() 153 | { 154 | var tile; 155 | var walkable; 156 | 157 | //for each tile, add a default AStarNode with x, y and walkable properties according to the tilemap/tileset datas 158 | for(var y=0; y < this._tilemap.height; y++) 159 | { 160 | for(var x=0; x < this._tilemap.width; x++) 161 | { 162 | tile = this._tilemap.layers[this._layerIndex].data[y][x]; 163 | walkable = this._tilemap.tilesets[this._tilesetIndex].tileProperties[tile.index - 1][this._walkablePropName] !== "false" ? true : false; 164 | tile.properties.astarNode = new Phaser.Plugin.AStar.AStarNode(x, y, walkable); 165 | } 166 | } 167 | 168 | }; 169 | 170 | 171 | /** 172 | * Find a path between to tiles coordinates 173 | * @method Phaser.Plugin.AStar#findPath 174 | * @public 175 | * @param {Phaser.Point} startPoint - The start point x, y in tiles coordinates to search a path. 176 | * @param {Phaser.Point} goalPoint - The goal point x, y in tiles coordinates that you trying to reach. 177 | * @return {Phaser.Plugin.AStar.AStarPath} The Phaser.Plugin.AStar.AStarPath that results 178 | */ 179 | Phaser.Plugin.AStar.prototype.findPath = function(startPoint, goalPoint) 180 | { 181 | var path = new Phaser.Plugin.AStar.AStarPath(); 182 | 183 | var start = this._tilemap.layers[this._layerIndex].data[startPoint.y][startPoint.x].properties.astarNode; //:AStarNode; 184 | var goal = this._tilemap.layers[this._layerIndex].data[goalPoint.y][goalPoint.x].properties.astarNode 185 | 186 | path.start = start; 187 | path.goal = goal; 188 | 189 | this._open = []; 190 | this._closed = []; 191 | this._visited = []; 192 | 193 | this._open.push(start); 194 | 195 | start.g = 0; 196 | start.h = this[this._distanceFunction](start, goal); 197 | start.f = start.h; 198 | start.parent = null; 199 | 200 | //Loop until there are no more nodes to search 201 | while(this._open.length > 0) 202 | { 203 | //Find lowest f in this._open 204 | var f = Infinity; 205 | var x; 206 | for (var i=0; i 0) { 348 | 349 | n = map[y][x-1].properties.astarNode; 350 | if (n.walkable) { 351 | n.travelCost = Phaser.Plugin.AStar.COST_ORTHOGONAL; 352 | neighbors.push(n); 353 | } 354 | } 355 | //East 356 | if (x < this._tilemap.width-1) { 357 | n = map[y][x+1].properties.astarNode; 358 | if (n.walkable) { 359 | n.travelCost = Phaser.Plugin.AStar.COST_ORTHOGONAL; 360 | neighbors.push(n); 361 | } 362 | } 363 | //North 364 | if (y > 0) { 365 | n = map[y-1][x].properties.astarNode; 366 | if (n.walkable) { 367 | n.travelCost = Phaser.Plugin.AStar.COST_ORTHOGONAL; 368 | neighbors.push(n); 369 | } 370 | } 371 | //South 372 | if (y < this._tilemap.height-1) { 373 | n = map[y+1][x].properties.astarNode; 374 | if (n.walkable) { 375 | n.travelCost = Phaser.Plugin.AStar.COST_ORTHOGONAL; 376 | neighbors.push(n); 377 | } 378 | } 379 | 380 | //If diagonals aren't used do not search for other neighbors and return orthogonal search result 381 | if(this._useDiagonal === false) 382 | return neighbors; 383 | 384 | //NorthWest 385 | if (x > 0 && y > 0) { 386 | n = map[y-1][x-1].properties.astarNode; 387 | if (n.walkable 388 | && map[y][x-1].properties.astarNode.walkable 389 | && map[y-1][x].properties.astarNode.walkable 390 | ) { 391 | n.travelCost = Phaser.Plugin.AStar.COST_DIAGONAL; 392 | neighbors.push(n); 393 | } 394 | } 395 | //NorthEast 396 | if (x < this._tilemap.width-1 && y > 0) { 397 | n = map[y-1][x+1].properties.astarNode; 398 | if (n.walkable 399 | && map[y][x+1].properties.astarNode.walkable 400 | && map[y-1][x].properties.astarNode.walkable 401 | ) { 402 | n.travelCost = Phaser.Plugin.AStar.COST_DIAGONAL; 403 | neighbors.push(n); 404 | } 405 | } 406 | //SouthWest 407 | if (x > 0 && y < this._tilemap.height-1) { 408 | n = map[y+1][x-1].properties.astarNode; 409 | if (n.walkable 410 | && map[y][x-1].properties.astarNode.walkable 411 | && map[y+1][x].properties.astarNode.walkable 412 | ) { 413 | n.travelCost = Phaser.Plugin.AStar.COST_DIAGONAL; 414 | neighbors.push(n); 415 | } 416 | } 417 | //SouthEast 418 | if (x < this._tilemap.width-1 && y < this._tilemap.height-1) { 419 | n = map[y+1][x+1].properties.astarNode; 420 | if (n.walkable 421 | && map[y][x+1].properties.astarNode.walkable 422 | && map[y+1][x].properties.astarNode.walkable 423 | ) { 424 | n.travelCost = Phaser.Plugin.AStar.COST_DIAGONAL; 425 | neighbors.push(n); 426 | } 427 | } 428 | 429 | return neighbors; 430 | }; 431 | 432 | 433 | /** 434 | * Calculate a distance between tow astar nodes coordinates according to the Manhattan method 435 | * @method Phaser.Plugin.AStar-distManhattan 436 | * @private 437 | * @param {Phaser.Plugin.AStar.AStarNode} nodeA - The A node. 438 | * @param {Phaser.Plugin.AStar.AStarNode} nodeB - The B node. 439 | * @return {number} The distance between nodeA and nodeB 440 | */ 441 | Phaser.Plugin.AStar.prototype.distManhattan = function (nodeA, nodeB) 442 | { 443 | return Math.abs(nodeA.x - nodeB.x) + Math.abs(nodeA.y - nodeB.y); 444 | }; 445 | 446 | /** 447 | * Calculate a distance between tow astar nodes coordinates according to the Euclidian method. More accurate 448 | * @method Phaser.Plugin.AStar-distEuclidian 449 | * @private 450 | * @param {Phaser.Plugin.AStar.AStarNode} nodeA - The A node. 451 | * @param {Phaser.Plugin.AStar.AStarNode} nodeB - The B node. 452 | * @return {number} The distance between nodeA and nodeB 453 | */ 454 | Phaser.Plugin.AStar.prototype.distEuclidian = function(nodeA, nodeB) 455 | { 456 | return Math.sqrt(Math.pow((nodeA.x - nodeB.x), 2) + Math.pow((nodeA.y -nodeB.y), 2)); 457 | }; 458 | 459 | 460 | /** 461 | * Tells if a tile is walkable from its tilemap coordinates 462 | * @method Phaser.Plugin.AStar-isWalkable 463 | * @public 464 | * @param {number} x - The x coordiante of the tile in tilemap's coordinate. 465 | * @param {number} y - The y coordinate of the tile in tilemap's coordinate. 466 | * @return {boolean} The distance between nodeA and nodeB 467 | */ 468 | Phaser.Plugin.AStar.prototype.isWalkable = function(x, y) 469 | { 470 | return this._tilemap.layers[this._layerIndex].data[y][x].properties.astarNode.walkable; 471 | }; 472 | 473 | 474 | /** 475 | * @properties {string} version - The version number of Phaser.Plugin.AStar read only 476 | */ 477 | Object.defineProperty(Phaser.Plugin.AStar.prototype, "version", { 478 | 479 | get: function () { 480 | return Phaser.Plugin.AStar.VERSION; 481 | } 482 | 483 | }); 484 | 485 | 486 | /** 487 | * AStarNode is an object that stores AStar value. Each tile have an AStarNode in their properties 488 | * @class Phaser.Plugin.AStar.AStarNode 489 | * @constructor 490 | * @param {number} x - The x coordinate of the tile. 491 | * @param {number} y - The y coordinate of the tile. 492 | * @param {boolean} isWalkable - Is this tile is walkable? 493 | */ 494 | Phaser.Plugin.AStar.AStarNode = function(x, y, isWalkable) 495 | { 496 | 497 | /** 498 | * @property {number} x - The x coordinate of the tile. 499 | */ 500 | this.x = x; 501 | 502 | /** 503 | * @property {number} y - The y coordinate of the tile. 504 | */ 505 | this.y = y; 506 | 507 | /** 508 | * @property {number} g - The total travel cost from the start point. Sum of COST_ORTHOGONAL and COST_DIAGONAL 509 | */ 510 | this.g = 0; 511 | 512 | /** 513 | * @property {number} h - The remaing distance as the crow flies between this node and the goal. 514 | */ 515 | this.h = 0; 516 | 517 | /** 518 | * @property {number} f - The weight. Sum of g + h. 519 | */ 520 | this.f = 0; 521 | 522 | /** 523 | * @property {Phaser.Plugin.AStar.AStarNode} parent - Where do we come from? It's an AStarNode reference needed to reconstruct a path backwards (from goal to start point) 524 | */ 525 | this.parent; 526 | 527 | /** 528 | * @property {boolean} walkable - Is this node is walkable? 529 | */ 530 | this.walkable = isWalkable; 531 | 532 | /** 533 | * @property {number} travelCost - The cost to travel to this node, COST_ORTHOGONAL or COST_DIAGONAL 534 | */ 535 | this.travelCost; 536 | }; 537 | 538 | 539 | /** 540 | * AStarPath is an object that stores a searchPath result. 541 | * @class Phaser.Plugin.AStar.AStarPath 542 | * @constructor 543 | * @param {array} nodes - An array of nodes coordinates sorted backward from goal to start point. 544 | * @param {Phaser.Plugin.AStarNode} start - The start AStarNode used for the searchPath. 545 | * @param {Phaser.Plugin.AStarNode} goal - The goal AStarNode used for the searchPath. 546 | */ 547 | Phaser.Plugin.AStar.AStarPath = function(nodes, start, goal) 548 | { 549 | /** 550 | * @property {array} nodes - Array of AstarNodes x, y coordiantes that are the path solution from goal to start point. 551 | */ 552 | this.nodes = nodes || []; 553 | 554 | /** 555 | * @property {Phaser.Plugin.Astar.AStarNode} start - Reference to the start point used by findPath. 556 | */ 557 | this.start = start || null; 558 | 559 | /** 560 | * @property {Phaser.Plugin.Astar.AStarNode} goal - Reference to the goal point used by findPath. 561 | */ 562 | this.goal = goal || null; 563 | 564 | /** 565 | * @property {array} visited - Array of AStarNodes that the findPath algorythm has visited. Used for debug only. 566 | */ 567 | this.visited = []; 568 | }; 569 | 570 | 571 | /** 572 | * Debug method to draw the last calculated path by AStar 573 | * @method Phaser.Utils.Debug.AStar 574 | * @param {Phaser.Plugin.AStar} astar- The AStar plugin that you want to debug. 575 | * @param {number} x - X position on camera for debug display. 576 | * @param {number} y - Y position on camera for debug display. 577 | * @param {string} color - Color to stroke the path line. 578 | * @return {void} 579 | */ 580 | Phaser.Utils.Debug.prototype.AStar = function(astar, x, y, color, showVisited) 581 | { 582 | if (this.context == null) 583 | { 584 | return; 585 | } 586 | 587 | var pathLength = 0; 588 | if(astar._lastPath !== null) 589 | { 590 | pathLength = astar._lastPath.nodes.length; 591 | } 592 | 593 | color = color || 'rgb(255,255,255)'; 594 | 595 | game.debug.start(x, y, color); 596 | 597 | 598 | if(pathLength > 0) 599 | { 600 | var node = astar._lastPath.nodes[0]; 601 | this.context.strokeStyle = color; 602 | this.context.beginPath(); 603 | this.context.moveTo((node.x * astar._tilemap.tileWidth) + (astar._tilemap.tileWidth/2) - game.camera.view.x, (node.y * astar._tilemap.tileHeight) + (astar._tilemap.tileHeight/2) - game.camera.view.y); 604 | 605 | for(var i=0; i 7 | * @copyright 2013 Photon Storm Ltd. 8 | * @license https://github.com/photonstorm/phaser/blob/master/license.txt MIT License 9 | */ 10 | 11 | Phaser.Plugins.ColorHarmony.prototype = { 12 | 13 | /** 14 | * Returns a Complementary Color Harmony for the given color. 15 | *

A complementary hue is one directly opposite the color given on the color wheel

16 | *

Value returned in 0xAARRGGBB format with Alpha set to 255.

17 | * 18 | * @method Phaser.Plugin.ColorHarmony#getComplementHarmony 19 | * @param {Number} color The color to base the harmony on. 20 | * @return {Number} 0xAARRGGBB format color value. 21 | */ 22 | getComplementHarmony: function (color) { 23 | 24 | var hsv = Phaser.Color.RGBtoHSV(color); 25 | var opposite = Phaser.Color.game.math.wrapValue(hsv.hue, 180, 359); 26 | return Phaser.Color.HSVtoRGB(opposite, 1.0, 1.0); 27 | 28 | }, 29 | 30 | /** 31 | * Returns an Analogous Color Harmony for the given color. 32 | *

An Analogous harmony are hues adjacent to each other on the color wheel

33 | *

Values returned in 0xAARRGGBB format with Alpha set to 255.

34 | * 35 | * @method Phaser.Plugin.ColorHarmony#getAnalogousHarmony 36 | * @param {Number} color The color to base the harmony on. 37 | * @param {Number} threshold Control how adjacent the colors will be (default +- 30 degrees) 38 | * @return {Object} Object containing 3 properties: color1 (the original color), color2 (the warmer analogous color) and color3 (the colder analogous color) 39 | */ 40 | getAnalogousHarmony: function (color, threshold) { 41 | if (typeof threshold === "undefined") { threshold = 30; } 42 | var hsv = Phaser.Color.RGBtoHSV(color); 43 | if(threshold > 359 || threshold < 0) { 44 | throw new Error("Color Warning: Invalid threshold given to getAnalogousHarmony()"); 45 | } 46 | var warmer = Phaser.Color.game.math.wrapValue(hsv.hue, 359 - threshold, 359); 47 | var colder = Phaser.Color.game.math.wrapValue(hsv.hue, threshold, 359); 48 | return { 49 | color1: color, 50 | color2: Phaser.Color.HSVtoRGB(warmer, 1.0, 1.0), 51 | color3: Phaser.Color.HSVtoRGB(colder, 1.0, 1.0), 52 | hue1: hsv.hue, 53 | hue2: warmer, 54 | hue3: colder 55 | }; 56 | }, 57 | 58 | /** 59 | * Returns an Split Complement Color Harmony for the given color. 60 | *

A Split Complement harmony are the two hues on either side of the color's Complement

61 | *

Values returned in 0xAARRGGBB format with Alpha set to 255.

62 | * 63 | * @method Phaser.Plugin.ColorHarmony#getSplitComplementHarmony 64 | * @param {Number} color The color to base the harmony on 65 | * @param {Number} threshold Control how adjacent the colors will be to the Complement (default +- 30 degrees) 66 | * @return {Object} An object containing 3 properties: color1 (the original color), color2 (the warmer analogous color) and color3 (the colder analogous color) 67 | */ 68 | getSplitComplementHarmony: function (color, threshold) { 69 | if (typeof threshold === "undefined") { threshold = 30; } 70 | var hsv = Phaser.Color.RGBtoHSV(color); 71 | if(threshold >= 359 || threshold <= 0) { 72 | throw new Error("Phaser.Color Warning: Invalid threshold given to getSplitComplementHarmony()"); 73 | } 74 | var opposite = Phaser.Color.game.math.wrapValue(hsv.hue, 180, 359); 75 | var warmer = Phaser.Color.game.math.wrapValue(hsv.hue, opposite - threshold, 359); 76 | var colder = Phaser.Color.game.math.wrapValue(hsv.hue, opposite + threshold, 359); 77 | return { 78 | color1: color, 79 | color2: Phaser.Color.HSVtoRGB(warmer, hsv.saturation, hsv.value), 80 | color3: Phaser.Color.HSVtoRGB(colder, hsv.saturation, hsv.value), 81 | hue1: hsv.hue, 82 | hue2: warmer, 83 | hue3: colder 84 | }; 85 | }, 86 | 87 | /** 88 | * Returns a Triadic Color Harmony for the given color. 89 | *

A Triadic harmony are 3 hues equidistant from each other on the color wheel

90 | *

Values returned in 0xAARRGGBB format with Alpha set to 255.

91 | * 92 | * @method Phaser.Plugin.ColorHarmony#getTriadicHarmony 93 | * @param {Number} color The color to base the harmony on. 94 | * @return {Object} An Object containing 3 properties: color1 (the original color), color2 and color3 (the equidistant colors) 95 | */ 96 | getTriadicHarmony: function (color) { 97 | var hsv = Phaser.Color.RGBtoHSV(color); 98 | var triadic1 = Phaser.Color.game.math.wrapValue(hsv.hue, 120, 359); 99 | var triadic2 = Phaser.Color.game.math.wrapValue(triadic1, 120, 359); 100 | return { 101 | color1: color, 102 | color2: Phaser.Color.HSVtoRGB(triadic1, 1.0, 1.0), 103 | color3: Phaser.Color.HSVtoRGB(triadic2, 1.0, 1.0) 104 | }; 105 | } 106 | 107 | }; 108 | -------------------------------------------------------------------------------- /Juicy/Juicy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | /** 5 | * @author Jeremy Dowell 6 | * @license {@link http://www.wtfpl.net/txt/copying/|WTFPL} 7 | */ 8 | 9 | /** 10 | * Creates a new `Juicy` object. 11 | * 12 | * @class Phaser.Plugin.Juicy 13 | * @constructor 14 | * 15 | * @param {Phaser.Game} game Current game instance. 16 | */ 17 | Phaser.Plugin.Juicy = function (game) { 18 | 19 | Phaser.Plugin.call(this, game); 20 | 21 | /** 22 | * @property {Phaser.Rectangle} _boundsCache - A reference to the current world bounds. 23 | * @private 24 | */ 25 | this._boundsCache = Phaser.Utils.extend(false, {}, this.game.world.bounds); 26 | 27 | /** 28 | * @property {number} _shakeWorldMax - The maximum world shake radius 29 | * @private 30 | */ 31 | this._shakeWorldMax = 20; 32 | 33 | /** 34 | * @property {number} _shakeWorldTime - The maximum world shake time 35 | * @private 36 | */ 37 | this._shakeWorldTime = 0; 38 | 39 | /** 40 | * @property {number} _trailCounter - A count of how many trails we're tracking 41 | * @private 42 | */ 43 | this._trailCounter = 0; 44 | 45 | /** 46 | * @property {object} _overScales - An object containing overscaling configurations 47 | * @private 48 | */ 49 | this._overScales = {}; 50 | 51 | /** 52 | * @property {number} _overScalesCounter - A count of how many overScales we're tracking 53 | * @private 54 | */ 55 | this._overScalesCounter = 0; 56 | }; 57 | 58 | 59 | Phaser.Plugin.Juicy.prototype = Object.create(Phaser.Plugin.prototype); 60 | Phaser.Plugin.Juicy.prototype.constructor = Phaser.Plugin.Juicy; 61 | 62 | 63 | 64 | /** 65 | * Creates a new `Juicy.ScreenFlash` object. 66 | * 67 | * @class Phaser.Plugin.Juicy.ScreenFlash 68 | * @constructor 69 | * 70 | * @param {Phaser.Game} game - Current game instance. 71 | * @param {string} color='white' - The color to flash the screen. 72 | * @memberof Phaser.Plugin.Juicy 73 | */ 74 | Phaser.Plugin.Juicy.ScreenFlash = function(game, color) { 75 | color = color || 'white'; 76 | var bmd = game.add.bitmapData(game.width, game.height); 77 | bmd.ctx.fillStyle = color; 78 | bmd.ctx.fillRect(0,0, game.width, game.height); 79 | 80 | Phaser.Sprite.call(this, game, 0,0, bmd); 81 | this.alpha = 0; 82 | }; 83 | 84 | Phaser.Plugin.Juicy.ScreenFlash.prototype = Object.create(Phaser.Sprite.prototype); 85 | Phaser.Plugin.Juicy.ScreenFlash.prototype.constructor = Phaser.Plugin.Juicy.ScreenFlash; 86 | 87 | 88 | /** 89 | * Flashes the screen 90 | * 91 | * @param {number} [maxAlpha=1] - The maximum alpha to flash the screen to 92 | * @param {number} [duration=100] - The duration of the flash in milliseconds 93 | * @method Phaser.Plugin.Juicy.ScreenFlash#flash 94 | * @memberof Phaser.Plugin.Juicy.ScreenFlash 95 | */ 96 | Phaser.Plugin.Juicy.ScreenFlash.prototype.flash = function(maxAlpha, duration) { 97 | maxAlpha = maxAlpha || 1; 98 | duration = duration || 100; 99 | var flashTween = this.game.add.tween(this).to({alpha: maxAlpha}, 100, Phaser.Easing.Bounce.InOut, true,0, 0, true); 100 | flashTween.onComplete.add(function() { 101 | this.alpha = 0; 102 | }, this); 103 | }; 104 | 105 | /** 106 | * Creates a new `Juicy.Trail` object. 107 | * 108 | * @class Phaser.Plugin.Juicy.Trail 109 | * @constructor 110 | * 111 | * @param {Phaser.Game} game - Current game instance. 112 | * @param {number} [trailLength=100] - The length of the trail 113 | * @param {number} [color=0xFFFFFF] - The color of the trail 114 | * @memberof Phaser.Plugin.Juicy 115 | */ 116 | Phaser.Plugin.Juicy.Trail = function(game, trailLength, color) { 117 | Phaser.Graphics.call(this, game, 0,0); 118 | 119 | /** 120 | * @property {Phaser.Sprite} target - The target sprite whose movement we want to create the trail from 121 | */ 122 | this.target = null; 123 | /** 124 | * @property {number} trailLength - The number of segments to use to create the trail 125 | */ 126 | this.trailLength = trailLength || 100; 127 | /** 128 | * @property {number} trailWidth - The width of the trail 129 | */ 130 | this.trailWidth = 15.0; 131 | 132 | /** 133 | * @property {boolean} trailScale - Whether or not to taper the trail towards the end 134 | */ 135 | this.trailScaling = false; 136 | 137 | /** 138 | * @property {Phaser.Sprite} trailColor - The color of the trail 139 | */ 140 | this.trailColor = color || 0xFFFFFF; 141 | 142 | /** 143 | * @property {Array} _segments - A historical collection of the previous position of the target 144 | * @private 145 | */ 146 | this._segments = []; 147 | /** 148 | * @property {Array} _verts - A collection of vertices created from _segments 149 | * @private 150 | */ 151 | this._verts = []; 152 | /** 153 | * @property {Array} _segments - A collection of indices created from _verts 154 | * @private 155 | */ 156 | this._indices = []; 157 | 158 | }; 159 | 160 | Phaser.Plugin.Juicy.Trail.prototype = Object.create(Phaser.Graphics.prototype); 161 | Phaser.Plugin.Juicy.Trail.prototype.constructor = Phaser.Plugin.Juicy.Trail; 162 | 163 | /** 164 | * Updates the Trail if a target is set 165 | * 166 | * @method Phaser.Plugin.Juicy.Trail#update 167 | * @memberof Phaser.Plugin.Juicy.Trail 168 | */ 169 | 170 | Phaser.Plugin.Juicy.Trail.prototype.update = function() { 171 | if(this.target) { 172 | this.x = this.target.x; 173 | this.y = this.target.y; 174 | this.addSegment(this.target.x, this.target.y); 175 | this.redrawSegments(this.target.x, this.target.y); 176 | } 177 | }; 178 | 179 | /** 180 | * Adds a segment to the segments list and culls the list if it is too long 181 | * 182 | * @param {number} [x] - The x position of the point 183 | * @param {number} [y] - The y position of the point 184 | * 185 | * @method Phaser.Plugin.Juicy.Trail#addSegment 186 | * @memberof Phaser.Plugin.Juicy.Trail 187 | */ 188 | Phaser.Plugin.Juicy.Trail.prototype.addSegment = function(x, y) { 189 | var segment; 190 | 191 | while(this._segments.length > this.trailLength) { 192 | segment = this._segments.shift(); 193 | } 194 | if(!segment) { 195 | segment = new Phaser.Point(); 196 | } 197 | 198 | segment.x = x; 199 | segment.y = y; 200 | 201 | this._segments.push(segment); 202 | }; 203 | 204 | 205 | /** 206 | * Creates and draws the triangle trail from segments 207 | * 208 | * @param {number} [offsetX] - The x position of the object 209 | * @param {number} [offsetY] - The y position of the object 210 | * 211 | * @method Phaser.Plugin.Juicy.Trail#redrawSegments 212 | * @memberof Phaser.Plugin.Juicy.Trail 213 | */ 214 | Phaser.Plugin.Juicy.Trail.prototype.redrawSegments = function(offsetX, offsetY) { 215 | this.clear(); 216 | var s1, // current segment 217 | s2, // previous segment 218 | vertIndex = 0, // keeps track of which vertex index we're at 219 | offset, // temporary storage for amount to extend line outwards, bigger = wider 220 | ang, //temporary storage of the inter-segment angles 221 | sin = 0, // as above 222 | cos = 0; // again as above 223 | 224 | // first we make sure that the vertice list is the same length as we we want 225 | // each segment (except the first) will create to vertices with two values each 226 | if (this._verts.length !== (this._segments.length -1) * 4) { 227 | // if it's not correct, we clear the entire list 228 | this._verts = []; 229 | } 230 | 231 | // now we loop over all the segments, the list has the "youngest" segment at the end 232 | var prevAng = 0; 233 | 234 | for(var j = 0; j < this._segments.length; ++j) { 235 | // store the active segment for convenience 236 | s1 = this._segments[j]; 237 | 238 | // if there's a previous segment, time to do some math 239 | if(s2) { 240 | // we calculate the angle between the two segments 241 | // the result will be in radians, so adding half of pi will "turn" the angle 90 degrees 242 | // that means we can use the sin and cos values to "expand" the line outwards 243 | ang = Math.atan2(s1.y - s2.y, s1.x - s2.x) + Math.PI / 2; 244 | sin = Math.sin(ang); 245 | cos = Math.cos(ang); 246 | 247 | // now it's time to creat ethe two vertices that will represent this pair of segments 248 | // using a loop here is probably a bit overkill since it's only two iterations 249 | for(var i = 0; i < 2; ++i) { 250 | // this makes the first segment stand out to the "left" of the line 251 | // annd the second to the right, changing that magic number at the end will alther the line width 252 | offset = ( -0.5 + i / 1) * this.trailWidth; 253 | 254 | // if trail scale effect is enabled, we scale down the offset as we move down the list 255 | if(this.trailScaling) { 256 | offset *= j / this._segments.length; 257 | } 258 | 259 | // finally we put to values in the vert list 260 | // using the segment coordinates as a base we add the "extended" point 261 | // offsetX and offsetY are used her to move the entire trail 262 | this._verts[vertIndex++] = s1.x + cos * offset - offsetX; 263 | this._verts[vertIndex++] = s1.y + sin * offset - offsetY; 264 | } 265 | } 266 | // finally store the current segment as the previous segment and go for another round 267 | s2 = s1.copyTo({}); 268 | } 269 | // we need at least four vertices to draw something 270 | if(this._verts.length >= 8) { 271 | // now, we have a triangle "strip", but flash can't draw that without 272 | // instructions for which vertices to connect, so it's time to make those 273 | 274 | // here, we loop over all the vertices and pair them together in triangles 275 | // each group of four vertices forms two triangles 276 | for(var k = 0; k < this._verts.length; k++) { 277 | this._indices[k * 6 + 0] = k * 2 + 0; 278 | this._indices[k * 6 + 1] = k * 2 + 1; 279 | this._indices[k * 6 + 2] = k * 2 + 2; 280 | this._indices[k * 6 + 3] = k * 2 + 1; 281 | this._indices[k * 6 + 4] = k * 2 + 2; 282 | this._indices[k * 6 + 5] = k * 2 + 3; 283 | } 284 | this.beginFill(this.trailColor); 285 | this.drawTriangles(this._verts, this._indices); 286 | this.endFill(); 287 | 288 | } 289 | }; 290 | 291 | 292 | 293 | 294 | 295 | 296 | /** 297 | * Add a Sprite reference to this Plugin. 298 | * All this plugin does is move the Sprite across the screen slowly. 299 | * @type {Phaser.Sprite} 300 | */ 301 | 302 | /** 303 | * Begins the screen shake effect 304 | * 305 | * @param {number} [duration=20] - The duration of the screen shake 306 | * @param {number} [strength=20] - The strength of the screen shake 307 | * 308 | * @method Phaser.Plugin.Juicy#shake 309 | * @memberof Phaser.Plugin.Juicy 310 | */ 311 | Phaser.Plugin.Juicy.prototype.shake = function (duration, strength) { 312 | this._shakeWorldTime = duration || 20; 313 | this._shakeWorldMax = strength || 20; 314 | this.game.world.setBounds(this._boundsCache.x - this._shakeWorldMax, this._boundsCache.y - this._shakeWorldMax, this._boundsCache.width + this._shakeWorldMax, this._boundsCache.height + this._shakeWorldMax); 315 | }; 316 | 317 | 318 | /** 319 | * Creates a 'Juicy.ScreenFlash' object 320 | * 321 | * @param {string} color - The color of the screen flash 322 | * 323 | * @type {Phaser.Plugin.Juicy.ScreenFlash} 324 | */ 325 | 326 | Phaser.Plugin.Juicy.prototype.createScreenFlash = function(color) { 327 | return new Phaser.Plugin.Juicy.ScreenFlash(this.game, color); 328 | }; 329 | 330 | 331 | /** 332 | * Creates a 'Juicy.Trail' object 333 | * 334 | * @param {number} length - The length of the trail 335 | * @param {number} color - The color of the trail 336 | * 337 | * @type {Phaser.Plugin.Juicy.Trail} 338 | */ 339 | Phaser.Plugin.Juicy.prototype.createTrail = function(length, color) { 340 | return new Phaser.Plugin.Juicy.Trail(this.game, length, color); 341 | }; 342 | 343 | 344 | /** 345 | * Creates the over scale effect on the given object 346 | * 347 | * @param {Phaser.Sprite} object - The object to over scale 348 | * @param {number} [scale=1.5] - The scale amount to overscale by 349 | * @param {Phaser.Point} [initialScale=new Phaser.Point(1,1)] - The initial scale of the object 350 | * 351 | */ 352 | Phaser.Plugin.Juicy.prototype.overScale = function(object, scale, initialScale) { 353 | scale = scale || 1.5; 354 | var id = this._overScalesCounter++; 355 | initialScale = initialScale || new Phaser.Point(1,1); 356 | var scaleObj = this._overScales[id]; 357 | if(!scaleObj) { 358 | scaleObj = { 359 | object: object, 360 | cache: initialScale.copyTo({}) 361 | }; 362 | } 363 | scaleObj.scale = scale; 364 | 365 | this._overScales[id] = scaleObj; 366 | }; 367 | 368 | /** 369 | * Creates the jelly effect on the given object 370 | * 371 | * @param {Phaser.Sprite} object - The object to gelatinize 372 | * @param {number} [strength=0.2] - The strength of the effect 373 | * @param {number} [delay=0] - The delay of the snap-back tween. 50ms are automaticallly added to whatever the delay amount is. 374 | * @param {Phaser.Point} [initialScale=new Phaser.Point(1,1)] - The initial scale of the object 375 | * 376 | */ 377 | Phaser.Plugin.Juicy.prototype.jelly = function(object, strength, delay, initialScale) { 378 | strength = strength || 0.2; 379 | delay = delay || 0; 380 | initialScale = initialScale || new Phaser.Point(1, 1); 381 | 382 | this.game.add.tween(object.scale).to({x: initialScale.x + (initialScale.x * strength)}, 50, Phaser.Easing.Quadratic.InOut, true, delay) 383 | .to({x: initialScale.x}, 600, Phaser.Easing.Elastic.Out, true); 384 | 385 | this.game.add.tween(object.scale).to({y: initialScale.y + (initialScale.y * strength)}, 50, Phaser.Easing.Quadratic.InOut, true, delay + 50) 386 | .to({y: initialScale.y}, 600, Phaser.Easing.Elastic.Out, true); 387 | }; 388 | 389 | /** 390 | * Creates the mouse stretch effect on the given object 391 | * 392 | * @param {Phaser.Sprite} object - The object to mouse stretch 393 | * @param {number} [strength=0.5] - The strength of the effect 394 | * @param {Phaser.Point} [initialScale=new Phaser.Point(1,1)] - The initial scale of the object 395 | * 396 | */ 397 | Phaser.Plugin.Juicy.prototype.mouseStretch = function(object, strength, initialScale) { 398 | strength = strength || 0.5; 399 | initialScale = initialScale || new Phaser.Point(1,1); 400 | object.scale.x = initialScale.x + (Math.abs(object.x - this.game.input.activePointer.x) / 100) * strength; 401 | object.scale.y = initialScale.y + (initialScale.y * strength) - (object.scale.x * strength); 402 | }; 403 | 404 | /** 405 | * Runs the core update function and causes screen shake and overscaling effects to occur if they are queued to do so. 406 | * 407 | * @method Phaser.Plugin.Juicy#update 408 | * @memberof Phaser.Plugin.Juicy 409 | */ 410 | Phaser.Plugin.Juicy.prototype.update = function () { 411 | var scaleObj; 412 | // Screen Shake 413 | if(this._shakeWorldTime > 0) { 414 | var magnitude = (this._shakeWorldTime / this._shakeWorldMax) * this._shakeWorldMax; 415 | var x = this.game.rnd.integerInRange(-magnitude, magnitude); 416 | var y = this.game.rnd.integerInRange(-magnitude, magnitude); 417 | 418 | this.game.camera.x = x; 419 | this.game.camera.y = y; 420 | this._shakeWorldTime--; 421 | if(this._shakeWorldTime <= 0) { 422 | this.game.world.setBounds(this._boundsCache.x, this._boundsCache.x, this._boundsCache.width, this._boundsCache.height); 423 | } 424 | } 425 | 426 | // over scales 427 | for(var s in this._overScales) { 428 | if(this._overScales.hasOwnProperty(s)) { 429 | scaleObj = this._overScales[s]; 430 | if(scaleObj.scale > 0.01) { 431 | scaleObj.object.scale.x = scaleObj.scale * scaleObj.cache.x; 432 | scaleObj.object.scale.y = scaleObj.scale * scaleObj.cache.y; 433 | scaleObj.scale -= this.game.time.elapsed * scaleObj.scale * 0.35; 434 | } else { 435 | scaleObj.object.scale.x = scaleObj.cache.x; 436 | scaleObj.object.scale.y = scaleObj.cache.y; 437 | delete this._overScales[s]; 438 | } 439 | } 440 | } 441 | }; 442 | 443 | // for browserify compatibility 444 | if(typeof module === 'object' && module.exports) { 445 | module.exports = Phaser.Plugin.Juicy; 446 | } 447 | 448 | 449 | 450 | // Draw Triangles Polyfill for back compatibility 451 | if(!Phaser.Graphics.prototype.drawTriangle) { 452 | Phaser.Graphics.prototype.drawTriangle = function(points, cull) { 453 | var triangle = new Phaser.Polygon(points); 454 | if (cull) { 455 | var cameraToFace = new Phaser.Point(this.game.camera.x - points[0].x, this.game.camera.y - points[0].y); 456 | var ab = new Phaser.Point(points[1].x - points[0].x, points[1].y - points[0].y); 457 | var cb = new Phaser.Point(points[1].x - points[2].x, points[1].y - points[2].y); 458 | var faceNormal = cb.cross(ab); 459 | if (cameraToFace.dot(faceNormal) > 0) { 460 | this.drawPolygon(triangle); 461 | } 462 | } else { 463 | this.drawPolygon(triangle); 464 | } 465 | return; 466 | }; 467 | 468 | /* 469 | * Draws {Phaser.Polygon} triangles 470 | * 471 | * @param {Array|Array} vertices - An array of Phaser.Points or numbers that make up the vertices of the triangles 472 | * @param {Array} {indices=null} - An array of numbers that describe what order to draw the vertices in 473 | * @param {boolean} [cull=false] - Should we check if the triangle is back-facing 474 | * @method Phaser.Graphics.prototype.drawTriangles 475 | */ 476 | 477 | Phaser.Graphics.prototype.drawTriangles = function(vertices, indices, cull) { 478 | 479 | var point1 = new Phaser.Point(), 480 | point2 = new Phaser.Point(), 481 | point3 = new Phaser.Point(), 482 | points = [], 483 | i; 484 | 485 | if (!indices) { 486 | if(vertices[0] instanceof Phaser.Point) { 487 | for(i = 0; i < vertices.length / 3; i++) { 488 | this.drawTriangle([vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]], cull); 489 | } 490 | } else { 491 | for (i = 0; i < vertices.length / 6; i++) { 492 | point1.x = vertices[i * 6 + 0]; 493 | point1.y = vertices[i * 6 + 1]; 494 | point2.x = vertices[i * 6 + 2]; 495 | point2.y = vertices[i * 6 + 3]; 496 | point3.x = vertices[i * 6 + 4]; 497 | point3.y = vertices[i * 6 + 5]; 498 | this.drawTriangle([point1, point2, point3], cull); 499 | } 500 | 501 | } 502 | } else { 503 | if(vertices[0] instanceof Phaser.Point) { 504 | for(i = 0; i < indices.length /3; i++) { 505 | points.push(vertices[indices[i * 3 ]]); 506 | points.push(vertices[indices[i * 3 + 1]]); 507 | points.push(vertices[indices[i * 3 + 2]]); 508 | if(points.length === 3) { 509 | this.drawTriangle(points, cull); 510 | points = []; 511 | } 512 | 513 | } 514 | } else { 515 | for (i = 0; i < indices.length; i++) { 516 | point1.x = vertices[indices[i] * 2]; 517 | point1.y = vertices[indices[i] * 2 + 1]; 518 | points.push(point1.copyTo({})); 519 | if (points.length === 3) { 520 | this.drawTriangle(points, cull); 521 | points = []; 522 | } 523 | } 524 | } 525 | } 526 | }; 527 | } 528 | -------------------------------------------------------------------------------- /KineticScrolling/KineticScrolling.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Phaser Kinetic Scrolling Plugin 3 | * @author Juan Nicholls 4 | * @copyright 2015 Juan Nicholls - http://jdnichollsc.github.io/Phaser-Kinetic-Scrolling-Plugin/ 5 | * @license {@link http://opensource.org/licenses/MIT} 6 | * @version 0.1.2 7 | */ 8 | 9 | (function (Phaser) { 10 | 'use strict'; 11 | 12 | /** 13 | * Kinetic Scrolling is a Phaser plugin that allows vertical and horizontal scrolling with kinetic motion. 14 | * It works with the Phaser.Camera 15 | * 16 | * @class Phaser.Plugin.KineticScrolling 17 | * @constructor 18 | * @param {Object} game - The Game object is the instance of the game, where the magic happens. 19 | * @param {Any} parent - The object that owns this plugin, usually Phaser.PluginManager. 20 | */ 21 | Phaser.Plugin.KineticScrolling = function (game, parent) { 22 | Phaser.Plugin.call(this, game, parent); 23 | 24 | 25 | this.dragging = false; 26 | this.timestamp = 0; 27 | this.callbackID = 0; 28 | 29 | this.targetX = 0; 30 | this.targetY = 0; 31 | 32 | this.autoScrollX = false; 33 | this.autoScrollY = false; 34 | 35 | this.startX = 0; 36 | this.startY = 0; 37 | 38 | this.velocityX = 0; 39 | this.velocityY = 0; 40 | 41 | this.amplitudeX = 0; 42 | this.amplitudeY = 0; 43 | 44 | this.directionWheel = 0; 45 | 46 | this.velocityWheelX = 0; 47 | this.velocityWheelY = 0; 48 | 49 | this.settings = { 50 | kineticMovement: true, 51 | timeConstantScroll: 325, //really mimic iOS 52 | horizontalScroll: true, 53 | verticalScroll: false, 54 | horizontalWheel: true, 55 | verticalWheel: false, 56 | deltaWheel: 40 57 | }; 58 | }; 59 | 60 | Phaser.Plugin.KineticScrolling.prototype = Object.create(Phaser.Plugin.prototype); 61 | Phaser.Plugin.KineticScrolling.prototype.constructor = Phaser.Plugin.KineticScrolling; 62 | 63 | /** 64 | * Change Default Settings of the plugin 65 | * 66 | * @method Phaser.Plugin.KineticScrolling#configure 67 | * @param {Object} [options] - Object that contain properties to change the behavior of the plugin. 68 | * @param {number} [options.timeConstantScroll=325] - The rate of deceleration for the scrolling. 69 | * @param {boolean} [options.kineticMovement=true] - Enable or Disable the kinematic motion. 70 | * @param {boolean} [options.horizontalScroll=true] - Enable or Disable the horizontal scrolling. 71 | * @param {boolean} [options.verticalScroll=false] - Enable or Disable the vertical scrolling. 72 | * @param {boolean} [options.horizontalWheel=true] - Enable or Disable the horizontal scrolling with mouse wheel. 73 | * @param {boolean} [options.verticalWheel=false] - Enable or Disable the vertical scrolling with mouse wheel. 74 | * @param {number} [options.deltaWheel=40] - Delta increment of the mouse wheel. 75 | */ 76 | Phaser.Plugin.KineticScrolling.prototype.configure = function (options) { 77 | 78 | if (options) { 79 | for (var property in options) { 80 | if (this.settings.hasOwnProperty(property)) { 81 | this.settings[property] = options[property]; 82 | } 83 | } 84 | } 85 | 86 | }; 87 | 88 | /** 89 | * Start the Plugin. 90 | * 91 | * @method Phaser.Plugin.KineticScrolling#start 92 | */ 93 | Phaser.Plugin.KineticScrolling.prototype.start = function () { 94 | 95 | this.game.input.onDown.add(this.beginMove, this); 96 | 97 | this.callbackID = this.game.input.addMoveCallback(this.moveCamera, this); 98 | 99 | this.game.input.onUp.add(this.endMove, this); 100 | 101 | this.game.input.mouse.mouseWheelCallback = this.mouseWheel.bind(this); 102 | }; 103 | 104 | /** 105 | * Event triggered when a pointer is pressed down, resets the value of variables. 106 | */ 107 | Phaser.Plugin.KineticScrolling.prototype.beginMove = function () { 108 | 109 | this.startX = this.game.input.x; 110 | this.startY = this.game.input.y; 111 | 112 | this.dragging = true; 113 | 114 | this.timestamp = Date.now(); 115 | 116 | this.velocityY = this.amplitudeY = this.velocityX = this.amplitudeX = 0; 117 | 118 | }; 119 | 120 | /** 121 | * Event triggered when the activePointer receives a DOM move event such as a mousemove or touchmove. 122 | * The camera moves according to the movement of the pointer, calculating the velocity. 123 | */ 124 | Phaser.Plugin.KineticScrolling.prototype.moveCamera = function (pointer, x, y) { 125 | 126 | if (!this.dragging) return; 127 | 128 | this.now = Date.now(); 129 | var elapsed = this.now - this.timestamp; 130 | this.timestamp = this.now; 131 | 132 | if (this.settings.horizontalScroll) { 133 | var delta = x - this.startX; //Compute move distance 134 | this.startX = x; 135 | this.velocityX = 0.8 * (1000 * delta / (1 + elapsed)) + 0.2 * this.velocityX; 136 | this.game.camera.x -= delta; 137 | } 138 | 139 | if (this.settings.verticalScroll) { 140 | var delta = y - this.startY; //Compute move distance 141 | this.startY = y; 142 | this.velocityY = 0.8 * (1000 * delta / (1 + elapsed)) + 0.2 * this.velocityY; 143 | this.game.camera.y -= delta; 144 | } 145 | 146 | }; 147 | 148 | /** 149 | * Event triggered when a pointer is released, calculates the automatic scrolling. 150 | */ 151 | Phaser.Plugin.KineticScrolling.prototype.endMove = function () { 152 | 153 | this.dragging = false; 154 | this.autoScrollX = false; 155 | this.autoScrollY = false; 156 | 157 | if (!this.settings.kineticMovement) return; 158 | 159 | this.now = Date.now(); 160 | 161 | if (this.velocityX > 10 || this.velocityX < -10) { 162 | this.amplitudeX = 0.8 * this.velocityX; 163 | this.targetX = Math.round(this.game.camera.x - this.amplitudeX); 164 | this.autoScrollX = true; 165 | } 166 | 167 | if (this.velocityY > 10 || this.velocityY < -10) { 168 | this.amplitudeY = 0.8 * this.velocityY; 169 | this.targetY = Math.round(this.game.camera.y - this.amplitudeY); 170 | this.autoScrollY = true; 171 | } 172 | 173 | }; 174 | 175 | /** 176 | * Event called after all the core subsystems and the State have updated, but before the render. 177 | * Create the deceleration effect. 178 | */ 179 | Phaser.Plugin.KineticScrolling.prototype.update = function () { 180 | 181 | this.elapsed = Date.now() - this.timestamp; 182 | 183 | if (this.autoScrollX && this.amplitudeX != 0) { 184 | 185 | var delta = -this.amplitudeX * Math.exp(-this.elapsed / this.settings.timeConstantScroll); 186 | if (delta > 0.5 || delta < -0.5) { 187 | this.game.camera.x = this.targetX - delta; 188 | } 189 | else { 190 | this.autoScrollX = false; 191 | this.game.camera.x = this.targetX; 192 | } 193 | } 194 | 195 | if (this.autoScrollY && this.amplitudeY != 0) { 196 | 197 | var delta = -this.amplitudeY * Math.exp(-this.elapsed / this.settings.timeConstantScroll); 198 | if (delta > 0.5 || delta < -0.5) { 199 | this.game.camera.y = this.targetY - delta; 200 | } 201 | else { 202 | this.autoScrollY = false; 203 | this.game.camera.y = this.targetY; 204 | } 205 | } 206 | 207 | if (this.settings.horizontalWheel && (this.velocityWheelX < -0.1 || this.velocityWheelX > 0.1)) { 208 | 209 | this.game.camera.x -= this.velocityWheelX; 210 | this.velocityWheelX *= 0.95; 211 | } 212 | 213 | if (this.settings.verticalWheel && (this.velocityWheelY < -0.1 || this.velocityWheelY > 0.1)) { 214 | 215 | this.game.camera.y -= this.velocityWheelY; 216 | this.velocityWheelY *= 0.95; 217 | } 218 | }; 219 | 220 | /** 221 | * Event called when the mousewheel is used, affect the direction of scrolling. 222 | */ 223 | Phaser.Plugin.KineticScrolling.prototype.mouseWheel = function (event) { 224 | if (!this.settings.horizontalWheel && !this.settings.verticalWheel) return; 225 | 226 | event.preventDefault(); 227 | 228 | var delta = this.game.input.mouse.wheelDelta * 120 / this.settings.deltaWheel; 229 | 230 | if (this.directionWheel != this.game.input.mouse.wheelDelta) { 231 | this.velocityWheelX = 0; 232 | this.velocityWheelY = 0; 233 | this.directionWheel = this.game.input.mouse.wheelDelta; 234 | } 235 | 236 | if (this.settings.horizontalWheel) { 237 | this.autoScrollX = false; 238 | 239 | this.velocityWheelX += delta; 240 | } 241 | 242 | if (this.settings.verticalWheel) { 243 | this.autoScrollY = false; 244 | 245 | this.velocityWheelY += delta; 246 | } 247 | 248 | }; 249 | 250 | /** 251 | * Stop the Plugin. 252 | * 253 | * @method Phaser.Plugin.KineticScrolling#stop 254 | */ 255 | Phaser.Plugin.KineticScrolling.prototype.stop = function () { 256 | 257 | this.game.input.onDown.remove(this.beginMove, this); 258 | 259 | this.game.input.deleteMoveCallback(this.callbackID); 260 | 261 | this.game.input.onUp.remove(this.endMove, this); 262 | 263 | this.game.input.mouse.mouseWheelCallback = null; 264 | 265 | }; 266 | 267 | } (Phaser)); 268 | -------------------------------------------------------------------------------- /ProTracker/ProTracker.js: -------------------------------------------------------------------------------- 1 | /* 2 | amiga protracker module player for web audio api 3 | (c) 2012-2014 firehawk/tda (firehawk@haxor.fi) 4 | 5 | originally hacked together in a weekend, so please excuse 6 | me for the spaghetti code. :) 7 | 8 | feel free to use this player in your website/demo/whatever 9 | if you find it useful. drop me an email if you do. 10 | 11 | AMIGAAAAAAAAH!! 12 | 13 | all code licensed under MIT license: 14 | http://opensource.org/licenses/MIT 15 | 16 | kinda sorta changelog: 17 | (sep 2014) 18 | - fixed bug with E8x sync and added 80x to also function for sync 19 | events due to problems with some protracker versions (thanks spot) 20 | (aug 2014) 21 | - added sync event queue for E8x commands 22 | - changed the amiga fixed filter model to allow changes at runtime 23 | - three stereo separation modes now, 0=amiga, 1=65/35, 2=mono 24 | - a few bugfixes, thanks spot^uprough and esau^traktor for reporting 25 | * fixed bug in slide-to-note when 300 with no preceeding 3xy 26 | * fixed vibrato depth on ticks 1+ to match tick 0 27 | * added boolean variable for disabling A500 fixed lowpass filter 28 | * added a delay on module start, number of buffers selectable 29 | * fixed sample loop discarding pointer overflow 30 | (may 2014) 31 | - added boolean variable for the amiga led filter for ui stuff 32 | (jan 2014) 33 | - disabled ee0 filter command for tracks with over 4 channels to 34 | make mod.dope play correctly 35 | (oct 2013) 36 | - added support for firefox 24 37 | (apr 2013) 38 | - changed the logic for pattern break/jump. mod.pattern_skank now 39 | plays correctly. 40 | (feb 2013) 41 | - fixed NaN samples with mod.fractured and mod.multicolour (thanks Aegis!) 42 | (jan 2013) 43 | - fixed vibrato amplitude (was half of what it should be, apparently) 44 | - fixed to work on safari again (thanks Matt Diamond @ stackoverflow.com) 45 | (dec 2012) 46 | - replaced effect switch-statement with jumptables 47 | - fixed clicks (bad loops, empty samples) 48 | - fixed playback bug with sample-only rows 49 | - added amiga 500 lowpass filters (not 100% authentic, though) 50 | - added compressor to output 51 | - latest safari has broken web audio so chrome-only for now 52 | (aug 2012) 53 | - first version written from scratch 54 | 55 | todo: 56 | - pattern looping is way broken in mod.black_queen 57 | - properly test EEx delay pattern 58 | - implement the rest of the effects 59 | - optimize for more speed!! SPEEEED!! 60 | * switch to fixed point sample pointers, Math.floor() is _slow_ on iOS 61 | */ 62 | 63 | // constructor for protracker player object 64 | // @class Phaser.Plugin.ProTracker 65 | function Protracker() 66 | { 67 | var i, t; 68 | 69 | this.initialize(); 70 | this.clearsong(); 71 | 72 | this.url=""; 73 | this.loading=false; 74 | this.ready=false; 75 | this.playing=false; 76 | this.buffer=0; 77 | this.mixerNode=0; 78 | this.paused=false; 79 | this.repeat=false; 80 | this.filter=false; 81 | 82 | this.separation=1; 83 | this.palclock=true; 84 | this.amiga500=true; 85 | 86 | this.autostart=false; 87 | this.bufferstodelay=4; // adjust this if you get stutter after loading new song 88 | this.delayfirst=0; 89 | this.delayload=0; 90 | 91 | this.syncqueue=[]; 92 | 93 | this.onReady=function(){}; 94 | this.onPlay=function(){}; 95 | this.onStop=function(){}; 96 | 97 | this.context = null; 98 | this.samplerate=44100; 99 | this.bufferlen=2048; 100 | 101 | // paula period values 102 | this.baseperiodtable=new Array( 103 | 856,808,762,720,678,640,604,570,538,508,480,453, 104 | 428,404,381,360,339,320,302,285,269,254,240,226, 105 | 214,202,190,180,170,160,151,143,135,127,120,113); 106 | 107 | // finetune multipliers 108 | this.finetunetable=new Array(); 109 | for(t=0;t<16;t++) this.finetunetable[t]=Math.pow(2, (t-8)/12/8); 110 | 111 | // calc tables for vibrato waveforms 112 | this.vibratotable=new Array(); 113 | for(t=0;t<4;t++) { 114 | this.vibratotable[t]=new Array(); 115 | for(i=0;i<64;i++) { 116 | switch(t) { 117 | case 0: 118 | this.vibratotable[t][i]=127*Math.sin(Math.PI*2*(i/64)); 119 | break; 120 | case 1: 121 | this.vibratotable[t][i]=127-4*i; 122 | break; 123 | case 2: 124 | this.vibratotable[t][i]=(i<32)?127:-127; 125 | break; 126 | case 3: 127 | this.vibratotable[t][i]=(1-2*Math.random())*127; 128 | break; 129 | } 130 | } 131 | } 132 | 133 | // effect jumptables 134 | this.effects_t0 = new Array( 135 | this.effect_t0_0, this.effect_t0_1, this.effect_t0_2, this.effect_t0_3, this.effect_t0_4, this.effect_t0_5, this.effect_t0_6, this.effect_t0_7, 136 | this.effect_t0_8, this.effect_t0_9, this.effect_t0_a, this.effect_t0_b, this.effect_t0_c, this.effect_t0_d, this.effect_t0_e, this.effect_t0_f); 137 | this.effects_t0_e = new Array( 138 | this.effect_t0_e0, this.effect_t0_e1, this.effect_t0_e2, this.effect_t0_e3, this.effect_t0_e4, this.effect_t0_e5, this.effect_t0_e6, this.effect_t0_e7, 139 | this.effect_t0_e8, this.effect_t0_e9, this.effect_t0_ea, this.effect_t0_eb, this.effect_t0_ec, this.effect_t0_ed, this.effect_t0_ee, this.effect_t0_ef); 140 | this.effects_t1 = new Array( 141 | this.effect_t1_0, this.effect_t1_1, this.effect_t1_2, this.effect_t1_3, this.effect_t1_4, this.effect_t1_5, this.effect_t1_6, this.effect_t1_7, 142 | this.effect_t1_8, this.effect_t1_9, this.effect_t1_a, this.effect_t1_b, this.effect_t1_c, this.effect_t1_d, this.effect_t1_e, this.effect_t1_f); 143 | this.effects_t1_e = new Array( 144 | this.effect_t1_e0, this.effect_t1_e1, this.effect_t1_e2, this.effect_t1_e3, this.effect_t1_e4, this.effect_t1_e5, this.effect_t1_e6, this.effect_t1_e7, 145 | this.effect_t1_e8, this.effect_t1_e9, this.effect_t1_ea, this.effect_t1_eb, this.effect_t1_ec, this.effect_t1_ed, this.effect_t1_ee, this.effect_t1_ef); 146 | 147 | 148 | } 149 | 150 | 151 | 152 | // create the web audio context 153 | Protracker.prototype.createContext = function() 154 | { 155 | if ( typeof AudioContext !== 'undefined') { 156 | this.context = new AudioContext(); 157 | } else { 158 | this.context = new webkitAudioContext(); 159 | } 160 | this.samplerate=this.context.sampleRate; 161 | this.bufferlen=(this.samplerate > 44100) ? 4096 : 2048; 162 | 163 | // Amiga 500 fixed filter at 6kHz. WebAudio lowpass is 12dB/oct, whereas 164 | // older Amigas had a 6dB/oct filter at 4900Hz. 165 | this.filterNode=this.context.createBiquadFilter(); 166 | if (this.amiga500) { 167 | this.filterNode.frequency.value=6000; 168 | } else { 169 | this.filterNode.frequency.value=28867; 170 | } 171 | 172 | // "LED filter" at 3275kHz - off by default 173 | this.lowpassNode=this.context.createBiquadFilter(); 174 | this.lowpassNode.frequency.value=28867; 175 | this.filter=false; 176 | 177 | // mixer 178 | if ( typeof this.context.createJavaScriptNode === 'function') { 179 | this.mixerNode=this.context.createJavaScriptNode(this.bufferlen, 1, 2); 180 | } else { 181 | this.mixerNode=this.context.createScriptProcessor(this.bufferlen, 1, 2); 182 | } 183 | this.mixerNode.module=this; 184 | this.mixerNode.onaudioprocess=Protracker.prototype.mix; 185 | 186 | // compressor for a bit of volume boost, helps with multich tunes 187 | this.compressorNode=this.context.createDynamicsCompressor(); 188 | 189 | // patch up some cables :) 190 | this.mixerNode.connect(this.filterNode); 191 | this.filterNode.connect(this.lowpassNode); 192 | this.lowpassNode.connect(this.compressorNode); 193 | this.compressorNode.connect(this.context.destination); 194 | } 195 | 196 | 197 | 198 | // play loaded and parsed module with webaudio context 199 | Protracker.prototype.play = function() 200 | { 201 | if (this.context==null) this.createContext(); 202 | 203 | if (!this.ready) return false; 204 | if (this.paused) { 205 | this.paused=false; 206 | return true; 207 | } 208 | this.endofsong=false; 209 | this.paused=false; 210 | this.initialize(); 211 | this.flags=1+2; 212 | this.playing=true; 213 | this.onPlay(); 214 | this.delayfirst=this.bufferstodelay; 215 | return true; 216 | } 217 | 218 | 219 | 220 | // pause playback 221 | Protracker.prototype.pause = function() 222 | { 223 | if (!this.paused) { 224 | this.paused=true; 225 | } else { 226 | this.paused=false; 227 | } 228 | } 229 | 230 | 231 | 232 | // stop playback 233 | Protracker.prototype.stop = function() 234 | { 235 | this.playing=false; 236 | this.onStop(); 237 | this.delayload=1; 238 | } 239 | 240 | 241 | 242 | // stop playing but don't call callbacks 243 | Protracker.prototype.stopaudio = function(st) 244 | { 245 | this.playing=st; 246 | } 247 | 248 | 249 | 250 | // jump positions forward/back 251 | Protracker.prototype.jump = function(step) 252 | { 253 | this.tick=0; 254 | this.row=0; 255 | this.position+=step; 256 | this.flags=1+2; 257 | if (this.position<0) this.position=0; 258 | if (this.position >= this.songlen) this.stop(); 259 | } 260 | 261 | 262 | 263 | // set whether module repeats after songlen 264 | Protracker.prototype.setrepeat = function(rep) 265 | { 266 | this.repeat=rep; 267 | } 268 | 269 | 270 | 271 | // set stereo separation mode (0=paula, 1=betterpaula (60/40), 2=mono) 272 | Protracker.prototype.setseparation = function(sep) 273 | { 274 | this.separation=sep; 275 | } 276 | 277 | 278 | 279 | // set amiga video standard (false=NTSC, true=PAL) 280 | Protracker.prototype.setamigatype = function(clock) 281 | { 282 | this.palclock=clock; 283 | } 284 | 285 | 286 | 287 | // set autostart to play immediately after loading 288 | Protracker.prototype.setautostart = function(st) 289 | { 290 | this.autostart=st; 291 | } 292 | 293 | 294 | 295 | 296 | 297 | // set amiga model - changes fixed filter state 298 | Protracker.prototype.setamigamodel = function(amiga) 299 | { 300 | if (amiga=="600" || amiga=="1200" || amiga=="4000") { 301 | this.amiga500=false; 302 | if (this.filterNode) this.filterNode.frequency.value=28867; 303 | } else { 304 | this.amiga500=true; 305 | if (this.filterNode) this.filterNode.frequency.value=6000; 306 | } 307 | } 308 | 309 | 310 | 311 | // are there E8x sync events queued? 312 | Protracker.prototype.hassyncevents = function() 313 | { 314 | return (this.syncqueue.length != 0); 315 | } 316 | 317 | 318 | 319 | // pop oldest sync event nybble from the FIFO queue 320 | Protracker.prototype.popsyncevent = function() 321 | { 322 | return this.syncqueue.pop(); 323 | } 324 | 325 | 326 | 327 | // clear song data 328 | Protracker.prototype.clearsong = function() 329 | { 330 | this.title=""; 331 | this.signature=""; 332 | this.songlen=1; 333 | this.repeatpos=0; 334 | this.patterntable=new ArrayBuffer(128); 335 | for(i=0;i<128;i++) this.patterntable[i]=0; 336 | 337 | this.channels=4; 338 | 339 | this.sample=new Array(); 340 | this.samples=31; 341 | for(i=0;i<31;i++) { 342 | this.sample[i]=new Object(); 343 | this.sample[i].name=""; 344 | this.sample[i].length=0; 345 | this.sample[i].finetune=0; 346 | this.sample[i].volume=64; 347 | this.sample[i].loopstart=0; 348 | this.sample[i].looplength=0; 349 | this.sample[i].data=0; 350 | } 351 | 352 | this.patterns=0; 353 | this.pattern=new Array(); 354 | this.note=new Array(); 355 | 356 | this.looprow=0; 357 | this.loopstart=0; 358 | this.loopcount=0; 359 | 360 | this.patterndelay=0; 361 | this.patternwait=0; 362 | 363 | this.syncqueue=[]; 364 | } 365 | 366 | 367 | // initialize all player variables 368 | Protracker.prototype.initialize = function() 369 | { 370 | this.syncqueue=[]; 371 | 372 | this.tick=0; 373 | this.position=0; 374 | this.row=0; 375 | this.offset=0; 376 | this.flags=0; 377 | 378 | this.speed=6; 379 | this.bpm=125; 380 | this.breakrow=0; 381 | this.patternjump=0; 382 | this.patterndelay=0; 383 | this.patternwait=0; 384 | this.endofsong=false; 385 | 386 | this.channel=new Array(); 387 | for(i=0;i0x1f) && (this.buffer[st+j]<0x7f)) ? 484 | (String.fromCharCode(this.buffer[st+j])) : 485 | (" "); 486 | j++; 487 | } 488 | this.sample[i].length=2*(this.buffer[st+22]*256 + this.buffer[st+23]); 489 | this.sample[i].finetune=this.buffer[st+24]; 490 | if (this.sample[i].finetune > 7) this.sample[i].finetune=this.sample[i].finetune-16; 491 | this.sample[i].volume=this.buffer[st+25]; 492 | this.sample[i].loopstart=2*(this.buffer[st+26]*256 + this.buffer[st+27]); 493 | this.sample[i].looplength=2*(this.buffer[st+28]*256 + this.buffer[st+29]); 494 | if (this.sample[i].looplength==2) this.sample[i].looplength=0; 495 | if (this.sample[i].loopstart>this.sample[i].length) { 496 | this.sample[i].loopstart=0; 497 | this.sample[i].looplength=0; 498 | } 499 | } 500 | 501 | this.songlen=this.buffer[950]; 502 | if (this.buffer[951] != 127) this.repeatpos=this.buffer[951]; 503 | for(i=0;i<128;i++) { 504 | this.patterntable[i]=this.buffer[952+i]; 505 | if (this.patterntable[i] > this.patterns) this.patterns=this.patterntable[i]; 506 | } 507 | this.patterns+=1; 508 | var patlen=4*64*this.channels; 509 | 510 | this.pattern=new Array(); 511 | this.note=new Array(); 512 | for(i=0;ispd) { mod.tick++; mod.offset=0; mod.flags|=1; } 561 | if (mod.tick>=mod.speed) { 562 | 563 | if (mod.patterndelay) { // delay pattern 564 | if (mod.tick < ((mod.patternwait+1)*mod.speed)) { 565 | mod.patternwait++; 566 | } else { 567 | mod.row++; mod.tick=0; mod.flags|=2; mod.patterndelay=0; 568 | } 569 | } 570 | else { 571 | 572 | if (mod.flags&(16+32+64)) { 573 | if (mod.flags&64) { // loop pattern? 574 | mod.row=mod.looprow; 575 | mod.flags&=0xa1; 576 | mod.flags|=2; 577 | } 578 | else { 579 | if (mod.flags&16) { // pattern jump/break? 580 | //console.log("break to pattern " + mod.patternjump + " row "+mod.breakrow); 581 | mod.position=mod.patternjump; 582 | mod.row=mod.breakrow; 583 | mod.patternjump=0; 584 | mod.breakrow=0; 585 | mod.flags&=0xe1; 586 | mod.flags|=2; 587 | } 588 | } 589 | mod.tick=0; 590 | } else { 591 | mod.row++; mod.tick=0; mod.flags|=2; 592 | } 593 | } 594 | } 595 | if (mod.row>=64) { mod.position++; mod.row=0; mod.flags|=4; } 596 | if (mod.position>=mod.songlen) { 597 | if (mod.repeat) { 598 | mod.position=0; 599 | } else { 600 | this.endofsong=true; 601 | mod.stop(); 602 | } 603 | return; 604 | } 605 | } 606 | 607 | 608 | 609 | // mix an audio buffer with data 610 | Protracker.prototype.mix = function(ape) { 611 | var f; 612 | var p, pp, n, nn; 613 | var mod; 614 | if (ape.srcElement) { 615 | mod=ape.srcElement.module; 616 | } else { 617 | mod=this.module; 618 | } 619 | outp=new Array(); 620 | 621 | var bufs=new Array(ape.outputBuffer.getChannelData(0), ape.outputBuffer.getChannelData(1)); 622 | var buflen=ape.outputBuffer.length; 623 | for(var s=0;s3) mod.channel[ch].vibratopos=0; 650 | mod.channel[ch].flags|=3; // recalc speed 651 | mod.channel[ch].noteon=1; 652 | } 653 | // in either case, set the slide to note target 654 | mod.channel[ch].slideto=n; 655 | } 656 | nn=mod.pattern[p][pp+0]&0xf0 | mod.pattern[p][pp+2]>>4; 657 | if (nn) { 658 | mod.channel[ch].sample=nn-1; 659 | mod.channel[ch].volume=mod.sample[nn-1].volume; 660 | if (!n && (mod.channel[ch].samplepos > mod.sample[nn-1].length)) mod.channel[ch].samplepos=0; 661 | } 662 | } 663 | } 664 | mod.channel[ch].voiceperiod=mod.channel[ch].period; 665 | 666 | // kill empty samples 667 | if (!mod.sample[mod.channel[ch].sample].length) mod.channel[ch].noteon=0; 668 | 669 | // effects 670 | if (mod.flags&1) { 671 | if (!mod.tick) { 672 | // process only on tick 0 673 | mod.effects_t0[mod.channel[ch].command](mod, ch); 674 | } else { 675 | mod.effects_t1[mod.channel[ch].command](mod, ch); 676 | } 677 | } 678 | 679 | // recalc note number from period 680 | if (mod.channel[ch].flags&2) { 681 | for(var np=0; np=mod.channel[ch].period) mod.channel[ch].note=np; 683 | mod.channel[ch].semitone=7; 684 | if (mod.channel[ch].period>=120) 685 | mod.channel[ch].semitone=mod.baseperiodtable[mod.channel[ch].note]-mod.baseperiodtable[mod.channel[ch].note+1]; 686 | } 687 | 688 | // recalc sample speed and apply finetune 689 | if ((mod.channel[ch].flags&1 || mod.flags&2) && mod.channel[ch].voiceperiod) 690 | mod.channel[ch].samplespeed= 691 | (mod.palclock ? 7093789.2 : 7159090.5)/(mod.channel[ch].voiceperiod*2) * mod.finetunetable[mod.sample[mod.channel[ch].sample].finetune+8] / mod.samplerate; 692 | 693 | // advance vibrato on each new tick 694 | if (mod.flags&1) { 695 | mod.channel[ch].vibratopos+=mod.channel[ch].vibratospeed; 696 | mod.channel[ch].vibratopos&=0x3f; 697 | } 698 | 699 | // mix channel to output 700 | och=och^(ch&1); 701 | f=0.0; 702 | if (mod.channel[ch].noteon) { 703 | if (mod.sample[mod.channel[ch].sample].length > mod.channel[ch].samplepos) 704 | f=(1.0/mod.channels) * 705 | (mod.sample[mod.channel[ch].sample].data[Math.floor(mod.channel[ch].samplepos)]*mod.channel[ch].volume)/64.0; 706 | outp[och]+=f; 707 | mod.channel[ch].samplepos+=mod.channel[ch].samplespeed; 708 | } 709 | if (s==0) mod.vu[ch]=Math.abs(f); 710 | 711 | // loop or end samples 712 | if (mod.channel[ch].noteon) { 713 | if (mod.sample[mod.channel[ch].sample].loopstart || mod.sample[mod.channel[ch].sample].looplength) { 714 | if (mod.channel[ch].samplepos >= (mod.sample[mod.channel[ch].sample].loopstart+mod.sample[mod.channel[ch].sample].looplength)) { 715 | mod.channel[ch].samplepos-=mod.sample[mod.channel[ch].sample].looplength; 716 | } 717 | } else { 718 | if (mod.channel[ch].samplepos >= mod.sample[mod.channel[ch].sample].length) { 719 | mod.channel[ch].noteon=0; 720 | } 721 | } 722 | } 723 | 724 | // clear channel flags 725 | mod.channel[ch].flags=0; 726 | } 727 | mod.offset++; 728 | mod.flags&=0x70; 729 | } 730 | 731 | // a more headphone-friendly stereo separation (aka. betterpaula) 732 | if (mod.separation) { 733 | t=outp[0]; 734 | if (mod.separation==2) { 735 | outp[0]=outp[0]*0.5 + outp[1]*0.5; 736 | outp[1]=outp[1]*0.5 + t*0.5; 737 | } else { 738 | outp[0]=outp[0]*0.65 + outp[1]*0.35; 739 | outp[1]=outp[1]*0.65 + t*0.35; 740 | } 741 | } 742 | bufs[0][s]=outp[0]; 743 | bufs[1][s]=outp[1]; 744 | } 745 | if (mod.delayfirst>0) mod.delayfirst--; //=false; 746 | mod.delayload=0; 747 | } 748 | 749 | 750 | 751 | // 752 | // tick 0 effect functions 753 | // 754 | Protracker.prototype.effect_t0_0=function(mod, ch) { // 0 arpeggio 755 | mod.channel[ch].arpeggio=mod.channel[ch].data; 756 | } 757 | Protracker.prototype.effect_t0_1=function(mod, ch) { // 1 slide up 758 | if (mod.channel[ch].data) mod.channel[ch].slidespeed=mod.channel[ch].data; 759 | } 760 | Protracker.prototype.effect_t0_2=function(mod, ch) { // 2 slide down 761 | if (mod.channel[ch].data) mod.channel[ch].slidespeed=mod.channel[ch].data; 762 | } 763 | Protracker.prototype.effect_t0_3=function(mod, ch) { // 3 slide to note 764 | if (mod.channel[ch].data) mod.channel[ch].slidetospeed=mod.channel[ch].data; 765 | } 766 | Protracker.prototype.effect_t0_4=function(mod, ch) { // 4 vibrato 767 | if (mod.channel[ch].data&0x0f && mod.channel[ch].data&0xf0) { 768 | mod.channel[ch].vibratodepth=(mod.channel[ch].data&0x0f); 769 | mod.channel[ch].vibratospeed=(mod.channel[ch].data&0xf0)>>4; 770 | } 771 | mod.channel[ch].voiceperiod+= 772 | (mod.channel[ch].vibratodepth/32)*mod.channel[ch].semitone*(mod.vibratotable[mod.channel[ch].vibratowave&3][mod.channel[ch].vibratopos]/127); 773 | mod.channel[ch].flags|=1; 774 | } 775 | Protracker.prototype.effect_t0_5=function(mod, ch) { // 5 776 | } 777 | Protracker.prototype.effect_t0_6=function(mod, ch) { // 6 778 | } 779 | Protracker.prototype.effect_t0_7=function(mod, ch) { // 7 780 | } 781 | Protracker.prototype.effect_t0_8=function(mod, ch) { // 8 unused, used for syncing 782 | mod.syncqueue.unshift(mod.channel[ch].data&0x0f); 783 | } 784 | Protracker.prototype.effect_t0_9=function(mod, ch) { // 9 set sample offset 785 | mod.channel[ch].samplepos=mod.channel[ch].data*256; 786 | } 787 | Protracker.prototype.effect_t0_a=function(mod, ch) { // a 788 | } 789 | Protracker.prototype.effect_t0_b=function(mod, ch) { // b pattern jump 790 | mod.breakrow=0; 791 | mod.patternjump=mod.channel[ch].data; 792 | mod.flags|=16; 793 | } 794 | Protracker.prototype.effect_t0_c=function(mod, ch) { // c set volume 795 | mod.channel[ch].volume=mod.channel[ch].data; 796 | } 797 | Protracker.prototype.effect_t0_d=function(mod, ch) { // d pattern break 798 | mod.breakrow=((mod.channel[ch].data&0xf0)>>4)*10 + (mod.channel[ch].data&0x0f); 799 | if (!(mod.flags&16)) mod.patternjump=mod.position+1; 800 | mod.flags|=16; 801 | } 802 | Protracker.prototype.effect_t0_e=function(mod, ch) { // e 803 | var i=(mod.channel[ch].data&0xf0)>>4; 804 | mod.effects_t0_e[i](mod, ch); 805 | } 806 | Protracker.prototype.effect_t0_f=function(mod, ch) { // f set speed 807 | if (mod.channel[ch].data > 32) { 808 | mod.bpm=mod.channel[ch].data; 809 | } else { 810 | if (mod.channel[ch].data) mod.speed=mod.channel[ch].data; 811 | } 812 | } 813 | 814 | 815 | 816 | // 817 | // tick 0 effect e functions 818 | // 819 | Protracker.prototype.effect_t0_e0=function(mod, ch) { // e0 filter on/off 820 | if (mod.channels > 4) return; // use only for 4ch amiga tunes 821 | if (mod.channel[ch].data&0x0f) { 822 | mod.lowpassNode.frequency.value=3275; 823 | mod.filter=true; 824 | } else { 825 | mod.lowpassNode.frequency.value=28867; 826 | mod.filter=false; 827 | } 828 | } 829 | Protracker.prototype.effect_t0_e1=function(mod, ch) { // e1 fine slide up 830 | mod.channel[ch].period-=mod.channel[ch].data&0x0f; 831 | if (mod.channel[ch].period < 113) mod.channel[ch].period=113; 832 | } 833 | Protracker.prototype.effect_t0_e2=function(mod, ch) { // e2 fine slide down 834 | mod.channel[ch].period+=mod.channel[ch].data&0x0f; 835 | if (mod.channel[ch].period > 856) mod.channel[ch].period=856; 836 | mod.channel[ch].flags|=1; 837 | } 838 | Protracker.prototype.effect_t0_e3=function(mod, ch) { // e3 set glissando 839 | } 840 | Protracker.prototype.effect_t0_e4=function(mod, ch) { // e4 set vibrato waveform 841 | mod.channel[ch].vibratowave=mod.channel[ch].data&0x07; 842 | } 843 | Protracker.prototype.effect_t0_e5=function(mod, ch) { // e5 set finetune 844 | } 845 | Protracker.prototype.effect_t0_e6=function(mod, ch) { // e6 loop pattern 846 | if (mod.channel[ch].data&0x0f) { 847 | if (mod.loopcount) { 848 | mod.loopcount--; 849 | } else { 850 | mod.loopcount=mod.channel[ch].data&0x0f; 851 | } 852 | if (mod.loopcount) mod.flags|=64; 853 | } else { 854 | mod.looprow=mod.row; 855 | } 856 | } 857 | Protracker.prototype.effect_t0_e7=function(mod, ch) { // e7 858 | } 859 | Protracker.prototype.effect_t0_e8=function(mod, ch) { // e8, use for syncing 860 | mod.syncqueue.unshift(mod.channel[ch].data&0x0f); 861 | } 862 | Protracker.prototype.effect_t0_e9=function(mod, ch) { // e9 863 | } 864 | Protracker.prototype.effect_t0_ea=function(mod, ch) { // ea fine volslide up 865 | mod.channel[ch].volume+=mod.channel[ch].data&0x0f; 866 | if (mod.channel[ch].volume > 64) mod.channel[ch].volume=64; 867 | } 868 | Protracker.prototype.effect_t0_eb=function(mod, ch) { // eb fine volslide down 869 | mod.channel[ch].volume-=mod.channel[ch].data&0x0f; 870 | if (mod.channel[ch].volume < 0) mod.channel[ch].volume=0; 871 | } 872 | Protracker.prototype.effect_t0_ec=function(mod, ch) { // ec 873 | } 874 | Protracker.prototype.effect_t0_ed=function(mod, ch) { // ed delay sample 875 | if (mod.tick==(mod.channel[ch].data&0x0f)) { 876 | // start note 877 | var p=mod.patterntable[mod.position]; 878 | var pp=mod.row*4*mod.channels + ch*4; 879 | n=(mod.pattern[p][pp]&0x0f)<<8 | mod.pattern[p][pp+1]; 880 | if (n) { 881 | mod.channel[ch].period=n; 882 | mod.channel[ch].voiceperiod=mod.channel[ch].period; 883 | mod.channel[ch].samplepos=0; 884 | if (mod.channel[ch].vibratowave>3) mod.channel[ch].vibratopos=0; 885 | mod.channel[ch].flags|=3; // recalc speed 886 | mod.channel[ch].noteon=1; 887 | } 888 | n=mod.pattern[p][pp+0]&0xf0 | mod.pattern[p][pp+2]>>4; 889 | if (n) { 890 | mod.channel[ch].sample=n-1; 891 | mod.channel[ch].volume=mod.sample[n-1].volume; 892 | } 893 | } 894 | } 895 | Protracker.prototype.effect_t0_ee=function(mod, ch) { // ee delay pattern 896 | mod.patterndelay=mod.channel[ch].data&0x0f; 897 | mod.patternwait=0; 898 | } 899 | Protracker.prototype.effect_t0_ef=function(mod, ch) { // ef 900 | } 901 | 902 | 903 | 904 | // 905 | // tick 1+ effect functions 906 | // 907 | Protracker.prototype.effect_t1_0=function(mod, ch) { // 0 arpeggio 908 | if (mod.channel[ch].data) { 909 | var apn=mod.channel[ch].note; 910 | if ((mod.tick%3)==1) apn+=mod.channel[ch].arpeggio>>4; 911 | if ((mod.tick%3)==2) apn+=mod.channel[ch].arpeggio&0x0f; 912 | if (apn>=0 && apn <= mod.baseperiodtable.length) 913 | mod.channel[ch].voiceperiod = mod.baseperiodtable[apn]; 914 | mod.channel[ch].flags|=1; 915 | } 916 | } 917 | Protracker.prototype.effect_t1_1=function(mod, ch) { // 1 slide up 918 | mod.channel[ch].period-=mod.channel[ch].slidespeed; 919 | if (mod.channel[ch].period<113) mod.channel[ch].period=113; 920 | mod.channel[ch].flags|=3; // recalc speed 921 | } 922 | Protracker.prototype.effect_t1_2=function(mod, ch) { // 2 slide down 923 | mod.channel[ch].period+=mod.channel[ch].slidespeed; 924 | if (mod.channel[ch].period>856) mod.channel[ch].period=856; 925 | mod.channel[ch].flags|=3; // recalc speed 926 | } 927 | Protracker.prototype.effect_t1_3=function(mod, ch) { // 3 slide to note 928 | if (mod.channel[ch].period < mod.channel[ch].slideto) { 929 | mod.channel[ch].period+=mod.channel[ch].slidetospeed; 930 | if (mod.channel[ch].period > mod.channel[ch].slideto) 931 | mod.channel[ch].period=mod.channel[ch].slideto; 932 | } 933 | if (mod.channel[ch].period > mod.channel[ch].slideto) { 934 | mod.channel[ch].period-=mod.channel[ch].slidetospeed; 935 | if (mod.channel[ch].period>4); 964 | if (mod.channel[ch].volume>64) mod.channel[ch].volume=64; 965 | } 966 | if (!(mod.channel[ch].data&0xf0)) { 967 | // x is zero, slide down 968 | mod.channel[ch].volume-=(mod.channel[ch].data&0x0f); 969 | if (mod.channel[ch].volume<0) mod.channel[ch].volume=0; 970 | } 971 | } 972 | Protracker.prototype.effect_t1_b=function(mod, ch) { // b pattern jump 973 | } 974 | Protracker.prototype.effect_t1_c=function(mod, ch) { // c set volume 975 | } 976 | Protracker.prototype.effect_t1_d=function(mod, ch) { // d pattern break 977 | } 978 | Protracker.prototype.effect_t1_e=function(mod, ch) { // e 979 | var i=(mod.channel[ch].data&0xf0)>>4; 980 | mod.effects_t1_e[i](mod, ch); 981 | } 982 | Protracker.prototype.effect_t1_f=function(mod, ch) { // f 983 | } 984 | 985 | 986 | 987 | // 988 | // tick 1+ effect e functions 989 | // 990 | Protracker.prototype.effect_t1_e0=function(mod, ch) { // e0 991 | } 992 | Protracker.prototype.effect_t1_e1=function(mod, ch) { // e1 993 | } 994 | Protracker.prototype.effect_t1_e2=function(mod, ch) { // e2 995 | } 996 | Protracker.prototype.effect_t1_e3=function(mod, ch) { // e3 997 | } 998 | Protracker.prototype.effect_t1_e4=function(mod, ch) { // e4 999 | } 1000 | Protracker.prototype.effect_t1_e5=function(mod, ch) { // e5 1001 | } 1002 | Protracker.prototype.effect_t1_e6=function(mod, ch) { // e6 1003 | } 1004 | Protracker.prototype.effect_t1_e7=function(mod, ch) { // e7 1005 | } 1006 | Protracker.prototype.effect_t1_e8=function(mod, ch) { // e8 1007 | } 1008 | Protracker.prototype.effect_t1_e9=function(mod, ch) { // e9 retrig sample 1009 | if (mod.tick%(mod.channel[ch].data&0x0f)==0) 1010 | mod.channel[ch].samplepos=0; 1011 | } 1012 | Protracker.prototype.effect_t1_ea=function(mod, ch) { // ea 1013 | } 1014 | Protracker.prototype.effect_t1_eb=function(mod, ch) { // eb 1015 | } 1016 | Protracker.prototype.effect_t1_ec=function(mod, ch) { // ec cut sample 1017 | if (mod.tick==(mod.channel[ch].data&0x0f)) 1018 | mod.channel[ch].volume=0; 1019 | } 1020 | Protracker.prototype.effect_t1_ed=function(mod, ch) { // ed delay sample 1021 | mod.effect_t0_ed(mod, ch); 1022 | } 1023 | Protracker.prototype.effect_t1_ee=function(mod, ch) { // ee 1024 | } 1025 | Protracker.prototype.effect_t1_ef=function(mod, ch) { // ef 1026 | } 1027 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Phaser Logo](http://www.photonstorm.com/wp-content/uploads/2013/09/phaser_10_release.jpg) 2 | 3 | # Phaser Plugins 4 | 5 | This is the Phaser Plugins repository. 6 | 7 | If you're looking for the framework itself, please go here: https://github.com/photonstorm/phaser 8 | 9 | 10 | ## Contributing 11 | 12 | - If you find a bug in a Plugin then please report it on [GitHub Issues][issues] or our [Support Forum][forum]. 13 | 14 | - You can issue Pull Requests for new plugins or fixes to existing ones against the `master` branch. 15 | 16 | - If you have a Plugin request, or have written a game or demo that shows a Phaser Plugin in use, then please get in touch. We'd love to hear from you! Either post to our [forum][forum] or email: rich@photonstorm.com 17 | 18 | 19 | ## Bugs? 20 | 21 | Please add them to the [Issue Tracker][issues] with as much info as possible, especially source code demonstrating the issue. 22 | 23 | 24 | ## About Phaser 25 | 26 | Phaser is a fast, free and fun open source game framework for making desktop and mobile browser HTML5 games. It uses [Pixi.js](https://github.com/GoodBoyDigital/pixi.js/) internally for fast 2D Canvas and WebGL rendering. 27 | 28 | By Richard Davey, [Photon Storm](http://www.photonstorm.com) 29 | 30 | * View the [Official Website](http://phaser.io) 31 | * Follow on [Twitter](https://twitter.com/photonstorm) 32 | * Join the [Forum](http://www.html5gamedevs.com/forum/14-phaser/) 33 | * Source code for 300+ [Phaser Examples](https://github.com/photonstorm/phaser-examples) or [browse them online](http://examples.phaser.io) 34 | * Read the [documentation online](http://docs.phaser.io) 35 | * Join our [#phaserio IRC channel](http://www.html5gamedevs.com/topic/4470-official-phaserio-irc-channel-phaserio-on-freenode/) on freenode 36 | * Subscribe to the [Phaser Newsletter](https://confirmsubscription.com/h/r/369DE48E3E86AF1E) and we'll email you when new versions are released. 37 | 38 | 39 | ## License 40 | 41 | Phaser and all examples are released under the [MIT License](http://opensource.org/licenses/MIT). This does not include any assets (art, music, sounds). However it is up to the individual Plugin authors which license they release under, so please check for a `license.txt` file in the plugin folder. If one is not present you may assume the MIT License. 42 | 43 | [issues]: https://github.com/photonstorm/phaser-plugins/issues 44 | [contribute]: https://github.com/photonstorm/phaser/blob/master/CONTRIBUTING.md 45 | [phaser]: https://github.com/photonstorm/phaser-plugins 46 | [forum]: http://www.html5gamedevs.com/forum/14-phaser/ 47 | 48 | [![Analytics](https://ga-beacon.appspot.com/UA-44006568-2/phaser/index)](https://github.com/igrigorik/ga-beacon) 49 | -------------------------------------------------------------------------------- /SamplePlugin/SamplePlugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A Sample Plugin demonstrating how to hook into the Phaser plugin system. 3 | * @class Phaser.Plugin.SamplePlugin 4 | */ 5 | Phaser.Plugin.SamplePlugin = function (game, parent) { 6 | 7 | Phaser.Plugin.call(this, game, parent); 8 | 9 | this.sprite = null; 10 | 11 | }; 12 | 13 | // Extends the Phaser.Plugin template, setting up values we need 14 | Phaser.Plugin.SamplePlugin.prototype = Object.create(Phaser.Plugin.prototype); 15 | Phaser.Plugin.SamplePlugin.prototype.constructor = Phaser.Plugin.SamplePlugin; 16 | 17 | /** 18 | * Add a Sprite reference to this Plugin. 19 | * All this plugin does is move the Sprite across the screen slowly. 20 | * @type {Phaser.Sprite} 21 | */ 22 | Phaser.Plugin.SamplePlugin.prototype.addSprite = function (sprite) { 23 | 24 | this.sprite = sprite; 25 | 26 | }; 27 | 28 | /** 29 | * This is run when the plugins update during the core game loop. 30 | */ 31 | Phaser.Plugin.SamplePlugin.prototype.update = function () { 32 | 33 | if (this.sprite) 34 | { 35 | this.sprite.x += 0.5; 36 | } 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /SaveCPU/SaveCPU.js: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2014 Ivanix Mobile LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | /* 27 | * Plugin: SaveCPU 28 | * Author: Ivanix @ Ivanix Mobile LLC 29 | * Purpose: Reduce CPU usage caused from redudant rendering 30 | * on idle or static display scenes 31 | * reduce fps for casual/puzzle games 32 | * 33 | * 34 | * Configurable Properties: 35 | * 36 | * [renderOnFPS] 37 | * Constrains maximum FPS to value set. 38 | * Reasonable values from 0 to 60 39 | * Default value 30 40 | * Set value to 0 disable rendering based on FPS 41 | * and use methods described below. 42 | * 43 | * [renderOnPointerChange] 44 | * Render when pointer movement detected. 45 | * Possible values "true" or "false" 46 | * Default: false 47 | * Note that renderOnFPS must be set to 0 48 | * 49 | * 50 | * Callable Methods: 51 | * 52 | * [forceRender()] 53 | * Forces rendering during core game loop 54 | * Can be called independently or in tandem with above properties. 55 | * Should be called inside update function. 56 | * @class Phaser.Plugin.SaveCPU 57 | */ 58 | 59 | /*global 60 | Phaser: true, 61 | window: true 62 | */ 63 | /*jslint nomen: true */ 64 | 65 | Phaser.Plugin.SaveCPU = function (game, parent) { 66 | 'use strict'; 67 | Phaser.Plugin.call(this, game, parent); 68 | 69 | }; 70 | Phaser.Plugin.SaveCPU.prototype = Object.create(Phaser.Plugin.prototype); 71 | Phaser.Plugin.SaveCPU.constructor = Phaser.Plugin.SaveCPU; 72 | 73 | Phaser.Plugin.SaveCPU.prototype.init = function () { 74 | 'use strict'; 75 | var thisObj; 76 | 77 | this.__defineSetter__("renderOnFPS", function(v) { 78 | this._renderOnFPS = v; 79 | this._tsdiff = (1000 / v); 80 | }); 81 | this.__defineGetter__("renderOnFPS", function() { 82 | return this._renderOnFPS; 83 | }); 84 | this._tsdiff = 0; 85 | 86 | // fps default 87 | this.renderOnFPS = 30; 88 | this.renderOnPointerChange = false; 89 | this.renderDirty = true; 90 | 91 | if(this.game.updateRender) { 92 | this._init1(); 93 | } else { 94 | this._init0(); 95 | } 96 | 97 | this.fpsDirty = 0; 98 | this.hrts = 0; 99 | 100 | thisObj = this; 101 | window.requestAnimationFrame(function(hrts) { 102 | thisObj._trackFPS(hrts); 103 | }); 104 | }; 105 | Phaser.Plugin.SaveCPU.prototype._init0 = function () { 106 | this.renderType = this.game.renderType; 107 | this.switchRender = this._switchRender0; 108 | }; 109 | Phaser.Plugin.SaveCPU.prototype._init1 = function () { 110 | var game = this.game; 111 | 112 | game.updateRenderReal = game.updateRender; 113 | game.updateRenderNull = function() {}; 114 | this.switchRender = this._switchRender1; 115 | }; 116 | Phaser.Plugin.SaveCPU.prototype._trackFPS = function (hrts) { 117 | var thisObj, diff; 118 | 119 | diff = hrts - this.hrts; 120 | if (diff > this._tsdiff) { 121 | this.fpsDirty = true; 122 | this.hrts = hrts; 123 | } 124 | 125 | thisObj = this; 126 | window.requestAnimationFrame(function(hrts) { 127 | thisObj._trackFPS(hrts); 128 | }); 129 | 130 | }; 131 | Phaser.Plugin.SaveCPU.prototype._switchRender0 = function () { 132 | 'use strict'; 133 | var game = this.game; 134 | if (this.renderDirty) { 135 | game.renderType = this.renderType; 136 | } else { 137 | game.renderType = Phaser.HEADLESS; 138 | } 139 | this.renderDirty = false; 140 | }; 141 | Phaser.Plugin.SaveCPU.prototype._switchRender1 = function () { 142 | 'use strict'; 143 | var game = this.game; 144 | if (this.renderDirty) { 145 | game.updateRender = game.updateRenderReal; 146 | } else { 147 | game.updateRender = game.updateRenderNull; 148 | } 149 | }; 150 | Phaser.Plugin.SaveCPU.prototype.forceRender = function () { 151 | 'use strict'; 152 | this.renderDirty = true; 153 | }; 154 | Phaser.Plugin.SaveCPU.prototype._forceRenderOnPointerChange = function () { 155 | 'use strict'; 156 | if(!this.renderOnPointerChange) { 157 | return false; 158 | } 159 | var input = this.game.input; 160 | 161 | if (input.oldx !== input.x || input.oldy !== input.y) { 162 | this.forceRender(); 163 | input.oldx = input.x; 164 | input.oldy = input.y; 165 | } 166 | if (input.oldDown !== input.activePointer.isDown) { 167 | this.forceRender(); 168 | input.oldDown = input.activePointer.isDown; 169 | } 170 | }; 171 | Phaser.Plugin.SaveCPU.prototype._forceRenderOnFPS = function () { 172 | 'use strict'; 173 | 174 | if(this.renderOnFPS && this.fpsDirty) { 175 | this.fpsDirty = false; 176 | this.forceRender(); 177 | return true; 178 | } else { 179 | return false; 180 | } 181 | }; 182 | Phaser.Plugin.SaveCPU.prototype.postUpdate = function () { 183 | 'use strict'; 184 | if (this.renderDirty || this._forceRenderOnFPS()|| this._forceRenderOnPointerChange()) { 185 | this.switchRender(); 186 | return; 187 | } 188 | }; 189 | Phaser.Plugin.SaveCPU.prototype.postRender= function () { 190 | 'use strict'; 191 | if (this.renderDirty) { 192 | this.renderDirty = false; 193 | this.switchRender(); 194 | } 195 | }; 196 | 197 | 198 | -------------------------------------------------------------------------------- /ScreenShake/ScreenShake.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Plugin to make screen shake FX (makes number of short camera movements). 5 | * 6 | * Usage: 7 | * in main create function: 8 | * game.plugins.screenShake = game.plugins.add(Phaser.Plugin.ScreenShake); 9 | * 10 | * in function where need to call shake FX: 11 | * game.plugins.screenShake.setup({ //if need to replace default plugin settings 12 | * shakeX: true, 13 | * shakeY: false 14 | * }); 15 | * this.game.plugins.screenShake.shake(10); //pass shake count value 16 | * 17 | * 18 | * 19 | * @author Dmitry Maslov 20 | * @copyright 2014 Dmitry Maslov 21 | * @license http://choosealicense.com/licenses/mit 22 | * 23 | */ 24 | Phaser.Plugin.ScreenShake = function(game, parent){ 25 | Phaser.Plugin.call(this, game, parent); 26 | 27 | //settings by default 28 | this._settings = { 29 | shakesCount: 0, 30 | shakeX: true, 31 | shakeY: true, 32 | sensCoef: 0.5 33 | }; 34 | this.game.camera.bounds = null; 35 | 36 | /** 37 | * screen shake FX. 38 | */ 39 | this._moveCamera = function(){ 40 | if(this._settings.shakesCount > 0){ 41 | var sens = this._settings.shakesCount * this._settings.sensCoef; 42 | 43 | if(this._settings.shakesCount % 2){ 44 | this.game.camera.x += this._settings.shakeX ? sens : 0; 45 | this.game.camera.y += this._settings.shakeY ? sens : 0; 46 | } 47 | else{ 48 | this.game.camera.x -= this._settings.shakeX ? sens : 0; 49 | this.game.camera.y -= this._settings.shakeY ? sens : 0; 50 | } 51 | 52 | this._settings.shakesCount--; 53 | 54 | if(this._settings.shakesCount === 0){ 55 | this.game.camera.setPosition(0, 0); 56 | } 57 | } 58 | }; 59 | }; 60 | 61 | Phaser.Plugin.ScreenShake.prototype = Object.create(Phaser.Plugin.prototype); 62 | Phaser.Plugin.ScreenShake.prototype.constructor = Phaser.Plugin.ScreenShake; 63 | 64 | 65 | /** 66 | * Change default settings object values with passed object value. 67 | * 68 | * @method Phaser.Plugin.ScreenShake#setup 69 | * @param {object} [obj] - Passed object to merge 70 | */ 71 | Phaser.Plugin.ScreenShake.prototype.setup = function(obj){ 72 | this._settings = Phaser.Utils.extend(false, this._settings, obj); 73 | }; 74 | 75 | 76 | /** 77 | * Pass value of count shakes. 78 | * 79 | * @method Phaser.Plugin.ScreenShake#shake 80 | * @param {number} [count] - Value of count shakes 81 | */ 82 | Phaser.Plugin.ScreenShake.prototype.shake = function(count){ 83 | this._settings.shakesCount = count; 84 | }; 85 | 86 | Phaser.Plugin.ScreenShake.prototype.update = function(){ 87 | this._moveCamera(); 88 | }; 89 | -------------------------------------------------------------------------------- /TilemapWalker/TilemapWalker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Richard Davey 3 | * @copyright 2014 Photon Storm Ltd. 4 | * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} 5 | */ 6 | 7 | /** 8 | * Creates an object that is placed within a layer of a Phaser.Tilemap and can be moved around and rotated using the direction commands. 9 | * 10 | * @class Phaser.Plugin.TilemapWalker 11 | * @constructor 12 | * @param {Phaser.Game} game - Game reference to the currently running game. 13 | * @param {Phaser.Tilemap} map - A reference to the Tilemap this TilemapWalker belongs to. 14 | * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to this.currentLayer. 15 | * @param {number} x - X position of the top left of the area to copy (given in tiles, not pixels) 16 | * @param {number} y - Y position of the top left of the area to copy (given in tiles, not pixels) 17 | * @return {Phaser.Plugin.TilemapWalker} 18 | */ 19 | Phaser.Plugin.TilemapWalker = function (game, map, layer, x, y) { 20 | 21 | /** 22 | * @property {Phaser.Game} game - A reference to the currently running Game. 23 | */ 24 | this.game = game; 25 | 26 | /** 27 | * @property {Phaser.Tilemap} map - A reference to the Tilemap this TilemapWalker belongs to. 28 | */ 29 | this.map = map; 30 | 31 | /** 32 | * @property {number} locationLayer - The current layer of the location marker. 33 | */ 34 | this.locationLayer = map.getLayer(layer); 35 | 36 | /** 37 | * @property {Phaser.Point} location - The current marker location. You can move the marker with the movement methods. 38 | */ 39 | this.location = new Phaser.Point(); 40 | 41 | /** 42 | * @property {number} facing - The direction the location marker is facing. You can rotate it using the turn and face methods. 43 | * @default 44 | */ 45 | this.facing = Phaser.Tilemap.NORTH; 46 | 47 | /** 48 | * @property {boolean} collides - Does the TilemapWalker collide with the tiles in the map set for collision? If so it cannot move through them. 49 | * @default 50 | */ 51 | this.collides = true; 52 | 53 | /** 54 | * @property {array} history - An array containing a history of movements through the map. 55 | */ 56 | this.history = []; 57 | 58 | // TODO: History limit, History scan, record how many times walker has been on a tile before 59 | 60 | if (typeof x !== 'undefined' && typeof y !== 'undefined') 61 | { 62 | this.setLocation(x, y); 63 | } 64 | 65 | return this; 66 | 67 | }; 68 | 69 | Phaser.Plugin.TilemapWalker.prototype = { 70 | 71 | /** 72 | * Sets the location marker to the given x/y coordinates within the map. 73 | * Once set you can move the marker around via the movement and turn methods. 74 | * 75 | * @method Phaser.Tilemap#setLocation 76 | * @param {number} x - X position of the top left of the area to copy (given in tiles, not pixels) 77 | * @param {number} y - Y position of the top left of the area to copy (given in tiles, not pixels) 78 | * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to this.currentLayer. 79 | * @return {boolean} True if the location could be set, otherwise false. 80 | */ 81 | setLocation: function (x, y, layer) { 82 | 83 | if (this.checkTile(x, y)) 84 | { 85 | this.location.set(x, y); 86 | this.history.push( { x: x, y: y }); 87 | 88 | return true; 89 | } 90 | 91 | return false; 92 | 93 | }, 94 | 95 | /** 96 | * Checks if the given x/y coordinate has a tile into which we can move. 97 | * 98 | * @method Phaser.Tilemap#checkTile 99 | * @param {number} x - X position of the top left of the area to copy (given in tiles, not pixels) 100 | * @param {number} y - Y position of the top left of the area to copy (given in tiles, not pixels) 101 | * @return {boolean} True if the location can be moved into, false if not. 102 | */ 103 | checkTile: function (x, y) { 104 | 105 | if (this.map.hasTile(x, y, this.locationLayer)) 106 | { 107 | if (this.collides) 108 | { 109 | var tile = this.map.getTile(x, y, this.locationLayer); 110 | 111 | if (tile && tile.collides) 112 | { 113 | return false; 114 | } 115 | } 116 | 117 | return true; 118 | } 119 | 120 | return false; 121 | 122 | }, 123 | 124 | updateLocation: function (x, y) { 125 | 126 | if (this.checkTile(this.location.x + x, this.location.y + y, this.locationLayer)) 127 | { 128 | this.location.set(this.location.x + x, this.location.y + y); 129 | this.history.push( { x: x, y: y }); 130 | 131 | return true; 132 | } 133 | 134 | return false; 135 | 136 | }, 137 | 138 | moveForward: function () { 139 | 140 | if (this.facing === Phaser.Tilemap.NORTH) 141 | { 142 | return this.updateLocation(0, -1); 143 | } 144 | else if (this.facing === Phaser.Tilemap.EAST) 145 | { 146 | return this.updateLocation(1, 0); 147 | } 148 | else if (this.facing === Phaser.Tilemap.SOUTH) 149 | { 150 | return this.updateLocation(0, 1); 151 | } 152 | else if (this.facing === Phaser.Tilemap.WEST) 153 | { 154 | return this.updateLocation(-1, 0); 155 | } 156 | 157 | }, 158 | 159 | moveBackward: function () { 160 | 161 | if (this.facing === Phaser.Tilemap.NORTH) 162 | { 163 | return this.updateLocation(0, 1); 164 | } 165 | else if (this.facing === Phaser.Tilemap.EAST) 166 | { 167 | return this.updateLocation(-1, 0); 168 | } 169 | else if (this.facing === Phaser.Tilemap.SOUTH) 170 | { 171 | return this.updateLocation(0, -1); 172 | } 173 | else if (this.facing === Phaser.Tilemap.WEST) 174 | { 175 | return this.updateLocation(1, 0); 176 | } 177 | 178 | }, 179 | 180 | moveLeft: function () { 181 | 182 | if (this.facing === Phaser.Tilemap.NORTH) 183 | { 184 | return this.updateLocation(-1, 0); 185 | } 186 | else if (this.facing === Phaser.Tilemap.EAST) 187 | { 188 | return this.updateLocation(0, -1); 189 | } 190 | else if (this.facing === Phaser.Tilemap.SOUTH) 191 | { 192 | return this.updateLocation(1, 0); 193 | } 194 | else if (this.facing === Phaser.Tilemap.WEST) 195 | { 196 | return this.updateLocation(0, 1); 197 | } 198 | 199 | }, 200 | 201 | moveRight: function () { 202 | 203 | if (this.facing === Phaser.Tilemap.NORTH) 204 | { 205 | return this.updateLocation(1, 0); 206 | } 207 | else if (this.facing === Phaser.Tilemap.EAST) 208 | { 209 | return this.updateLocation(0, 1); 210 | } 211 | else if (this.facing === Phaser.Tilemap.SOUTH) 212 | { 213 | return this.updateLocation(-1, 0); 214 | } 215 | else if (this.facing === Phaser.Tilemap.WEST) 216 | { 217 | return this.updateLocation(0, -1); 218 | } 219 | 220 | }, 221 | 222 | turnLeft: function () { 223 | 224 | this.facing--; 225 | 226 | if (this.facing === -1) 227 | { 228 | this.facing = 3; 229 | } 230 | 231 | }, 232 | 233 | turnRight: function () { 234 | 235 | this.facing++; 236 | 237 | if (this.facing === 4) 238 | { 239 | this.facing = 0; 240 | } 241 | 242 | }, 243 | 244 | putTile: function (index) { 245 | 246 | this.map.putTile(index, this.location.x, this.location.y, this.locationLayer); 247 | 248 | }, 249 | 250 | getTileFromLocation: function (x, y) { 251 | 252 | return this.map.getTile(this.location.x + x, this.location.y + y, this.locationLayer, true); 253 | 254 | }, 255 | 256 | getTile: function () { 257 | 258 | return this.map.getTile(this.location.x, this.location.y, this.locationLayer, true); 259 | 260 | }, 261 | 262 | getTiles: function (width, height, center) { 263 | 264 | var startX; 265 | var startX; 266 | var endX; 267 | var endY; 268 | var incX; 269 | var incY; 270 | 271 | var hw = Math.floor(width / 2); 272 | var hh = Math.floor(height / 2); 273 | 274 | // For now we assume that center = bottom middle tile 275 | 276 | if (center) 277 | { 278 | startX = this.location.x - hw; 279 | endX = this.location.x + hw; 280 | incX = 1; 281 | 282 | startY = this.location.y - hh; 283 | endY = this.location.y + hh; 284 | incY = 1; 285 | } 286 | else 287 | { 288 | if (this.facing === Phaser.Tilemap.NORTH) 289 | { 290 | startX = this.location.x - hw; 291 | endX = this.location.x + hw; 292 | incX = 1; 293 | 294 | // bottom middle align 295 | startY = this.location.y - (height - 1); 296 | endY = this.location.y; 297 | incY = 1; 298 | } 299 | else if (this.facing === Phaser.Tilemap.EAST) 300 | { 301 | startX = this.location.x; 302 | endX = this.location.x + (width - 1); 303 | incX = 1; 304 | 305 | // bottom middle align 306 | startY = this.location.y - hh; 307 | endY = this.location.y + hh; 308 | incY = 1; 309 | } 310 | else if (this.facing === Phaser.Tilemap.SOUTH) 311 | { 312 | startX = this.location.x - hw; 313 | endX = this.location.x + hw; 314 | incX = 1; 315 | 316 | // bottom middle align 317 | startY = this.location.y; 318 | endY = this.location.y + (height - 1); 319 | incY = 1; 320 | } 321 | else if (this.facing === Phaser.Tilemap.WEST) 322 | { 323 | startX = this.location.x - (width - 1); 324 | endX = this.location.x; 325 | incX = 1; 326 | 327 | // bottom middle align 328 | startY = this.location.y - hh; 329 | endY = this.location.y + hh; 330 | incY = 1; 331 | } 332 | } 333 | 334 | var output = []; 335 | var row = []; 336 | 337 | for (var y = startY; y <= endY; y += incY) 338 | { 339 | row = []; 340 | 341 | for (var x = startX; x <= endX; x += incX) 342 | { 343 | var tile = this.map.getTile(x, y, this.locationLayer, true); 344 | 345 | if (tile) 346 | { 347 | row.push(tile.index); 348 | } 349 | else 350 | { 351 | // out of bounds, so block it off 352 | row.push(0); 353 | } 354 | } 355 | 356 | output.push(row); 357 | } 358 | 359 | // console.log(printMatrix(output)); 360 | 361 | if (this.facing === Phaser.Tilemap.EAST) 362 | { 363 | output = rotateMatrix(output, 90); 364 | } 365 | else if (this.facing === Phaser.Tilemap.SOUTH) 366 | { 367 | output = rotateMatrix(output, 180); 368 | } 369 | else if (this.facing === Phaser.Tilemap.WEST) 370 | { 371 | output = rotateMatrix(output, -90); 372 | } 373 | 374 | // console.log('rotate'); 375 | // console.log(printMatrix(output)); 376 | 377 | return output; 378 | 379 | }, 380 | 381 | getMiniMap: function (width, height) { 382 | 383 | var startX; 384 | var startX; 385 | var endX; 386 | var endY; 387 | var diff = 0; 388 | 389 | var hw = Math.floor(width / 2); 390 | var hh = Math.floor(height / 2); 391 | 392 | startX = this.location.x - hw; 393 | endX = this.location.x + hw; 394 | 395 | startY = this.location.y - hh; 396 | endY = this.location.y + hh; 397 | 398 | // Bounds 399 | if (startX < 0) 400 | { 401 | endX += Math.abs(startX); 402 | startX = 0; 403 | } 404 | 405 | if (endX > this.map.width) 406 | { 407 | diff = endX - this.map.width; 408 | endX = this.map.width; 409 | startX -= diff; 410 | } 411 | 412 | if (startY < 0) 413 | { 414 | endY += Math.abs(startY); 415 | startY = 0; 416 | } 417 | 418 | if (endY > this.map.height) 419 | { 420 | diff = endY - this.map.height; 421 | endY = this.map.height; 422 | startY -= diff; 423 | } 424 | 425 | var output = []; 426 | var row = []; 427 | var walkerPosition = { x: 0, y: 0 }; 428 | 429 | for (var y = startY; y < endY; y++) 430 | { 431 | row = []; 432 | 433 | for (var x = startX; x < endX; x++) 434 | { 435 | // Walker? 436 | if (x === this.location.x && y === this.location.y) 437 | { 438 | walkerPosition.x = row.length; 439 | walkerPosition.y = output.length; 440 | } 441 | 442 | var tile = this.map.getTile(x, y, this.locationLayer, true); 443 | 444 | if (tile) 445 | { 446 | row.push(tile.index); 447 | } 448 | else 449 | { 450 | // out of bounds, so block it off 451 | row.push(0); 452 | } 453 | } 454 | 455 | output.push(row); 456 | } 457 | 458 | // console.log(printMatrix(output)); 459 | 460 | return { walker: walkerPosition, tiles: output }; 461 | 462 | }, 463 | 464 | getTileAhead: function (distance) { 465 | 466 | if (typeof distance === 'undefined') { distance = 1; } 467 | 468 | if (this.facing === Phaser.Tilemap.NORTH) 469 | { 470 | return this.getTileFromLocation(0, -distance); 471 | } 472 | else if (this.facing === Phaser.Tilemap.EAST) 473 | { 474 | return this.getTileFromLocation(distance, 0); 475 | } 476 | else if (this.facing === Phaser.Tilemap.SOUTH) 477 | { 478 | return this.getTileFromLocation(0, distance); 479 | } 480 | else if (this.facing === Phaser.Tilemap.WEST) 481 | { 482 | return this.getTileFromLocation(-distance, 0); 483 | } 484 | 485 | }, 486 | 487 | getTileAheadLeft: function (distance) { 488 | 489 | if (typeof distance === 'undefined') { distance = 1; } 490 | 491 | if (this.facing === Phaser.Tilemap.NORTH) 492 | { 493 | return this.getTileFromLocation(-distance, -distance); 494 | } 495 | else if (this.facing === Phaser.Tilemap.EAST) 496 | { 497 | return this.getTileFromLocation(distance, -distance); 498 | } 499 | else if (this.facing === Phaser.Tilemap.SOUTH) 500 | { 501 | return this.getTileFromLocation(distance, distance); 502 | } 503 | else if (this.facing === Phaser.Tilemap.WEST) 504 | { 505 | return this.getTileFromLocation(-distance, distance); 506 | } 507 | 508 | }, 509 | 510 | getTileAheadRight: function (distance) { 511 | 512 | if (typeof distance === 'undefined') { distance = 1; } 513 | 514 | if (this.facing === Phaser.Tilemap.NORTH) 515 | { 516 | return this.getTileFromLocation(distance, -distance); 517 | } 518 | else if (this.facing === Phaser.Tilemap.EAST) 519 | { 520 | return this.getTileFromLocation(distance, distance); 521 | } 522 | else if (this.facing === Phaser.Tilemap.SOUTH) 523 | { 524 | return this.getTileFromLocation(-distance, distance); 525 | } 526 | else if (this.facing === Phaser.Tilemap.WEST) 527 | { 528 | return this.getTileFromLocation(-distance, -distance); 529 | } 530 | 531 | }, 532 | 533 | getTileBehind: function (distance) { 534 | 535 | if (typeof distance === 'undefined') { distance = 1; } 536 | 537 | if (this.facing === Phaser.Tilemap.NORTH) 538 | { 539 | return this.getTileFromLocation(0, distance); 540 | } 541 | else if (this.facing === Phaser.Tilemap.EAST) 542 | { 543 | return this.getTileFromLocation(-distance, 0); 544 | } 545 | else if (this.facing === Phaser.Tilemap.SOUTH) 546 | { 547 | return this.getTileFromLocation(0, -distance); 548 | } 549 | else if (this.facing === Phaser.Tilemap.WEST) 550 | { 551 | return this.getTileFromLocation(distance, 0); 552 | } 553 | 554 | }, 555 | 556 | getTileBehindLeft: function (distance) { 557 | 558 | if (typeof distance === 'undefined') { distance = 1; } 559 | 560 | if (this.facing === Phaser.Tilemap.NORTH) 561 | { 562 | return this.getTileFromLocation(-distance, distance); 563 | } 564 | else if (this.facing === Phaser.Tilemap.EAST) 565 | { 566 | return this.getTileFromLocation(-distance, -distance); 567 | } 568 | else if (this.facing === Phaser.Tilemap.SOUTH) 569 | { 570 | return this.getTileFromLocation(distance, -distance); 571 | } 572 | else if (this.facing === Phaser.Tilemap.WEST) 573 | { 574 | return this.getTileFromLocation(distance, distance); 575 | } 576 | 577 | }, 578 | 579 | getTileBehindRight: function (distance) { 580 | 581 | if (typeof distance === 'undefined') { distance = 1; } 582 | 583 | if (this.facing === Phaser.Tilemap.NORTH) 584 | { 585 | return this.getTileFromLocation(distance, distance); 586 | } 587 | else if (this.facing === Phaser.Tilemap.EAST) 588 | { 589 | return this.getTileFromLocation(-distance, distance); 590 | } 591 | else if (this.facing === Phaser.Tilemap.SOUTH) 592 | { 593 | return this.getTileFromLocation(-distance, -distance); 594 | } 595 | else if (this.facing === Phaser.Tilemap.WEST) 596 | { 597 | return this.getTileFromLocation(distance, -distance); 598 | } 599 | 600 | }, 601 | 602 | getTileLeft: function (distance) { 603 | 604 | if (typeof distance === 'undefined') { distance = 1; } 605 | 606 | if (this.facing === Phaser.Tilemap.NORTH) 607 | { 608 | return this.getTileFromLocation(-distance, 0); 609 | } 610 | else if (this.facing === Phaser.Tilemap.EAST) 611 | { 612 | return this.getTileFromLocation(0, -distance); 613 | } 614 | else if (this.facing === Phaser.Tilemap.SOUTH) 615 | { 616 | return this.getTileFromLocation(distance, 0); 617 | } 618 | else if (this.facing === Phaser.Tilemap.WEST) 619 | { 620 | return this.getTileFromLocation(0, distance); 621 | } 622 | 623 | }, 624 | 625 | getTileRight: function (distance) { 626 | 627 | if (typeof distance === 'undefined') { distance = 1; } 628 | 629 | if (this.facing === Phaser.Tilemap.NORTH) 630 | { 631 | return this.getTileFromLocation(distance, 0); 632 | } 633 | else if (this.facing === Phaser.Tilemap.EAST) 634 | { 635 | return this.getTileFromLocation(0, distance); 636 | } 637 | else if (this.facing === Phaser.Tilemap.SOUTH) 638 | { 639 | return this.getTileFromLocation(-distance, 0); 640 | } 641 | else if (this.facing === Phaser.Tilemap.WEST) 642 | { 643 | return this.getTileFromLocation(0, -distance); 644 | } 645 | 646 | } 647 | 648 | }; 649 | 650 | // Original from http://jsfiddle.net/MrPolywhirl/NH42z/ - tided up and de-globalised by Richard Davey 651 | var rotateMatrix = function (matrix, direction) { 652 | 653 | direction = ((direction % 360) + 360) % 360; 654 | 655 | var ret = matrix; 656 | 657 | var transpose = function (m) { 658 | var result = new Array(m[0].length); 659 | for (var i = 0; i < m[0].length; i++) { 660 | result[i] = new Array(m.length - 1); 661 | for (var j = m.length - 1; j > -1; j--) { 662 | result[i][j] = m[j][i]; 663 | } 664 | } 665 | return result; 666 | }; 667 | 668 | var reverseRows = function (m) { 669 | return m.reverse(); 670 | }; 671 | 672 | var reverseCols = function (m) { 673 | for (var i = 0; i < m.length; i++) { 674 | m[i].reverse(); 675 | } 676 | return m; 677 | }; 678 | 679 | var rotate90Left = function (m) { 680 | m = transpose(m); 681 | m = reverseRows(m); 682 | return m; 683 | }; 684 | 685 | var rotate90Right = function (m) { 686 | m = reverseRows(m); 687 | m = transpose(m); 688 | return m; 689 | }; 690 | 691 | var rotate180 = function (m) { 692 | m = reverseCols(m); 693 | m = reverseRows(m); 694 | return m; 695 | }; 696 | 697 | if (direction == 90 || direction == -270) { 698 | return rotate90Left(ret); 699 | } else if (direction == -90 || direction == 270) { 700 | return rotate90Right(ret); 701 | } else if (Math.abs(direction) == 180) { 702 | return rotate180(ret); 703 | } 704 | 705 | return matrix; 706 | }; 707 | 708 | var pad = function (val, amt, ch) { 709 | ch = typeof ch !== 'undefined' ? ch : ' '; 710 | var str = val 711 | var max = Math.abs(amt); 712 | while (str.length < max) { 713 | if (amt < 0) { 714 | str += ch; 715 | } else { 716 | str = ch + str; 717 | } 718 | } 719 | return str; 720 | }; 721 | 722 | var printMatrix = function (matrix) { 723 | var str = ''; 724 | for (var r = 0; r < matrix.length; r++) { 725 | for (var c = 0; c < matrix[r].length; c++) { 726 | var cell = matrix[r][c].toString(); 727 | if (cell != 'undefined') { 728 | str += pad(cell, 2); 729 | } else { 730 | str += '?'; 731 | } 732 | if (c < matrix[r].length - 1) { 733 | str += ' |'; 734 | } 735 | } 736 | if (r < matrix.length - 1) { 737 | str += '\n'; 738 | for (var i = 0; i < matrix[r].length; i++) { 739 | str += '---' 740 | if (i < matrix[r].length - 1) { 741 | str += '+'; 742 | } 743 | } 744 | str += '\n'; 745 | } 746 | } 747 | return str; 748 | }; 749 | 750 | -------------------------------------------------------------------------------- /VirtualJoystick/VirtualJoystick.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A Virtual Joystick 3 | * @class Phaser.Plugin.VirtualJoystick 4 | */ 5 | Phaser.Plugin.VirtualJoystick = function (game, parent) { 6 | 7 | Phaser.Plugin.call(this, game, parent); 8 | 9 | this.x = 0; 10 | this.y = 0; 11 | this.limit = 10; 12 | 13 | this.baseCircle; 14 | 15 | this.baseBMD; 16 | this.nubBMD; 17 | 18 | this.base; 19 | this.nub; 20 | 21 | this.baseCenter; 22 | this.nubCenter; 23 | 24 | this.isDragging = false; 25 | 26 | this.angle = 0; 27 | this.distance = 0; 28 | this.force = 0; 29 | this.deltaX = 0; 30 | this.deltaY = 0; 31 | this.speed = 0; 32 | 33 | this.pointer = null; 34 | 35 | this.callbackID = -1; 36 | 37 | }; 38 | 39 | Phaser.Plugin.VirtualJoystick.prototype = Object.create(Phaser.Plugin.prototype); 40 | Phaser.Plugin.VirtualJoystick.prototype.constructor = Phaser.Plugin.VirtualJoystick; 41 | 42 | Phaser.Plugin.VirtualJoystick.prototype.init = function (x, y, baseDiameter, stickDiameter, limit, baseColor, stickColor) { 43 | 44 | if (typeof baseDiameter === 'undefined') { baseDiameter = 80; } 45 | if (typeof stickDiameter === 'undefined') { stickDiameter = 60; } 46 | if (typeof limit === 'undefined') { limit = Math.floor(baseDiameter / 2); } 47 | if (typeof baseColor === 'undefined') { baseColor = 'rgba(255, 0, 0, 0.5)'; } 48 | if (typeof stickColor === 'undefined') { stickColor = 'rgba(0, 255, 0, 0.7)'; } 49 | 50 | this.x = x; 51 | this.y = y; 52 | this.isDragging = false; 53 | 54 | this.limit = limit; 55 | this.limitPoint = new Phaser.Point(x, y); 56 | this.location = new Phaser.Point(x, y); 57 | 58 | var radius = Math.floor(baseDiameter / 2); 59 | var nr = Math.floor(stickDiameter / 2); 60 | 61 | this.baseCircle = new Phaser.Circle(x, y, baseDiameter); 62 | 63 | this.baseBMD = this.game.make.bitmapData(baseDiameter, baseDiameter); 64 | this.nubBMD = this.game.make.bitmapData(stickDiameter, stickDiameter); 65 | 66 | this.baseBMD.circle(radius, radius, radius, baseColor); 67 | this.nubBMD.circle(nr, nr, nr, stickColor); 68 | 69 | // Base 70 | this.base = this.game.make.sprite(x, y, this.baseBMD); 71 | this.base.anchor.set(0.5); 72 | 73 | // Nub (stick) 74 | this.nub = this.game.make.sprite(x, y, this.nubBMD); 75 | this.nub.anchor.set(0.5); 76 | 77 | this.nub.inputEnabled = true; 78 | this.nub.events.onInputDown.add(this.startDrag, this); 79 | this.nub.events.onInputUp.add(this.stopDrag, this); 80 | 81 | }; 82 | 83 | Phaser.Plugin.VirtualJoystick.prototype.start = function () { 84 | 85 | this.game.stage.addChild(this.base); 86 | this.game.stage.addChild(this.nub); 87 | 88 | if (this.callbackID > -1) 89 | { 90 | this.game.input.deleteMoveCallback(this.callbackID); 91 | } 92 | 93 | this.callbackID = this.game.input.addMoveCallback(this.move, this); 94 | 95 | this.isDragging = false; 96 | 97 | this.distance = 0; 98 | this.speed = 0; 99 | this.force = 0; 100 | 101 | this.deltaX = 0; 102 | this.deltaY = 0; 103 | 104 | this.nub.x = this.base.x; 105 | this.nub.y = this.base.y; 106 | 107 | this.base.visible = true; 108 | this.nub.visible = true; 109 | 110 | this.limitPoint.set(this.base.x, this.base.y); 111 | this.location.set(this.base.x, this.base.y); 112 | 113 | }; 114 | 115 | Phaser.Plugin.VirtualJoystick.prototype.stop = function () { 116 | 117 | // if (this.nub.parent === null || this.base.parent === null) 118 | // { 119 | // return; 120 | // } 121 | 122 | this.base.visible = false; 123 | this.nub.visible = false; 124 | 125 | this.nub.x = this.base.x; 126 | this.nub.y = this.base.y; 127 | 128 | // this.nub.input.enabled = false; 129 | 130 | // this.game.stage.removeChild(this.base); 131 | // this.game.stage.removeChild(this.nub); 132 | 133 | // this.game.input.deleteMoveCallback(this.callbackID); 134 | 135 | }; 136 | 137 | Phaser.Plugin.VirtualJoystick.prototype.startDrag = function (nub, pointer) { 138 | 139 | this.isDragging = true; 140 | 141 | this.pointer = pointer; 142 | 143 | this.location.set(pointer.x, pointer.y); 144 | 145 | this.distance = Phaser.Point.distance(this.base, this.location, true); 146 | this.angle = this.game.math.wrapAngle(this.location.angle(this.base, true) + 180); 147 | this.force = this.game.math.percent(this.distance, this.limit); 148 | 149 | this.deltaX = Math.cos(this.game.math.degToRad(this.angle)); 150 | this.deltaY = Math.sin(this.game.math.degToRad(this.angle)); 151 | 152 | }; 153 | 154 | Phaser.Plugin.VirtualJoystick.prototype.stopDrag = function (nub, pointer) { 155 | 156 | console.log('stopDrag'); 157 | 158 | this.isDragging = false; 159 | 160 | this.distance = 0; 161 | this.angle = 0; 162 | this.force = 0; 163 | 164 | this.nub.x = this.base.x; 165 | this.nub.y = this.base.y; 166 | 167 | this.deltaX = 0; 168 | this.deltaY = 0; 169 | 170 | this.limitPoint.set(this.base.x, this.base.y); 171 | 172 | }; 173 | 174 | Phaser.Plugin.VirtualJoystick.prototype.move = function (pointer, x, y) { 175 | 176 | if (!this.isDragging) 177 | { 178 | return; 179 | } 180 | 181 | this.location.set(x, y); 182 | 183 | this.distance = Phaser.Point.distance(this.base, this.location, true); 184 | this.angle = this.game.math.wrapAngle(this.location.angle(this.base, true) + 180); 185 | this.force = this.game.math.percent(this.distance, this.limit); 186 | 187 | if (this.distance < this.limit) 188 | { 189 | this.limitPoint.copyFrom(this.location); 190 | } 191 | else 192 | { 193 | this.baseCircle.circumferencePoint(this.angle, true, this.limitPoint); 194 | } 195 | 196 | this.nub.position.set(this.limitPoint.x, this.limitPoint.y); 197 | 198 | this.deltaX = Math.cos(this.game.math.degToRad(this.angle)); 199 | this.deltaY = Math.sin(this.game.math.degToRad(this.angle)); 200 | 201 | }; 202 | 203 | /** 204 | * Given the speed calculate the velocity and return it as a Point object, or set it to the given point object. 205 | * One way to use this is: velocityFromAngle(angle, 200, sprite.velocity) which will set the values directly to the sprites velocity and not create a new Point object. 206 | * 207 | * @method Phaser.Plugin.VirtualJoystick#setVelocity 208 | * @param {Phaser.Sprite} sprite - The Sprite to set the velocity on. The Sprite must have a physics body already set. The value will be set into Sprite.body.velocity. 209 | * @param {number} [minSpeed=0] - The minimum speed the Sprite will move if the joystick is at its default (non-moved) position. 210 | * @param {number} [maxSpeed=100] - The maximum speed the Sprite will move if the joystick is at its full extent. 211 | * @return {Phaser.Sprite} The Sprite object. 212 | */ 213 | Phaser.Plugin.VirtualJoystick.prototype.setVelocity = function (sprite, minSpeed, maxSpeed) { 214 | 215 | if (typeof minSpeed === 'undefined') { minSpeed = 0; } 216 | if (typeof maxSpeed === 'undefined') { maxSpeed = 200; } 217 | 218 | if (this.force === 0 && minSpeed === 0) 219 | { 220 | sprite.body.velocity.set(0, 0); 221 | } 222 | else 223 | { 224 | var speed = (maxSpeed - minSpeed) * this.force; 225 | 226 | sprite.body.velocity.set(this.deltaX * speed, this.deltaY * speed); 227 | } 228 | 229 | return sprite; 230 | 231 | }; 232 | 233 | Phaser.Plugin.VirtualJoystick.prototype.update = function () { 234 | 235 | if (this.isDragging && (!this.pointer.isDown || !this.pointer.withinGame)) 236 | { 237 | this.stopDrag(); 238 | } 239 | 240 | }; 241 | -------------------------------------------------------------------------------- /Webcam/Webcam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides access to the Webcam (if available) 3 | * @class Phaser.Plugin.Webcam 4 | */ 5 | Phaser.Plugin.Webcam = function (game, parent) { 6 | 7 | Phaser.Plugin.call(this, game, parent); 8 | 9 | if (!game.device.getUserMedia) 10 | { 11 | return false; 12 | } 13 | 14 | navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; 15 | 16 | this.context = null; 17 | this.stream = null; 18 | 19 | this.video = document.createElement('video'); 20 | this.video.autoplay = true; 21 | 22 | this.onConnect = new Phaser.Signal(); 23 | this.onError = new Phaser.Signal(); 24 | 25 | }; 26 | 27 | Phaser.Plugin.Webcam.prototype = Object.create(Phaser.Plugin.prototype); 28 | Phaser.Plugin.Webcam.prototype.constructor = Phaser.Plugin.Webcam; 29 | 30 | Phaser.Plugin.Webcam.prototype.start = function (width, height, context) { 31 | 32 | // console.log('Webcam start', width, height); 33 | 34 | this.context = context; 35 | 36 | if (!this.stream) 37 | { 38 | navigator.getUserMedia( { video: { mandatory: { minWidth: width, minHeight: height } } }, this.connectCallback.bind(this), this.errorCallback.bind(this)); 39 | } 40 | 41 | }; 42 | 43 | Phaser.Plugin.Webcam.prototype.stop = function () { 44 | 45 | if (this.stream) 46 | { 47 | this.stream.stop(); 48 | this.stream = null; 49 | } 50 | 51 | }; 52 | 53 | Phaser.Plugin.Webcam.prototype.connectCallback = function (stream) { 54 | 55 | this.stream = stream; 56 | 57 | this.video.src = window.URL.createObjectURL(this.stream); 58 | 59 | this.onConnect.dispatch(this.video); 60 | 61 | }; 62 | 63 | Phaser.Plugin.Webcam.prototype.errorCallback = function (event) { 64 | 65 | this.onError.dispatch(event); 66 | 67 | }; 68 | 69 | Phaser.Plugin.Webcam.prototype.grab = function (context, x, y) { 70 | 71 | if (this.stream) 72 | { 73 | context.drawImage(this.video, x, y); 74 | } 75 | 76 | }; 77 | 78 | Phaser.Plugin.Webcam.prototype.update = function () { 79 | 80 | if (this.stream) 81 | { 82 | this.context.drawImage(this.video, 0, 0); 83 | } 84 | 85 | }; 86 | 87 | /** 88 | * @name Phaser.Plugin.Webcam#active 89 | * @property {boolean} active - Is this Webcam plugin capturing a video stream or not? 90 | * @readonly 91 | */ 92 | Object.defineProperty(Phaser.Plugin.Webcam.prototype, "active", { 93 | 94 | get: function() { 95 | return (this.stream); 96 | } 97 | 98 | }); 99 | -------------------------------------------------------------------------------- /YM/YM.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------ 2 | Tweaked for Phaser.js framework by Nicolas CHALLEIL Aka STuFF 3 | (removed all amiga song playback, handle ArrayBuffer) 4 | 5 | Copyright (c) 2011 Antoine Santo Aka NoNameNo 6 | 7 | This File is part of the CODEF project. 8 | 9 | More info : http://codef.santo.fr 10 | Demo gallery http://www.wab.com 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | THE SOFTWARE. 29 | ------------------------------------------------------------------------------*/ 30 | 31 | !(function () { 32 | var CODEF_AUDIO_CONTEXT = null; 33 | var CODEF_AUDIO_NODE = null; 34 | 35 | function YM (data) { 36 | 37 | if (typeof window.AudioContext !== 'undefined') { 38 | 39 | CODEF_AUDIO_CONTEXT = new AudioContext(); // Atari YM Format !!! ;) 40 | CODEF_AUDIO_NODE = CODEF_AUDIO_CONTEXT.createScriptProcessor(8192); 41 | YmConst_PLAYER_FREQ = CODEF_AUDIO_CONTEXT.sampleRate; 42 | 43 | this.vu = [0, 0, 0]; 44 | 45 | this.info = null; 46 | 47 | this.player = new YmProcessor(this); 48 | this.parse(data); 49 | this.stereo_value = false; 50 | } 51 | } 52 | 53 | YM.prototype.parse = function (data) { 54 | var binString = new dataType(); 55 | var ff = []; 56 | 57 | if (data instanceof ArrayBuffer) { 58 | data = new Uint8Array(data); 59 | } 60 | 61 | for (var z = 0, l = data.length; z < l; z++) { 62 | ff[z] = String.fromCharCode(data[z]); 63 | } 64 | 65 | binString.data = ff.join(''); 66 | 67 | this.player.stereo = this.stereo_value; 68 | 69 | data = binString; 70 | 71 | this.player.load(data); 72 | 73 | this.info = { 74 | title: this.player.song.title || 'unknown', 75 | author: this.player.song.author || 'unknown', 76 | comment: this.player.song.comment || 'unknown' 77 | } 78 | } 79 | 80 | YM.prototype.play = function () { 81 | if (this.player) { 82 | this.player.play(); 83 | } 84 | } 85 | 86 | YM.prototype.stop = function () { 87 | if (this.player) { 88 | this.player.stop(); 89 | } 90 | } 91 | 92 | YM.prototype.clearsong = function () { 93 | if (this.player) { 94 | this.player.reset(); 95 | } 96 | } 97 | 98 | YM.prototype.stereo = function (stat) { 99 | this.stereo_value = stat; 100 | } 101 | 102 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 103 | // 104 | // YM replay routine 105 | // 106 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 107 | 108 | //////////////////////////////////////////////////////////////////// 109 | // 110 | // YmConst.js 111 | // 112 | //////////////////////////////////////////////////////////////////// 113 | var YmConst_BUFFER_SIZE = 8192; 114 | var YmConst_PLAYER_FREQ = 48000; 115 | var YmConst_DRUM_PREC = 15; 116 | 117 | var YmConst_AMSTRAD_FREQ = 1000000; 118 | var YmConst_ATARI_FREQ = 2000000; 119 | var YmConst_SPECTRUM_FREQ = 1773400; 120 | 121 | var YmConst_INTERLEAVED = 1; 122 | var YmConst_DRUM_SIGNED = 2; 123 | var YmConst_DRUM_4BITS = 4; 124 | var YmConst_TIME_CONTROL = 8; 125 | var YmConst_LOOP_MODE = 16; 126 | 127 | var YmConst_MFP_PREDIV = [0, 4, 10, 16, 50, 64, 100, 200]; 128 | 129 | var YmConst_MONO = [ 130 | 0.00063071586250394, 0.00163782667521185, 0.00269580167037975, 0.00383515935748365, 131 | 0.00590024516535946, 0.00787377544480728, 0.01174962614825892, 0.01602221747489853, 132 | 0.02299061047191789, 0.03141371908729311, 0.04648986276843572, 0.06340728985463016, 133 | 0.09491256447035126, 0.13414919481999166, 0.21586759036022013, 0.33333333333333333 134 | ]; 135 | 136 | var YmConst_STEREO = [ 137 | 0.00094607379375591, 0.00245674001281777, 0.00404370250556963, 0.00575273903622547, 138 | 0.00885036774803918, 0.01181066316721091, 0.01762443922238838, 0.02403332621234779, 139 | 0.03448591570787683, 0.04712057863093966, 0.06973479415265358, 0.09511093478194525, 140 | 0.14236884670552690, 0.20122379222998749, 0.32380138554033021, 0.50000000000000000 141 | ]; 142 | 143 | var YmConst_ENVELOPES = [ 144 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 161 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 162 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 165 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 166 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 167 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 168 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 169 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 170 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 171 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 172 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 173 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 174 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 176 | ]; 177 | 178 | //////////////////////////////////////////////////////////////////// 179 | // 180 | // YmSong.js 181 | // 182 | //////////////////////////////////////////////////////////////////// 183 | function YmSong(stream) { 184 | this.title; 185 | this.author; 186 | this.comment; 187 | 188 | this.attribs; 189 | this.clock; 190 | this.digidrums; 191 | this.drums; 192 | this.frames = new Array(); 193 | this.frameSize; 194 | this.length; 195 | this.rate; 196 | this.restart; 197 | this.supported = true; 198 | 199 | this.data = new dataType(); 200 | this.data.data = stream; 201 | 202 | this.init = function () { 203 | this.decode(); 204 | if (this.attribs & YmConst_INTERLEAVED) this.deinterleave(); 205 | 206 | for (i = 0; i < this.length; ++i) { 207 | this.frames[i] = this.data.readBytes(0, this.frameSize); 208 | } 209 | 210 | } 211 | 212 | this.decode = function () { 213 | var digidrum; 214 | var i; 215 | var id = this.data.readMultiByte(4, "txt"); 216 | 217 | switch (id) { 218 | case "YM2!": 219 | case "YM3!": 220 | case "YM3b": 221 | this.frameSize = 14; 222 | this.length = (this.data.data.length - 4) / this.frameSize; 223 | this.clock = YmConst_ATARI_FREQ; 224 | this.rate = 50; 225 | this.restart = (id != "YM3b") ? 0 : this.data.readByte(); 226 | this.attribs = YmConst_INTERLEAVED | YmConst_TIME_CONTROL; 227 | break; 228 | 229 | case "YM4!": 230 | this.supported = false; 231 | break; 232 | 233 | case "YM5!": 234 | case "YM6!": 235 | id = this.data.readMultiByte(8, "txt"); 236 | if (id != "LeOnArD!") { 237 | this.supported = false; 238 | return; 239 | } 240 | 241 | this.length = this.data.readInt(); 242 | this.attribs = this.data.readInt(); 243 | this.drums = this.data.readShort(); 244 | this.clock = this.data.readInt(); 245 | this.rate = this.data.readShort(); 246 | this.restart = this.data.readInt(); 247 | this.data.readShort(); 248 | 249 | if (this.drums) { 250 | this.digidrums = new Array(); 251 | 252 | for (i = 0; i < this.drums; ++i) { 253 | this.digidrum = new Digidrum(this.data.readInt()); 254 | 255 | if (this.digidrum.size != 0) { 256 | this.digidrum.wave.data = this.data.readBytes(0, this.digidrum.size); 257 | this.digidrum.convert(this.attribs); 258 | this.digidrums[i] = this.digidrum; 259 | } 260 | } 261 | this.attribs &= (~YmConst_DRUM_4BITS); 262 | } 263 | 264 | this.title = this.data.readString(); 265 | this.author = this.data.readString(); 266 | this.comment = this.data.readString(); 267 | 268 | this.frameSize = 16; 269 | this.attribs = YmConst_INTERLEAVED | YmConst_TIME_CONTROL; 270 | break; 271 | 272 | case "MIX1": 273 | supported = false; 274 | break; 275 | 276 | case "YMT1": 277 | case "YMT2": 278 | supported = false; 279 | break; 280 | 281 | default: 282 | supported = false; 283 | break; 284 | } 285 | 286 | } 287 | 288 | this.deinterleave = function () { 289 | var i; 290 | var j; 291 | var s = 0; 292 | 293 | var p = new Array(); 294 | var r = new Array(); 295 | 296 | for (i = 0; i < this.frameSize; ++i) p[i] = this.data.pos + (this.length * i); 297 | 298 | for (i = 0; i < this.length; ++i) { 299 | for (j = 0; j < this.frameSize; ++j) r[j + s] = this.data.data[i + p[j]]; 300 | s += this.frameSize; 301 | } 302 | 303 | this.data.data = ""; 304 | this.data.data = r; 305 | this.data.pos = 0; 306 | this.attribs &= (~YmConst_INTERLEAVED); 307 | } 308 | 309 | this.init(); 310 | 311 | } 312 | 313 | //////////////////////////////////////////////////////////////////// 314 | // 315 | // YmProcessor.js 316 | // 317 | //////////////////////////////////////////////////////////////////// 318 | function YmProcessor(ym) { 319 | this.ym = ym; 320 | this.counter; 321 | 322 | this.sound; 323 | this.soundChannel; 324 | this.soundChannelPos; 325 | this.song; 326 | this.loop = 1; 327 | this.stereo = 0; 328 | 329 | this.audioFreq; 330 | this.clock; 331 | this.registers = new Array(); 332 | this.volumeEnv; 333 | 334 | this.buffer; 335 | this.bufferSize; 336 | this.voiceA = new YmChannel(this);; 337 | this.voiceB = new YmChannel(this);; 338 | this.voiceC = new YmChannel(this);; 339 | 340 | this.samplesTick; 341 | this.samplesLeft; 342 | this.frame; 343 | 344 | this.envData; 345 | this.envPhase; 346 | this.envPos; 347 | this.envShape; 348 | this.envStep; 349 | 350 | this.noiseOutput; 351 | this.noisePos; 352 | this.noiseStep; 353 | this.rng; 354 | 355 | this.syncBuzzer; 356 | this.syncBuzzerPhase; 357 | this.syncBuzzerStep; 358 | __self = this; 359 | 360 | 361 | 362 | this.init = function () { 363 | var i; 364 | 365 | this.bufferSize = YmConst_BUFFER_SIZE; 366 | this.buffer = new Array(); 367 | 368 | for (i = 0; i < this.bufferSize; ++i) this.buffer[i] = new Sample(); 369 | 370 | this.envData = YmConst_ENVELOPES; 371 | } 372 | 373 | this.load = function (stream) { 374 | var monLHa = new LHa(); 375 | this.song = new YmSong(monLHa.unpack(stream)); 376 | 377 | this.audioFreq = YmConst_PLAYER_FREQ; 378 | this.clock = this.song.clock; 379 | this.samplesTick = this.audioFreq / this.song.rate; 380 | 381 | return this.song.supported; 382 | } 383 | 384 | this.play = function () { 385 | CODEF_AUDIO_NODE.onaudioprocess = function (event) { 386 | __self.mixer(event); 387 | 388 | __self.ym.vu[0] = __self.voiceA.vol; 389 | __self.ym.vu[1] = __self.voiceB.vol; 390 | __self.ym.vu[2] = __self.voiceC.vol; 391 | } 392 | } 393 | 394 | this.mixer = function (e) { 395 | var b = 0; 396 | var i = 0; 397 | var mixed = 0; 398 | var mixPos = 0; 399 | var sample; 400 | var size = 0; 401 | var toMix = 0; 402 | var value = 0; 403 | 404 | while (mixed < this.bufferSize) { 405 | if (this.samplesLeft == 0) { 406 | if (this.frame >= this.song.length) { 407 | if (this.loop) { 408 | this.frame = this.song.restart; 409 | } else { 410 | this.stop(); 411 | return; 412 | } 413 | } 414 | 415 | this.syncBuzzerStop(); 416 | 417 | for (i = 0; i < this.song.frameSize; i++) { 418 | this.registers[i] = this.song.frames[this.frame][i].charCodeAt(0); 419 | } 420 | this.frame++; 421 | //this.registers = this.song.frames[this.frame++]; 422 | this.updateEffects(1, 6, 14); 423 | this.updateEffects(3, 8, 15); 424 | 425 | this.writeRegisters(); 426 | this.samplesLeft = this.samplesTick; 427 | } 428 | 429 | toMix = this.samplesLeft; 430 | if ((mixed + toMix) > this.bufferSize) 431 | toMix = this.bufferSize - mixed; 432 | size = mixPos + toMix; 433 | 434 | for (i = mixPos; i < size; ++i) { 435 | sample = this.buffer[i]; 436 | 437 | if (this.noisePos & 65536) { 438 | b = (this.rng & 1) ^ ((this.rng >> 2) & 1); 439 | this.rng = (this.rng >> 1) | (b << 16); 440 | this.noiseOutput ^= (b ? 0 : 65535); 441 | this.noisePos &= 65535; 442 | } 443 | 444 | this.volumeEnv = this.envData[Math.floor((this.envShape << 6) + (this.envPhase << 5) + (this.envPos >> 26))]; 445 | 446 | this.voiceA.computeVolume(); 447 | this.voiceB.computeVolume(); 448 | this.voiceC.computeVolume(); 449 | 450 | b = this.voiceA.enabled() & (this.noiseOutput | this.voiceA.mixNoise); 451 | var toto = this.voiceA.getvolume(); 452 | sample.voiceA = (b) ? this.voiceA.getvolume() : -1; 453 | b = this.voiceB.enabled() & (this.noiseOutput | this.voiceB.mixNoise); 454 | sample.voiceB = (b) ? this.voiceB.getvolume() : -1; 455 | b = this.voiceC.enabled() & (this.noiseOutput | this.voiceC.mixNoise); 456 | sample.voiceC = (b) ? this.voiceC.getvolume() : -1; 457 | 458 | this.voiceA.next(); 459 | this.voiceB.next(); 460 | this.voiceC.next(); 461 | 462 | this.noisePos += this.noiseStep; 463 | this.envPos += this.envStep; 464 | if (this.envPos > 2147483647) 465 | this.envPos -= 2147483647; 466 | if (this.envPhase == 0 && this.envPos < this.envStep) 467 | envPhase = 1; 468 | 469 | if (this.syncBuzzer) { 470 | this.syncBuzzerPhase += this.syncBuzzerStep; 471 | 472 | if (this.syncBuzzerPhase & 1073741824) { 473 | this.envPos = 0; 474 | this.envPhase = 0; 475 | this.syncBuzzerPhase &= 0x3fffffff; 476 | } 477 | } 478 | } 479 | 480 | mixed += toMix; 481 | mixPos = size; 482 | this.samplesLeft -= toMix; 483 | } 484 | 485 | var l = event.outputBuffer.getChannelData(0); 486 | var r = event.outputBuffer.getChannelData(1); 487 | 488 | if (this.stereo) { 489 | for (i = 0; i < this.bufferSize; ++i) { 490 | sample = this.buffer[i]; 491 | l[i] = sample.left(); 492 | r[i] = sample.right(); 493 | } 494 | } else { 495 | 496 | for (i = 0; i < this.bufferSize; ++i) { 497 | value = this.buffer[i].mono(); 498 | l[i] = value; 499 | r[i] = value; 500 | } 501 | } 502 | 503 | } 504 | 505 | this.writeRegisters = function () { 506 | var p; 507 | 508 | this.registers[0] &= 255; 509 | this.registers[1] &= 15; 510 | this.voiceA.computeTone(this.registers[1], this.registers[0]); 511 | 512 | this.registers[2] &= 255; 513 | this.registers[3] &= 15; 514 | this.voiceB.computeTone(this.registers[3], this.registers[2]); 515 | 516 | this.registers[4] &= 255; 517 | this.registers[5] &= 15; 518 | this.voiceC.computeTone(this.registers[5], this.registers[4]); 519 | 520 | this.registers[6] &= 31; 521 | 522 | if (this.registers[6] < 3) { 523 | this.noisePos = 0; 524 | this.noiseOutput = 65535; 525 | this.noiseStep = 0; 526 | } else { 527 | p = this.clock / ((this.registers[6] << 3) * this.audioFreq); 528 | this.noiseStep = Math.floor(p * 32768); 529 | } 530 | 531 | this.registers[7] &= 255; 532 | 533 | this.voiceA.mixTone = (this.registers[7] & 1) ? 65535 : 0; 534 | this.voiceB.mixTone = (this.registers[7] & 2) ? 65535 : 0; 535 | this.voiceC.mixTone = (this.registers[7] & 4) ? 65535 : 0; 536 | 537 | this.voiceA.mixNoise = (this.registers[7] & 8) ? 65535 : 0; 538 | this.voiceB.mixNoise = (this.registers[7] & 16) ? 65535 : 0; 539 | this.voiceC.mixNoise = (this.registers[7] & 32) ? 65535 : 0; 540 | 541 | this.registers[8] &= 31; 542 | this.voiceA.setvolume(this.registers[8]); 543 | this.registers[9] &= 31; 544 | this.voiceB.setvolume(this.registers[9]); 545 | this.registers[10] &= 31; 546 | this.voiceC.setvolume(this.registers[10]); 547 | 548 | this.registers[11] &= 255; 549 | this.registers[12] &= 255; 550 | p = (this.registers[12] << 8) | this.registers[11]; 551 | 552 | if (p < 3) { 553 | this.envStep = 0; 554 | } else { 555 | p = this.clock / ((p << 8) * this.audioFreq); 556 | this.envStep = Math.floor(p * 1073741824); 557 | } 558 | 559 | if (this.registers[13] == 255) { 560 | this.registers[13] = 0; 561 | } else { 562 | this.registers[13] &= 15; 563 | this.envPhase = 0; 564 | this.envPos = 0; 565 | this.envShape = this.registers[13]; 566 | } 567 | 568 | } 569 | 570 | this.updateEffects = function (code, preDiv, count) { 571 | var index = 0; 572 | var tmpFreq = 0; 573 | var voice = 0; 574 | 575 | code = this.registers[code] & 0xf0; 576 | preDiv = (this.registers[preDiv] >> 5) & 7; 577 | count = this.registers[count]; 578 | 579 | if (code & 0x30) { 580 | voice = ((code & 0x30) >> 4) - 1; 581 | 582 | switch (code & 0xc0) { 583 | case 0x00: 584 | case 0x80: 585 | break; 586 | case 0x40: 587 | index = this.registers[voice + 8] & 31; 588 | 589 | if ((index >= 0) && (index < this.song.drums)) { 590 | preDiv = YmConst_MFP_PREDIV[preDiv] * count; 591 | if (preDiv > 0) { 592 | tmpFreq = 2457600 / preDiv; 593 | 594 | if (voice == 0) { 595 | this.voiceA.drum = this.song.digidrums[index]; 596 | this.voiceA.drumStart(tmpFreq); 597 | } else if (voice == 1) { 598 | this.voiceB.drum = this.song.digidrums[index]; 599 | this.voiceB.drumStart(tmpFreq); 600 | } else if (voice == 2) { 601 | this.voiceC.drum = this.song.digidrums[index]; 602 | this.voiceC.drumStart(tmpFreq); 603 | } 604 | } 605 | } 606 | break; 607 | case 0xc0: 608 | break; 609 | } 610 | } 611 | } 612 | 613 | this.syncBuzzerStart = function (timerFreq, shapeEnv) { 614 | this.envShape = this.shapeEnv & 15; 615 | this.syncBuzzerStep = (this.timerFreq * 1073741824) / this.audioFreq;; 616 | this.syncBuzzerPhase = 0; 617 | this.syncBuzzer = true; 618 | } 619 | 620 | this.syncBuzzerStop = function () { 621 | this.syncBuzzer = false; 622 | this.syncBuzzerPhase = 0; 623 | this.syncBuzzerStep = 0; 624 | } 625 | 626 | this.stop = function () { 627 | 628 | this.reset(); 629 | return true; 630 | } 631 | 632 | this.reset = function () { 633 | var i; 634 | 635 | this.voiceA = new YmChannel(this); 636 | this.voiceB = new YmChannel(this); 637 | this.voiceC = new YmChannel(this); 638 | this.samplesLeft = 0; 639 | this.frame = 0; 640 | 641 | this.registers = new Array(); 642 | for (i = 0; i < 16; ++i) 643 | this.registers[i] = 0; 644 | this.registers[7] = 255; 645 | 646 | this.writeRegisters(); 647 | this.volumeEnv = 0; 648 | 649 | this.noiseOutput = 65535; 650 | this.noisePos = 0; 651 | this.noiseStep = 0; 652 | this.rng = 1; 653 | 654 | this.envPhase = 0; 655 | this.envPos = 0; 656 | this.envShape = 0; 657 | this.envStep = 0; 658 | 659 | this.syncBuzzerStop(); 660 | } 661 | 662 | this.init(); 663 | this.reset(); 664 | 665 | CODEF_AUDIO_NODE.connect(CODEF_AUDIO_CONTEXT.destination); 666 | 667 | } 668 | 669 | //////////////////////////////////////////////////////////////////// 670 | // 671 | // Sample.js 672 | // 673 | //////////////////////////////////////////////////////////////////// 674 | function Sample() { 675 | this.voiceA = -1; 676 | this.voiceB = -1; 677 | this.voiceC = -1; 678 | 679 | 680 | 681 | this.mono = function () { 682 | var v = YmConst_MONO; 683 | var vol = 0.0; 684 | 685 | if (this.voiceA > -1) vol += v[this.voiceA]; 686 | if (this.voiceB > -1) vol += v[this.voiceB]; 687 | if (this.voiceC > -1) vol += v[this.voiceC]; 688 | return vol; 689 | } 690 | 691 | this.left = function () { 692 | var v = YmConst_STEREO; 693 | var vol = 0.0; 694 | 695 | if (this.voiceA > -1) vol += v[this.voiceA]; 696 | if (this.voiceB > -1) vol += v[this.voiceB]; 697 | return vol; 698 | } 699 | 700 | this.right = function () { 701 | var v = YmConst_STEREO; 702 | var vol = 0.0; 703 | 704 | if (this.voiceB > -1) vol += v[this.voiceB]; 705 | if (this.voiceC > -1) vol += v[this.voiceC]; 706 | return vol; 707 | } 708 | } 709 | //////////////////////////////////////////////////////////////////// 710 | // 711 | // YmChannel.js 712 | // 713 | //////////////////////////////////////////////////////////////////// 714 | function YmChannel(processor) { 715 | this.mixNoise = 0; 716 | this.mixTone = 0; 717 | this.mode = 0; 718 | this.position = 0; 719 | this.step = 0; 720 | 721 | this.digidrum = 0; 722 | this.drum = 0; 723 | this.drumPos = 0; 724 | this.drumStep = 0; 725 | 726 | this.processor = processor; 727 | this.vol = 0; 728 | 729 | this.enabled = function () { 730 | return (this.position >> 30) | this.mixTone; 731 | } 732 | 733 | this.getvolume = function () { 734 | return (this.mode) ? this.processor.volumeEnv : this.vol; 735 | } 736 | 737 | this.setvolume = function (value) { 738 | if (value & 16) 739 | this.mode = true 740 | else 741 | this.mode = false; 742 | this.vol = value; 743 | } 744 | 745 | this.next = function () { 746 | this.position += this.step; 747 | if (this.position > 2147483647) this.position -= 2147483647; 748 | } 749 | 750 | this.computeTone = function (high, low) { 751 | var p = (high << 8) | low; 752 | 753 | if (p < 5) { 754 | this.position = 1073741824; 755 | this.step = 0; 756 | } else { 757 | p = this.processor.clock / ((p << 3) * this.processor.audioFreq); 758 | this.step = Math.floor(p * 1073741824); 759 | } 760 | } 761 | 762 | this.computeVolume = function () { 763 | var pos; 764 | 765 | if (this.digidrum) { 766 | pos = this.drumPos >> YmConst_DRUM_PREC; 767 | this.vol = this.drum.data[pos] / 16; //6; 768 | this.mixNoise = 65535; 769 | this.mixTone = 65535; 770 | 771 | this.drumPos += this.drumStep; 772 | pos = this.drumPos >> YmConst_DRUM_PREC; 773 | if (pos >= this.drum.size) 774 | this.digidrum = false; 775 | } 776 | } 777 | 778 | this.drumStart = function (drumFreq) { 779 | this.digidrum = true; 780 | this.drumPos = 0; 781 | this.drumStep = (this.drumFreq << 15) / this.processor.audioFreq; 782 | } 783 | 784 | this.drumStop = function () { 785 | this.digidrum = false; 786 | } 787 | 788 | } 789 | 790 | //////////////////////////////////////////////////////////////////// 791 | // 792 | // Digidrum.js 793 | // 794 | //////////////////////////////////////////////////////////////////// 795 | function Digidrum(size) { 796 | this.data; 797 | this.repeatLen; 798 | this.size; 799 | this.wave = null; 800 | 801 | this.size = size; 802 | 803 | this.wave = new dataType(); 804 | 805 | this.convert = function (attribs) { 806 | var b; 807 | var i; 808 | this.data = new Array; 809 | 810 | if (attribs & YmConst_DRUM_4BITS) { 811 | for (i = 0; i < this.size; ++i) { 812 | b = (this.wave.readByte() & 15) >> 7; 813 | this.data[i] = YmConst_MONO[b]; 814 | } 815 | } else { 816 | for (i = 0; i < this.size; ++i) { 817 | this.data[i] = this.wave.readByte(); // / 255; 818 | } 819 | } 820 | this.wave = null; 821 | } 822 | 823 | } 824 | 825 | function dataType() { 826 | this.data; 827 | this.pos = 0; 828 | this.endian = "BIG"; 829 | 830 | 831 | this.readBytes = function (offset, nb) { 832 | var tmp = ""; 833 | for (var i = 0; i < nb; i++) { 834 | tmp += this.data[offset + this.pos++]; 835 | } 836 | return tmp; 837 | } 838 | 839 | this.readMultiByte = function (nb, type) { 840 | if (type == "txt") { 841 | var tmp = ""; 842 | for (var i = 0; i < nb; i++) { 843 | tmp += this.data[this.pos++] 844 | } 845 | return tmp; 846 | } 847 | } 848 | 849 | this.readInt = function () { 850 | var tmp1 = parseInt(this.data[this.pos + 0].charCodeAt(0).toString(16), 16); 851 | var tmp2 = parseInt(this.data[this.pos + 1].charCodeAt(0).toString(16), 16); 852 | var tmp3 = parseInt(this.data[this.pos + 2].charCodeAt(0).toString(16), 16); 853 | var tmp4 = parseInt(this.data[this.pos + 3].charCodeAt(0).toString(16), 16); 854 | if (this.endian == "BIG") 855 | var tmp = (tmp1 << 24) | (tmp2 << 16) | (tmp3 << 8) | tmp4; 856 | else 857 | var tmp = (tmp4 << 24) | (tmp3 << 16) | (tmp2 << 8) | tmp1; 858 | this.pos += 4; 859 | return tmp; 860 | } 861 | 862 | this.readShort = function () { 863 | var tmp1 = parseInt(this.data[this.pos + 0].charCodeAt(0).toString(16), 16); 864 | var tmp2 = parseInt(this.data[this.pos + 1].charCodeAt(0).toString(16), 16); 865 | var tmp = (tmp1 << 8) | tmp2; 866 | this.pos += 2; 867 | return tmp; 868 | } 869 | this.readByte = function () { 870 | var tmp = parseInt(this.data[this.pos].charCodeAt(0).toString(16), 16) 871 | this.pos += 1; 872 | return tmp; 873 | } 874 | this.readString = function () { 875 | var tmp = ""; 876 | while (1) { 877 | if (this.data[this.pos++].charCodeAt(0) != 0) 878 | tmp += this.data[this.pos - 1]; 879 | else 880 | return tmp; 881 | } 882 | } 883 | 884 | this.substr = function (start, nb) { 885 | return this.data.substr(start, nb); 886 | } 887 | 888 | this.bytesAvailable = function () { 889 | return this.length - this.pos; 890 | } 891 | } 892 | 893 | ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 894 | // 895 | // LHA depack routine 896 | // 897 | ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 898 | 899 | function LHa() { 900 | this.data; 901 | 902 | this.source; 903 | this.buffer; 904 | this.output; 905 | this.srcSize; 906 | this.dstSize; 907 | this.srcPos; 908 | this.dstPos; 909 | 910 | this.c_Table; 911 | this.p_Table; 912 | this.c_Len; 913 | this.p_Len; 914 | this.l_Tree; 915 | this.r_Tree; 916 | 917 | this.bitBuffer; 918 | this.bitCount; 919 | this.subBuffer; 920 | this.blockSize; 921 | this.fillBufferSize; 922 | this.fillIndex; 923 | this.decodei; 924 | this.decodej; 925 | 926 | 927 | this.data = ""; 928 | this.buffer = new Array(); 929 | this.output = new Array(); 930 | 931 | this.c_Table = new Array(); 932 | this.p_Table = new Array(); 933 | this.c_Len = new Array(); 934 | this.p_Len = new Array(); 935 | this.l_Tree = new Array(); 936 | this.r_Tree = new Array(); 937 | 938 | 939 | this.unpack = function (source) { 940 | this.header = new LHaHeader(source); 941 | if (this.header.size == 0 || this.header.method != "-lh5-" || this.header.level != 0) return source.data; 942 | 943 | this.source = source; 944 | this.srcSize = this.header.packed; 945 | this.srcPos = this.source.pos; 946 | this.dstSize = this.header.original; 947 | 948 | this.fillBufferSize = 0; 949 | this.bitBuffer = 0; 950 | this.bitCount = 0; 951 | this.subBuffer = 0; 952 | this.fillBuffer(16); 953 | this.blockSize = 0; 954 | this.decodej = 0; 955 | 956 | var l = this.dstSize; 957 | var n; 958 | var np; 959 | 960 | while (l != 0) { 961 | n = l > 8192 ? 8192 : l; 962 | this.decode(n); 963 | np = n > this.dstSize ? this.dstSize : n; 964 | 965 | if (np > 0) { 966 | this.output.pos = 0; 967 | for (var yop = 0; yop < np; yop++) { 968 | this.data += String.fromCharCode(this.output[yop]); 969 | } 970 | this.dstPos += np; 971 | this.dstSize -= np; 972 | } 973 | 974 | l -= n; 975 | } 976 | 977 | this.buffer = ""; 978 | this.output = new Array; 979 | return this.data; 980 | } 981 | 982 | this.decode = function (count) { 983 | var c; 984 | var r = 0; 985 | 986 | while (--this.decodej >= 0) { 987 | this.output[r] = this.output[this.decodei]; 988 | this.decodei = ++this.decodei & 8191; 989 | if (++r == count) return; 990 | } 991 | 992 | for (;;) { 993 | c = this.decode_c(); 994 | 995 | if (c <= 255) { 996 | this.output[r] = c; 997 | if (++r == count) return; 998 | } else { 999 | this.decodej = c - 253; 1000 | this.decodei = (r - this.decode_p() - 1) & 8191; 1001 | 1002 | while (--this.decodej >= 0) { 1003 | this.output[r] = this.output[this.decodei]; 1004 | this.decodei = ++this.decodei & 8191; 1005 | if (++r == count) return; 1006 | } 1007 | } 1008 | } 1009 | } 1010 | 1011 | this.decode_c = function () { 1012 | var j; 1013 | var mask = 0; 1014 | 1015 | if (this.blockSize == 0) { 1016 | this.blockSize = this.getBits(16); 1017 | this.read_p(19, 5, 3); 1018 | this.read_c(); 1019 | this.read_p(14, 4, -1); 1020 | } 1021 | 1022 | this.blockSize--; 1023 | j = this.c_Table[this.bitBuffer >> 4]; 1024 | 1025 | if (j >= 510) { 1026 | mask = 1 << 3; 1027 | 1028 | do { 1029 | j = (this.bitBuffer & mask) ? this.r_Tree[j] : this.l_Tree[j]; 1030 | mask >>= 1; 1031 | } while (j >= 510); 1032 | } 1033 | 1034 | this.fillBuffer(this.c_Len[j]); 1035 | return j & 0xffff; 1036 | } 1037 | 1038 | 1039 | 1040 | this.decode_p = function () { 1041 | var j = this.p_Table[this.bitBuffer >> 8]; 1042 | var mask = 0; 1043 | 1044 | if (j >= 14) { 1045 | mask = 1 << 7; 1046 | 1047 | do { 1048 | j = (this.bitBuffer & mask) ? this.r_Tree[j] : this.l_Tree[j]; 1049 | mask >>= 1; 1050 | } while (j >= 14); 1051 | } 1052 | 1053 | this.fillBuffer(this.p_Len[j]); 1054 | if (j != 0) j = (1 << (j - 1)) + this.getBits(j - 1); 1055 | return j & 0xffff; 1056 | } 1057 | 1058 | this.read_c = function () { 1059 | var c; 1060 | var i = 0; 1061 | var mask = 0 1062 | var n = this.getBits(9); 1063 | 1064 | if (n == 0) { 1065 | c = this.getBits(9); 1066 | for (i = 0; i < 510; ++i) this.c_Len[i] = 0; 1067 | for (i = 0; i < 4096; ++i) this.c_Table[i] = c; 1068 | } else { 1069 | while (i < n) { 1070 | c = this.p_Table[this.bitBuffer >> 8]; 1071 | 1072 | if (c >= 19) { 1073 | mask = 1 << 7; 1074 | do { 1075 | c = (this.bitBuffer & mask) ? this.r_Tree[c] : this.l_Tree[c]; 1076 | mask >>= 1; 1077 | } while (c >= 19); 1078 | } 1079 | 1080 | this.fillBuffer(this.p_Len[c]); 1081 | 1082 | if (c <= 2) { 1083 | if (c == 0) 1084 | c = 1; 1085 | else if (c == 1) 1086 | c = this.getBits(4) + 3; 1087 | else 1088 | c = this.getBits(9) + 20; 1089 | 1090 | while (--c >= 0) this.c_Len[i++] = 0; 1091 | } else { 1092 | this.c_Len[i++] = c - 2; 1093 | } 1094 | } 1095 | 1096 | while (i < 510) this.c_Len[i++] = 0; 1097 | this.makeTable(510, this.c_Len, 12, this.c_Table); 1098 | } 1099 | } 1100 | 1101 | this.read_p = function (nn, nbit, iSpecial) { 1102 | var c; 1103 | var i = 0; 1104 | var mask = 0; 1105 | var n = this.getBits(nbit); 1106 | 1107 | if (n == 0) { 1108 | c = this.getBits(nbit); 1109 | for (i = 0; i < nn; ++i) this.p_Len[i] = 0; 1110 | for (i = 0; i < 256; ++i) this.p_Table[i] = c; 1111 | } else { 1112 | while (i < n) { 1113 | c = this.bitBuffer >> 13; 1114 | 1115 | if (c == 7) { 1116 | mask = 1 << 12; 1117 | 1118 | while (mask & this.bitBuffer) { 1119 | mask >>= 1; 1120 | c++; 1121 | } 1122 | } 1123 | 1124 | this.fillBuffer(c < 7 ? 3 : c - 3); 1125 | this.p_Len[i++] = c; 1126 | 1127 | if (i == iSpecial) { 1128 | c = this.getBits(2); 1129 | while (--c >= 0) this.p_Len[i++] = 0; 1130 | } 1131 | } 1132 | 1133 | while (i < nn) this.p_Len[i++] = 0; 1134 | this.makeTable(nn, this.p_Len, 8, this.p_Table); 1135 | } 1136 | } 1137 | 1138 | this.getBits = function (n) { 1139 | var r = this.bitBuffer >> (16 - n); 1140 | this.fillBuffer(n); 1141 | return r & 0xffff; 1142 | } 1143 | 1144 | this.fillBuffer = function (n) { 1145 | var np; 1146 | 1147 | this.bitBuffer = (this.bitBuffer << n) & 0xffff; 1148 | 1149 | while (n > this.bitCount) { 1150 | this.bitBuffer |= this.subBuffer << (n -= this.bitCount); 1151 | this.bitBuffer &= 0xffff; 1152 | 1153 | if (this.fillBufferSize == 0) { 1154 | this.fillIndex = 0; 1155 | np = this.srcSize > 4064 ? 4064 : this.srcSize; 1156 | 1157 | if (np > 0) { 1158 | this.source.pos = this.srcPos; 1159 | this.buffer = this.source.readBytes(0, np); 1160 | this.srcPos += np; 1161 | this.srcSize -= np; 1162 | } 1163 | 1164 | this.fillBufferSize = np; 1165 | } 1166 | 1167 | if (this.fillBufferSize > 0) { 1168 | this.fillBufferSize--; 1169 | this.subBuffer = this.buffer[this.fillIndex++].charCodeAt(0); 1170 | } else { 1171 | this.subBuffer = 0; 1172 | } 1173 | 1174 | this.bitCount = 8; 1175 | } 1176 | 1177 | this.bitBuffer |= this.subBuffer >> (this.bitCount -= n); 1178 | this.bitBuffer &= 0xffff; 1179 | } 1180 | 1181 | this.makeTable = function (nchar, bitlen, tablebits, table) { 1182 | var a = nchar; 1183 | var h; 1184 | var i; 1185 | var j; 1186 | var k; 1187 | var l; 1188 | var n; 1189 | var p; 1190 | var t; 1191 | var r; 1192 | var c = new Array(); 1193 | var w = new Array(); 1194 | var s = new Array(); 1195 | var mask = 1 << (15 - tablebits); 1196 | for (i = 0; i < nchar; ++i) 1197 | c[i] = 0; 1198 | 1199 | for (i = 0; i < nchar; ++i) c[bitlen[i]]++; 1200 | 1201 | s[1] = 0; 1202 | for (i = 1; i < 17; ++i) s[i + 1] = (s[i] + (c[i] << (16 - i))) & 0xffff; 1203 | 1204 | if (s[17] != 0) return false; 1205 | j = 16 - tablebits; 1206 | 1207 | for (i = 1; i <= tablebits; ++i) { 1208 | s[i] >>= j; 1209 | w[i] = 1 << (tablebits - i); 1210 | } 1211 | 1212 | while (i < 17) w[i] = 1 << (16 - i++); 1213 | i = s[tablebits + 1] >> j; 1214 | 1215 | if (i != 0) { 1216 | k = 1 << tablebits; 1217 | while (i != k) table[i++] = 0; 1218 | } 1219 | 1220 | for (h = 0; h < nchar; ++h) { 1221 | if ((l = bitlen[h]) == 0) continue; 1222 | n = s[l] + w[l]; 1223 | 1224 | if (l <= tablebits) { 1225 | for (i = s[l]; i < n; ++i) table[i] = h; 1226 | } else { 1227 | i = l - tablebits; 1228 | k = s[l]; 1229 | p = k >> j; 1230 | t = table; 1231 | 1232 | while (i != 0) { 1233 | if (t[p] == 0) { 1234 | this.l_Tree[a] = 0; 1235 | this.r_Tree[a] = 0; 1236 | t[p] = a++; 1237 | } 1238 | 1239 | r = (k & mask) ? this.r_Tree : this.l_Tree; 1240 | k <<= 1; 1241 | i--; 1242 | } 1243 | 1244 | r[t[p]] = h; 1245 | } 1246 | s[l] = n; 1247 | } 1248 | 1249 | return true; 1250 | } 1251 | 1252 | } 1253 | 1254 | 1255 | function LHaHeader(source) { 1256 | this.size; 1257 | this.checksum; 1258 | this.method; 1259 | this.packed; 1260 | this.original; 1261 | this.timeStamp; 1262 | this.attribute; 1263 | this.level; 1264 | this.nameLength; 1265 | this.name; 1266 | 1267 | source.endian = "LITTLE"; 1268 | source.pos = 0; 1269 | 1270 | this.size = source.readByte(); 1271 | this.checksum = source.readByte(); 1272 | this.method = source.readMultiByte(5, "txt"); 1273 | this.packed = source.readInt(); 1274 | this.original = source.readInt(); 1275 | this.timeStamp = source.readInt(); 1276 | this.attribute = source.readByte(); 1277 | this.level = source.readByte(); 1278 | this.nameLength = source.readByte(); 1279 | this.name = source.readMultiByte(this.nameLength, "txt"); 1280 | 1281 | source.readShort(); 1282 | } 1283 | 1284 | window.YM = YM; 1285 | }()); 1286 | --------------------------------------------------------------------------------