1 /**
  2  * The Render Engine
  3  * SpriteLoader
  4  *
  5  * @fileoverview An extension of the image resource loader for handling
  6  *               sprites.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1556 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.resources.loaders.SpriteLoader",
 37     "requires":[
 38         "R.resources.loaders.ImageLoader",
 39         "R.resources.types.Sprite"
 40     ]
 41 });
 42 
 43 /**
 44  * @class Loads sprite resources and makes them available to the system.  Sprites are
 45  *        defined by an external JSON resource file.  A sprite definition file
 46  *        is a JSON file which can support single-line comments.  The format
 47  *        describes the image which contains the bitmap, the size of the bitmap,
 48  *        the version of the file, and the sprites.  Sprites can be either single
 49  *        frames or animations.  Animations are expected to be sequentially organized
 50  *        in the bitmap from left to right.  Each frame of an animation must be the exact
 51  *        same dimensions.
 52  *        <p/>
 53  *        A frame is simply defined by the upper left corner of the sprite and the
 54  *        width and height of the frame.  For an animation, the first four arguments are
 55  *        the same as a frame, followed by the frame count, the millisecond delay between
 56  *        frames, and the type of animation (either "loop" or "toggle").  A looped animation
 57  *        will play all frames, indicated by the frame count, and then start again from the
 58  *        beginning of the animation.  A toggled animation will play from the first to
 59  *        the last frame, then play from the last to the first, and then repeat.  The
 60  *        first and last frame will not be repeated in a toggled animation.  Thus, if
 61  *        the frames are A, B, C, then the toggle will play as A, B, C, B, A, B...
 62  * <pre>
 63  * {
 64  *    // Sprite definition file v2
 65  *    "bitmapImage": "bitmapFile.ext",
 66  *    "bitmapSize": [320, 320],
 67  *    "version": 2
 68  *    "sprites": {
 69  *        "stand": [0, 0, 32, 32],
 70  *        "walk": [32, 0, 32, 32, 3, 150, "loop"]
 71  *    }
 72  * }
 73  * </pre>
 74  *        <i>Note:</i> The new file structure is a bit more compact, and is indicated with
 75  *        the "version" key in the file, set to the value 2.  Version 1 will be deprecated
 76  *        and will not be supported in a future release of The Render Engine.
 77  *
 78  * @constructor
 79  * @param name {String=SpriteLoader} The name of the resource loader
 80  * @extends R.resources.loaders.ImageLoader
 81  */
 82 R.resources.loaders.SpriteLoader = function () {
 83     return R.resources.loaders.ImageLoader.extend(/** @scope R.resources.loaders.SpriteLoader.prototype */{
 84 
 85         sprites:null,
 86 
 87         queuedSprites:0,
 88 
 89         /** @private */
 90         constructor:function (name) {
 91             this.base(name || "SpriteLoader");
 92             this.sprites = {};
 93             this.queuedSprites = 0;
 94         },
 95 
 96         /**
 97          * Load a sprite resource from a URL.
 98          *
 99          * @param name {String} The name of the resource
100          * @param url {String} The URL where the resource is located
101          */
102         load:function (name, url /*, info, path */) {
103             if (!arguments[2]) {
104                 var loc = window.location;
105                 if (url.indexOf(loc.protocol) != -1 && url.indexOf(loc.hostname) == -1) {
106                     Assert(false, "Sprites must be located on this server");
107                 }
108 
109                 this.queuedSprites++;
110                 var thisObj = this;
111 
112                 // Get the file from the server
113                 R.engine.Script.loadJSON(url, function (spriteInfo) {
114                     // get the path to the resource file
115                     var path = url.substring(0, url.lastIndexOf("/"));
116                     thisObj.load(name, url, spriteInfo, path + "/");
117                     thisObj.afterLoad(name, spriteInfo, path + "/");
118                 });
119             }
120             else {
121                 var info = arguments[2], path = arguments[3];
122                 info.bitmapImage = path + info.bitmapImage;
123                 R.debug.Console.info("Loading sprite: " + name + " @ " + info.bitmapImage);
124 
125                 // Load the sprite image file
126                 if (!info.version || info.version == 1) {
127                     this.base(name, info.bitmapImage, info.bitmapWidth, info.bitmapHeight);
128                 } else if (info.version == 2) {
129                     this.base(name, info.bitmapImage, info.bitmapSize[0], info.bitmapSize[1]);
130                 }
131 
132                 // Store the sprite info
133                 this.sprites[name] = info;
134 
135                 // Since the path that is stored by ImageLoader is the path to the image
136                 // and not the descriptor, we need to override the value
137                 this.setPathUrl(name, url);
138 
139                 this.queuedSprites--;
140             }
141         },
142 
143         /**
144          * Called after the data has been loaded, passing along the info object and name
145          * of the sprite resource.
146          * @param name {String} The name of the sprite resource
147          * @param info {Object} The sprite resource definition
148          */
149         afterLoad:function (name, info) {
150         },
151 
152         /**
153          * Get the sprite resource with the specified name from the cache.  The
154          * object returned contains the bitmap as <tt>image</tt> and
155          * the sprite definition as <tt>info</tt>.
156          *
157          * @param name {String} The name of the object to retrieve
158          * @return {Object} An object with two keys: "image" and "info"
159          */
160         get:function (name) {
161             var bitmap = this.base(name);
162             var sprite = {
163                 resourceName:name,
164                 image:bitmap,
165                 info:this.sprites[name]
166             };
167             return sprite;
168         },
169 
170         /**
171          * Check to see if a named resource is "ready for use".
172          * @param name {String} The name of the resource to check ready status for,
173          *             or <tt>null</tt> for all resources in loader.
174          * @return {Boolean} <tt>true</tt> if the resource is loaded and ready to use
175          */
176         isReady:function (name) {
177             // If sprites are queued, we can't be totally ready
178             if (this.queuedSprites > 0) {
179                 return false;
180             }
181 
182             return this.base(name);
183         },
184 
185         /**
186          * Creates a {@link R.resources.types.Sprite} object representing the named sprite.
187          *
188          * @param resource {String} The name of a loaded sprite resource
189          * @param sprite {String} The name of the sprite from the resource
190          * @return {R.resources.types.Sprite} A {@link R.resources.types.Sprite} instance
191          */
192         getSprite:function (resource, sprite) {
193             var info = this.get(resource).info;
194             return R.resources.types.Sprite.create(sprite, info.sprites[sprite], this.get(resource), info.version || 1, this);
195         },
196 
197         /**
198          * Get the names of all the sprites available in a resource.
199          *
200          * @param resource {String} The name of the resource
201          * @return {Array} All of the sprite names in the given loaded resource
202          */
203         getSpriteNames:function (resource) {
204             var s = [];
205             var spr = this.sprites[resource].sprites;
206             for (var i in spr) {
207                 s.push(i);
208             }
209             return s;
210         },
211 
212         /**
213          * Export all of the sprites in the specified resource, as a JavaScript object, with the
214          * sprite name as the key and the corresponding {@link R.resources.types.Sprite} as the value.
215          * @param resource {String} The name of the sprite resource
216          * @param [spriteNames] {Array} An optional array of sprites to export, by name,
217          *         or <code>null</tt> to export all sprites
218          */
219         exportAll:function (resource, spriteNames) {
220             var o = {};
221             var sprites = this.getSpriteNames(resource);
222             for (var i in sprites) {
223                 if (!spriteNames || R.engine.Support.indexOf(spriteNames, sprites[i]) != -1) {
224                     o[sprites[i]] = this.getSprite(resource, sprites[i]);
225                 }
226             }
227             return o;
228         },
229 
230         /**
231          * The name of the resource this loader will get.
232          * @returns {String} The string "sprite"
233          */
234         getResourceType:function () {
235             return "sprite";
236         }
237 
238     }, /** @scope R.resources.loaders.SpriteLoader.prototype */ {
239         /**
240          * Get the class name of this object.
241          * @return {String} The string "R.resources.loaders.SpriteLoader"
242          */
243         getClassName:function () {
244             return "R.resources.loaders.SpriteLoader";
245         }
246     });
247 
248 }