package flash.display {
  import flash.events.EventDispatcher;
  import flash.display.IBitmapDrawable;
  import String;
  import flash.accessibility.AccessibilityProperties;
  import flash.geom.Point;
  import Object;
  import flash.display.LoaderInfo;
  import flash.geom.Rectangle;
  import Array;
  import flash.display.Shader;
  import Boolean;
  import flash.geom.Vector3D;
  import Number;
  import flash.display.DisplayObjectContainer;
  import flash.display.Stage;
  import flash.geom.Transform;
  import flash.events.Event;
  public class DisplayObject extends EventDispatcher implements IBitmapDrawable {
    public function DisplayObject() {}
    public native function get root():DisplayObject;
    public native function get stage():Stage;
    public native function get name():String;
    public native function set name(value:String):void;
    public native function get parent():DisplayObjectContainer;
    public native function get mask():DisplayObject;
    public native function set mask(value:DisplayObject):void;
    public native function get visible():Boolean;
    public native function set visible(value:Boolean):void;
    public native function get x():Number;
    public native function set x(value:Number):void;
    public native function get y():Number;
    public native function set y(value:Number):void;
    public native function get z():Number;
    public native function set z(value:Number):void;
    public native function get scaleX():Number;
    public native function set scaleX(value:Number):void;
    public native function get scaleY():Number;
    public native function set scaleY(value:Number):void;
    public native function get scaleZ():Number;
    public native function set scaleZ(value:Number):void;
    public native function get mouseX():Number;
    public native function get mouseY():Number;
    public native function get rotation():Number;
    public native function set rotation(value:Number):void;
    public native function get rotationX():Number;
    public native function set rotationX(value:Number):void;
    public native function get rotationY():Number;
    public native function set rotationY(value:Number):void;
    public native function get rotationZ():Number;
    public native function set rotationZ(value:Number):void;
    public native function get alpha():Number;
    public native function set alpha(value:Number):void;
    public native function get width():Number;
    public native function set width(value:Number):void;
    public native function get height():Number;
    public native function set height(value:Number):void;
    public native function get cacheAsBitmap():Boolean;
    public native function set cacheAsBitmap(value:Boolean):void;
    public native function get opaqueBackground():Object;
    public native function set opaqueBackground(value:Object):void;
    public native function get scrollRect():Rectangle;
    public native function set scrollRect(value:Rectangle):void;
    public native function get filters():Array;
    public native function set filters(value:Array):void;
    public native function get blendMode():String;
    public native function set blendMode(value:String):void;
    public native function get transform():Transform;
    public native function set transform(value:Transform):void;
    public native function get scale9Grid():Rectangle;
    public native function set scale9Grid(innerRectangle:Rectangle):void;
    public native function globalToLocal(point:Point):Point;
    public native function localToGlobal(point:Point):Point;
    public native function getBounds(targetCoordinateSpace:DisplayObject):Rectangle;
    public native function getRect(targetCoordinateSpace:DisplayObject):Rectangle;
    public native function get loaderInfo():LoaderInfo;
    public function hitTestObject(obj:DisplayObject):Boolean { notImplemented("hitTestObject"); }
    public function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean = false):Boolean { notImplemented("hitTestPoint"); }
    public native function get accessibilityProperties():AccessibilityProperties;
    public native function set accessibilityProperties(value:AccessibilityProperties):void;
    public native function globalToLocal3D(point:Point):Vector3D;
    public native function local3DToGlobal(point3d:Vector3D):Point;
    public native function set blendShader(value:Shader):void;
  }
}

/*
 * Copyright 2014 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package flash.display {
import flash.accessibility.AccessibilityProperties;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Transform;
import flash.geom.Vector3D;

[native(cls='DisplayObjectClass')]
public class DisplayObject extends EventDispatcher implements IBitmapDrawable {
  public function DisplayObject() {}
  public native function get root():DisplayObject;
  public native function get stage():Stage;
  public native function get name():String;
  public native function set name(value:String):void;
  public native function get parent():DisplayObjectContainer;
  public native function get mask():DisplayObject;
  public native function set mask(value:DisplayObject):void;
  public native function get visible():Boolean;
  public native function set visible(value:Boolean):void;
  public native function get x():Number;
  public native function set x(value:Number):void;
  public native function get y():Number;
  public native function set y(value:Number):void;
  public native function get z():Number;
  public native function set z(value:Number):void;
  public native function get scaleX():Number;
  public native function set scaleX(value:Number):void;
  public native function get scaleY():Number;
  public native function set scaleY(value:Number):void;
  public native function get scaleZ():Number;
  public native function set scaleZ(value:Number):void;
  public native function get mouseX():Number;
  public native function get mouseY():Number;
  public native function get rotation():Number;
  public native function set rotation(value:Number):void;
  public native function get rotationX():Number;
  public native function set rotationX(value:Number):void;
  public native function get rotationY():Number;
  public native function set rotationY(value:Number):void;
  public native function get rotationZ():Number;
  public native function set rotationZ(value:Number):void;
  public native function get alpha():Number;
  public native function set alpha(value:Number):void;
  public native function get width():Number;
  public native function set width(value:Number):void;
  public native function get height():Number;
  public native function set height(value:Number):void;
  public native function get cacheAsBitmap():Boolean;
  public native function set cacheAsBitmap(value:Boolean):void;
  public native function get opaqueBackground():Object;
  public native function set opaqueBackground(value:Object):void;
  public native function get scrollRect():Rectangle;
  public native function set scrollRect(value:Rectangle):void;
  public native function get filters():Array;
  public native function set filters(value:Array):void;
  public native function get blendMode():String;
  public native function set blendMode(value:String):void;
  public native function get transform():Transform;
  public native function set transform(value:Transform):void;
  public native function get scale9Grid():Rectangle;
  public native function set scale9Grid(innerRectangle:Rectangle):void;

  public native function get loaderInfo():LoaderInfo;

  public native function get accessibilityProperties():AccessibilityProperties;

  public native function set accessibilityProperties(value:AccessibilityProperties):void;

  public native function set blendShader(value:Shader):void;

  public native function globalToLocal(point:Point):Point;

  public native function localToGlobal(point:Point):Point;

  public native function getBounds(targetCoordinateSpace:DisplayObject):Rectangle;

  public native function getRect(targetCoordinateSpace:DisplayObject):Rectangle;

  public function hitTestObject(obj:DisplayObject):Boolean {
    return _hitTest(false, 0, 0, false, obj);
  }

  public function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean = false):Boolean {
    return _hitTest(true, x, y, shapeFlag, null);
  }

  public native function globalToLocal3D(point:Point):Vector3D;

  public native function local3DToGlobal(point3d:Vector3D):Point;

  private native function _hitTest(use_xy: Boolean, x: Number, y: Number, useShape: Boolean,
                                   hitTestObject: DisplayObject): Boolean;
}
}

Implement as3 part of DisplayObject

Implement as3 part of DisplayObject


//
// Flump - Copyright 2012 Three Rings Design

package flump.display {

import flash.utils.ByteArray;

import flump.executor.Executor;
import flump.executor.Future;

import starling.core.Starling;

/**
 * Loads zip files created by the flump exporter and parses them into Library instances.
 */
public class LibraryLoader
{
    /**
     * Loads a Library from the zip in the given bytes.
     *
     * @param bytes The bytes containing the zip
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources out of the
     * bytes. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadBytes (bytes :ByteArray, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(bytes, scaleFactor).load);
    }

    /**
     * Loads a Library from the zip at the given url.
     *
     * @param bytes The url where the zip can be found
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources from the
     * url. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadURL (url :String, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(url, scaleFactor).load);
    }

    /** @private */
    public static const LIBRARY_LOCATION :String = "library.json";
    /** @private */
    public static const MD5_LOCATION :String = "md5";
    /** @private */
    public static const VERSION_LOCATION :String = "version";

    /**
     * @private
     * The version produced and parsable by this version of the code. The version in a resources
     * zip must equal the version compiled into the parsing code for parsing to succeed.
     */
    public static const VERSION :String = "0";
}

}

import flash.events.Event;
import flash.geom.Point;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Dictionary;

import deng.fzip.FZip;
import deng.fzip.FZipErrorEvent;
import deng.fzip.FZipEvent;
import deng.fzip.FZipFile;

import flump.display.Library;
import flump.display.LibraryLoader;
import flump.display.Movie;
import flump.executor.Executor;
import flump.executor.Future;
import flump.executor.FutureTask;
import flump.executor.load.ImageLoader;
import flump.executor.load.LoadedImage;
import flump.mold.AtlasMold;
import flump.mold.AtlasTextureMold;
import flump.mold.LibraryMold;
import flump.mold.MovieMold;
import flump.mold.TextureGroupMold;

import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;

interface SymbolCreator
{
    function create (library :Library) :DisplayObject;
}

class LibraryImpl
    implements Library
{
    public function LibraryImpl (creators :Dictionary) {
        _creators = creators;
    }

    public function createMovie (symbol :String) :Movie {
        return Movie(createDisplayObject(symbol));
    }

    public function createImage (symbol :String) :DisplayObject {
        const disp :DisplayObject = createDisplayObject(symbol);
        if (disp is Movie) throw new Error(symbol + " is a movie, not a texture");
        return disp;
    }

    public function get movieSymbols () :Vector.<String> {
        const names :Vector.<String> = new Vector.<String>();
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is MovieCreator) names.push(creatorName);
        }
        return names;
    }

    public function get imageSymbols () :Vector.<String> {
        const names :Vector.<String> = new Vector.<String>();
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is ImageCreator) names.push(creatorName);
        }
        return names;
    }

    public function createDisplayObject (name :String) :DisplayObject {
        var creator :SymbolCreator = _creators[name];
        if (creator == null) throw new Error("No such id '" + name + "'");
        return creator.create(this);
    }

    protected var _creators :Dictionary;
}

class Loader
{
    public function Loader (toLoad :Object, scaleFactor :Number) {
        _scaleFactor = (scaleFactor > 0 ? scaleFactor : Starling.contentScaleFactor);
        _toLoad = toLoad;
    }

    public function load (future :FutureTask) :void {
        _future = future;

        _zip.addEventListener(Event.COMPLETE, _future.monitoredCallback(onZipLoadingComplete));
        _zip.addEventListener(FZipErrorEvent.PARSE_ERROR, _future.fail);
        _zip.addEventListener(FZipEvent.FILE_LOADED, _future.monitoredCallback(onFileLoaded));

        if (_toLoad is String) _zip.load(new URLRequest(String(_toLoad)));
        else _zip.loadBytes(ByteArray(_toLoad));
    }

    protected function onFileLoaded (e :FZipEvent) :void {
        const loaded :FZipFile = _zip.removeFileAt(_zip.getFileCount() - 1);
        const name :String = loaded.filename;
        if (name == LibraryLoader.LIBRARY_LOCATION) {
            const jsonString :String = loaded.content.readUTFBytes(loaded.content.length);
            _lib = LibraryMold.fromJSON(JSON.parse(jsonString));
        } else if (name.indexOf(PNG, name.length - PNG.length) != -1) {
            _pngBytes[name] = loaded.content;
        } else if (name == LibraryLoader.VERSION_LOCATION) {
            const zipVersion :String = loaded.content.readUTFBytes(loaded.content.length)
            if (zipVersion != LibraryLoader.VERSION) {
                throw new Error("Zip is version " + zipVersion + " but the code needs " + LibraryLoader.VERSION);
            }
            _versionChecked = true;
        } else if (name == LibraryLoader.MD5_LOCATION ) { // Nothing to verify
        } else {} // ignore unknown files
    }

    protected function onZipLoadingComplete (..._) :void {
        _zip = null;
        if (_lib == null) throw new Error(LibraryLoader.LIBRARY_LOCATION + " missing from zip");
        if (!_versionChecked) throw new Error(LibraryLoader.VERSION_LOCATION + " missing from zip");
        const loader :ImageLoader = new ImageLoader();
        _pngLoaders.terminated.add(_future.monitoredCallback(onPngLoadingComplete));

        // Determine the scale factor we want to use
        var textureGroup :TextureGroupMold = _lib.bestTextureGroupForScaleFactor(_scaleFactor);
        if (textureGroup != null) {
            for each (var atlas :AtlasMold in textureGroup.atlases) {
                loadAtlas(loader, atlas);
            }
        }
        _pngLoaders.shutdown();
    }

    protected function loadAtlas (loader :ImageLoader, atlas :AtlasMold) :void {
        const pngBytes :* = _pngBytes[atlas.file];
        if (pngBytes === undefined) {
            throw new Error("Expected an atlas '" + atlas.file + "', but it wasn't in the zip");
        }
        const atlasFuture :Future = loader.loadFromBytes(pngBytes, _pngLoaders);
        atlasFuture.failed.add(onPngLoadingFailed);
        atlasFuture.succeeded.add(function (img :LoadedImage) :void {
            const baseTexture :Texture = Texture.fromBitmapData(
                img.bitmapData,
                true,   // generateMipMaps - do we want this?
                false,  // optimizeForRenderToTexture
                atlas.scaleFactor);

            for each (var atlasTexture :AtlasTextureMold in atlas.textures) {
                _creators[atlasTexture.symbol] = new ImageCreator(
                    Texture.fromTexture(baseTexture, atlasTexture.bounds),
                    atlasTexture.offset,
                    atlasTexture.symbol);
            }
        });
    }

    protected function onPngLoadingComplete (..._) :void {
        for each (var movie :MovieMold in _lib.movies) {
            movie.fillLabels();
            _creators[movie.id] = new MovieCreator(movie, _lib.frameRate);
        }
        _future.succeed(new LibraryImpl(_creators));
    }

    protected function onPngLoadingFailed (e :*) :void {
        if (_future.isComplete) return;
        _future.fail(e);
        _pngLoaders.shutdownNow();
    }

    protected var _toLoad :Object;
    protected var _scaleFactor :Number;
    protected var _future :FutureTask;
    protected var _versionChecked :Boolean;

    protected var _zip :FZip = new FZip();
    protected var _lib :LibraryMold;

    protected const _creators :Dictionary = new Dictionary();//<name, ImageCreator/MovieCreator>
    protected const _pngBytes :Dictionary = new Dictionary();//<String name, ByteArray>
    protected const _pngLoaders :Executor = new Executor(1);

    protected static const PNG :String = ".png";
}

class ImageCreator
    implements SymbolCreator
{
    public var texture :Texture;
    public var offset :Point;
    public var symbol :String;

    public function ImageCreator (texture :Texture, offset :Point, symbol :String) {
        this.texture = texture;
        this.offset = offset;
        this.symbol = symbol;
    }

    public function create (library :Library) :DisplayObject {
        const image :Image = new Image(texture);
        image.x = offset.x;
        image.y = offset.y;
        const holder :Sprite = new Sprite();
        holder.addChild(image);
        holder.name = symbol;
        return holder;
    }
}

class MovieCreator
    implements SymbolCreator
{
    public var mold :MovieMold;
    public var frameRate :Number;

    public function MovieCreator (mold :MovieMold, frameRate :Number) {
        this.mold = mold;
        this.frameRate = frameRate;
    }

    public function create (library :Library) :DisplayObject {
        return new Movie(mold, frameRate, library);
    }
}

//
// Flump - Copyright 2012 Three Rings Design

package flump.display {

import flash.utils.ByteArray;

import flump.executor.Executor;
import flump.executor.Future;

import starling.core.Starling;

/**
 * Loads zip files created by the flump exporter and parses them into Library instances.
 */
public class LibraryLoader
{
    /**
     * Loads a Library from the zip in the given bytes.
     *
     * @param bytes The bytes containing the zip
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources out of the
     * bytes. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadBytes (bytes :ByteArray, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(bytes, scaleFactor).load);
    }

    /**
     * Loads a Library from the zip at the given url.
     *
     * @param bytes The url where the zip can be found
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources from the
     * url. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadURL (url :String, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(url, scaleFactor).load);
    }

    /** @private */
    public static const LIBRARY_LOCATION :String = "library.json";
    /** @private */
    public static const MD5_LOCATION :String = "md5";
    /** @private */
    public static const VERSION_LOCATION :String = "version";

    /**
     * @private
     * The version produced and parsable by this version of the code. The version in a resources
     * zip must equal the version compiled into the parsing code for parsing to succeed.
     */
    public static const VERSION :String = "1";
}

}

import flash.events.Event;
import flash.geom.Point;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Dictionary;

import deng.fzip.FZip;
import deng.fzip.FZipErrorEvent;
import deng.fzip.FZipEvent;
import deng.fzip.FZipFile;

import flump.display.Library;
import flump.display.LibraryLoader;
import flump.display.Movie;
import flump.executor.Executor;
import flump.executor.Future;
import flump.executor.FutureTask;
import flump.executor.load.ImageLoader;
import flump.executor.load.LoadedImage;
import flump.mold.AtlasMold;
import flump.mold.AtlasTextureMold;
import flump.mold.LibraryMold;
import flump.mold.MovieMold;
import flump.mold.TextureGroupMold;

import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;

interface SymbolCreator
{
    function create (library :Library) :DisplayObject;
}

class LibraryImpl
    implements Library
{
    public function LibraryImpl (creators :Dictionary) {
        _creators = creators;
    }

    public function createMovie (symbol :String) :Movie {
        return Movie(createDisplayObject(symbol));
    }

    public function createImage (symbol :String) :DisplayObject {
        const disp :DisplayObject = createDisplayObject(symbol);
        if (disp is Movie) throw new Error(symbol + " is a movie, not a texture");
        return disp;
    }

    public function get movieSymbols () :Vector.<String> {
        const names :Vector.<String> = new Vector.<String>();
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is MovieCreator) names.push(creatorName);
        }
        return names;
    }

    public function get imageSymbols () :Vector.<String> {
        const names :Vector.<String> = new Vector.<String>();
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is ImageCreator) names.push(creatorName);
        }
        return names;
    }

    public function createDisplayObject (name :String) :DisplayObject {
        var creator :SymbolCreator = _creators[name];
        if (creator == null) throw new Error("No such id '" + name + "'");
        return creator.create(this);
    }

    protected var _creators :Dictionary;
}

class Loader
{
    public function Loader (toLoad :Object, scaleFactor :Number) {
        _scaleFactor = (scaleFactor > 0 ? scaleFactor : Starling.contentScaleFactor);
        _toLoad = toLoad;
    }

    public function load (future :FutureTask) :void {
        _future = future;

        _zip.addEventListener(Event.COMPLETE, _future.monitoredCallback(onZipLoadingComplete));
        _zip.addEventListener(FZipErrorEvent.PARSE_ERROR, _future.fail);
        _zip.addEventListener(FZipEvent.FILE_LOADED, _future.monitoredCallback(onFileLoaded));

        if (_toLoad is String) _zip.load(new URLRequest(String(_toLoad)));
        else _zip.loadBytes(ByteArray(_toLoad));
    }

    protected function onFileLoaded (e :FZipEvent) :void {
        const loaded :FZipFile = _zip.removeFileAt(_zip.getFileCount() - 1);
        const name :String = loaded.filename;
        if (name == LibraryLoader.LIBRARY_LOCATION) {
            const jsonString :String = loaded.content.readUTFBytes(loaded.content.length);
            _lib = LibraryMold.fromJSON(JSON.parse(jsonString));
        } else if (name.indexOf(PNG, name.length - PNG.length) != -1) {
            _pngBytes[name] = loaded.content;
        } else if (name == LibraryLoader.VERSION_LOCATION) {
            const zipVersion :String = loaded.content.readUTFBytes(loaded.content.length)
            if (zipVersion != LibraryLoader.VERSION) {
                throw new Error("Zip is version " + zipVersion + " but the code needs " + LibraryLoader.VERSION);
            }
            _versionChecked = true;
        } else if (name == LibraryLoader.MD5_LOCATION ) { // Nothing to verify
        } else {} // ignore unknown files
    }

    protected function onZipLoadingComplete (..._) :void {
        _zip = null;
        if (_lib == null) throw new Error(LibraryLoader.LIBRARY_LOCATION + " missing from zip");
        if (!_versionChecked) throw new Error(LibraryLoader.VERSION_LOCATION + " missing from zip");
        const loader :ImageLoader = new ImageLoader();
        _pngLoaders.terminated.add(_future.monitoredCallback(onPngLoadingComplete));

        // Determine the scale factor we want to use
        var textureGroup :TextureGroupMold = _lib.bestTextureGroupForScaleFactor(_scaleFactor);
        if (textureGroup != null) {
            for each (var atlas :AtlasMold in textureGroup.atlases) {
                loadAtlas(loader, atlas);
            }
        }
        _pngLoaders.shutdown();
    }

    protected function loadAtlas (loader :ImageLoader, atlas :AtlasMold) :void {
        const pngBytes :* = _pngBytes[atlas.file];
        if (pngBytes === undefined) {
            throw new Error("Expected an atlas '" + atlas.file + "', but it wasn't in the zip");
        }
        const atlasFuture :Future = loader.loadFromBytes(pngBytes, _pngLoaders);
        atlasFuture.failed.add(onPngLoadingFailed);
        atlasFuture.succeeded.add(function (img :LoadedImage) :void {
            const baseTexture :Texture = Texture.fromBitmapData(
                img.bitmapData,
                true,   // generateMipMaps - do we want this?
                false,  // optimizeForRenderToTexture
                atlas.scaleFactor);

            for each (var atlasTexture :AtlasTextureMold in atlas.textures) {
                _creators[atlasTexture.symbol] = new ImageCreator(
                    Texture.fromTexture(baseTexture, atlasTexture.bounds),
                    atlasTexture.offset,
                    atlasTexture.symbol);
            }
        });
    }

    protected function onPngLoadingComplete (..._) :void {
        for each (var movie :MovieMold in _lib.movies) {
            movie.fillLabels();
            _creators[movie.id] = new MovieCreator(movie, _lib.frameRate);
        }
        _future.succeed(new LibraryImpl(_creators));
    }

    protected function onPngLoadingFailed (e :*) :void {
        if (_future.isComplete) return;
        _future.fail(e);
        _pngLoaders.shutdownNow();
    }

    protected var _toLoad :Object;
    protected var _scaleFactor :Number;
    protected var _future :FutureTask;
    protected var _versionChecked :Boolean;

    protected var _zip :FZip = new FZip();
    protected var _lib :LibraryMold;

    protected const _creators :Dictionary = new Dictionary();//<name, ImageCreator/MovieCreator>
    protected const _pngBytes :Dictionary = new Dictionary();//<String name, ByteArray>
    protected const _pngLoaders :Executor = new Executor(1);

    protected static const PNG :String = ".png";
}

class ImageCreator
    implements SymbolCreator
{
    public var texture :Texture;
    public var offset :Point;
    public var symbol :String;

    public function ImageCreator (texture :Texture, offset :Point, symbol :String) {
        this.texture = texture;
        this.offset = offset;
        this.symbol = symbol;
    }

    public function create (library :Library) :DisplayObject {
        const image :Image = new Image(texture);
        image.x = offset.x;
        image.y = offset.y;
        const holder :Sprite = new Sprite();
        holder.addChild(image);
        holder.name = symbol;
        return holder;
    }
}

class MovieCreator
    implements SymbolCreator
{
    public var mold :MovieMold;
    public var frameRate :Number;

    public function MovieCreator (mold :MovieMold, frameRate :Number) {
        this.mold = mold;
        this.frameRate = frameRate;
    }

    public function create (library :Library) :DisplayObject {
        return new Movie(mold, frameRate, library);
    }
}

Bump our data format version

Bump our data format version


/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 *   Marc Alcaraz <ekameleon@gmail.com>.
 */

package com.google.analytics
{
    import com.google.analytics.core.Buffer;
    import com.google.analytics.core.Ecommerce;
    import com.google.analytics.core.EventTracker;
    import com.google.analytics.core.GIFRequest;
    import com.google.analytics.core.IdleTimer;
    import com.google.analytics.core.ServerOperationMode;
    import com.google.analytics.core.TrackerCache;
    import com.google.analytics.core.TrackerMode;
    import com.google.analytics.core.ga_internal;
    import com.google.analytics.debug.DebugConfiguration;
    import com.google.analytics.debug.Layout;
    import com.google.analytics.events.AnalyticsEvent;
    import com.google.analytics.external.AdSenseGlobals;
    import com.google.analytics.external.HTMLDOM;
    import com.google.analytics.external.JavascriptProxy;
    import com.google.analytics.utils.Environment;
    import com.google.analytics.v4.Bridge;
    import com.google.analytics.v4.Configuration;
    import com.google.analytics.v4.GoogleAnalyticsAPI;
    import com.google.analytics.v4.Tracker;

    import core.version;

    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.EventDispatcher;

    EventTracker;
    ServerOperationMode;

    /**
     * Dispatched after the factory has built the tracker object.
     * @eventType com.google.analytics.events.AnalyticsEvent.READY
     */
    [Event(name="ready", type="com.google.analytics.events.AnalyticsEvent")]

    /**
     * Google Analytic Tracker Code (GATC)'s code-only component.
     */
    public class GATracker implements AnalyticsTracker
    {
        private var _ready:Boolean = false;

        private var _display:DisplayObject;
        private var _eventDispatcher:EventDispatcher;
        private var _tracker:GoogleAnalyticsAPI;

        //factory
        private var _config:Configuration;
        private var _debug:DebugConfiguration;
        private var _env:Environment;
        private var _buffer:Buffer;
        private var _gifRequest:GIFRequest;
        private var _jsproxy:JavascriptProxy;
        private var _dom:HTMLDOM;
        private var _adSense:AdSenseGlobals;
        private var _idleTimer:IdleTimer;
        private var _ecom:Ecommerce;

        //object properties
        private var _account:String;
        private var _mode:String;
        private var _visualDebug:Boolean;

        /**
         * Indicates if the tracker is automatically build.
         */
        public static var autobuild:Boolean = true;

        /**
         * The version of the tracker.
         */
        public static var version:core.version = API.version;

        /**
         * Creates a new GATracker instance.
         *
Note: the GATracker need to be instancied and added to the Stage or at least
         * being placed in a display list.

         */
        public function GATracker( display:DisplayObject, account:String,
                                   mode:String = "AS3", visualDebug:Boolean = false,
                                   config:Configuration = null, debug:DebugConfiguration = null )
        {
            _display = display;
            _eventDispatcher = new EventDispatcher( this ) ;
            _tracker = new TrackerCache();

            this.account     = account;
            this.mode        = mode;
            this.visualDebug = visualDebug;

            if( !debug )
            {
                this.debug = new DebugConfiguration();
            }

            if( !config )
            {
                this.config = new Configuration( debug );
            }
            else
            {
            	this.config = config;
            }

            if( autobuild )
            {
                _factory();
            }
        }

        /**
         * @private
         * Factory to build the different trackers
         */
        private function _factory():void
        {
            _jsproxy = new JavascriptProxy( debug );

            if( visualDebug )
            {
                debug.layout = new Layout( debug, _display );
                debug.active = visualDebug;
            }

            var activeTracker:GoogleAnalyticsAPI;
            var cache:TrackerCache = _tracker as TrackerCache;

            switch( mode )
            {
                case TrackerMode.BRIDGE :
                {
                    activeTracker = _bridgeFactory();
                    break;
                }

                case TrackerMode.AS3 :
                default:
                {
                    activeTracker = _trackerFactory();
                }
            }

            if( !cache.isEmpty() )
            {
                cache.tracker = activeTracker;
                cache.flush();
            }

            _tracker = activeTracker;

            _ready = true;
            dispatchEvent( new AnalyticsEvent( AnalyticsEvent.READY, this ) );
        }

        /**
        * @private
        * Factory method for returning a Tracker object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _trackerFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (AS3) v" + version +"\naccount: " + account );

            /* note:
               for unit testing and to avoid 2 different branches AIR/Flash
               here we will detect if we are in the Flash Player or AIR
               and pass the infos to the LocalInfo

               By default we will define "Flash" for our local tests
            */

            _adSense    = new AdSenseGlobals( debug );

            _dom        = new HTMLDOM( debug );
            _dom.cacheProperties();

            _env        = new Environment( "", "", "", debug, _dom );

            _buffer     = new Buffer( config, debug, false );
            _gifRequest = new GIFRequest( config, debug, _buffer, _env );
            _idleTimer  = new IdleTimer( config, debug, _display, _buffer );
            _ecom       = new Ecommerce ( _debug );

            /* note:
               To be able to obtain the URL of the main SWF containing the GA API
               we need to be able to access the stage property of a DisplayObject,
               here we open the internal namespace to be able to set that reference
               at instanciation-time.

               We keep the implementation internal to be able to change it if required later.
            */
            use namespace ga_internal;
            _env.url = _display.stage.loaderInfo.url;

            return new Tracker( account, config, debug, _env, _buffer, _gifRequest, _adSense, _ecom );
        }

        /**
        * @private
        * Factory method for returning a Bridge object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _bridgeFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (Bridge) v" + version +"\naccount: " + account );
            return new Bridge( account, _debug, _jsproxy );
        }

        /**
         * Indicates the account value of the tracking.
         */
        public function get account():String
        {
            return _account;
        }

        /**
         * @private
         */
        public function set account( value:String ):void
        {
            _account = value;
        }

        /**
         * Determinates the Configuration object of the tracker.
         */
        public function get config():Configuration
        {
            return _config;
        }

        /**
         * @private
         */
        public function set config( value:Configuration ):void
        {
            _config = value;
        }

        /**
         * Determinates the DebugConfiguration of the tracker.
         */
        public function get debug():DebugConfiguration
        {
            return _debug;
        }

        /**
         * @private
         */
        public function set debug( value:DebugConfiguration ):void
        {
            _debug = value;
        }

        /**
         * Indicates if the tracker is ready to use.
         */
        public function isReady():Boolean
        {
            return _ready;
        }

        /**
         * Indicates the mode of the tracking "AS3" or "Bridge".
         */
        public function get mode():String
        {
            return _mode;
        }

        /**
         * @private
         */
        public function set mode( value:String ):void
        {
            _mode = value ;
        }

        /**
         * Indicates if the tracker use a visual debug.
         */
        public function get visualDebug():Boolean
        {
            return _visualDebug;
        }

        /**
         * @private
         */
        public function set visualDebug( value:Boolean ):void
        {
            _visualDebug = value;
        }

        /**
         * Builds the tracker.
         */
        public function build():void
        {
            if( !isReady() )
            {
                _factory();
            }
        }

        // IEventDispatcher implementation

        /**
         * Allows the registration of event listeners on the event target.
         * @param type A string representing the event type to listen for. If eventName value is "ALL" addEventListener use addGlobalListener
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         * @param priority Determines the priority level of the event listener.
         * @param useWeakReference Indicates if the listener is a weak reference.
         */
        public function addEventListener( type:String, listener:Function, useCapture:Boolean = false, priority:int = 0,
                                          useWeakReference:Boolean = false):void
        {
            _eventDispatcher.addEventListener( type, listener, useCapture, priority, useWeakReference );
        }

        /**
         * Dispatches an event into the event flow.
         * @param event The Event object that is dispatched into the event flow.
         * @return true if the Event is dispatched.
         */
        public function dispatchEvent( event:Event ):Boolean
        {
            return _eventDispatcher.dispatchEvent( event );
        }

        /**
         * Checks whether the EventDispatcher object has any listeners registered for a specific type of event.
         * This allows you to determine where altered handling of an event type has been introduced in the event flow heirarchy by an EventDispatcher object.
         */
        public function hasEventListener( type:String ):Boolean
        {
            return _eventDispatcher.hasEventListener( type );
        }

        /**
         * Removes a listener from the EventDispatcher object.
         * If there is no matching listener registered with the EventDispatcher object, then calling this method has no effect.
         * @param type Specifies the type of event.
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         */
        public function removeEventListener( type:String, listener:Function, useCapture:Boolean = false ):void
        {
            _eventDispatcher.removeEventListener( type, listener, useCapture );
        }

        /**
         * Checks whether an event listener is registered with this EventDispatcher object or any of its ancestors for the specified event type.
         * This method returns true if an event listener is triggered during any phase of the event flow when an event of the specified type is dispatched to this EventDispatcher object or any of its descendants.
         * @return A value of true if a listener of the specified type will be triggered; false otherwise.
         */
        public function willTrigger( type:String ):Boolean
        {
            return _eventDispatcher.willTrigger( type );
        }

        include "common.txt"

    }
}

/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 *   Marc Alcaraz <ekameleon@gmail.com>.
 */

package com.google.analytics
{
    import com.google.analytics.core.Buffer;
    import com.google.analytics.core.Ecommerce;
    import com.google.analytics.core.EventTracker;
    import com.google.analytics.core.GIFRequest;
    import com.google.analytics.core.ServerOperationMode;
    import com.google.analytics.core.TrackerCache;
    import com.google.analytics.core.TrackerMode;
    import com.google.analytics.core.ga_internal;
    import com.google.analytics.debug.DebugConfiguration;
    import com.google.analytics.debug.Layout;
    import com.google.analytics.events.AnalyticsEvent;
    import com.google.analytics.external.AdSenseGlobals;
    import com.google.analytics.external.HTMLDOM;
    import com.google.analytics.external.JavascriptProxy;
    import com.google.analytics.utils.Environment;
    import com.google.analytics.v4.Bridge;
    import com.google.analytics.v4.Configuration;
    import com.google.analytics.v4.GoogleAnalyticsAPI;
    import com.google.analytics.v4.Tracker;

    import core.version;

    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.EventDispatcher;

    EventTracker;
    ServerOperationMode;

    /**
     * Dispatched after the factory has built the tracker object.
     * @eventType com.google.analytics.events.AnalyticsEvent.READY
     */
    [Event(name="ready", type="com.google.analytics.events.AnalyticsEvent")]

    /**
     * Google Analytic Tracker Code (GATC)'s code-only component.
     */
    public class GATracker implements AnalyticsTracker
    {
        private var _ready:Boolean = false;

        private var _display:DisplayObject;
        private var _eventDispatcher:EventDispatcher;
        private var _tracker:GoogleAnalyticsAPI;

        //factory
        private var _config:Configuration;
        private var _debug:DebugConfiguration;
        private var _env:Environment;
        private var _buffer:Buffer;
        private var _gifRequest:GIFRequest;
        private var _jsproxy:JavascriptProxy;
        private var _dom:HTMLDOM;
        private var _adSense:AdSenseGlobals;
        private var _ecom:Ecommerce;

        //object properties
        private var _account:String;
        private var _mode:String;
        private var _visualDebug:Boolean;

        /**
         * Indicates if the tracker is automatically build.
         */
        public static var autobuild:Boolean = true;

        /**
         * The version of the tracker.
         */
        public static var version:core.version = API.version;

        /**
         * Creates a new GATracker instance.
         *
Note: the GATracker need to be instancied and added to the Stage or at least
         * being placed in a display list.

         */
        public function GATracker( display:DisplayObject, account:String,
                                   mode:String = "AS3", visualDebug:Boolean = false,
                                   config:Configuration = null, debug:DebugConfiguration = null )
        {
            _display = display;
            _eventDispatcher = new EventDispatcher( this ) ;
            _tracker = new TrackerCache();

            this.account     = account;
            this.mode        = mode;
            this.visualDebug = visualDebug;

            if( !debug )
            {
                this.debug = new DebugConfiguration();
            }

            if( !config )
            {
                this.config = new Configuration( debug );
            }
            else
            {
            	this.config = config;
            }

            if( autobuild )
            {
                _factory();
            }
        }

        /**
         * @private
         * Factory to build the different trackers
         */
        private function _factory():void
        {
            _jsproxy = new JavascriptProxy( debug );

            if( visualDebug )
            {
                debug.layout = new Layout( debug, _display );
                debug.active = visualDebug;
            }

            var activeTracker:GoogleAnalyticsAPI;
            var cache:TrackerCache = _tracker as TrackerCache;

            switch( mode )
            {
                case TrackerMode.BRIDGE :
                {
                    activeTracker = _bridgeFactory();
                    break;
                }

                case TrackerMode.AS3 :
                default:
                {
                    activeTracker = _trackerFactory();
                }
            }

            if( !cache.isEmpty() )
            {
                cache.tracker = activeTracker;
                cache.flush();
            }

            _tracker = activeTracker;

            _ready = true;
            dispatchEvent( new AnalyticsEvent( AnalyticsEvent.READY, this ) );
        }

        /**
        * @private
        * Factory method for returning a Tracker object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _trackerFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (AS3) v" + version +"\naccount: " + account );

            /* note:
               for unit testing and to avoid 2 different branches AIR/Flash
               here we will detect if we are in the Flash Player or AIR
               and pass the infos to the LocalInfo

               By default we will define "Flash" for our local tests
            */

            _adSense    = new AdSenseGlobals( debug );

            _dom        = new HTMLDOM( debug );
            _dom.cacheProperties();

            _env        = new Environment( "", "", "", debug, _dom );

            _buffer     = new Buffer( config, debug, false );
            _gifRequest = new GIFRequest( config, debug, _buffer, _env );
            _ecom       = new Ecommerce ( _debug );

            /* note:
               To be able to obtain the URL of the main SWF containing the GA API
               we need to be able to access the stage property of a DisplayObject,
               here we open the internal namespace to be able to set that reference
               at instanciation-time.

               We keep the implementation internal to be able to change it if required later.
            */
            use namespace ga_internal;
            _env.url = _display.stage.loaderInfo.url;

            return new Tracker( account, config, debug, _env, _buffer, _gifRequest, _adSense, _ecom );
        }

        /**
        * @private
        * Factory method for returning a Bridge object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _bridgeFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (Bridge) v" + version +"\naccount: " + account );
            return new Bridge( account, _debug, _jsproxy );
        }

        /**
         * Indicates the account value of the tracking.
         */
        public function get account():String
        {
            return _account;
        }

        /**
         * @private
         */
        public function set account( value:String ):void
        {
            _account = value;
        }

        /**
         * Determinates the Configuration object of the tracker.
         */
        public function get config():Configuration
        {
            return _config;
        }

        /**
         * @private
         */
        public function set config( value:Configuration ):void
        {
            _config = value;
        }

        /**
         * Determinates the DebugConfiguration of the tracker.
         */
        public function get debug():DebugConfiguration
        {
            return _debug;
        }

        /**
         * @private
         */
        public function set debug( value:DebugConfiguration ):void
        {
            _debug = value;
        }

        /**
         * Indicates if the tracker is ready to use.
         */
        public function isReady():Boolean
        {
            return _ready;
        }

        /**
         * Indicates the mode of the tracking "AS3" or "Bridge".
         */
        public function get mode():String
        {
            return _mode;
        }

        /**
         * @private
         */
        public function set mode( value:String ):void
        {
            _mode = value ;
        }

        /**
         * Indicates if the tracker use a visual debug.
         */
        public function get visualDebug():Boolean
        {
            return _visualDebug;
        }

        /**
         * @private
         */
        public function set visualDebug( value:Boolean ):void
        {
            _visualDebug = value;
        }

        /**
         * Builds the tracker.
         */
        public function build():void
        {
            if( !isReady() )
            {
                _factory();
            }
        }

        // IEventDispatcher implementation

        /**
         * Allows the registration of event listeners on the event target.
         * @param type A string representing the event type to listen for. If eventName value is "ALL" addEventListener use addGlobalListener
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         * @param priority Determines the priority level of the event listener.
         * @param useWeakReference Indicates if the listener is a weak reference.
         */
        public function addEventListener( type:String, listener:Function, useCapture:Boolean = false, priority:int = 0,
                                          useWeakReference:Boolean = false):void
        {
            _eventDispatcher.addEventListener( type, listener, useCapture, priority, useWeakReference );
        }

        /**
         * Dispatches an event into the event flow.
         * @param event The Event object that is dispatched into the event flow.
         * @return true if the Event is dispatched.
         */
        public function dispatchEvent( event:Event ):Boolean
        {
            return _eventDispatcher.dispatchEvent( event );
        }

        /**
         * Checks whether the EventDispatcher object has any listeners registered for a specific type of event.
         * This allows you to determine where altered handling of an event type has been introduced in the event flow heirarchy by an EventDispatcher object.
         */
        public function hasEventListener( type:String ):Boolean
        {
            return _eventDispatcher.hasEventListener( type );
        }

        /**
         * Removes a listener from the EventDispatcher object.
         * If there is no matching listener registered with the EventDispatcher object, then calling this method has no effect.
         * @param type Specifies the type of event.
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         */
        public function removeEventListener( type:String, listener:Function, useCapture:Boolean = false ):void
        {
            _eventDispatcher.removeEventListener( type, listener, useCapture );
        }

        /**
         * Checks whether an event listener is registered with this EventDispatcher object or any of its ancestors for the specified event type.
         * This method returns true if an event listener is triggered during any phase of the event flow when an event of the specified type is dispatched to this EventDispatcher object or any of its descendants.
         * @return A value of true if a listener of the specified type will be triggered; false otherwise.
         */
        public function willTrigger( type:String ):Boolean
        {
            return _eventDispatcher.willTrigger( type );
        }

        include "common.txt"

    }
}

remove idle timer instanciation

remove idle timer instanciation


package battlecode.client.viewer.render {
    import battlecode.common.ActionType;
    import battlecode.common.Direction;
    import battlecode.common.GameConstants;
    import battlecode.common.MapLocation;
    import battlecode.common.RobotType;
    import battlecode.common.Team;
    import battlecode.events.RobotEvent;

    import mx.containers.Canvas;
    import mx.controls.Image;
    import mx.core.UIComponent;

    [Event(name="indicatorStringChange", type="battlecode.events.RobotEvent")]
    public class DrawRobot extends Canvas implements DrawObject {
        private var actions:Vector.<DrawAction>;

        private var broadcastAnimation:BroadcastAnimation;
        private var explosionAnimation:ExplosionAnimation;

        private var overlayCanvas:UIComponent;
        private var imageCanvas:UIComponent;
        private var hatCanvas:UIComponent;
        private var image:Image;

        // size
        private var overrideSize:Number;

        // indicator strings
        private var indicatorStrings:Vector.<String> = new Vector.<String>(3, true);

        // movement animation
        private var drawX:Number, drawY:Number;
        private var movementDelay:uint;

        // attack animation
        private var targetLocation:MapLocation;

        private var selected:Boolean = false;

        private var robotID:uint;
        private var type:String;
        private var team:String;
        private var location:MapLocation;
        private var direction:String;
        private var energon:Number = 0;
        private var maxEnergon:Number = 0;
        private var alive:Boolean = true;
        private var hats:Array;
        private var hatImages:Array;

        public function DrawRobot(robotID:uint, type:String, team:String, overrideSize:Number = 0) {
            this.robotID = robotID;
            this.type = type;
            this.team = team;
            this.maxEnergon = RobotType.maxEnergon(type);
            this.movementDelay = 0;
            this.hats = new Array();
            this.hatImages = new Array();

            this.actions = new Vector.<DrawAction>();

            this.overrideSize = overrideSize;

            // set the unit avatar image
            var avatarClass:Class = ImageAssets.getRobotAvatar(type, team);
            this.imageCanvas = new UIComponent();
            this.image = new Image();
            this.image.source = avatarClass;
            this.image.width = getImageSize(true);
            this.image.height = getImageSize(true);
            this.image.x = -this.image.width / 2;
            this.image.y = -this.image.height / 2;
            this.imageCanvas.addChild(image);
            this.addChild(imageCanvas);

            this.width = this.image.width;
            this.height = this.image.height;

            this.overlayCanvas = new UIComponent();
            this.addChild(overlayCanvas);

            this.hatCanvas = new UIComponent();
            this.addChild(hatCanvas);

            // set the hit area for click selection
            this.hitArea = imageCanvas;

            // animations
            this.broadcastAnimation = new BroadcastAnimation(0, team);
            this.addChild(broadcastAnimation);

            this.explosionAnimation = new ExplosionAnimation();
            this.addChild(explosionAnimation);
        }

        public function clone():DrawObject {
            var d:DrawRobot = new DrawRobot(robotID, type, team, overrideSize);
            d.location = location;
            d.energon = energon;
            d.movementDelay = movementDelay;
            d.targetLocation = targetLocation;
            d.alive = alive;
            d.hats = hats.concat();
            d.hatImages = new Array();

            d.actions = new Vector.<DrawAction>(actions.length);
            for each (var o:DrawAction in actions) {
                d.actions.push(o.clone());
            }

            d.removeChild(d.broadcastAnimation);
            d.removeChild(d.explosionAnimation);
            d.broadcastAnimation = broadcastAnimation.clone() as BroadcastAnimation;
            d.explosionAnimation = explosionAnimation.clone() as ExplosionAnimation;
            d.addChild(d.broadcastAnimation);
            d.addChild(d.explosionAnimation);

            return d;
        }

        private function addAction(d:DrawAction):void {
            actions.push(d);
        }

        public function getRobotID():uint {
            return robotID;
        }

        public function getType():String {
            return type;
        }

        public function getTeam():String {
            return team;
        }

        public function getLocation():MapLocation {
            return location;
        }

        public function getSelected():Boolean {
            return selected;
        }

        public function getIndicatorString(index:uint):String {
            return indicatorStrings[index];
        }

        public function setLocation(location:MapLocation):void {
            this.location = location;
        }

        public function setEnergon(amt:Number):void {
            this.energon = Math.min(Math.max(0, amt), maxEnergon);
        }

        public function setSelected(val:Boolean):void {
            this.selected = val;
        }

        public function setIndicatorString(str:String, index:uint):void {
            indicatorStrings[index] = str;
            dispatchEvent(new RobotEvent(RobotEvent.INDICATOR_STRING_CHANGE, false, false, this));
        }

        public function setOverrideSize(overrideSize:Number):void {
            this.overrideSize = overrideSize;
            this.draw(true);
        }

        public function capture():void {
            this.addAction(new DrawAction(ActionType.CAPTURING, GameConstants.CAPTURE_DELAY));
        }

        public function layMine():void {
            this.addAction(new DrawAction(ActionType.MINING, GameConstants.MINE_LAY_DELAY));
        }

        public function stopLayingMine():void {
            this.addAction(new DrawAction(ActionType.MINING_STOPPING, GameConstants.MINE_LAY_STOP_DELAY));
        }

        public function diffuseMine(hasUpgrade:Boolean):void {
            this.addAction(new DrawAction(ActionType.DEFUSING, hasUpgrade ? GameConstants.MINE_DIFFUSE_UPGRADE_DELAY: GameConstants.MINE_DIFFUSE_DELAY))
        }

        public function attack(targetLocation:MapLocation):void {
            this.targetLocation = targetLocation;
            this.addAction(new DrawAction(ActionType.ATTACKING, RobotType.attackDelay(type)));
        }

        public function broadcast():void {
            this.broadcastAnimation.broadcast();
        }

        public function destroyUnit():void {
            this.explosionAnimation.explode();
            this.alive = false;
        }

        public function moveToLocation(location:MapLocation):void {
            this.direction = this.location.directionTo(location);
            this.movementDelay = Direction.isDiagonal(direction) ?
                    RobotType.movementDelayDiagonal(type) :
                    RobotType.movementDelay(type);
            this.location = location;
            this.addAction(new DrawAction(ActionType.MOVING, movementDelay));
        }

        public function wearHat(hat:int):void {
            hats.push(hat);
        }

        public function isAlive():Boolean {
            return alive || explosionAnimation.isAlive();
        }

        public function updateRound():void {
            // update actions
            for (var i:uint = 0; i < actions.length; i++) {
                var o:DrawAction = actions[i];
                o.decreaseRound();
                if (o.getRounds() <= 0) {
                    actions.splice(i, 1);
                    i--;
                }
            }

            // update animations
            broadcastAnimation.updateRound();
            explosionAnimation.updateRound();

            // update tooltip
            this.toolTip = "Robot " + getRobotID() + " " + getType() + " Energon: " + energon + " Loc: " + getLocation().toString();
        }

        public function draw(force:Boolean = false):void {
            if (explosionAnimation.isExploding()) {
                this.imageCanvas.visible = false;
                this.overlayCanvas.visible = false;
                this.graphics.clear();

                explosionAnimation.draw(force);
                return;
            }

            // draw direction
            this.imageCanvas.rotation = directionToRotation(direction);

            if (force) {
                this.image.width = getImageSize(true);
                this.image.height = getImageSize(true);
                this.image.x = -this.image.width / 2;
                this.image.y = -this.image.height / 2;
            }

            // clear the graphics object once
            this.graphics.clear();
            this.overlayCanvas.graphics.clear();

            var o:DrawAction;
            var movementRounds:uint = 0;
            for each (o in actions) {
                if (o.getType() == ActionType.MOVING) {
                    movementRounds = o.getRounds();
                    break;
                }
            }

            drawX = calculateDrawX(movementRounds);
            drawY = calculateDrawY(movementRounds);

            for each (o in actions) {
                switch (o.getType()) {
                    case ActionType.ATTACKING:
                        drawAttack();
                        break;
                    case ActionType.MOVING:
                        drawMovement();
                        break;
                    case ActionType.CAPTURING:
                    case ActionType.MINING:
                    case ActionType.MINING_STOPPING:
                    case ActionType.DEFUSING:
                        drawActionBar(o);
                        break;
                }
            }

            drawEnergonBar();
            drawSelected();
            drawHats();

            // draw animations
            broadcastAnimation.draw(force);
        }

        ///////////////////////////////////////////////////////
        ////////////// DRAWING HELPER FUNCTIONS ///////////////
        ///////////////////////////////////////////////////////

        private function drawEnergonBar():void {
            if (!RenderConfiguration.showEnergon() && getType() != RobotType.HQ)
                return;

            var ratio:Number = energon / maxEnergon;
            var size:Number = getImageSize(true);
            this.graphics.lineStyle();
            this.graphics.beginFill(0x00FF00, 0.8);
            this.graphics.drawRect(-size / 2, size / 2, ratio * size, 5 * getImageScale());
            this.graphics.endFill();
            this.graphics.beginFill(0x000000, 0.8);
            this.graphics.drawRect(-size / 2 + ratio * size, size / 2, (1 - ratio) * size, 5 * getImageScale());
            this.graphics.endFill();
        }

        private function drawActionBar(action:DrawAction):void {
            var yOffset:Number = RenderConfiguration.showEnergon() ? 5 * getImageScale() : 0;
            var color:uint;
            switch (action.getType()) {
                case ActionType.CAPTURING:
                    color = 0x4C4CFF;
                    break;
                case ActionType.MINING:
                    color = 0xFF00CC;
                    break;
                case ActionType.MINING_STOPPING:
                    color = 0xFF0000;
                    break;
                case ActionType.DEFUSING:
                    color = 0x00FFFF;
                    break;
                default:
                    color = 0x000000;
                    break;
            }

            var ratio:Number;
            switch (action.getType()) {
                case ActionType.MINING_STOPPING:
                    ratio = action.getRounds() / action.getMaxRounds();
                    break;
                default:
                    ratio = (action.getMaxRounds() - action.getRounds()) / action.getMaxRounds();
                    break;
            }
            var size:Number = getImageSize(true);
            this.graphics.lineStyle();
            this.graphics.beginFill(color, 0.8);
            this.graphics.drawRect(-size / 2, size / 2 + yOffset, ratio * size, 5 * getImageScale());
            this.graphics.endFill();
            this.graphics.beginFill(0x000000, 0.8);
            this.graphics.drawRect(-size / 2 + ratio * size, size / 2  + yOffset, (1 - ratio) * size, 5 * getImageScale());
            this.graphics.endFill();
        }

        private function drawAttack():void {
            var targetOffsetX:Number = (targetLocation.getX() - location.getX()) * getImageSize();
            var targetOffsetY:Number = (targetLocation.getY() - location.getY()) * getImageSize();

            this.graphics.lineStyle(2, team == Team.A ? 0xFF0000 : 0x0000FF);
            this.graphics.moveTo(0, 0);
            this.graphics.lineTo(targetOffsetX - drawX, targetOffsetY - drawY);
            this.graphics.drawCircle(targetOffsetX - drawX, targetOffsetY - drawY, getImageSize() / 2 * .6);
        }

        private function drawMovement():void {
            if (RenderConfiguration.showDiscrete()) return;

            this.x += drawX;
            this.y += drawY;
        }

        private function drawSelected():void {
            var size:Number = getImageSize(true);

            if (selected) {
                this.graphics.lineStyle(2, 0xFFFFFF);
                this.graphics.moveTo(-size / 2, -size / 4);
                this.graphics.lineTo(-size / 2, -size / 2);
                this.graphics.lineTo(-size / 4, -size / 2);
                this.graphics.moveTo(size / 2, -size / 4);
                this.graphics.lineTo(size / 2, -size / 2);
                this.graphics.lineTo(size / 4, -size / 2);
                this.graphics.moveTo(size / 2, size / 4);
                this.graphics.lineTo(size / 2, size / 2);
                this.graphics.lineTo(size / 4, size / 2);
                this.graphics.moveTo(-size / 2, size / 4);
                this.graphics.lineTo(-size / 2, size / 2);
                this.graphics.lineTo(-size / 4, size / 2);
            }
        }

        private function drawHats():void {
            if (!RenderConfiguration.showHats()) {
                hatCanvas.visible = false;
                return;
            }

            hatCanvas.visible = true;
            while (hatImages.length < hats.length) {
                var hatImage:Image = new Image();
                var hatSource:Class = ImageAssets.getHatAvatar(hats[hatImages.length]);
                hatImage.source = new hatSource();
                hatImage.width = RenderConfiguration.getGridSize();
                hatImage.height = RenderConfiguration.getGridSize();
                hatImage.x = -RenderConfiguration.getGridSize() / 2;
                hatImage.y = -RenderConfiguration.getGridSize() - hatImage.height * hatImages.length;
                hatCanvas.addChild(hatImage);
                hatImages.push(hatImage);
            }
        }

        ///////////////////////////////////////////////////////
        ////////////// PRIVATE HELPER FUNCTIONS ///////////////
        ///////////////////////////////////////////////////////

        private function getImageSize(scale:Boolean = false):Number {
            if (overrideSize)
                return overrideSize;
            return RenderConfiguration.getGridSize() * (scale ? getUnitScale(type) : 1.0);
        }

        private function getImageScale():Number {
            if (overrideSize)
                return 1.0;
            return RenderConfiguration.getScalingFactor();
        }

        private function getUnitScale(type:String):Number {
            switch (type) {
                case RobotType.HQ:
                    return 2.0;
                case RobotType.TOWER:
                    return 1.5;
                default:
                    return 1.0;
            }
        }

        private function getUnitOffset(type:String):Number {
            return 0;
        }

        private function directionToRotation(dir:String):int {
            switch (dir) {
                case Direction.NORTH:
                    return -90;
                case Direction.NORTH_EAST:
                    return -45;
                case Direction.EAST:
                    return 0;
                case Direction.SOUTH_EAST:
                    return 45;
                case Direction.SOUTH:
                    return 90;
                case Direction.SOUTH_WEST:
                    return 135;
                case Direction.WEST:
                    return 180;
                case Direction.NORTH_WEST:
                    return -135;
                default:
                    return 0;
            }
        }

        private function directionOffsetX(dir:String):int {
            switch (dir) {
                case Direction.NORTH_EAST:
                    return +1;
                case Direction.NORTH_WEST:
                    return -1;
                case Direction.SOUTH_EAST:
                    return +1;
                case Direction.SOUTH_WEST:
                    return -1;
                case Direction.NORTH:
                    return 0;
                case Direction.SOUTH:
                    return 0;
                case Direction.EAST:
                    return +1;
                case Direction.WEST:
                    return -1;
                default:
                    return 0;
            }
        }

        private function directionOffsetY(dir:String):int {
            switch (dir) {
                case Direction.NORTH_EAST:
                    return -1;
                case Direction.NORTH_WEST:
                    return -1;
                case Direction.SOUTH_EAST:
                    return +1;
                case Direction.SOUTH_WEST:
                    return +1;
                case Direction.NORTH:
                    return -1;
                case Direction.SOUTH:
                    return +1;
                case Direction.EAST:
                    return 0;
                case Direction.WEST:
                    return 0;
                default:
                    return 0;
            }
        }

        private function calculateDrawX(rounds:uint):Number {
            if (RenderConfiguration.showDiscrete()) return 0;
            return -1 * getImageSize() * directionOffsetX(direction) * (rounds / movementDelay);
        }

        private function calculateDrawY(rounds:uint):Number {
            if (RenderConfiguration.showDiscrete()) return 0;
            return -1 * getImageSize() * directionOffsetY(direction) * (rounds / movementDelay);
        }

    }

}

package battlecode.client.viewer.render {
    import battlecode.common.ActionType;
    import battlecode.common.Direction;
    import battlecode.common.GameConstants;
    import battlecode.common.MapLocation;
    import battlecode.common.RobotType;
    import battlecode.common.Team;
    import battlecode.events.RobotEvent;

    import mx.containers.Canvas;
    import mx.controls.Image;
    import mx.core.UIComponent;

    [Event(name="indicatorStringChange", type="battlecode.events.RobotEvent")]
    public class DrawRobot extends Canvas implements DrawObject {
        private var actions:Vector.<DrawAction>;

        private var broadcastAnimation:BroadcastAnimation;
        private var explosionAnimation:ExplosionAnimation;

        private var overlayCanvas:UIComponent;
        private var imageCanvas:UIComponent;
        private var hatCanvas:UIComponent;
        private var image:Image;

        // size
        private var overrideSize:Number;

        // indicator strings
        private var indicatorStrings:Vector.<String> = new Vector.<String>(3, true);

        // movement animation
        private var drawX:Number, drawY:Number;
        private var movementDelay:uint;

        // attack animation
        private var targetLocation:MapLocation;

        private var selected:Boolean = false;

        private var robotID:uint;
        private var type:String;
        private var team:String;
        private var location:MapLocation;
        private var direction:String;
        private var energon:Number = 0;
        private var maxEnergon:Number = 0;
        private var alive:Boolean = true;
        private var hats:Array;
        private var hatImages:Array;

        public function DrawRobot(robotID:uint, type:String, team:String, overrideSize:Number = 0) {
            this.robotID = robotID;
            this.type = type;
            this.team = team;
            this.maxEnergon = RobotType.maxEnergon(type);
            this.movementDelay = 1;
            this.hats = new Array();
            this.hatImages = new Array();

            this.actions = new Vector.<DrawAction>();

            this.overrideSize = overrideSize;

            // set the unit avatar image
            var avatarClass:Class = ImageAssets.getRobotAvatar(type, team);
            this.imageCanvas = new UIComponent();
            this.image = new Image();
            this.image.source = avatarClass;
            this.image.width = getImageSize(true);
            this.image.height = getImageSize(true);
            this.image.x = -this.image.width / 2;
            this.image.y = -this.image.height / 2;
            this.imageCanvas.addChild(image);
            this.addChild(imageCanvas);

            this.width = this.image.width;
            this.height = this.image.height;

            this.overlayCanvas = new UIComponent();
            this.addChild(overlayCanvas);

            this.hatCanvas = new UIComponent();
            this.addChild(hatCanvas);

            // set the hit area for click selection
            this.hitArea = imageCanvas;

            // animations
            this.broadcastAnimation = new BroadcastAnimation(0, team);
            this.addChild(broadcastAnimation);

            this.explosionAnimation = new ExplosionAnimation();
            this.addChild(explosionAnimation);
        }

        public function clone():DrawObject {
            var d:DrawRobot = new DrawRobot(robotID, type, team, overrideSize);
            d.location = location;
            d.energon = energon;
            d.movementDelay = movementDelay;
            d.targetLocation = targetLocation;
            d.alive = alive;
            d.hats = hats.concat();
            d.hatImages = new Array();

            d.actions = new Vector.<DrawAction>(actions.length);
            for each (var o:DrawAction in actions) {
                d.actions.push(o.clone());
            }

            d.removeChild(d.broadcastAnimation);
            d.removeChild(d.explosionAnimation);
            d.broadcastAnimation = broadcastAnimation.clone() as BroadcastAnimation;
            d.explosionAnimation = explosionAnimation.clone() as ExplosionAnimation;
            d.addChild(d.broadcastAnimation);
            d.addChild(d.explosionAnimation);

            return d;
        }

        private function addAction(d:DrawAction):void {
            actions.push(d);
        }

        public function getRobotID():uint {
            return robotID;
        }

        public function getType():String {
            return type;
        }

        public function getTeam():String {
            return team;
        }

        public function getLocation():MapLocation {
            return location;
        }

        public function getSelected():Boolean {
            return selected;
        }

        public function getIndicatorString(index:uint):String {
            return indicatorStrings[index];
        }

        public function setLocation(location:MapLocation):void {
            this.location = location;
        }

        public function setEnergon(amt:Number):void {
            this.energon = Math.min(Math.max(0, amt), maxEnergon);
        }

        public function setSelected(val:Boolean):void {
            this.selected = val;
        }

        public function setIndicatorString(str:String, index:uint):void {
            indicatorStrings[index] = str;
            dispatchEvent(new RobotEvent(RobotEvent.INDICATOR_STRING_CHANGE, false, false, this));
        }

        public function setOverrideSize(overrideSize:Number):void {
            this.overrideSize = overrideSize;
            this.draw(true);
        }

        public function capture():void {
            this.addAction(new DrawAction(ActionType.CAPTURING, GameConstants.CAPTURE_DELAY));
        }

        public function layMine():void {
            this.addAction(new DrawAction(ActionType.MINING, GameConstants.MINE_LAY_DELAY));
        }

        public function stopLayingMine():void {
            this.addAction(new DrawAction(ActionType.MINING_STOPPING, GameConstants.MINE_LAY_STOP_DELAY));
        }

        public function diffuseMine(hasUpgrade:Boolean):void {
            this.addAction(new DrawAction(ActionType.DEFUSING, hasUpgrade ? GameConstants.MINE_DIFFUSE_UPGRADE_DELAY: GameConstants.MINE_DIFFUSE_DELAY))
        }

        public function attack(targetLocation:MapLocation):void {
            this.targetLocation = targetLocation;
            this.addAction(new DrawAction(ActionType.ATTACKING, RobotType.attackDelay(type)));
        }

        public function broadcast():void {
            this.broadcastAnimation.broadcast();
        }

        public function destroyUnit():void {
            this.explosionAnimation.explode();
            this.alive = false;
        }

        public function moveToLocation(location:MapLocation):void {
            this.direction = this.location.directionTo(location);
            this.movementDelay = Direction.isDiagonal(direction) ?
                    RobotType.movementDelayDiagonal(type) :
                    RobotType.movementDelay(type);
            this.location = location;
            this.addAction(new DrawAction(ActionType.MOVING, movementDelay));
        }

        public function wearHat(hat:int):void {
            hats.push(hat);
        }

        public function isAlive():Boolean {
            return alive || explosionAnimation.isAlive();
        }

        public function updateRound():void {
            // update actions
            for (var i:uint = 0; i < actions.length; i++) {
                var o:DrawAction = actions[i];
                o.decreaseRound();
                if (o.getRounds() <= 0) {
                    actions.splice(i, 1);
                    i--;
                }
            }

            // update animations
            broadcastAnimation.updateRound();
            explosionAnimation.updateRound();

            // update tooltip
            this.toolTip = "Robot " + getRobotID() + " " + getType() + " Energon: " + energon + " Loc: " + getLocation().toString();
        }

        public function draw(force:Boolean = false):void {
            if (explosionAnimation.isExploding()) {
                this.imageCanvas.visible = false;
                this.overlayCanvas.visible = false;
                this.graphics.clear();

                explosionAnimation.draw(force);
                return;
            }

            // draw direction
            this.imageCanvas.rotation = directionToRotation(direction);

            if (force) {
                this.image.width = getImageSize(true);
                this.image.height = getImageSize(true);
                this.image.x = -this.image.width / 2;
                this.image.y = -this.image.height / 2;
            }

            // clear the graphics object once
            this.graphics.clear();
            this.overlayCanvas.graphics.clear();

            var o:DrawAction;
            var movementRounds:uint = 0;
            for each (o in actions) {
                if (o.getType() == ActionType.MOVING) {
                    movementRounds = o.getRounds();
                    break;
                }
            }

            drawX = calculateDrawX(movementRounds);
            drawY = calculateDrawY(movementRounds);

            for each (o in actions) {
                switch (o.getType()) {
                    case ActionType.ATTACKING:
                        drawAttack();
                        break;
                    case ActionType.MOVING:
                        drawMovement();
                        break;
                    case ActionType.CAPTURING:
                    case ActionType.MINING:
                    case ActionType.MINING_STOPPING:
                    case ActionType.DEFUSING:
                        drawActionBar(o);
                        break;
                }
            }

            drawEnergonBar();
            drawSelected();
            drawHats();

            // draw animations
            broadcastAnimation.draw(force);
        }

        ///////////////////////////////////////////////////////
        ////////////// DRAWING HELPER FUNCTIONS ///////////////
        ///////////////////////////////////////////////////////

        private function drawEnergonBar():void {
            if (!RenderConfiguration.showEnergon() && getType() != RobotType.HQ)
                return;

            var ratio:Number = energon / maxEnergon;
            var size:Number = getImageSize(true);
            this.graphics.lineStyle();
            this.graphics.beginFill(0x00FF00, 0.8);
            this.graphics.drawRect(-size / 2, size / 2, ratio * size, 5 * getImageScale());
            this.graphics.endFill();
            this.graphics.beginFill(0x000000, 0.8);
            this.graphics.drawRect(-size / 2 + ratio * size, size / 2, (1 - ratio) * size, 5 * getImageScale());
            this.graphics.endFill();
        }

        private function drawActionBar(action:DrawAction):void {
            var yOffset:Number = RenderConfiguration.showEnergon() ? 5 * getImageScale() : 0;
            var color:uint;
            switch (action.getType()) {
                case ActionType.CAPTURING:
                    color = 0x4C4CFF;
                    break;
                case ActionType.MINING:
                    color = 0xFF00CC;
                    break;
                case ActionType.MINING_STOPPING:
                    color = 0xFF0000;
                    break;
                case ActionType.DEFUSING:
                    color = 0x00FFFF;
                    break;
                default:
                    color = 0x000000;
                    break;
            }

            var ratio:Number;
            switch (action.getType()) {
                case ActionType.MINING_STOPPING:
                    ratio = action.getRounds() / action.getMaxRounds();
                    break;
                default:
                    ratio = (action.getMaxRounds() - action.getRounds()) / action.getMaxRounds();
                    break;
            }
            var size:Number = getImageSize(true);
            this.graphics.lineStyle();
            this.graphics.beginFill(color, 0.8);
            this.graphics.drawRect(-size / 2, size / 2 + yOffset, ratio * size, 5 * getImageScale());
            this.graphics.endFill();
            this.graphics.beginFill(0x000000, 0.8);
            this.graphics.drawRect(-size / 2 + ratio * size, size / 2  + yOffset, (1 - ratio) * size, 5 * getImageScale());
            this.graphics.endFill();
        }

        private function drawAttack():void {
            var targetOffsetX:Number = (targetLocation.getX() - location.getX()) * getImageSize();
            var targetOffsetY:Number = (targetLocation.getY() - location.getY()) * getImageSize();

            this.graphics.lineStyle(2, team == Team.A ? 0xFF0000 : 0x0000FF);
            this.graphics.moveTo(0, 0);
            this.graphics.lineTo(targetOffsetX - drawX, targetOffsetY - drawY);
            this.graphics.drawCircle(targetOffsetX - drawX, targetOffsetY - drawY, getImageSize() / 2 * .6);
        }

        private function drawMovement():void {
            if (RenderConfiguration.showDiscrete()) return;

            this.x += drawX;
            this.y += drawY;
        }

        private function drawSelected():void {
            var size:Number = getImageSize(true);

            if (selected) {
                this.graphics.lineStyle(2, 0xFFFFFF);
                this.graphics.moveTo(-size / 2, -size / 4);
                this.graphics.lineTo(-size / 2, -size / 2);
                this.graphics.lineTo(-size / 4, -size / 2);
                this.graphics.moveTo(size / 2, -size / 4);
                this.graphics.lineTo(size / 2, -size / 2);
                this.graphics.lineTo(size / 4, -size / 2);
                this.graphics.moveTo(size / 2, size / 4);
                this.graphics.lineTo(size / 2, size / 2);
                this.graphics.lineTo(size / 4, size / 2);
                this.graphics.moveTo(-size / 2, size / 4);
                this.graphics.lineTo(-size / 2, size / 2);
                this.graphics.lineTo(-size / 4, size / 2);
            }
        }

        private function drawHats():void {
            if (!RenderConfiguration.showHats()) {
                hatCanvas.visible = false;
                return;
            }

            hatCanvas.visible = true;
            while (hatImages.length < hats.length) {
                var hatImage:Image = new Image();
                var hatSource:Class = ImageAssets.getHatAvatar(hats[hatImages.length]);
                hatImage.source = new hatSource();
                hatImage.width = RenderConfiguration.getGridSize();
                hatImage.height = RenderConfiguration.getGridSize();
                hatImage.x = -RenderConfiguration.getGridSize() / 2;
                hatImage.y = -RenderConfiguration.getGridSize() - hatImage.height * hatImages.length;
                hatCanvas.addChild(hatImage);
                hatImages.push(hatImage);
            }
        }

        ///////////////////////////////////////////////////////
        ////////////// PRIVATE HELPER FUNCTIONS ///////////////
        ///////////////////////////////////////////////////////

        private function getImageSize(scale:Boolean = false):Number {
            if (overrideSize)
                return overrideSize;
            return RenderConfiguration.getGridSize() * (scale ? getUnitScale(type) : 1.0);
        }

        private function getImageScale():Number {
            if (overrideSize)
                return 1.0;
            return RenderConfiguration.getScalingFactor();
        }

        private function getUnitScale(type:String):Number {
            switch (type) {
                case RobotType.HQ:
                    return 2.0;
                case RobotType.TOWER:
                    return 1.5;
                default:
                    return 1.0;
            }
        }

        private function getUnitOffset(type:String):Number {
            return 0;
        }

        private function directionToRotation(dir:String):int {
            switch (dir) {
                case Direction.NORTH:
                    return -90;
                case Direction.NORTH_EAST:
                    return -45;
                case Direction.EAST:
                    return 0;
                case Direction.SOUTH_EAST:
                    return 45;
                case Direction.SOUTH:
                    return 90;
                case Direction.SOUTH_WEST:
                    return 135;
                case Direction.WEST:
                    return 180;
                case Direction.NORTH_WEST:
                    return -135;
                default:
                    return 0;
            }
        }

        private function directionOffsetX(dir:String):int {
            switch (dir) {
                case Direction.NORTH_EAST:
                    return +1;
                case Direction.NORTH_WEST:
                    return -1;
                case Direction.SOUTH_EAST:
                    return +1;
                case Direction.SOUTH_WEST:
                    return -1;
                case Direction.NORTH:
                    return 0;
                case Direction.SOUTH:
                    return 0;
                case Direction.EAST:
                    return +1;
                case Direction.WEST:
                    return -1;
                default:
                    return 0;
            }
        }

        private function directionOffsetY(dir:String):int {
            switch (dir) {
                case Direction.NORTH_EAST:
                    return -1;
                case Direction.NORTH_WEST:
                    return -1;
                case Direction.SOUTH_EAST:
                    return +1;
                case Direction.SOUTH_WEST:
                    return +1;
                case Direction.NORTH:
                    return -1;
                case Direction.SOUTH:
                    return +1;
                case Direction.EAST:
                    return 0;
                case Direction.WEST:
                    return 0;
                default:
                    return 0;
            }
        }

        private function calculateDrawX(rounds:uint):Number {
            if (RenderConfiguration.showDiscrete()) return 0;
            return -1 * getImageSize() * directionOffsetX(direction) * (rounds / movementDelay);
        }

        private function calculateDrawY(rounds:uint):Number {
            if (RenderConfiguration.showDiscrete()) return 0;
            return -1 * getImageSize() * directionOffsetY(direction) * (rounds / movementDelay);
        }

    }

}

fix attack animation for non-moving units