1 /**
  2  * The Render Engine
  3  * Engine Class
  4  *
  5  * @fileoverview The main engine class
  6  *
  7  * @author: Brett Fattori (brettf@renderengine.com)
  8  * @author: $Author: bfattori@gmail.com $
  9  * @version: $Revision: 1557 $
 10  *
 11  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 12  *
 13  * Permission is hereby granted, free of charge, to any person obtaining a copy
 14  * of this software and associated documentation files (the "Software"), to deal
 15  * in the Software without restriction, including without limitation the rights
 16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 17  * copies of the Software, and to permit persons to whom the Software is
 18  * furnished to do so, subject to the following conditions:
 19  *
 20  * The above copyright notice and this permission notice shall be included in
 21  * all copies or substantial portions of the Software.
 22  *
 23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 26  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 28  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 29  * THE SOFTWARE.
 30  *
 31  */
 32 
 33 /**
 34  * @class The main engine class which is responsible for keeping the world up to date.
 35  * Additionally, the Engine will track and display metrics for optimizing a game. Finally,
 36  * the Engine is responsible for maintaining the local client's <tt>worldTime</tt>.
 37  * <p/>
 38  * The engine includes methods to load scripts and stylesheets in a serialized fashion
 39  * and report on the sound engine status.  Since objects are tracked by the engine, a list
 40  * of all game objects can be obtained from the engine.  The engine also contains the root
 41  * rendering context, or "default" context.  For anything to be rendered, or updated by the
 42  * engine, it will need to be added to a child of the default context.
 43  * <p/>
 44  * Other methods allow for starting or shutting down then engine, toggling metric display,
 45  * setting of the base "frames per second", toggling of the debug mode, and processing of
 46  * the script and function queue.
 47  * <p/>
 48  * Since JavaScript is a single-threaded environment, frames are generated serially.  One
 49  * frame must complete before another can be rendered.  By default, if frames are missed,
 50  * the engine will wait until the next logical frame can be rendered.  The engine can also
 51  * run where it doesn't skip frames, and instead runs a constant frame clock.  This
 52  * doesn't guarantee that the engine will run at a fixed frame rate.
 53  *
 54  * @static
 55  */
 56 R.Engine = Base.extend(/** @scope R.Engine.prototype */{
 57     version:"@ENGINE_VERSION",
 58     HOME_URL:"@HOME_URL",
 59     REF_NAME:"@REF_NAME",
 60 
 61     constructor:null,
 62 
 63     // Global engine options
 64     options:{},
 65 
 66     /*
 67      * Engine objects
 68      */
 69     idRef:0, // Object reference Id
 70     gameObjects:{}, // Live objects cache
 71     timerPool:{}, // Pool of running timers
 72     livingObjects:0, // Count of live objects
 73 
 74     /*
 75      * Engine info
 76      */
 77     fpsClock:16, // The clock rate (ms)
 78     FPS:undefined, // Calculated frames per second
 79     frameTime:0, // Amount of time taken to render a frame
 80     engineLocation:null, // URI of engine
 81     defaultContext:null, // The default rendering context
 82     debugMode:false, // Global debug flag
 83     localMode:false, // Local run flag
 84     started:false, // Engine started flag
 85     running:false, // Engine running flag
 86     shuttingDown:false, // Engine is shutting down
 87     upTime:0, // The startup time
 88     downTime:0, // The shutdown time
 89     skipFrames:true, // Skip missed frames
 90     totalFrames:0,
 91     droppedFrames:0,
 92     pclRebuilds:0,
 93 
 94     /*
 95      * Sound engine info
 96      */
 97     soundsEnabled:false, // Sound engine enabled flag
 98 
 99     /**
100      * The current time of the world on the client.  This time is updated
101      * for each frame generated by the Engine.
102      * @type {Number}
103      * @memberOf R.Engine
104      */
105     worldTime:0, // The world time
106 
107     /** @private */
108     lastTime:0, // The last timestamp the world was drawn
109 
110     /**
111      * The number of milliseconds the engine has been running.  This time is updated
112      * for each frame generated by the Engine.
113      * @type {Number}
114      * @memberOf R.Engine
115      */
116     liveTime:0, // The "alive" time (worldTime-upTime)
117 
118     /** @private */
119     shutdownCallbacks:[], // Methods to call when the engine is shutting down
120 
121     $GAME:null, // Reference to the game object
122 
123     // Issue #18 - Intrinsic loading dialog
124     loadingCSS:"<style type='text/css'>div.loadbox {width:325px;height:30px;padding:10px;font:10px Arial;border:1px outset gray;-moz-border-radius:10px;-webkit-border-radius:10px} #engine-load-progress { position:relative;border:1px inset gray;width:300px;height:5px} #engine-load-progress .bar {background:silver;}</style>",
125 
126     //====================================================================================================
127     //====================================================================================================
128     //                                      ENGINE PROPERTIES
129     //====================================================================================================
130     //====================================================================================================
131 
132     /**
133      * Set/override the engine options.
134      * @param opts {Object} Configuration options for the engine
135      * @memberOf R.Engine
136      * @private
137      */
138     setOptions:function (opts) {
139         // Check for a "defaults" key
140         var configOpts;
141         if (opts.defaults) {
142             configOpts = opts.defaults;
143         }
144 
145         // See if the OS has a key
146         var osOpts, platformDefaults, versionDefaults, platformVersions;
147         if (opts["platforms"] && opts["platforms"][R.engine.Support.sysInfo().OS]) {
148             // Yep, extract that one
149             osOpts = opts["platforms"][R.engine.Support.sysInfo().OS];
150 
151             // Check for platform defaults
152             if (osOpts && osOpts["defaults"]) {
153                 platformDefaults = osOpts["defaults"];
154             }
155         }
156 
157         // Check for general version options
158         if (opts["versions"]) {
159             versionDefaults = {};
160             for (var v in opts["versions"]) {
161                 if (R.engine.Support.sysInfo().version == v) {
162                     // Add version specific matches
163                     versionDefaults = $.extend(versionDefaults, opts["versions"][v]);
164                 }
165 
166                 if (parseFloat(R.engine.Support.sysInfo().version) >= parseFloat(v)) {
167                     // Add version match options
168                     versionDefaults = $.extend(versionDefaults, opts["versions"][v]);
169                 }
170             }
171         }
172 
173         // Finally, check the OS for version options
174         if (osOpts && osOpts["versions"]) {
175             platformVersions = {};
176             for (var v in osOpts["versions"]) {
177                 if (R.engine.Support.sysInfo().version == v) {
178                     // Add  version specific options
179                     platformVersions = $.extend(platformVersions, osOpts["versions"][v]);
180                 }
181 
182                 if (parseFloat(R.engine.Support.sysInfo().version) >= parseFloat(v)) {
183                     // Add version match options
184                     platformVersions = $.extend(platformVersions, osOpts["versions"][v]);
185                 }
186             }
187         }
188 
189         $.extend(R.Engine.options, configOpts, platformDefaults, versionDefaults, platformVersions);
190     },
191 
192     /**
193      * Set the debug mode of the engine.  Engine debugging enables helper objects
194      * which visually assist in debugging game objects.  To specify the console debug
195      * message output level, see {@link R.debug.Console@setDebuglevel}.
196      * <p/>
197      * Engine debug helper objects include:
198      * <ul>
199      * <li>A left/up glyph at the origin of objects using the {@link R.components.Transform2D} component</li>
200      * <li>Yellow outline in the shape of the collision hull of {@link R.objects.Object2D}, if assigned</li>
201      * <li>Yellow outline around objects using box or circle collider components</li>
202      * <li>Green outline around objects which are rendered with the {@link R.components.Billboard2D} component</li>
203      * <li>Blue outline around box and circle rigid body objects</li>
204      * <li>Red lines from anchor points in jointed {@link R.objects.PhysicsActor} objects</li>
205      * </ul>
206      *
207      * @param mode {Boolean} <tt>true</tt> to enable debug mode
208      * @memberOf R.Engine
209      */
210     setDebugMode:function (mode) {
211         R.Engine.debugMode = mode;
212     },
213 
214     /**
215      * Query the debugging mode of the engine.
216      *
217      * @return {Boolean} <tt>true</tt> if the engine is in debug mode
218      * @memberOf R.Engine
219      */
220     getDebugMode:function () {
221         return R.Engine.debugMode;
222     },
223 
224     /**
225      * Returns <tt>true</tt> if SoundManager2 is loaded and initialized
226      * properly.  The resource loader and play manager will use this
227      * value to execute properly.
228      * @return {Boolean} <tt>true</tt> if the sound engine was loaded properly
229      * @memberOf R.Engine
230      */
231     isSoundEnabled:function () {
232         return R.Engine.soundsEnabled;
233     },
234 
235     /**
236      * Set the FPS (frames per second) the engine runs at.  This value
237      * is mainly a suggestion to the engine as to how fast you want to
238      * redraw frames.  If frame execution time is long, frames will be
239      * processed as time is available. See the metrics to understand
240      * available time versus render time.
241      *
242      * @param fps {Number} The number of frames per second to refresh
243      *                     Engine objects.
244      * @memberOf R.Engine
245      */
246     setFPS:function (fps) {
247         Assert((fps != 0), "You cannot have a framerate of zero!");
248         R.Engine.fpsClock = Math.floor(1000 / fps);
249         R.Engine.FPS = undefined;
250     },
251 
252     /**
253      * Get the FPS (frames per second) the engine is set to run at.
254      * @return {Number}
255      * @memberOf R.Engine
256      */
257     getFPS:function () {
258         if (!R.Engine.FPS) {
259             R.Engine.FPS = Math.floor((1 / R.Engine.fpsClock) * 1000);
260         }
261         return R.Engine.FPS;
262     },
263 
264     /**
265      * Get the actual FPS (frames per second) the engine is running at.
266      * This value will vary as load increases or decreases due to the
267      * number of objects being rendered.  A faster machine will be able
268      * to handle a higher FPS setting.
269      * @return {Number}
270      * @memberOf R.Engine
271      */
272     getActualFPS:function () {
273         return Math.floor((1 / R.Engine.frameTime) * 1000);
274     },
275 
276     /**
277      * Get the amount of time allocated to draw a single frame.
278      * @return {Number} Milliseconds allocated to draw a frame
279      * @memberOf R.Engine
280      */
281     getFrameTime:function () {
282         return R.Engine.fpsClock;
283     },
284 
285     /**
286      * Get the amount of time it took to draw the last frame.  This value
287      * varies per frame drawn, based on visible objects, number of operations
288      * performed, and other factors.  The draw time can be used to optimize
289      * your game for performance.
290      * @return {Number} Milliseconds required to draw the frame
291      * @memberOf R.Engine
292      */
293     getDrawTime:function () {
294         return R.Engine.frameTime;
295     },
296 
297     /**
298      * Get the load the currently rendered frame is putting on the engine.
299      * The load represents the amount of
300      * work the engine is doing to render a frame.  A value less
301      * than one indicates the the engine can render a frame within
302      * the amount of time available.  Higher than one indicates the
303      * engine cannot render the frame in the time available.
304      * <p/>
305      * Faster machines will be able to handle more load.  You can use
306      * this value to gauge how well your game is performing.
307      * @return {Number}
308      * @memberOf R.Engine
309      */
310     getEngineLoad:function () {
311         return (R.Engine.frameTime / R.Engine.fpsClock);
312     },
313 
314     /**
315      * Get the default rendering context for the Engine.  This
316      * is the <tt>document.body</tt> element in the browser.
317      *
318      * @return {RenderContext} The default rendering context
319      * @memberOf R.Engine
320      */
321     getDefaultContext:function () {
322         if (R.Engine.defaultContext == null) {
323             R.Engine.defaultContext = R.rendercontexts.DocumentContext.create();
324         }
325 
326         return R.Engine.defaultContext;
327     },
328 
329     /**
330      * Override the engine's default context.  The engine will use
331      * the {@link R.rendercontexts.DocumentContext} as the default context,
332      * unless otherwise specified.
333      * @param defaultContext {R.rendercontexts.AbstracRenderContext} The context to use as the start of the
334      *      scene graph.
335      * @memberOf R.Engine
336      */
337     setDefaultContext:function (defaultContext) {
338         Assert(defaultContext instanceof R.rendercontexts.AbstractRenderContext, "Setting default engine context to object which is not a render context!");
339         R.Engine.defaultContext = defaultContext;
340     },
341 
342     /**
343      * Get the game object that has been loaded by the engine.  The game object isn't valid until the game is loaded.
344      * @return {R.engine.Game}
345      */
346     getGame:function () {
347         return R.Engine.$GAME;
348     },
349 
350     /**
351      * Get the path to the engine.  Uses the location of the <tt>/runtime/engine.js</tt>
352      * file that was initially loaded to determine the URL where the engine is running from.
353      * When files are included, or classes are loaded, they are loaded relative to the engine's
354      * location on the server.
355      *
356      * @return {String} The path/URL where the engine is located
357      * @memberOf R.Engine
358      */
359     getEnginePath:function () {
360         if (R.Engine.engineLocation == null) {
361             // Determine the path of the "engine.js" file
362             var head = document.getElementsByTagName("head")[0];
363             var scripts = head.getElementsByTagName("script");
364             for (var x = 0; x < scripts.length; x++) {
365                 var src = scripts[x].src;
366                 var m = src.match(/(.*\/engine)\/runtime\/engine\.js/);
367                 if (src != null && m) {
368                     // Get the path
369                     R.Engine.engineLocation = m[1];
370                     break;
371                 }
372             }
373         }
374 
375         return R.Engine.engineLocation;
376     },
377 
378     //====================================================================================================
379     //====================================================================================================
380     //                                  GLOBAL OBJECT MANAGEMENT
381     //====================================================================================================
382     //====================================================================================================
383 
384     /**
385      * Create an instance of an object within the Engine and get a unique Id for it.
386      * This is called by any object that extends from {@link R.engine.PooledObject}.
387      *
388      * @param obj {R.engine.PooledObject} An object within the engine
389      * @return {String} The global Id of the object
390      * @memberOf R.Engine
391      */
392     create:function (obj) {
393         if (R.Engine.shuttingDown === true) {
394             R.debug.Console.warn("Engine shutting down, '" + obj + "' destroyed because it would create an orphaned reference");
395             obj.destroy();
396             return null;
397         }
398 
399         Assert((R.Engine.started === true), "Creating an object when the engine is stopped!", obj);
400 
401         R.Engine.idRef++;
402         var objId = obj.getName() + R.Engine.idRef;
403         R.debug.Console.log("CREATED Object ", objId, "[", obj, "]");
404         R.Engine.livingObjects++;
405 
406         return objId;
407     },
408 
409     /**
410      * Destroys an object instance within the Engine.
411      *
412      * @param obj {R.engine.PooledObject} The object, managed by the engine, to destroy
413      * @memberOf R.Engine
414      */
415     destroy:function (obj) {
416         if (obj == null) {
417             R.debug.Console.warn("NULL reference passed to Engine.destroy()!  Ignored.");
418             return;
419         }
420 
421         var objId = obj.getId();
422         R.debug.Console.log("DESTROYED Object ", objId, "[", obj, "]");
423         R.Engine.livingObjects--;
424     },
425 
426     /**
427      * Add a timer to the pool so it can be cleaned up when
428      * the engine is shutdown, or paused when the engine is
429      * paused.
430      * @param timerName {String} The timer name
431      * @param timer {R.lang.Timer} The timer to add
432      * @memberOf R.Engine
433      */
434     addTimer:function (timerName, timer) {
435         R.Engine.timerPool[timerName] = timer;
436     },
437 
438     /**
439      * Remove a timer from the pool when it is destroyed.
440      * @param timerName {String} The timer name
441      * @memberOf R.Engine
442      */
443     removeTimer:function (timerName) {
444         R.Engine.timerPool[timerName] = null;
445         delete R.Engine.timerPool[timerName];
446     },
447 
448     /**
449      * Get an object by the Id that was assigned during the call to {@link #create}.
450      * Only objects that are contained within other objects will be found.  Discreetly
451      * referenced objects cannot be located by Id.
452      *
453      * @param id {String} The Id of the object to locate
454      * @return {R.engine.PooledObject} The object
455      * @memberOf R.Engine
456      */
457     getObject:function (id) {
458         function search(container) {
459             var itr = container.iterator();
460             while (itr.hasNext()) {
461                 var obj = itr.next();
462                 if (obj.getId && (obj.getId() === id)) {
463                     itr.destroy();
464                     return obj;
465                 }
466                 if (obj instanceof R.struct.Container) {
467                     // If the object is a container, search inside of it
468                     return search(obj);
469                 }
470             }
471             itr.destroy();
472             return null;
473         }
474 
475         // Start at the engine's default context
476         return search(R.Engine.getDefaultContext());
477     },
478 
479     //====================================================================================================
480     //====================================================================================================
481     //                                    ENGINE PROCESS CONTROL
482     //====================================================================================================
483     //====================================================================================================
484 
485     /**
486      * Load the minimal scripts required for the engine to start.
487      * @private
488      * @memberOf R.Engine
489      */
490     loadEngineScripts:function () {
491         // Engine stylesheet
492         R.engine.Script.loadStylesheet("/css/engine.css");
493 
494         // The basics needed by the engine to get started
495         R.engine.Linker._doLoad("R.engine.Game");
496         R.engine.Linker._doLoad("R.engine.PooledObject");
497         R.engine.Linker._doLoad("R.lang.Iterator");
498         R.engine.Linker._doLoad("R.rendercontexts.AbstractRenderContext");
499         R.engine.Linker._doLoad("R.rendercontexts.RenderContext2D");
500         R.engine.Linker._doLoad("R.rendercontexts.HTMLElementContext");
501         R.engine.Linker._doLoad("R.rendercontexts.DocumentContext");
502 
503         // Load the timers so that we don't require developers to do it
504         R.engine.Linker._doLoad("R.lang.AbstractTimer");
505         R.engine.Linker._doLoad("R.lang.IntervalTimer");
506         R.engine.Linker._doLoad("R.lang.MultiTimeout");
507         R.engine.Linker._doLoad("R.lang.OneShotTimeout");
508         R.engine.Linker._doLoad("R.lang.OneShotTrigger");
509         R.engine.Linker._doLoad("R.lang.Timeout");
510     },
511 
512     /**
513      * Starts the engine and loads the basic engine scripts.  When all scripts required
514      * by the engine have been loaded the {@link #run} method will be called.
515      *
516      * @param debugMode {Boolean} <tt>true</tt> to set the engine into debug mode
517      *                            which allows the output of messages to the console.
518      * @memberOf R.Engine
519      */
520     startup:function (debugMode) {
521         Assert((R.Engine.running == false), "An attempt was made to restart the engine!");
522 
523         // Check for supported browser
524         if (!R.Engine.browserSupportCheck()) {
525             return false;
526         }
527 
528         R.Engine.upTime = R.now();
529         //R.Engine.debugMode = debugMode ? true : false;
530         R.Engine.started = true;
531         R.Engine.totalFrames = 0;
532 
533         // Load the required scripts
534         R.Engine.loadEngineScripts();
535         return true;
536     },
537 
538     /**
539      * Starts or resumes the engine.  This will be called after all scripts have been loaded.
540      * You will also need to call this if you {@link #pause} the engine.  Any paused timers
541      * will also be resumed.
542      * @memberOf R.Engine
543      */
544     run:function () {
545         if (R.Engine.shuttingDown || R.Engine.running) {
546             return;
547         }
548 
549         // Restart all of the timers
550         for (var tm in R.Engine.timerPool) {
551             R.Engine.timerPool[tm].restart();
552         }
553 
554         var mode = "[";
555         mode += (R.Engine.debugMode ? "DEBUG" : "");
556         mode += (R.Engine.localMode ? (mode.length > 0 ? " LOCAL" : "LOCAL") : "");
557         mode += "]";
558         R.debug.Console.warn(">>> Engine started. " + (mode != "[]" ? mode : ""));
559         R.Engine.running = true;
560         R.Engine.shuttingDown = false;
561 
562         R.debug.Console.debug(">>> sysinfo: ", R.engine.Support.sysInfo());
563 
564         R.Engine._pauseTime = R.now();
565         R.Engine._stepOne = 0;
566         R.Engine.lastTime = R.now() - R.Engine.fpsClock;
567 
568         // Start world timer
569         R.Engine.engineTimer();
570     },
571 
572     /**
573      * Steps the engine when paused.  Any timers that were paused, stay paused while stepping.
574      * @memberOf R.Engine
575      */
576     step:function () {
577         if (R.Engine.running) {
578             // Need to pause the engine to step
579             return;
580         }
581 
582         R.Engine._stepOne = 1;
583         R.Engine.engineTimer();
584     },
585 
586     /**
587      * Pauses the engine and any running timers.
588      * @memberOf R.Engine
589      */
590     pause:function () {
591         if (R.Engine.shuttingDown) {
592             return;
593         }
594 
595         // Pause all of the timers
596         R.debug.Console.debug("Pausing all timers");
597         for (var tm in R.Engine.timerPool) {
598             R.Engine.timerPool[tm].pause();
599         }
600 
601         R.debug.Console.warn(">>> Engine paused <<<");
602         window.clearTimeout(R.Engine.globalTimer);
603         R.Engine.running = false;
604         R.Engine._pauseTime = R.now();
605     },
606 
607     /**
608      * Add a method to be called when the engine is being shutdown.  Use this
609      * method to allow an object, which is not referenced by the engine, to
610      * perform cleanup actions.
611      *
612      * @param fn {Function} The callback function
613      * @memberOf R.Engine
614      */
615     onShutdown:function (fn) {
616         if (R.Engine.shuttingDown === true) {
617             return;
618         }
619 
620         R.Engine.shutdownCallbacks.push(fn);
621     },
622 
623     /**
624      * Shutdown the engine.  Stops the global timer and cleans up (destroys) all
625      * objects that have been created and added to the engine, starting at the default
626      * engine context.
627      * @memberOf R.Engine
628      */
629     shutdown:function () {
630         if (R.Engine.shuttingDown) {
631             // Prevent another shutdown
632             return;
633         }
634 
635         R.Engine.shuttingDown = true;
636 
637         if (!R.Engine.running && R.Engine.started) {
638             // If the engine is not currently running (i.e. paused)
639             // restart it and then re-perform the shutdown
640             R.Engine.running = true;
641             setTimeout(function () {
642                 R.Engine.shutdown();
643             }, (R.Engine.fpsClock * 2));
644             return;
645         }
646 
647         R.Engine.started = false;
648         R.debug.Console.warn(">>> Engine shutting down...");
649 
650         // Stop world timer
651         R.global.clearTimeout(R.Engine.globalTimer);
652 
653         // Run through shutdown callbacks to allow unreferenced objects
654         // to clean up references, etc.
655         while (R.Engine.shutdownCallbacks.length > 0) {
656             R.Engine.shutdownCallbacks.shift()();
657         }
658 
659         if (R.Engine.metricDisplay) {
660             R.Engine.metricDisplay.remove();
661             R.Engine.metricDisplay = null;
662         }
663 
664         // Cancel all of the timers
665         R.debug.Console.debug(">>> Cancelling all timers");
666         for (var tm in R.Engine.timerPool) {
667             R.Engine.timerPool[tm].cancel();
668         }
669         R.Engine.timerPool = {};
670 
671         R.Engine.downTime = R.now();
672         R.debug.Console.warn(">>> Engine stopped.  Runtime: " + (R.Engine.downTime - R.Engine.upTime) + "ms");
673         R.debug.Console.warn(">>>   frames generated: ", R.Engine.totalFrames);
674 
675         R.Engine.running = false;
676 
677         // Kill off the default context and anything
678         // that's attached to it.  We'll alert the
679         // developer if there's an issue with orphaned objects
680         R.Engine.getDefaultContext().destroy();
681 
682         // Dump the object pool
683         R.engine.PooledObject.objectPool = null;
684 
685         AssertWarn((R.Engine.livingObjects == 0), "Object references were not cleaned up!");
686 
687         R.Engine.loadedScripts = {};
688         R.Engine.scriptLoadCount = 0;
689         R.Engine.scriptsProcessed = 0;
690         R.Engine.defaultContext = null;
691 
692         // Shutdown complete
693         R.Engine.shuttingDown = false;
694     },
695 
696     /**
697      * See {@link #define} instead.
698      * @deprecated
699      * @memberOf R.Engine
700      */
701     initObject:function (objectName, primaryDependency, fn) {
702         throw new Error("Unsupported - See R.Engine.define() instead");
703     },
704 
705     /**
706      * Defines a new class.  The format of the object definition is:
707      * <pre>
708      * R.Engine.define({
709     *    "class": "[class name]",
710     *    "requires": [
711     *       "R.[package name].[dependency]"
712     *    ],
713     *    "depends": [
714     *       "[dependency]"
715     *    ],
716     *    "includes": [
717     *       "/path/to/file.js"
718     *    ]
719     * });
720      * </pre>
721      * Each class must define its class name via the "class" key.  This is the name that
722      * other classes will use to locate the class object.  The <tt>"requires"</tt> key defines the
723      * classes within the engine that the class is dependent upon.  Anything that falls into
724      * the <tt>"R."</tt> namespace should be declared as a requirement here. The "requires" key
725      * performs class loading for these objects automatically.  In other words, you do not need
726      * to load classes which start with <tt>"R."</tt>.
727      * <p/>
728      * If your class has dependencies on classes <i>not defined in the <tt>"R"</tt> namespace</i>,
729      * they should be declared via the <tt>"depends"</tt> array.  These are classes which your game
730      * classes need to load via {@link R.engine.Game#load} calls.  For files which just need to be
731      * loaded, use the <tt>"include"</tt> key to tell the engine where the file is.
732      * <p/>
733      * Until all requirements, dependencies, and included files have been loaded and/or initialized,
734      * a class will, itself, not be initialized. Be aware of class dependencies so you do not create
735      * circular dependencies.  First-level circular dependencies are okay, such as <tt>A</tt> requires
736      * <tt>B</tt>, while <tt>B</tt> requires <tt>A</tt>.  But second, third, and so on circular
737      * dependencies will cause your classes to remain unresolved. The engine will not start the game,
738      * and an erro message will be sent to the console listing classes which were resolved and those
739      * which are unresolved.
740      * <p/>
741      * The <tt>"requires"</tt>, <tt>"includes"</tt> and <tt>"depends"</tt> keys are optional.  You
742      * can either omit them entirely, set them to <code>null</code>, or assign an empty array to them.
743      * <p/>
744      * The <tt>"depends"</tt> key is the only way your game classes can establish class dependencies
745      * which are <i>not in the <tt>"R."</tt> namespace</i>.  Classes specified via the
746      * <tt>"depends"</tt> key are not loaded via the engine class loader like <tt>"requires"</tt>
747      * does.  Instead, your game will need to load the classes.  For example:
748      * <pre>
749      * R.Engine.define({
750     *    "class": "Foo",
751     *    "requires": [
752     *       "R.rendercontexts.CanvasContext"
753     *    ],
754     *    "depends": [
755     *       "Bar"
756     *    ]
757     * });
758      *
759      * // Load the Bar class
760      * R.engine.Game.load("bar.js");
761      * </pre>
762      * After receiving the definition, the engine will load <tt>R.rendercontexts.CanvasContext</tt>
763      * for <tt>Foo</tt>. The call to <code>R.engine.Game.load("bar.js")</code> would load the
764      * <tt>Bar</tt> class.  When the context and <tt>Bar</tt> have loaded and initialized, <tt>Foo</tt>
765      * can be initialized which will enable any classes dependent on <tt>Foo</tt> to be initialized.
766      *
767      * @param classDef {Object} The object's definition
768      * @memberOf R.Engine
769      */
770     define:function (classDef) {
771         R.engine.Linker.define(classDef);
772     },
773 
774     /**
775      * Check the current browser to see if it is supported by the
776      * engine.  If it isn't, there's no reason to load the remainder of
777      * the engine.  This check can be disabled with the <tt>disableBrowserCheck</tt>
778      * query parameter set to <tt>true</tt>.
779      * <p/>
780      * If the browser isn't supported, the engine is shutdown and a message is
781      * displayed.
782      * @memberOf R.Engine
783      * @private
784      */
785     browserSupportCheck:function () {
786         if (R.engine.Support.checkBooleanParam("disableBrowserCheck")) {
787             return true;
788         }
789         var sInfo = R.engine.Support.sysInfo();
790         var msg = "This browser is not currently supported by <i>" + R.Engine.REF_NAME + "</i>.<br/><br/>";
791         msg += "Please go <a href='" + R.Engine.HOME_URL + "' target='_blank'>here</a> for more information.";
792         switch (sInfo.browser) {
793             case "iPhone":
794             case "android":
795             case "msie":
796             case "chrome":
797             case "Wii":
798             case "safari":
799             case "safarimobile":
800             case "mozilla":
801             case "firefox":
802             case "opera":
803                 return true;
804             default:
805                 R.debug.Console.warn("Unsupported Browser");
806                 $("body", document).empty().append($("<div style='font:12pt Arial,sans-serif;'>").html(msg));
807                 return false;
808         }
809     },
810 
811     /**
812      * Prints the version of the engine.
813      * @memberOf R.Engine
814      */
815     toString:function () {
816         return "The Render Engine " + R.Engine.version;
817     },
818 
819     //====================================================================================================
820     //====================================================================================================
821     //                                        THE WORLD TIMER
822     //====================================================================================================
823     //====================================================================================================
824 
825     /**
826      * This is the process which updates the world.  It starts with the default
827      * context, telling it to update itself.  Since each context is a container,
828      * all of the objects in the container will be called to update, and then
829      * render themselves.
830      *
831      * @private
832      * @memberOf R.Engine
833      */
834     engineTimer:function () {
835         if (R.Engine.shuttingDown) {
836             return;
837         }
838 
839         if (!R.Engine.running && R.Engine._stepOne == 0) {
840             // Not stepping, done here
841             return;
842         }
843 
844         var nextFrame = R.Engine.fpsClock;
845 
846         // Update the world
847         if ((R.Engine._stepOne == 1 || R.Engine.running) && R.Engine.getDefaultContext() != null) {
848             R.Engine.vObj = 0;
849             R.Engine.rObjs = 0;
850             //R.Engine.pclRebuilds = 0;
851 
852 
853             // Render a frame
854             R.Engine.worldTime = R.Engine._stepOne == 1 ? R.Engine._pauseTime : R.now();
855             R.Engine.lastTime = R.Engine._stepOne == 1 ? R.Engine.worldTime - R.Engine.fpsClock : R.Engine.lastTime;
856 
857             var deltaTime = R.Engine.worldTime - R.Engine.lastTime;
858 
859             // Tick the game
860             if (R.Engine.$GAME) {
861                 R.Engine.$GAME.tick(R.Engine.worldTime, deltaTime);
862             }
863 
864             // Pass parent context, world time, delta time
865             R.Engine.getDefaultContext().update(null, R.Engine.worldTime, deltaTime);
866             R.Engine.lastTime = R.Engine.worldTime;
867             R.Engine.frameTime = R.now() - R.Engine.worldTime;
868 
869             if (R.Engine._stepOne == 1) {
870                 R.Engine._pauseTime += R.Engine.frameTime;
871             }
872 
873             R.Engine.liveTime = R.Engine.worldTime - R.Engine.upTime;
874 
875             // Count the number of frames generated
876             R.Engine.totalFrames++;
877 
878             // Determine when the next frame should draw
879             // If we've gone over the allotted time, wait until the next available frame
880             var f = nextFrame - R.Engine.frameTime;
881             nextFrame = (R.Engine.skipFrames ? (f > 0 ? f : nextFrame) : R.Engine.fpsClock);
882             R.Engine.droppedFrames += (f <= 0 ? Math.round((f * -1) / R.Engine.fpsClock) : 0);
883 
884             // Update the metrics display
885             R.Engine.doMetrics();
886         }
887 
888         if (R.Engine._stepOne == 1) {
889             // If stepping, don't re-call the engine timer automatically
890             R.Engine._stepOne = 0;
891             return;
892         }
893 
894         // When the process is done, start all over again
895         if (R.Engine.options.nativeAnimationFrame) {
896             R.global.nativeFrame(R.Engine.engineTimer /*, R.Engine.getDefaultContext().getSurface()*/);
897         } else {
898             R.Engine.globalTimer = setTimeout(function _engineTimer() {
899                 R.Engine.engineTimer();
900             }, nextFrame);
901         }
902     },
903 
904     /**
905      * @private
906      */
907     doMetrics:function () {
908         if (R.debug && R.debug.Metrics) {
909             R.debug.Metrics.doMetrics();
910         }
911     },
912 
913     // ======================================================
914     // References to R.engine.Script methods
915     // ======================================================
916 
917     /**
918      * Include a script file.
919      *
920      * @param scriptURL {String} The URL of the script file
921      * @memberOf R.Engine
922      */
923     include:function (scriptURL) {
924         R.engine.Script.include(scriptURL);
925     },
926 
927     /**
928      * Loads a game's script.  This will wait until the specified
929      * <tt>gameObjectName</tt> is available before running it.  Doing so will
930      * ensure that all dependencies have been resolved before starting a game.
931      * Also creates the default rendering context for the engine.
932      * <p/>
933      * All games should execute this method to start their processing, rather than
934      * using the script loading mechanism for engine or game scripts.  This is used
935      * for the main game script only.  Normally it would appear in the game's "index" file.
936      * <pre>
937      *  <script type="text/javascript">
938      *     // Load the game script
939      *     Engine.loadGame('game.js','Spaceroids');
940      *  </script>
941      * </pre>
942      *
943      * @param gameSource {String} The URL of the game script.
944      * @param gameObjectName {String} The string name of the game object to execute.  When
945      *                       the framework if ready, the <tt>startup()</tt> method of this
946      *                       object will be called.
947      * @param [gameDisplayName] {String} An optional string to display in the loading dialog
948      * @memberOf R.Engine
949      */
950     loadGame:function (gameSource, gameObjectName, gameDisplayName) {
951         R.engine.Script.loadGame(gameSource, gameObjectName, gameDisplayName);
952     }
953 
954 }, { // Interface
955     /** @private */
956     globalTimer:null
957 });
958 
959