Source: MC.js

// MC.js
// MaxCanvas 2D engine for the HTML5 <canvas> element
// Copyright Max Bryans (Contact @ maxsfw3@gmail.com)

/**
* (<b>MaxCanvas</b>)
* <br> Version 1.0 (June 2018 - April 2019)
* <br> First Release
* <br>All Engine Variables reside in this namespace.
* <br><b>Avoid extending as future versions may overwrite your work !<b>
* @name <b>MC</b>
* @namespace MC
*/

var MC = {
    _canvas: null,
    _maths: null,
    _draw: null,
    _utils: null,
    _mouse: null,
    _keys: null,
    _Typeface: null,
    _GUI: null,
    _ConBox: null,
    _Point: null,
    _Color: null,
    _Picture: null,
    _SpriteBin: null
    };  // End of var MC Object Declaration


////////////////////    
// INITIALISATION //
////////////////////
/**
*<hr>
*Initialises the MaxCanvas (MC) Engine by passing it a reference to the canvas it is to use.
*<br>(1) If the parameter string "fullWindow" is entered, the canvas will automatically re-size to the browser window size.  It will subsequently be resized with the window.
*@param {Object} canvas - HTML canvas to be used
*@param {String} [fullWindow] - (1)
*@example
*var c=document.getElementById("canvas");
*MC.init(c,"fullWindow");
*/
MC.init = function(canvas,fullWindow) {
    // Establish Internal Namespaces
    _canvas = this.canvas;
    _game = this.game;
    _maths = this.maths;
    _draw = this.draw;
    _utils = this.utils;
    _mouse = this.mouse;
    _keys = this.keys;
    _ConBox = this.ConBox;
    _GUI = this.GUI;
    _Typeface = this.Typeface;
    _Point = this.Point;
    _Color = this.Color;
    _Picture = this.Picture;
    _Sprite = this.Sprite;
    _SpriteBin = this.SpriteBin;
    
    _canvas.canvas = canvas;
    _draw.context = canvas.getContext("2d");
    _canvas.width = canvas.width;
    _canvas.height = canvas.height;
    _canvas.midX = _canvas.width / 2;
    _canvas.midY = _canvas.height / 2;
    _canvas.setBounds();
    
    _backCanvas = document.createElement('canvas');
    _backCanvasContext = _backCanvas.getContext('2d');
    
    if (fullWindow) {
        if (fullWindow === "fullWindow") {
            this.canvas.fullWindow = true;
            _canvas.windowResize();
        }
    }
    window.onresize = function(e) { _canvas.windowResize(); }; // establish listener for windows re-size. will fullScreen canvas to match if _canvas.fullWindow == true 
    _game.gravity = new _Point();
    _game.mouse = new _Point();
    _game.oldTime = new Date().getTime();
    _game.text = new _Typeface("Arial", 20, "Black");
    _game.conBoxDefaults = {colorBody: "Orange", colorBodyHover: "DarkGreen", colorEdge: "Black", colorEdgeHover: "White", edgeWidth: 5};
    _mouse.init(); // Mouse Events Listeners added here
    _keys.init(); // Keyboard Listeners added here

    
}; // End of INITIALISATION

////////////////////    
//     CANVAS     //
////////////////////
/**
*<hr>
*The canvas object contains references to and records properties of the canvas associated with the MaxCanvas engine at Initialisation
*<br>Properties are automatically updated as required.
*@property {Object} canvas Reference to the canvas associated with this instance of the MaxCanvas engine.
*<br>Defined at initialisation
*@property {Number} width Width of the full canvas (in pixels)
*@property {Number} height Height of the full canvas (in pixels)
*@property {Number} midX X coordinate of the canvas middle (in pixels)
*@property {Number} midY Y coordinate of the canvas middle (in pixels)
*@property {Number} top Top of the canvas after "bounds" have been applied (in pixels)
*@property {Number} bottom Bottom of the canvas after "bounds" have been applied (in pixels)
*@property {Number} left Left of the canvas after "bounds" have been applied (in pixels)
*@property {Number} right Right of the canvas after "bounds" have been applied (in pixels)
*@property {Boolean} fullWindow <tt>true</tt> if the Canvas is set to size to the window (and resize with it)
*@interface
*/
MC.canvas = {
    canvas: null,
    width: 0,
    height: 0,
    midX: 0,
    midY: 0,
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    fullWindow: false,
    
    /**
     *<hr>
     *Sets the <tt>canvas.top, bottom, left</tt> and <tt>right</tt> properties to reflect <tt>game.bounds</tt> and canvas dimensions
     *<br>CAUTION.  This is intended for Engine internal use.  Developers should use <tt>game.setBounds()</tt> to safely change a canvas's bounds
     */
    setBounds: function() {
        this.top = this.height * _game.bounds.top;
        this.bottom = this.height * _game.bounds.bottom;
        this.left = this.width * _game.bounds.left;
        this.right = this.width * _game.bounds.right;
    },
    
    /**
     *<hr>
     *Sets the canvas element's to a new width and height
     *<br>All <tt>canvas</tt> properties (including bounds) are automatically reset
     *@param {Number} newWidth New canvas width (in pixels)
     *@param {Number} newHeight New canvas height (in pixels)
     */
    setSize: function(newWidth,newHeight) {
        if (_utils.isNumeric(newWidth) && _utils.isNumeric(newHeight)) {
            this.canvas.width = newWidth;
            this.canvas.height = newHeight;
            this.width = newWidth;
            this.height = newHeight;
            this.midX = newWidth/2;
            this.midY = newHeight/2;
            this.setBounds();
        }
    },
    
    /**
     *<hr>
     *Console logs the canvas properties
     */
    log: function() {
        console.log("Canvas status >> W: "+this.width+", H: "+this.height+", top: "+this.top+", bottom: "+this.bottom+", left: "+this.left+", right: "+this.right+", fullWindow: "+this.fullWindow);
    },
    
    /**
     *<hr>
     *Handles window re-size
     *<br>CAUTION.  This is intended for Engine internal use and is called automatically if required.
     */
    windowResize: function () {
        if (this.fullWindow) {
            this.canvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
            this.canvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
                    // next 2 lines from --- https://stackoverflow.com/questions/242608/disable-browsers-vertical-and-horizontal-scrollbars (June 2018)
                    document.documentElement.style.overflow = 'hidden';  // firefox, chrome
                    document.body.scroll = "no"; // ie only
            this.canvas.style.border = "none";
            this.canvas.style.left = '0px';
            this.canvas.style.top =  '0px';
            this.canvas.style.right = '0px';
            this.canvas.style.bottom =  '0px';
            this.width = this.canvas.width;
            this.height = this.canvas.height;
            this.midX = this.Width/2;
            this.midY = this.Height/2;
            this.setBounds();
        }
    }
    
    
}; // End of CANVAS

////////////////////    
//     GAME       //
////////////////////
/**
*<hr>
*The game object contains essential game related properties
*@property  {MC.Point} gravity The Global gravity acceleration vector to be applied to all Sprites which have their <tt>gravity</tt> property set to <tt>true</tt>
*<br>Set at initialisation to <tt>(0,0)</tt>
*@property {Number} deltaTime The time in SECONDS since the last frame
*<br>Refreshed by calling the MC.game.update() method within any game loop <tt>requestAnimationFrame()</tt> calls
*<br> So useful some developers use <tt>var DT = MC.game.deltaTime;</tt> as a shortcut
*@property {Number} oldTime Time of the previous frame
*<br>WARNING: used internally by the MC engine. <b>DO NOT MODIFY</b>
*<br>Initialised to Game Start time.
*@property {Object} bounds Comprising <tt>top, bottom, left</tt> and <tt>right</tt>.
*<br>These are the ratio of the actual canvas to be to be considered the "game boundry" for the purposes of Sprite edge checks.
*<br>Set to the whole canvas at initialisation.  i.e. <tt>(top: 0, bottom: 1, left: 0, right: 1)</tt> 
*<br>e.g. a 800 x 600px canvas could have it's bounds set to <tt>(top: 0, bottom: 1, left: 0.25, right: 0.75)</tt> in which case the game boundries would be a 400px central column in the middle of the canvas.
* <NB>CAUTION use <tt>game.setBounds()</tt> to modify
*@property {Number} physicsUpdates=1 - The number of physics updates to be applied each during the update cycle
*@property {MC.Point} mouse The canvas coordinates of the user mouse pointer (and mirrors <tt>MC.mouse.pos</tt>)
*<br>This is usually considered important enough for Developers to move it into Global name space with <tt>var mouse = MC.game.mouse</tt>.  Allowing simple <tt>mouse.x</tt> and <tt>mouse.y</tt> to be used
*@property {Boolean} paused=false When <tt>true</tt> then MC.game.deltaTime will not update
*@property {MC.Typeface} text Defining the default MC.Engine text font, size and color.  Used by any text when nothing else is defined.  Initialised by <tt>MC.init()</tt>
*@property {Object} conBoxDefaults Set during <tt>MC.init()</tt> This container object holds the default values for the MC.ConBox objects.
*<br>N.B. ConBox default font is the same as the above <tt>MC.game.text.font</tt> value. Edit these to save having to do it for every new box.
*<br><b>Values</b>
*<br>colorBody (default = "Orange")
*<br>colorBodyHover (default = "DarkGreen")
*<br>colorEdge (default = "Black")
*<br>colorEdgeHover (default = "White")
*<br>edgeWidth (default = 5)
*@interface
*/
MC.game = {
    gravity: null,  // initialised as a Point(0,0)
    deltaTime: 0,
    oldTime: null,
    bounds: {top: 0, bottom: 1, left: 0, right: 1},
    physicsUpdates: 1,
    mouse: null,
    paused: false,
    text: null,
    conBoxDefaults: null,

    
    /**
     *<hr>
     *<br> Call this method in any <tt>requestAnimationFrame()</tt> to update MC.game.deltaTime
     *<br> If paused (i.e. <tt>MC.game.paused == true</tt>) deltaTime is set to 0 (zero)
     */
    update: function() {
        var Now = new Date().getTime();
        if (!this.paused) {
            this.deltaTime = (Now - this.oldTime)/1000;
        }
        else {
            this.deltaTime = 0;
        }
        this.oldTime = Now;    
    },

    /**
     *<hr>Sets the game boundaries on the canvas (as proportions of canvas height and width)
     *<br> All parameters are clamped to 0-1
     *@param {Number} top Top boundary
     *@param {Number} bottom Bottom boundary
     *@param {Number} left Left boundary
     *@param {Number} right Right boundary
     */
    setBounds: function (top,bottom,left,right) {
        this.bounds.top = _maths.clamp(top,0,1);
        this.bounds.bottom = _maths.clamp(bottom,0,1);
        this.bounds.left = _maths.clamp(left,0,1);
        this.bounds.right = _maths.clamp(right,0,1);
        _canvas.setBounds();
    },
    
    /**
     *<hr>
     *Resets the game boundaries to mirror the full canvas height/width  i.e. the equivalent of calling <tt>setBounds(0,1,0,1);</tt>
     */
    resetBounds: function () {
        this.bounds.top = 0;
        this.bounds.bottom = 1;
        this.bounds.left = 0;
        this.bounds.right = 1;
        _canvas.setBounds();
    },
    
    /**
     *<hr>
     *Displays the game FPS (0 is paused) in the top left of the canvas
     *<br>Drawn using the <tt>MC.game.text</tt> default Typeface
     *<br>FPS is logged in 10fps increments (to render it legible)
     */
    fpsLog : function () {
        var t = "FPS: "+ ~~(1/this.deltaTime/10)*10;
        _draw.text(t,new MC.Point(2,2+this.text.size));
    },
    
    /**
     *<hr>
     *Displays the mouse co-ordinates in the bottom left of the canvas
     *<br>Drawn using the <tt>MC.game.text</tt> default Typeface
     */
     mouseLog : function () {
        _draw.text("Mouse "+ this.mouse.x + "/" + this.mouse.y,new MC.Point(2,_canvas.bottom - 4));
     }
    
    
    
}; // End of GAME

////////////////////    
//     MATHS      //
////////////////////
/**
*The maths object provides useful methods for Developer and Engine internal use.
*@property {Number} TO_RADIANS Ratio for converting Degrees to Radians (i.e. Pi / 180)
*@property {Number} TO_DEGREES - Ratio for converting Radians to Degrees (i.e. 180 / Pi)
*@property {Number} TWOPI (Pi * 2)
*@interface
*
*/
MC.maths = {
    TO_RADIANS: Math.PI / 180,
    TO_DEGREES: 180 / Math.PI,
    TWOPI: 2 * Math.PI,
 
    /**
    *<hr>
    *Degree to Radian convertion
    *@param {Number} angle - Angle in Degrees
    *@returns {Number} Angle in Radians
    */
    degToRad: function (angle) {
        return angle * _maths.TO_RADIANS;
    },
    
    /**
    *<hr>
    *Radian to Degree convertion
    *@param {Number} angle - Angle in Radians
    *@returns {Number} Angle in Degrees
    */
    radToDeg: function (angle) {
        return angle * _maths.TO_DEGREES;
    },
    
    /**
    *<hr>
    *@param {Number} - returns a minimum 2 length HEX number.
    *<br>  Used internally to convert Color values from rgb to Hex notation
    *@returns {string}
    */
    toHex: function(number) {
        // https://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hex-in-javascript (June 2018)
        var mod = "";
        var Ret = "";
        if (number < 0) {number = 0xFFFFFFFF + number + 1; mod = "-";}
        Ret = number.toString(16).toUpperCase();
        if (Ret.length < 2) {Ret = "0"+Ret;}
        Ret = mod+Ret;
        return Ret;
    },
    
     /**
     *<hr>
     *Clamps a number between a pair of values
     *@param {Number} value - Number to be clamped
     *@param {Number} min - Minimum clamp value
     *@param {Number} max - Maximum clamp value
     *@returns {Number} The clamped value
     */
    clamp: function (value, min, max) {
        if (value >= max) return max;
        else if (value <= min) {return min;}
        else return value;
    },
    
     /**
     *<hr>
     *Returns a Random Integer between the provided values (inclusive)
     *@param {Number} min - Mimimum value for the random number
     *@param {Number} max - Maximum value for the random number
     *@returns {Number} A Random Integer within the required range
     */
    randBetween: function(min, max) {
         return Math.floor(Math.random() * (max - min + 1)) + min;
    },
    
     /**
     *<hr>
     *Linear Interpolation between two values (either points or numbers)
     *@param {Number|MC.Point} start - Starting (datum) point or number
     *@param {Number|MC.Point} finish - Target point or number
     *@param {Number} ratio - Proportion of travel required (N.B. decimals and -ve numbers are accepted)
     *@returns {Number|MC.Point}
     *@throws Console warning when neither a pair of Points or numbers passed in.  Start parameter is returned.
     */
    lerp: function(start,finish,ratio) {
        if (_utils.hasXY(start) && _utils.hasXY(finish)) {
            return new _Point(start.x + ((finish.x - start.x) * ratio), start.y + ((finish.y - start.y) * ratio));
        }
        else if (_utils.isNumeric(start) && _utils.isNumeric(finish)) {
            return start + ((finish - start) * ratio);
        }
        else {
            console.log("WARNING: Neither a pair of points or numbers passed to lerp()");            
            return start;
        }
    },
    
     /**
     *<hr>
     *Returns the lowest value in a list of arguments OR an array
     *<br>Similar to <tt>Math.min()</tt> but works when passed an array of numbers
     *@param {Number|Array} arg Comma seperated list of numbers OR an Array of numbers
     *@returns {Number} The lowest number presented
     *@throws Console warning when nothing passed in.  0 (zero) returned
     */
    minOf: function(arg) {
        if (arguments.length == 0) {
            console.log("WARNING: nothing passed to minOf()");
            return 0;
        }
        var L = 0;
        var lowest = 0;
        if (typeof arg === "array" || arg instanceof Array) {
            lowest = arg[0];
            L = arg.length;
            for (var i = 1 ; i < L; i++)
                if (arg[i] < lowest) lowest = arg[i];
            return lowest;
        }
        else {
            L = arguments.length;
            lowest = arguments[0];
            for (var i = 1 ; i < L; i++)
                if (arguments[i] < lowest) lowest = arguments[i];
            return lowest;
        }
    },
    
    /**
    *<hr>
    *Returns the highest value in a list of arguments OR an array
    *<br>Similar to <tt>Math.max()</tt> but works when passed an array of numbers
    *@param {Number|Array} arg Comma seperated list of numbers OR an Array of numbers
    *@returns {Number} The highest number presented
    *@throws Console warning when nothing passed in.  0 (zero) returned
    */
    maxOf: function(arg) {
        if (arguments.length == 0) {
            console.log("WARNING: nothing passed to maxOf()");
            return 0;
        }
        var L = 0;
        var highest = 0;
        if (typeof arg === "array" || arg instanceof Array) {
            highest = arg[0];
            L = arg.length;
            for (var i = 1 ; i < L; i++)
                if (arg[i] > highest) highest = arg[i];
            return highest;
        }
        else {
            L = arguments.length;
            highest = arguments[0];
            for (var i = 1 ; i < L; i++)
                if (arguments[i] > highest) highest = arguments[i];
            return highest;
        }
    },
    
    /**
    *<hr>
    *Returns the mean/average value in a list of arguments OR an array
    *@param {Number|Array} arg Comma seperated list of numbers OR an Array of numbers
    *@returns {Number} The average value
    *@throws Console warning when nothing passed in.  0 (zero) returned
    */
    meanOf: function (arg) {
        if (arguments.length == 0) {
            console.log("WARNING: nothing passed to meanOf()");
            return 0;
        }
        var L = 0;
        var count = 0;
        if (typeof arg === "array" || arg instanceof Array) {
            L = arg.length;
            for (var i = 0 ; i < L; i++) count += arg[i];
            return count / L;
        }
        else {
            L = arguments.length;
            for (var i = 0 ; i < L; i++) count += arguments[i];
            return count / L;
        }
    },
    
     /**
     *<hr>
     *Returns the scalar distance between two points
     *<br>Points can be entered in either <tt>(Point1,Point2)</tt> OR <tt>(x1,y1,x2,y2)</tt> format
     *@param {Number|MC.Point} x1_or_pnt1 - First Point OR first point's x value
     *@param {Number|MC.Point} y1_or_pnt2 - Second Point OR first point's y value
     *@param {Number} [x2] - Second point's x value
     *@param {Number} [y2] - Second point's y value
     *@throws Console warning if neither 2 nor 4 arguments presented.  0 (zero) returned
     *@returns {Number} the range between the points
     */
    range: function(x1_or_pnt1,y1_or_pnt2,x2,y2) {
        if (arguments.length !== 2 && arguments.length !== 4) {
            console.log("WARNING: incorrect number of parameters passed to range()");
            return 0;
        }
        var X1 =0,Y1=0,X2=0,Y2=0;
        if (arguments.length == 2 && _utils.hasXY(x1_or_pnt1) && _utils.hasXY(y1_or_pnt2) ) {
            X1 = x1_or_pnt1.x;
            Y1 = x1_or_pnt1.y;
            X2 = y1_or_pnt2.x;
            Y2 = y1_or_pnt2.y;
        }
        else {   
            X1 = x1_or_pnt1;
            Y1 = y1_or_pnt2;
            X2 = x2;
            Y2 = y2;
        }
        return Math.sqrt(Math.pow(Math.abs(X1 - X2),2) + Math.pow(Math.abs(Y1 - Y2),2));
    },
    
         /**
     *<hr>
     *Returns the scalar distance SQUARED between two points
     *<br>Much more efficient for Hit Calculation purposes
     *<br>Points can be entered in either <tt>(Point1,Point2)</tt> OR <tt>(x1,y1,x2,y2)</tt> format
     *@param {Number|MC.Point} x1_or_pnt1 - First Point OR first point's x value
     *@param {Number|MC.Point} y1_or_pnt2 - Second Point OR first point's y value
     *@param {Number} [x2] - Second point's x value
     *@param {Number} [y2] - Second point's y value
     *@throws Console warning if neither 2 nor 4 arguments presented.  0 (zero) returned
     *@returns {Number} the range squared between the points
     */
    rangeSqr: function(x1_or_pnt1,y1_or_pnt2,x2,y2) {
        if (arguments.length !== 2 && arguments.length !== 4) {
            console.log("WARNING: incorrect number of parameters passed to range()");
            return 0;
        }
        var X1 =0,Y1=0,X2=0,Y2=0;
        if (arguments.length == 2 && _utils.hasXY(x1_or_pnt1) && _utils.hasXY(y1_or_pnt2) ) {
            X1 = x1_or_pnt1.x;
            Y1 = x1_or_pnt1.y;
            X2 = y1_or_pnt2.x;
            Y2 = y1_or_pnt2.y;
        }
        else {   
            X1 = x1_or_pnt1;
            Y1 = y1_or_pnt2;
            X2 = x2;
            Y2 = y2;
        }
        return Math.pow(Math.abs(X1 - X2),2) + Math.pow(Math.abs(Y1 - Y2),2);
    },
    
    /**
    *<hr>
    *Returns the manhatten distance between two points
    *<br>Points can be entered in either <tt>(Point1,Point2)</tt> OR <tt>(x1,y1,x2,y2)</tt> format
    *@param {Number|MC.Point} x1_or_pnt1 - First Point OR first point's x value
    *@param {Number|MC.Point} y1_or_pnt2 - Second Point OR first point's y value
    *@param {Number} [x2] - Second point's x value
    *@param {Number} [y2] - Second point's y value
    *@throws Console warning if neither 2 nor 4 arguments presented.  0 (zero) returned
    *@returns {Number} the manhatten range between the points
    */
    manhatten: function(x1_or_pnt1,y1_or_pnt2,x2,y2) {
        if (arguments.length !== 2 && arguments.length !== 4) {
            console.log("WARNING: incorrect number of parameters passed to manhatten()");
            return 0;
        }
        var X1 =0,Y1=0,X2=0,Y2=0;
        if (arguments.length == 2 && _utils.hasXY(x1_or_pnt1) && _utils.hasXY(y1_or_pnt2) ) {
            X1 = x1_or_pnt1.x;
            Y1 = x1_or_pnt1.y;
            X2 = y1_or_pnt2.x;
            Y2 = y1_or_pnt2.y;
        }
        else {   
            X1 = x1_or_pnt1;
            Y1 = y1_or_pnt2;
            X2 = x2;
            Y2 = y2;
        }
        return (Math.abs(X1 - X2) + Math.abs(Y1 - Y2));
    },
    
    /**
    *<hr>
    *Returns the Dot Product (aka Scalar Product) of two vectors (points) i.e. <tt>[(x1*x2)+(y1*y2)]</tt>
    *<br>Points can be entered in either <tt>(Point1,Point2)</tt> OR <tt>(x1,y1,x2,y2)</tt> format
    *@param {Number|MC.Point} x1_or_pnt1 - First Point OR first point's x value
    *@param {Number|MC.Point} y1_or_pnt2 - Second Point OR first point's y value
    *@param {Number} [x2] - Second point's x value
    *@param {Number} [y2] - Second point's y value
    *@throws Console warning if neither 2 nor 4 arguments presented.  0 (zero) returned
    *@returns {Number} the Dot Product of the two vectors 
    */
    dotProd: function(x1_or_pnt1,y1_or_pnt2,x2,y2) {
        if (arguments.length !== 2 && arguments.length !== 4) {
            console.log("WARNING: incorrect number of parameters passed to dotProd()");
            return 0;
        }
        var X1 =0,Y1=0,X2=0,Y2=0;
        if (arguments.length == 2 && _utils.hasXY(x1_or_pnt1) && _utils.hasXY(y1_or_pnt2) ) {
            X1 = x1_or_pnt1.x;
            Y1 = x1_or_pnt1.y;
            X2 = y1_or_pnt2.x;
            Y2 = y1_or_pnt2.y;
        }
        else {   
            X1 = x1_or_pnt1;
            Y1 = y1_or_pnt2;
            X2 = x2;
            Y2 = y2;
        }
        return (X1 * X2) + (Y1 * Y2);        
    },
    
    /**
    *<hr>
    *Returns the Cross Product of two vectors (points) i.e. <tt>[(x1*y2)-(y1*x2)]</tt>
    *<br>Points can be entered in either <tt>(Point1,Point2)</tt> OR <tt>(x1,y1,x2,y2)</tt> format
    *@param {Number|MC.Point} x1_or_pnt1 - First Point OR first point's x value
    *@param {Number|MC.Point} y1_or_pnt2 - Second Point OR first point's y value
    *@param {Number} [x2] - Second point's x value
    *@param {Number} [y2] - Second point's y value
    *@throws Console warning if neither 2 nor 4 arguments presented.  0 (zero) returned
    *@returns {Number} the Cross Product of the two vectors 
    */
    crossProd: function(x1_or_pnt1,y1_or_pnt2,x2,y2) {
        if (arguments.length !== 2 && arguments.length !== 4) {
            console.log("WARNING: incorrect number of parameters passed to crossProd()");
            return 0;
        }
        var X1 =0,Y1=0,X2=0,Y2=0;
        if (arguments.length == 2 && _utils.hasXY(x1_or_pnt1) && _utils.hasXY(y1_or_pnt2) ) {
            X1 = x1_or_pnt1.x;
            Y1 = x1_or_pnt1.y;
            X2 = y1_or_pnt2.x;
            Y2 = y1_or_pnt2.y;
        }
        else {   
            X1 = x1_or_pnt1;
            Y1 = y1_or_pnt2;
            X2 = x2;
            Y2 = y2;
        }
        return (X1 * Y2) - (Y1 * X2);        
    },
    
     /**
     *<hr>
     * Returns a NEW normalised vector (point) of what was passed in
     * <br>  N.B to normalise the original vector itself use <tt>Point.normalise()</tt> instead
     * @param {MC.Point} - the vector
     * @returns {MC.Point} A <tt>new</tt> normalised vector
     * @throws Console warning if parameter does not have x and y properties.  Input parameter is returned.
     */
    normalOf: function(vector) {
        if (_utils.hasXY(vector)) {
            var ret = new _Point(0,0);
            var L = _maths.range(ret,vector);
            ret.x = vector.x / L;
            ret.y = vector.y / L;
            return ret;
        }
        else {
            console.log("WARNING: item without x and/or y passed to normalOf()");
            return vector;
        }
    },
    
    /**
     *<hr>
     *Returns the surface area of a triangle from the lengths of it's sides (using Heron's formula)
     *<br>Accepts either 3 lengths, or 3 points representing the corner co-ordinates can be used
     *<br>CAUTION: do NOT mix parameter types
     *@param {Number|MC.Point} len_or_Pnt1 - First side length or vertex coordinates
     *@param {Number|MC.Point} len_or_Pnt2 - Second side length or vertex coordinates
     *@param {Number|MC.Point} len_or_Pnt3 - Third side length or vertex coordinates
     *@returns {Number} Area of the Triangle
     */
    areaOfTriangle: function (len_or_Pnt1,len_or_Pnt2,len_or_Pnt3) {
        var l1,l2,l3,p = 0;
        if (_utils.hasXY(len_or_Pnt1) && _utils.hasXY(len_or_Pnt2) && _utils.hasXY(len_or_Pnt3)) {
            l1 = _maths.range(len_or_Pnt1,len_or_Pnt2);
            l2 = _maths.range(len_or_Pnt2,len_or_Pnt3);
            l3 = _maths.range(len_or_Pnt3,len_or_Pnt1);
        }
        else {
            l1 = len_or_Pnt1;
            l2 = len_or_Pnt2;
            l3 = len_or_Pnt3;
        }
        var p = (l1 + l2 + l3)/2;
        return Math.sqrt( p*(p-l1)*(p-l2)*(p-l3) );
    }

    
    
    
}; // End of MATHS

////////////////////    
//      DRAW      //
////////////////////
/**
*The draw object provides methods for drawing to the HTML5 canvas element.
*@property {object} context Reference to the canvas 2dcontext.
*<br>Populated during <tt>MC.init()</tt>
*@interface
*
*/
MC.draw = {
    context: null,
     /**
     *<hr>
     *Clears the canvas by filling it with a single color.
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *@param {MC.Color|string} color - Desired canvas color (1)
     */
    clearCanvas: function (color) {
    var c = this.context;    
    c.fillStyle = _utils.colorOrString(color);
    c.fillRect(0,0,c.canvas.width,c.canvas.height);
    return;
    },
    
    /**
     *<hr>
     *Fills the canvas area defined as "inBounds" with a single color.
     *<br>See <tt>game.setBounds()</tt> for more details
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *@param {MC.Color|string} color - Desired canvas color (1)
     */
    clearInBounds: function (color) {
    var c = this.context;    
    c.fillStyle = _utils.colorOrString(color);
    c.fillRect(_canvas.left,_canvas.top,(_canvas.right - _canvas.left),(_canvas.bottom - _canvas.top));
    return;
    },
    
    /**
     *<hr>
     *Fills the canvas area NOT defined as "inBounds" with a single color.
     *<br>See <tt>game.setBounds()</tt> for more details
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *@param {MC.Color|string} color - Desired canvas color (1)
     */
    clearOutBounds: function (color) {
    var c = this.context;    
    c.fillStyle = _utils.colorOrString(color);
    if (_canvas.top > 0) c.fillRect(0,0,c.canvas.width,_canvas.top);
    if (_canvas.bottom < c.canvas.height) c.fillRect(0,_canvas.bottom,c.canvas.width,c.canvas.height - _canvas.bottom);
    if (_canvas.left > 0) c.fillRect(0,0,_canvas.left,c.canvas.height);
    if (_canvas.right < c.canvas.width) c.fillRect(_canvas.right,0,c.canvas.width - _canvas.right,c.canvas.height);
    return;
    },
    
     /**
     *<hr>
     *Draws a line from one Point to another
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *@param {MC.Point} from - Line start point
     *@param {MC.Point} to - Line finish point
     *@param {MC.Color|string} color - Line color (1)
     *@param {Number} [width=1] - Desired Line width
     */
    line: function (from,to,color,width) {
            var alpha = false;
            var c = this.context;
            if (color.a !== 1) {
                c.globalAlpha = color.a;
                alpha = true;
            }
            c.beginPath();
            c.moveTo(from.x,from.y);
            c.lineWidth = width || 1;
            c.strokeStyle = _utils.colorOrString(color);
            c.lineTo(to.x, to.y);
            c.closePath();
            c.stroke();
            if (alpha) c.globalAlpha = 1;
            return;
    },
    /**
    *<hr>
     *Draws a line through a series of Points
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *@param {Array} pointsArray - Array of <tt>Point<tt> 
     *@param {MC.Color|string} color - Line color (1)
     *@param {Number} [width=1] - Desired Line width
     */
    polyLine : function (pointsArray,color,width) {
            var len = pointsArray.length;
            for (var i = 0; i < len - 1 ; i = i+1) {
                _draw.line(pointsArray[i],pointsArray[i+1],color,width);
            }
    },
    
     /**
     *<hr>
     *Draws a Circle
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *<br> 2 - Omission of borderColor and edgeWidth parameters results in a filled circle (with no border)
     *@param {MC.Point} centre - Circle centre
     *@param {Number} radius - Circle Radius
     *@param {MC.Color|string|null} fillColor - Circle fill color (1).  <tt>null</tt> results in an non-filled circle
     *@param {MC.Color|string} [borderColor] - Circle border line color (1)(2).
     *@param {Number} [edgeWidth=0] - Circle border line width (2)
     */
    circle: function (centre,radius,fillColor,borderColor,edgeWidth) {
            var alpha = false;
            var c = this.context;
            if (fillColor !== null && fillColor.a !== 1) {
                c.globalAlpha = fillColor.a;
                alpha = true;
            }
            c.beginPath();
            c.arc(centre.x,centre.y,radius,0, _maths.TWOPI,false);
            if (fillColor !== null) {
                c.fillStyle = _utils.colorOrString(fillColor);
                c.fill();
            }
            if (alpha) c.globalAlpha = 1;
            if (borderColor) {
                if (borderColor && borderColor.a !== 1) {
                    c.globalAlpha = borderColor.a;
                    alpha = true;
                }
                c. lineWidth = edgeWidth || 1;
                c.strokeStyle = _utils.colorOrString(borderColor);
                c.stroke();
            }
            if (alpha) c.globalAlpha = 1;
            c.closePath();
            return;
    },
    
    /**
     *<hr>
     *Draw an angled rectangle
     *<br>N.B. See <tt>Draw.rectBasic()</tt> for a non-rotated rectangle
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *<br> 2 - Omission of <tt>borderColor</tt> and <tt>edgeWidth</tt> parameters results in a filled rectangle (with no border)
     *@param {MC.Point} centre - Rectangle centre
     *@param {Number} width - Rectangle width
     *@param {Number} height - Rectangle height
     *@param {Number} angle - Draw angle (degrees)
     *@param {MC.Color|string|null} fillColor - Rectangle fill color (1).  <tt>null</tt> results in an non-filled rectangle
     *@param {MC.Color|string} [borderColor] - Rectangle border line color (1)(2).
     *@param {Number} [edgeWidth=0] - Rectangle border line width (2)
     */
    rect: function (centre,width,height,angle,fillColor,borderColor,edgeWidth) {
            var alpha = false;
            var c = this.context;
            c.save();            
            c.beginPath();
            if (fillColor !== null && fillColor.a !== 1) {
                c.globalAlpha = fillColor.a;
                alpha = true;
            }           
            c.translate(centre.x,centre.y);
            if (angle !== 0 && angle !== 360) {
                c.rotate(angle * _maths.TO_RADIANS);  
            }
            if (fillColor !== null) {
                c.fillStyle = _utils.colorOrString(fillColor);
                c.fillRect(-(width/2),-(height/2),width,height);
            }
            if (alpha) c.globalAlpha = 1;
            if (borderColor && borderColor.a !== 1) {
                c.globalAlpha = borderColor.a;
                alpha = true;
            }
            if (borderColor && borderColor !== null) {
                c. lineWidth = edgeWidth || 1;
                c.strokeStyle = _utils.colorOrString(borderColor);
                c.rect(-(width/2),-(height/2),width,height);
                c.stroke();
            }
            c.closePath();
            c.restore();           
            if (alpha) c.globalAlpha = 1;
            return;
    },
    
     /**
     *<hr>
     *Draws an axis-aligned rectangle.
     *<br>This method is slightly quicker than <tt>Draw.rect()</tt>
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *<br> 2 - Omission of <tt>borderColor</tt> and <tt>edgeWidth</tt> parameters results in a filled rectangle (with no border)
     *@param {MC.Point} centre - Rectangle centre
     *@param {Number} width - Rectangle width
     *@param {Number} height - Rectangle height
     *@param {MC.Color|string|null} fillColor - Rectangle fill color (1).  <tt>null</tt> results in an non-filled rectangle
     *@param {MC.Color|string} [borderColor] - Rectangle border line color (1)(2).
     *@param {number} [edgeWidth=1] - Rectangle border line width (2)
     */
    rectBasic: function (centre,width,height,fillColor,borderColor,edgeWidth) {
            var alpha = false;
            var c = this.context;         
            c.beginPath();
            if (fillColor !== null && fillColor.a !== 1) {
                c.globalAlpha = fillColor.a;
                alpha = true;
            }           
            if (fillColor !== null) {
                c.fillStyle = _utils.colorOrString(fillColor);
                c.fillRect(centre.x-(width/2),centre.y-(height/2),width,height);
            }
            if (alpha) c.globalAlpha = 1;
            if (borderColor && borderColor.a !== 1) {
                c.globalAlpha = borderColor.a;
                alpha = true;
            }
            if (borderColor && borderColor !== null) {
                c. lineWidth = edgeWidth || 1;
                c.strokeStyle = _utils.colorOrString(borderColor);
                c.rect(centre.x-(width/2),centre.y-(height/2),width,height);
                c.stroke();
            }
            c.closePath();        
            if (alpha) c.globalAlpha = 1;
            return;
    },
    
    /**
     *<hr>
     *Draws a closed shape using an array of points relative to its centre
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *<br> 2 - Omission of <tt>borderColor</tt> and <tt>edgeWidth</tt> parameters results in a filled shape (with no border)
     *@param {MC.Point} centre - Draw centre of the shape
     *@param {Array} pointsArray - Array of draw Points relative to the draw centre
     *@param {MC.Color|string|null} fillColor - Shape fill color (1).  <tt>null</tt> results in an non-filled shape
     *@param {MC.Color|string} [borderColor] - Shape border line color (1)(2).
     *@param {Number} [edgeWidth=1] - Shape border line width (2)
     *
     */
    array: function (centre,pointsArray,fillColor,borderColor,edgeWidth) {
            var c = this.context;
            var alpha = false;
            var L = pointsArray.length;
            c.save();
            c.translate(centre.x,centre.y);
            c.beginPath();
            c.moveTo(pointsArray[0].x,pointsArray[0].y);
            for (var i = 1; i < L ; i++) {
                c.lineTo(pointsArray[i].x,pointsArray[i].y);
            }
            c.closePath();
            if (fillColor !== null && fillColor.a !== 1) {
                c.globalAlpha = fillColor.a;
                alpha = true;
            }
            if (fillColor !== null) {
                c.fillStyle = _utils.colorOrString(fillColor);
                c.fill();
            }
            c.globalAlpha = 1;
            if (borderColor && borderColor.a !== 1) {
                c.globalAlpha = borderColor.a;
                alpha = true;
            }
            if (borderColor && borderColor !== null) {
                c. lineWidth = edgeWidth || 1;
                c.strokeStyle = _utils.colorOrString(borderColor);
                c.stroke();
            }
            c.restore();  
            if (alpha) c.globalAlpha = 1;
            return;
    },
    
    /**
     *<hr>
     *Draws a regular polygon
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *<br> 2 - Omission of <tt>borderColor</tt> and <tt>edgeWidth</tt> parameters results in a filled polygon (with no border)
     *@param {MC.Point} centre - Draw centre of the shape
     *@param {Number} sides - Number of sides
     *@param {Number} radius - Distance from centre to each vertex
     *@param {Number} angle - In degrees (clockwise), 0 = first vertex drawn to the right of centre, 90 = below centre, etc
     *@param {MC.Color|string|null} fillColor - Shape fill color (1).  <tt>null</tt> results in an non-filled polygon
     *@param {MC.Color|string} [borderColor] - Shape border line color (1)(2).
     *@param {Number} [edgeWidth=1] - Shape border line width (2)
     */
    poly: function (centre,sides,radius,angle,fillColor,borderColor,edgeWidth) {
        _draw.array(centre,_utils.getPolyArray(sides,radius,angle),fillColor,borderColor,edgeWidth);
    },
    
        /**
     *<hr>
     *Draws a regular star
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *<br> 2 - Omission of <tt>borderColor</tt> and <tt>edgeWidth</tt> parameters results in a filled star (with no border)
     *@param {MC.Point} centre - Draw centre of the star
     *@param {Number} points - Number of points
     *@param {Number} OuterRadius - Distance from centre to each point
     *@param {Number} InnerRadius - Distance from centre to each inner point
     *@param {Number} angle - In degrees (clockwise), 0 = first vertex drawn to the right of centre, 90 = below centre, etc
     *@param {MC.Color|string|null} fillColor - Shape fill color (1).  <tt>null</tt> results in an non-filled star
     *@param {MC.Color|string} [borderColor] - Shape border line color (1)(2).
     *@param {Number} [edgeWidth=1] - Shape border line width (2)
     */
    star: function (centre,points,outerRadius,innerRadius,angle,fillColor,borderColor,edgeWidth) {
        _draw.array(centre,_utils.getStarArray(points,outerRadius,innerRadius,angle),fillColor,borderColor,edgeWidth);
    },
    
    /**
     *<hr>
     *Draws an <tt>image</tt> on the canvas
     *<br>This method mirrors (but does modify) a native <tt>canvas</tt> API call (specifically <tt>context.drawImage()</tt>) with the addition of an <tt>angle</tt> parameter
     *<br>N.B. this method requires EXACTLY the first 3,5 or all 7 parameters.
     *<br>(1) Only using <tt>(image,centre,angle)</tt> the image is drawn at 1:1 scale, centred and rotated about the <tt>centre</tt> point
     *<br>(2) Adding the <tt>width,height</tt> pair scales the image to the appropriate size (in pixels)
     *<br>(3) Adding the <tt>start,finish</tt> pair clips the image to a rectangle defined by <tt>start</tt> (top left corner) and <tt>finish</tt> (bottom right hand corner).  Essential for using sprite sheets.
     *<BR>(4) If any <tt>start</tt> or <tt>finish</tt> parameter is less or equal to 1, then that ratio of the native image width/height will be applied.
     *<br>If a Gray Box with Red cross appears, then this method has not been called and the <tt>Picture</tt> object has drawn a Placeholder to represent an image that has not loaded.
     *@param {Image} image - The JS <tt>Image()</tt> to be drawn (1)
     *@param {MC.Point} centre - Canvas location of the centre of the image (1)
     *@param {Number} angle - Angle (degrees) the image is to be rotated about the centre.  0 (zero) = no rotation (1)
     *@param {Number} [width] - Width image is to be displayed with (before any rotation).  If used then <tt>height</tt> must be specified (2)
     *@param {Number} [height] - As per width, but image display height in pixels (2)
     *@param {MC.Point} [start] - For displaying only a section of the image, start is a point representing the top left coordinates of the section ot be displayed.  If <tt>start</tt> is specified then <tt>finish</tt> has to be too (3)(4)
     *@param {MC.Point} [finish] - As per <tt>start</tt> above, except this is the bottom right of the selection to be displayed (3)(4)
     *
     */
    picture: function (image,centre,angle,width,height,start,finish) {
        var c = this.context;
        var DX = image.width;
        var DY = image.height;
        var offX =  width / 2 || DX/2;
        var offY = height/2 || DY/2;
        c.save();
        c.translate(centre.x,centre.y);
        c.rotate(angle * _maths.TO_RADIANS);           
        c.translate(-offX,-offY);
     
        if (arguments.length == 3) {
            c.drawImage(image,0,0);
        }
        else if (arguments.length == 5) {
            c.drawImage(image,0,0,width,height);
        }
        else if (arguments.length == 7) {
            var x1 = start.x;
            if (x1 <=1) x1 *= DX;
            var y1 = start.y;
            if (y1 <=1) y1 *= DY;
            var x2 = finish.x;
            if (x2 <=1) x2 *= DX;
            var y2 = finish.y;
            if (y2 <=1) y2 *= DY;
            c.drawImage(image,x1,y1,x2 - x1,y2 - y1,0,0,width,height);
            // context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
        }
         c.restore(); 
    },
    
    /**
     *<hr>
     *Displays a warning marker (a 20x20 grey square with a red cross)
     *<br>Used to indicate an <tt>Image()</tt> that is not loaded
     *@param {MC.Point} position - Canvas location for the centre of the marker
     */
    loadWarning: function (position) {
        var pos = new _Point(position.x,position.y);
        _draw.rectBasic(pos.clone(),20,20,"DarkGray");
        _draw.line(pos.clone().add(-6,-6),pos.clone().add(6,6),"Red",4);
        _draw.line(pos.clone().add(6,-6),pos.clone().add(-6,6),"Red",4);
        
    },
    
    /**
     *<hr>
     *Draws a 6 sided dice with the designated value
     *<br> N.B. this is somewhat intensive method and is only recommended for when caught short without a dice image file ! 
     *<br> N.B. Alpha (transparency) is only supported via a MC.Color parameter
     *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color
     *@param {MC.Point} centre - Centre of the dice
     *@param {Number} size - Height and width of the dice
     *@param {Number} value - Dice value (clamped to 1-6)
     *@param {MC.Color|string} dieColor - Dice color (1)
     *@param {MC.Color|string} pipColor - Color of dice pips (1)
     */
    d6: function (centre,size,value,dieColor,pipColor)
    {
        var c = this.context;
        var D = new _Point(centre.x,centre.y); // allows non-point items with x: and y: properties to be used
        var sml = size / 8;
        var lrg = size * (3/8);
       
        _draw.circle(D.clone().add(-lrg,-lrg),sml,"Black");
        _draw.circle(D.clone().add(lrg,-lrg),sml,"Black");
        _draw.circle(D.clone().add(-lrg,lrg),sml, "Black");
        _draw.circle(D.clone().add(lrg,lrg),sml, "Black");
        c.fillStyle = "Black";
        c.fillRect(centre.x-lrg-1,centre.y-(size/2)-1,2*lrg+2, size+2);
        c.fillRect(centre.x-(size/2)-1,centre.y-lrg-1, size+2, 2*lrg+2);
        
        _draw.circle(D.clone().add(-lrg+1,-lrg+1),sml, dieColor);
        _draw.circle(D.clone().add(lrg-1, -lrg+1),sml, dieColor);
        _draw.circle(D.clone().add(-lrg+1, lrg-1),sml, dieColor);
        _draw.circle(D.clone().add(lrg-1, lrg-1),sml, dieColor);
        c.fillStyle = _utils.colorOrString(dieColor);
        c.fillRect(centre.x-lrg,centre.y-(size/2)+1,2*lrg, size-2);
        c.fillRect(centre.x-(size/2)+1,centre.y-lrg, size-2, 2*lrg);
        
        value = ~~_maths.clamp(value,1,6);
        switch (value)
        {
            case 1:
                _draw.circle(D,sml-1.5, pipColor);
                break;
            case 2:
                _draw.circle(D.clone().add(size/4, -size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(-size /4, size/4),sml-1.5, pipColor);
                break;
            case 3:
                _draw.circle(D,sml-1.5, pipColor);               
                _draw.circle(D.clone().add(-size/4, -size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, size/4),sml-1.5, pipColor);
                break;
            case 4:
                _draw.circle(D.clone().add(-size/4, -size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(-size/4, size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, -size/4),sml-1.5, pipColor);
                break;
            case 5:
                _draw.circle(D,sml-1.5, pipColor);
                _draw.circle(D.clone().add(-size/4, -size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(-size/4, size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, -size/4),sml-1.5, pipColor);                
                break;
            case 6:
                _draw.circle(D.clone().add(-size/4, -size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(-size/4, size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, -size/4),sml-1.5, pipColor);
                _draw.circle(D.clone().add(-size/4, 0),sml-1.5, pipColor);
                _draw.circle(D.clone().add(size /4, 0),sml-1.5, pipColor);                
                break;
            default:
        }
        return;
    },
    
    /**
     *<hr>
     *Renders text to the canvas
     *<br>All such text is drawn with the bottom left corner of the text appearing at the screen coordinates defined by the position MC.Point
     *<br><b>Web Safe Fonts</b>  Use these to ensure multi-browser support
     *<br>Arial, Helvetica, Time New Roman, Times, Courier New, Courier, Verdana, Georgia, Palatino, Garamond, Bookman, Comic Sans MS, Trebuchet MS,Arial Black, Impact
     *@param {String} text The text string to be rendered
     *@param {MC.Point} position Screen coordinates of the bottom left of the text
     *@param {MC.Typeface|MC.Color|String} [typeface = MC.game.text] Desired Typeface (font, size and color) .. otherwise <tt>MC.game.text</tt> is used
     *<br>OR if a Color object is used the <tt>MC.game.text</tt> default font and size will be used with the desired color
     *<br>OR a HTML5 color string name can be used instead (with <tt>MC.game.text defined font and size)
     *@example
     *var myTypeface = new MC.Typeface("Comic Sans MS",25,MC.utils.randomColor());
     *var pos = new MC.Point(100,120);
     *
     *MC.draw.text("Hello, World", pos, myTypeface);
     */
    // Thanks to https://websitesetup.org/web-safe-fonts-html-css/ (Dec 2018)
    text: function text (text,position,typeface) {
        var c = this.context;
        var alpha = false;
        var t;
        if (typeface === undefined) {
            t = _game.text;
        }
        else if (typeface instanceof MC.Typeface) {
            t = typeface;
        }
        else {
            t = new _Typeface (_game.text.font,_game.text.size,typeface);
        }
        if (t.color instanceof MC.Color && t.color.a !== 1) {
            alpha = true;
            c.globalAlpha = t.color.a;
        }
        c.font = t.log();
        c.fillStyle = _utils.colorOrString(t.color);
        c.fillText(text,position.x,position.y);
        if (alpha) {
            c.globalAlpha = 1;
        }
    },
    
    /**
     *<hr>
     *Uses the HTML5 canvas method of <tt>measureText()</tt> to establish the best, centralised, fit for a piece of text in a box
     *<br>Used internally by the <tt>MC.ConBox</tt> object
     *@param {String} text to be fitted
     *@param {Number} width of the box
     *@param {Number} height of the box
     *@param {String} font font to be used in the text fit
     *@returns {Object} An Object comprising of {offset: {MC.Point}, size : {Number}}
     */
      textFit: function (text,width,height,font) {
        var c = this.context;
        var s = height +1;
        var len = 0;        
        do {
            s--;
            c.font = s+"px "+font;
            len = c.measureText(text).width;
            }
        while (len > width);
        // MAGIC NUMBER ALERT dividing size by 2.6 gets better results
        var ret = {offset: new _Point(-len/2,s/2.6), size: s};     
        return ret;
      }

    
}; // End of DRAW

////////////////////    
//   UTILITIES    //
////////////////////
/**
*The utils (utilities) object provides internal engine methods.
*<br>Developers may find them useful.
*@interface
*
*/
MC.utils= {
    /**
     *<hr>
     *Checks if a "color" is a string (i.e. HTML5 color name) or a {Color} object
     *<br>Returns either the HTML5 string or the {Color} as a "rgb(r,g,b)" string
     *@param {string|MC.Color} color
     *@returns {string}
     */
    colorOrString: function (color) {
        // https://stackoverflow.com/questions/4059147/check-if-a-variable-is-a-string-in-javascript (June 2018)  
        if (typeof color === "string" || color instanceof String) return color;
        else {return color.rgb();}
    },
    
    /**
     *<hr>
     *Checks if the entered items has x: and y: properties
     *<br>Used as confirmation and to assist Method Polymorphism
     *@param {anything} item
     *@returns {Boolean} True if both <tt>item.x</tt> and <tt>item.y</tt> exist; else False
     */
    hasXY: function(item) {
        return (item.hasOwnProperty("x") && item.hasOwnProperty("y"));
    },
    
    /**
     *<hr>
     *Checks if an item is a number
     *@param {anything} n
     *@returns {Boolean} True if <tt>n</tt> is a number; else False
     */
    isNumeric: function(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
        // thanks to http://stackoverflow.com/questions/9716468/is-there-any-function-like-isnumeric-in-javascript-to-validate-numbers  (May 2017)
    },
    
    /**
     *<hr>
     *Shuffles an array
     *@param {Array} o - Array to be scrambled
     *@returns {Array} The same array, just in a different order
     */
    shuffle: function(o) {
        for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
        return o;
    },
    
    /**
     *<hr>
     * Mixes two MC Color objects and returns a NEW Color object
     * <br> N.B. alpha (<tt>Color.a</tt>) is also mixed
     * @param {MC.Color} color1 - First Color
     * @param {MC.Color} color2 - Second Color
     * @param {Number} ratio - Ratio of how much travel required from first color to second (as a decimal 0-1)
     * @returns {MC.Color} a NEW color object
     */
    mix: function (color1,color2, ratio) {
        return new _Color(_maths.clamp(~~(color1.r + ((color2.r - color1.r)*ratio)),0,255),
                          _maths.clamp(~~(color1.g + ((color2.g - color1.g)*ratio)),0,255),
                          _maths.clamp(~~(color1.b + ((color2.b - color1.b)*ratio)),0,255),
                          _maths.clamp(color1.a + ((color2.a - color1.a)*ratio),0,1) );
    },
    
    /**
     *Creates an array of points representing a regular polygon's vertices about a central point
     *<br>Used internally by various MC.draw methods (esp. <tt>MC.draw.poly()</tt>)
     *@param {Number} sides - # of sides to the polygon
     *@param {Number} radius - Distance from Poly centre to each vertex
     *@param {Number} angle - Angle (in degrees) of first vertex.  0 = right of centre, 90 = below centre etc.
     *@returns {Array.<MC.Point>} Array of <tt>MC.Point</tt> objects
     *
     */
    getPolyArray: function (sides, radius, angle) {
        var arr = [];
        arr[0] = new _Point(radius,0).rotate(angle);
        for (var i = 1; i < sides; i++) arr.push(arr[0].clone().rotate(i * (360/sides)));
        return arr;
    },
    
    /**
     *Creates an array of points representing a regular star's vertices about a central point
     *<br>Used internally by various MC.draw methods (esp. <tt>MC.draw.star()</tt>)
     *@param {Number} sides - # of points to the star
     *@param {Number} outerRadius - Distance from star centre to each point
     *@param {Number} innerRadius - Distance from centre to inner points
     *@param {Number} angle - Angle (in degrees) of first point.  0 = right of centre, 90 = below centre etc.
     *@returns {Array.<MC.Point>} Array of <tt>MC.Point</tt> objects
     *
     */
    getStarArray: function (points, outerRadius, innerRadius, angle) {
        var arr = [];
        var p1 = new _Point(outerRadius,0).rotate(angle);
        var p2 = new _Point(innerRadius,0).rotate(angle + (180/points));
        for (var i = 0; i < points; i++) {
            arr.push(p1.clone().rotate(i * (360/points)));
            arr.push(p2.clone().rotate(i * (360/points)));
        }
        return arr;
    },
    
    /**
     *Creates an array of points representing a rectangles vertices around a central point
     *<br>Used internally by various MC methods
     *@param {Number} width - Rectangle width
     *@param {Number} height - Rectangle height
     *@param {Number} angle - Angle (in degrees) of the rectangle rotation about centre.  0 = axis aligned, positive = clockwise
     *@returns {Array.<MC.Point>} Array of <tt>MC.Point</tt> objects
     *
     */
    getRectArray: function (width, height, angle) {
        var op = new _Point(width/2,height/2);
        var arr = [op.clone().rotate(angle),
                   op.clone().times(-1,1).rotate(angle),
                   op.clone().times(-1,-1).rotate(angle),
                   op.clone().times(1,-1).rotate(angle) ];
        return arr;
    },
    
    // NOT CURRENTLY IMPLEMENTED
    blankCanvas: function (width,height) {
        var MyNewCanvas = document.createElement("canvas");
        MyNewCanvas.width = width;
        MyNewCanvas.height = height;
        return MyNewCanvas;
    },
    
    /**
     *<hr>
     *Accepts an angle (in degrees) and returns it's value in the range of 0-360
     *@param {Number} angle - Angle to be fixed
     *@returns {Number} Same angle within range 0-360
     *@throws Returns original parameter if it is non-numeric
     */
    fixAngle: function(angle) {
        var out = angle;
        if (_utils.isNumeric(out))
        {
            out = out % 360;
            if (out < 0) out += 360;
        }
        return out;
    },
    
    /**
    *<hr>
    *Creates a random opaque Color Object.
    *@returns a <tt>new</tt> {MC.Color}
    */
    randomColor: function() {
       return new _Color(_maths.randBetween(0,255),_maths.randBetween(0,255),_maths.randBetween(0,255),1);
   },
   
   /**
    *<hr>
    *Checks if an object is valid candidate for a hitTest, Specifically:
    *<br>1. is a MC.Sprite
    *<br>2. has <tt>collider === true</tt>
    *<br>3. is <tt>dead === false</tt>
    *<br>Used Internally an a TARGET by the MC Engine, recommended that the Developer call it on any Object prior to attempting to make it do a <tt>hitTest()</tt> call.
    *@param {Object} object1 (ideally MC.Sprite) to be tested
    *@param {Object} [object2] Optional second Object to test
    *@returns {Boolean} <tt>True</tt> if all parameter Objects are valid hitTest candidate(s)
    *@throws Console warning if invalid
    */
   validHT: function(object1,object2) {
        // Internal Helper Function
            var spriteTest = function(o) {
            if (o instanceof MC.Sprite) {
                if (o.collider === true && o.dead !== true) {
                    return true;
                }
            }
            return false;
        }
    // Actual method
    if (arguments.length == 1) {
        return spriteTest(object1);
    }
    else if (arguments.length == 2) {
        if (spriteTest(object1) && spriteTest(object2)) {
            return true;
        }
    }
    else {
        return false;
    }
   },
   
   /**
    *<hr>
    *Checks if 2 passed Sprite Bounding Boxes intersect (indicating a possible hit) OR whether a point is inside a Bounding Box
    *<br>Used internally by the MC Engine
    *@param {Object} bb1 First MC.Sprite.boundingBox
    *@param {Object|MC.Point} bb2 Second MC.Sprite.boundingBox OR an MC.Point
    *@returns {Boolean} <tt>True</tt> if the two boxes (or single box and point) intersect
    */
   checkBB: function(bb1, bb2) {
    // Point in Boundingbox
    if (bb2 instanceof MC.Sprite) {
        if (bb2.x > bb1.r || bb2.x < bb1.l || bb2.y > bb1.b || bb2.y < bb1.t) {
            return false;
        }
        return true;
    }
    // Boundingbox on BoundingBox
    if (bb1.l > bb2.r || bb1.r < bb2.l || bb1.t > bb2.b || bb1.b < bb2.t) {
        return false;
    }
     return true;
   },
   
   /**
    *<hr>
    *Checks if a point coordinate is within the bounds defined by an Array of 3 further points
    *<br>Used internally by the MC engine.
    *@param {MC.Point} Point to be tested
    *@param {Array.<MC.Point>} Array defining bounds of the triangle
    *@returns {Boolean} <tt>true</tt> if the point lies within the triangle, else <tt>false</tt>
    */
    pointInTri: function (Point, Array) {
        // WARNING: Here be dragons !! ;p
        var c1 = 0; var c2 = 0; var test1; var test2;
        var D = Point.clone().minus(Array[0]);
        var V = Array[1].clone().minus(Array[0]);
            test1 = V.isLeft(D); test2 = V.isRight(D);
            if (test1 && !test2) { c1++;}
            else if (!test1 && test2) {c2++;}
            else {c1++; c2++;}
        D = Point.clone().minus(Array[1]);
        V = Array[2].clone().minus(Array[1]);
            test1 = V.isLeft(D); test2 = V.isRight(D);
            if (test1 && !test2) { c1++;}
            else if (!test1 && test2) {c2++;}
            else {c1++; c2++;}
        D = Point.clone().minus(Array[2]);
        V = Array[0].clone().minus(Array[2]);
            test1 = V.isLeft(D); test2 = V.isRight(D);
            if (test1 && !test2) { c1++;}
            else if (!test1 && test2) {c2++;}
            else {c1++; c2++;}
        if (c1 == 3 || c2 == 3) {return true;} else {return false;} 
    },
    
    /**
     *<hr>
     *Checks if a point coordinate is within a circle defined by centre (point) and radius
     *@param {MC.Point} Point to be tested
     *@param {MC.Point} Centre of circle to be tested against
     *@param {Number} Radius of circle
     *@returns {Boolean} <tt>true</tt> if the point is within (or on the perimeter of) the circle, else <tt>false</tt>
     */
    pointInCirc: function (Point,Centre,Radius) {
        return _maths.rangeSqr(Point,Centre) <= Radius * Radius;
    },
 
     /**
     *<hr>
     *Checks if a point coordinate is within a rectangle defined by an array of points
     *@param {MC.Point} Point to be tested
     *@param {Array.<MC.Point>} Corners of the Rectangle
     *<br> winding direction does not matter but points should be ordered as if going around the perimeter of the rectangle
     *@returns {Boolean} <tt>true</tt> if the point is within (or on the perimeter of) the rectangle, else <tt>false</tt>
     *@throws Console warning if Corners Array does not have 4 members.
     */   
    pointInRect: function (Point, Corners) {
        if (Corners.length != 4) {
            console.log("WARNING: wrong Array length passed to MC.utils.pointInRect()");
            return false;
        }
        if (_utils.pointInTri(Point,new Array (Corners[0],Corners[1],Corners[3]))) {
            return true;
        }
        return _utils.pointInTri(Point,new Array (Corners[2],Corners[1],Corners[3]));
    },
    
         /**
     *<hr>
     *Checks if a point coordinate lies within the shape defined by a second array of points
     *<br>This only produces reliable results with CONVEX shapes.
     *@param {MC.Point} Point to be tested
     *@param {Array.<MC.Point>} Array of points defining a shape to be tested against
     *<br> winding direction does not matter but points should be ordered as if going around the perimeter of the shape
     *@returns {Boolean} <tt>true</tt> if the point is within (or on the perimeter of) the shape, else <tt>false</tt>
     */   
    pointInShape: function (Point,Array) {
        var arr = [3];  var len = Array.length;
        var x = 0; var y = 0;
        arr[0] = new _Point(); // mid point
        for (var i = 0 ; i < len ; i++) {
            arr[0].add(Array[i]);
        }
        arr[0].div(len);
        for (var i = 0; i < len ; i++) {
                arr[1] = Array[i];
                if (i == len -1) {arr[2] = Array[0]; } else {arr[2] = Array[i+1];}
                if (_utils.pointInTri(Point,arr)) { return true;}
            }
        return false;
    },
    
     /**
     *<hr>
     *Checks if any of an Array of point coordinates lies within the shape defined by a second array of points (and vice versa)
     *<br>This only produces reliable results with shapes when each line between the vertices and centre is internal
     *@param {Array.<MC.Point>} First array of points defining a shape to be tested
     *@param {Array.<MC.Point>} Second array of points defining another shape to be tested
     *<br> winding direction does not matter but points should be ordered as if going around the perimeter of the shapes
     *@returns {Boolean} <tt>true</tt> if any of the points is within (or on the perimeter of) the other shape, else <tt>false</tt>
     */   
    shapeInShape: function (First,Second) {
        var len1 = First.length; var len2 = Second.length;
        for (var i = 0; i < len1 ; i++) {
            if (_utils.pointInShape(First[i],Second)) { return true;}  }
        for (var i = 0; i < len2 ; i++) {
            if (_utils.pointInShape(Second[i],First)) { return true;}  }
        return false;
    },
    // TO DO: gonna need to leverage line intersection too
    // http://thirdpartyninjas.com/blog/2008/10/07/line-segment-intersection/
    // https://bl.ocks.org/1wheel/464141fe9b940153e636
    
    /**
     *<hr>
     *Checks is any of the vertices of a shape (defined by an array of MC.Point's) are within or touching a circle
     *@param {Array.<MC.Point>} Shape points array to be tested
     *@param {MC.Point} Centre of the circle to be tested against
     *@param {Number} Radius of the circle
     *@returns {Boolean} <tt>True</tt> if any of the points intersect the circle, else <tt>false</tt>
     */
    shapeInCircle: function (Shape,Centre,Radius) {
        // pointInCirc: function (Point,Centre,Radius)
        var len = Shape.length;
        for (var i = 0; i < len ; i++)
        {
            if (_utils.pointInCirc(Shape[i],Centre,Radius)) return true;
        }
        return false;
    },
    
    /**
     *<hr>
     *Checks if 2 circles intersect
     *@param {MC.Point} Centre1 centre of the first circle
     *@param {Number} Radius1 radius of the first circle
     *@param {MC.Point} Centre2 centre of the second circle
     *@param {Number} Radius2 radius of the second circle
     *@returns {Boolean} <tt>true</tt> if the circle intersect, else <tt>false</tt>
     */
    circInCircle: function (Centre1,Radius1,Centre2,Radius2) {
        return _maths.rangeSqr(Centre1,Centre2) <= ((Radius1 + Radius2) * (Radius1 + Radius2)); 
    },
    
    /**
     *<hr>
     *copies the object using the native JS <tt>Object.assign()</tt> method
     *@param {Object} thing What is to be copied
     *@returns {Object} a <tt>new</tt> copt of the object
     *CAUTION: only sure to work with enumerable items.  May(will) not work 100% for nested elements within the object
     */
    // Thanks to https://medium.com/@Farzad_YZ/3-ways-to-clone-objects-in-javascript-f752d148054d (Jan 2019)
    // https://scotch.io/bar-talk/copying-objects-in-javascript (Jan 2019)
    // https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript (Jan 2019)
    cloneWithAssign: function (thing) {
        return Object.assign({},thing);
    }

    
    
    
    
    
}; // End of UTILITIES

////////////////////    
//   MOUSE        //
////////////////////
/**
*<hr>
*The <tt>MC.mouse</tt> object contains all mouse related variables.
*<br>Upon initialisation it sets up document event listeners for <tt>mousemove, mouseup, mousedown & wheel</tt>
*<br>The following functions are defined, but empty, for Developers to customise (see Example)
*<br>(a) <tt>onClick() & onClickUp()</tt> fired when ANY mouse button is depressed or released
*<br>(b) <tt>onClickL() & onClickUpL()</tt> as above, Left Mouse Button only
*<br>(c) <tt>onClickM() & onClickUpM()</tt> as above, Middle Mouse Button only
*<br>(d) <tt>onClickR() & onClickUpR()</tt> as above, Right Mouse Button only.  <br>CAUTION: most browsers have build in RH button features
*<br>(e) <tt>wheelUp() & wheelDown()</tt> fires when the mouse wheel is scrolled.  <br>CAUTION: works for Chrome, support varies for other browsers
*@property {MC.Point} pos Current mouse screen coordinates
*@property {MC.Point} clickStart Screen coordinates of ANY mouse button down click
*@property {MC.Point} clickFinish Screen coordinates of ANY mouse button release
*@property {Boolean} mouseDown <tt>true</tt> if ANY mouse button is currently depressed, else <tt>false</tt>
*@property {MC.Point} clickStartL Screen coordinates of the LEFT mouse button down click
*@property {MC.Point} clickFinishL Screen coordinates of the LEFT mouse button release
*@property {Boolean} mouseDownL <tt>true</tt> if LEFT mouse button is currently depressed, else <tt>false</tt>
*@property {MC.Point} clickStartM Screen coordinates of the MIDDLE mouse button down click
*@property {MC.Point} clickFinishM Screen coordinates of the MIDDLE button release
*@property {Boolean} mouseDownL <tt>true</tt> if MIDDLE mouse button is currently depressed, else <tt>false</tt>
*@property {MC.Point} clickStartR Screen coordinates of the RIGHT mouse button down click
*@property {MC.Point} clickFinishR Screen coordinates of the RIGHT button release
*@property {Boolean} mouseDownL <tt>true</tt> if RIGHT mouse button is currently depressed, else <tt>false</tt>
*@interface
*@example
*MC.mouse.onClickL = function () {
*    console.log("Left Mouse Clicked");
*    mySprite.moveTo(MC.mouse.clickStartL);
*};
*
*MC.mouse.wheelUp = function () {
*    if (mySprite.hit(MC.mouse.pos)) {
*        mySprite.scale += 0.2;
*    }
*};
*/
MC.mouse = {
/**
 *<hr>
 *Used to Initialise the MC.mouse object, sets up properties and event listeners
 *<br>Called automatically by the <tt>MC.game.init()</tt> method
 */
    init: function () {
    this.lastPos = new MC.Point(); 
    this.pos = new MC.Point();
    this.clickStart = new MC.Point();
    this.clickFinish = new MC.Point();
    this.clickStartL = new MC.Point();
    this.clickFinishL = new MC.Point();
    this.clickStartM = new MC.Point();
    this.clickFinishM = new MC.Point();
    this.clickStartR = new MC.Point();
    this.clickFinishR = new MC.Point();
    window.addEventListener("mousemove", getMouse, false); // MOUSE MOVE
    function  getMouse(event) {
        _mouse.lastPos.set(_mouse.pos);  
        _mouse.pos.x = _game.mouse.x = Math.round(event.x - canvas.offsetLeft + document.body.scrollLeft + document.documentElement.scrollLeft);
        _mouse.pos.y = _game.mouse.y = Math.round(event.y - canvas.offsetTop + document.body.scrollTop + document.documentElement.scrollTop);
        }
    window.addEventListener("mousedown", mouseDown, false);
    function mouseDown(event) {
        _mouse.mouseDown = true;
        _mouse.clickStart.set(_mouse.pos);
        _mouse.onClick();
        switch (event.button) {
            case 0:
                _mouse.mouseDownL = true;
                _mouse.clickStartL.set(_mouse.pos);
                _mouse.onClickL();
                break;
            case 1:
                _mouse.mouseDownM = true;
                _mouse.clickStartM.set(_mouse.pos);
                _mouse.onClickM();
                break;
            case 2:
                _mouse.mouseDownR = true;
                _mouse.clickStartR.set(_mouse.pos);
                _mouse.onClickR();
                break;
            default:
                break;
        }
    }
    window.addEventListener("mouseup", mouseUp, false);
    function mouseUp (event) {
        _mouse.mouseDown = false;
        _mouse.clickFinish.set(_mouse.pos);
        _mouse.onClickUp();
        switch (event.button) {
            case 0:
                _mouse.mouseDownL = false;
                _mouse.clickFinishL.set(_mouse.pos);
                _mouse.onClickUpL();
                break;
            case 1:
                _mouse.mouseDownM = false;
                _mouse.clickFinishM.set(_mouse.pos);
                _mouse.onClickUpM();
                break;
            case 2:
                _mouse.mouseDownR = false;
                _mouse.clickFinishR.set(_mouse.pos);
                _mouse.onClickUpR();
                break;
            default:
                break;
        }
    }
    window.addEventListener("wheel",wheel,false);
    // further research required https://stackoverflow.com/questions/14926366/mousewheel-event-in-modern-browsers (Dec 2018)
    function wheel (event) {
        if (event.deltaY < 0) {
            _mouse.wheelUp();
        }
        else if (event.deltaY > 0) {
            _mouse.wheelDown();
        }
    }
    },
      
    lastPos: null,
    pos: null,    
    clickStart: null,
    clickFinish: null,
    mouseDown: false,
    onClick: function () { },
    onClickUp: function () { },           
    clickStartL: null,
    clickFinishL: null,
    mouseDownL: false,
    onClickL: function () { },
    onClickUpL: function () { },            
    clickStartM: null,
    clickFinishM: null,
    mouseDownM: false,
    onClickM: function () { },
    onClickUpM: function () { },           
    clickStartR: null,
    clickFinishR: null,
    mouseDownR: false,
    onClickR: function () { },
    onClickUpR: function () { },   
    wheelUp: function () { },
    wheelDown: function () { }
    
}; // End of MOUSE

////////////////////    
//    KEYS        //
////////////////////
/**
*<hr>
*The <tt>MC.keys</tt> object contains all key related variables.
*<br>Upon initialisation it sets up document event listeners for <tt>keyup & keydown</tt> events
*<br>Each supported key has a <tt>MC.keys.#</tt> member object where the # is the key name
*<br>Within this are: <hr>
*<br>  (1) down {Boolean}  <tt>true</tt> if the key is currently depressed, else <tt>false</tt>
*<br>  (2) onDown {function} A placeholder function for Developer customisation, called when the key is depressed
*<br>  (3) onUp {function} A placeholder function for Developer customisation, called when the key is released <hr>
*<br>The following keys are supported by the MaxCanvas engine:
*<br>N.B. these names are to be used exactly as typed here
*<br><b>Misc: </b><tt>back, tab, enter, shift, ctrl, caps, esc, space, pageUp, pageDown, end, home, leftArrow, upArrow, rightArrow, downArrow, del</tt>  (N.B. if a choice between left and right, the left is supported.  e.g. the left shift key is supported, the right is not)
*<br><b>Numbers: </b><tt>_0,_1,_2,_3,_4,_5,_6,_7,_8,_9</tt>   (N.B. There are prefixed with an underscore to comply with JS naming rules)
*<br><b>Letters: </b><tt>a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z</tt>
*<br><b>NumberPad: </b><tt>num0,num1,num2,num3,num4,num5,num6,num7,num8,num9</tt>
*<br>Other keys are not supported as they may vary based upon international keyboard layouts or have possible browser pre-sets (e.g. F5 is refresh)
*@interface
*@example
*MC.keys.p.onDown = function () {
*       myPrintFunction();
*};
*
*MC.keys.space.onUp = function () {
*       if (gun.status == "ready") {
*           gun.fire();
*       }
*};
*
*MC.keys.m.onDown = function () {
*       if (MC.keys.ctrl.down) {
*           console.log("MaxCanvas is great!!");
*       }
*};
*
*/
MC.keys = {
    /**
     *<hr>
     *Init initialises the <tt>MC.key</tt> object, by setting up the keyboard event handlers and pointing them at the different key property objects
     *<br>Called automatically during the <tt>MC.init()</tt> call.  There should be no need for a Developer to use it.
     */
    init: function() {
        window.addEventListener("keyup", keyUp,false);
        function keyUp(event) {
            switch (event.keyCode) {
            case 8: _keys.back.down = false; _keys.back.onUp(); break;
            case 9: _keys.tab.down = false; _keys.tab.onUp(); break;
            case 13: _keys.enter.down = false; _keys.enter.onUp(); break;
            case 16: _keys.shift.down = false; _keys.shift.onUp(); break;
            case 17: _keys.ctrl.down = false; _keys.ctrl.onUp(); break;
            case 18: _keys.alt.down = false; _keys.alt.onUp(); break;
            case 20: _keys.caps.down = false; _keys.caps.onUp(); break;
            case 27: _keys.esc.down = false; _keys.esc.onUp(); break;
            case 32: _keys.space.down = false; _keys.space.onUp(); break;
            case 33: _keys.pageUp.down = false; _keys.pageUp.onUp(); break;
            case 34: _keys.pageDown.down = false; _keys.pageDown.onUp(); break;
            case 35: _keys.end.down = false; _keys.end.onUp(); break;
            case 36: _keys.home.down = false; _keys.home.onUp(); break;
            case 37: _keys.leftArrow.down = false; _keys.leftArrow.onUp(); break;
            case 38: _keys.upArrow.down = false; _keys.upArrow.onUp(); break;
            case 39: _keys.rightArrow.down = false; _keys.rightArrow.onUp(); break;
            case 40: _keys.downArrow.down = false; _keys.downArrow.onUp(); break;
            case 46: _keys.del.down = false; _keys.del.onUp(); break;
            case 48: _keys._0.down = false; _keys._0.onUp(); break;
            case 49: _keys._1.down = false; _keys._1.onUp(); break;
            case 50: _keys._2.down = false; _keys._2.onUp(); break;
            case 51: _keys._3.down = false; _keys._3.onUp(); break;
            case 52: _keys._4.down = false; _keys._4.onUp(); break;
            case 53: _keys._5.down = false; _keys._5.onUp(); break;
            case 54: _keys._6.down = false; _keys._6.onUp(); break;
            case 55: _keys._7.down = false; _keys._7.onUp(); break;
            case 56: _keys._8.down = false; _keys._8.onUp(); break;
            case 57: _keys._9.down = false; _keys._9.onUp(); break;
            case 65: _keys.a.down = false; _keys.a.onUp(); break;
            case 66: _keys.b.down = false; _keys.b.onUp(); break;
            case 67: _keys.c.down = false; _keys.c.onUp(); break;
            case 68: _keys.d.down = false; _keys.d.onUp(); break;
            case 69: _keys.e.down = false; _keys.e.onUp(); break;
            case 70: _keys.f.down = false; _keys.f.onUp(); break;
            case 71: _keys.g.down = false; _keys.g.onUp(); break;
            case 72: _keys.h.down = false; _keys.h.onUp(); break;
            case 73: _keys.i.down = false; _keys.i.onUp(); break;
            case 74: _keys.j.down = false; _keys.j.onUp(); break;
            case 75: _keys.k.down = false; _keys.k.onUp(); break;
            case 76: _keys.l.down = false; _keys.l.onUp(); break;
            case 77: _keys.m.down = false; _keys.m.onUp(); break;
            case 78: _keys.n.down = false; _keys.n.onUp(); break;
            case 79: _keys.o.down = false; _keys.o.onUp(); break;
            case 80: _keys.p.down = false; _keys.p.onUp(); break;
            case 81: _keys.q.down = false; _keys.q.onUp(); break;
            case 82: _keys.r.down = false; _keys.r.onUp(); break;
            case 83: _keys.s.down = false; _keys.s.onUp(); break;
            case 84: _keys.t.down = false; _keys.t.onUp(); break;
            case 85: _keys.u.down = false; _keys.u.onUp(); break;
            case 86: _keys.v.down = false; _keys.v.onUp(); break;
            case 87: _keys.w.down = false; _keys.w.onUp(); break;
            case 88: _keys.x.down = false; _keys.x.onUp(); break;
            case 89: _keys.y.down = false; _keys.y.onUp(); break;
            case 90: _keys.z.down = false; _keys.z.onUp(); break;
            case 96: _keys.num0.down = false; _keys.num0.onUp(); break;
            case 97: _keys.num1.down = false; _keys.num1.onUp(); break;
            case 98: _keys.num2.down = false; _keys.num2.onUp(); break;
            case 99: _keys.num3.down = false; _keys.num3.onUp(); break;
            case 100: _keys.num4.down = false; _keys.num4.onUp(); break;
            case 101: _keys.num5.down = false; _keys.num5.onUp(); break;
            case 102: _keys.num6.down = false; _keys.num6.onUp(); break;
            case 103: _keys.num7.down = false; _keys.num7.onUp(); break;
            case 104: _keys.num8.down = false; _keys.num8.onUp(); break;
            case 105: _keys.num9.down = false; _keys.num9.onUp(); break;
                default: break;   
            }
        }
        window.addEventListener("keydown", keyDown,false);
        function keyDown(event) {
            switch (event.keyCode) {
                case 8: _keys.back.down = true; _keys.back.onDown();  break;
                case 9: _keys.tab.down = true; _keys.tab.onDown();  break;
                case 13: _keys.enter.down = true; _keys.enter.onDown();  break;
                case 16: _keys.shift.down = true; _keys.shift.onDown();  break;
                case 17: _keys.ctrl.down = true; _keys.ctrl.onDown();  break;
                case 18: _keys.alt.down = true; _keys.alt.onDown();  break;
                case 20: _keys.caps.down = true; _keys.caps.onDown();  break;
                case 27: _keys.esc.down = true; _keys.esc.onDown();  break;
                case 32: _keys.space.down = true; _keys.space.onDown();  break;
                case 33: _keys.pageUp.down = true; _keys.pageUp.onDown();  break;
                case 34: _keys.pageDown.down = true; _keys.pageDown.onDown();  break;
                case 35: _keys.end.down = true; _keys.end.onDown();  break;
                case 36: _keys.home.down = true; _keys.home.onDown();  break;
                case 37: _keys.leftArrow.down = true; _keys.leftArrow.onDown();  break;
                case 38: _keys.upArrow.down = true; _keys.upArrow.onDown();  break;
                case 39: _keys.rightArrow.down = true; _keys.rightArrow.onDown();  break;
                case 40: _keys.downArrow.down = true; _keys.downArrow.onDown();  break;
                case 46: _keys.del.down = true; _keys.del.onDown();  break;
                case 48: _keys._0.down = true; _keys._0.onDown();  break;
                case 49: _keys._1.down = true; _keys._1.onDown();  break;
                case 50: _keys._2.down = true; _keys._2.onDown();  break;
                case 51: _keys._3.down = true; _keys._3.onDown();  break;
                case 52: _keys._4.down = true; _keys._4.onDown();  break;
                case 53: _keys._5.down = true; _keys._5.onDown();  break;
                case 54: _keys._6.down = true; _keys._6.onDown();  break;
                case 55: _keys._7.down = true; _keys._7.onDown();  break;
                case 56: _keys._8.down = true; _keys._8.onDown();  break;
                case 57: _keys._9.down = true; _keys._9.onDown();  break;
                case 65: _keys.a.down = true; _keys.a.onDown();  break;
                case 66: _keys.b.down = true; _keys.b.onDown();  break;
                case 67: _keys.c.down = true; _keys.c.onDown();  break;
                case 68: _keys.d.down = true; _keys.d.onDown();  break;
                case 69: _keys.e.down = true; _keys.e.onDown();  break;
                case 70: _keys.f.down = true; _keys.f.onDown();  break;
                case 71: _keys.g.down = true; _keys.g.onDown();  break;
                case 72: _keys.h.down = true; _keys.h.onDown();  break;
                case 73: _keys.i.down = true; _keys.i.onDown();  break;
                case 74: _keys.j.down = true; _keys.j.onDown();  break;
                case 75: _keys.k.down = true; _keys.k.onDown();  break;
                case 76: _keys.l.down = true; _keys.l.onDown();  break;
                case 77: _keys.m.down = true; _keys.m.onDown();  break;
                case 78: _keys.n.down = true; _keys.n.onDown();  break;
                case 79: _keys.o.down = true; _keys.o.onDown();  break;
                case 80: _keys.p.down = true; _keys.p.onDown();  break;
                case 81: _keys.q.down = true; _keys.q.onDown();  break;
                case 82: _keys.r.down = true; _keys.r.onDown();  break;
                case 83: _keys.s.down = true; _keys.s.onDown();  break;
                case 84: _keys.t.down = true; _keys.t.onDown();  break;
                case 85: _keys.u.down = true; _keys.u.onDown();  break;
                case 86: _keys.v.down = true; _keys.v.onDown();  break;
                case 87: _keys.w.down = true; _keys.w.onDown();  break;
                case 88: _keys.x.down = true; _keys.x.onDown();  break;
                case 89: _keys.y.down = true; _keys.y.onDown();  break;
                case 90: _keys.z.down = true; _keys.z.onDown();  break;
                case 96: _keys.num0.down = true; _keys.num0.onDown();  break;
                case 97: _keys.num1.down = true; _keys.num1.onDown();  break;
                case 98: _keys.num2.down = true; _keys.num2.onDown();  break;
                case 99: _keys.num3.down = true; _keys.num3.onDown();  break;
                case 100: _keys.num4.down = true; _keys.num4.onDown();  break;
                case 101: _keys.num5.down = true; _keys.num5.onDown();  break;
                case 102: _keys.num6.down = true; _keys.num6.onDown();  break;
                case 103: _keys.num7.down = true; _keys.num7.onDown();  break;
                case 104: _keys.num8.down = true; _keys.num8.onDown();  break;
                case 105: _keys.num9.down = true; _keys.num9.onDown();  break;
                default: break;
            }
        }
    },
    back : {down: false, onDown: function(){},onUp: function(){}},
    tab : {down: false, onDown: function(){},onUp: function(){}},
    enter : {down: false, onDown: function(){},onUp: function(){}},
    shift : {down: false, onDown: function(){},onUp: function(){}},
    ctrl : {down: false, onDown: function(){},onUp: function(){}},
    alt : {down: false, onDown: function(){},onUp: function(){}},
    caps : {down: false, onDown: function(){},onUp: function(){}},
    esc : {down: false, onDown: function(){},onUp: function(){}},
    space : {down: false, onDown: function(){},onUp: function(){}},
    pageUp : {down: false, onDown: function(){},onUp: function(){}},
    pageDown : {down: false, onDown: function(){},onUp: function(){}},
    end : {down: false, onDown: function(){},onUp: function(){}},
    home : {down: false, onDown: function(){},onUp: function(){}},
    leftArrow : {down: false, onDown: function(){},onUp: function(){}},
    upArrow : {down: false, onDown: function(){},onUp: function(){}},
    rightArrow : {down: false, onDown: function(){},onUp: function(){}},
    downArrow : {down: false, onDown: function(){},onUp: function(){}},
    del : {down: false, onDown: function(){},onUp: function(){}},
    _0 : {down: false, onDown: function(){},onUp: function(){}},
    _1 : {down: false, onDown: function(){},onUp: function(){}},
    _2 : {down: false, onDown: function(){},onUp: function(){}},
    _3 : {down: false, onDown: function(){},onUp: function(){}},
    _4 : {down: false, onDown: function(){},onUp: function(){}},
    _5 : {down: false, onDown: function(){},onUp: function(){}},
    _6 : {down: false, onDown: function(){},onUp: function(){}},
    _7 : {down: false, onDown: function(){},onUp: function(){}},
    _8 : {down: false, onDown: function(){},onUp: function(){}},
    _9 : {down: false, onDown: function(){},onUp: function(){}},
    a : {down: false, onDown: function(){},onUp: function(){}},
    b : {down: false, onDown: function(){},onUp: function(){}},
    c : {down: false, onDown: function(){},onUp: function(){}},
    d : {down: false, onDown: function(){},onUp: function(){}},
    e : {down: false, onDown: function(){},onUp: function(){}},
    f : {down: false, onDown: function(){},onUp: function(){}},
    g : {down: false, onDown: function(){},onUp: function(){}},
    h : {down: false, onDown: function(){},onUp: function(){}},
    i : {down: false, onDown: function(){},onUp: function(){}},
    j : {down: false, onDown: function(){},onUp: function(){}},
    k : {down: false, onDown: function(){},onUp: function(){}},
    l : {down: false, onDown: function(){},onUp: function(){}},
    m : {down: false, onDown: function(){},onUp: function(){}},
    n : {down: false, onDown: function(){},onUp: function(){}},
    o : {down: false, onDown: function(){},onUp: function(){}},
    p : {down: false, onDown: function(){},onUp: function(){}},
    q : {down: false, onDown: function(){},onUp: function(){}},
    r : {down: false, onDown: function(){},onUp: function(){}},
    s : {down: false, onDown: function(){},onUp: function(){}},
    t : {down: false, onDown: function(){},onUp: function(){}},
    u : {down: false, onDown: function(){},onUp: function(){}},
    v : {down: false, onDown: function(){},onUp: function(){}},
    w : {down: false, onDown: function(){},onUp: function(){}},
    x : {down: false, onDown: function(){},onUp: function(){}},
    y : {down: false, onDown: function(){},onUp: function(){}},
    z : {down: false, onDown: function(){},onUp: function(){}},
    num0 : {down: false, onDown: function(){},onUp: function(){}},
    num1 : {down: false, onDown: function(){},onUp: function(){}},
    num2 : {down: false, onDown: function(){},onUp: function(){}},
    num3 : {down: false, onDown: function(){},onUp: function(){}},
    num4 : {down: false, onDown: function(){},onUp: function(){}},
    num5 : {down: false, onDown: function(){},onUp: function(){}},
    num6 : {down: false, onDown: function(){},onUp: function(){}},
    num7 : {down: false, onDown: function(){},onUp: function(){}},
    num8 : {down: false, onDown: function(){},onUp: function(){}},
    num9 : {down: false, onDown: function(){},onUp: function(){}}

}; // End of KEYS

////////////////////    
//   PRIMITIVES   //
////////////////////

////////////////////    
//    GUI         //
////////////////////
/**
*<hr>
*The <tt>MC.GUI</tt> object provides a simple way to manage multiple <tt>MC.ConBox</tt> objects
*@constructor
*@param {MC.ConBox} [ConBoxList] Comma seperated list of the ConBox's to be added
*<br>N.B. Non-Control Box items will be ignored
*@returns {MC.GUI} A <tt>new</tt> MC.GUI object
*@property {Array} bin The Array holding the Control Box items
*/
MC.GUI = function(ConBoxList) {
    this.bin = new Array();
    if (ConBoxList !== undefined) {
        this.push(ConBoxList);
    }
    return this;
};

/**
 *<hr>
 *Adds a <tt>MC.ConBox</tt> object to the end of the array.
 *@param {MC.ConBox} ConBoxList Comma seperated list of the ConBox's to be added
 *<br>N.B. Non-Control Box items will be ignored
 *@returns {this} enables method chaining
 */
MC.GUI.prototype.push = function (ConBoxList) {
    var len = arguments.length;
    for (var i = 0 ; i < len ; i++) {
        if (arguments[i] instanceof MC.ConBox) {
            this.bin.push(arguments[i]);
        }
    }
    return this;
};

/**
 *<hr>
 *Removes the last item from the <tt>bin</tt> array.
 *@returns {this} Enables method chaining
 */
MC.GUI.prototype.pop = function () {
    this.bin.pop();
    return this;
};

/**
 *<hr>
 *Renders all Control Boxes held in the GUI to the canvas.
 *<br>This call accepts 3 sets of parameters
 *<br>(1) Nothing, and all Control Boxes will be positioned as per their individual <tt>pos</tt> property.
 *<br>(2) An MC.Point, the GUI will auto fit it's contents using this position as a datum for the top left corner.
 *<br>(3) Two numbers, which mirror and MC.Point (and GUI rendered as above)
 *<br>In the latter two cases, the Control Boxes will be rendered in a left aligned vertical list
 *@param {MC.Point|Number} [position_or_x]
 *@param {Number} [y]
 *<br>N.B. Each Control Box's <tt>pos</tt> attribute is overwritten with their new rendered position
 *@returns {this} Enables method chaining
 */
MC.GUI.prototype.render = function (position_or_x,y) {
    var len = this.bin.length;
    if (position_or_x === undefined) {
        for (var i = 0; i < len ; i++) {
            if (this.bin[i] instanceof MC.ConBox) this.bin[i].render();
        }
        return this;
    }
    var position = new MC.Point;
    if (_utils.hasXY(position_or_x)) {
        position.set(position_or_x);
    }
    else if (arguments.length == 2 && _utils.isNumeric(position_or_x) && _utils.isNumeric(y)) {
        position.set(position_or_x,y);
    }
    
        this.clean(); // ensure we only have ConBoxes in the bin.
        len = this.bin.length;
        var o = new MC.Point();
        o.set(position);
        var P = 0;
        for (var i = 0; i < len ; i++) {
            P = _maths.maxOf(P, this.bin[i].edgeWidth);
        }
        P *= 2;
        for (var i = 0; i < len; i++) {
            if (i > 0) {
                o.set(P + this.bin[i].width / 2 + position.x, o.y + (this.bin[i].height + this.bin[i-1].height)/2 + P);
            }
            else {
                o.set (P + this.bin[i].width / 2 + position.x, o.y + (this.bin[i].height / 2) + P);
            }
                this.bin[i].render(o);            
            }

      return this;        
};

/**
 *<hr>
 *Removes all non <tt>MC.ConBox</tt> from the GUI.
 *<br>Called automatically by various other methods
 *@returns {this} Enables method chaining
 */
MC.GUI.prototype.clean = function () {
    var len = this.bin.length;
    var arr = new Array();
    for (var i = 0; i < len ; i++) {
        if (this.bin[i] instanceof MC.ConBox) {
            arr.push(this.bin[i]);
        }
    }
    this.bin = arr;
    return this;
};

/**
 *<hr>
 *Removes all elements from the GUI, making it available for reuse
 *@returns {this} Enables method chaining
 */
MC.GUI.prototype.empty = function () {
    var len = this.bin.length;
    for (var i = 0; i < len; i++) {
        this.bin[i] = null;
    }
    this.clean();
}

/**
 *<hr>
 *Calls the <tt>update()</tt> method of all ConBox's in the GUI
 *<br>This method should be called within any game loop, but only needs to be called once per frame.
 *<br>Therefore it is advised to call it AFTER an <tt>MC.game.physicsUpdate</tt> loop
 */
MC.GUI.prototype.update = function () {
    var len = this.bin.length;
    for (var i = 0; i < len ; i++) {
        if (this.bin[i] instanceof MC.ConBox) this.bin[i].update();
    }
    return this;
};

/**
 *<hr>
 *Calls the <tt>checkClick()</tt> method of all ConBox's
 *@returns {this} Enables method chaining
 */
MC.GUI.prototype.checkClick = function (mouse) {
    var len = this.bin.length;
    var m = mouse || _game.mouse;
        for (var i = 0; i < len ; i++) {
        if (this.bin[i] instanceof MC.ConBox) {
            this.bin[i].checkClick(mouse);
        }
    }
    return this;
};

/**
 *<hr>
 *Cycles through all ConBox's and calls their <tt>onClick()</tt> method if their <tt>hover</tt> property is <tt>true</tt>
 *@returns {this} Enables method chaining
 */
MC.GUI.prototype.click = function () {
    var len = this.bin.length;
    for (var i = 0; i < len ; i++) {
        if (this.bin[i] instanceof MC.ConBox) {
            if (this.bin[i].hover === true) {
                this.bin[i].onClick();
            }
        }
    }
    return this;
};

////////////////////    
//    ConBox      //
////////////////////
/**
*<hr>
*The <tt>MC.ConBox</tt> (short for "Control Box") object provides a basic clickable element, or button, on the canvas.
*<br>Although they can be manually managed, they are best implemented en-masse with the <tt>MS.GUI</tt> object
*<br>ConBox's constructor calls are made with a value object in key: value pairs (see the <tt>MC.ConBox.setValues()</tt> method below)
*<br>ConBox(es) have a variety of features, namely:
*<br>... Axis Aligned Rectangle or Circular form
*<br>... configurable to have a "hover over" color change
*<br>... text which is auto scaled to fit
*<br>... placeholder <tt>onClick()</tt> function for Developer customisation
*@constructor
*@this {ConBox}
*@returns {ConBox} a <tt>new</tt> ConBox object
*@property {String} [type="r"] ConBox type is either "r" for Rectangle, or "c" for Circular
*@property {MC.Point} [pos= mid Canvas] Screen Co-ordinates of the ConBox CENTRE
*@property {Number} [width=250] Width of the Control box
*<br>N.B. this is the diameter of any circular ConBox's
*@property {Number} [height = 50] Height of the ConBox (autoset to match Width for circular ConBox's)
*@property {String} [font = MC.game.text.font] Desired Font
*@property {Number} [fSize=50] Font size to be used displaying any text
*<br>Automatically set upon any setting or change of <tt>width, height or text</tt> properties
*@property {MC.Color|String} [colorBody=MC.game.conBoxDefaults.colorBody] The base (inactive) colour of the button
*@property {MC.Color|String} [colorBodyHover=MC.game.conBoxDefaults.colorBodyHover] The base colour of the button, when <tt>hover</tt> property is true.  i.e. the mouse pointer is over it
*@property {MC.Color|String} [colorEdge=MC.game.conBoxDefaults.colorEdge]  The base (inactive) colour of the button edge and text
*@property {MC.Color|String} [colorEdgeHover=MC.game.conBoxDefaults.colorEdgeHover]  The base colour of the button edge and text, when <tt>hover</tt> property is true
*@property {Number} [edgeWidth=MC.game.conBoxDefaults.edgeWidth] Size of the border to be applied to the button
*@property {String} [text="To Be Arranged"] Text (if any) to be displayed on the button
*@property {Boolean} [hover=false] Configured in the <tt>update()</tt> method.  <tt>True</tt> if the mouse cursor is over the button, else <tt>false</tt>
*@property {MC.Point} [offset] Internally used value to reflect the start position of the button text (from centre)
*@property {String} [oldText] Internally used flag variable to detect whether text re-size is required
*@property {Number} [oldWidth] Internally used flag variable to detect whether text re-size is required
*@property {Number} [oldHeight] Internally used flag variable to detect whether text re-size is required
*@property {MC.Typeface} [typeface] Internally used object for text rendering
*@property {Boolean} [visible=true] Visible ConBox's will be rendered, but not if this is set to <tt>false</tt>
*/
MC.ConBox = function (values) {
    // set defaults
    this.type = "r";
    this.pos = new MC.Point(_canvas.midX,_canvas.midY);
    this.width = 250;
    this.height = 50;
    this.font = _game.text.font;
    this.fSize = 50;
    this.colorBody = _game.conBoxDefaults.colorBody;
    this.colorBodyHover = _game.conBoxDefaults.colorBodyHover;
    this.colorEdge = _game.conBoxDefaults.colorEdge;
    this.colorEdgeHover = _game.conBoxDefaults.colorEdgeHover;
    this.edgeWidth = _game.conBoxDefaults.edgeWidth;
    this.text = "To Be Arranged";
    this.offset = new MC.Point();
    this.hover = false;
    this.oldText = "To Be Arranged";
    this.oldWidth = 250;
    this.oldHeight = 50;
    this.typeFace = new _Typeface();
    this.visible = true;
    // configure (as required)
    this.setValues(values);
};

/**
 *<hr>
 *Configures the ConBox via an object consisting of key: value pairs
 *<br>Called automatically upon construction, also calls the <tt>MC.ConBox.fitText()</tt> method
 *@param {Object} values The values to be applied
 *@throws Console warning if key value does not exist in the ConBox 
 *@returns {this} Enables method chaining
 *@example
 *var myConBox = new MC.ConBox (
 *                             {width: 150,
 *                              height: 30,
 *                              pos: new MC.Point(300,200),
 *                              font: "Impact",
 *                              text: "Print Hello World",
 *                              onClick: function () { console.log("Hello World");
 *                                                     myOtherFunction(); },
 *                              } );
 */
MC.ConBox.prototype.setValues = function (values) {
    for (var key in values) {
        var newValue = values[key];
        if (newValue === undefined) {
            console.log("WARNING: Value of "+ key+ " not set in ConBox.setValues()");
            continue;
        }
        var oldValue = this[key];
        if (oldValue === undefined) {
            console.log("WARNING: Property "+ newValue+ " does not exist in ConBox.setValues()");
            continue;
        }
        this[key] = newValue;
    }    
    this.textFit();
    return this;    
};

/**
*<hr>
* Clones the ConBox, and returns a new one.  Useful for rapid prototyping
*@returns {ConBox} a <tt>new</tt> ConBox object (with the same parameters)
*<br> WARNING: any custom behaviour defined in the <tt>onClick()</tt> function are NOT copied
*/
MC.ConBox.prototype.clone = function () {
    var ret = new _ConBox();
    ret.setValues({
        type: this.type,
        pos: this.pos.clone(),
        width: this.width,
        height: this.height,
        font: this.font,
        fSize: this.fSize,
        colorBody: this.colorBody,
        colorBodyHover: this.colorBodyHover,
        colorEdge: this.colorEdge,
        colorEdgeHover: this.colorEdgeHover,
        edgeWidth:  this.edgeWidth,
        text:  this.text,
        offset:  this.offset,
        hover:  this.hover,
        oldText: this.oldText,
        oldWidth: this.oldWidth,
        oldHeight: this.oldHeight,
        typeFace: this.typeFace.clone(),
        visible: this.visible,
    });
    return ret;
};


/**
 *<hr>
 *Developer customisable function determining what is to happen when the button is clicked
 */
MC.ConBox.prototype.onClick = function () {
    console.log(this.text + " clicked");
}; 

/**
 *<hr>
 *Calls the <tt>update()</tt> method and if the mouse if over the button (i.e. <tt>hover == true</tt>) calls the <tt>onClick()</tt> method
 *@param {MC.Point} [mouse=MC.game.mouse] Screen coordinates to be be checked against
 */
MC.ConBox.prototype.checkClick = function (mouse) {
  this.update(mouse);
  if (this.hover) this.onClick();
};

/**
 *<hr>
 *Checks if the mouse coordinates are over the button.  Is so, sets the <tt>hover</tt> property to <tt>true</tt>
 *<br>Both the <tt>clickCheck()</tt> and <tt>render()</tt> methods depend upon the <tt>hover</tt> property
 *<br>N.B. this need only be called once per frame, so in any <tt>requestAnimationFrame()</tt> gameloop it is recommended that this call is made after any <tt>MC.game.physicsUpates</tt> loop but before any render calls
 *@param {MC.Point} [mouse=MC.game.mouse] Mouse coordinates to be checked
 *@returns {this} Enables method chaining
 */
MC.ConBox.prototype.update = function (mouse) {
    var m = mouse || _game.mouse;
    if (this.type == "r") {
        if (m.x < this.pos.x-(this.width/2) || m.x > this.pos.x+(this.width/2) || m.y < this.pos.y - (this.height/2) || m.y > this.pos.y + (this.height/2) ) {
            this.hover = false;
            return this;
        }
    }
    if (this.type == "c") {
        if (_maths.rangeSqr(this.pos,m) > (this.width/2)*(this.width/2)) {
            this.hover = false;
            return this;
        }
    }
    this.hover = true;
    return this;
};

/**
 *<hr>
 *Renders the ConBox to the canvas, colours used depend upon the <tt>hover</tt> value
 *@param {MC.Point} [position=this.pos] Screen coordinates of the centre point of the button.
 *<br>N.B. The ConBox <tt>pos</tt> property will be overwritten by any newly presented position
 */
MC.ConBox.prototype.render = function (position) {
    if (!this.visible) {
        return this;
    }
    if (position !== undefined) {
        if (_utils.hasXY(position)) {
            this.pos.set(position);
        }
    }
    var c1 = this.colorBody;
    var c2 = this.colorEdge;
    if (this.hover) {
        c1 = this.colorBodyHover;
        c2 = this.colorEdgeHover;
    }
    if (this.oldText != this.text || this.oldWidth != this.width || this.oldHeight != this.Height) {
        this.setValues();
    }
    this.typeFace.color = c2;
    
    if (this.type == "r") {
        _draw.rectBasic(this.pos,this.width,this.height,c1,c2,this.edgeWidth);
        _draw.text(this.text,this.pos.clone().add(this.offset),this.typeFace);
    }
    if (this.type == "c") {
        _draw.circle(this.pos,this.width/2,c1,c2,this.edgeWidth);
        _draw.text(this.text,this.pos.clone().add(this.offset),this.typeFace);
    }
    return this;
};
/**
 *<hr>
 *Autofits the text to fit within the button.
 *<br>Called automatically upon construction, <tt>setValues</tt> or when an <tt>update()</tt> detects a change between current and "old" values of width, height or text
 */
MC.ConBox.prototype.textFit = function () {
    var o;
    if (this.type == "r") {
        var o = _draw.textFit(this.text,this.width * 0.9,this.height * 0.75,this.font);
    }
    if (this.type == "c") {
        var o = _draw.textFit(this.text,this.width * 0.65,this.height * 0.65,this.font);
    }
    this.offset = o.offset;
    this.typeFace.size = this.fSize = o.size;
    this.oldText = this.text;
    this.oldWidth = this.width;
    this.oldHeight = this.height;
    this.typeFace.font = this.font;
    this.typeFace.size = this.fSize;
    this.typeFace.color = this.colorEdge;
    if (this.type == "c") this.height = this.width;
    return this;
};



////////////////////    
//   TYPEFACE     //
////////////////////
/**
*<hr>
*The <tt>MC.Typeface</tt> Object provides a simple container for recording text formats
*@namespace MC.Typeface
*@constructor
*@param {String} Font The desired HTML5 font
*@param {Number} Size The font size
*@param {String|MC.Color} Color The text colour.  Either an HTML5 color string or <tt>MC.Color</tt> can be used
*@this {Typeface}
*@returns {Typeface} a <tt>new</tt> Typeface object
*/
MC.Typeface = function (Font,Size,Color) {
    this.font = Font;
    this.size = Size;
    this.color = Color;
    return this;
};

/**
*<hr>
*Clones the calling Typeface and returns a new one, useful for rapid prototyping
*@returns {Typeface} a <tt>new</tt> Typeface object (with the same values)
*/
MC.Typeface.prototype.clone = function () {
    return new MC.Typeface(this.font,this.size,this.color);
};

/**
 *<hr>Returns a string concatentation of the <tt>size+"Px "+font</tt>
 *<br>Used internally by the <tt>MC.draw.text()</tt> method
 */
MC.Typeface.prototype.log = function () {
  return this.size+"Px "+this.font;  
};

////////////////////    
//   PICTURE      //
////////////////////
/**
<hr>
* The MC Picture Object provides extensions to the native JS <tt>Image()</tt> object.
*A new <tt>Picture</tt> object can be created by 1 of two methods:
*<br>(1) Passing in an existing JS <tt>Image()</tt> object or
*<br>(2) Passing in a string representing the required Image source.  <tt>Picture</tt> objects initialise themselves once the Image is loaded, and will not crash if <tt>render()</tt> is called before loading is complete
*
* @class
* @namespace MC.Picture 
* @constructor
* @this {Picture}
* @param {Image|String|MC.Point} image -  a JS <tt>Image()</tt> object OR a string direction to one e.g. "../images/bullet_cat.jpg" OR an MC.Point which creates a blank image with width of image.x and height of image.y (the latter being ready for subsequent manipulation)
* @returns {Picture} A new Picture object
* @property img {HTML_Image}
* @property loaded=false {Boolean} Set to <tt>true</tt> once Image has been loaded
* @property width=0 {Number} Native Width of the Image, set upon loading
* @property height=0 {Number} Native Height of the Image, set upon loading
* @property imageData {Array.<Number>} Raw bitmap Image data (needs width and height to be unwrapped)
*/
MC.Picture = function(image) {
    this.img = null;
    this.loaded = false;
    this.width = 0;
    this.height = 0;
    this.imageData = null;  // NOT IMPLEMENTED - Awaiting "dirty Canvas" decisions
    if (image instanceof HTMLImageElement) {
        this.init(image);
    }
    else if (typeof image ==="string" || image instanceof String) {
        var myself = this;       
        var pending = new Image();
        //pending.crossOrigin = '';
        pending.src = image;
        pending.onload = function () {
            myself.init(pending);
        };
    }
    else if (image instanceof _Point) {
        this.img = new Image(image.x,image.y);
        var data = [image.x];
        this.init(this.img);
    }
    return this;
};

/**
 *<hr>
 *Initialises the <tt>Picture</tt>
 *<br>CAUTION: This is automatically run upon image file loading.  Creating a <tt>new</tt> object is always more reliable than editing properties.
 */
MC.Picture.prototype.init = function (image) {
    this.img = image;
    this.width = this.img.width;
    this.height = this.img.height;
    this.loaded = true;
    //console.log(this.img.crossOrigin);
    this.img.crossOrigin = '';
    //console.log(this.img.crossOrigin);
    //console.log(this.img);
    //this.populateImageData();
    return this;
};

// NOT IMPLEMENTED - Awaiting "dirty Canvas" decisions
MC.Picture.prototype.populateImageData = function () {
    _backCanvas.width = this.img.width;
    _backCanvas.height = this.img.height;
    _backCanvasContext.drawImage(this.img,0,0);
    this.imageData = _backCanvasContext.getImageData(0,0,this.img.width, this.img.height);
};

/**
 *<hr>
 *Renders the picture to the canvas
 *<br> See MC.draw.picture() for parameter details
 *<br>N.B. this method requires EXACTLY the first 2,3 or all 6 parameters.
 *<br>(1) Only using <tt>(centre,angle)</tt> means the image is drawn at 1:1 scale, centred and rotated about the <tt>centre</tt> point
 *<br>(2) Adding the <tt>width,height</tt> pair scales the image to the appropriate size (in pixels)
 *<br>(3) Adding the <tt>start,finish</tt> pair clips the image to a rectangle defined by <tt>start</tt> (top left corner) and <tt>finish</tt> (bottom right hand corner).  Essential for using sprite sheets.
 *<br>(4) If any <tt>start</tt> or <tt>finish</tt> parameter is less or equal to 1, then that ratio of the native image width/height will be applied.
 *<br>A Gray Box with Red cross appears (<tt>draw.loadWarning()</tt>) is draw is this method is called and the image has not loaded
 *@param {MC.Point} centre - Canvas location of the centre of the image (1)
 *@param {Number} angle - Angle (degrees) the image is to be rotated about the centre.  0 (zero) = no rotation (1)
 *@param {Number} [width] - Width image is to be displayed with (before any rotation).  If used then <tt>height</tt> must be specified (2)
 *@param {Number} [height] - As per width, but image display height in pixels (2)
 *@param {MC.Point} [start] - For displaying only a section of the image, start is a point representing the top left coordinates of the section ot be displayed.  If <tt>start</tt> is specified then <tt>finish</tt> has to be too (3)(4)
 *@param {MC.Point} [finish] - As per <tt>start</tt> above, except this is the bottom right of the selection to be displayed (3)(4)
 */
MC.Picture.prototype.render = function (centre,angle,width,height,start,finish) {
    if (!this.loaded) {
        _draw.loadWarning(centre);
    }
    else {
        var L = arguments.length;
        if (L == 2)  _draw.picture(this.img,centre,angle);
        else if (L == 4) _draw.picture(this.img,centre,angle,width,height);
        else if (L == 6) _draw.picture(this.img,centre,angle,width,height,start,finish);
    }
    return;
};

// NOT IMPLEMENTED - Awaiting "dirty Canvas" decisions
MC.Picture.prototype.getPixel = function (XRatio,YRatio) {
    var out = new _Color();
    var W = this.img.width;
    var X = Math.floor(W * XRatio);
    var Y = Math.floor(this.img.height * YRatio);
    W *= 4;
    var Base = (Y * W) + (X * 4);
    // Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas (Sept 2018)
    out.r = this.img.imageData.data[Base];
    out.g = this.img.imageData.data[Base + 1];
    out.b = this.img.imageData.data[Base + 2];
    out.a = this.img.imageData.data[Base + 3];

    return out;
};

// NOT IMPLEMENTED - Awaiting "dirty Canvas" decisions
MC.Picture.prototype.paintPixel = function (XRatio, YRatio, Color) {
    var W = this.img.width;
    var X = Math.floor(W * XRatio);
    var Y = Math.floor(this.img.height * YRatio);
    W *= 4;
    var Base = (Y * W) + (X * 4);
    // Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas (Sept 2018)
    this.img.imageData.data[Base] = Color.r;
    this.img.imageData.data[Base + 1] = Color.g;
    this.img.imageData.data[Base + 2] = Color.b;
    this.img.imageData.data[Base + 3] = Color.a;
};

    


////////////////////    
//     POINT      //
////////////////////
/**
<hr>
* Creates a 2 Dimensional Point / Vector
*
* @class
* @namespace MC.Point 
* @constructor
* @this {MC.Point}
* @param {Number|Object} [x=0] -  The x value for the Point, if an Object is entered which has numeric <tt>>x & y</tt> properties, such as another MC.Point, then the new Point is created with these values
* @param {Number} [y=0] - The y value for the Point
* @property x {Number}
* @property y {Number}
* @returns {MC.Point}
*/
MC.Point = function(x,y) {
    if (x !== undefined &&_utils.hasXY(x)) {
        if (_utils.isNumeric(x.x) && _utils.isNumeric(x.y)) {
            this.x = x.x;
            this.y = x.y;
        }        
    }
    else {
        this.x = x || 0;
        this.y = y || 0;
    }   
    return this;
};

/**
 *<hr>
*Console logs the value of a Point (for debugging and testing purposes)
*@returns {this} Enables method chaining
*/
MC.Point.prototype.log = function () {
    console.log(" Point X: "+this.x,",Y: "+this.y);
    return this;
};

/**
 *<hr>
 *Adds another point/vector or <tt>(x,y)</tt> to the calling point
 *<br>N.B Any object with numeric <tt>x</tt> and <tt>y</tt> properties can be added to a Point
 *@param {MC.Point|Number} point_or_x - Point (or <tt>x</tt> value) to add
 *@param {Number} [y] - <tt>y</tt> value to add
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.add = function (point_or_x,y) {
    if (_utils.hasXY(point_or_x)) {
        if (_utils.isNumeric(point_or_x.x) && _utils.isNumeric(point_or_x.y)) {
            this.x += point_or_x.x;
            this.y += point_or_x.y;
        }
    }
    else if (_utils.isNumeric(point_or_x) && _utils.isNumeric(y)) {
        this.x += point_or_x;
        this.y += y;
    }
    return this;
};

/**
 *<hr>
 *Subtracts another point/vector or <tt>(x,y)</tt> from the calling point
 *<br>N.B Any object with numeric <tt>x</tt> and <tt>y</tt> properties can be subtracted from a Point
 *@param {MC.Point|Number} point_or_x - Point (or <tt>x</tt> value) to subtract
 *@param {Number} [y] - <tt>y</tt> value to subtract
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.minus = function (point_or_x,y) {
    if (_utils.hasXY(point_or_x)) {
        if (_utils.isNumeric(point_or_x.x) && _utils.isNumeric(point_or_x.y)) {
            this.x -= point_or_x.x;
            this.y -= point_or_x.y;
        }
    }
    else if (_utils.isNumeric(point_or_x) && _utils.isNumeric(y)) {
        this.x -= point_or_x;
        this.y -= y;
    }
    return this;
};

/**
 *<hr>
 *Multiplies the point/vector by a scalar value
 *@param {Number} x - Number to multiply the Point by OR (if <tt>y</tt> is included) number to multiply the <tt>x</tt> property by
 *@param {Number} [y] - Number to multiply the <tt>y</tt> value by
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.times = function (x,y) {
    if (arguments.length == 1) {
        if (_utils.isNumeric(x)) {
        this.x *= x;
        this.y *= x;
        }    
    }
    else if (arguments.length == 2) {
        if (_utils.isNumeric(x) && _utils.isNumeric(y)) {
        this.x *= x;
        this.y *= y;
        }    
    }
    return this;
};

/**
 *<hr>
 *Divides the point/vector by a scalar value
 *@param {Number} - Number to divide Point by
 *@returns {this} Enables method chaining
 *@throws Console warning if attempting to divide by 0 (zero).  Point is returned unchanged.
 */
MC.Point.prototype.div = function (number) {
    if (number === 0) {
        console.log("WARNING attempt to divide a Point by zero");
        return this;
    }
    else if (_utils.isNumeric(number)) {
        this.x /= number;
        this.y /= number;
    }
    return this;
};

 /**
  *<hr>
  *Checks if the Point (as a vector) it to the left of another
  *@param {MC.Point} Vector to be checked against.
  *<br> N.B anything with <tt>.x</tt> and <tt>.y</tt> properties can be checked against
  *@returns {Boolean} <tt>true</tt> if the vector is to the left, else <tt>false</tt>
  */
 MC.Point.prototype.isLeft = function (Vector) {
    if (_utils.hasXY(Vector)) {
        if (_maths.crossProd(this,Vector) < 0) return true;
    }
    return false;
 };

 /**
  *<hr>
  *Checks if the Point (as a vector) it to the right of another
  *@param {MC.Point} Vector to be checked against.
  *<br> N.B anything with <tt>.x</tt> and <tt>.y</tt> properties can be checked against
  *@returns {Boolean} <tt>true</tt> if the vector is to the right, else <tt>false</tt>
  */
 MC.Point.prototype.isRight = function (Vector) {
    if (_utils.hasXY(Vector)) {
        if (_maths.crossProd(this,Vector) > 0) return true;
    }
    return false;
 };

/**
 *<hr>
 *Sets the point to equal another point/vector OR a <tt>(x,y)</tt>
 *<br>N.B Any object with numeric <tt>x</tt> and <tt>y</tt> properties can be used
 *@param {MC.Point|Number} point_or_x - Point to equal OR the new <tt>x</tt> value
 *@param {Number} [y] - y value
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.set = function (point_or_x,y) {
    if (_utils.hasXY(point_or_x)) {
        if (_utils.isNumeric(point_or_x.x) && _utils.isNumeric(point_or_x.y)) {
            this.x = point_or_x.x;
            this.y = point_or_x.y;
        }
    }
    else if (arguments.length == 2) {
        if (_utils.isNumeric(point_or_x) && _utils.isNumeric(y)) {
            this.x = point_or_x;
            this.y = y;
        }
    }
    return this;    
};

/**
 *<hr>
 *Reports the scalar length of the vector / point
 *@returns {Number} Length of the vector
 */
MC.Point.prototype.getLength = function() {
    return Math.sqrt((this.x * this.x) + (this.y * this.y));
};

/**
 *<hr>
 *Reports the scalar length SQUARED of the vector / point
 *@returns {Number} Length of the vector
 */
MC.Point.prototype.getLengthSqr = function() {
    return (this.x * this.x) + (this.y * this.y);
};

/**
 *<hr>
 *Normalises the Vector.
 *<br>NB. See <tt>MS.maths.normalOf()</tt> if you need the normal but want to keep the original vector
 *<br>Both <tt>Point.normalise()</tt> OR <tt>Point.normalize()</tt> will work.
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.normalise = function() {
    var L = this.getLength();
    this.x /= L;
    this.y /= L;
    return this;
};

// spelling alternative
MC.Point.prototype.normalize = function () {
    this.normalise();
    return this;
};

/**
 *<hr>
 *Sets the length of a vector
 *@param {Number} - Desired vector length
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.setLength = function (length) {
    if (_utils.isNumeric(length)) {
            this.normalise().times(length);
    }
    return this;
};

/**
 *<hr>
 *Inverts / reverses the direction of the vector
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.invert = function () {
    this.x *= -1;
    this.y *= -1;
    return this;
};

/**
 *<hr>
 *Returns the angle of the vector (as if it's a line with origin 0,0)
 *@returns {Number} The angle of the vector (in degrees clockwise)
 *@example
 * ..........| 270
 *        (0,-1)
 *  180      |        360
 *  ------------(1,0)---->
 *           |         0
 *           |  (1,1)
 *       90  |
 *var pnt = new MC.Point(1,0);
 *var angle = pnt.getAngle(); // 0
 *pnt.set(1,1);
 *angle = pnt.getAngle(); // 45
 *pnt.set(0,-1);
 *angle = pnt.getAngle(); // 270
 */
MC.Point.prototype.getAngle = function () {
    return _utils.fixAngle(Math.atan2(this.y , this.x) * _maths.TO_DEGREES);
};

/**
 *<hr>
 *Rotates a vector (as if it's a line with origin 0,0 ... or if as a coordinate with optional MC.Point as an origin)
 *@param {Number} - Angle to rotate by (+ve = clockwise)
 *@param {MC.Point} [origin] optional origin for a coordinate rotation
 *@returns {this} Enables method chaining
 *
 */
MC.Point.prototype.rotate = function (degrees,origin) {
    var o = new _Point(0,0);
    if (origin !== undefined && _utils.hasXY(origin)) {
        o.x = origin.x;
        o.y = origin.y;
    }
    if (degrees !== 0 && _utils.isNumeric(degrees)) {
        this.minus(o);
        var s = Math.sin(degrees * _maths.TO_RADIANS);
        var c = Math.cos(degrees * _maths.TO_RADIANS);
        var nx = (this.x * c) - (this.y * s);
        var ny = (this.x * s) + (this.y * c);
        this.x = nx;
        this.y = ny;
        this.add(o);
    }
    return this;
};

/**
 *<hr>
 *Set a vector to an angle (as if it's a line with origin 0,0)
 *@param {Number} - Desired angle
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.setAngle = function (angle) {
    if (angle !== 0 && _utils.isNumeric(angle)) {
        this.rotate(angle - this.getAngle());
    }
    return this;
};

/**
 *<hr>
 *Calculates the angle from one vector to another
 *N.B. +ve = clockwise, so in some circumstances this can be used to determine direction (i.e. a velocity vector with a relational direction vector as target) 
 *<br> 0-90 -- target is bearing front right
 *<br> 90-180 -- target is bearing rear right
 *<br> 180-270 -- target is bearing rear left
 *<br> 270-360 -- target is bearing front left
 *
 *@param {MC.Point} - Target vector/point
 *@returns {Number} Angle from calling vector to target vector/point (in degrees)
 *@throws Console warning if target does not have <tt>x</tt> and <tt>y</tt> properties (or they are non-numeric).  Calling vector is unchanged and 0 (zero) returned
 */
MC.Point.prototype.angleTo = function (target) {
    if (_utils.hasXY(target)) {
        if (_utils.isNumeric(target.x) && _utils.isNumeric(target.y)) {
            var tp = new _Point(target.x,target.y); // allows a non-Point object which has x: & y: to be used as a target
            return _utils.fixAngle(tp.getAngle() - this.getAngle());
        }
    }
    // not returned, ergo invalid target
    console.log("WARNING: invalid properties passed to Point.angleTo()");
    return 0;
};

/**
 *<hr>
 *Clones the Point/Vector and returns a copy
 *@returns {MC.Point} A NEW Point Object
 */
MC.Point.prototype.clone = function () {
    return new _Point(this.x,this.y);
};

/**
 *<hr>
 *Returns the Point with Absolute values, e.g. (5,-4) is returned as (5,4)
 *@returns {this} Enables method chaining
 */
MC.Point.prototype.abs = function()
{
    if (this.x < 0) {
        this.x *= -1;
    }
    if (this.y < 0) {
        this.y *= -1;
    }
    return this;
};

/**
 *<hr>
 *Scales the vector
 *@param {Number} scale - Scale (aka multiplier) to be applied
 *@returns {this} Enables method chaining
 *@throws Console warning if a non-numeric or 0 (zero) scale applied.  Original Point returned
 */
MC.Point.prototype.scale = function (scale) {
    if (scale === 1) { // quick "get out" to save time for non-event
        return this;
    }
    if (_utils.isNumeric(scale) && scale !== 0) {
        this.x *= scale;
        this.y *= scale;
        return this;
    }
    else {
        console.log("WARNING: improper scale passed to Point.scale()");
        return this;
    }
};

/**
 *<hr>
 *Clamps the length of the vector to a maximum value OR between a pair of values
 *<br>N.B. Useful for limiting velocities
 *@param {Number} [min=0] - Minimum length of clamp value
 *@param {Number} max - Maximum length of clamp value
 *@return {this} Enables method chaining
 */
 MC.Point.prototype.clamp = function (min,max) {
    var L = this.getLength();
    var big = L;
    var small = 0;
    if (arguments.length == 1 && _utils.isNumeric(min)) {
        big = Math.abs(min);
    }
    else if (arguments.length == 2 && _utils.isNumeric(min) && _utils.isNumeric(max)) {
        big = Math.abs(max);
        small = Math.abs(min);
    }
    if (L > big) {
        this.setLength(big);
    }
    else if (L < small) {
        this.setLength(small);
    }
    return this;
};

/**
 *<hr>
 *Checks if the Point's <tt>x</tt> and <tt>y</tt> properties match those of another item (which may, or may not be another Point)
 *@param {Object} item - Object for comparison
 *@returns {Boolean} <tt>true</tt> if both <tt>x</tt> and <tt>y</tt> properties match
 */
MC.Point.prototype.equals = function (item) {
    if (_utils.hasXY(item)) {
        if (_utils.isNumeric(item.x) && _utils.isNumeric(item.y)) {
            return (this.x == item.x && this.y == item.y);
        }
    }
    return false;
};

////////////////////    
//     COLOR      //
////////////////////
/**
 *<hr>
 *Creates a Color Object. 
 *<br>MC uses rbg (0-255) notation with alpha (0-1)
 *<br>N.B.  HTML5 default names colours (e.g. "Aqua" and "Salmon") can be used instead
 *
 *@class
 *@namespace MC.Color
 *@constructor
 *@this {MC.Color}
 *@param {Number} [r=255] - Red Color component (clamped 0-255)
 *@param {Number} [g=255] - Green Color component (clamped 0-255)
 *@param {Number} [b=255] - Blue Color component (clamped 0-255)
 *@param {Number} [a=1] - Color alpha (aka transparency).  1 = solid, 0 = fully transparent
 *@returns {Color}
*/
MC.Color= function (r,g,b,a) {
    this.r = (r === 0) ? 0 : ~~_maths.clamp(r,0,255) || 255;
    this.g = (g === 0) ? 0 : ~~_maths.clamp(g,0,255) || 255;
    this.b = (b === 0) ? 0 : ~~_maths.clamp(b,0,255) || 255;
    this.a = (a === 0) ? 0 : a || 1;
    return this;
};

/**
 *<hr>
 *Shifts the color towards a second Color, by the ratio provided
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 *@param {MC.Color} Target Color to be shifted towards
 *@param {Number} Ratio ratio of the shift, Values are capped between 0-1
 *@throws Console warning if a non-MC.Color is passed in.  Color itself is unchanged
 *<br>N.B Alpha (a) transparency value is also shifted
 */
MC.Color.prototype.lerp = function (Target, Ratio) {
    Ratio = _maths.clamp(0,1);
    if (!(Target instanceof MC.Color)) {
        console.log("WARNING: non-MC.Color passed to MC.Color.lerp()");
        return this;
    }
    this.addR((Target.r - this.r) * Ratio);
    this.addG((Target.g - this.g) * Ratio);
    this.addB((Target.b - this.b) * Ratio);
    this.a += (Target.a - this.a) * Ratio;
    return this;
};

/**
 *<hr>
 *Returns the RGB value of a {MC.Color} in hex format.  e.g. "#AA05F5"
 *@this {MC.Color}
 *@returns {string}
 **/
MC.Color.prototype.toHex = function () {
    return "#"+_maths.toHex(this.r)+_maths.toHex(this.g)+_maths.toHex(this.b);
};

/**
 *<hr>
 *Returns the RGB value of a {MC.Color} in rbg(r,g,b) format.  e.g. "rgb(120,0,255)"
 *@this {MC.Color}
 *@returns {string}
 **/
MC.Color.prototype.rgb = function () {
    return "rgb("+this.r+","+this.g+","+this.b+")";
};

/**
 *<hr>
 *Adds <tt>r</tt> to the Color.r
 *<br>Result is clamped between 0 and 255
 *@param {Number} - value to add the R Color component
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.addR = function (r) {
    this.r = ~~_maths.clamp(this.r + r ,0,255);
    return this;
};

/**
 *<hr>
 *Adds <tt>r</tt> to the Color.g
 *<br>Result is clamped between 0 and 255
 *@param {Number} - value to add the G Color component
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.addG = function (g) {
    this.g = ~~_maths.clamp(this.g + g ,0,255);
    return this;
};

/**
 *<hr>
 *Adds <tt>r</tt> to the Color.b
 *<br>Result is clamped between 0 and 255
 *@param {Number} - value to add the B Color component
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.addB = function (b) {
    this.b = ~~_maths.clamp(this.b + b ,0,255);
    return this;
};

/**
 *<hr>
 *Subracts <tt>r</tt> from the Color.r
 *<br>Result is clamped between 0 and 255
 *@param {Number} - value to subtract from the R Color component
 *@this {MC.Color}
 *@returns {this} Enables methof chaining
 */
MC.Color.prototype.minusR = function (r) {
    this.r = ~~_maths.clamp(this.r - r ,0,255);
    return this;
};

/**
 *<hr>
 *Subtracts <tt>r</tt> from the Color.g
 *<br>Result is clamped between 0 and 255
 *@param {Number} - value to subtract from the G Color component
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.minusG = function (g) {
    this.g = ~~_maths.clamp(this.g - g ,0,255);
    return this;
};

/**
 *<hr>
 *Subtracts <tt>r</tt> from the Color.b
 *<br>Result is clamped between 0 and 255
 *@param {Number} - value to subtract from the B Color component
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.minusB = function (b) {
    this.b = ~~_maths.clamp(this.b - b ,0,255);
    return this;
};

/**
 *<hr>
 *Inverts the Color (i.e. Red value becomes (255 - Red), etc)
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.invert = function () {   
    this.r = ~~_maths.clamp(255-this.r,0,255);
    this.g = ~~_maths.clamp(255-this.g,0,255);
    this.b = ~~_maths.clamp(255-this.b,0,255);
    return this;
};

/**
 *<hr>
 *Converts the Color to Grayscale
 *<br> Uses a Luminocity formula of (Red * 0.21 + Green * 0.72 + Blue * 0.07).
 *<br> See <a href="https://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/" target ="_blank">this link</a> for more info (opens in a new window)
 *<br> N.B both <tt>Color.toGray()</tt> and <tt>Color.toGrey()</tt> work
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.toGrey = function () {
    // formula from https://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/ (June 2018)
    this.r = this.g = this.b = _maths.clamp(~~((this.r * 0.21) + (this.g * 0.72) + (this.b * 0.07)),0,255);
    return this;
};

// Spelling alternative of toGrey() (above)
MC.Color.prototype.toGray = function () {
    this.toGrey();
    return this;
};

/**
 *<hr>
 *Converts Color to Sepia
 *<br> See <a href="https://stackoverflow.com/questions/1061093/how-is-a-sepia-tone-created" target ="_blank">this link</a> for more info (opens in a new window)
 *@this {MC.Color}
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.toSepia = function () {
    // formula from https://stackoverflow.com/questions/1061093/how-is-a-sepia-tone-created (June 2018)
    var R = this.r;
    var G = this.g;
    var B = this.b;
    this.r = _maths.clamp(~~((R * 0.393)+(G * 0.769)+(B * 0.189)),0,255);
    this.g = _maths.clamp(~~((R * 0.349)+(G * 0.686)+(B * 0.168)),0,255);
    this.b = _maths.clamp(~~((R * 0.272)+(G * 0.534)+(B * 0.131)),0,255);
    return this;
};

/**
 *<hr>
 *Returns a string of the Color attributes in rbga format e.g. "rgba(120,45,67,0.34)"
 *@returns {string}
 */
MC.Color.prototype.rgba = function() {
    return "rbga("+this.r+","+this.g+","+this.b+","+this.a+")";
};

/**
 *<hr>
 *Sets a Color
 *<br>Either a Color can be entered, or <tt>(r,g,b,a)</tt> (with the <tt>a</tt> being optional)
 *@param {Number|MC.Color} red_or_Color - Red component (clamped to 0-255) OR the Color to be matched
 *@param {Number} [green] - Green component (clamped to 0-255)
 *@param {Number} [blue] - Blue component (clamped to 0-255)
 *@param {Number} [alpha] - Transparency (clamped to 0-1)
 *@returns {this} Enables method chaining
 *
 */
MC.Color.prototype.set = function (red_or_Color,green, blue, alpha) {
    if (arguments.length == 1) {
        this.r = ~~_maths.clamp(red_or_Color.r,0,255);
        this.g = ~~_maths.clamp(red_or_Color.g,0,255);
        this.b = ~~_maths.clamp(red_or_Color.b,0,255);
        this.a = _maths.clamp(red_or_Color.a,0,1);
        return this;
    }
    else {
        this.r = ~~_maths.clamp(red_or_Color,0,255);
        this.g = ~~_maths.clamp(green,0,255);
        this.b = ~~_maths.clamp(blue,0,255);
        if (arguments.length == 4) {
            this.a = _maths.clamp(alpha,0,1);
        }
        return this;
    }
};

/**
 *<hr>
 *Sets the Color's Red component
 *<br> N.B although e.g. <tt>Color.r = 25;</tt> could be used, this method clamps the value between legal 0-255
 *@param {Number} - The new Red color component
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.setR = function (red) {
    if (_utils.isNumeric(red)) {
        this.r = ~~_maths.clamp(red,0,255);
    }
    return this;    
};

/**
 *<hr>
 *Sets the Color's Green component
 *<br> N.B although e.g. <tt>Color.g = 25;</tt> could be used, this method clamps the value between valid 0-255
 *@param {Number} - The new Green color component
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.setG = function (green) {
    if (_utils.isNumeric(green)) {
        this.g = ~~_maths.clamp(green,0,255);
    }
    return this;    
};

/**
 *<hr>
 *Sets the Color's Blue component
 *<br> N.B although e.g. <tt>Color.r = 25;</tt> could be used, this method clamps the value between valid 0-255
 *@param {Number} - The new Blue color component
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.setB = function (blue) {
    if (_utils.isNumeric(blue)) {
        this.b = ~~_maths.clamp(blue,0,255);
    }
    return this;    
};

/**
 *<hr>
 *Sets the Color's Alpha component
 *<br> N.B although e.g. <tt>Color.a = 0.3;</tt> could be used, this method clamps the value between valid 0-1
 *@param {Number} - The new alpha color component
 *@returns {this} Enables method chaining
 */
MC.Color.prototype.setA = function (alpha) {
    if (_utils.isNumeric(alpha)) {
        this.a = _maths.clamp(alpha,0,1);
    }
    return this;    
};

/**
 *<hr>
 *Clones the current Color and returns a NEW copy
 *@returns {MC.Color} A NEW color object with the same parameters
 */
MC.Color.prototype.clone = function () {
    return new _Color(this.r,this.g,this.b,this.a);
};

////////////////////    
//   SPRITEBIN    //
////////////////////
/**
*<hr>
*Creates <tt>new SpriteBin</tt> object.
*<br>SpriteBin's provide a useful set of functions for the easy management of Sprite Collections.
*<br>SpriteBin's sizes are adjusted as necessary at run-time.
*@class
*@namespace MC.SpriteBin
*@constructor
*@property {Array} bin The internal array holding the Sprites
*@property {Boolean} clean Internal flag to trigger <tt>cleanBin()</tt>
*@this {MC.SpriteBin}
*@returns {MC.SpriteBin} A <tt>new</tt> SpriteBin object
*/
MC.SpriteBin = function () {
    this.bin = new Array();
    this.clean = true;
    this.DT;
};

/**
 *<hr>
 *Calls the <tt>update()</tt> function on each Object in the SpriteBin (if it has it).
 *Elements with <tt>null</tt> or Sprites with <tt>.dead == True</tt> are flagged to require clean up.
 *@param {Number} [deltaTime=MC.game.deltaTime] DeltaTime (before any physicsUpdates calculation) in Seconds.
 *@returns {this} Enables Method chaining
 */
MC.SpriteBin.prototype.update = function (deltaTime) {
    this.DT = deltaTime || _game.deltaTime;

    var len = this.bin.length;
    if (len == 0) {
        return this;
    }
    for (var i = 0; i < len; i++)
    {
        if (this.bin[i] === undefined || this.bin[i] === null || this.bin[i].dead === true) {
           this.clean = false;
           continue;
        }
        else if(typeof this.bin[i].update === 'function') {
            this.bin[i].update(this.DT);
        }
    }
    
    return this;
};

/**
 *<hr>
 *Calls the <tt>render()</tt> function on each Object in the SpriteBin (if it has it) so it is drawn on the game canvas
 *@returns {this} Enables Method chaining
 */
MC.SpriteBin.prototype.render = function () {
    var len = this.bin.length;
    for (var i = 0; i < len; i++) {
        if(this.bin[i] !== null && this.bin[i].dead !== true && typeof this.bin[i].render === 'function') {
            this.bin[i].render();
        }
    }
    if (!this.clean) {
        this.cleanBin();
    }
    return this;
};
/**
 *<hr>
 *Cycles through all Sprites in the bin and removes any which are <tt>null</tt> or where <tt>Sprite.dead == true</tt>
 *<br>Called automatically at the end of any <tt>render()</tt> call should <tt>update()</tt> have found any candidates for deletion
 */
MC.SpriteBin.prototype.cleanBin = function () {
    var ret = new Array();
    var len = this.bin.length;
    for (var i = 0; i < len;i++) {
        if (this.bin[i] !== null && this.bin[i].dead !== true) {
            ret.push(this.bin[i]);
        }
    }
    this.bin = ret;
    this.clean = true;
};

/**
 *<hr>
 *Cycles through the Spritebin and calls the <tt>bounce()</tt> method of each Sprite against the target Parameter
 *@param {MC.Sprite|MC.SpriteBin} [target=self] Target for the <tt>bounce()</tt> call
 *<br>If omitted, each Sprite in the bin will be bounced off each other
 *<br>If an MC.Sprite, target is a single sprite
 *<br>If a SpriteBin, each Sprite in the calling bin will be bounced off all the Sprites in the target bin.
 *<br>WARNING: this last simple call can have a huge performance hit if Spritebins (and/or number of physicsUpdates) are large
 *@returns {this} Enables method chaining.
 */
MC.SpriteBin.prototype.bounce = function (target) {
    
    if (target === undefined) { // self hit bounce call
        var len = this.bin.length;
        for (var i = 0 ; i < len ; i++) {
            if (!_utils.validHT(this.bin[i])) continue;
                for (var j = 0; j < len; j++) {
                    if (!_utils.validHT(this.bin[j] || i == j)) {
                        continue;
                    }
                    this.bin[i].bounce(this.bin[j]);
                }
        }
    }
    
    else if (target instanceof MC.Sprite) { // single Sprite
        // Quick get out of jail check
        if (!_utils.validHT(target)) {
            return this;
        }
        // valid target, can proceed
        var len = this.bin.length;
        for (var i = 0; i < len; i++) {
            if (_utils.validHT(this.bin[i])) {
                this.bin[i].bounce(target);
            }
        }
    }
    
    else if (target instanceof MC.SpriteBin) { // other SpriteBin
        var len = this.bin.length;
        var l2 = target.bin.length;
        for (var i = 0 ; i < len ; i++) {
            if (!_utils.validHT(this.bin[i])) continue;
                for (var j = 0; j < l2 ; j++) {
                    if (_utils.validHT(target.bin[j])) {
                        this.bin[i].bounce(target.bin[j]);
                    }
                }
        }
    }
    return this;
};

/**
 *<hr>
 *Empties the bin of everything.  Useful for game or level resets
 *<br>The SpriteBin itself is still available for use
 *@returns {this} Enabled Method Chaining
 */
MC.SpriteBin.prototype.empty = function () {
    var len = this.bin.length;
    for (var i = 0; i < len; i++) this.pop();
    return this;
};

/**
 *<hr>
 *Adds Sprite(s) to the END of the SpriteBin list
 *@param {MC.Sprite} sprites Comma seperated list of sprite(s)
 *<br>N.B. Non-sprites will be ignored
 *@returns {this} Enabled Method Chaining
 */
MC.SpriteBin.prototype.push = function (sprites) {
    var len = arguments.length;
    for (var i = 0; i < len; i++) {
        if (arguments[i] instanceof MC.Sprite) {
            this.bin.push(arguments[i]);
        }
    }
    return this;
};

/**
 *<hr>
 *Removes the last object from the SpriteBin list
 *@returns {this} Enabled Method Chaining
 */
MC.SpriteBin.prototype.pop = function () {
    this.bin.pop();
    return this;
};


////////////////////    
//    SPRITE      //
////////////////////
/**
 *<hr>
 *Creates a <tt>Sprite</tt> Object. 
 *<br>MC considers all drawn assets to be Sprites
 *<br>Constructor can be passed an Object of key: value pairs at configuration (see example below), or subsequently with the <tt>setValues(parameters)</tt> method
 *<br>Sprites use the <tt>MC.Sprite.update</tt> methos to automatically change position, rotation, scale, animation etc etc.
 *<br>Developers should augment the <tt>MC.Sprite.dev</tt> Object to further customise the Sprite's behaviour.  CAUTION: Developers are recommended to ONLY modify this namespace, otherwise further MC Engine updates may break your code.
 *<br>To assist, the <MC.Sprite.dev.update(param)</tt> method is automatically called after <tt>MC.Sprite.update()</tt>
 *@example
 *new MC.Sprite({
 *	type :"circ",
 *	size1 : 20,
 *	edgeColor : MC.utils.randomColor(),
 *	edgeWidth : 2,
 *	wrapAround : true,
 *	pos : new MC.Point(MC.maths.randBetween(0,MC.canvas.right),MC.maths.randBetween(0,MC.canvas.bottom)),
 *	fillColor : MC.utils.randomColor(),
 *	vel : new MC.Point(MC.maths.randBetween(-100,100),MC.maths.randBetween(-100,100))
 *	});
 *
 *@class
 *@namespace MC.Sprite
 *@constructor
 *@this {MC.Sprite}
 *@param {Object} [type="circ"] MUST be in format of {type:"circ"} or other type i.e. "poly", "star", "rect", "anim", "pict" or "free"
 *@property {string} type="circ" Must be either "circ", "poly", "star", "rect", "anim", "pict" or "free"
 *@property {Number} size1=0 For "circ" = radius, "poly" & "star" = outer radius, all other types = width
 *@property {Number} size2=0 For "star" = inner radius, "rect", "anim" & "pict" = height
 *@property {Number} sides=0 Number of sides for a "poly" or points for a "star"
 *@property {Number} scale=1 Scale modifier applied to the base <tt>size1</tt> & <tt>size2</tt> properties
 *@property {Object} dev A Place holder Object for Developer customisation.
 *<br>Initialised with placeholder functions for
 *<br>(1)  <tt>dev.update()</tt> (automatically called at the end of any <tt>MC.Sprite.update()</tt> call) and
 *<br>(2)  <tt>dev.onKill()</tt> (automatically called at the end of the <tt>MC.Sprite.kill()</tt> call)
 *<br>CAUTION: It is highly recommended that any Developer ONLY use this namespace to customise Sprites (and their behaviour).
 *@property {Array.<MC.Point>} pointsArray Used internally by the MC Engine
 *@property {Object} boundingBox Used internally by the MC Engine
 *@property {Number} innerRadius Radius of the largest circle which can be contained within the Sprite (centred on it's position)
 *@property {Object} hitBox Used internally by the MC Engine
 *@property {MC.Color|string} fillColor Will also accept a HTML5 colour name string, e.g. "Black" or "Salmon"
 *@property {MC.Color|string} edgeColor As above
 *@property {Number} edgeWidth=0 N.B. greater than 1 adds to the render size of the object, but not for purposes of hit calculations
 *@property {MC.Point} pos=Mid-Screen In-game canvas position of the CENTRE of the Sprite
 *@property {MC.Point} vel=0 In-game velocity of the Sprite (in pixels per second)
 *@property {MC.Point} acc=0 In-game Acceleration of the Sprite (in pixels per second per second)
 *@property {Number} frontAngle=0 Rotational offset to indicate the "forward" direction of the Sprite (particulary useful with Sprite Sheets)
 *@property {Number} angle=0 Current rotational angle of the sprite
 *@property {Number} angleVel=0 Angular Velocity of the Sprite (in degrees per second, +ve = clockwise)
 *@property {Boolean} gravity=False if True, Sprite will be accelerated by the value set in <tt>MC.game.gravity</tt>
 *@property {Boolean} followNose=False if True, Sprite will automatically rotate to match it's velocity vector
 *@property {Boolean} edgeBounce=False if True, Sprite will bounce off the edges of the game bounds (as defined <tt>MC.game.setBounds()</tt>)
 *<br>N.B.  Sprites edgeBouncing have their position retarded by any edge interpeneration
 *@property {Boolean} edgeKill=False if true, Sprites that leave the Canvas Bounds will call the kill() method.
 *<br>N.B. the kill () method only clears references to Sprites held in a MC.SpriteBin.
 *@property {Boolean} wrapAround=False if True, Sprite will leave the game bounds and return from the other side
 *@property {Boolean} mobile=True if False, Sprite will not move (or rotate) during <tt>update()</tt> - useful as a pause since other movement properties are maintained
 *@property {Boolean} collider=True if false, Sprite Bounding Box is not maintained and all hitTest()'s will be ignored.  Useful for background objects
 *<br>Set this to false when not required, as it also saves some computing overhead.
 *<br>WARNING:  must be <tt>True</tt> for <tt>wrapAround</tt> and <tt>edgeBounce</tt> to work.
 *@property {MC.Point} lastNormal During <tt>bounce()</tt> lastNormal is updated with the normal of the last collision surface
 *@property {MC.Point} lastIP As above; this vector reports the Interpenetration of a sprite after a Hit Detection. In the direction of the lastNormal.
 *@property {false|Object_reference} lastHit A reference to the last Sprite hit by this one.  Reset to <tt>false</tt> after a "failed" <tt>Hit()</tt> test
 *@property {Number} restitution=1 Value applied by default in any <tt>reflect()</tt> calls.  1 = perfectly elastic, 0 = all velocity killed on impact
 *@property {Boolean} dead=False "dead" sprites are flagged for destuction (and will not <tt>update()</tt>, or <tt>render()</tt>)
 *<br>CAUTION Sprites in SpriteBin's are set to null if they have been detected during an <tt>update()</tt> call.
 *<br>If manaually handling Sprites, Developer's need to find other ways to kill references to dead Sprites 
 *@property {Number} killAge=null Age at which the Sprite will die, i.e. toggle <tt>dead</tt> to <tt>true</tt>
 *<br>Age is incremented during <tt>update()</tt>, and not when </tt>MC.game.paused == true</tt>
 *@property {Number} age=0 In-game age of the Sprite (in Seconds) updated in <tt>update()</tt>
 *@returns {MC.Sprite} a new Object
*/

MC.Sprite = function (parameters) {   
    // circ,poly,star,rect,anim,pict,free
    this.type = "circ";
    this.size1 = 0;
    this.size2 = 0;
    this.sides = 0;
    this.scale = 1;
    this.dev = {update: function () {},
                onKill: function () {} };
    this.pointsArray = null;
    this.boundingBox = {t:0, b:0, r:0, l:0, pos: new _Point(0,0), angle: 0, scale: 1};
    this.hitBox = {type: "circ", scale: 1};
    this.innerRadius = 0;
    this.fillColor = new _Color();
    this.edgeColor = new _Color();
    this.edgeWidth = 0;
    this.pos = new _Point(_canvas.midX,_canvas.midY);
    this.vel = new _Point();
    //this.speedClamp = null;
    this.acc = new _Point();
    this.frontAngle = 0;   
    this.angle = 0;
    this.angleVel = 0;
    //this.angleVelClamp = null;
    this.gravity = false;
    this.followNose = false;
    this.edgeBounce = false;
    this.edgeKill = false;
    this.wrapAround = false;
    this.mobile = true;
    this.collider = true;
    this.lastNormal = new MC.Point();
    this.lastIP = new MC.Point();
    this.lastHit = false;
    //this.physics = true;
    //this.mass = 0;
    //this.density = 0;
    this.restitution = 1; 
    this.dead = false;
    this.killAge = null;
    //this.startTime = new Date().getTime();
    this.age = 0;
    //this.user1 = null;
    //this.user2 = null;
    //this.user3 = null;
    //this.tags = [];
    
    // N.B. special parameters for Animations and Pictures are added during setValues(below)
    this.setValues(parameters);

};
/**
 *<hr>
 *Enables Sprite Sheet animation for a <tt>type:"anim"</tt> Sprite
 *@param {Number} GridX The number of horizontal frames in the Sprite Sheet
 *@param {Number} GridY The number of vertical frames in the Sprite Sheet
 *@param {Number} StartX The X frame section for the first of the animation frames (first = 0, last = GridX -1)
 *@param {Number} StartY The Y frame section for the first of the animation frames (first = 0, last = GridY -1)
 *@param {Number} FinishX The X frame section for the last of the animation frames (first = 0, last = GridX -1)
 *@param {Number} FinishY The Y frame section for the last of the animation frames (first = 0, last = GridY -1)
 *@param {Number} SecPerFrame Time between frame transitions
 *@param {Number} [BaseRotation=0] Base Rotation to be applied to the Sprite sheet (to counter any art issues)
 *@returns {this} Enables method chaining
 */
MC.Sprite.prototype.setAnimation = function (GridX, GridY,StartX,StartY,FinishX,FinishY, SecPerFrame,BaseRotation) {
    this.animGrid.x = GridX;
    this.animGrid.y = GridY;
    this.animStart.x = this.animCurrent.x = StartX;
    this.animStart.y = this.animCurrent.y = StartY;
    this.animFinish.x = FinishX;
    this.animFinish.y = FinishY;
    this.animSecPerFrame = SecPerFrame;
    this.animBaseRotation = BaseRotation || 0;
    return this;
};

/**
 *<hr>
 *Clones the Sprite and returns a <tt>new</tt> copy.
 *N.B. the <tt>MC.Sprite.dev</tt> object may or may not be deep-copied, depending upon how it has been Developer customised
 *@returns {MC.Sprite} a <tt>new</tt> Sprite with the same propeties (EXCEPTION: age is set to 0)
 */
MC.Sprite.prototype.clone = function () {
  // DEVELOPER NOTE -  Extremely important to remember to augment this based upon future MC.Sprite property changes
  var ret = new _Sprite({type: this.type});
  ret.type = this.type;
  ret.size1 = this.size1;
  ret.size2 = this.size2;
  ret.sides = this.sides;
  ret.scale = this.scale;
  ret.dev = _utils.cloneWithAssign(this.dev);
  if (this.pointsArray == null) ret.pointsArray = null;
    else { var len = this.pointsArray.length;
           ret.pointsArray = new Array();
           for (var i = 0 ; i < len ; i++) ret.pointsArray[i] = this.pointsArray[i].clone();
           };
  //ret.boundingBox.t = this.boundingBox.t;
  //ret.boundingBox.b = this.boundingBox.b;
  //ret.boundingBox.r = this.boundingBox.r;
  //ret.boundingBox.l = this.boundingBox.l;
  //ret.boundingBox.pos.set(this.boundingBox.pos);
  //ret.boundingBox.angle = this.boundingBox.angle;
  //ret.boundingBox.scale = this.boundingBox.scale;
  ret.hitBox.type = this.hitBox.type;
  ret.hitBox.scale = this.hitBox.scale;
  ret.innerRadius = this.innerRadius;
  if (this.fillColor instanceof _Color) ret.fillColor = this.fillColor.clone();
  else {ret.fillColor = this.fillColor};
  if (this.edgeColor instanceof _Color) ret.edgeColor = this.edgeColor.clone();
  else {ret.edgeColor = this.edgeColor};
  ret.edgeWidth = this.edgeWidth;  
  ret.pos.set(this.pos);
  ret.vel.set(this.vel);
  ret.acc.set(this.acc);
  ret.frontAngle = this.frontAngle;
  ret.angle = this.angle;
  ret.angleVel = this.angleVel;
  ret.gravity = this.gravity;
  ret.followNose = this.followNose;
  ret.edgeBounce = this.edgeBounce;
  ret.edgeKill = this.edgeKill;
  ret.wrapAround = this.wrapAround;
  ret.mobile = this.mobile;
  ret.collider = this.collider;
  ret.lastNormal.set(this.lastNormal);
  ret.lastIP.set(this.lastIP);
  ret.restitution = this.restitution;
  ret.dead = this.dead;
  ret.killAge = this.killAge;
  ret.age = 0;
  
  if (this.type == "anim" || this.type == "pict") {
    ret.picture = new _Picture(this.picture.img);
    ret.loaded = this.loaded;
  }
  
  if (this.type == "anim") {
    ret.animGrid.set(this.animGrid);
    ret.animStart.set(this.animStart);
    ret.animFinish.set(this.animFinish);
    ret.animCurrent.set(this.animCurrent);
    ret.animSecPerFrame = this.animSecPerFrame;
    ret.animBaseRotation = this.animBaseRotation;
    ret.animTimer = this.animTimer;
  }
  
  ret.updateBounds();
  return ret;
  
};


/**
 *<hr>
 *Configures a Sprite by passing an object made of key:value pairs e.g. <tt>{size1 = 34, size2 = 10,fillColor = new MC.Color(125,230,10,1)}</tt>
 *<br> N.B. called automatically by the Constructor with any such dictionary used as a parameter
 *@throws Console warning if key is not present in the Sprite
 *@param {Object} values {Object of key:value pairs}
 *
 */
MC.Sprite.prototype.setValues = function (values) {    
    if (values === undefined || values === null) {
        return this;
    }
    for (var key in values) {
        var newValue = values[key];
        if (newValue === undefined) {
            console.log("WARNING: Value of "+ key+ " not set in Sprite.setValues()");
            continue;
        }
        var oldValue = this[key];
        if (oldValue === undefined) {
            console.log("WARNING: Property "+ newValue+ " does not exist in Sprite.setValues()");
            continue;
        }
        this[key] = newValue;
        
            // if it's an animation or picture ... best we add the necessary parameters
            if (this[key] === "anim" || this[key] === "pict") {
            this.picture = new _Picture();
            this.loaded = false;
        }
        
        if (this[key] === "anim") {
            // FOR ANIMATIONS ONLY
            this.animGrid = new _Point();
            this.animStart = new _Point();
            this.animFinish = new _Point();
            this.animCurrent = new _Point();
            this.animSecPerFrame = 0;
            this.animBaseRotation = 0;
            this.animTimer = 0;
        }
        // establish hitBox type and populate defaults
        // default
        this.hitBox.type = "rect";
        if (this[key] === "circ") this.hitBox.type = "circ";
        if (this[key] === "poly" || this[key] === "star") this.hitBox.type = "poly";
    }
    this.updateBounds();
    return this;
};

/**
 *<hr>
 *Kills the Sprite by setting it's <tt>dead</tt> property to <tt>true</tt>
 *<br>Additionally, calls the <tt>dev.onKill()</tt> method which can be customised by the Developer
 *<br>CAUTION: <tt>MC.SpriteBin</tt>'s handle setting of their contents to null if a <tt>dead</tt> Sprite is detected.  Destruction of manually managed Sprites is the responsibility of the Developer.
 *@param {Number} [delay=0] In seconds, if provided, sets the Sprite killAge, and once reached will recall this method (and therefore fire the <tt>dev.onKill()</tt> method)
 */
MC.Sprite.prototype.kill = function (delay) {
    if (delay === undefined || delay == 0) {
        this.dead = true;
        this.dev.onKill();
    }
    else {
        this.killAge = this.age + delay;
    }
};

/**
 *<hr>
 *Automatically updates the Sprite's position due to velocity, acceleration etc.
 *This method should be called within any game <tt>requestAnimationFrame()</tt> game loop, and within any required <MC.game.physicsUpdates</tt> loop
 *Once complete, this method calls the Developer customisable <tt>MC.Sprite.dev.update()</tt> method
 *@param {Number} [DeltaTime=MC.game.deltaTime] The Time increment (in Seconds) to be applied to the Sprite
 *<br>N.B. usually <tt>MC.game.deltaTime</tt> and applied within the GameLoop
 */
MC.Sprite.prototype.update = function (DeltaTime) {
    var DT = 0;
    if (DeltaTime !== undefined) {
        DT = DeltaTime / _game.physicsUpdates;
    }
    else {
        DT = _game.deltaTime / _game.physicsUpdates;
    }
    var A;
    this.age += DT;
    if (this.killAge !== null && this.age >= this.killAge) {
        this.kill(0);
    }
    // confirm required images are loaded (and process if not)
    if (this.type === "pict" || this.type === "anim") {
        if (this.picture.loaded && !this.loaded) {
            this.size1 = this.picture.width;
            this.size2 = this.picture.height;
            this.pointsArray = null;
            this.loaded = true;
        }
    }
    // advance and process Animation
    if (this.type === "anim" && this.loaded) {
        this.animTimer += DT;
        if (this.animTimer >= 1/this.animSecPerFrame) {
            // need to proceed Animation step
            if (this.animCurrent.equals(this.animFinish)) {
                this.animCurrent.set(this.animStart);
            }
            else {
                if (this.animCurrent.x < this.animGrid.x -1) {
                    this.animCurrent.x += 1;
                }
                else {
                    this.animCurrent.x = 0 ;
                    this.animCurrent.y += 1;
                }
            }
        this.animTimer %= 1/this.animSecPerFrame;
        }
    }
    
    if (this.mobile && !this.dead) {
        A = this.acc.clone();
        if (this.gravity) {
            A.add(_game.gravity.clone().div(_game.physicsUpdates));
        }
        this.vel.add(A.times(DT));
        this.pos.add(this.vel.x * DT, this.vel.y * DT);
        this.angle += this.angleVel * DT;
        if (this.followNose === true && (this.vel.x !== 0 || this.vel.y !== 0)) {
            this.angle = this.vel.getAngle();
        }
        this.angle = _utils.fixAngle(this.angle);
        


        if (this.edgeBounce === true) {
            var bounced = false;
            if (this.boundingBox.t < _canvas.top && this.vel.y < 0) {
                this.vel.times(1,-1);
                this.pos.y += this.boundingBox.t - _canvas.top
                bounced = true;
            }
            if (this.boundingBox.b > _canvas.bottom && this.vel.y > 0) {
                this.vel.times(1,-1);
                this.pos.y += _canvas.bottom - this.boundingBox.b;
                bounced = true;
            }
            if (this.boundingBox.l < _canvas.left && this.vel.x < 0) {
                this.vel.times(-1,1);
                this.pos.x += _canvas.left - this.boundingBox.l;
                bounced = true;
            }
            if (this.boundingBox.r > _canvas.right && this.vel.x > 0) {
                this.vel.times(-1,1);
                this.pos.x -= this.boundingBox.r - _canvas.right;
                bounced = true;
            }
            if (bounced === true && this.followNose === true) {
                this.angle = this.vel.getAngle() + this.frontAngle;
            }
        }

        if (this.wrapAround === true) {
            var t = this.boundingBox.t;
            var b = this.boundingBox.b;
            var l = this.boundingBox.l;
            var r = this.boundingBox.r;
            if (t > _canvas.bottom && this.vel.y > 0) {
                this.pos.y = _canvas.top - (b - t)/2;
            }
            if (b < _canvas.top && this.vel.y < 0) {
                this.pos.y = _canvas.bottom + (b - t)/2;
            }
            if (r < _canvas.left && this.vel.x < 0) {
                this.pos.x = _canvas.right + (r - l)/2;
            }
            if (l > _canvas.right && this.vel.x > 0) {
                this.pos.x = _canvas.left - (r - l)/2;
            }
        }
        
        if (this.edgeKill === true) {
            if (this.boundingBox.t > _canvas.bottom ||
                this.boundingBox.b < _canvas.top ||
                this.boundingBox.r < _canvas.left ||
                this.boundingBox.l > _canvas.right) {
                this.kill();
            }
        }
        
        this.updateBounds();
    }
    // IMPORTANT - Herein lies the Developers avenue for customisation
    this.dev.update();
    
};

/**
 *<hr>
 *Renders the Sprite to the canvas (as defined by <tt>MC.game.canvas</tt>)
 *<br>Usually called at the end of the game loop (after <tt>update()</tt>)
 *@returns {this} Enables method chaining
 */
MC.Sprite.prototype.render = function () {
    if (this.type === "circ") {
        _draw.circle(this.pos,(this.size1 * this.scale),this.fillColor,this.edgeColor,this.edgeWidth);
    }
    if (this.type === "poly" || this.type === "star" || this.type === "rect" || this.type === "free") {
        _draw.array(this.pos,this.pointsArray,this.fillColor,this.edgeColor,this.edgeWidth);
    }
    if (this.type === "pict") {        
        this.picture.render(this.pos,this.angle+this.frontAngle,this.size1 * this.scale,this.size2 * this.scale);
    }
    if (this.type === "anim") {
        var dx = (this.picture.width / this.animGrid.x) * this.scale;
        var dy = (this.picture.height / this.animGrid.y) * this.scale;
        this.picture.render(this.pos,this.angle+this.frontAngle+this.animBaseRotation,
                            dx,dy,
                            new _Point(this.animCurrent.x/this.animGrid.x,this.animCurrent.y/this.animGrid.y),
                            new _Point((this.animCurrent.x + 1)/this.animGrid.x,(this.animCurrent.y + 1)/this.animGrid.y)
                           );
    }
    return this;
};

/**
 *<hr>
 *Refreshes the internally held bounding box coordinates
 *<br>This is called automatically upon <tt>update()</tt> and Developers should not need to call it
 *<br>However: Sprites that are manually moved (by direct manipulation of the <tt>pos</tt> or <tt>angle</tt> properties), or flagged as NOT <tt>mobile</tt> may need it
 */
MC.Sprite.prototype.updateBounds = function () {
    // Non-colliders do not require Bounds
    if (!this.collider) {
        return;
    }
    var DAngle, DScale, len = 0; // For "free" points Array handling

    function setFlags(o) { // Internal function, no need for users to know.  Sets boundingBox flags re angle, pos and scale
        o.boundingBox.angle = o.angle;
        o.boundingBox.pos.set(o.pos);
        o.boundingBox.scale = o.scale;
        return;
    }
    
    function findEdges(o) { // Internal function, loops through pointsArray and populates boundingBox t,b,l & r
        if (this.pointsArray !== null) {
            len = o.pointsArray.length;
            var t = o.pointsArray[0].y;
            var b = o.pointsArray[0].y;
            var l = o.pointsArray[0].x;
            var r = o.pointsArray[0].x;
            for (var i = 1; i < len ; i = i+1) {
                t = _maths.minOf(o.pointsArray[i].y, t);
                b = _maths.maxOf(o.pointsArray[i].y, b);
                l = _maths.minOf(o.pointsArray[i].x, l);
                r = _maths.maxOf(o.pointsArray[i].x, r);
            }
            o.boundingBox.t = t + o.pos.y;
            o.boundingBox.b = b + o.pos.y;
            o.boundingBox.l = l + o.pos.x;
            o.boundingBox.r = r + o.pos.x;
        }
        return;
    }
    
    if (!this.dead) {
        
        if (this.type === "circ") { // get circles out of the way
                this.boundingBox.t = this.pos.y - (this.size1 * this.scale);
                this.boundingBox.b = this.pos.y + (this.size1 * this.scale);
                this.boundingBox.l = this.pos.x - (this.size1 * this.scale);
                this.boundingBox.r = this.pos.x + (this.size1 * this.scale);
                setFlags(this);
                this.innerRadius = this.getInnerRadius();
            return; // no more circles in this function.  Only pointArrays
        }
        
        if (this.type === "poly") {
            this.pointsArray = _utils.getPolyArray(this.sides,this.size1 * this.scale,this.angle + this.frontAngle);
        }
        else if (this.type === "star") {
            this.pointsArray = _utils.getStarArray(this.sides,this.size1* this.scale,this.size2* this.scale,this.angle + this.frontAngle);
        }
        else if (this.type === "rect" || this.type === "pict") {
            this.pointsArray = _utils.getRectArray(this.size1 * this.scale, this.size2 * this.scale,this.angle + this.frontAngle);
        }
        else if (this.type === "anim") {
            this.pointsArray = _utils.getRectArray((this.size1 * this.scale)/this.animGrid.x, (this.size2 * this.scale)/this.animGrid.y,this.angle + this.frontAngle + this.animBaseRotation);
        }
        else if (this.type === "free") { // the expensive one !!
            if (this.boundingBox.angle !== this.angle || this.scale !== this.boundingBox.scale) {
                DAngle = this.boundingBox.angle - this.angle;
                DScale = this.scale / this.boundingBox.scale;
                len = this.pointsArray.length;
                for (var i = 0; i < len; i++) {
                    this.pointsArray[i].rotate(DAngle).scale(DScale);
                }
            }  
        }
        setFlags(this);
        findEdges(this);
        this.innerRadius = this.getInnerRadius();
        return;
    }
};

/**
 *<hr>
 *Draws a 1x pixel outline showing the sprite's Bounding Box
 *<br> Useful for debugging purposes
 *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color (White is default)
 *@param {Color|string} [color="White"] - Desired outline color (1)
 *@returns {this} Enabled method chaining
 */
MC.Sprite.prototype.drawBB = function(color = "White") {
    
    MC.draw.polyLine( [new _Point(this.boundingBox.l,this.boundingBox.t),
                       new _Point(this.boundingBox.r,this.boundingBox.t),
                       new _Point(this.boundingBox.r,this.boundingBox.b),
                       new _Point(this.boundingBox.l,this.boundingBox.b),
                       new _Point(this.boundingBox.l,this.boundingBox.t)], _utils.colorOrString(color),1);
    return this;
};

/**
 *<hr>
 *Draws a 1x pixel circle representing the Sprite's inner Circle.
 *<br>Useful for debugging purposes
 *<br> 1 - either an MC.Color object or an HTML5 color name can be used as a Color (White is default)
 *@param {Color|string} [color="White"] - Desired outline color (1)
 *@returns {this} Enabled method chaining
 */
MC.Sprite.prototype.drawIC = function(color = "White") {
    MC.draw.circle(this.pos,this.innerRadius,null,_utils.colorOrString(color),2);
    return this;
 };


/**
 *<hr>
 *Reflects the sprite from a surface normal, resulting in a new direction of motion (and possible reduction of speed)
 *<br>N.B currently the MC Engine does not support transfer of momentum (rotational or otherwise) between objects, so speed and rotation angle are preserved
 *Used during the Sprite<tt> bounce()</tt> method calls
 *@param {MC.Point} normal Normal of the surface impacted.
 *@param {Number} [restitution=this.restitution] Used if the collision requires a restitution other than the Sprite's own.
 *@returns {this} Enables method chaining
 */
MC.Sprite.prototype.reflect = function (normal,restitution)
{
    var V = this.vel.clone();
    var N = new _Point();
    if (normal !== undefined)
        N.set(normal).normalise();
    else {
        N.set(this.lastNormal).normalise() }
    N.times(2 * _maths.dotProd(V,N));
    V.minus(N);
    var R = this.restitution;
    if (restitution !== undefined) {
        R = restitution;
    }
    this.vel = V.times(R);
    return this;
};

/**
 *<hr>
 *Checks is a Sprite is moving towards a particular postion
 *@param {MC.Point|MC.Sprite} target Screen location or Sprite sought
 *@returns {Boolean} True is the Sprite's velocity will take it closer to the target Location
 */
MC.Sprite.prototype.towards = function (target) {
    var t = new MC.Point();
    if (target instanceof MC.Sprite) {
        t.set(target.pos).minus(this.pos);
    }
    else {
        t.set(target).minus(this.pos);
    }
    var a = this.vel.angleTo(t);
    if (a < 90 || a > 270) {
        return true;
    }
    return false;
};

/**
 *<hr>
 *Moves the Sprite's position by the desired amount
 *<br>This is the prefered method to translate a Sprite, as it's bounding box is automatically updated.
 *@param {MC.Point} Vector Translation required
 *@returns {this} Enables Method Chaining
 */
MC.Sprite.prototype.moveBy = function(Vector) {
    if (_utils.hasXY(Vector)) {
        this.pos.add(Vector);
        this.updateBounds();
    }
    return this; 
};

/**
 *<hr>
 *Moves the Sprite's position to the desired place
 *<br>This is the prefered method to move a Sprite, as it's bounding box is automatically updated.
 *@param {MC.Point} Position where the new Sprite centre will be
 *@returns {this} Enables Method Chaining
 */
MC.Sprite.prototype.moveTo = function(Position) {
    if (_utils.hasXY(Position)) {
        this.pos.set(Position);
        this.updateBounds();
    }
    return this; 
};

/**
 *<hr>
 *Rotates the Sprite by the desired amount
 *<br>This is the prefered method to rotate a Sprite, as it's bounding box is automatically updated.
 *@param {angle} Angle Rotation required (in degrees, +ve = clockwise)
 *@returns {this} Enables Method Chaining
 */
MC.Sprite.prototype.rotateBy = function(angle) {
    if (_utils.isNumeric(angle)) {
        this.angle += angle;
        this.updateBounds();
    }
    return this; 
};


/**
 *<hr>
 *Retards the Sprite's position by the last recorded <tt>lastIP</tt> value.
 *<br>The <tt>lastIP</tt> is then reset to zero.
 *@returns {this} Enables method chaining
 */
MC.Sprite.prototype.retard = function() {
    this.pos.add(this.lastIP);
    this.lastIP.set(0,0);
    return this;
};

/**
 *<hr>
 *Resets the Sprite's <tt>lastNormal</tt> and <tt>lastIP</tt> properties
 *<br>Called automatically after a failed <tt>hitTest()</tt>
 *@returns {this} Enables method chaining
 */
MC.Sprite.prototype.resetHit = function () {
    this.lastIP.set(0,0);
    this.lastNormal.set(0,0);
    this.lastHit = false;
    return this;
};

/**
 *<hr>
 *detects whether a Sprite has hit another and, if so, reflects off it
 *<br>N.B. This is an approximation using the velocity of the calling Sprite.
 *<br>The Sprite's <tt>lastIP, lastNormal and lastHit</tt> properties are reset at the beginning of this call.
 *<br>So Developers can use <tt>if (mySprite.lastHit) { // do something} </tt> to detect a bounce during the update cycle
 *<br>Rotating targets may lead to irregular results.
 *<br>Velocity will be reduced based upon the calling sprite's Restitution value.  Otherwise speed is maintained.
 *<br>For simplicity Sprite.type "star" and "poly" are treated as circles, and there is no transfer of momentum/speed between objects.
 *<br>The following collision types are supported
 *<br>... Circle on Circle
 *<br>... Circle on rotated Rectangle (and vice versa)
 *<br>... Axis Aligned Rectangles
 *<br>Non-axis aligned rectangles will "bounce" but use their (and/or target's) outer bounding boxes
 *@param {MC.Sprite|MC.SpriteBin} target The Sprite to be tested against, if a SpriteBin is specified, then the <tt>bounce()</tt> method is called against all the contents
 *@returns {this} Enables Method Chaining
 *@throws Console warning if target is undefined or otherwise not valid
 *<br>.
 */
MC.Sprite.prototype.bounce = function (target) {
    this.resetHit();
    var hit = false;
    if (target === undefined) {
        console.log("WARNING.  No target passed to hitTest():  self returned");
        return this;
    }
    if (target instanceof MC.Sprite) {
        b(this,target);
    }
    else if (target instanceof MC.SpriteBin) {
        var len = target.bin.length;
        for (var i = 0; i < len ; i++) b(this,target.bin[i]);
    }

    function b(me,t) {
        
        if (!_utils.validHT(t)) {
            console.log("WARNING.  Invalid target passed to hitTest():  self returned");
            return this;
        }
        // Quick Bounding Box Check
        if (!_utils.checkBB(me.boundingBox,t.boundingBox)) {
            return this;
        }
        // Circle on Circle (types)
        if ((me.type == "circ" || me.type == "poly" || me.type == "star") && (t.type == "circ" || t.type == "poly" || t.type == "star") ) {
            if(me.hitTestCircOnCirc(t)) hit = true;
        }
        // Circle on Rectangle(s)
        else if ((me.type == "circ" || me.type == "poly" || me.type == "star") && (t.type == "anim" || t.type == "rect" || t.type == "pict")) {
            if(me.hitTestCircOnRect(t)) hit = true;
        }
        // Rect on Circ
        else if ((t.type == "circ" || t.type == "poly" || t.type == "star") && (me.type == "anim" || me.type == "rect" || me.type == "pict")) {
            if(me.hitTestCircOnRect(t)) hit = true;
        }
        else if ((t.type == "anim" || t.type == "rect") && (me.type == "anim" || me.type == "rect" || me.type == "pict")) {
            if(me.AARectHit(t)) hit = true;
        }
        if (hit) {
            me.lastHit = t;
            me.moveBy(me.lastIP);
            me.reflect();
        }
    }    
    return this;    
};

// Internal Function
MC.Sprite.prototype.hitTestCircOnCirc = function (sprite) {
    var s1,s2;
    if (this.type === "star") {
        s1 = _maths.maxOf(this.size1,this.size2);
    }
    else { s1 = this.size1;}
    if (sprite.type === "star") {
        s2 = _maths.maxOf(sprite.size1,sprite.size2);
    }
    else { s2 = sprite.size1;}
    
    var R = (s1 * this.scale) + (s2 * sprite.scale);
    var RSqr = R*R;
    // no hit
    if (RSqr < _maths.rangeSqr(this.pos,sprite.pos)) {
        this.resetHit();
        return false;
    }
    // hit
    else {
        var norm = sprite.pos.clone().minus(this.pos).normalise()
        this.lastNormal = norm;
        var D = R - _maths.range(this.pos,sprite.pos);
        this.lastIP = norm.invert().times(D);
        this.lastHit = sprite;
        return true;
    }
};

// Internal Function
MC.Sprite.prototype.hitTestCircOnRect = function (sprite) {
    // establish what is the circle, and what is the rectangle
    var rect, circ;
    var swap = false;
    if (this.type == "circ") {
        circ = this;
        rect = sprite;
    }
    else {
        circ = sprite;
        rect = this;
        swap = true;
    }
    
    // rectangle variables
    var rc = rect.pos.clone();                // rectangle centre   
    var rw = rect.size1 * rect.scale / 2;   // rect width (halved)
    var rh = rect.size2 * rect.scale / 2;   // rect height (halved)
    var rr = rect.angle + rect.frontAngle;  // rectangle rotation
    // divide for Spritesheet elements
    if (rect.type == "anim") {
            rw /= rect.animGrid.x;
            rh /= rect.animGrid.y;
            rr += rect.animBaseRotation;
        }  
    // circle variables    
    var cc = circ.pos.clone();  // circle centre
    var cr = circ.size1 * circ.scale;           // circle radius
    var cv = circ.vel.clone();      // circle velocity
    if (swap) {
        cv = rect.vel.clone().invert(); // or (depending on case ... the Rectangle velocity)
    }
    
    // set circle centre, so Rectangle Origin is @ (0,0)
    cc.minus(rc);
    // Rotate Circle Centre and Velocity to make Rectangle Axis-aligned
    cc.rotate(-rr);
    cv.rotate(-rr);
    
    // Now have to establish what quadrent the Circle centre is, and rotate by 0, -90, 90 or 180 
    // So brings the circle centre into the +x/+y quadrent N.B. rectangle height and width will be swapped from -90 and 90 degree cases
    var MasterR = 0;
    if (cc.x <= 0 && cc.y >= 0) {
       MasterR -= 90;
       var tmp = rw; rw = rh; rh = tmp;}
    else if (cc.x >= 0 && cc.y <= 0) {
        MasterR += 90;
       var tmp = rw; rw = rh; rh = tmp;}
    else if (cc.x <= 0 && cc.y <= 0) {
        MasterR += 180;}
    
    // rotate cc and cv around origin
    cc.rotate(MasterR);
    cv.rotate(MasterR);
    /*
     * (0,0)------------------------------------
     *  |                        |
     *  |                        |
     *  |                        | cl (cc1 LEFT)
     *  |                        |
     *  |                        |
     *---------------------------(rw,rh)
     *  |             ct             (cc3 CORNER)     
     *  |          (cc2 TOP)
     */
    // Sanity preserving variables
    var cx = cc.x; var cy = cc.y; var ct = cy - cr; var cl = cx - cr;
    // Quick Escape check
    if (ct >= rh  || cl >= rw) {
        this.resetHit();
       return false;
    }
    var Norm = new _Point();
    var IP = new _Point();
    
    // cc1 Left of Circle (not corner)
    if (cl <= rw && cy <= rh && cv.x <= 0) {
        this.resetHit();
        Norm.set(1,0).rotate(rr - MasterR);
        IP.set(Norm).times(rw-cl);
        if (swap) {
            Norm.invert();
            IP.invert();
        }
        this.lastNormal = Norm;
        this.lastIP = IP;
        this.lastHit = sprite;
        return true;
    }
    
    // cc2 Top of Circle (not corner)
    if (ct <= rh && cx <= rw && cv.y <= 0) {
        this.resetHit();
        Norm.set(0,1).rotate(rr - MasterR);
        IP.set(Norm).times(rh-ct);
        if (swap) {
            Norm.invert();
            IP.invert();
        }
        this.lastNormal = Norm;
        this.lastIP = IP;
        this.lastHit = sprite;
        return true;
    }

    // cc3 (corner)
    if (cx >= rw && cy >= rh) {
        // inside radius?
        var R = _maths.range(cx,cy,rw,rh);
        if (R >= cr || (cv.x > 0 && cv.y > 0)) {
            this.resetHit();
            return false;
        }
        else {
            Norm = cc.clone().minus(rw,rh).rotate(rr - MasterR).normalise();
            IP = this.lastNormal.clone().times(cr - R);
            if (swap) {
                Norm.invert();
                IP.invert();
            }
            this.lastNormal = Norm;
            this.lastIP = IP;
            this.lastHit = sprite;
            return true;
        }   
    }
    // Just in case
   this.resetHit();
   return false;
};

// internal function
MC.Sprite.prototype.AARectHit = function (sprite) {
    
    var bb1 = this.boundingBox;
    var vel = this.vel.clone();
    var bb2 = sprite.boundingBox;
    
    var penx = 0;
    var peny = 0;
    
    if (vel.x > 0 && bb1.r > bb2.l) { // going right
        penx = bb2.l - bb1.r;
    }
    if (vel.x < 0 && bb1.l < bb2.r) { // going left
        penx = bb2.r - bb1.l;
    }
    if (vel.y > 0 && bb1.b > bb2.t) { // going down
        peny = bb2.t - bb1.b;
    }
    if (vel.y < 0 && bb1.t < bb2.b) { // going up
        peny = bb2.b - bb1.t;
    }
    if (Math.abs(penx) < Math.abs(peny)) {
        // using X
        this.lastIP.set(penx,0);
        this.lastNormal.set(this.lastIP).normalise();
        this.lastHit = sprite;
        return true;
    }
    else {
        this.lastIP.set(0,peny);
        this.lastNormal.set(this.lastIP).normalise();
        this.lastHit = sprite;
        return true;
    }   
    this.resetHit();
    return false;
};

// Internal Function BUGGED DO NOT USE
MC.Sprite.prototype.hitTestRectOnRect = function (sprite) {
    var swap = false;
    var R1 = this;
    var R2 = sprite;
    var norm = new MC.Point(0,0);
    var IP = new MC.Point(0,0);
    
    if(InBox(R2,R1,1)) {R1.lastNormal = norm; R1.lastIP = IP; R1.vel.set(0,0);};
    
    function InBox(T1,T2,o) {
    
    // Calling Rectangle
    var r1c = T1.pos.clone();         // postion
    var r1v = T1.vel.clone();         // velocity
    if (0 == 2) {
        r1v.set(TS.vel);
    }
    var r1w = T1.size1 * T1.scale/2;  // width
    var r1h = T1.size2 * T1.scale/2;  // height
    var r1a = T1.angle + T1.frontAngle; // angle
    if (T1.type === "anim") {           // if animated
        r1w /= T1.animGrid.x;
        r1h /= T1.animGrid.y;
        r1a += T1.animBaseRotation;
    }
    // Target Rectangle
    var r2c = T2.pos.clone();
    var r2w = T2.size1 * T2.scale/2;
    var r2h = T2.size2 * T2.scale/2;
    var r2a = T2.angle + T2.frontAngle;
    if (T2.type === "anim") {
        r2w /= T2.animGrid.x;
        r2h /= T2.animGrid.y;
        r2a += T2.animBaseRotation;
    }
    
    // Place parent (r1) at Origin, rotate to 0 
    r2c.minus(r1c).rotate(-r1a);
    
    // establish secondary rotation to bring sprite to x+,y+
    var MasterR = 0;
    if (r2c.x <= 0 && r2c.y >= 0) {
       MasterR -= 90;
       var tmp = r1w; r1w = r1h; r1h = tmp;}
    else if (r2c.x >= 0 && r2c.y <= 0) {
        MasterR += 90;
       var tmp = r1w; r1w = r1h; r1h = tmp;}
    else if (r2c.x <= 0 && r2c.y <= 0) {
        MasterR += 180;}
        
    // rotate centre again and set velocity
    r2c.rotate(MasterR);
    // make original sprite velocity the relative velocity of the Target
    r1v.rotate(-r1a + MasterR);
    if (o == 2) {
        r1v.invert();
    }
   
   // quick get out if moving away
   if (r1v.x > 0 && r1v.y > 0) {
        return false;
   }
   
    // Array of target Sprite Corners
    var C2 = new Array();
    C2[0] = new MC.Point(r2w,r2h).rotate(r2a+MasterR).add(r2c);
    C2[1] = new MC.Point(-r2w,r2h).rotate(r2a+MasterR).add(r2c);
    C2[2] = new MC.Point(-r2w,-r2h).rotate(r2a+MasterR).add(r2c);
    C2[3] = new MC.Point(r2w,-r2h).rotate(r2a+MasterR).add(r2c);

    // pot luck method ...  returns first corner gets there wins
    for (var i = 0; i < 4 ; i++)
    {
        if (C2[i].x > r1w || C2[i].x < 0 || C2[i].y > r1h || C2[i].y < 0 ) continue; // if either x,y > r1w,r1h .. no hit
        // X goes first
        if (r1v.x < 0) {
            norm.set(-1,0).rotate(r1a - MasterR);
            IP = norm.clone().times(r1w - C2[i].x);
            return true;
        }
        if (r1v.y < 0) {
            norm.set(0,-1).rotate(r1a - r2a - MasterR);
            IP = norm.clone().times(r1h - C2[i].y);
            return true;        
        }
    }
    return false;
    } 
};

/**
 *<hr>
 *Returns an array of the screen coordinates of the vertices of a non-circular sprite
 *@returns {Array.<MC.Point>} Screen coordinates of the Sprite's vertices
 */
MC.Sprite.prototype.getPointsArray = function () {
    var ret = new Array();
    var len = this.pointsArray.length;
    for (var i = 0; i < len ; i++) {
        ret[i] = this.pointsArray[i].clone().add(this.pos);
    }
    return ret;
};
/**
 *<hr>
 *Returns the inner Radius of the Sprite.  Anything inside this radius is 100% certain to be be inside the Sprite
 *<br>CAUTION: "free" type sprites can have interesting results.
 *<br> Used internally by the MC engine to populate the sprite's <tt>innerRadius</tt> property
 *@returns {Number} The Sprite's inner radius
 */
MC.Sprite.prototype.getInnerRadius = function () {
    if (this.type == "circ") return this.size1 * this.scale;
    else if (this.type == "rect" || this.type == "pict") return _maths.minOf(this.size1,this.size2) * (this.scale / 2);
    else if (this.type == "star") return _maths.minOf(this.size1,this.size2) * this.scale;
    else if (this.type == "poly") return (this.size1 * this.scale) * Math.cos(Math.PI / this.sides);
    else if (this.type == "anim") {
          if (this.loaded) {
            return _maths.minOf(this.size1 / this.animGrid.x,this.size2/this.animGrid.y) * (this.scale / 2);
          }
          else {
            return 1;
          }
        }
    else {
        var o = new _Point(0,0);
        var d = _maths.rangeSqr(o,this.pointsArray[0]);
        var len = this.pointsArray.length;
        for (var i = 1; i < len; i++) {
            d = _maths.minOf(d,_maths.rangeSqr(o,this.pointsArray[i]));
            }
        return Math.sqrt(d);
        }
};

// Internal Function
MC.Sprite.prototype.hitSprite = function(target) {
    
        // Auto reset
        this.resetHit();
        
        // Bounding Boxes check
        if (!_utils.checkBB(this.boundingBox,target.boundingBox)) {
            return false;
        }
        // Inner Circle check
        if (_utils.circInCircle(this.pos,this.innerRadius,target.pos,target.innerRadius)) {
            this.lastHit = target;
            return true;
        }
        // eliminate any circles
        if (this.type == "circ") {
            if (target.type == "circ") {
                return false; // has been determined by the Inner Circle check
            }
            else if (target.type == "rect" || target.type == "anim" || target.type == "pict") {
                var res = this.hitTestCircOnRect(target);
                if (res) {
                    this.lastHit = target;
                    return true;
                }
                this.resetHit();
                return false;
            }
            else {
                if (_utils.shapeInCircle(target.getPointsArray(),this.pos,this.innerRadius)) {
                    this.lastHit = target;
                    return true;
                }
                else {
                    return false;
                }
            }
        }
        
        if (target.type == "circ") {
            if (this.type == "rect" || this.type == "rect" || this.type == "pict") {
                var res = target.hitTestCircOnRect(this);
                if (res) {
                    this.lastHit = target;
                    return true;
                }
                return false;
            }
            else {
                if (_utils.shapeInCircle(this.getPointsArray(),target.pos,target.innerRadius)) {
                    this.lastHit = target;
                    return true;
                }
                else {
                    return false; 
                }
            }
        }
        //straight shape on shape now
        if (_utils.shapeInShape(this.getPointsArray(),target.getPointsArray())) {
                this.lastHit = target;
                return true;
        }
        else {
                return true;
        }
};
/**
 *<hr>
 *Checks if a Sprite has "hit" (i.e. intersects with) something
 *@param {MC.Point|MC.Sprite|MC.SpriteBin} target The something to check intersection with
 *<br>N.B. it is very common for the "Point" option to be the Mouse Coordinates, e.g. <tt>MySprite.hit(MC.game.mouse)</tt>
 *<br>N.B. Sprites with <tt>collider</tt> property set to </tt>false</tt> will always retuns a <tt>false</t>>
 *@returns {Boolean} <tt>true</tt> if the point or any sprite (singular or in the SpriteBin) intersects the calling Sprite, else <tt>false</tt>
 */
MC.Sprite.prototype.hit = function (target) {
    if (this.collider == false) {
        return false;
    }
    if (target instanceof MC.Point) {
        // Bounding Box check
        if (!_utils.checkBB(this.boundingBox,target)) {
            return false;
        }
        if (this.type == "circ") {
            return _utils.pointInCirc(target,this.pos,this.size1* this.scale);
        }
        // innerRadius Check
        if (_utils.pointInCirc(target, this.pos,this.innerRadius)) {
            return true;
        }
        else if (this.type == "rect" || this.type == "anim" || this.type == "pict") {
            return _utils.pointInRect(target,this.getPointsArray());
        }
        else {
            return _utils.pointInShape(target,this.getPointsArray());
        }
    }
    if (target instanceof MC.Sprite) {
        return this.hitSprite(target);
    }
    
    if (target instanceof MC.SpriteBin) {
        var len = target.bin.length;
        var res = false;
        for (var i = 0; i < len; i++) {
            res = false;
            if (target.bin[i] == null || target.bin[i] == undefined) {
                continue;
            }
            if (target.bin[i] instanceof MC.Sprite) {
                if (!target.bin[i].dead && target.bin[i].collider) {
                    res = this.hitSprite(target.bin[i]);
                }
            }
            if (res) {
                return true;
            }
        }
        return false;
    }
    
};

// End of File .. Nothing further to see here.  Thanks for coming 8-)